def _Cmd(self, command, mode=None, called_already=False): _ = mode # Strip question marks and short-circuit if we have nothing more. command = command.replace('?', '') if not command: return '' try: self._connection.child.send(command + '\r') self._connection.child.expect(command + '\n') result = '' while True: i = self._connection.child.expect( [self._connection.re_prompt, self.RE_PAGER], timeout=self.timeout_response, searchwindowsize=128) # HP prefers \n\r to \r\n. result += self._connection.child.before.replace( '\n\r', os.linesep) if i == 1: self._connection.child.send(' ') else: break # Check if the device told us our command was not recognized. if self.RE_INVALID.search(result) is not None: raise exceptions.CmdError( 'Command %r invalid on %s(%s)' % (command, self.host, self.loopback_ipv4)) return result except pexpect.TIMEOUT, e: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e)))
def _Cmd(self, command, mode=None): """CiscoXR wrapper for ParamikoDevice._Cmd().""" result = super(CiscoXrDevice, self)._Cmd(command, mode) if result.endswith("% Invalid input detected at '^' marker.\r\n"): raise exceptions.CmdError('Invalid input: %s' % command) if result.endswith('% Bad hostname or protocol not running\r\n'): raise exceptions.CmdError( 'Bad hostname or protocol not running: %s' % command) if result.endswith('% Incomplete command.\r\n'): raise exceptions.CmdError('Incomplete command: %s' % command) return result
def _Cmd(self, command, mode=None): def SendAndWait(command): """Sends a command and waits for a response.""" self._connection.child.send(command+'\r') self._connection.child.expect('\r\n', timeout=self.timeout_response) self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_response, searchwindowsize=128) return self._connection.child.before.replace('\r\n', os.linesep) # Quieten pylint. _ = mode # We strip question-marks ('?') from the input as they upset the # buffering for minimal gain (they work only on ASA and not on FTOS). command = command.replace('?', '') result = '' try: result = SendAndWait(command) except pexpect.TIMEOUT as e: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e))) except pexpect.EOF: # Retry once on EOF error, in case we have been idle disconnected. try: self.connected = False self._connection.Connect() self._DisablePager() self.connected = True result = SendAndWait(command) except pexpect.EOF: raise exceptions.CmdError('Failed with EOF error twice.') except pexpect_connection.ConnectionError as e: raise exceptions.CmdError('Auto-reconnect failed: %s' % e) except pexpect_connection.TimeoutError as e: raise exceptions.CmdError('Auto-reconnect timed out: %s' % e) # Fix trailing \r to \n (if \n of last \r\n is captured by prompt). if result and result[-1] == '\r': result = result[:-1] + '\n' if (result.endswith(INVALID_1) or result.endswith(INVALID_2) or result.endswith(INVALID_3) or result.endswith(INVALID_4) or result.endswith(INVALID_5) or ( result.endswith('\n') and result[result[:-1].rfind('\n') + 1:].startswith( INVALID_6_PREFIX))): raise exceptions.CmdError('Command failed: %s' % result) return result
def _Cmd(self, command, mode=None): # Enforce that the 'ping' and 'monitor' commands have a count (else they # never complete). JunOS allows these to be abbreviated to 'p' and 'mo' # since no other commands begin with those prefixes. if command.startswith('p') or command.startswith('mo'): if ' count ' not in command: # Use 5 pings by default (same as default for 'ping <host> rapid'). command += ' count 5' # Enforce that traceroute and monitor have the stderr (header) and stdout # merged, in that order. if command.startswith('tr') or command.startswith('mo'): merge_stderr_first = True else: merge_stderr_first = False # Run the modified command. result = super(JunosDevice, self)._Cmd(command, mode=mode, merge_stderr_first=merge_stderr_first, require_low_chanid=True) # Report JunOS errors on stdout as CmdError. if result.startswith('\nerror: '): raise exceptions.CmdError(result[1:]) # Drop the leading \n. else: return result
def _Cmd(self, command, mode=None): """Cisco Nexus wrapper for ParamikoDevice._Cmd().""" result = super(CiscoNexusDevice, self)._Cmd(command, mode) # On Successful execution of a command. # ssh [email protected] 'show version'. # Password:. # Cisco Nexus Operating System (NX-OS) Software # TAC support: http://www.cisco.com/tac. # [output truncated]. # Incomplete Command Example. # [ mijith@pulsar: ~ ]. # $ ssh [email protected] 'show' # Syntax error while parsing 'show'. # Cmd exec error. # Invalid Command Example. # [ mijith@pulsar: ~ ]. # $ ssh [email protected] 'foo'. # Syntax error while parsing 'foo'. # Cmd exec error. if result.endswith(INVALID_OUT): raise exceptions.CmdError('INVALID COMMAND: %s' % command) return result
def _DisablePager(self): """Disables the pager.""" try: self._connection.child.send(self.disable_pager_command) self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_connect, searchwindowsize=128) except (pexpect.EOF, pexpect.TIMEOUT), e: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e)))
def _Cmd(self, command, mode=None): def SendAndWait(command): """Sends a command and waits for a response.""" self._connection.child.send(command + '\r') self._connection.child.expect('\r\n', timeout=self.timeout_response) self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_response, searchwindowsize=128) return self._connection.child.before.replace('\r\n', os.linesep) _ = mode command = command.replace('?', '') result = '' try: result = SendAndWait(command) except pexpect.TIMEOUT as e: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e))) except pexpect.EOF: # Retry once on EOF error, in case we have been idle disconnected. try: self.connected = False self._connection.Connect() self._DisablePager() self.connected = True result = SendAndWait(command) except pexpect.EOF: raise exceptions.CmdError('Failed with EOF error twice.') except pexpect_connection.ConnectionError as e: raise exceptions.CmdError('Auto-reconnect failed: %s' % e) except pexpect_connection.TimeoutError as e: raise exceptions.CmdError('Auto-reconnect timed out: %s' % e) # Fix trailing \r to \n (if \n of last \r\n is captured by prompt). if result and result[-1] == '\r': result = result[:-1] + '\n' if result.endswith(INVALID_OUT1) or result.startswith(INVALID_OUT2): raise exceptions.CmdError('Command failed: %s' % result) return result
def _Cmd(self, command, mode=None): def SendAndWait(command): """Sends a command and waits for a response.""" self._connection.child.send(command + '\r') self._connection.child.expect('\r\n', timeout=self.timeout_response) self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_response, searchwindowsize=128) return self._connection.child.before.replace('\r\n', os.linesep) _ = mode command = command.replace('?', '') if next((command for prefix in self.verboten_commands if command.startswith(prefix)), False): raise exceptions.CmdError( 'Command %s is not permitted on Brocade devices.' % command) result = '' try: result = SendAndWait(command) except pexpect.TIMEOUT, e: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e)))
def _DisablePager(self): """Enables and logs in so the pager can be disabled.""" # Maximum terminal size on sw version M.08.74 (8095) is 1920x1000. try: self._connection.child.send('terminal length 1000\r') self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_act_user, searchwindowsize=128) self._connection.child.send('terminal width 1920\r') self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_act_user, searchwindowsize=128) except (pexpect.EOF, pexpect.TIMEOUT), e: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e)))
def Cmd(self, command, mode=None): """Executes a command. Concrete classes must define _Cmd with the same arguments. Args: command: A string, the command to execute. mode: A string, the CLI mode to use for this command (e.g., 'shell' on Netscaler). The empty string or None will use the device's default mode. Returns: A string, the response. Raises: exceptions.CmdError: An error occurred inside the call to _Cmd. """ if not command: raise exceptions.CmdError('No command supplied for Cmd() method.') else: if not mode: mode = None return self._Cmd(command, mode=mode)
def _SetConfig(self, unused_destination_file, data, canary): """Upload config to a Brocade router (TI/FI). Args: unused_destination_file: Unused. data: A string, the data to copy to destination_file. canary: A boolean, if True, only canary check the configuration, don't apply it. Returns: A base_device.SetConfigResult. Transcript of any device interaction that occurred during the _SetConfig. Raises: exceptions.CmdError: An error occurred inside the call to _Cmd. """ # Canarying is not supported on BROCADE. if canary: raise exceptions.SetConfigCanaryingError( '%s devices do not support ' 'configuration canarying.' % self.vendor_name) # The result object. result = base_device.SetConfigResult() # Check for a connection to the Brocade. if not self._GetConnected(): raise exceptions.SetConfigError('Cannot use unless already ' 'connected to the device.') # Derive our config prompt from the discovered prompt. self._connection.config_prompt = re.compile( self._connection.re_prompt.pattern[:-2] + r'\(config\S*\)' + self._connection.re_prompt.pattern[-2:]) # Enter config mode. self._connection.child.send('configure terminal\r') self._connection.child.expect('\r\n', timeout=self.timeout_response) self._connection.child.expect(self._connection.config_prompt, timeout=self.timeout_response, searchwindowsize=128) def SendAndWait(command): """Sends a command and waits for a response. Args: command: str; A single config line. Returns: A string; the last response. Raises: exceptions.SetConfigError: When we unexpectedly exit configuration mode while setting config. """ self._connection.child.send(command + '\r') self._connection.child.expect('\r\n', timeout=self.timeout_response) pindex = self._connection.child.expect( [self._connection.config_prompt, self._connection.re_prompt], timeout=self.timeout_response, searchwindowsize=128) # We unexpectedly exited config mode. Too many exits or ctrl-z. if pindex == 1: raise exceptions.SetConfigError( 'Unexpectedly exited config mode after line: %s' % command) return self._connection.child.before.replace('\r\n', os.linesep) lines = [x.strip() for x in data.splitlines()] # Remove any 'end' lines. Multiple ends could be bad. lines = [line for line in lines if line != 'end'] for line in lines: if next((line for prefix in self.verboten_config if line.startswith(prefix)), False): raise exceptions.CmdError( 'Command %s is not permitted on Brocade devices.' % line) if line: line_result = SendAndWait(line) if (line_result.startswith('Invalid input -> ') or line_result == 'Not authorized to execute this command.\n'): raise exceptions.CmdError('Command failed: %s' % line_result) self._connection.child.send('end\r') self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_act_user) self._connection.child.send('wr mem\r') self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_act_user) self._Disconnect() result.transcript = 'SetConfig applied the file successfully.' return result
result = '' try: result = SendAndWait(command) except pexpect.TIMEOUT, e: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e))) except pexpect.EOF: # Retry once on EOF error, in case we have been idle disconnected. try: self.connected = False self._connection.Connect() self._DisablePager() self.connected = True result = SendAndWait(command) except pexpect.EOF: raise exceptions.CmdError('Failed with EOF error twice.') except pexpect_connection.ConnectionError, e: raise exceptions.CmdError('Auto-reconnect failed: %s' % e) except pexpect_connection.TimeoutError, e: raise exceptions.CmdError('Auto-reconnect timed out: %s' % e) # Fix trailing \r to \n (if \n of last \r\n is captured by prompt). if result and result[-1] == '\r': result = result[:-1] + '\n' if (result.startswith('Invalid input -> ') or result == 'Not authorized to execute this command.\n'): if result.endswith('\nType ? for a list\n'): result = result[:-19] elif result.endswith('\n'): result = result[:-1]
def _Cmd(self, command, mode=None, merge_stderr_first=False, send=None, require_low_chanid=False): response = '' retries_left = 1 while True: try: chan = self._ssh_client.get_transport().open_session() chan.settimeout(self.timeout_response) if require_low_chanid and chan.remote_chanid > _LOW_CHANID_THRESHOLD: # We should not be having multiple channels open. If we do, # close them before proceeding. logging.error( 'Remote ssh channel id %d exceeded %d when opening session to ' '%s(%s), reconnecting.', chan.remote_chanid, _LOW_CHANID_THRESHOLD, self.host, self.loopback_ipv4) self.Disconnect() self.Connect(self._username, self._password, self._ssh_keys, self._enable_password) chan = self._ssh_client.get_transport().open_session() chan.exec_command(command) stdin = chan.makefile('wb', -1) stdout = chan.makefile('rb', -1) stderr = chan.makefile_stderr('rb', -1) if send is not None: stdin.write(send) stdout_data = stdout.read() stderr_data = stderr.read() # Request channel close by remote peer. chan.close() break except paramiko.SSHException as e: msg = str(e) logging.error('%s(%s) Cmd(%r, mode=%r): %s', self.host, self.loopback_ipv4, command, mode, msg) raise exceptions.CmdError(msg) except AttributeError: # This occurs when self._ssh_client becomes None after a Paramiko # failure. Pause momentarily, try to reconnect and loop to resend # the command. time.sleep(0.25) try: if retries_left: self._Connect(self._username, self._password, self._ssh_keys) retries_left -= 1 continue else: raise exceptions.CmdError( 'Failed to exec_command after retry.') except paramiko.SSHException as e: msg = str(e) logging.error('%s(%s) Cmd(%r, mode=%r): %s', self.host, self.loopback_ipv4, command, mode, msg) raise exceptions.ConnectError(msg) except Exception as e: # Paramiko may raise any exception, so catch and log it here. msg = '%s:%s(%s) Cmd(%r, mode=%r): %s: %s' % ( type(e), self.host, self.loopback_ipv4, command, mode, e.__class__.__name__, str(e)) logging.exception(msg) raise exceptions.CmdError('%s: %s' % (e.__class__.__name__, str(e))) # Remove stderr lines started with 'waiting for'. if stderr_data and not merge_stderr_first: out = [] for l in stderr_data.splitlines(): if not l.startswith('waiting for'): out.append(l) stderr_data = '\n'.join(out) # Marshal the response from the stdout/err channels and handle errors. if stderr_data and not merge_stderr_first: raise exceptions.CmdError(stderr_data) elif stdout_data: if merge_stderr_first and stderr_data: response = stderr_data response += stdout_data else: # Sometimes, a command (e.g., 'show system license keys') returns # nothing. This can mean that the channel went away on us, and we # got no data back (and no error). if self.connected: logging.warn('Both STDOUT and STDERR empty after %s on %s(%s)', repr(command), self.host, self.loopback_ipv4) else: raise exceptions.CmdError( 'Connection to %s(%s) was terminated.' % (self.host, self.loopback_ipv4)) return response
break # Check if the device told us our command was not recognized. if self.RE_INVALID.search(result) is not None: raise exceptions.CmdError( 'Command %r invalid on %s(%s)' % (command, self.host, self.loopback_ipv4)) return result except pexpect.TIMEOUT, e: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e))) except pexpect.EOF, e: if not called_already: return self._Cmd(command, mode=mode, called_already=True) else: self.connected = False raise exceptions.CmdError('%s: %s' % (e.__class__, str(e))) def _DisablePager(self): """Enables and logs in so the pager can be disabled.""" # Maximum terminal size on sw version M.08.74 (8095) is 1920x1000. try: self._connection.child.send('terminal length 1000\r') self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_act_user, searchwindowsize=128) self._connection.child.send('terminal width 1920\r') self._connection.child.expect(self._connection.re_prompt, timeout=self.timeout_act_user, searchwindowsize=128) except (pexpect.EOF, pexpect.TIMEOUT), e: self.connected = False