def __init__(self, **kwargs: Any): super().__init__(**kwargs) if self.transport_name in ASYNCIO_TRANSPORTS: raise ScrapliValueError( "provided transport is *not* an sync transport, must use an sync transport with" " the (sync)Driver(s)") self.channel = Channel( transport=self.transport, base_channel_args=self._base_channel_args, )
def __init__(self, **kwargs: Any) -> None: """ Scrape Object Scrape is the base class for NetworkDriver, and subsequent platform specific drivers (i.e. IOSXEDriver). Scrape can be used on its own and offers a semi-pexpect like experience in that it doesn't know or care about privilege levels, platform types, and things like that. *Note* most arguments passed to Scrape do not actually get assigned to the scrape object itself, but instead are used to construct the Transport and Channel classes that Scrape relies on, see Transport and Channel docs for details. Args: kwargs: Keyword arguments to pass to `ScrapeBase` -- see `ScrapeBase` for available args Returns: N/A # noqa: DAR202 Raises: TransportPluginError: if attempting to use an asyncio transport plugin """ super().__init__(**kwargs) if self._transport in ASYNCIO_TRANSPORTS: raise TransportPluginError( f"Attempting to use transport type {self._transport} with a sync driver, " "must use a non-asyncio transport") self.channel = Channel(transport=self.transport, **self.channel_args)
def open(self): # Overriding "normal" network driver open method as we don't need to worry about disable # paging or pre login handles; ignoring this makes the mocked file read/write pieces simpler self.transport = self.transport_class(**self.transport_args) self.transport.open() self.channel = Channel(self.transport, **self.channel_args) self._current_priv_level = self.privilege_levels[ self.default_desired_privilege_level]
class Driver(BaseDriver): def __init__(self, **kwargs: Any): super().__init__(**kwargs) if self.transport_name in ASYNCIO_TRANSPORTS: raise ScrapliValueError( "provided transport is *not* an sync transport, must use an sync transport with" " the (sync)Driver(s)") self.channel = Channel( transport=self.transport, base_channel_args=self._base_channel_args, ) def __enter__(self) -> "Driver": """ Enter method for context manager Args: N/A Returns: Driver: opened Driver object Raises: N/A """ self.open() return self def __exit__( self, exception_type: Optional[Type[BaseException]], exception_value: Optional[BaseException], traceback: Optional[TracebackType], ) -> None: """ Exit method to cleanup for context manager Args: exception_type: exception type being raised exception_value: message from exception being raised traceback: traceback from exception being raised Returns: None Raises: N/A """ self.close() def open(self) -> None: """ Open the scrapli connection Args: N/A Returns: None Raises: N/A """ self._pre_open_closing_log(closing=False) self.transport.open() self.channel.open() if self.transport_name in ("system", ) and not self.auth_bypass: self.channel.channel_authenticate_ssh( auth_password=self.auth_password, auth_private_key_passphrase=self.auth_private_key_passphrase, ) if (self.transport_name in ( "telnet", "asynctelnet", ) and not self.auth_bypass): self.channel.channel_authenticate_telnet( auth_username=self.auth_username, auth_password=self.auth_password) if self.on_open: self.on_open(self) self._post_open_closing_log(closing=False) def close(self) -> None: """ Close the scrapli connection Args: N/A Returns: None Raises: N/A """ self._pre_open_closing_log(closing=True) if self.on_close: self.on_close(self) self.transport.close() self.channel.close() self._post_open_closing_log(closing=True) def commandeer(self, conn: "Driver", execute_on_open: bool = True) -> None: """ Commandeer an existing connection Used to "take over" or "commandeer" a connection. This method accepts a second scrapli conn object and "steals" the transport from this connection and uses it for the current instance. The primary reason you would want this is to use a `GenericDriver` to connect to a console server and then to "commandeer" that connection and convert it to a "normal" network driver connection type (i.e. Junos, EOS, etc.) once connected to the network device (via the console server). Right now closing the connection that "commandeers" the initial connection will *also close the original connection* -- this is because we are re-using the transport in this new conn. In the future perhaps this will change to *not* close the original connection so users can handle any type of cleanup operations that need to happen on the original connection. Alternatively, you can simply continue using the "original" connection to close things for yourself or do any type of clean up work (just dont close the commandeering connection!). Args: conn: connection to commandeer execute_on_open: execute the `on_open` function of the current object once the existing connection has been commandeered Returns: None Raises: N/A """ original_logger = conn.logger original_transport = conn.transport original_transport_logger = conn.transport.logger original_channel_logger = conn.channel.logger original_channel_channel_log = conn.channel.channel_log self.logger = original_logger self.channel.logger = original_channel_logger self.channel.transport = original_transport self.transport = original_transport self.transport.logger = original_transport_logger if original_channel_channel_log is not None: # if the original connection had a channel log we also commandeer that; note that when # the new connection is closed this will also close the channel log; see docstring. self.channel.channel_log = original_channel_channel_log if execute_on_open is True and self.on_open is not None: self.on_open(self)
class Driver(BaseDriver): def __init__(self, **kwargs: Any): super().__init__(**kwargs) if self.transport_name in ASYNCIO_TRANSPORTS: raise ScrapliValueError( "provided transport is *not* an sync transport, must use an sync transport with" " the (sync)Driver(s)") self.channel = Channel( transport=self.transport, base_channel_args=self._base_channel_args, ) def __enter__(self) -> "Driver": """ Enter method for context manager Args: N/A Returns: Driver: opened Driver object Raises: N/A """ self.open() return self def __exit__( self, exception_type: Optional[Type[BaseException]], exception_value: Optional[BaseException], traceback: Optional[TracebackType], ) -> None: """ Exit method to cleanup for context manager Args: exception_type: exception type being raised exception_value: message from exception being raised traceback: traceback from exception being raised Returns: None Raises: N/A """ self.close() def open(self) -> None: """ Open the scrapli connection Args: N/A Returns: None Raises: N/A """ self._pre_open_closing_log(closing=False) self.transport.open() if self.transport_name in ("system", ) and not self.auth_bypass: self.channel.channel_authenticate_ssh( auth_password=self.auth_password, auth_private_key_passphrase=self.auth_private_key_passphrase, ) if (self.transport_name in ( "telnet", "asynctelnet", ) and not self.auth_bypass): self.channel.channel_authenticate_telnet( auth_username=self.auth_username, auth_password=self.auth_password) if self.on_open: self.on_open(self) self._post_open_closing_log(closing=False) def close(self) -> None: """ Close the scrapli connection Args: N/A Returns: None Raises: N/A """ self._pre_open_closing_log(closing=True) if self.on_close: self.on_close(self) if self.channel.channel_log: self.channel.channel_log.close() self.transport.close() self._post_open_closing_log(closing=True)
def __init__( self, host: str = "", port: int = 22, auth_username: str = "", auth_password: str = "", auth_private_key: str = "", auth_strict_key: bool = True, auth_bypass: bool = False, timeout_socket: int = 5, timeout_transport: int = 10, timeout_ops: int = 30, timeout_exit: bool = True, keepalive: bool = False, keepalive_interval: int = 30, keepalive_type: str = "network", keepalive_pattern: str = "\005", comms_prompt_pattern: str = r"^[a-z0-9.\-@()/:]{1,48}[#>$]\s*$", comms_return_char: str = "\n", comms_ansi: bool = False, ssh_config_file: Union[str, bool] = False, ssh_known_hosts_file: Union[str, bool] = False, on_open: Optional[Callable[..., Any]] = None, on_close: Optional[Callable[..., Any]] = None, transport: str = "system", transport_options: Optional[Dict[str, Any]] = None, ): """ Scrape Object Scrape is the base class for NetworkDriver, and subsequent platform specific drivers (i.e. IOSXEDriver). Scrape can be used on its own and offers a semi-pexpect like experience in that it doesn't know or care about privilege levels, platform types, and things like that. *Note* most arguments passed to Scrape do not actually get assigned to the scrape object itself, but instead are used to construct the Transport and Channel classes that Scrape relies on, see Transport and Channel docs for details. Args: host: host ip/name to connect to port: port to connect to auth_username: username for authentication auth_private_key: path to private key for authentication auth_password: password for authentication auth_strict_key: strict host checking or not -- applicable for system ssh driver only auth_bypass: bypass ssh key or password auth for devices without authentication, or that have auth prompts after ssh session establishment. Currently only supported on system transport; ignored on other transports timeout_socket: timeout for establishing socket in seconds timeout_transport: timeout for ssh|telnet transport in seconds timeout_ops: timeout for ssh channel operations timeout_exit: True/False close transport if timeout encountered. If False and keepalives are in use, keepalives will prevent program from exiting so you should be sure to catch Timeout exceptions and handle them appropriately keepalive: whether or not to try to keep session alive keepalive_interval: interval to use for session keepalives keepalive_type: network|standard -- 'network' sends actual characters over the transport channel. This is useful for network-y type devices that may not support 'standard' keepalive mechanisms. 'standard' attempts to use whatever 'standard' keepalive mechanisms are available in the selected transport mechanism. Check the transport documentation for details on what is supported and/or how it is implemented for any given transport driver keepalive_pattern: pattern to send to keep network channel alive. Default is u'\005' which is equivalent to 'ctrl+e'. This pattern moves cursor to end of the line which should be an innocuous pattern. This will only be entered *if* a lock can be acquired. This is only applicable if using keepalives and if the keepalive type is 'network' comms_prompt_pattern: raw string regex pattern -- preferably use `^` and `$` anchors! this is the single most important attribute here! if this does not match a prompt, scrapli will not work! IMPORTANT: regex search uses multi-line + case insensitive flags. multi-line allows for highly reliably matching for prompts however we do NOT strip trailing whitespace for each line, so be sure to add '\\s?' or similar if your device needs that. This should be mostly sorted for you if using network drivers (i.e. `IOSXEDriver`). Lastly, the case insensitive is just a convenience factor so i can be lazy. comms_return_char: character to use to send returns to host comms_ansi: True/False strip comms_ansi characters from output ssh_config_file: string to path for ssh config file, True to use default ssh config file or False to ignore default ssh config file ssh_known_hosts_file: string to path for ssh known hosts file, True to use default known file locations. Only applicable/needed if `auth_strict_key` is set to True on_open: callable that accepts the class instance as its only argument. this callable, if provided, is executed immediately after authentication is completed. Common use cases for this callable would be to disable paging or accept any kind of banner message that prompts a user upon connection on_close: callable that accepts the class instance as its only argument. this callable, if provided, is executed immediately prior to closing the underlying transport. Common use cases for this callable would be to save configurations prior to exiting, or to logout properly to free up vtys or similar. transport: system|telnet or a plugin -- type of transport to use for connection system uses system available ssh (/usr/bin/ssh) ssh2 uses ssh2-python *has been migrated to a plugin paramiko uses... paramiko *has been migrated to a plugin telnet uses telnetlib choice of driver depends on the features you need. in general system is easiest as it will just 'auto-magically' use your ssh config file ('~/.ssh/config' or '/etc/ssh/config_file'). ssh2 is very very fast as it is a thin wrapper around libssh2 however it is slightly feature limited. paramiko is slower than ssh2, but has more features built in (though scrapli does not expose/support them all). transport_options: dictionary of options to pass to selected transport class; see docs for given transport class for details of what to pass here Returns: N/A # noqa: DAR202 Raises: N/A """ # create a dict of all "initialization" args for posterity and for passing to Transport # and Channel objects self._initialization_args: Dict[str, Any] = {} self._setup_host(host=host, port=port) self._setup_auth( auth_username=auth_username, auth_password=auth_password, auth_private_key=auth_private_key, auth_strict_key=auth_strict_key, auth_bypass=auth_bypass, ) self._setup_timeouts( timeout_socket=timeout_socket, timeout_transport=timeout_transport, timeout_ops=timeout_ops, timeout_exit=timeout_exit, ) self._setup_keepalive( keepalive=keepalive, keepalive_type=keepalive_type, keepalive_interval=keepalive_interval, keepalive_pattern=keepalive_pattern, ) self._setup_comms( comms_prompt_pattern=comms_prompt_pattern, comms_return_char=comms_return_char, comms_ansi=comms_ansi, ) self._setup_callables(on_open=on_open, on_close=on_close) if transport not in ("system", "telnet"): LOG.info(f"Non-core transport `{transport}` selected") self._transport = transport if transport != "telnet": self._setup_ssh_args(ssh_config_file=ssh_config_file, ssh_known_hosts_file=ssh_known_hosts_file) self._initialization_args["transport_options"] = transport_options self.transport_class, self.transport_args = self._transport_factory( transport=transport) # so transport drivers don't need to support `transport_options` as an argument, if no # transport options provided, do nothing, otherwise add this to the args we ship to the # transport class if transport_options is not None: self.transport_args["transport_options"] = transport_options self.transport = self.transport_class(**self.transport_args) self.channel_args: Dict[str, Any] = {} for arg in CHANNEL_ARGS: if arg == "transport": continue self.channel_args[arg] = self._initialization_args.get(arg) self.channel = Channel(transport=self.transport, **self.channel_args)