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
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
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
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
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
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
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
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

Below is an example script that will run concurrently across multiple network devices:

from CiscoAutomationFramework.ThreadLib import SSH, start_threads


class MyDevice(SSH):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.interfaces_connected = []

    def during_login(self, ssh):
        # Code here will run every time no matter what
        for line in ssh.send_command_get_output('show interface status'):
            if 'connected' in line and not 'notconn' in line:
                self.interfaces_connected.append(line.split()[0])

    def secondary_action(self, ssh):
        """
        If there is some secondary action needed to be performed, code it here
        If there is nothing you need to do you DO NOT need to define this method

        To execute this, when starting threads via the start_threads method set
        perform_secondary_action=True
        """
        pass

    def post_secondary_action(self, ssh):
        """
        If you need to something post the secondary action taking place, code it here.
        an example would be re gathering data or saving config

        If there is nothing you need to do you DO NOT need to define this method

        To execute this, when starting threads via the start_threads method set
        perform_secondary_action=True
        """
        pass


if __name__ == '__main__':

    username = 'myusername'
    password = 'mypassword'

    # replace with IP addresses of network devices on your network
    ips = ['192.168.1.1', '192.168.2.1', '192.168.3.1', '192.168.4.1', '192.168.5.1', '192.168.6.1']
    threads = start_threads(MyDevice, ips, username, password, wait_for_threads=True)
    for thread in threads:
        print(thread.hostname, len(thread.interfaces_connected))