Skip to content

Instantly share code, notes, and snippets.

@vnetman
Created March 21, 2019 03:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vnetman/becf9a989fe06053af38e8d6032e26e1 to your computer and use it in GitHub Desktop.
Save vnetman/becf9a989fe06053af38e8d6032e26e1 to your computer and use it in GitHub Desktop.
Netmiko script to place an interface in a VLAN based on its description
#!/usr/bin/env python3
import sys
import re
import logging
import ipaddress
import netrc
from netmiko import Netmiko
# Netmiko script to place different interfaces in different VLANs based
# on the interface descriptions
# Login credentials are stored in ~/.netrc
#
# .netrc entries look like this:
#
# machine 192.168.1.110 login my_login_id password l0gin_pa$$w0rd account ena6le_pa$$w0rd
# machine access_switch login my_login_id password l0gin_pa$$w0rd
#
# In the first example above, the machine is specified by its IP address.
# The enable password is given against the 'account' field (which is really a
# hack).
# In the second example above, the machine is specified by its name, and it
# has no enable password (hence the absence of the 'account' field).
#
# Ensure .netrc is protected by appropriate r/w permissions
DESIRED_SETTINGS = {
# This is the device to be configured. Credentials must be specified in
# ~/.netrc as described above
'device' : '192.168.1.110',
# Map interface descriptions to VLAN Ids
'desc_to_vlan_map': {'vendor lab' : 119,
'hardware lab': 120,
'sensors' : 121,
'lobby' : 122}}
def main():
'''Main program logic
'''
logging.basicConfig(level=logging.INFO, # Change INFO to DEBUG for debugging
format='%(asctime)s %(levelname)s: %(message)s',
stream=sys.stdout)
device = make_connection_to_device()
# Get the list of interfaces currently configured
current_interface_list = get_interface_list(device)
if not current_interface_list:
print('Failed to read current interface list, aborting',
file=sys.stderr)
device.disconnect()
sys.exit(-1)
# The list of commands to be pushed
cfg_commands = []
for this_if in current_interface_list:
logging.debug('interface found: %s', this_if)
this_if_cfg = get_current_interface_running_config(device, this_if)
for this_cfg_line in this_if_cfg:
if not this_cfg_line.startswith(' description'):
# uninteresting line
continue
logging.debug('\"%s\" is a description line', this_cfg_line)
for pattern in DESIRED_SETTINGS['desc_to_vlan_map']:
vlan_id = DESIRED_SETTINGS['desc_to_vlan_map'][pattern]
if (' description ' + pattern).lower() != this_cfg_line.lower():
# description doesn't match this pattern
continue
logging.info('Will place %s in vlan %d because of description '
'match \"%s\"', this_if, vlan_id, pattern)
cfg_commands.append('interface ' + this_if)
cfg_commands.append(' switchport access vlan {}'.format(vlan_id))
cfg_commands.append(' switchport mode access')
print('The following configuration will be applied:')
print('------------------------------')
for _ in cfg_commands:
print(_)
print('------------------------------')
config_result = device.send_config_set(cfg_commands)
print('Configuration result:')
print('------------------------------')
print(config_result)
print('------------------------------')
print('Done, disconnecting from device.')
device.disconnect()
sys.exit(0)
#---
def get_interface_list(device):
'''Return a list of currently configured interfaces on the device
'''
# Lines in the 'show interface summary' look like this:
#
# *: interface is up
# IHQ: pkts in input hold queue IQD: pkts dropped from input queue
# OHQ: pkts in output hold queue OQD: pkts dropped from output queue
# RXBS: rx rate (bits/sec) RXPS: rx rate (pkts/sec)
# TXBS: tx rate (bits/sec) TXPS: tx rate (pkts/sec)
# TRTL: throttle count
#
# Interface IHQ IQD OHQ OQD RXBS RXPS TXBS TXPS TRTL
#-------------------------------------------------------------------------
#* Vlan1 0 0 0 0 0 0 0 0 0
#* FastEthernet0/1 0 0 0 0 0 0 0 0 0
# FastEthernet0/2 0 0 0 0 0 0 0 0 0
# regex to match the header separator
re_header_separator = re.compile(r'^-{50}.*$') # At least 50 '-' characters
# regex to extract the interface name
re_intf_line = re.compile(r'''^ # start of line
\*? # there may be a '*' char
\s+ # one or more spaces
([^\s]+)\s # All nonspace chars until a space
.*$''', # Ignore the rest of the line
re.VERBOSE)
# beginning of the string
# there may be a single '*'
# one or more spaces
# grab everything that's not a space, followed by a space
# don't care about anything after that
# till the end of the string
interface_list = []
header_finished = False
sh_op = device.send_command('show interface summary')
for line in sh_op.split('\n'):
# Ignore all lines until we see the '------' header separator
if not header_finished:
match_obj = re_header_separator.search(line)
if match_obj:
header_finished = True
continue
match_obj = re_intf_line.search(line)
if match_obj:
interface_list.append(match_obj.group(1))
return interface_list
#---
def get_current_interface_running_config(device, interface):
'''Return a list of lines containing the given interface's running
configuration'''
interface_config = []
sh_cmd_op = device.send_command('show run interface {}'.format(interface))
for line in sh_cmd_op.split('\n'):
# Ignore uninteresting lines
if not line:
continue
if line.startswith('Building configuration...'):
continue
if line.startswith('Current configuration'):
continue
if line == '!':
continue
if line == 'end':
break
interface_config.append(line)
return interface_config
#---
def make_connection_to_device():
'''Helper invoked from main() to set up a netmiko connection to the
device, and put it into enable mode'''
# access the netrc to read the credentials
try:
rc = netrc.netrc()
except FileNotFoundError as e:
print('(Failed to access netrc file for gathering ',
'login credentials: {})'.format(str(e)), file=sys.stderr)
sys.exit(-1)
netmiko_device_info = {}
netmiko_device_info['host'] = DESIRED_SETTINGS['device']
netmiko_device_info['device_type'] = 'cisco_ios'
try:
host = netmiko_device_info['host']
(login, enable_password, password) = rc.authenticators(host)
except TypeError:
print('No entry in netrc file for device "{}", and no default '
'either.'.format(netmiko_device_info['host']), file=sys.stderr)
sys.exit(-1)
# Fill in the user name / password / enable password device_info
# attributes from the info we read from .netrc
netmiko_device_info['username'] = login
netmiko_device_info['password'] = password
if enable_password:
netmiko_device_info['secret'] = enable_password
print('Connecting to device_info "{}"...'.format(
netmiko_device_info['host']), end='', flush=True)
device = Netmiko(**netmiko_device_info)
print('connected.')
print('Entering enable mode...', end='', flush=True)
device.enable()
print('done.')
prompt = device.find_prompt()
print('Prompt is "{}"'.format(prompt))
return device
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment