Skip to content

Commit 4864906

Browse files
committed
Fix operations to use native_ssh instead of nx-api
1 parent 43f1188 commit 4864906

3 files changed

Lines changed: 57 additions & 19 deletions

File tree

changes/mm.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed an issue where nx-api commands were mixed with ssh commands.

pyntc/devices/nxos_device.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,9 @@ def _get_file_system(self):
328328
Raises:
329329
FileSystemNotFoundError: When the module is unable to determine the default file system.
330330
"""
331-
raw_data = self.show("dir", raw_text=True)
331+
self.open()
332+
raw_data = self.native_ssh.send_command("dir", read_timeout=30)
333+
332334
try:
333335
file_system = re.search(r"bootflash:", raw_data).group(0)
334336
except AttributeError:
@@ -343,7 +345,8 @@ def _get_free_space(self, file_system=None):
343345
if file_system is None:
344346
file_system = self._get_file_system()
345347

346-
raw_data = self.show(f"dir {file_system}", raw_text=True)
348+
self.open()
349+
raw_data = self.native_ssh.send_command(f"dir {file_system}", read_timeout=30)
347350
# Example NXOS dir output: 47171194880 bytes free
348351
match = re.search(r"(\d+)\s+bytes\s+free", raw_data)
349352
if match is None:

tests/unit/test_devices/test_nxos_device.py

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22

33
import mock
4+
from hypothesis import given, strategies as st
45

56
from pyntc.devices.base_device import RollbackError
67
from pyntc.devices.nxos_device import NXOSDevice
@@ -286,22 +287,21 @@ def test_refresh(self):
286287
self.assertIsNone(self.device._uptime)
287288
self.assertFalse(hasattr(self.device.native, "_facts"))
288289

289-
@mock.patch.object(NXOSDevice, "show", return_value="bootflash:")
290-
def test_get_file_system(self, mock_show):
290+
def test_get_file_system(self):
291+
self.device.native_ssh.send_command.return_value = "bootflash:"
291292
self.assertEqual(self.device._get_file_system(), "bootflash:")
292-
mock_show.assert_called_with("dir", raw_text=True)
293+
self.device.native_ssh.send_command.assert_called_with("dir", read_timeout=30)
293294

294-
@mock.patch.object(NXOSDevice, "show", return_value="no filesystems here")
295-
def test_get_file_system_not_found(self, mock_show):
295+
def test_get_file_system_not_found(self):
296+
self.device.native_ssh.send_command.return_value = "no filesystems here"
296297
with self.assertRaises(FileSystemNotFoundError):
297298
self.device._get_file_system()
298-
mock_show.assert_called_with("dir", raw_text=True)
299+
self.device.native_ssh.send_command.assert_called_with("dir", read_timeout=30)
299300

300-
@mock.patch.object(NXOSDevice, "show")
301-
def test_get_free_space(self, mock_show):
301+
def test_get_free_space(self):
302302
"""Test _get_free_space parses NXOS dir output correctly."""
303303
# NXOS dir output format with free space at the end
304-
mock_show.return_value = """Directory of bootflash:/
304+
self.device.native_ssh.send_command.return_value = """Directory of bootflash:/
305305
4096 Mar 03 22:47:15 2026 .rpmstore/
306306
4733329408 bytes used
307307
47171194880 bytes free
@@ -310,25 +310,25 @@ def test_get_free_space(self, mock_show):
310310
"""
311311
result = self.device._get_free_space()
312312
self.assertEqual(result, 47171194880)
313-
mock_show.assert_called_with("dir bootflash:", raw_text=True)
313+
# Should call _get_file_system (which uses SSH) and then dir command via SSH
314+
ssh_calls = self.device.native_ssh.send_command.call_args_list
315+
self.assertTrue(any("dir" in str(call) for call in ssh_calls))
314316

315-
@mock.patch.object(NXOSDevice, "show")
316-
def test_get_free_space_with_custom_filesystem(self, mock_show):
317+
def test_get_free_space_with_custom_filesystem(self):
317318
"""Test _get_free_space uses custom file system when provided."""
318-
mock_show.return_value = """Directory of disk0:/
319+
self.device.native_ssh.send_command.return_value = """Directory of disk0:/
319320
1000000 bytes used
320321
2000000 bytes free
321322
3000000 bytes total
322323
323324
"""
324325
result = self.device._get_free_space("disk0:")
325326
self.assertEqual(result, 2000000)
326-
mock_show.assert_called_with("dir disk0:", raw_text=True)
327+
self.device.native_ssh.send_command.assert_called_with("dir disk0:", read_timeout=30)
327328

328-
@mock.patch.object(NXOSDevice, "show")
329-
def test_get_free_space_raises_on_parse_error(self, mock_show):
329+
def test_get_free_space_raises_on_parse_error(self):
330330
"""Test _get_free_space raises CommandError when output can't be parsed."""
331-
mock_show.return_value = "Directory of bootflash:/\nNo free space info here\n"
331+
self.device.native_ssh.send_command.return_value = "Directory of bootflash:/\nNo free space info here\n"
332332
with self.assertRaises(CommandError):
333333
self.device._get_free_space()
334334

@@ -455,6 +455,40 @@ def test_remote_file_copy_query_string_not_supported(self):
455455
with self.assertRaises(ValueError):
456456
self.device.remote_file_copy(src, file_system="bootflash:")
457457

458+
@given(
459+
scheme=st.sampled_from(["http", "https", "scp", "sftp", "ftp", "tftp"]),
460+
hostname=st.text(min_size=1, max_size=20, alphabet=st.characters(whitelist_categories=("Ll", "Lu", "Nd"))),
461+
filename=st.text(min_size=1, max_size=20, alphabet=st.characters(whitelist_categories=("Ll", "Lu", "Nd", "Pd"))),
462+
checksum=st.text(min_size=32, max_size=32, alphabet=st.characters(whitelist_categories=("Ll", "Nd"))),
463+
)
464+
def test_remote_file_copy_uses_ssh_for_filesystem_detection(self, scheme, hostname, filename, checksum):
465+
"""Property-based test: remote_file_copy should use SSH for _get_file_system calls.
466+
467+
This test verifies that the SSH/HTTP protocol mismatch bug is fixed by ensuring
468+
that _get_file_system always uses SSH for file system operations.
469+
"""
470+
src = FileCopyModel(
471+
download_url=f"{scheme}://{hostname}/{filename}",
472+
checksum=checksum,
473+
file_name=filename,
474+
hashing_algorithm="md5",
475+
timeout=30,
476+
)
477+
478+
# Mock SSH operations to simulate successful file system detection
479+
self.device.native_ssh.send_command.return_value = "Directory of bootflash:/\n47171194880 bytes free"
480+
self.device.native_ssh.find_prompt.return_value = "host#"
481+
482+
# Mock verify_file to return True (file already exists and verified)
483+
with mock.patch.object(NXOSDevice, "verify_file", return_value=True):
484+
# This should complete without attempting HTTP connections
485+
self.device.remote_file_copy(src)
486+
487+
# Verify that SSH was used for directory command (filesystem detection)
488+
ssh_calls = self.device.native_ssh.send_command.call_args_list
489+
self.assertTrue(any("dir" in str(call) for call in ssh_calls),
490+
"Expected SSH 'dir' command for filesystem detection")
491+
458492

459493
if __name__ == "__main__":
460494
unittest.main()

0 commit comments

Comments
 (0)