def test_boot_options_show_run(self, mock_boot): results = [ CommandError("show bootvar", "fail"), CommandError("show bootvar", "fail"), f"boot system flash bootflash:/{BOOT_IMAGE}", "Directory of bootflash:/", ] self.device.native.send_command_timing.side_effect = results boot_options = self.device.boot_options self.assertEqual(boot_options, {"sys": BOOT_IMAGE}) self.device.native.send_command_timing.assert_called_with( "show run | inc boot")
def set_boot_options(self, image_name, **vendor_specifics): file_system = vendor_specifics.get("file_system") if file_system is None: file_system = self._get_file_system() file_system_files = self.show("dir {0}".format(file_system)) if re.search(image_name, file_system_files) is None: raise NTCFileNotFoundError(hostname=self.facts.get("hostname"), file=image_name, dir=file_system) try: self.config_list([ 'no boot system', 'boot system {0}/{1}'.format(file_system, image_name) ]) except CommandError: file_system = file_system.replace(':', '') self.config_list([ 'no boot system', 'boot system {0} {1}'.format(file_system, image_name) ]) if self.get_boot_options()["sys"] != image_name: raise CommandError( command="boot command", message="Setting boot command did not yield expected results", )
def show(self, command, raw_text=False): try: response_list = self.show_list([command], raw_text=raw_text) except CommandListError as e: raise CommandError(e.command, e.message) return response_list[0]
def show(self, commands, raw_text=False): """Send configuration commands to a device. Args: commands (str, list): String with single command, or list with multiple commands. raw_text (bool, optional): False if encode should be json, True if encoding is text. Defaults to False. Raises: CommandError: Issue with the command provided. CommandListError: Issue with a command in the list provided. """ if not raw_text: encoding = "json" else: encoding = "text" original_commands_is_str = isinstance(commands, str) if original_commands_is_str: commands = [commands] try: response = self.native.enable(commands, encoding=encoding) response_list = self._parse_response(response, raw_text=raw_text) if original_commands_is_str: return response_list[0] return response_list except EOSCommandError as e: if original_commands_is_str: raise CommandError(e.commands, e.message) raise CommandListError(commands, e.commands[len(e.commands) - 1], e.message)
def config(self, commands, format="set"): """Send configuration commands to a device. Args: commands (str, list): String with single command, or list with multiple commands. Raises: ConfigLoadError: Issue with loading the command. CommandError: Issue with the command provided, if its a single command, passed in as a string. CommandListError: Issue with a command in the list provided. """ if isinstance(commands, str): try: self.cu.load(commands, format=format) self.cu.commit() except ConfigLoadError as e: raise CommandError(commands, e.message) else: try: for command in commands: self.cu.load(command, format=format) self.cu.commit() except ConfigLoadError as e: raise CommandListError(commands, command, e.message)
def set_boot_options(self, image_name, **vendor_specifics): current_boot = self.show("show running-config | inc ^boot system ") file_system = vendor_specifics.get("file_system") if file_system is None: file_system = self._get_file_system() file_system_files = self.show("dir {0}".format(file_system)) if re.search(image_name, file_system_files) is None: raise NTCFileNotFoundError( # TODO: Update to use hostname hostname=self.host, file=image_name, dir=file_system, ) current_images = current_boot.splitlines() commands_to_exec = ["no {0}".format(image) for image in current_images] commands_to_exec.append("boot system {0}/{1}".format(file_system, image_name)) self.config_list(commands_to_exec) self.save() if self.boot_options["sys"] != image_name: raise CommandError( command="boot system {0}/{1}".format(file_system, image_name), message="Setting boot command did not yield expected results", )
def show(self, commands): """Send configuration commands to a device. Args: commands (str, list): String with single command, or list with multiple commands. Raises: CommandError: Issue with the command provided. CommandListError: Issue with a command in the list provided. """ original_commands_is_str = isinstance(commands, str) if original_commands_is_str: commands = [commands] responses = [] for command in commands: if not command.startswith("show"): if original_commands_is_str: raise CommandError( command, 'Juniper "show" commands must begin with "show".') raise CommandListError( commands, command, 'Juniper "show" commands must begin with "show".') response = self.native.cli(command, warning=False) responses.append(response) if original_commands_is_str: return responses[0] return responses
def set_boot_options(self, image_name, **vendor_specifics): file_system = vendor_specifics.get("file_system") if file_system is None: file_system = self._get_file_system() file_system_files = self.show("dir {0}".format(file_system)) if re.search(image_name, file_system_files) is None: raise NTCFileNotFoundError(hostname=self.hostname, file=image_name, dir=file_system) try: command = "boot system {0}/{1}".format(file_system, image_name) self.config(["no boot system", command]) except CommandListError: # TODO: Update to CommandError when deprecating config_list file_system = file_system.replace(":", "") command = "boot system {0} {1}".format(file_system, image_name) self.config(["no boot system", command]) self.save() new_boot_options = self.boot_options["sys"] if new_boot_options != image_name: raise CommandError( command=command, message= "Setting boot command did not yield expected results, found {0}" .format(new_boot_options), )
def show(self, command, raw_text=True): if not raw_text: raise ValueError('Juniper only supports raw text output. \ Append " | display xml" to your commands for a structured string.') if not command.startswith('show'): raise CommandError(command, 'Juniper "show" commands must begin with "show".') return self.native.cli(command, warning=False)
def _send_command(self, command, expect_string=None): if expect_string is None: response = self.native.send_command_timing(command) else: response = self.native.send_command(command, expect_string=expect_string) if "% " in response or "Error:" in response: raise CommandError(command, response) return response
def _send_command(self, command, expect=False, expect_string=""): if expect: if expect_string: response = self.native.send_command_expect(command, expect_string=expect_string) else: response = self.native.send_command_expect(command) else: response = self.native.send_command_timing(command) if "% " in response or "Error:" in response: raise CommandError(command, response) return response
def _send_command(self, command, expect=False, expect_string=''): if expect: if expect_string: response = self.native.send_command_expect(command, expect_string=expect_string) else: response = self.native.send_command_expect(command) else: response = self.native.send_command(command) if '% ' in response or 'Error:' in response: raise CommandError(command, response) return response
def test_boot_options_show_boot(self, mock_boot): show_boot_out = ( "Current Boot Variables:\n" "BOOT variable = flash:/cat3k_caa-universalk9.16.11.03a.SPA.bin;\n\n" "Boot Variables on next reload:\n" f"BOOT variable = flash:/{BOOT_IMAGE};\n" "Manual Boot = no\n" "Enable Break = no\n" "Boot Mode = DEVICE\n" "iPXE Timeout = 0") results = [CommandError("show bootvar", "fail"), show_boot_out] self.device.native.send_command_timing.side_effect = results boot_options = self.device.boot_options self.assertEqual(boot_options, {"sys": BOOT_IMAGE}) self.device.native.send_command_timing.assert_called_with("show boot")
def set_boot_options(self, image_name, **vendor_specifics): file_system = vendor_specifics.get("file_system") if file_system is None: file_system = self._get_file_system() file_system_files = self.show("dir {0}".format(file_system), raw_text=True) if re.search(image_name, file_system_files) is None: raise NTCFileNotFoundError(hostname=self.facts.get("hostname"), file=image_name, dir=file_system) self.show("install source {0}{1}".format(file_system, image_name)) if self.get_boot_options()["sys"] != image_name: raise CommandError( command="install source {0}".format(image_name), message="Setting install source did not yield expected results", )
def config(self, commands): """Send configuration commands to a device. Args: commands (str, list): String with single command, or list with multiple commands. Raises: CommandError: Issue with the command provided. CommandListError: Issue with a command in the list provided. """ try: self.native.config(commands) except EOSCommandError as e: if isinstance(commands, str): raise CommandError(commands, e.message) raise CommandListError(commands, e.commands[len(e.commands) - 1], e.message)
def _send_command(self, command, expect_string=None, **kwargs): # Set command args and assign the command to command_string argument command_args = {"command_string": command} # Check for an expect_string being passed in if expect_string is not None: command_args["expect_string"] = expect_string # Update command_args with additional arguments passed in, must be a valid Netmiko argument command_args.update(kwargs) response = self.native.send_command(**command_args) if "% " in response or "Error:" in response: raise CommandError(command, response) return response
def set_boot_options(self, image_name, **vendor_specifics): """ Set the version to boot on the device. Args: image_name (str): The version to boot into on next reload. Raises: NTCFileNotFoundError: When the version is not listed in ``boot_options``. Example: >>> device = AIREOSDevice(**connection_args) >>> device.boot_options { 'backup': '8.8.125.0', 'primary': '8.9.100.0', 'sys': '8.9.100.0' } >>> device.set_boot_options("8.8.125.0") >>> device.boot_options { 'backup': '8.8.125.0', 'primary': '8.9.100.0', 'sys': '8.8.125.0' } """ if self.boot_options["primary"] == image_name: boot_command = "boot primary" elif self.boot_options["backup"] == image_name: boot_command = "boot backup" else: raise NTCFileNotFoundError(image_name, "'show boot'", self.host) self.config(boot_command) self.save() if not self.boot_options["sys"] == image_name: raise CommandError( command=boot_command, message="Setting boot command did not yield expected results", )
def _check_command_output_for_errors(self, command, command_response): """ Check response from device to see if an error was reported. Args: command (str): The command that was sent to the device. Raises: CommandError: When ``command_response`` reports an error in sending ``command``. Example: >>> device = IOSDevice(**connection_args) >>> command = "show version" >>> command_response = "output from show version" >>> device._check_command_output_for_errors(command, command_response) >>> command = "invalid command" >>> command_response = "% invalid command" >>> device._check_command_output_for_errors(command, command_resposne) CommandError: ... >>> """ if "% " in command_response or "Error:" in command_response: raise CommandError(command, command_response)
def _send_command(self, command, expect=False, expect_string=""): """ Send single command to device. Args: command (str): The command to send to the device. expect (bool): Whether to send a different expect string than normal prompt. expect_string (str): The expected prompt after running the command. Returns: str: The response from the device after issuing the ``command``. Raises: CommandError: When the returned data indicates the command failed. Example: >>> device = AIREOSDevice(**connection_args) >>> sysinfo = device._send_command("show sysinfo") >>> print(sysinfo) Product Version.....8.2.170.0 System Up Time......3 days 2 hrs 20 mins 30 sec ... >>> """ if expect: if expect_string: response = self.native.send_command_expect( command, expect_string=expect_string) else: response = self.native.send_command_expect(command) else: response = self.native.send_command_timing(command) if "Incorrect usage" in response or "Error:" in response: raise CommandError(command, response) return response
def config(self, command): try: self.native.config(command) except CLIError as e: raise CommandError(command, str(e))
def show(self, command, raw_text=False): try: return self.native.show(command, raw_text=raw_text) except CLIError as e: raise CommandError(command, str(e))
class TestIOSDevice(unittest.TestCase): @mock.patch.object(IOSDevice, "open") @mock.patch.object(IOSDevice, "close") @mock.patch("netmiko.cisco.cisco_ios.CiscoIosSSH", autospec=True) def setUp(self, mock_miko, mock_close, mock_open): self.device = IOSDevice("host", "user", "pass") mock_miko.send_command_timing.side_effect = send_command mock_miko.send_command_expect.side_effect = send_command_expect self.device.native = mock_miko def tearDown(self): # Reset the mock so we don't have transient test effects self.device.native.reset_mock() def test_config(self): command = "interface fastEthernet 0/1" result = self.device.config(command) self.assertIsNone(result) self.device.native.send_command_timing.assert_called_with(command) def test_bad_config(self): command = "asdf poknw" try: with self.assertRaisesRegex(CommandError, command): self.device.config(command) finally: self.device.native.reset_mock() def test_config_list(self): commands = ["interface fastEthernet 0/1", "no shutdown"] result = self.device.config_list(commands) self.assertIsNone(result) for cmd in commands: self.device.native.send_command_timing.assert_any_call(cmd) def test_bad_config_list(self): commands = ["interface fastEthernet 0/1", "apons"] results = ["ok", "Error: apons"] self.device.native.send_command_timing.side_effect = results with self.assertRaisesRegex(CommandListError, commands[1]): self.device.config_list(commands) def test_show(self): command = "show ip arp" result = self.device.show(command) self.assertIsInstance(result, str) self.assertIn("Protocol", result) self.assertIn("Address", result) self.device.native.send_command_timing.assert_called_with(command) def test_bad_show(self): command = "show microsoft" self.device.native.send_command_timing.return_value = "Error: Microsoft" with self.assertRaises(CommandError): self.device.show(command) def test_show_list(self): commands = ["show version", "show clock"] result = self.device.show_list(commands) self.assertIsInstance(result, list) self.assertIn("uptime is", result[0]) self.assertIn("UTC", result[1]) calls = list(mock.call(x) for x in commands) self.device.native.send_command_timing.assert_has_calls(calls) def test_bad_show_list(self): commands = ["show badcommand", "show clock"] results = ["Error: badcommand", "14:31:57.089 PST Tue Feb 10 2008"] self.device.native.send_command_timing.side_effect = results with self.assertRaisesRegex(CommandListError, "show badcommand"): self.device.show_list(commands) def test_save(self): result = self.device.save() self.assertTrue(result) self.device.native.send_command_timing.assert_any_call( "copy running-config startup-config") @mock.patch("pyntc.devices.ios_device.FileTransfer", autospec=True) def test_file_copy_remote_exists(self, mock_ft): self.device.native.send_command_timing.side_effect = None self.device.native.send_command_timing.return_value = "flash: /dev/null" mock_ft_instance = mock_ft.return_value mock_ft_instance.check_file_exists.return_value = True mock_ft_instance.compare_md5.return_value = True result = self.device.file_copy_remote_exists("source_file") self.assertTrue(result) @mock.patch("pyntc.devices.ios_device.FileTransfer", autospec=True) def test_file_copy_remote_exists_bad_md5(self, mock_ft): self.device.native.send_command_timing.side_effect = None self.device.native.send_command_timing.return_value = "flash: /dev/null" mock_ft_instance = mock_ft.return_value mock_ft_instance.check_file_exists.return_value = True mock_ft_instance.compare_md5.return_value = False result = self.device.file_copy_remote_exists("source_file") self.assertFalse(result) @mock.patch("pyntc.devices.ios_device.FileTransfer", autospec=True) def test_file_copy_remote_exists_not(self, mock_ft): self.device.native.send_command_timing.side_effect = None self.device.native.send_command_timing.return_value = "flash: /dev/null" mock_ft_instance = mock_ft.return_value mock_ft_instance.check_file_exists.return_value = False mock_ft_instance.compare_md5.return_value = True result = self.device.file_copy_remote_exists("source_file") self.assertFalse(result) @mock.patch("pyntc.devices.ios_device.FileTransfer", autospec=True) def test_file_copy(self, mock_ft): self.device.native.send_command_timing.side_effect = None self.device.native.send_command_timing.return_value = "flash: /dev/null" mock_ft_instance = mock_ft.return_value mock_ft_instance.check_file_exists.side_effect = [False, True] self.device.file_copy("path/to/source_file") mock_ft.assert_called_with(self.device.native, "path/to/source_file", "source_file", file_system="flash:") mock_ft_instance.enable_scp.assert_any_call() mock_ft_instance.establish_scp_conn.assert_any_call() mock_ft_instance.transfer_file.assert_any_call() @mock.patch("pyntc.devices.ios_device.FileTransfer", autospec=True) def test_file_copy_different_dest(self, mock_ft): self.device.native.send_command_timing.side_effect = None self.device.native.send_command_timing.return_value = "flash: /dev/null" mock_ft_instance = mock_ft.return_value mock_ft_instance.check_file_exists.side_effect = [False, True] self.device.file_copy("source_file", "dest_file") mock_ft.assert_called_with(self.device.native, "source_file", "dest_file", file_system="flash:") mock_ft_instance.enable_scp.assert_any_call() mock_ft_instance.establish_scp_conn.assert_any_call() mock_ft_instance.transfer_file.assert_any_call() @mock.patch("pyntc.devices.ios_device.FileTransfer", autospec=True) def test_file_copy_fail(self, mock_ft): self.device.native.send_command_timing.side_effect = None self.device.native.send_command_timing.return_value = "flash: /dev/null" mock_ft_instance = mock_ft.return_value mock_ft_instance.transfer_file.side_effect = Exception mock_ft_instance.check_file_exists.return_value = False with self.assertRaises(FileTransferError): self.device.file_copy("source_file") def test_reboot(self): self.device.reboot(confirm=True) self.device.native.send_command_timing.assert_any_call("reload") def test_reboot_with_timer(self): self.device.reboot(confirm=True, timer=5) self.device.native.send_command_timing.assert_any_call("reload in 5") def test_reboot_no_confirm(self): self.device.reboot() assert not self.device.native.send_command_timing.called @mock.patch.object(IOSDevice, "_get_file_system", return_value="bootflash:") def test_boot_options_show_bootvar(self, mock_boot): self.device.native.send_command_timing.side_effect = None self.device.native.send_command_timing.return_value = f"BOOT variable = bootflash:{BOOT_IMAGE}" boot_options = self.device.boot_options self.assertEqual(boot_options, {"sys": BOOT_IMAGE}) self.device.native.send_command_timing.assert_called_with( "show bootvar") @mock.patch.object(IOSDevice, "_get_file_system", return_value="flash:") def test_boot_options_show_boot(self, mock_boot): show_boot_out = ( "Current Boot Variables:\n" "BOOT variable = flash:/cat3k_caa-universalk9.16.11.03a.SPA.bin;\n\n" "Boot Variables on next reload:\n" f"BOOT variable = flash:/{BOOT_IMAGE};\n" "Manual Boot = no\n" "Enable Break = no\n" "Boot Mode = DEVICE\n" "iPXE Timeout = 0") results = [CommandError("show bootvar", "fail"), show_boot_out] self.device.native.send_command_timing.side_effect = results boot_options = self.device.boot_options self.assertEqual(boot_options, {"sys": BOOT_IMAGE}) self.device.native.send_command_timing.assert_called_with("show boot") @mock.patch.object(IOSDevice, "_get_file_system", return_value="bootflash:") def test_boot_options_show_run(self, mock_boot): results = [ CommandError("show bootvar", "fail"), CommandError("show bootvar", "fail"), f"boot system flash bootflash:/{BOOT_IMAGE}", "Directory of bootflash:/", ] self.device.native.send_command_timing.side_effect = results boot_options = self.device.boot_options self.assertEqual(boot_options, {"sys": BOOT_IMAGE}) self.device.native.send_command_timing.assert_called_with( "show run | inc boot") @mock.patch.object(IOSDevice, "_get_file_system", return_value="flash:") @mock.patch.object(IOSDevice, "config_list", return_value=None) def test_set_boot_options(self, mock_cl, mock_fs): with mock.patch(BOOT_OPTIONS_PATH, new_callable=mock.PropertyMock) as mock_boot: mock_boot.return_value = {"sys": BOOT_IMAGE} self.device.set_boot_options(BOOT_IMAGE) mock_cl.assert_called_with( ["no boot system", f"boot system flash:/{BOOT_IMAGE}"]) @mock.patch.object(IOSDevice, "_get_file_system", return_value="flash:") @mock.patch.object(IOSDevice, "config_list", side_effect=[CommandError("boot system", "fail"), None]) def test_set_boot_options_with_spaces(self, mock_cl, mock_fs): with mock.patch(BOOT_OPTIONS_PATH, new_callable=mock.PropertyMock) as mock_boot: mock_boot.return_value = {"sys": BOOT_IMAGE} self.device.set_boot_options(BOOT_IMAGE) mock_cl.assert_called_with( ["no boot system", f"boot system flash {BOOT_IMAGE}"]) @mock.patch.object(IOSDevice, "_get_file_system", return_value="flash:") def test_set_boot_options_no_file(self, mock_fs): with self.assertRaises(NTCFileNotFoundError): self.device.set_boot_options("bad_image.bin") @mock.patch.object(IOSDevice, "_get_file_system", return_value="flash:") @mock.patch.object(IOSDevice, "boot_options", return_value={"sys": "bad_image.bin"}) @mock.patch.object(IOSDevice, "config_list", return_value=None) def test_set_boot_options_bad_boot(self, mock_cl, mock_bo, mock_fs): with self.assertRaises(CommandError): self.device.set_boot_options(BOOT_IMAGE) mock_bo.assert_called_once() def test_backup_running_config(self): filename = "local_running_config" self.device.backup_running_config(filename) with open(filename, "r") as f: contents = f.read() self.assertEqual(contents, self.device.running_config) os.remove(filename) def test_rollback(self): self.device.rollback("good_checkpoint") self.device.native.send_command_timing.assert_called_with( "configure replace flash:good_checkpoint force") def test_bad_rollback(self): # TODO: change to what the protocol would return self.device.native.send_command_timing.return_value = "Error: rollback unsuccessful" with self.assertRaises(RollbackError): self.device.rollback("bad_checkpoint") def test_checkpoint(self): self.device.checkpoint("good_checkpoint") self.device.native.send_command_timing.assert_any_call( "copy running-config good_checkpoint") def test_facts(self): expected = { "uptime": 413940, "vendor": "cisco", "uptime_string": "04:18:59:00", "interfaces": ["FastEthernet0/0", "FastEthernet0/1"], "hostname": "rtr2811", "fqdn": "N/A", "os_version": "15.1(3)T4", "serial_number": "", "model": "2811", "vlans": [], "cisco_ios_ssh": { "config_register": "0x2102" }, } facts = self.device.facts self.assertEqual(facts, expected) self.device.native.send_command_timing.reset_mock() facts = self.device.facts self.assertEqual(facts, expected) self.device.native.send_command_timing.assert_not_called() def test_running_config(self): expected = self.device.show("show running-config") self.assertEqual(self.device.running_config, expected) def test_starting_config(self): expected = self.device.show("show startup-config") self.assertEqual(self.device.startup_config, expected) def test_enable_from_disable(self): self.device.native.check_enable_mode.return_value = False self.device.native.check_config_mode.return_value = False self.device.enable() self.device.native.check_enable_mode.assert_called() self.device.native.enable.assert_called() self.device.native.check_config_mode.assert_called() self.device.native.exit_config_mode.assert_not_called() def test_enable_from_enable(self): self.device.native.check_enable_mode.return_value = True self.device.native.check_config_mode.return_value = False self.device.enable() self.device.native.check_enable_mode.assert_called() self.device.native.enable.assert_not_called() self.device.native.check_config_mode.assert_called() self.device.native.exit_config_mode.assert_not_called() def test_enable_from_config(self): self.device.native.check_enable_mode.return_value = True self.device.native.check_config_mode.return_value = True self.device.enable() self.device.native.check_enable_mode.assert_called() self.device.native.enable.assert_not_called() self.device.native.check_config_mode.assert_called() self.device.native.exit_config_mode.assert_called()
def config(self, command): try: self.config_list([command]) except CommandListError as e: raise CommandError(e.command, e.message)
def config(self, command, format="set"): try: self.cu.load(command, format=format) self.cu.commit() except ConfigLoadError as e: raise CommandError(command, e.message)