def scp_fixture_get(request): """ Create an FileTransfer object (direction=get) Return a tuple (ssh_conn, scp_handle) """ device_under_test = request.config.getoption('test_device') test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device['verbose'] = False ssh_conn = ConnectHandler(**device) dest_file_system = 'flash:' source_file = 'test9.txt' local_file = 'testx.txt' dest_file = local_file direction = 'get' scp_transfer = FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system, direction=direction) scp_transfer.establish_scp_conn() # Make sure SCP is enabled scp_transfer.enable_scp() # Delete the test transfer files if os.path.exists(local_file): os.remove(local_file) return (ssh_conn, scp_transfer)
def scp_file_transfer(request): """ Testing file_transfer Return the netmiko connection object """ platform_args = get_platform_args() # Create the files with open("test9.txt", "w") as f: # Not important what it is in the file f.write("no logging console\n") with open("test2_src.txt", "w") as f: # Not important what it is in the file f.write("no logging console\n") f.write("logging buffered 10000\n") device_under_test = request.config.getoption("test_device") test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device["verbose"] = False ssh_conn = ConnectHandler(**device) platform = device["device_type"] file_system = platform_args[platform]["file_system"] source_file = "test9.txt" dest_file = "test9.txt" local_file = "testx.txt" alt_file = "test2.txt" direction = "put" scp_transfer = FileTransfer( ssh_conn, source_file=source_file, dest_file=dest_file, file_system=file_system, direction=direction, ) scp_transfer.establish_scp_conn() # Delete the test transfer files if scp_transfer.check_file_exists(): func = platform_args[platform]["delete_file"] func(ssh_conn, file_system, dest_file) if os.path.exists(local_file): os.remove(local_file) if os.path.exists(alt_file): os.remove(alt_file) return (ssh_conn, file_system)
def scp_file_transfer(request): """ Testing file_transfer Return the netmiko connection object """ platform_args = get_platform_args() # Create the files with open("test9.txt", "w") as f: # Not important what it is in the file f.write("no logging console\n") with open("test2_src.txt", "w") as f: # Not important what it is in the file f.write("no logging console\n") f.write("logging buffered 10000\n") device_under_test = request.config.getoption('test_device') test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device['verbose'] = False ssh_conn = ConnectHandler(**device) platform = device['device_type'] file_system = platform_args[platform]['file_system'] source_file = 'test9.txt' dest_file = 'test9.txt' local_file = 'testx.txt' alt_file = 'test2.txt' direction = 'put' scp_transfer = FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=file_system, direction=direction) scp_transfer.establish_scp_conn() # Delete the test transfer files if scp_transfer.check_file_exists(): func = platform_args[platform]['delete_file'] func(ssh_conn, file_system, dest_file) if os.path.exists(local_file): os.remove(local_file) if os.path.exists(alt_file): os.remove(alt_file) return (ssh_conn, file_system)
def scp_fixture(request): """ Create an FileTransfer object. Return a tuple (ssh_conn, scp_handle) """ device_under_test = request.config.getoption('test_device') test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device['verbose'] = False ssh_conn = ConnectHandler(**device) dest_file_system = 'flash:' source_file = 'test9.txt' dest_file = 'test9.txt' local_file = 'testx.txt' direction = 'put' scp_transfer = FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system, direction=direction) scp_transfer.establish_scp_conn() # Make sure SCP is enabled scp_transfer.enable_scp() # Delete the test transfer files if scp_transfer.check_file_exists(): delete_file_ios(ssh_conn, dest_file_system, dest_file) if os.path.exists(local_file): os.remove(local_file) return (ssh_conn, scp_transfer)
def scp_fixture_get(request): """ Create an FileTransfer object (direction=get) Return a tuple (ssh_conn, scp_handle) """ platform_args = get_platform_args() device_under_test = request.config.getoption('test_device') test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device['verbose'] = False ssh_conn = ConnectHandler(**device) platform = device['device_type'] dest_file_system = platform_args[platform]['file_system'] source_file = 'test9.txt' local_file = 'testx.txt' dest_file = local_file direction = 'get' scp_transfer = FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system, direction=direction) scp_transfer.establish_scp_conn() # Make sure SCP is enabled if platform_args[platform]['enable_scp']: scp_transfer.enable_scp() # Delete the test transfer files if os.path.exists(local_file): os.remove(local_file) return (ssh_conn, scp_transfer)
def scp_fixture(request): """ Create an FileTransfer object. Return a tuple (ssh_conn, scp_handle) """ platform_args = get_platform_args() # Create the files with open("test9.txt", "w") as f: # Not important what it is in the file f.write("no logging console\n") with open("test2_src.txt", "w") as f: # Not important what it is in the file f.write("no logging console\n") f.write("logging buffered 10000\n") device_under_test = request.config.getoption("test_device") test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device["verbose"] = False ssh_conn = ConnectHandler(**device) platform = device["device_type"] dest_file_system = platform_args[platform]["file_system"] if "ciena_saos" in platform and ssh_conn.username: dest_file_system = f"/tmp/users/{ssh_conn.username}" source_file = "test9.txt" dest_file = "test9.txt" local_file = "testx.txt" direction = "put" scp_transfer = FileTransfer( ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system, direction=direction, ) scp_transfer.establish_scp_conn() # Make sure SCP is enabled if platform_args[platform]["enable_scp"]: scp_transfer.enable_scp() # Delete the test transfer files if scp_transfer.check_file_exists(): func = platform_args[platform]["delete_file"] func(ssh_conn, dest_file_system, dest_file) if os.path.exists(local_file): os.remove(local_file) return (ssh_conn, scp_transfer)
def scp_fixture_get(request): """ Create an FileTransfer object (direction=get) Return a tuple (ssh_conn, scp_handle) """ platform_args = get_platform_args() device_under_test = request.config.getoption("test_device") test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device["verbose"] = False ssh_conn = ConnectHandler(**device) platform = device["device_type"] dest_file_system = platform_args[platform]["file_system"] source_file = "test9.txt" local_file = "testx.txt" dest_file = local_file direction = "get" scp_transfer = FileTransfer( ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system, direction=direction, ) scp_transfer.establish_scp_conn() # Make sure SCP is enabled if platform_args[platform]["enable_scp"]: scp_transfer.enable_scp() # Delete the test transfer files if os.path.exists(local_file): os.remove(local_file) return (ssh_conn, scp_transfer)
def scp_copy(customer, device, sourcefile, destfile, direction): ssh_conn = netmiko_connect(customer, device) dest_file_system = 'disk0:' scp_transfer = FileTransfer( ssh_conn = ssh_conn, source_file=sourcefile, dest_file=destfile, file_system=dest_file_system, direction=direction, ) if direction == "put": if not scp_transfer.check_file_exists(): if not scp_transfer.verify_space_available(): raise ValueError("Insufficient space available on remote device") else: if not scp_transfer.verify_space_available(): raise ValueError("Insufficient space available on remote device") # Enable scp on ASA ssh_conn.send_config_set("ssh scopy enable") # Transfer the file print("Transferring file:", sourcefile) try: scp_transfer.establish_scp_conn() scp_transfer.transfer_file() # in case of a get ASA close the scp connection and we get EOFError except (Exception, socket.error, EOFError) as err: print(err) # Disable scp on ASA ssh_conn.send_config_set("no ssh scopy enable") # File verification print("Verifying file") if scp_transfer.verify_file(): print("Source and destination MD5 matches") else: raise ValueError("MD5 failure between source and destination files") return
def transfer_file(self, file): with FileTransfer(self.device_connection, source_file=file, dest_file=file) as scp_transfer: if scp_transfer.check_file_exists(): return file + ' already exists!' else: if not scp_transfer.verify_space_available(): return 'Insufficient space available!' scp_transfer.transfer_file() # Transfer file if scp_transfer.verify_file(): return 'Transfer complete! \nsrc and dst MD5 match!' else: return 'Transfer failed! \nMD5 mismatch on src and dst!'
def main(): net_device = { 'device_type': 'cisco_ios', 'ip': '192.168.56.105', 'username': '******', 'password': '******', } print("\nLogging in to Router") ssh_conn = ConnectHandler(**net_device) print() # GET OPERATION source_file = 'config_backup.txt' dest_file = 'config_backup.txt' dest_file_system = 'flash:' with FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system, direction='get') as scp_transfer: scp_transfer.get_file() print("File transfer completed\n\n")
def main(): net_device = { 'device_type': 'cisco_asa', 'ip': ASA_IP, 'username': ASA_USER, 'password': ASA_USER, 'secret': ASA_ENABLE, 'port': 22, } ssh_conn = ConnectHandler(**net_device) dest_file_system = 'disk0:' source_file = BACKUP_FILE dest_file = 'backup.cfg' with FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system) as scp_transfer: if scp_transfer.check_file_exists(): ssh_conn.send_command( f'delete /noconfirm {dest_file_system}:/{dest_file}') if not scp_transfer.verify_space_available(): raise ValueError("Insufficient space available on remote device") asa_scp_handler(ssh_conn, mode='enable') scp_transfer.transfer_file() asa_scp_handler(ssh_conn, mode='disable') if not scp_transfer.verify_file(): raise ValueError( "MD5 failure between source and destination files")
def scp_file_transfer(request): """ Testing file_transfer Return the netmiko connection object """ platform_args = { 'cisco_ios': { 'file_system': 'flash:', 'enable_scp': True, 'delete_file': delete_file_ios, }, 'juniper_junos': { 'file_system': '/var/tmp', 'enable_scp': False, 'delete_file': delete_file_generic, }, 'arista_eos': { 'file_system': '/mnt/flash', 'enable_scp': False, 'delete_file': delete_file_generic, }, 'cisco_nxos': { 'file_system': 'bootflash:', 'enable_scp': False, 'delete_file': delete_file_nxos, }, 'cisco_xr': { 'file_system': 'disk0:', 'enable_scp': False, # Delete pattern is the same on IOS-XR 'delete_file': delete_file_ios, }, } # Create the files with open("test9.txt", "w") as f: # Not important what it is in the file f.write("no logging console\n") with open("test2_src.txt", "w") as f: # Not important what it is in the file f.write("no logging console\n") f.write("logging buffered 10000\n") device_under_test = request.config.getoption('test_device') test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device['verbose'] = False ssh_conn = ConnectHandler(**device) platform = device['device_type'] file_system = platform_args[platform]['file_system'] source_file = 'test9.txt' dest_file = 'test9.txt' local_file = 'testx.txt' alt_file = 'test2.txt' direction = 'put' scp_transfer = FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=file_system, direction=direction) scp_transfer.establish_scp_conn() # Delete the test transfer files if scp_transfer.check_file_exists(): func = platform_args[platform]['delete_file'] func(ssh_conn, file_system, dest_file) if os.path.exists(local_file): os.remove(local_file) if os.path.exists(alt_file): os.remove(alt_file) return (ssh_conn, file_system)
def scp_fixture_get(request): """ Create an FileTransfer object (direction=get) Return a tuple (ssh_conn, scp_handle) """ platform_args = { 'cisco_ios': { 'file_system': 'flash:', 'enable_scp': True, 'delete_file': delete_file_ios, }, 'juniper_junos': { 'file_system': '/var/tmp', 'enable_scp': False, 'delete_file': delete_file_generic, }, 'arista_eos': { 'file_system': '/mnt/flash', 'enable_scp': False, 'delete_file': delete_file_generic, }, 'cisco_nxos': { 'file_system': 'bootflash:', 'enable_scp': False, 'delete_file': delete_file_nxos, }, 'cisco_xr': { 'file_system': 'disk0:', 'enable_scp': False, # Delete pattern is the same on IOS-XR 'delete_file': delete_file_ios, }, } device_under_test = request.config.getoption('test_device') test_devices = parse_yaml(PWD + "/etc/test_devices.yml") device = test_devices[device_under_test] device['verbose'] = False ssh_conn = ConnectHandler(**device) platform = device['device_type'] dest_file_system = platform_args[platform]['file_system'] source_file = 'test9.txt' local_file = 'testx.txt' dest_file = local_file direction = 'get' scp_transfer = FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system, direction=direction) scp_transfer.establish_scp_conn() # Make sure SCP is enabled if platform_args[platform]['enable_scp']: scp_transfer.enable_scp() # Delete the test transfer files if os.path.exists(local_file): os.remove(local_file) return (ssh_conn, scp_transfer)
class iosfw(object): def __init__( self, hostname=None, username=None, password=None, timeout=60, driver="ios", optional_args=None, config_file="./config/config.yaml", image_file="./config/images.yaml", ): """ Initializes connection and file transfer """ # Config self.config_file = config_file self.config = self._read_yaml_file(config_file) self.image_file = image_file self.image_info = self._read_yaml_file(image_file) # Logging self.log_method = self.config["log_method"] self.log_file = self.config["log_file"] self.file_log_level = self.config["file_log_level"] self.console_log_level = self.config["console_log_level"] file_level = getattr(logging, self.file_log_level.upper(), None) console_level = getattr(logging, self.console_log_level.upper(), None) self.log = logging.getLogger(__name__) self.log.setLevel(logging.DEBUG) if self.log_method == "tee" or self.log_method == "file": fmt_str = "%(asctime)s [%(levelname)s] %(message)s" formatter = logging.Formatter(fmt_str) fh = logging.FileHandler(self.log_file) fh.setLevel(file_level) fh.setFormatter(formatter) self.log.addHandler(fh) if self.log_method == "tee" or self.log_method == "console": formatter = logging.Formatter("%(message)s") ch = logging.StreamHandler() ch.setLevel(console_level) ch.setFormatter(formatter) self.log.addHandler(ch) self.log.debug("Config file: {}".format(self.config_file)) self.log.debug("Config parameters: {}".format(self.config)) self.log.debug("Image file: {}".format(self.image_file)) self.log.debug("Image info: {}".format(self.image_info)) # Set up connection if hostname is None: hostname = str(input("Hostname or IP: ")) if username is None: whoami = getpass.getuser() username = str(input("Username [{}]: ".format(whoami))) or whoami if password is None: password = getpass.getpass() if optional_args is None: optional_args = {} secret = getpass.getpass("Enable secret: ") optional_args.update({"secret": secret}) if self.config["ssh_config_file"]: optional_args.update( {"ssh_config_file": self.config["ssh_config_file"]}) napalm_driver = napalm.get_network_driver(driver) self.napalm = napalm_driver( hostname=hostname, username=username, password=password, timeout=timeout, optional_args=optional_args, ) self.log.info("===============================================") self.log.info("Opening connection to {}...".format(hostname)) self.napalm.open() # Aliases and info self.device = self.napalm.device # Netmiko session self.facts = self.napalm.get_facts() # NAPALM facts self.os_version = self.facts["os_version"] self.model = self.facts["model"] self.hostname = self.facts["hostname"] self.fqdn = self.facts["fqdn"] self.transport = self.napalm.transport # ssh or telnet self.upgrade_image_exists = False self.upgrade_image_valid = False self.upgrade_space_available = False self.transfer_proto = self.config["transfer_proto"] self.transfer_source = self.config["transfer_source"] self.exec_timeout = self.get_exec_timeout() self.running_image_path = self.get_running_image() self.running_image_name = self._get_basename(self.running_image_path) self.running_image_feature_set = self._get_image_feature_set( self.running_image_name) self.upgrade_image_name = self.get_upgrade_image_name() self.upgrade_version = self.get_upgrade_version() self.running_version = self.get_running_version() self.upgrade_image_src_path = self._get_src_path( self.upgrade_image_name, local=True) self.upgrade_image_dest_path = self._get_dest_path( self.upgrade_image_name) self.boot_image_path = self.get_boot_image() self.firmware_installed = self.check_firmware_installed() self.needs_upgrade = self.check_needs_upgrade() self.upgrade_cmd = self._get_upgrade_cmd() self.upgrade_method = self._get_upgrade_method() self.transfer_proto = self.config["transfer_proto"] self.needs_reload = self.check_needs_reload() self.reload_scheduled = self.check_reload_scheduled() self.old_images = self.get_old_images() if self.config["delete_running_image"] != "never": self.can_delete_running_image = True else: self.can_delete_running_image = False if self.config["delete_old_images"] != "never": self.can_delete_old_images = True else: self.can_delete_old_images = False self.log.info("Connected to {} ({}) as {} via {}".format( self.hostname, self.model, self.device.username, self.transport)) self.log_upgrade_state() def log_upgrade_state(self, refresh=False): """ Logs upgrade-related facts about device """ if refresh: self.refresh_upgrade_state() self.log.info("Running version: {}".format(self.running_version)) self.log.info("Upgrade version: {}".format(self.upgrade_version)) if self.firmware_installed: if self.needs_reload: self.log.info("Upgrade status: FIRMWARE INSTALLED") if self.reload_scheduled: t = self.reload_scheduled["absolute_time"] msg = "Reload status: RELOAD SCHEDULED for {}".format(t) self.log.info(msg) else: self.log.info("Reload status: NEEDS RELOAD") else: self.log.info("Upgrade status: COMPLETE") else: self.log.info("Upgrade status: NEEDS UPGRADE") def refresh_upgrade_state(self, log=False): """ Updates device status """ self.running_image_path = self.get_running_image() self.boot_image_path = self.get_boot_image() self.firmware_installed = self.check_firmware_installed() self.needs_reload = self.check_needs_reload() self.reload_scheduled = self.check_reload_scheduled() self.old_images = self.get_old_images() if log: self.log_upgrade_state() def __del__(self): # Proxied SSH connections generate a harmless ProcessLookupError # on exit try: self.napalm.__del__() except OSError.ProcessLookupError: pass def _get_upgrade_cmd(self): """ Returns a command string for auto-upgrade, if supported """ flags = " " image_src = self._get_src_path() image_dest = self._get_dest_path() if self.transfer_source == "localhost": img = image_dest else: img = image_src cmds = ["request", "software install", "archive download-sw", "copy"] for cmd in cmds: output = self.device.send_command(cmd + " ?") if "Incomplete command" in output: method = cmd if ("allow-feature-upgrade" in output and not self.config["match_feature_set"]): flags += "/allow-feature-upgrade " break if method == "request": if "ISR" in self.model: target = "node" else: target = "switch all" return ("request platform software package install {} " "file {} new auto-copy".format(target, img)) if method == "software install": return "software install file {} new on-reboot".format(img) if method == "archive download-sw": if self.config["delete_running_image"] == "never": flags += "/safe /leave-old-sw " else: flags += "/overwrite " return "archive download-sw{}{}".format(flags, img) if method == "copy": return "copy {} {}".format(image_src, image_dest) def _get_upgrade_method(self): """ Checks whether IOS supports automatic or manual upgrade """ if re.search(r"^copy", self.upgrade_cmd): return "manual" else: return "auto" def _strip_extension(self, file_name=None): """ Returns a file name without the extension """ if file_name is None: file_name = self.upgrade_image_name split = self.upgrade_image_name.split(".") del split[-1] return ".".join(split) def _get_src_path(self, file_name=None, local=False): """ Returns full source file path """ proto = self.config["transfer_proto"] un = self.config["transfer_username"] or "" pw = self.config["transfer_password"] or "" path = self.config["transfer_path"] src = self.config["transfer_source"] if file_name is None: file_name = self.upgrade_image_name if proto == "scp" or local: path = self.config["src_image_path"] return "{}/{}".format(path, file_name) elif proto == "ftp": return "{}://{}:{}@{}{}{}".format(proto, un, pw, src, path, file_name) else: return "{}://{}{}{}".format(proto, src, path, file_name) def _get_dest_path(self, file_name=None, absolute=True): """ Returns full destination file path absolute (bool): True = path includes dest_filesystem False = path does not include dest_filesystem """ if file_name is None: file_name = self.upgrade_image_name full_path = "" if absolute: full_path += self.config["dest_filesystem"] full_path += "{}{}".format(self.config["dest_image_path"], file_name) return full_path def _read_yaml_file(self, file_name): """ Reads and parses YAML file """ with open(file_name, "r") as f: file_contents = f.read() parsed_yaml = yaml.load(file_contents, Loader=yaml.FullLoader) return parsed_yaml def _file_md5(self, file_name): """ Compute MD5 hash of local file_name """ with open(file_name, "rb") as f: file_contents = f.read() file_hash = hashlib.md5(file_contents).hexdigest() return file_hash def _check_remote_file_exists(self, file_path): """ Checks if a file exists on the remote filesystem """ cmd = "dir {}".format(file_path) output = self.device.send_command(cmd) if "No such file" in output: return False elif "Directory of" in output: return True else: self.log.critical("Unexpected output from " "_check_remote_file_exists(): \n" "{}".format(output)) return False def _get_image_feature_set(self, file_name): """ Parses the feature set string from an IOS file name e.g.: ipbasek9, universalk9, ipservicesk9 """ split = re.split(r"[-\.]", file_name) if split: if len(split) > 1: return split[1] else: return False else: return False def _check_image_feature_set(self, file_name): """ Checks if a given image's feature set matches the running image's feature set. Ignores K9 in the feature set string, so 'ipbasek9' matches 'ipbase' """ regex = r"[Kk]9" set1 = re.sub(regex, "", self.running_image_feature_set) set2 = re.sub(regex, "", self._get_image_feature_set(file_name)) return set1 == set2 def _scp_tqdm(self, t): """ Provides progress bar """ # https://github.com/tqdm/tqdm/blob/master/examples/tqdm_wget.py last_b = [0] def update_to(filename, size, sent): t.total = size t.desc = filename t.update(sent - last_b[0]) last_b[0] = sent return update_to def _write_config(self): """ Writes running configuration to NVRAM """ cmd = "write memory" output = self.device.send_command_expect(cmd) if "OK" in output or output == "#": return True else: self.log.warning("Unexpected output from `write memory`: \n" "{}".format(output)) return False def _send_write_config_set(self, config_set): """ Sends configuration set to device and writes to NVRAM """ output = self.device.send_config_set(config_set) if "Invalid input" not in output: self._write_config() return True else: self.log.critical("Device reports invalid configuration " "commands.\nCommands: {}\nOutput: " "{}\n".format(config_set, output)) return False def close(self): """ Closes all connections to device and logs """ # Proxied SSH connections generate a harmless ProcessLookupError # on exit try: self.napalm.close() except OSError.ProcessLookupError: pass handlers = self.log.handlers[:] for h in handlers: h.close() self.log.removeHandler(h) def _ensure_enable_not_config(self): """ Places device in enable mode and takes out of config mode """ if not self.device.check_enable_mode(): self.device.enable() if self.device.check_config_mode(): self.device.exit_config_mode() def get_upgrade_image_name(self): """ Returns the file name of the device's upgrade image """ for file_name, attrs in self.image_info.items(): if self.model in attrs["models"]: if self.config["match_feature_set"]: if not self._check_image_feature_set(file_name): continue upgrade_image_path = self._get_src_path(file_name) if self.config["transfer_source"] != "localhost": return file_name elif os.path.exists(upgrade_image_path): upgrade_image_md5 = self._file_md5(upgrade_image_path) if upgrade_image_md5.lower() == attrs["md5"].lower(): return file_name else: msg = ("MD5 for image {} " "does not match MD5 in " "config.\nImage MD5: {} " "\nConfig MD5: {}\n".format( file_name, attrs["md5"], upgrade_image_md5)) self.log.critical(msg) return False else: self.log.critical("Image file does not exist: " "{}".format(upgrade_image_path)) return False self.log.critical("Could not find upgrade image for model " "{} in image file " "{}.".format(self.model, self.image_file)) return False def _get_basename(self, file_path): """ Returns a file name from a file path Example input: 'flash:/c3750-ipbase-mz.122-25.SEE1.bin' Example output: 'c3750-ipbase-mz.122-25.SEE1.bin' """ return re.split(r"[:/]", file_path)[-1] def _get_path(self, file_path, include_trailing_slash=False): """ Returns a file's path, not including the file itself """ basename = self._get_basename(file_path) if not include_trailing_slash: basename = "/{}".format(basename) return file_path.replace(basename, "") def _get_version_from_file(self, file_name, raw=False): """ Returns a version string from a file name """ # c2900-universalk9-mz.SPA.157-3.M4b.bin # c2960s-universalk9-tar.152-2.E9.tar # c2960x-universalk9-tar.152-4.E8.tar # c3550-ipbase-tar.122-44.SE6.tar # c3550-ipbasek9-mz.122-44.SE6.bin # c3560-ipbasek9-mz.122-55.SE12.bin # c3560-ipbasek9-tar.122-55.SE12.tar # c3560e-ipbasek9-mz.150-2.SE11.bin # c3560e-ipbasek9-tar.150-2.SE11.tar # c3560e-universalk9-mz.152-4.E8.bin # c3560e-universalk9-tar.152-4.E8.tar # c3750-ipbaselmk9-tar.122-55.SE12.tar # c3750e-ipbasek9-mz.150-2.SE11.bin # c3750e-universalk9-tar.152-4.E8.tar # cat3k_caa-universalk9.16.03.08.SPA.bin # cat9k_iosxe.16.11.01.SPA.bin pattern = r"(\d+\.\d+\.\d+)\.SPA" match = re.search(pattern, file_name) if match: if raw: return match.group(1) else: return re.sub(r"\.0", ".", match.group(1)) pattern = r"(\d{3})-(\d+)\.(\w+)" match = re.search(pattern, file_name) if match: if raw: return match.group(0) else: train = "{}.{}".format(match.group(1)[:2], match.group(1)[2:]) throttle = match.group(2) rebuild = match.group(3) return "{}({}){}".format(train, throttle, rebuild) def get_upgrade_version(self, raw=False): """ Parses image name to return IOS version string """ return self._get_version_from_file(self.upgrade_image_name, raw) def get_running_version(self): """ Parses self.os_version for IOS version string """ pattern = r"ersion ([^,]+)," match = re.search(pattern, self.os_version) if match: return match.group(1) else: return False def get_running_image(self): """ Returns the remote path of the image running in memory """ search_string = "System image file is " cmd = "show ver | i {}".format(search_string) output = self.device.send_command(cmd) if search_string in output: return output.replace(search_string, "").replace('"', "") else: self.log.critical("Could not find running image. " "Last output:\n{}".format(output)) return False def get_boot_image(self): """ Returns the remote path of the image used on next reload """ cmd = "show boot | include BOOT" output = self.device.send_command(cmd) if "Invalid input" in output: self.log.debug("Device does not support `show boot`. " "Checking running config...") cmd = "show run | i boot" output = self.device.send_command(cmd) self.log.debug("Output from `{}`: \n{}".format(cmd, output)) if "boot system" in output: match = re.search(r"boot system ([^\n]+)\n", output) self.log.debug("Found boot image {}".format(match.group(1))) return match.group(1) else: self.log.debug("No `boot system` directives found in config. " "Inferring first image in dest_fs.") return self.get_installed_images()[0] else: return output.split(": ")[-1].strip() def get_installed_images(self): """ Returns list of images installed on dest_fs """ results = [] dest_fs = self.config["dest_filesystem"] cmd = "dir {}".format(dest_fs) output = self.device.send_command_timing(cmd) for line in output.split("\n"): file_name = re.split(r"\s+", line)[-1] # c3560e-universalk9-mz.152-4.E8 # c3560-ipbasek9-mz.122-55.SE12 # c2900-universalk9-mz.SPA.152-4.M4.bin # pattern = r'\w+-\w+-\w+.\d+-\w+\.\w+' pattern = r"\d+-\w+\.\w+" match = re.search(pattern, file_name) if match: results.append("{}/{}".format(dest_fs, file_name)) return results def get_old_images(self): """ Checks dest_filesystem for old image files """ results = [] installed_images = self.get_installed_images() for image in installed_images: img_version = self._get_version_from_file(image) if (img_version != self.running_version and img_version != self.upgrade_version): results.append(image) return results def get_exec_timeout(self): """ Returns config line for line vty exec-timeout, if exists """ cmd = "sh run | i exec-timeout" self.log.debug("Executing command `{}`...".format(cmd)) output = self.device.send_command(cmd) self.log.debug("Command output:\n{}".format(output)) if output: output = output.split("\n") return output[-1] def disable_exec_timeout(self): """ Disables line vty exec-timeout """ config_set = ["line vty 0 15", "no exec-timeout"] self.log.info("Disabling line vty exec-timeout...") return self._send_write_config_set(config_set) def ensure_exec_timeout_disabled(self): """ Disables line vty exec timeout, if not already disabled """ if self.exec_timeout: return self.disable_exec_timeout() else: return True def restore_exec_timeout(self): """ Restores line vty exec-timeout """ if self.exec_timeout != " exec-timeout 0 0": config_set = ["line vty 0 15", self.exec_timeout] self.log.info("Restoring line vty exec-timeout...") return self._send_write_config_set(config_set) else: return True def ensure_exec_timeout_restored(self): """ Restores line vty exec-timeout, if needed """ if self.exec_timeout != " exec-timeout 0 0": return self.restore_exec_timeout() else: return True def set_boot_image(self, new_boot_image_path=None): """ Configures device to boot given image on next reload """ if new_boot_image_path is None: new_boot_image_path = self.upgrade_image_dest_path config_set = [ "no boot system", "boot system {}".format(new_boot_image_path) ] return self._send_write_config_set(config_set) def ensure_boot_image(self, new_boot_image_path=None): """ Ensures the given image is set to boot, if not already. new_boot_image_path (str): full destination path to boot image, including filesystem Does nothing if already set. """ if new_boot_image_path is None: new_boot_image_path = self.upgrade_image_dest_path self.log.info("Checking boot image...") current_boot_image_path = self.get_boot_image() if current_boot_image_path != new_boot_image_path: self.log.info( "Setting boot image to {}...".format(new_boot_image_path)) if self.set_boot_image(new_boot_image_path): confirm = self.get_boot_image() if confirm == new_boot_image_path: self.log.info( "Success! New boot image set to {}.".format(confirm)) return True else: self.log.info( "Boot image already set to {}.".format(new_boot_image_path)) return True def check_scp(self): """ Checks if SCP is enabled """ # I could find no more "correct" way of verifying SCP is running cmd = "show run | include scp" output = self.napalm.device.send_command_expect(cmd) if "ip scp server enable" in output: return True else: return False def fix_scp(self): """ Attempts to enable/fix SCP """ config_set = self.config["fix_scp"] if config_set: output = self.device.send_config_set(config_set) if "Invalid input" not in output: output += self.device.send_command_expect("write memory") return True else: self.log.critical("Problem fixing SCP config. Last output: " "\n{}".format(output)) return False else: self.log.critical( "No 'fix_scp' values found in {}. Cannot proceed.".format( self.config_file)) return False def fix_ntp(self): """ Fixes NTP if possible """ fix_ntp_cmds = self.config["fix_ntp"] cmd = "show run | i ntp" ntp_config = self.device.send_command(cmd).split("\n") null_ntp_cmds = [] for line in ntp_config: null_ntp_cmds.append("no {}".format(line)) self.log.debug( "Removing existing NTP config: \n{}".format(null_ntp_cmds)) output = self.device.send_config_set(null_ntp_cmds) self.log.debug("Output: \n{}".format(output)) self.log.debug("Sending new NTP config: \n{}".format(fix_ntp_cmds)) output += self.device.send_config_set(fix_ntp_cmds) self.log.debug("Output: \n{}".format(output)) def ensure_scp(self): """ Enables SCP if it is not already running properly. """ self.log.info("Checking SCP...") check = self.check_scp() if check: self.log.info("SCP already enabled and running.") else: self.log.info("SCP not running. Enabling now...") fixed = self.fix_scp() if fixed: self.log.info("SCP enabled successfully!") def check_upgrade_image_running(self): """ Check if running image is the current version """ if self.upgrade_version == self.running_version: return True else: return False def check_needs_upgrade(self): """ Inverse of check_upgrade_image_running() """ if self.check_upgrade_image_running(): return False else: return True def check_firmware_installed(self): """ Checks if upgrade package is already installed """ version = self.get_upgrade_version(raw=True) dest_fs = self.config["dest_filesystem"] files = self.device.send_command("dir {}".format(dest_fs)) if "packages.conf" in self.boot_image_path: conf = self.device.send_command("more flash:packages.conf") if version in conf: return True else: return False if version in files: return True else: return False def check_needs_reload(self): """ Check if running image does not equal boot image """ if self.upgrade_method == "auto": if self.check_needs_upgrade() and self.check_firmware_installed(): return True else: return False else: if self.running_image_path != self.boot_image_path: return True else: return False def check_reload_scheduled(self): """ Check if a reload is scheduled """ self._ensure_enable_not_config() output = self.device.send_command("show reload") pattern = r"Reload scheduled for (.+?) \(in (.+?)\)" match = re.search(pattern, output) if match: return { "absolute_time": match.group(1), "relative_time": match.group(2) } else: return False def cancel_reload(self): """ Cancels pending reload, if any """ cmd = "reload cancel" output = self.device.send_command_timing(cmd) if "ABORTED" in output: # strange, we need an [enter] to get our prompt back # after `reload cancel` output += self.device.send_command_timing("\n") return True elif "No reload is scheduled" in output: return True else: self.log.critical("Unexpected output from `{}`:" "\n{}".format(cmd, output)) return False def schedule_reload(self, reload_at=None, reload_in=None, save_modified_config=True): """ Schedules reload Overwrites pending reload, if already scheduled Defaults to midnight tonight (technically, 00:00 tomorrow) reload_at (str) Absolute time to reload device at in 'hh:mm' format reload_in (str) Relative time (delay from now) before reloading device in 'mmm' or 'hhh:mm' format save_modified_config (bool) Whether or not to save outstanding config changes, if any """ # Should be hh:mm, but h:mm is valid; IOS prepends 0 # e.g.: `reload at 7:35` schedules reload at 07:35 (7:35am) reload_at_pattern = r"^\d{1,2}:\d{2}$" reload_in_pattern = r"^\d{1,3}$|^\d{1,3}:\d{2}$" if reload_at is None: reload_at = self.config["reload_at"] if reload_in is None: reload_in = self.config["reload_in"] # Validate inputs if reload_at is str and reload_in is str: raise ValueError("Use either reload_in or reload_at, not both") if reload_at is not str and reload_in is not str: reload_at = "00:00" if reload_at: reload_at = str(reload_at).strip() if re.match(reload_at_pattern, reload_at): cmd = "reload at {}".format(reload_at) else: self.log.critical("reload_at must be 'hh:mm' or " "'h:mm' ('{}' given)".format(reload_at)) return False if reload_in: reload_in = str(reload_in).strip() if re.match(reload_in_pattern, reload_in): cmd = "reload in {}".format(reload_in) else: self.log.critical("reload_in must be 'mmm' or 'hhh:mm' " "('{}' given)".format(reload_in)) return False # Schedule the reload self._ensure_enable_not_config() e = r"(Save|Proceed|\#)" output = self.device.send_command(cmd, expect_string=e) if "The date and time must be set first" in output: self.log.debug("Clock not set.") if self.config["fix_ntp"]: self.log.debug("Attempting NTP fix...") self.fix_ntp() self.log.debug("Waiting 30 seconds for NTP to sync...") sleep(30) return self.schedule_reload() else: self.log.critical("No fix_ntp parameters given. " "Cannot continue with reload.") return False if "Save?" in output: if save_modified_config: response = "yes" else: response = "no" output += self.device.send_command(response, expect_string=r"Proceed", delay_factor=2) if "Proceed" in output: output += self.device.send_command_timing("\n") else: self.log.critical("Unexpected output from `{}`:" "\n{}".format(cmd, output)) return False check_reload_scheduled = self.check_reload_scheduled() if check_reload_scheduled: return check_reload_scheduled else: msg = ("Tried to schedule reload with `{}`, " "but check_reload_scheduled() failed. " "Output:\n{}".format(cmd, output)) self.log.critical(msg) return False def ensure_reload_scheduled(self): """ Schedules a reload, if not already scheduled. """ scheduled = self.check_reload_scheduled() if not scheduled: self.log.info("Scheduling reload...") return self.schedule_reload() else: return scheduled self.log.info("Reload scheduled for {} ({} away)".format( scheduled["absolute_time"], scheduled["relative_time"])) def _delete_file(self, file_name): """ Deletes a remote file from device """ cmd = "del /recursive /force {}".format(file_name) output = self.device.send_command_timing(cmd) # Successful command returns no output if output: self.log.critical( "Unexpected output from `del`: \n{}".format(output)) return False else: return True def ensure_file_removed(self, file_name, delete_path=True): """ Deletes a remote file from device only if it exists. Optionally deletes the full path (any parent folders). """ if delete_path: path = self._get_path(file_name) if path != self.config["dest_filesystem"]: self.log.info("Removing {}...".format(path)) return self._delete_file(path) else: self.log.info("Removing {}...".format(file_name)) return self._delete_file(file_name) else: return self._delete_file(file_name) def delete_running_image(self): """ Deletes currently running image, including folder """ self.ensure_file_removed(self.running_image_path) self.log.info("Running image deleted.") def remove_old_images(self): """ Deletes images on dest_filesystem that are not running """ cmd = "request platform software package clean switch all" output = self.device.send_command(cmd, expect_string=r"(proceed|\#)") self.log.debug(output) if "Nothing to " in output: self.log.info("Found no old images to remove.") return True elif "proceed" in output: self.log.debug("Proceeding with package clean...") output += self.device.send_command("y", expect_string=r"\#") if "Files deleted" in output: self.log.info("Removed old images.") return True else: self.log.warning("Unexpected output from remove_old_images():" "\n{}".format(output)) return False elif "Invalid input" in output: if self.old_images: for image in self.old_images: if self._check_remote_file_exists(image): if self.ensure_file_removed(image): self.log.info("Removed successfully.") else: self.log.critical( "Unexpected output from remove_old_images():\n{}".format( output)) return False def _init_transfer(self, src_file=None, dest_file=None): """ Sets up file transfer session. Even if we don't use scp to copy the image, the class is still useful for checking image existence, free space, etc. """ if src_file is None: src_file = self.upgrade_image_src_path if dest_file is None: dest_file = self._get_dest_path(absolute=False) if self.transport == "ssh": ft_args = { "ssh_conn": self.device, "source_file": src_file, "dest_file": self._get_dest_path(dest_file, absolute=False), "file_system": self.config["dest_filesystem"], } self.ft = FileTransfer(**ft_args) elif self.transport == "telnet": self.ft = None raise NotImplementedError else: raise ValueError("Transport must be ssh or telnet.") def request_scp_transfer(self): """ Begins SCP file transfer with progress """ self.ensure_scp() self._init_transfer() source = self.upgrade_image_src_path dest = self.upgrade_image_dest_path ssh_connect_params = self.ft.ssh_ctl_chan._connect_params_dict() self.ft.scp_conn = self.ft.ssh_ctl_chan._build_ssh_client() self.ft.scp_conn.connect(**ssh_connect_params) with tqdm(unit="b", unit_scale=True, ascii=True) as t: self.progress = self._scp_tqdm(t) self.ft.scp_client = scp.SCPClient( self.ft.scp_conn.get_transport(), progress=self.progress) self.ft.scp_client.put(source, dest) def request_transfer(self): """ Starts file transfer and upgrade process """ if self.transfer_proto == "scp": self.request_scp_transfer() else: cmd = self.upgrade_cmd self.log.debug("Transferring image with: {}".format(cmd)) output = self.device.send_command(cmd, expect_string=r"filename") output += self.device.send_command("\n", delay_factor=100, expect_string=r"copied") self.device.find_prompt() def request_install(self): """ Requests automated upgrade """ cmd = self.upgrade_cmd self.log.info("Installing new firmware...") self.log.debug("Upgrade command: {}".format(cmd)) if self.upgrade_method == "manual": if self.copy_validate_image(): self.ensure_boot_image() return True else: return False else: self.log.info("NOTE: No status updates possible during install. " "Expect this to take about 10 minutes, and in some " "cases, significantly longer.") output = self.device.send_command(cmd, delay_factor=100) self.log.debug(output) if "Error" in output: self.log.critical("Install failed:") for line in output.split("\n"): if "Error" in line: self.log.critical(line) return False elif "All software images installed" in output or "SUCCESS" in output: self.log.info("Install successful!") return True else: self.log.critical("Unexpected output from " "request_install():\n" "{}".format(output)) return False def ensure_install(self): """ Checks if firmware install is necessary, requesting if so """ src_file = self._get_src_path(local=True) self._init_transfer(src_file) if not self.firmware_installed: if self.ensure_free_space(): if self.transfer_source == "localhost": if self.request_transfer(): self.request_install() return self.request_install() else: self.log.info("New firmware already installed!") return True def ensure_free_space(self): """ Checks for available free space, clearing if possible """ self.log.info("Checking free space...") self.upgrade_file_size = os.stat(self.upgrade_image_src_path).st_size # Estimate 10% decompression overhead comp_overhead = self.upgrade_file_size * 1.1 if self.ft.remote_space_available() >= comp_overhead: self.log.info("Found enough free space!") return True else: self.log.info("Not enough space.") if self.old_images and self.can_delete_old_images: self.log.info("Removing old images...") self.remove_old_images() # Need to wait after deleting image before checking # free space, or we get an exception sleep(30) if self.ft.verify_space_available(): return True else: if self.can_delete_running_image: self.log.info("Removing running image...") self.delete_running_image() sleep(30) if self.ft.verify_space_available(): return True else: self.log.critical("Still not enough space." "Cannot continue.") return False else: self.log.critical("Still not enough space." "Cannot continue.") return False def copy_validate_image(self): """ Copies and validates image file """ if self.ft.verify_space_available(): msg = "Starting transfer. Expect this to take several minutes..." self.log.info(msg) self.request_transfer() self.log.info("Transfer complete! Verifying hash...") if self.ft.verify_file(): self.log.info("Hash verified!") return True else: self.log.critical( "Failed hash check after transfer. Can't continue.") return False else: self.log.critical( "Not enough space for upgrade image. Can't continue.") return False def ensure_image_state(self): """ If possible, transfers and verifies image on device """ self._init_transfer() self.log.info("Checking device for upgrade image {}...".format( self.upgrade_image_dest_path)) self.upgrade_image_exists = self.ft.check_file_exists() if self.upgrade_image_exists: self.log.info("Found! Verifying hash...") self.upgrade_image_valid = self.ft.verify_file() if self.upgrade_image_valid: self.log.info("Hash verified!") return True else: self.log.warning("Failed hash check. Re-copying image.") return self.copy_validate_image() else: self.log.info("Not found.") self.ensure_free_space() return self.copy_validate_image() def ensure_old_image_removal(self): """ Deletes old images if requested """ if self.config["delete_old_images"] == "always": return self.remove_old_images() def ensure_running_image_removal(self): """ Removes running image if requested """ if self.config["delete_running_image"] == "always": if self._check_remote_file_exists(self.running_image_path): self.log.info("Removing running image...") return self.delete_running_image() def upgrade(self): """ Performs firmware upgrade """ start_t = datetime.now() start = start_t.strftime("%X %Y-%m-%d") if self.needs_upgrade and not self.firmware_installed: self.log.info("Starting upgrade on {} " "at {}...".format(self.hostname, start)) if self.config["disable_exec_timeout"]: self.ensure_exec_timeout_disabled() if self.ensure_install(): self.refresh_upgrade_state() self.ensure_old_image_removal() self.ensure_running_image_removal() self.ensure_reload_scheduled() status = "completed" else: status = "failed" if self.config["disable_exec_timeout"]: self.ensure_exec_timeout_restored() end_t = datetime.now() end = end_t.strftime("%X %Y-%m-%d") self.log.info("Upgrade on {} {} at {}".format( self.hostname, status, end)) elif self.needs_reload and not self.reload_scheduled: self.ensure_reload_scheduled() else: self.log.info("No action needed.") end_t = datetime.now() self.log.info("Total time elapsed: {}".format(end_t - start_t))
import sys # ADJUST TO REQUIRED VALUES target_version = '16.9.5' image_filename = "imagetest.txt" verification_commands = ['show run', 'show ip int br', 'show version'] def verify_version(ssh): show_version = ssh.send_command('show version') for line in show_version.splitlines(): if line.startswith('Cisco IOS Software'): current_version = line.split(',')[2].split()[1] return current_version def image_transfer(ssh, image_filename) with FileTransfer(ssh,source_file=image_filename,dest_file=image_filename) as scp_transfer: if scp_transfer.check_file_exists(): print(f"File {image_filename} already exists on device.") else: if not scp_transfer.verify_space_available(): raise ValueError("Insufficient free space available on device") else: print("Enabling SCP") scp_transfer.enable_scp() print("Transferring file...") scp_transfer.transfer_file() print("Disabling SCP") scp_transfer.disable_scp() print("Verifying file") if scp_transfer.verify_file():
def _file_copy_instance(self, src, dest=None, file_system="flash:"): if dest is None: dest = os.path.basename(src) fc = FileTransfer(self.native_ssh, src, dest, file_system=file_system) return fc
def main(): """Script to upgrade a Cisco ASA.""" ip_addr = raw_input("Enter ASA IP address: ") my_pass = getpass() start_time = datetime.now() print ">>>> {}".format(start_time) #CREATE DEVICE_DICT net_device = { 'device_type': 'cisco_asa', 'ip': ip_addr, 'username': '******', 'password': my_pass, 'secret': my_pass, 'port': 22, } #CONNECT TO DEVIC_DICT print "\nLogging in to ASA" ssh_conn = ConnectHandler(**net_device) print #INITIAL ADJUSTMENT TO TRANSFER IMAGE FILE dest_file_system = 'disk0:' source_file = 'test1.txt' dest_file = 'test1.txt' alt_dest_file = 'asa825-59-k8.bin' scp_changed = False with FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system) as scp_transfer: #CHECK FOR IF ALREADY EXISTS AND SUFFICIENT SPACE if not scp_transfer.check_file_exists(): if not scp_transfer.verify_space_available(): raise ValueError( "Insufficient space available on remote device") print "Enabling SCP" output = asa_scp_handler(ssh_conn, mode='enable') print output print "\nTransferring file\n" scp_transfer.transfer_file() print "Disabling SCP" output = asa_scp_handler(ssh_conn, mode='disable') print output print "\nVerifying file" if scp_transfer.verify_file(): print "Source and destination MD5 matches" else: raise ValueError( "MD5 failure between source and destination files") print "\nSending boot commands" full_file_name = "{}/{}".format(dest_file_system, alt_dest_file) boot_cmd = 'boot system {}'.format(full_file_name) output = ssh_conn.send_config_set([boot_cmd]) print output print "\nVerifying state" output = ssh_conn.send_command('show boot') print output #UNCOMMENT TO PERFORM WR MEM AND RELOAD print "\nWrite mem and reload" output = ssh_conn.send_command_expect('write mem') output += ssh_conn.send_command('reload') output += ssh_conn.send_command('y') print output #TIME TO EXECUTE print "\n>>>> {}".format(datetime.now() - start_time) print
def upgrade_asa(device, image_type, image_location, dest_drive="disk0:", reboot=False, backup_config=False, backup_location=None): """ Method to upgrade a Cisco ASA OS version or ASDM Version :param device: Device to upgrade :type device: Device :param image_type: Image file to be uploaded - ASDM or ASA :type image_type: str :param image_location: location of the source image file on local computer :type image_location: str :param dest_drive: destination drive that the image file will be uploaded. Default: "Disk0:" :type dest_drive: str :param reboot: indicates if a reboot is required :type reboot: bool :param backup_config: indicates if a config backup is required :type backup_config: bool :param backup_location: location to save backups :type backup_location: str :return: Device with updated attributes """ try: connection = DeviceHelper.connect_to_device( ipaddr=device.ip_address, credentials=device.credentials, enable_mode=True, device_type=["cisco_asa"]) device.connected = True prompt = connection.find_prompt() device.name = prompt[0:(len(prompt) - 1)] # Backup config if backup_config: _logger.info(f"{device.ip_address} - Starting Config Backup") config = connection.send_command("sh run") DeviceHelper.backup_config(config, backup_location, device.name) _logger.info(f"{device.ip_address} - Completed Config") image_file_name = Path(image_location).name # Check if scp is enabled output = connection.send_command("sh run | i ssh scopy enable") if output.lower() is "ssh copy enable": _logger.debug(f"{device.ip_address} - SCP enabled") scp_enabled = True else: _logger.debug(f"{device.ip_address} - SCP not enabled. Enabling") scp_enabled = False # enable SCP connection.send_config_set(["ssh scopy enable"]) with FileTransfer(connection, source_file=image_location, dest_file=image_file_name, file_system=dest_drive) as scp: _logger.debug( f"{device.ip_address} - Starting to copy image to device") # check if there is free space if scp.verify_space_available(): # Transfer file scp.transfer_file() # verify file if not scp.verify_file(): device.error = "File transfer verifier failed" _logger.info(f"{device.ip_address} - file verifier failed") else: device.file_uploaded = True _logger.info( f"{device.ip_address} - Not enough space to image") else: device.error = "Not enough space to upload file" # disable scp if it was not enabled originally if not scp_enabled: connection.send_config_set(["no ssh scopy enable"]) _logger.debug(f"{device.ip_address} - Disabling SCP") # if file was uploaded set it as the new image if device.file_uploaded: asa_file_path = f"{dest_drive}/{image_file_name}" if image_type.lower() == "asa": config = f"boot system {asa_file_path}" _logger.debug( f"{device.ip_address} - setting ASA boot image to : {asa_file_path}" ) connection.send_config_set([config]) elif image_type.lower() == "asdm": config = f"asdm image {asa_file_path}" _logger.debug( f"{device.ip_address} - setting ASDM image to : {asa_file_path}" ) connection.send_config_set([config]) # save config _logger.debug(f"{device.ip_address} - Saving Config") connection.save_config() # reboot if set if reboot: _logger.info(f"{device.ip_address} - Rebooting device") connection.send_confg_set(["reboot"]) connection.send_confg_set(["y"]) # wait 5 mins to and check time.sleep(300) device.successfully_rebooted = DeviceHelper.ping( device.ip_address) if device.successfully_rebooted: _logger.info(f"{device.ip_address} - Device Rebooted") else: _logger.warning( f"{device.ip_address} - Device not rebooted") return device except ConnectionException as e: device.connected = False _logger.error(f"{device.ip_address} - Connection Exception") _logger.error(f"{e}") return device except ValueError as e: device.connected = False _logger.error(f"{device.ip_address} - Value Error") _logger.error(f"{e}") return device
def main(): ''' Ansible module to transfer files to Cisco IOS devices. ''' module = AnsibleModule( argument_spec=dict( host=dict(required=True), port=dict(default=22, required=False), username=dict(required=True), password=dict(required=True), source_file=dict(required=True), dest_file=dict(required=True), dest_file_system=dict(required=False, default='flash:'), enable_scp=dict(required=False, default=False, choices=BOOLEANS), overwrite=dict(required=False, default=True, choices=BOOLEANS), ), supports_check_mode=True ) net_device = { 'device_type': 'cisco_ios', 'ip': module.params['host'], 'username': module.params['username'], 'password': module.params['password'], 'port': int(module.params['port']), 'verbose': False, } ssh_conn = ConnectHandler(**net_device) source_file = module.params['source_file'] dest_file = module.params['dest_file'] dest_file_system = module.params['dest_file_system'] enable_scp = module.boolean(module.params['enable_scp']) overwrite = module.boolean(module.params['overwrite']) check_mode = module.check_mode scp_changed = False with FileTransfer(ssh_conn, source_file, dest_file, file_system=dest_file_system) as scp_transfer: # Check if file already exists and has correct MD5 if scp_transfer.check_file_exists() and scp_transfer.compare_md5(): module.exit_json(msg="File exists and has correct MD5", changed=False) if not overwrite and scp_transfer.check_file_exists(): module.fail_json(msg="File already exists and overwrite set to false") if check_mode: if not scp_transfer.verify_space_available(): module.fail_json(msg="Insufficient space available on remote device") module.exit_json(msg="Check mode: file would be changed on the remote device", changed=True) # Verify space available on remote file system if not scp_transfer.verify_space_available(): module.fail_json(msg="Insufficient space available on remote device") # Transfer file if enable_scp: scp_transfer.enable_scp() scp_changed = True scp_transfer.transfer_file() if scp_transfer.verify_file(): if scp_changed: scp_transfer.disable_scp() module.exit_json(msg="File successfully transferred to remote device", changed=True) module.fail_json(msg="File transfer to remote device failed")
def main(): """Script to upgrade a Cisco ASA.""" try: ip_addr = raw_input("Enter ASA IP address: ") except NameError: ip_addr = input("Enter ASA IP address: ") my_pass = getpass() start_time = datetime.now() print(">>>> {}".format(start_time)) net_device = { 'device_type': 'cisco_asa', 'ip': ip_addr, 'username': '******', 'password': my_pass, 'secret': my_pass, 'port': 22, } print("\nLogging in to ASA") ssh_conn = ConnectHandler(**net_device) print() # ADJUST TO TRANSFER IMAGE FILE dest_file_system = 'disk0:' source_file = 'test1.txt' dest_file = 'test1.txt' alt_dest_file = 'asa825-59-k8.bin' with FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system) as scp_transfer: if not scp_transfer.check_file_exists(): if not scp_transfer.verify_space_available(): raise ValueError( "Insufficient space available on remote device") print("Enabling SCP") output = asa_scp_handler(ssh_conn, mode='enable') print(output) print("\nTransferring file\n") scp_transfer.transfer_file() print("Disabling SCP") output = asa_scp_handler(ssh_conn, mode='disable') print(output) print("\nVerifying file") if scp_transfer.verify_file(): print("Source and destination MD5 matches") else: raise ValueError( "MD5 failure between source and destination files") print("\nSending boot commands") full_file_name = "{}/{}".format(dest_file_system, alt_dest_file) boot_cmd = 'boot system {}'.format(full_file_name) output = ssh_conn.send_config_set([boot_cmd]) print(output) print("\nVerifying state") output = ssh_conn.send_command('show boot') print(output) # UNCOMMENT TO PERFORM WR MEM AND RELOAD # print("\nWrite mem and reload") # output = ssh_conn.send_command_expect('write mem') # output += ssh_conn.send_command('reload') # output += ssh_conn.send_command('y') # print(output) print("\n>>>> {}".format(datetime.now() - start_time)) print()
def main(): arch = raw_input( "Is this a standalone or pair of ASAs [standalone|pair]? ") #gather variables and run upgrade for standalone architecture if arch == "standalone": ip_addr = raw_input( "Please enter the IP address or hostname of the ASA: ") user = "******" my_pass = getpass() start_time = datetime.now() image_location = 'disk0:' image_location = raw_input( "Enter the image location [disk0:]: ") or "disk0:" source_image = raw_input("Enter the image file name: ") dest_image = source_image dest_image = raw_input("Enter the destination image [" + dest_image + "]: ") or dest_image print ">>>> {}".format(start_time) standalone_asa = { 'device_type': 'cisco_asa', 'ip': ip_addr, 'username': user, 'password': my_pass, 'secret': my_pass, 'port': 22, } #connect and login to ASA print "\nConnecting and logging in to %s " % ip_addr ssh_conn = ConnectHandler(**standalone_asa) #verify there is enough space to transfer file, if not prompt for deletion with FileTransfer(ssh_conn, source_file=source_image, dest_file=dest_image, file_system=image_location) as scp_transfer: if scp_transfer.check_file_exists() == False: while scp_transfer.verify_space_available() == False: make_space = raw_input( "Insufficient space available on disk0:, " "would you like to make more? [yes|no] [yes]: " ) or 'yes' if make_space == 'yes': dir_disk = ssh_conn.send_command('dir disk0:') print "\nOutput from dir disk0:" print dir_disk file_to_delete = raw_input( "Enter file to be deleted: ") ssh_conn.send_command('delete disk0:/%s' % file_to_delete) if make_space == 'no': sys.exit( "\nExiting script, please go make space on the ASA " "before proceeding") scp_transfer.verify_space_available() #Verify SCP is enabled, if not enable it print "\nEnabling SCP..." output = asa_scp_handler(ssh_conn, mode='enable') print output #transfer the file to the ASA print "\nTransferring file to ASA..." scp_transfer.transfer_file() #Disable SCP print "\nDisabling SCP..." output = asa_scp_handler(ssh_conn, mode='disable') print output #Verify the file print "\Verifying the file..." if scp_transfer.verify_file(): print "\nSource and Destination File MD5 match!" else: raise ValueError( "\nMD5 failure between source and destination file!") full_file_name = "{}/{}".format(image_location, dest_image) boot_cmd = 'boot system {}'.format(full_file_name) output = ssh_conn.send_config_set([boot_cmd]) print output #Verify "show boot" print "\nVerifying boot variables..." output = ssh_conn.send_command('show boot') print output #write mem and reload the ASA print "\nWrite mem and reload" output = ssh_conn.send_command_expect('write mem') output += ssh_conn.send_command('reload') output += ssh_conn.send_command('y') print output elif scp_transfer.check_file_exists() == True: #Set boot commands full_file_name = "{}/{}".format(image_location, dest_image) boot_cmd = 'boot system {}'.format(full_file_name) output = ssh_conn.send_config_set([boot_cmd]) print output #Verify "show boot" print "\nVerifying boot variables..." output = ssh_conn.send_command('show boot') print output #write mem and reload the ASA print "\nWrite mem and reload" output = ssh_conn.send_command_expect('write mem') output += ssh_conn.send_command('reload') output += ssh_conn.send_command('y') print output #wait for 5 minutes and verify the system is running on the new code time.sleep(300) ssh_conn = ConnectHandler(**standalone_asa) show_ver = ssh_conn.send_command('show_version') show_ver_lines = show_ver.split("\n") for line in show_ver_lines: if "System image" in line: print "\n" + line print "\n>>>> {}".format(datetime.now() - start_time) print if arch == "pair": ip_addr_pri = raw_input( "Please enter the IP address or hostname of the primary ASA: ") ip_addr_sec = raw_input( "Please enter the IP address or hostname of the secondary ASA: ") user = getuser() my_pass = getpass() start_time = datetime.now() image_location = 'disk0:' image_location = raw_input( "Enter the image location [disk0:]: ") or "disk0:" source_image = raw_input("Enter the image file name: ") dest_image = source_image dest_image = raw_input("Enter the destination image [" + dest_image + "]: ") or dest_image print ">>>> {}".format(start_time) #primary_asa = { # 'device_type': 'cisco_asa', # 'ip': ip_addr_pri, # 'username': user, # 'password': my_pass, # 'secret': my_pass, # 'port': 22, #} secondary_asa = { 'device_type': 'cisco_asa', 'ip': ip_addr_sec, 'username': user, 'password': my_pass, 'secret': my_pass, 'port': 22, } asa_pair = [secondary_asa, secondary_asa] for asa in asa_pair: #connect and login to secondary ASA print "\nConnecting and logging in to %s " % asa ssh_conn = ConnectHandler(**asa) #verify that this is the secondary ASA print "\nVerifying this is the secondary ASA..." failover = ssh_conn.send_command("show failover") failover_lines = failover.split("\n") for line in failover_lines: if ("This host:" in line) and ("Standby Ready" in line): print "\nSecondary host verified" elif ("This host:" in line) and ("Active" in line): print "\n This is the primary ASA, failing over..." ssh_conn.send_command('failover active') #verify there is enough space to transfer file, if not prompt for deletion with FileTransfer(ssh_conn, source_file=source_image, dest_file=dest_image, file_system=image_location) as scp_transfer: if scp_transfer.check_file_exists() == False: while scp_transfer.verify_space_available() == False: make_space = raw_input( "Insufficient space available on disk0:, " "would you like to make more? [yes|no] [yes]: " ) or 'yes' if make_space == 'yes': dir_disk = ssh_conn.send_command('dir disk0:') print "\nOutput from dir disk0:" print dir_disk file_to_delete = raw_input( "Enter file to be deleted: ") ssh_conn.send_command('delete disk0:/%s' % file_to_delete) if make_space == 'no': sys.exit( "\nExiting script, please go make space on the ASA " "before proceeding") scp_transfer.verify_space_available() #Verify SCP is enabled, if not enable it print "\nEnabling SCP..." output = asa_scp_handler(ssh_conn, mode='enable') print output #transfer the file to the ASA print "\nTransferring file to ASA..." scp_transfer.transfer_file() #Disable SCP print "\nDisabling SCP..." output = asa_scp_handler(ssh_conn, mode='disable') print output #Verify the file print "\Verifying the file..." if scp_transfer.verify_file(): print "\nSource and Destination File MD5 match!" else: raise ValueError( "\nMD5 failure between source and destination file!" ) elif scp_transfer.check_file_exists() == True: #Set boot commands full_file_name = "{}/{}".format(image_location, dest_image) boot_cmd = 'boot system {}'.format(full_file_name) output = ssh_conn.send_config_set([boot_cmd]) print output #Verify "show boot" print "\nVerifying boot variables..." output = ssh_conn.send_command('show boot') print output #write mem and reload the ASA print "\nWrite mem and reload" output = ssh_conn.send_command_expect('write mem') output += ssh_conn.send_command('reload') output += ssh_conn.send_command('y') print output #wait for 5 minutes and verify the system is running on the new code time.sleep(300) ssh_conn = ConnectHandler(**asa) show_ver = ssh_conn.send_command('show_version') show_ver_lines = show_ver.split("\n") for line in show_ver_lines: if "System image" in line: print "\n" + line #Make the secondary ASA primary and verify print "\nFailing over ASA pair..." ssh_conn.send_command('failover active') print "\nVerifying this is the primary ASA..." failover = ssh_conn.send_command("show failover") failover_lines = failover.split("\n") for line in failover_lines: if ("This host:" in line) and ("Active" in line): print("\nFailover successful...") print "\n>>>> {}".format(datetime.now() - start_time) print
def main(): # Script to login to a Cisco IOS device ip_addr = input("Enter Cisco device IP address: ") my_pass = getpass() start_time = datetime.now() log_file = open("logging.txt", "w") log_file.write("Starting...") print(">>>> {}".format(start_time)) net_device = { 'device_type': 'cisco_ios', 'ip': ip_addr, 'username': '******', 'password': my_pass, 'secret': my_pass, 'port': 22, } print("\nLogging in to Cisco Device") ssh_conn = ConnectHandler(**net_device) print # Show current version of the software print("\nView software version") output = ssh_conn.send_command('show ver') print(output) # This can be used to send any command/configs """output = ssh_conn.send_command ('show run | inc logging') print(output) buffer_number = input("Enter logging buffer amount: ") config_commands = 'logging buffered {}'.format(buffer_number) output = ssh_conn.send_config_set([config_commands]) print(output) output = ssh_conn.send_command('show run | inc logging') print(output)""" # Image file settings dest_file_system = 'bootflash:' source_file = 'C:\\Users\\yongk\\Documents\\Images\\Cisco\\asr1000-universalk9.16.04.01.SPA.bin' # Use absolute path dest_file = 'asr1000-universalk9.16.04.01.SPA.bin' with FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system) as scp_transfer: if not scp_transfer.check_file_exists(): if not scp_transfer.verify_space_available(): raise ValuError( "Insufficient space available on remote device") print("Transferring file\n") scp_transfer.transfer_file() print("\nVerifying file") if scp_transfer.verify_file(): print("Source and destination MD5 hash match") else: raise ValueError( "MD5 hash between source and destination do not match") # Set the boot configuration print("\nSending boot commands") full_file_name = "{}/{}".format(dest_file_system, dest_file) boot_cmd = "boot system flash {}".format(full_file_name) output = ssh_conn.send_config_set([boot_cmd]) print(output) # Save the configuration print("\nWrite mem") output = ssh_conn.send_command('write mem') print(output) # Verify boot parameters print("\nVerifying state") output = ssh_conn.send_command('show boot') print(output) # Reboot print("\nReload") output = ssh_conn.send_command_timing('reload') output += ssh_conn.send_command_timing('y') print(output) print("\n>>>> Time taken: {}".format(datetime.now() - start_time)) print("\n") log_file.close()
from getpass import getpass from netmiko import ConnectHandler, FileTransfer def main(): net_device = { 'device_type': 'cisco_ios', 'ip': '10.101.14.1', 'username': '******', 'password': '******', } print("Logging in to Router\n") ssh_conn = ConnectHandler(**net_device) print() # GET OPERATION source_file = 'config_backup.txt' dest_file = 'config_backup.txt' dest_file_system = 'flash:' with FileTransfer(ssh_conn, source_file=source_file, dest_file=dest_file, file_system=dest_file_system, direction='get') as scp_transfer: scp_transfer.get_file() print("File transfer completed\n\n") if __name__ == "__main__": main()
def update_ios(self): # Test if active image is already the compliant image for this model and update the compliance variable if self.current_image == self.compliant_image: self.compliant = True if self.compliant: print "Device %s is already compliant, skipping...." % self.hostname elif self.username == None: print "No saved password for %s, skipping..." % self.hostname else: print "Device %s is not compliant, begin uploading file" % self.hostname # Initiate SSH Connection to device device = { 'device_type': 'cisco_ios', 'ip': self.ip_address, 'username': self.username, 'password': self.password } ssh_conn = ConnectHandler(**device) with FileTransfer(ssh_conn, source_file=self.compliant_image, dest_file=self.compliant_image, file_system=self.filesystem) as scp_transfer: if not scp_transfer.check_file_exists(): if not scp_transfer.verify_space_available(): raise ValueError( "Insufficient space available on remote device") print "Enabling SCP for device %s" % self.hostname output = ios_scp_handler(ssh_conn, mode='enable') output += ssh_conn.send_config_set( ['line vty 0 4', 'exec-timeout 0 0']) print output print "\nTransferring file to device %s\n" % self.hostname scp_transfer.transfer_file() # print "Disabling SCP" # output = ios_scp_handler(ssh_conn, mode='disable') # print output print "\nVerifying file on device %s" % self.hostname if scp_transfer.verify_file(): print "Source and destination MD5 matches" self.compliant = True self.reload_pending = True else: raise ValueError( "MD5 failure between source and destination files") print "\nSending boot commands & saving configuration" # Clear existing Boot Variable output = ssh_conn.send_config_set(['no boot system']) full_file_name = "{}{}".format(self.filesystem, self.compliant_image) boot_cmd = 'boot system {}'.format(full_file_name) output += ssh_conn.send_config_set([boot_cmd]) output += ssh_conn.send_command('wr') print output ssh_conn.send_command('show run | inc boot system') print "Boot variable set for device %s - %s" % (self.hostname, output) # If IOS Upload has succeeded, mark the device as compliant to prevent future upgrades prior to device rebooting and the active image being compliant. if self.compliant_image in output: self.compliance = True self.reload_pending = True
def scp_file(self, source_file, dest_file, file_system): ''' SCP file to remote device Return (status, msg) status = boolean msg = details on what happened ''' # Will automaticall enable SCP on remote device enable_scp = True debug = False with FileTransfer(self.device, source_file=source_file, dest_file=dest_file, file_system=file_system) as scp_transfer: if debug: base_time = datetime.now() print("check1: {}".format(base_time)) # Check if file already exists and has correct MD5 if scp_transfer.check_file_exists() and scp_transfer.compare_md5(): msg = "File already exists and has correct MD5: no SCP needed" return (True, msg) if not scp_transfer.verify_space_available(): msg = "Insufficient space available on remote device" return (False, msg) if debug: print( "check2 (file already exists, correct md5, space available)" ) print("Time delta: {}".format(datetime.now() - base_time)) base_time = datetime.now() if enable_scp: scp_transfer.enable_scp() if debug: print("check3 (enable_scp)") print("Time delta: {}".format(datetime.now() - base_time)) base_time = datetime.now() # Transfer file scp_transfer.transfer_file() if debug: print("check4 (transfer_file)") print("Time delta: {}".format(datetime.now() - base_time)) base_time = datetime.now() # Compares MD5 between local-remote files if scp_transfer.verify_file(): msg = "File successfully transferred to remote device" if debug: print("check5: {}".format(datetime.now())) return (True, msg) else: msg = "File transfer to remote device failed" return (False, msg) if debug: print("check5 (verify_file)") print("Time delta: {}".format(datetime.now() - base_time)) return (False, '')
#!/usr/bin/env python3 from netmiko import ConnectHandler, FileTransfer def create_conn(port: str) -> dict: return { "device_type": "juniper_junos_ssh", "host": "localhost", "username": "******", "password": "******", "port": port, } if __name__ == "__main__": vsrx1 = create_conn("2201") vsrx1_conn = ConnectHandler(**vsrx1) with FileTransfer(vsrx1_conn, source_file="load_conf.conf", dest_file="load_conf.conf") as scp_transfer: if not scp_transfer.check_file_exists(): if not scp_transfer.verify_space_available(): raise ValueError( "Insufficient space available on remote device") scp_transfer.transfer_file()