Interacting with Multiple Network Devices at the same time

CiscoAutomationFramework provides a way of running the same code on multiple devices concurrently. It takes into account situations where you will have a mix of IOS and Nexus and when you dont care, providing object to inherit from and override in both circumstances.

class CiscoAutomationFramework.ThreadLib.SSH(ip, username, password, enable_password=None, perform_secondary_action=False, **kwargs)

Base SSH object that you can inherit from when building a threaded script that will run across the network.

To build a script you must subclass this object and override the methods that are documented.

Method Execution Order The object logs into the device during_login is executed if perform_secondary_action is set to True secondary_action is executed and then post_secondary_action

during_login(ssh)

Every time this object runs against a device this method will be run. Override this method and add logic that needs to be run every time your object runs against a device.

I find I often add logic here that gathers data and does not modify configuration.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

post_secondary_action(ssh)

This method will run only if perform_secondary_action is set to True and AFTER secondary_action has completed.

I often find this is useful for containing logic that re gathers data or saves configuration.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

run() None

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.

secondary_action(ssh)

This method will run only if perform_secondary_action is set to True.

I often find this is useful for containing logic that modifies configuration that only needs to be run if a certain condition is met.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

class CiscoAutomationFramework.ThreadLib.SSHSplitDeviceType(ip, username, password, enable_password=None, perform_secondary_action=False, **kwargs)

Create a script by inheriting from this object if you are running the same script against both Nexus and IOS devices but the command are different on each platform or the sequence is different. Out of the box it will run the functions prepended with ios on ios and iosxe devices and functions prepended with nexus on nxos devices. Because it is implied that this script will be run against a mix of nexus and ios you must define the functions regardless if they will be used or not when inheriting from this object

during_login(ssh)

Every time this object runs against a device this method will be run. Override this method and add logic that needs to be run every time your object runs against a device.

I find I often add logic here that gathers data and does not modify configuration.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

abstract ios_during_login(ssh)

Every time this object runs against a device that is detected to be NOT Nexus this method will be run. Override this method and add logic that needs to be run every time your object runs against a device.

Must be overridden when inheriting from this object regardless if your script will run against this device type

I find I often add logic here that gathers data and does not modify configuration.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

abstract ios_post_secondary_action(ssh)

This method will run only if perform_secondary_action is set to True and AFTER secondary_action has completed and the device is detected to be NOT Nexus.

Must be overridden when inheriting from this object regardless if your script will run against this device type.

I often find this is useful for containing logic that re gathers data or saves configuration.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

abstract ios_secondary_action(ssh)

This method will run only if perform_secondary_action is set to True and the device is detected as not Nexus.

Must be overridden when inheriting from this object regardless if your script will run against this device type

I often find this is useful for containing logic that modifies configuration that only needs to be run if a certain condition is met.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

abstract nexus_during_login(ssh)

Every time this object runs against a device that is detected to be running Nexus this method will be run. Override this method and add logic that needs to be run every time your object runs against a device.

Must be overridden when inheriting from this object regardless if your script will run against this device type

I find I often add logic here that gathers data and does not modify configuration.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

abstract nexus_post_secondary_action(ssh)

This method will run only if perform_secondary_action is set to True and AFTER secondary_action has completed and the device is detected to be Nexus.

Must be overridden when inheriting from this object regardless if your script will run against this device type.

I often find this is useful for containing logic that re gathers data or saves configuration.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

abstract nexus_secondary_action(ssh)

This method will run only if perform_secondary_action is set to True and the device is detected Nexus.

Must be overridden when inheriting from this object regardless if your script will run against this device type.

I often find this is useful for containing logic that modifies configuration that only needs to be run if a certain condition is met.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

post_secondary_action(ssh)

This method will run only if perform_secondary_action is set to True and AFTER secondary_action has completed.

I often find this is useful for containing logic that re gathers data or saves configuration.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

secondary_action(ssh)

This method will run only if perform_secondary_action is set to True.

I often find this is useful for containing logic that modifies configuration that only needs to be run if a certain condition is met.

Parameters:

ssh (CiscoFirmware) – SSH object is provided to this method for interacting with the end device

Returns:

Nothing

class CiscoAutomationFramework.ThreadLib.ReadOnlySSH(ip, username, password, enable_password=None, perform_secondary_action=False, **kwargs)

Use this class to guarantee a script will not be able to change any configuration on a device

SSH object that inspects all commands sent to the device and if it detects “configuration terminal” or any variation such as “conf t” etc. It also wont let you reload the device or write erase the configuration it will throw an exception not allowing you to enter config terminal. This inspection happens in the transport engine so even integrated ssh methods such as cli_to_config_mode will throw the exception.

It is functionally equivalent to the “SSH” class, but wont let you into config mode or do any dangerous commands.

CiscoAutomationFramework.ThreadLib.start_threads(object, ips, username, password, enable_password=None, perform_secondary_action=False, wait_for_threads=False, **kwargs)

This helper function is a quick and easy way to start your threads. Gives you an option for waiting for threads to complete also. It will return the thread objects from the function for further processing by the main thread.

Parameters:
  • object ([SSH, SSHSplitDeviceType]) – Object you have written to perform your task. Must be inherted from SSH or SSHSplitDeviceType

  • ips (list[str]) – List of IP addresses or hostnames to run your worker object against

  • username (str) – Username to login with

  • password (str) – Password for user

  • enable_password (str) – Enable password if user does not have rights to privilege exec mode without enable password

  • perform_secondary_action (bool) – True if you want to run the secondary_action method against device

  • wait_for_threads (bool) – Wait for all threads to complete before returning

  • kwargs – Keyword arguments to be passed to your object. If your child class accepts additional arguments, pass them to the object via keyword arguments here.

Returns:

List of threads either running or completed depending on if wait_for_threads is True/False

Return type:

list[SSH, SSHSplitDeviceType, type(object)]

Example Scripts

An example script that will run concurrently across multiple network devices
 1from CiscoAutomationFramework.ThreadLib import SSH, start_threads
 2from CiscoAutomationFramework.util import column_print
 3
 4
 5class NetworkDevice(SSH):
 6
 7    def __init__(self, *args, **kwargs):
 8        super().__init__(*args, **kwargs)
 9        self.raw_clock_time = []
10
11    @property
12    def clock_time(self):
13        return ' '.join(x for x in self.raw_clock_time if x != '')
14
15    def during_login(self, ssh):
16        self.raw_clock_time = ssh.send_command_get_output('show clock')[1:-1]
17
18
19if __name__ == '__main__':
20    from getpass import getpass
21
22    username = input('Enter Username: ')
23    password = getpass(f'Enter Password for {username}: ')
24    ips = ['10.204.10.12', '10.204.10.13']  # replace with your IP addresses or get them programmatically
25
26    print('Reaching out to network devices!')
27    threads = start_threads(NetworkDevice, ips, username, password)
28
29    print('Waiting for threads to complete.')
30    for t in threads:
31        t.join()
32
33    data = [['Hostname', 'IP', 'Time']]
34    for thread in threads:
35        data.append([thread.hostname, thread.ip, thread.clock_time])
36    column_print(data, separator_char='-')