def send_command_non_blocking(self, command, timeout=60, throw_exception=True): logging.debug('Executing commands:\n %s' % command) if not self.ssh: raise ConnectionException('Device not open') stdin, stdout, stderr = self.ssh.exec_command(command, timeout=timeout) output = ''.join(stdout.readlines()) error = ''.join(stderr.readlines()) if len(error) > 0: msg = '%s:%s' % (command, error) logging.debug('error:' + msg) if throw_exception: raise CommandErrorException(msg) regex = re.compile('ERROR:') if len(regex.findall(output)) > 0: msg = '%s:%s' % (command, output) logging.debug('error:' + msg) if throw_exception: raise CommandErrorException(msg) return output[:-1], error[:-1] # Remove last newline charater.
def _load_config(self, filename=None, config=None, replace=False): if filename and config: raise ValueError("Cannot simultaneously set filename and config") self._lock() self._candidate = ["copy running-config flash:rollback-0"] if replace: self._candidate.append("copy default-config running-config") self._replace_config = True self._candidate.append("configure terminal") if filename is not None: with open(filename, "r") as f: self._candidate = f.readlines() else: if isinstance(config, list): lines = config else: lines = config.splitlines() for line in lines: if line.strip() == "": continue if line.startswith("!"): continue self._candidate.append(line) self._candidate.append("end") if any("source mac" in line for line in self._candidate) and self._version < LooseVersion("0.19.2"): # Waiting for fixed release raise CommandErrorException( "Cannot set source mac in MOS versions prior to 0.19.2") if any("banner motd" in line for line in self._candidate): raise CommandErrorException("Cannot set banner via JSONRPC API")
def compare_config(self): """Compare the running config with the candidate one.""" url = self._api_url + 'system/config/cfg_restore/latest_diff' check_url = url + '/status' data = { "server_type": "ST_FLASH", "file_name": "REST_Payload_Backup", "is_oobm": False } # trigger configuration comparison diff = self._apisession.post(url, json=data) if 300 > diff.status_code >= 200: diff_output = self._apisession.get(check_url) if diff_output.status_code == 200: if not diff_output.json()['diff_add_list'] and \ not diff_output.json()['diff_remove_list']: # return empty string to signal the candidate # and running configs are the same return "" else: return diff_output.json() else: raise CommandErrorException("diff generation failed,\ raise status") else: raise CommandErrorException("diff generation failed, raise status")
def cli(self, commands): cli_output = {} if not isinstance(commands, list): raise TypeError("Please enter a valid list of commands!") for command in commands: try: cli_output[command] = self.device.run_commands( [command], encoding="text")[0].get("output") # not quite fair to not exploit rum_commands # but at least can have better control to point to wrong command in case of failure except pyeapi.eapilib.CommandError: # for sure this command failed cli_output[command] = 'Invalid command: "{cmd}"'.format( cmd=command) raise CommandErrorException(str(cli_output)) except Exception as e: # something bad happened msg = 'Unable to execute command "{cmd}": {err}'.format( cmd=command, err=e) cli_output[command] = msg raise CommandErrorException(str(cli_output)) return cli_output
def _discover_file_system(self): try: return self.device._autodetect_fs() except Exception: msg = "Netmiko _autodetect_fs failed (to work around specify " \ "dest_file_system in optional_args)." raise CommandErrorException(msg)
def _commit_merge(self): commands = [ command for command in self.merge_candidate.splitlines() if command ] self.device.config_list(commands) if not self.device.save(): raise CommandErrorException('Unable to commit config!')
def _copy_run_start(self): output = self.device.save_config() if "complete" in output.lower(): return True else: msg = "Unable to save running-config to startup-config!" raise CommandErrorException(msg)
def send_command_std(self, command, timeout=60, throw_exception=True): logging.debug('Executing commands:\n %s' % command) if not self.ssh: raise ConnectionException('Device not open') chan = self.ssh.get_transport().open_session() chan.settimeout(timeout) chan.exec_command(command) retcode = chan.recv_exit_status() logging.debug('Command exited with code %d' % retcode) error_chan = chan.makefile_stderr() output_chan = chan.makefile() error = '' output = '' for e in error_chan.read(): error = error + self._read_wrapper(e) logging.debug("stderr: " + error) for o in output_chan.read(): output = output + self._read_wrapper(o) logging.debug("stdout: " + output) # Ignore stty error happen in some devices if "stty: standard input: Inappropriate ioctl for device" in error: error = error.replace( 'stty: standard input: Inappropriate ioctl for device\n', '') if len(error) > 0 and retcode != 0: msg = '%s:%s' % (command, error) logging.debug('error:' + msg) if throw_exception: raise CommandErrorException(msg) regex = re.compile('ERROR:') if len(regex.findall(output)) > 0: msg = '%s:%s' % (command, output) logging.debug('error:' + msg) if throw_exception: raise CommandErrorException(msg) return output[:-1], error[: -1], retcode # Remove last newline charater.
def _copy_run_start(self, filename='startup-config'): command = 'copy run {}'.format(filename) output = self.device.send_command(command) if 'complete' in output.lower(): return True else: msg = 'Unable to save running-config to {}!'.format(filename) raise CommandErrorException(msg)
def disable_paging(self): """ Disable paging on the device """ out_disable_paging = self.device.send_command('screen-length disable') if 'configuration is disabled for current user' in out_disable_paging: pass else: raise CommandErrorException( "Disable Paging cli command error: {}".format( out_disable_paging))
def _save_config(self, filename=''): """Save the current running config to the given file.""" command = 'save {}'.format(filename) save_log = self.device.send_command(command, max_loops=10, expect_string=r'Y/N') # Search pattern will not be detected when set a new hostname, so don't use auto_find_prompt=False save_log += self.device.send_command('y', expect_string=r'<.+>') search_result = re.search("successfully", save_log, re.M) if search_result is None: msg = "Failed to save config. Command output:{}".format(save_log) raise CommandErrorException(msg)
def _run_cmd(self, cmd): url = self._api_url + 'cli' data = {} data['cmd'] = cmd cmd_post = self._apisession.post(url, json=data) if cmd_post.status_code == 200: return base64.b64decode( cmd_post.json()['result_base64_encoded']).decode('utf-8') else: raise CommandErrorException("Parsing CLI commands failed")
def _load_config(self, config_file): command = 'rollback configuration to file {0}'.format(config_file) rollback_result = self.device.send_command(command, expect_string=r'Y/N') rollback_result += self.device.send_command('y', expect_string=r'[<\[].+[>\]]') search_result = re.search("clear the information", rollback_result, re.M) if search_result is not None: rollback_result += self.device.send_command('y', expect_string=r'<.+>') search_result = re.search("succeeded|finished", rollback_result, re.M) if search_result is None: msg = "Failed to load config. Command output:{}".format(rollback_result) raise CommandErrorException(msg)
def get_resp(self, endpoint="", data=None, params={}, throw=True, returnObject=False): """Get response from device and returne parsed json.""" full_url = self.base_url + endpoint f = None try: if data is not None: f = self.session.post(full_url, data=data, auth=(self.username, self.password), headers=self.headers, timeout=self.timeout, params=params, verify=False) else: f = self.session.get(full_url, auth=(self.username, self.password), headers=self.headers, timeout=self.timeout, params=params, verify=False) if (f.status_code != 200): if throw: errMsg = "Operation returned an error: {}".format( f.status_code) raise CommandErrorException(errMsg) else: return False if returnObject: return f else: return f.text except requests.exceptions.RequestException as e: if throw: raise ConnectionException(py23_compat.text_type(e)) else: return False
def _set_virtual_system(self, vsid: int) -> bool: """ | Switch to VSX context. Raises RuntimeError if failed. :return: bool """ try: if self._check_vsx_state() is True: if self._check_expert_mode() is True: command = 'vsenv {}'.format(vsid) else: command = 'set virtual-system {}'.format(vsid) vsid_regex = r'(?<=:){}'.format(vsid) expect_regex = r'(?<=:)\d+' output = self.device.send_command(command, expect_regex) if re.search(vsid_regex, output): return True else: raise CommandErrorException('cannot access virtual-system') else: raise ValidationException('VSX not enabled') except (socket.error, EOFError) as e: raise ConnectionClosedException(str(e))
def get_resp(self, endpoint="", data=None): """Get response from device and returne parsed json.""" full_url = self.base_url + endpoint f = None try: if data is not None: f = self.session.post(full_url, data=data, headers=self.headers, timeout=self.timeout, verify=False) else: f = self.session.get(full_url, headers=self.headers, timeout=self.timeout, verify=False) if (f.status_code != 200): raise CommandErrorException( "Operation returned an error: {}".format(f.status_code)) return f.json() except requests.exceptions.RequestException as e: raise ConnectionException(py23_compat.text_type(e))
def _copy_run_start(self, filename='startup-config'): results = self.device.save(filename=filename) if not results: msg = 'Unable to save running-config to {}!'.format(filename) raise CommandErrorException(msg)