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
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='-')