Saturday, October 6, 2018

Netmiko - Pyhon Library for SSH mgmt

open-source Python library that simplifies SSH management to network devices. The library is based on the Paramiko SSH library and is named Netmiko.
You can find the library at https://github.com/ktbyers/netmiko and the latest released version of the software can be downloaded here.
The purposes of the library are the following:
  • Successfully establish an SSH connection to the device
  • Simplify the execution of show commands and the retrieval of output data
  • Simplify execution of configuration commands including possibly commit actions
  • Do the above across a broad set of networking vendors and platforms

Netmiko has support for the following platforms:
  • Cisco IOS
  • Cisco IOS-XE
  • Cisco ASA
  • Cisco NX-OS
  • Cisco IOS-XR
  • Cisco WLC (limited testing)
  • Arista EOS
  • HP ProCurve
  • HP Comware (limited testing)
  • Juniper Junos
  • Brocade VDX (limited testing)
  • F5 LTM (experimental)
  • Huawei (limited testing)
As with anything involving programming, test extensively in your own environment and for your own specific use cases. It is your responsibility to know what you are doing and to have good testing processes in place.
Example 1, a simple SSH session to a Cisco router that executes the 'show ip int brief' command.
First, I must import the ConnectHandler factory function from Netmiko. This factory function selects the correct Netmiko class based upon the device_type. I then define a network device dictionary consisting of a device_type, ip, username, and password.
 
>>> from netmiko import ConnectHandler

>>> cisco_881 = {
...   'device_type': 'cisco_ios',
...   'ip': '10.10.10.227',
...   'username': 'pyclass',
...   'password': 'password',
... } 

At this point, I should be able to connect to the device.
Notice above that I have specified the device_type as 'cisco_ios'. The supported device_type's are cisco_ios, cisco_xe, cisco_asa, cisco_nxos, cisco_xr, cisco_wlc_ssh, arista_eos, hp_procurve, hp_comware, huawei, f5_ltm, juniper, and brocade_vdx.

Now in order to connect all I need to do is call the ConnectHandler factory function and pass in my earlier defined device dictionary:
 
>>> net_connect = ConnectHandler(**cisco_881)
SSH connection established to 10.10.10.227:22
Interactive SSH session established 

Alternatively, I could just call the ConnectHandler function directly and not use a dictionary (as follows):
 
>>> net_connect = ConnectHandler(device_type='cisco_ios', ip='10.10.10.227', username='pyclass', password='password') 

Now at this point we have an SSH connection.
 I can verify this by executing the .find_prompt() method
 
>>> net_connect.find_prompt()
u'pynet-rtr1#' 

I can also send commands down the SSH channel and receive the output back. 
Here, I use the .send_command() method to send the 'show ip int brief' command:
 
>>> output = net_connect.send_command("show ip int brief")
>>> print output
Interface                  IP-Address      OK? Method Status                Protocol
FastEthernet0              unassigned      YES unset  down                  down    
FastEthernet1              unassigned      YES unset  down                  down    
FastEthernet2              unassigned      YES unset  down                  down    
FastEthernet3              unassigned      YES unset  down                  down    
FastEthernet4              10.220.88.20    YES NVRAM  up                    up      
Vlan1                      unassigned      YES unset  down                  down   

Let's also try to make a configuration change to this router. First, let's look at the current logging configuration:
 
>>> output = net_connect.send_command("show run | inc logging")
>>> print output
logging buffered 8880
no logging console 

Now in order to make configuration changes, I create a list of configuration commands that I want to execute. This could be a single command or multiple commands.
 
>>> config_commands = ['logging buffered 19999'] 

I then execute the send_config_set() method. This method will enter configuration mode, execute the commands, and then exit configuration mode (note, there will be some exceptions to this behavior depending on the platform--for example, IOS-XR will not exit configuration mode due to pending changes).
 
>>> output = net_connect.send_config_set(config_commands)
>>> print output
config term
Enter configuration commands, one per line.  End with CNTL/Z.
pynet-rtr1(config)#logging buffered 19999
pynet-rtr1(config)#end
pynet-rtr1#  

I can then verify my change:
 
>>> output = net_connect.send_command("show run | inc logging")
>>> print output
logging buffered 19999
no logging console   

