def testSignBuffer(self): intemp = tempfile.NamedTemporaryFile() # Simulate osslsign writing the signed file outname = "%s.signed" % intemp.name open(outname, "w").write("content") with mock.patch.object(pexpect, "spawn"): with mock.patch.object(signing.subprocess, "check_call"): with mock.patch.object(tempfile, "NamedTemporaryFile", return_value=intemp): output = self.winsign.SignBuffer("asdflkjlaksjdf") self.assertEqual(output, "content") with mock.patch.object(pexpect, "spawn", side_effect=pexpect.ExceptionPexpect("blah")): with self.assertRaises(pexpect.ExceptionPexpect): output = self.winsign.SignBuffer("asdflkjlaksjdf")
def read_nonblocking(self, size=-1, timeout=-1): if self.child_fd == -1: raise ValueError('I/O operation on closed file') if not self.isalive(): r, w, e = select.select([self.child_fd], [], [], 0) if not r: self.flag_eof = True raise pexpect.EOF('End Of File (EOF) in read(). ' 'Braindead platform.') if timeout == -1: timeout = self.timeout r, w, e = select.select([self.child_fd], [], [], timeout) if not r: raise pexpect.TIMEOUT('Timeout (%s) exceeded in read().' % str(timeout)) if self.child_fd in r: try: s = self.child_fd.recv(size) except OSError as e: self.flag_eof = True raise pexpect.EOF('End Of File (EOF) in read(). ' 'Exception style platform.') if s == '': self.flag_eof = True raise pexpect.EOF('End Of File (EOF) in read(). ' 'Empty string style platform.') if self.logfile is not None: self.logfile.write(s) self.logfile.flush() return s raise pexpect.ExceptionPexpect( 'Reached an unexpected state in read().')
def get_output(self, code_override=None): text_output = "before:\n{before}\nafter:\n{after}".format( before=self.before if isinstance( self.before, basestring) else pformat(self.before, indent=4), after=self.after if isinstance( self.after, basestring) else pformat(self.after, indent=4)) if code_override is not None: conf.bash_log(pformat(self.args), code_override, text_output) return code_override, text_output if self.isalive(): conf.bash_log( pformat(self.args), 'Spawned process running: pid={pid}'.format(pid=self.pid), text_output) raise pexpect.ExceptionPexpect( 'Unable to return exit code. Spawned command is still running:\n' + text_output) conf.bash_log(pformat(self.args), self.exitstatus, text_output) return self.exitstatus, text_output
class ParamikoSpawn(pexpect.spawn): """A pexpect.spawn that works with Paramiko Channel objects. The Channel object returned by paramiko.SSHClient.invoke_shell() is suitable for use with this class. Attributes: child_fd: A paramiko.Channel object, the SSH channel (like a socket). """ def _set_channel(self, channel): self.child_fd = channel channel = property(lambda x: x.child_fd, _set_channel) @property def transport(self): return self.child_fd.get_transport() def isalive(self): try: return self.child_fd.get_transport().is_active() except AttributeError: return False def read_nonblocking(self, size=-1, timeout=-1): if self.child_fd == -1: raise ValueError('I/O operation on closed file') if not self.isalive(): r, w, e = select.select([self.child_fd], [], [], 0) if not r: self.flag_eof = True raise pexpect.EOF('End Of File (EOF) in read(). ' 'Braindead platform.') if timeout == -1: timeout = self.timeout r, w, e = select.select([self.child_fd], [], [], timeout) if not r: raise pexpect.TIMEOUT('Timeout (%s) exceeded in read().' % str(timeout)) if self.child_fd in r: try: s = self.child_fd.recv(size) except OSError, e: self.flag_eof = True raise pexpect.EOF('End Of File (EOF) in read(). ' 'Exception style platform.') if s == '': self.flag_eof = True raise pexpect.EOF('End Of File (EOF) in read(). ' 'Empty string style platform.') if self.logfile is not None: self.logfile.write(s) self.logfile.flush() return s raise pexpect.ExceptionPexpect('Reached an unexpected state in read().')
def error_message(exc_info, **options): """Formats exception or error information for logging and debugging. :param tuple exc_info: Exception details from sys module. :param Exception options: The exception object for pexpect's ExceptionPexpect (pex) or for subprocess' CalledProcessError (cpe). :return: The formatted message. .. seealso:: https://realpython.com/the-most-diabolical-python-antipattern/#why-log-the-full-stack-trace """ # Get keyword arguments. Initialize default to None to prevent SonarLint reference error pex = options.get("pex", pexpect.ExceptionPexpect(None)) cpe = options.get("cpe", subprocess.CalledProcessError(0, "", None)) # Unpack sys.exc_info() to get error information e_type, e_value, e_traceback = exc_info if pex.value: # This code is for pexpect.ExceptionPexpect. It retrieves the device's response to # pexpect.sendline() from the state of the spawned object (__str__ from ex) for # logging. Otherwise, if you match TIMEOUT or EOF when calling expect or expect_exact, # pexpect does not retain the state of the object, since it believes you will # handle the exception by other means (e.g., generic message to the user, etc.). # https://pexpect.readthedocs.io/en/stable/api/pexpect.html#spawn-class # https://pexpect.readthedocs.io/en/stable/_modules/pexpect/exceptions.html#TIMEOUT # For pexpect.run calls... if "searcher" not in str(pex): e_value = str(pex).strip("\r\n") # For pexpect.expect-type calls... else: # Log what was actually found during the pexpect call e_value = "Expected {0}, found \"{1}".format( str(pex).split("searcher: ")[1].split( "buffer (last 100 chars):")[0], str(pex).split("before (last 100 chars): ")[1].split("after:") [0]) # Remove any unwanted escape characters here, like backspaces, etc. # e_value = re.sub("[\b\r\n]", " ", e_value).replace(" ", " ") e_value = " ".join(e_value.replace("\b", "").split()) elif cpe.output: # This code is for subprocess.CalledProcessError. In Python 2.7, subprocess only # returns the reason for a non-zero return code (i.e., the CLI's response) in a # CalledProcessError object unless the shell option is set to True, which is unsafe due # to potential shell injections. # https://docs.python.org/2/library/subprocess.html#subprocess.check_output e_value = "'{0}' failed: {1}".format(cpe.cmd, cpe.output) # Move up the error stack to retrieve the function or method where the error or exception actually occurred, # instead of the line number where the function or method was called. if e_traceback.tb_next is not None: e_traceback = e_traceback.tb_next # Return the formatted message for logging # Start with a linefeed to avoid tailing device OS messages # Error message format: # - e_type: Type of error. # - e_value: Error message. # - e_traceback.tb_frame.f_code.co_filename: The name of the file that caused the error. # - e_traceback.tb_lineno: The line number where the error occurred. msg = ("\nType {0}: \"{1}\" in {2} at line {3}.\n".format( e_type.__name__, e_value, e_traceback.tb_frame.f_code.co_filename, e_traceback.tb_lineno)) log_message(msg, level=logging.ERROR) return msg
def main(): child = None try: print( "Lab 1: Connect to an unconfigured device through the Console port" ) device_hostname = "R2" gateway_ip_addr = "192.168.1.1" device_console_port = 5002 console_password = "******" # child = labs.lab01_telnet.connect(device_hostname, gateway_ip_addr, device_console_port, password=console_password) # TODO: NEED TO FIX EOL FOR VTY! child = labs.lab01_telnet.connect(device_hostname, "192.168.1.30", username="******", password="******") print("Lab 2: Access a network device's Privileged EXEC Mode") # Part 1: Up from User EXEC mode child.sendline("disable\r") child.expect_exact(device_hostname + ">") enable_password = "******" child = labs.lab02_exec_mode.run(child, device_hostname, enable_password) # Part 2: Down from Global Configuration mode child.sendline("configure terminal\r") child.expect_exact(device_hostname + "(config)#") child.sendline("interface FastEthernet0/0\r") child.expect_exact(device_hostname + "(config-if)#") child = labs.lab02_exec_mode.run(child, device_hostname, enable_password) print("Lab 3: Format a network device's flash memory") child = labs.lab03_format.run(child, device_hostname) print("Lab 4: Get information about a network device") child = labs.lab04_info.run(child, device_hostname) print("Lab 5: Enable Layer 3 communications") child = labs.lab05_enable_layer3.run(child, device_hostname, "192.168.1.30", new_netmask="255.255.255.0", commit=True) print("Lab 6a: Secure a network device") child = labs.lab06_secure_device.run(child, device_hostname, device_username="******", device_password="******", privilege=15, console_password="******", auxiliary_password="******", enable_password="******", commit=True) print("Lab 6b: Ping the device from the host") _, exitstatus = pexpect.run("ping -c 4 {0}".format("192.168.1.30"), withexitstatus=True) if exitstatus != 0: # No need to read the output. Ping returns a non-zero value if no packets are received raise RuntimeError("Cannot connect to netowrk device.") print( "Lab 6c: Connect to a configured device through the Console port") labs.lab01_telnet.disconnect(child) child.close() child = labs.lab01_telnet.connect(device_hostname, "192.168.1.30", username="******", password="******") print("Lab 6d: Access a network device's Privileged EXEC Mode") child = labs.lab02_exec_mode.run(child, device_hostname, enable_password) print("Lab 6e: Get information about a network device") child = labs.lab04_info.run(child, device_hostname) except ( pexpect.ExceptionPexpect, ValueError, RuntimeError, OSError, ): # Unpack sys.exc_info() to get error information e_type, e_value, _ = sys.exc_info() # Instantiate the message container err_msg = "" if e_type in ( pexpect.ExceptionPexpect, pexpect.EOF, pexpect.TIMEOUT, ): # Add a heading to EOF and TIMEOUT errors if pexpect.EOF: err_msg += "EOF reached: Child process exited unexpectedly.\n" elif pexpect.TIMEOUT: err_msg += "Timed-out looking for the expected search string.\n" # Add trace err_msg += pexpect.ExceptionPexpect(e_type).get_trace() # For pexpect.run calls... if "searcher" not in str(e_value): err_msg += (str(e_value).strip("\r\n")) # For pexpect.expect-type calls... else: # Log what was actually found during the pexpect call _e_value = "Expected {0}\nFound {1}.".format( str(e_value).split("searcher: ")[1].split( "buffer (last 100 chars):")[0].strip(), str(e_value).split("before (last 100 chars): ")[1].split( "after:")[0].strip()) # Remove any unwanted escape characters here, like backspaces, etc. e_value = _e_value.replace("\b", "") err_msg += _e_value elif e_type in ( ValueError, RuntimeError, OSError, ): err_msg += (traceback.format_exc().strip()) print(err_msg) finally: # Ensure the child is closed if child: print("Closing child...") child.close() print("Child closed.") print("Script complete. Have a nice day.")
class spawn(pexpect.spawn): """This class is a wrapper around pexpect.spawn to be able to talk to channels instead of normal file descriptors""" def set_channel(self, chan): self.child_fd = chan #.fileno() def set_console(self, console): self.console = console def isalive(self): try: return self.child_fd.get_transport().is_active() except AttributeError: return False def read_nonblocking(self, size=1, timeout=None): """ This reads at most size characters from the child application. It includes a timeout. If the read does not complete within the timeout period then a TIMEOUT exception is raised. If the end of file is read then an EOF exception will be raised. If a log file was set using setlog() then all data will also be written to the log file. Notice that if this method is called with timeout=None then it actually may block. This is a non-blocking wrapper around os.read(). It uses select.select() to implement a timeout. """ if self.child_fd == -1: raise ValueError('I/O operation on closed file') # Note that some systems like Solaris don't seem to ever give # an EOF when the child dies. In fact, you can still try to read # from the child_fd -- it will block forever or until TIMEOUT. # For this case, I test isalive() before doing any reading. # If isalive() is false, then I pretend that this is the same as EOF. if not self.isalive(): r, w, e = select.select([self.child_fd], [], [], 0) if not r: self.flag_eof = 1 raise pexpect.EOF( 'End Of File (EOF) in read(). Braindead platform.') timeout = 5.0 r, w, e = select.select([self.child_fd], [], [], timeout) if not r: print('Timeout (%s) exceeded in read().' % str(timeout)) raise pexpect.TIMEOUT('Timeout (%s) exceeded in read().' % str(timeout)) if self.child_fd in r: try: s = self.child_fd.recv(size) # s = os.read(self.child_fd, size) except OSError, e: self.flag_eof = 1 raise pexpect.EOF( 'End Of File (EOF) in read(). Exception style platform.') if s == '': self.flag_eof = 1 raise pexpect.EOF( 'End Of File (EOF) in read(). Empty string style platform.' ) if self.log_file != None: self.log_file.write(s) self.log_file.flush() if hasattr(self, 'console'): self.console.send(s) return s raise pexpect.ExceptionPexpect( 'Reached an unexpected state in read().')
def create_error_msg(exc_info): """Captures and formats exception or error information for logging and debugging. :param tuple exc_info: Exception details from the sys module. :return: The formatted message. .. seealso:: https://realpython.com/the-most-diabolical-python-antipattern/#why-log-the-full-stack-trace """ # Unpack sys.exc_info() to get error information e_type, e_value, _ = exc_info # Instantiate the message container err_msg = "" if e_type in ( pexpect.ExceptionPexpect, pexpect.EOF, pexpect.TIMEOUT, ): # This code retrieves the device's response to pexpect.sendline() # from the state of the spawned object (__str__ from ex). # If you match TIMEOUT or EOF when calling expect or expect_exact, # pexpect does not retain the state of the object for examination afterwards. # https://pexpect.readthedocs.io/en/stable/api/pexpect.html#spawn-class # https://pexpect.readthedocs.io/en/stable/_modules/pexpect/exceptions.html#TIMEOUT # Add a heading to EOF and TIMEOUT errors if pexpect.EOF: err_msg += "EOF reached: Child process exited unexpectedly.\n" elif pexpect.TIMEOUT: err_msg += "Timed-out looking for the expected search string.\n" # Add trace err_msg += pexpect.ExceptionPexpect(e_type).get_trace() # For pexpect.run calls... if "searcher" not in str(e_value): err_msg += (str(e_value).strip("\r\n")) # For pexpect.expect-type calls... else: # Log what was actually found during the pexpect call e_value = "Expected {0}\nFound {1}.".format( str(e_value).split("searcher: ")[1].split( "buffer (last 100 chars):")[0].strip(), str(e_value).split("before (last 100 chars): ")[1].split( "after:")[0].strip()) # Remove any unwanted escape characters here, like backspaces, etc. e_value = e_value.replace("\b", "") err_msg += e_value elif e_type in ( ValueError, RuntimeError, OSError, ): # This application manually raises two types of errors: ValueError and RuntimeError. # Invalid inputs will throw ValueErrors. # Non-zero exit statuses or other issues (e.g., missing credentials, etc.) will throw RuntimeErrors. # OSError captures non-Popen subprocess exceptions. In Python 2.7, subprocess only returns the # reason for a non-zero return code (i.e., the CLI's response) unless the shell option is set to True, # which is unsafe due to potential injections. # https://docs.python.org/2/library/subprocess.html#subprocess.check_output # All other errors and exceptions indicate syntax or semantic programming issues. err_msg += (traceback.format_exc().strip()) return err_msg