Category Archives: Puppet

Puppet – Cisco Switch Management

Cisco network devices with IOS as operating system can be managed through Puppet Enterprise, but some of its other features like ‘switchport nonegotiate’, ‘switchport voice vlan’ etc. are not included in the attributes of the ‘interface’ resource type of Puppet. Moreover, our requirement is to fetch the current running configuration of the Cisco switch, compare it with the base configuration and then display it on the Puppet Enterprise Console.

Our whole approach depends on below 3 events:

  • Fetching the base configuration of switch daily.
  • Fetching current configuration with every run of puppet agent.
  • Comparing the above 2 configurations.

We are using Cisco Catalyst 3650 switch in this endeavor, with puppet master (Puppet Enterprise 3.8) installed on CentOS 7. Below steps are explained for a single interface of the switch, but can be easily scaled to multiple interfaces.

Fetching Switch Configuration Daily

The script below is written in Python using PExpect module. It fetches the current running configuration of the switch for Gigabit Ethernet and converts to a yaml file. This script can be run a cron job on daily basis.

from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals

import sys
import pexpect
import string


switch_ip="<IPADDR>"
switch_un="user"
switch_pw="s3cr3t"

child=pexpect.spawnu('ssh %s@%s' % (switch_un, switch_ip))
child.logfile=sys.stdout
child.timeout=4
child.expect('Password:')
child.sendline(switch_pw)
child.expect('>')
child.sendline('en')
child.expect('Password:')
child.sendline('enable_pwd')
child.expect('#')

child.sendline('show running-config interface GigabitEthernet 1/0/1')

child.expect('#')
data=child.before
child.sendline('exit')
child.expect(pexpect.EOF)

list1=data.splitlines()

desc=None
avlan=None
mode1=None
nego=None
vvlan=None
duplex=None
bps=None
action=None

for member in list1:
	if "description" in member:
		desc=member.split('"')
		desc=desc[1]
		desc=desc.lstrip()

	if "switchport access" in member:
		avlan=member.split("vlan")
		avlan=avlan[1]
		avlan=avlan.lstrip()
	
	if "switchport mode" in member:
		mode1=member.split("mode")
		mode1=mode1[1]
		mode1=mode1.lstrip()
		
	if "switchport nonegotiate" in member:
		nego=member.split("switchport")
		nego=nego[1]
		nego=nego.lstrip()
	
	if "switchport voice" in member:
		vvlan=member.split("vlan")
		vvlan=vvlan[1]
		vvlan=vvlan.lstrip()

	if "duplex" in member:
		duplex=member.split("duplex")
		duplex=duplex[1]
		duplex=duplex.lstrip()
	
	if "storm-control" in member:
		if "broadcast level bps" in member:
			bps=member.split("bps")
			bps=bps[1]
			bps=bps.lstrip()
		elif "action" in member:
			action=member.split("action")
			action=action[1]
			action=action.lstrip()
			
	
	if "spanning-tree" in member:
		sptree=member.split("spanning-tree")
		sptree=sptree[1]
		sptree=sptree.lstrip()


with open('/etc/puppetlabs/facter/facts.d/bckupconfig121.yaml','w') as f:
	f.write('---\n')
	f.write('IfGB101Desc: %s\n' % desc)
	f.write('IfGB101AVlan: %s\n' % avlan)
	f.write('IfGB101Mode: %s\n' % mode1)
	f.write('IfGB101Nego: %s\n' % nego)
	f.write('IfGB101VVlan: %s\n' % vvlan)
	f.write('IfGB101Duplex: %s\n' % duplex)
	f.write('IfGB101Strmbps: %s \n' % bps)
	f.write('IfGB101Strmact: %s\n' % action)
	f.write('IfGB101SPtree: %s\n' % sptree)
	f.close()

 

This yaml file is stored in Hiera repository so that the configuration can be read in Puppet manifests.

Below is the sample yaml file stored in Hiera repository.

yamlHiera

Below is a snapshot of the hiera.yaml configuration file.

hieraconfig

Yaml files are named in below format:

dateyyyy-mm-dd.yaml

dateformat

Fetching current configuration with every run of puppet agent.

Once the daily configuration script is run and the data stored in yaml file, next step is to fetch the switch running configuration with every run of Puppet Agent. The script used for fetching this configuration is same as the one used before, only difference being the name of the yaml file and the location where it is stored. Also, this script will be called through Puppet manifest using the ‘exec’ resource.

I have named this file as ‘bckupconfig121.yaml’ and stored it in ‘/etc/puppetlabs/facter/facts.d/’. This location is the one where the external facts are stored. Thus, the data stored in ‘bckupconfig121.yaml’ can be invoked from the command line as external facts.

Below is a snapshot of ‘bckupconfig121.yaml’:

bckupconfig

Data stored in the above yaml file can be accessed using facter tool.

facter1

Comparison of the yaml files

For comparison of the yaml files generated in the above 2 steps, following code in puppet manifest is used.


class cisconw{

# Fetch configuration from Cisco switch with every run of Puppet Agent
exec{ "/usr/bin/python /home/pratyush/Cisco/ShowInterface-Agent.py": }

#Compare the values
if hiera("IfGB101Desc") != $::ifgb101desc{
fail("Description of Gigabit Ethernet Interface has changed to $::ifgb101desc")
}
}

If the values does not match, the puppet agent run fails on the server and will show up in ‘Failed’ nodes in Puppet Enterprise Console.

PEConsole

This may not be best or the last way, so let me know your thoughts on this approach in the comments section below.