Example 2, executing 'show arp' on a set of networking devices consisting of different vendors and platforms.
netmiko show arp image
First, I need to define the networking devices (real IP addresses and passwords have been hidden):
 
>>> from netmiko import ConnectHandler
>>> from datetime import datetime

>>> cisco_881 = {
...     'device_type': 'cisco_ios',
...     'ip':   '10.10.10.227',
...     'username': 'pyclass',
...     'password': 'password',
...     'verbose': False,
... }
>>> 
>>> cisco_asa = {
...     'device_type': 'cisco_asa',
...     'ip': '10.10.10.10',
...     'username': 'admin',
...     'password': 'password',
...     'secret': 'secret',
...     'verbose': False,
... }
>>> 
>>> cisco_xrv = {
...     'device_type': 'cisco_xr',
...     'ip':   '10.10.10.227',
...     'username': 'admin1',
...     'password': 'password',
...     'port': 9722,               # there is a firewall performing NAT in front of this device
...     'verbose': False,
... }
>>> 
>>> arista_veos_sw = {
...     'device_type': 'arista_eos',
...     'ip':   '10.10.10.227',
...     'username': 'admin1',
...     'password': 'password',
...     'port': 8522,               # there is a firewall performing NAT in front of this device
...     'verbose': False,
... }
>>> 
>>> hp_procurve = {
...     'device_type': 'hp_procurve',
...     'ip':   '10.10.10.227',
...     'username': 'admin',
...     'password': 'password',
...     'port': 9922,               # there is a firewall performing NAT in front of this device
...     'verbose': False,
... }
>>> 
>>> juniper_srx = {
...     'device_type': 'juniper',
...     'ip':   '10.10.10.227',
...     'username': 'pyclass',
...     'password': 'password',
...     'port': 9822,               # there is a firewall performing NAT in front of this device
...     'verbose': False,
... } 

Next, I need to create a Python list that includes all of these devices: 

>>> all_devices = [cisco_881, cisco_asa, cisco_xrv, arista_veos_sw, hp_procurve, juniper_srx] 

Now, I will create a for loop that iterates over all of these devices. Each time through the loop: the code will connect to the device, execute the 'show arp' command, and then display the output. I will also keep track of the time that it takes for the code execute.
Note, I have removed a couple of public IP addresses from the arp output. Additionally, I have changed the output slightly (so that you can more clearly see what I pasted in versus the output back from the devices).
 
>>> 
>>> start_time = datetime.now()
>>> for a_device in all_devices:
...     net_connect = ConnectHandler(**a_device)
...     output = net_connect.send_command("show arp")
...     print "\n\n>>>>>>>>> Device {0} <<<<<<<<<".format(a_device['device_type'])
...     print output
...     print ">>>>>>>>> End <<<<<<<<<"
... 
>>> end_time = datetime.now()
>>> 
>>> total_time = end_time - start_time 
 
Here is the output from the for loop (i.e. all of the "show arp" output): 

>>>>>>>>> Device cisco_ios <<<<<<<<<
Protocol  Address          Age (min)  Hardware Addr   Type   Interface
Internet  10.220.88.1             4   001f.9e92.16fb  ARPA   FastEthernet4
Internet  10.220.88.20            -   c89c.1dea.0eb6  ARPA   FastEthernet4
Internet  10.220.88.100          10   f0ad.4e01.d933  ARPA   FastEthernet4
>>>>>>>>> End <<<<<<<<<


>>>>>>>>> Device cisco_asa <<<<<<<<<
    inside 10.220.88.100 f0ad.4e01.d933 251
    inside 10.220.88.31 5254.0001.3737 311
    inside 10.220.88.39 6464.9be8.08c8 361
    inside 10.220.88.30 5254.0092.13bb 1451
    inside 10.220.88.10 0018.fe1e.b020 1700
    inside 10.220.88.29 5254.0098.69b6 3071
    inside 10.220.88.21 1c6a.7aaf.576c 3431
    inside 10.220.88.28 5254.00ee.446c 5111
    inside 10.220.88.38 0001.00ff.0001 8231
    inside 10.220.88.20 c89c.1dea.0eb6 11081
    inside 10.220.88.40 001c.c4bf.826a 12671
>>>>>>>>> End <<<<<<<<<


>>>>>>>>> Device cisco_xr <<<<<<<<<

Wed Dec 30 00:04:47.641 UTC

-------------------------------------------------------------------------------
0/0/CPU0
-------------------------------------------------------------------------------
Address         Age        Hardware Addr   State      Type  Interface
10.220.88.1     00:04:19   001f.9e92.16fb  Dynamic    ARPA  GigabitEthernet0/0/0/0
10.220.88.10    00:28:28   0018.fe1e.b020  Dynamic    ARPA  GigabitEthernet0/0/0/0
10.220.88.28    02:54:39   5254.00ee.446c  Dynamic    ARPA  GigabitEthernet0/0/0/0
10.220.88.29    01:37:22   5254.0098.69b6  Dynamic    ARPA  GigabitEthernet0/0/0/0
10.220.88.30    00:38:28   5254.0092.13bb  Dynamic    ARPA  GigabitEthernet0/0/0/0
10.220.88.31    02:19:09   5254.0001.3737  Dynamic    ARPA  GigabitEthernet0/0/0/0
10.220.88.38    -          0001.00ff.0001  Interface  ARPA  GigabitEthernet0/0/0/0
10.220.88.39    00:06:08   6464.9be8.08c8  Dynamic    ARPA  GigabitEthernet0/0/0/0
>>>>>>>>> End <<<<<<<<<


>>>>>>>>> Device arista_eos <<<<<<<<<
Address         Age (min)  Hardware Addr   Interface
10.220.88.1             0  001f.9e92.16fb  Vlan1, Ethernet1
10.220.88.21            0  1c6a.7aaf.576c  Vlan1, not learned
10.220.88.28            0  5254.00ee.446c  Vlan1, not learned
10.220.88.29            0  5254.0098.69b6  Vlan1, not learned
10.220.88.30            0  5254.0092.13bb  Vlan1, not learned
10.220.88.38            0  0001.00ff.0001  Vlan1, not learned
>>>>>>>>> End <<<<<<<<<


>>>>>>>>> Device hp_procurve <<<<<<<<<

 IP ARP table

  IP Address      MAC Address       Type    Port
  --------------- ----------------- ------- ----
  10.220.88.1     001f9e-9216fb     dynamic 19  
 

>>>>>>>>> End <<<<<<<<<


>>>>>>>>> Device juniper <<<<<<<<<

MAC Address       Address         Name                      Interface           Flags
00:1f:9e:92:16:fb 10.220.88.1     10.220.88.1               vlan.0              none
00:19:e8:45:ce:80 10.220.88.22    10.220.88.22              vlan.0              none
f0:ad:4e:01:d9:33 10.220.88.100   10.220.88.100             vlan.0              none
Total entries: 3

>>>>>>>>> End <<<<<<<<<


>>> print total_time
0:00:44.791650
>>>  
As you can see it took almost 45 seconds for the above for-loop to execute. This could be significantly improved by executing the SSH sessions in parallel, for an example of this see the code here

Some Netmiko methods that are generally available to you:
  • net_connect.config_mode() -- Enter into config mode
  • net_connect.check_config_mode() -- Check if you are in config mode, return a boolean
  • net_connect.exit_config_mode() -- Exit config mode
  • net_connect.clear_buffer() -- Clear the output buffer on the remote device
  • net_connect.enable() -- Enter enable mode
  • net_connect.exit_enable_mode() -- Exit enable mode
  • net_connect.find_prompt() -- Return the current router prompt
  • net_connect.commit(arguments) -- Execute a commit action on Juniper and IOS-XR
  • net_connect.disconnect() -- Close the SSH connection
  • net_connect.send_command(arguments) -- Send command down the SSH channel, return output back
  • net_connect.send_config_set(arguments) -- Send a set of configuration commands to remote device
  • net_connect.send_config_from_file(arguments) -- Send a set of configuration commands loaded from a file
Note:
 I specified 'arguments' in the above methods if arguments are required or reasonably common. Some of the other methods also support arguments, but you typically wouldn't need them.

No comments:

Post a Comment

Thank you for your comment. Will try to react as soon as possible.

Regards,

Networ King