Skip to main content

A better library for automating network gear with Cisco-style command line interfaces

Project description

# Sisqo

**Author:** Alex Forster (alex@alexforster.com)<br/>
**License:** BSD 3-Clause<br/>

### Overview

Sisqo is a better library for automating network gear with Cisco-style command line interfaces.

### Features

* Provides a fluid API for parsing `running-config` and `startup-config` into strongly typed hierarchical objects that can be traversed with regex-based searching
* Complete support for VT100-series terminal emulation, guaranteeing that what you see on the command line will also be what you receive from this library
* Automatically handles Cisco-style "more" pagination and prompt matching, allowing for seamless `read()`/`write()` semantics regardless of the target device's terminal settings
* Provides special API support for `enable` authorization
* Runs on any platform that has the OpenSSH binary installed
* Tested against Cisco IOS, Catalyst, Nexus, ASA/PIX, and ASR series devices

### Installation

`pip install sisqo`

**PyPI:** [https://pypi.python.org/pypi/sisqo/2.0.0](https://pypi.python.org/pypi/sisqo/2.0.0)

**Dependencies:**

* `ptyprocess` – a library for launching a subprocess in a pseudo terminal (pty)
* `pyte` – an in memory VTXXX-compatible terminal emulator library
* `wcwidth` – a library for wide-character width calculation

## API Documentation

**Example code:**

```python
from sys import exit

import sisqo

router = sisqo.SSH(host='router.example.com', username='jdoe')

with router:

if not router.connect('password123'):

exit(1) # could not authenticate with the router

if not router.enable('123456'):

exit(2) # could not enable on the router

router.write('show version')
versionInformation = router.read() #= "Cisco IOS Software, C2900 Software (C2900-UNIVERSALK9-M) ..."
print(versionInformation)

runningConfig = router.showRunningConfig()

# hostname router1
# !
# interface GigabitEthernet0/0
# shutdown
# interface GigabitEthernet0/1
# shutdown
# !
# router bgp 12345
# network 55.66.77.88/24
# neighbor 11.22.33.44
# remote-as 54321
# timers 7 21
# neighbor 22.33.44.55
# remote-as 98765
# timers 7 21

routerBGP = runningConfig.findChild('router bgp \d+').value #= "router bgp 12345"
asn = int(routerBGP.split()[-1]) #= 12345

print('BGP neighbors of ASN {}'.format(asn))

bgpNeighbors = bgpConfig.findChild('router bgp \d+').findChildren('neighbor .+')

for neighbor in bgpNeighbors: #= [<neighbor 11.22.33.44>, <neighbor 22.33.44.55>]

ipAddress = neighbor.value #= "neighbor 11.22.33.44"
ipAddress = ipAddress.split()[-1] #= "11.22.33.44"

print("Neighbor: {}".format(ipAddress))
```

### class _SSH_

*Note: this class can be used as a context manager (using a "with" statement)*

* **\_\_init\_\_ ( _username_: str, _host_: str, _port_: int?, _sshConfigFile_: str? )**

Creates an object that initiates an SSH connection as `username` to the provided `host` and `port` (default: *22*).

The OpenSSH client, by default, will obey the system's `/etc/ssh/ssh_config` file as well as the current user's `~/.ssh/config` file. You can provide a path to [a custom ssh_config file](http://man.openbsd.org/ssh_config) using the `sshConfigFile` argument, which will prevent these default configuration files from being considered.

* **_host_: readonly str**

Hostname or IP address to SSH into

* **_port_: readonly int**

Port number to connect to

* **_promptRegex_: str**

Regular Expression used to match shell prompts

* **_moreRegex_: str**

Regular Expression used to match Cisco-style "more" pagination prompts

* **_authenticate_ ( _password_: str?, _passphrase_: str?, _promptCallback_: lambda?, _promptState_: dict? )**

Allows the user to reliably respond to an authentication prompt (`password` and/or private key `passphrase`) if necessary.

This method also provides a convenient way to handle alternative prompts, for situations where something other than a password or passphrase are required (for example, a TOTP multi-factor challenge code).

The `promptCallback` parameter should be a function that responds to the alternative prompt. It will be called repeatedly until it either returns a correct response, or it returns *None* to indicate that it cannot answer the prompt successfully. The signature of the callback is:<br/>
`(prompt: str, state: dict[str, object], logger: logging.Logger) => bool|None`

The `promptState` parameter is a way to pass in persistent state information to the prompt callback via a dictionary. The same dictionary will be passed in for successive calls to `promptCallback`. It is seeded with *password* and *passphrase* properties by the `authenticate` method, corresponding to the provided arguments of the same name.

For example, to try guessing multiple passwords, one could do the following–

```python
from sys import exit
import ssh

state = {
'passwordsToTry': ['cisco', '123456', 'password123']
}

def onPrompt(prompt, state, logger):

if 'password:' not in prompt.lower(): return None

if len(state['passwordsToTry']) == 0: return None

return state['passwordsToTry'].pop()

with sisqo.SSH(host='router.example.com', username='cisco') as router:

if not router.authenticate(promptCallback=onPrompt, promptState=state):

exit(1) # none of the passwords we tried worked

# successfully authenticated using one of the three passwords we tried
router.write('show version')
```

* **_read_ ( _timeout_: int?, _stripPrompt_: bool?, _promptRegex_: re? ): str**

Reads from the target device up to the next prompt, with special handling for Cisco-style "more" pagination. If a prompt cannot be matched in the output, the read operation returns after `timeout` seconds (default: *10*). The `stripPrompt` argument can be used to control whether or not the text of the prompt is returned as part of the read operation (default: *True*). The `promptRegex` argument (default: *None*), if specified, overrides the class's `promptRegex` property.

* **_write_ ( _command_: str, _timeout_: int?, _consumeEcho_: bool? )**

Writes `command` to the target device. This function can optionally suppress the terminal's echoback. If `consumeEcho` is True (the default), this function will implicitly read up to `len(command)` bytes or until `timeout` seconds has passed (default: *10*). When manually responding to password prompts, you should set `consumeEcho` to *False* if the password is not typically echoed back to you as asterisks or otherwise.

*Warning: this function implicitly discards any previously unread data without returning it to the consumer.*

* **_enable_ ( _password_: str ): bool**

Helper function to elevate privileges on the target network gear, with special handling for the "Password" prompt.

*Warning: enable is not supported on certain Cisco-alike operating systems*

* **_showRunningConfig_ ( ): Configuration**

Helper function to retrieve the target device's *running-config* and parse it into a `Configuration` object.

* **_showStartupConfig_ ( ): Configuration**

Helper function to retrieve the target device's *startup-config* and parse it into a `Configuration` object.

*Warning: startup-config is not supported on certain Cisco-alike operating systems*

* **_disconnect_ ( )**

Closes the SSH connection with the target device, if open. Called automatically when exiting a context manager and/or when the object is garbage collected.


### class _Configuration_

*Note: instances of this class are returned from `SSH.showRunningConfig()` and `SSH.showStartupConfig()`*

* **\_\_init\_\_ ( _configString_: str )**

Parses a Cisco configuration file `configString` into a hierarchical, searchable representation of configuration lines.

* **_findChild_ ( _regex_: str ): Line**

Searches the root node of the hierarchy for the first line that matches the provided `regex`.

* **_findChildren_ ( _regex_: str ): list[Line]**

Searches the root node of the hierarchy for lines that match the provided `regex`.


### class _Line_

*Note: instances of this class are returned from `Configuration.findChild()` and `Configuration.findChildren()`*

* **\_\_init\_\_ ( _number_: int, _indent_: str, _value_: str )**

Creates an in-memory representation of a single line of a Cisco configuration file.

* **_findChild_ ( _regex_: str ): Line**

Searches the children of this node for the first line that matches the provided `regex` and returns that line.

* **_findChildren_ ( _regex_: str ): list[Line]**

Searches the children of this node for lines that match the provided `regex` and returns a list of matching lines.

* **property _value_: str**

Text of this configuration line, stripped of indentation

* **_parent_: Line**

Hierarchical parent of this configuration line

* **_children_: list[Line]**

List of hierarchical children of this configuration line

* **_lineNumber_: int**

Line number from the original configuration text

* **_indentation_: readonly int**

Indentation level of this configuration line

* **_depth_: readonly int**

Depth of this line in the configuration hierarchy


### class _NotConnectedError_ : _Exception_

Thrown when certain operations are tried on an `SSH` instance which is not connected.


### class _NotAuthenticatedError_ : _Exception_

Thrown when certain operations are tried on an `SSH` instance which has not yet authenticated.


### class _AlreadyAuthenticatedError_ : _Exception_

Thrown when authentication is tried on an `SSH` instance which has already authenticated.


### class _BadAuthenticationError_ : _Exception_

Thrown when authentication fails.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sisqo-2.0.0.tar.gz (10.3 kB view hashes)

Uploaded Source

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page