Skip to content

Add ASA pre-transfer free-space check (NAPPS-1087)#372

Merged
jeffkala merged 4 commits intonetworktocode:developfrom
jtdub:u/jtdub-napps-1087
Apr 22, 2026
Merged

Add ASA pre-transfer free-space check (NAPPS-1087)#372
jeffkala merged 4 commits intonetworktocode:developfrom
jtdub:u/jtdub-napps-1087

Conversation

@jtdub
Copy link
Copy Markdown
Contributor

@jtdub jtdub commented Apr 21, 2026

Summary

  • Implements the fail-open pre-transfer free-space check on Cisco ASA using the seam added in NAPPS-1091 (PR Add EOS pre-transfer free-space check (NAPPS-1091) #370), so OS-image transfers fail fast when disk0: lacks room instead of half-writing flash.
  • ASADevice._get_free_space parses the (N bytes free) trailer from dir output; file_copy calls _check_free_space(os.path.getsize(src)) before any SCP transfer; remote_file_copy calls _pre_transfer_space_check inside the existing not verify_file block so the check only fires when a transfer would actually happen.
  • Unit tests cover the dir-trailer parse, the missing-trailer raise, file_copy raising before SCP, remote_file_copy raising before any copy command, and the fail-open path. Integration tests mirror the EOS pattern for manual lab runs.

NAPPS-1087

Test plan

  • poetry run pytest tests/unit/test_devices/test_asa_device.py — all 143 pass.
  • poetry run pytest tests/unit — all 659 pass.
  • poetry run ruff check pyntc tests — clean.
  • Lab run: poetry run pytest tests/integration/test_asa_device.py -v with ASA_HOST / ASA_USER / ASA_PASS / ASA_SECRET + protocol URLs + FILE_CHECKSUM / FILE_SIZE / FILE_SIZE_UNIT set — verifies the probe, the oversized reject, and the fail-open path end-to-end.

🤖 Generated with Claude Code

Implements the fail-open pre-transfer free-space check on Cisco ASA
using the seam added in NAPPS-1091 (PR networktocode#370). Image transfers now
fail fast when ``disk0:`` lacks room instead of half-writing flash.

- ASADevice._get_free_space parses the ``(N bytes free)`` trailer from
  ``dir`` output (same format EOS exposes).
- file_copy calls _check_free_space with os.path.getsize on the local
  source before any SCP transfer.
- remote_file_copy calls _pre_transfer_space_check inside the existing
  ``not verify_file`` block so the check only fires when a transfer
  would actually happen; still fail-open when src.file_size_bytes is
  None.
- Unit tests cover the dir-trailer parse, the missing-trailer raise,
  file_copy raising before SCP, remote_file_copy raising before any
  copy command, and the fail-open path spying on _check_free_space.
- Existing file_copy unit tests updated to mock os.path.getsize and
  _check_free_space (previously they passed a non-existent path that
  would now blow up on os.path.getsize).
- Integration tests mirror the EOS pattern for manual lab runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jtdub jtdub marked this pull request as draft April 21, 2026 19:44
jtdub and others added 2 commits April 21, 2026 15:18
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Real ASA platforms print the dir trailer as
``(<N> bytes free/<pct>% free)`` — the closing paren is not adjacent
to ``free``. The previous regex anchored on ``\)`` and so would have
failed on live hardware (verified against an ASA 5512 running 9.9.2
at 10.1.100.80, which reports
``4118732800 bytes total (3580170240 bytes free/86% free)``). Drop
the trailing ``\)`` anchor and update the mock fixture + tests to
cover both the real-device shape and the legacy
``(N bytes free)`` emulator shape.

Bumped the ``test_remote_file_copy_raises_not_enough_free_space``
oversize from 2 GB to 10 GB because the mock now reports ~3.3 GB
free — 2 GB fits, 10 GB does not.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jtdub
Copy link
Copy Markdown
Contributor Author

jtdub commented Apr 21, 2026

% source .env && poetry run pytest tests/integration/test_asa_device.py           
========================================================= test session starts =========================================================
platform darwin -- Python 3.12.11, pytest-9.0.3, pluggy-1.6.0 -- /Users/jameswilliams/Library/Caches/pypoetry/virtualenvs/pyntc-gs002iVE-py3.12/bin/python
cachedir: .pytest_cache
rootdir: /Users/jameswilliams/Documents/code/pyntc
configfile: pyproject.toml
plugins: f5-sdk-3.0.21, requests-mock-1.12.1
collected 16 items                                                                                                                    

tests/integration/test_asa_device.py::test_device_connects PASSED                                                               [  6%]
tests/integration/test_asa_device.py::test_check_file_exists_false PASSED                                                       [ 12%]
tests/integration/test_asa_device.py::test_get_remote_checksum_after_exists PASSED                                              [ 18%]
tests/integration/test_asa_device.py::test_remote_file_copy_ftp PASSED                                                          [ 25%]
tests/integration/test_asa_device.py::test_remote_file_copy_tftp PASSED                                                         [ 31%]
tests/integration/test_asa_device.py::test_remote_file_copy_scp PASSED                                                          [ 37%]
tests/integration/test_asa_device.py::test_remote_file_copy_http PASSED                                                         [ 43%]
tests/integration/test_asa_device.py::test_remote_file_copy_https PASSED                                                        [ 50%]
tests/integration/test_asa_device.py::test_verify_file_after_copy PASSED                                                        [ 56%]
tests/integration/test_asa_device.py::test_get_free_space_returns_positive_int PASSED                                           [ 62%]
tests/integration/test_asa_device.py::test_check_free_space_succeeds_for_small_request PASSED                                   [ 68%]
tests/integration/test_asa_device.py::test_check_free_space_raises_when_required_exceeds_free PASSED                            [ 75%]
tests/integration/test_asa_device.py::test_file_size_unit_conversion_matches_device_free_space PASSED                           [ 81%]
tests/integration/test_asa_device.py::test_remote_file_copy_rejects_oversized_transfer PASSED                                   [ 87%]
tests/integration/test_asa_device.py::test_remote_file_copy_accepts_declared_size_within_free_space PASSED                      [ 93%]
tests/integration/test_asa_device.py::test_remote_file_copy_skips_space_check_when_file_size_omitted PASSED                     [100%]

=================================================== 16 passed in 108.52s (0:01:48) ====================================================

@jtdub jtdub marked this pull request as ready for review April 22, 2026 14:33
Copy link
Copy Markdown
Contributor

@mattmiller87 mattmiller87 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This matches what we were doing in EOS, IOS, NXOS. ASA seems almost identical with the "dir" command output.

@jeffkala jeffkala merged commit d317130 into networktocode:develop Apr 22, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants