Centralized access to device-configuration and other state-information using NAPALM

Since I still want to build a centralized solution for automated configuration of RMON-alarms to monitor Cisco cbQoS-packet/drop-counters a solution to retrieve and modify device configurations was needed.

The NAPALM (Network Automation and Programmability Abstraction Layer with Multivendor support) Framework seems to provide the required features.
NAPALM Installation
Some NAPALM-IOS dependencies have to be fulfilled first.

sudo apt-get install -y --force-yes libssl-dev libffi-dev python-dev python-cffi

The „partial installation“ seems to be not working anymore, the full installation uses some KB more ressources, not worth to think about the partial installation…

pip install napalm

IOS Preperation
To allow remote-access to the centralized NAPALM server these features need to be enabled in each IOS device:

  • Remote-Access via SSH,
  • SCP (Secure Copy),
  • the IOS „Archive“-feature is the foundation of NAPALM config-operations.
IOS-RTR#conf t
Enter configuration commands, one per line.  End with CNTL/Z.

! AAA preferred for production-systems, of course
IOS-RTR(config)#username rmond privilege 15 secret rmondpass

! required
IOS-RTR(config)#ip scp server enable

! no annoying [yes/no]-prompts for file-operations anymore
IOS-RTR(config)#file prompt quiet

! create the folder in the filesystem for the Archive
IOS-RTR(config)#do mkdir bootflash:/ARCHIVE

IOS-RTR(config)#archive
IOS-RTR(config-archive)#path bootflash:/ARCHIVE/bak-

Explore Napalm

Go the the python shell:

user@snmp-server:~$ python
>>> import napalm
>>> iosdriver = napalm.get_network_driver('ios')

Autodiscovery of the router-filesystem doesn’t work, i know the filesystem of my router and pass it as „optional argument“ to router-object.
*** todo: troubleshoot/fix the autodiscovery ***

>>> router = iosdriver(hostname='192.168.2.72', username='rmond',  password='rmondpass', 
optional_args={'port': 22, 'dest_file_system': 'bootflash:'})
>>> router.open()

Go to the router cli-shell:

  • user „rmond“ is logged in
IOS-RTR#who
    Line       User       Host(s)              Idle       Location
*  1 vty 0     user       idle                 00:00:00 192.168.2.109
   2 vty 1     rmond      idle                 00:00:02 192.168.2.89

Back to python, try some NAPALM-functions.

>>> print router.get_facts()
{u'os_version': u'CSR1000V Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 15.4(2)S3, RELEASE SOFTWARE (fc2)', 
u'uptime': 657600, u'interface_list': [u'GigabitEthernet1', u'GigabitEthernet2', u'GigabitEthernet3'], u'vendor': u'Cisco', 
u'serial_number': u'afdökjl0123', u'model': u'CSR1000V', u'hostname': u'IOS-RTR', u'fqdn': u'IOS-RTR.lab.local'}

Execute interactive EXEC-Commands.

>>> cliping=['ping 192.168.2.1']
>>> print router.cli(cliping)
{'ping 192.168.2.1': u'Type escape sequence to abort.\n
Sending 5, 100-byte ICMP Echos to 192.168.2.1, timeout is 2 seconds:\n!!!!!\n
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/1/2 ms'}

Fetch the current running-config.

>>> clishowrun=['show running-config']
>>> print router.cli(clishowrun)
{'show running-config': u'Building configuration...\n\nCurrent configuration : 2411 bytes\n!\n
! Last configuration change at 12:52:32 UTC Fri Nov 24 2017 by user\n! NVRAM config last updated at 10:24:26 UTC Fri Nov 24 2017 by user\n!\n
version 15.4\nservice timestamps debug datetime msec\nservice timestamps log datetime msec\n
...rmon event 10 log owner RMONevent\nrmon event 11 log owner RMONevent\n
rmon alarm 10001 cbQosCMStatsEntry.2.18.65536 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent\n
...
end'}

Read some lines from the running-config, filter using the pipe.

>>> cligetrmon=['show rmon alarms | inc RMONevent']
>>> print router.cli(cligetrmon)
{'show rmon alarms | inc RMONevent': u'Alarm 10001 is active, owned by RMONevent'}

Log-out if you are finished:

>>> router.close()

Linux: SNMPv3 with Python

Works out of the box.

  • if you’re fine with AES128-Encryption.

AES256 might not be a requirement in all cases, but having the opportunity to choose seems to be not absolutely absurd in 2017… Good news: The NetSNMP-AES192/256-patch is on the way.

EasySNMP installation and usage
I’d like to refer to Linux: SNMP with Python for the basics.

IOS-Config: VIEW/GROUP/USER
Take the opportunity and leverage SMP-Views to limit access to several SNMP-OIDs.

snmp-server view SV_EASYSNMP interfaces included
snmp-server view SV_EASYSNMP ciscoCdpMIB included
snmp-server view SV_EASYSNMP ciscoCBQosMIB included

ip access-list standard ACL_SNMP
 permit 192.168.2.89

snmp-server group SG_EASYSNMP v3 auth read SV_EASYSNMP access ACL_SNMP

snmp-server user EASYSNMP SG_EASYSNMP v3 auth sha AUTHPASS priv aes 128 PRIVPASS

EasySNMP: „Session“-Object with SNMPv3-Credentials
Find the official docs here: EasySNMP Session-API

Security level could be:

  • no_auth_or_privacy
    • If you want to use the user-based authentication without need for security
  • auth_without_privacy
    • Authentication only might be „good enough“ when traffic is fully kept within a management network
  • auth_with_privacy

Authentication-Procol:

  • MD5
  • SHA

Privacy-Protocol:

  • AES
    • AES128
  • DES, 3DES
    • For very outdated devices

Create the Session-Object

>>> session3 = Session(hostname='192.168.2.72', version=3,
security_level="auth_with_privacy", security_username="EASYSNMP",
auth_protocol="SHA", auth_password="AUTHPASS",
privacy_protocol="AES", privacy_password="PRIVPASS")

Use this „session“ as before.

>>> session3.walk("1.3.6.1.4.1.9.9.166.1.15.1.1.2")
[<SNMPVariable value='9' (oid='enterprises.9.9.166.1.15.1.1.2.18.65536', oid_index='',
snmp_type='COUNTER')>, <SNMPVariable value='0' (oid='enterprises.9.9.166.1.15.1.1.2.18.131072', oid_index='',
snmp_type='COUNTER')>, <SNMPVariable value='6039' (oid='enterprises.9.9.166.1.15.1.1.2.18.196608', oid_index='', snmp_type='COUNTER')>, <SNMPVariable value='0' (oid='enterprises.9.9.166.1.15.1.1.2.34.65536', oid_index='',
snmp_type='COUNTER')>, <SNMPVariable value='0' (oid='enterprises.9.9.166.1.15.1.1.2.34.131072', oid_index='',
snmp_type='COUNTER')>, <SNMPVariable value='0' (oid='enterprises.9.9.166.1.15.1.1.2.34.196608', oid_index='',
snmp_type='COUNTER')>]

Configure RMON Alarms&Events by script

Getting back to the original task..
Use a script on a centralized Controller-VM to figure out for which SNMP-OID RMON-Alarms should get configured

Get all current QoS-Drop-Counters, check the traffic-direction to monitor only outbount-queues, generate RMON-Alarms.

hostname = "192.168.2.72"

session = Session(hostname, community='READ', version=2)

cbqos = session.walk('1.3.6.1.4.1.9.9.166.1.15.1.1.13')

cmds = ["Configure on Host \""+hostname+"\"\n---"]
cmds.append("rmon event 10 log owner RMONevent")
cmds.append("rmon event 11 log owner RMONevent")

alarmID = 10001

for i in cbqos:
  oidList=i.oid.split(".")
  q=oidList.pop()
  p=oidList.pop()
  #print p,q
  ifTypeID=int(session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.2."+p).value)
  ifDirID=int(session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.3."+p).value)
  if (ifDirID==2):
    cmds.append("rmon alarm "+str(alarmID)+" "+i.oid+" 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent")
  alarmID += 1

for cmd in cmds:
  print cmd

Example Output:

Configure on Host "192.168.2.72"
---
rmon event 10 log owner RMONevent
rmon event 11 log owner RMONevent
rmon alarm 10001 enterprises.9.9.166.1.15.1.1.13.18.65536 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent
rmon alarm 10002 enterprises.9.9.166.1.15.1.1.13.18.131072 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent
rmon alarm 10003 enterprises.9.9.166.1.15.1.1.13.18.196608 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent
rmon alarm 10004 enterprises.9.9.166.1.15.1.1.13.34.65536 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent
rmon alarm 10005 enterprises.9.9.166.1.15.1.1.13.34.131072 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent
rmon alarm 10006 enterprises.9.9.166.1.15.1.1.13.34.196608 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent

Todo: Verify existing RMON-Alarm/Event-Configuration at the device
Todo: Push the config automatically to the device

Getting Details of a Traffic Class from the SNMP-MIB

Today I’ll show how retrieve additional details from already discoverd QoS-Counters. They are mostly descriptive, for human eyes.
The „Traffic-Direction“-Attribute might be relevant since in most cases only outbound drop-counters might be interesting, so the discovered list of OIDs could get filtered to process only those outbound OIDs.

Refresh: Retrieve all „QoS Packet-Counters“

>>> cbqos = session.walk('1.3.6.1.4.1.9.9.166.1.15.1.1.2')
>>> print cbqos
[<SNMPVariable value='9' (oid='enterprises.9.9.166.1.15.1.1.2.18.65536', oid_index='', snmp_type='COUNTER')>,
<SNMPVariable value='0' (oid='enterprises.9.9.166.1.15.1.1.2.18.131072', oid_index='', snmp_type='COUNTER')>,
<SNMPVariable value='1035' (oid='enterprises.9.9.166.1.15.1.1.2.18.196608', oid_index='', snmp_type='COUNTER')>,
<SNMPVariable value='0' (oid='enterprises.9.9.166.1.15.1.1.2.34.65536', oid_index='', snmp_type='COUNTER')>,
<SNMPVariable value='0' (oid='enterprises.9.9.166.1.15.1.1.2.34.131072', oid_index='', snmp_type='COUNTER')>,
<SNMPVariable value='0' (oid='enterprises.9.9.166.1.15.1.1.2.34.196608', oid_index='', snmp_type='COUNTER')>]

There are two Policy-Objects #P:

  • Policy #18
  • Policy #34

Both Policy-Objects contain three Traffic-Classes #Q:

  • Class #65535
  • Class #131072
  • Class #196608

Attributes of a bound Policy #P

Each Policy has at least two attributes:

  • Interface-Type of the Policy (5 : CoPP)
    • 1:mainInterface
    • 2:subInterface
    • 3:frDLCI
    • 4:atmPVC
    • 5:controlPlane
    • 6:vlanPort
    • 7:evc
  • Traffic-Direction
    • 1:input
    • 2:output
  • Interface bound to

Get the type of a Policy = „cbQosServicePolicyEntry.2.#P“ = „1.3.6.1.4.1.9.9.166.1.1.1.1.2.#P“

  • both are type „1“ = Main-Interface
>>> print session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.2.18")
<SNMPVariable value='1' (oid='enterprises.9.9.166.1.1.1.1.2.18', oid_index='', snmp_type='INTEGER')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.2.34")
<SNMPVariable value='1' (oid='enterprises.9.9.166.1.1.1.1.2.34', oid_index='', snmp_type='INTEGER')>

Direction = „cbQosServicePolicyEntry.3.#P“ = „1.3.6.1.4.1.9.9.166.1.1.1.1.3.#P“

  • both are direction „2“ = Output
>>> print session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.3.18")
<SNMPVariable value='2' (oid='enterprises.9.9.166.1.1.1.1.3.18', oid_index='', snmp_type='INTEGER')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.3.34")
<SNMPVariable value='2' (oid='enterprises.9.9.166.1.1.1.1.3.34', oid_index='', snmp_type='INTEGER')>

Interface-ID = „cbQosServicePolicyEntry.4.#P“ = „1.3.6.1.4.1.9.9.166.1.1.1.1.4.#P“ (cbQosIfIndex)

  • Policy#18 is bound to Interface #1
  • Policy#34 is bound to Interface #2
>>> print session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.4.18")
<SNMPVariable value='1' (oid='enterprises.9.9.166.1.1.1.1.4.18', oid_index='', snmp_type='INTEGER')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.4.34")
<SNMPVariable value='2' (oid='enterprises.9.9.166.1.1.1.1.4.34', oid_index='', snmp_type='INTEGER')>

Interface-NAME = „1.3.6.1.2.1.2.2.1.2.#IFID“ (ifDescr)

  • Interface#1 is named „GigabitEthernet1“
  • Interface#2 is named „GigabitEthernet2“
>>> print session.get("1.3.6.1.2.1.2.2.1.2.1")
<SNMPVariable value='GigabitEthernet1' (oid='ifDescr', oid_index='1', snmp_type='OCTETSTR')>
>>> print session.get("1.3.6.1.2.1.2.2.1.2.2")
<SNMPVariable value='GigabitEthernet2' (oid='ifDescr', oid_index='2', snmp_type='OCTETSTR')>

Attributes of the Traffic-Classes #Q in Policy #P

Each Traffic-Class has (beyond of all counters) the attribute:

  • Name

Class-ID = „1.3.6.1.4.1.9.9.166.1.5.1.1.2.#P.#Q“ (cbQosConfigIndex)

  • Class #65535 in Policy #18 has ID #309479785
  • Class #131072 in Policy #18 has ID #342719994
  • Class #196608 in Policy #18 has ID #1593
  • Class #65535 in Policy #34 has ID #309479785
  • Class #131072 in Policy #34 has ID #342719994
  • Class #196608 in Policy #34 has ID #1593

since the Class-IDs are the same, it seems to be one and the same policy-map bound to two interfaces

>>> print session.get("1.3.6.1.4.1.9.9.166.1.5.1.1.2.18.65536")
<SNMPVariable value='309479785' (oid='enterprises.9.9.166.1.5.1.1.2.18.65536', oid_index='', snmp_type='GAUGE')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.5.1.1.2.18.131072")
<SNMPVariable value='342719994' (oid='enterprises.9.9.166.1.5.1.1.2.18.131072', oid_index='', snmp_type='GAUGE')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.5.1.1.2.18.196608")
<SNMPVariable value='1593' (oid='enterprises.9.9.166.1.5.1.1.2.18.196608', oid_index='', snmp_type='GAUGE')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.5.1.1.2.34.196608")
<SNMPVariable value='1593' (oid='enterprises.9.9.166.1.5.1.1.2.34.196608', oid_index='', snmp_type='GAUGE')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.5.1.1.2.34.65536")
<SNMPVariable value='309479785' (oid='enterprises.9.9.166.1.5.1.1.2.34.65536', oid_index='', snmp_type='GAUGE')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.5.1.1.2.34.131072")
<SNMPVariable value='342719994' (oid='enterprises.9.9.166.1.5.1.1.2.34.131072', oid_index='', snmp_type='GAUGE')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.5.1.1.2.34.196608")
<SNMPVariable value='1593' (oid='enterprises.9.9.166.1.5.1.1.2.34.196608', oid_index='', snmp_type='GAUGE')>
>>>

Class-NAME = „1.3.6.1.4.1.9.9.166.1.7.1.1.1.#CLASS-ID“ (cbQosCMName)

  • Class #309479785 has the name „CM_VOIP_RTP“
  • Class #342719994 has the name „CM_VOIP_CTRL“
  • Class #1593 has the name „class-default“
>>> print session.get("1.3.6.1.4.1.9.9.166.1.7.1.1.1.309479785")
<SNMPVariable value='CM_VOIP_RTP' (oid='enterprises.9.9.166.1.7.1.1.1.309479785', oid_index='', snmp_type='OCTETSTR')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.7.1.1.1.342719994")
<SNMPVariable value='CM_VOIP_CTRL' (oid='enterprises.9.9.166.1.7.1.1.1.342719994', oid_index='', snmp_type='OCTETSTR')>
>>> print session.get("1.3.6.1.4.1.9.9.166.1.7.1.1.1.1593")
<SNMPVariable value='class-default' (oid='enterprises.9.9.166.1.7.1.1.1.1593', oid_index='', snmp_type='OCTETSTR')>

Put it all together
Fetch the list of all „Packet-Counter“-OIDs.

Get the Queue/Class-Details and the Packet-Counter per Class.

session = Session(hostname='192.168.2.72', community='READ', version=2)

cbqos = session.walk('1.3.6.1.4.1.9.9.166.1.15.1.1.2')

ifType=["","mainInterface","subInterface","frDLCI","atmPVC","controlPlane","vlanPort","evc"]
ifDir=["","input","output"]

for i in cbqos:
  oidList=i.oid.split(".")
  q=oidList.pop()
  p=oidList.pop()

  ifTypeID=int(session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.2."+p).value)
  ifDirID=int(session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.3."+p).value)
  ifID=session.get("1.3.6.1.4.1.9.9.166.1.1.1.1.4."+p).value
  ifName=session.get("1.3.6.1.2.1.2.2.1.2."+ifID).value
  classID=session.get("1.3.6.1.4.1.9.9.166.1.5.1.1.2."+p+"."+q).value
  className=session.get("1.3.6.1.4.1.9.9.166.1.7.1.1.1."+classID).value
  pktCounter=session.get(i.oid).value
  print ifName+"("+ifType[ifTypeID]+")"+ifDir[ifDirID],className,pktCounter+" Packets"

Example Output:

GigabitEthernet1(mainInterface)output CM_VOIP_RTP 9 Packets
GigabitEthernet1(mainInterface)output CM_VOIP_CTRL 0 Packets
GigabitEthernet1(mainInterface)output class-default 1716 Packets
GigabitEthernet2(mainInterface)output CM_VOIP_RTP 0 Packets
GigabitEthernet2(mainInterface)output CM_VOIP_CTRL 0 Packets
GigabitEthernet2(mainInterface)output class-default 0 Packets

Exploring the SNMP-MIB for Class-based QoS

Discover the OIDs representing the counter-values of all active traffic-classes

Ciscos „SNMP Object Navigator“ (http://mibs.cloudapps.cisco.com/ITDIT/MIBS/servlet/index) is our friend to get the base-OID when you know the name of the MIB:

  • Object-NAME <=> Object-ID (OID)
  • „cbQosCMStatsEntry“ <=> „1.3.6.1.4.1.9.9.166.1.15.1.1“

Each object is a set of all counters from „show policy-map interface“-command, the Object Navigator documents the ID of these counters, too.
„Exploring the SNMP-MIB for Class-based QoS“ weiterlesen

Refresher: RMON @ Cisco IOS

RMON Refresher
Think about this given Router-Configuration:

class-map match-all CM_VOIP_CTRL
 match dscp af31
class-map match-all CM_VOIP_RTP
 match dscp ef

policy-map PM_OUT
 class CM_VOIP_RTP
  priority percent 10
 class CM_VOIP_CTRL
  bandwidth percent 1
 class class-default
  fair-queue
!
interface GigabitEthernet1
 ip address 192.168.2.72 255.255.255.0
 service-policy output PM_OUT

Three Queues at interface Gig1:

  • CM_VOIP_RTP
  • CM_VOIP_CTRL
  • class-default

with per-Queue-Statistics:

  • Packet counters
  • Drop-counters
  • etc.

In these first examples, i don’t want to wait for queue-drops, i’ll just generate DSCP=EF-Traffic by the ping-command and watch the Queue-Packet-Counters, not Drops.
Configure RMON Alarms and Events
I’ll add two RMON-Events

rmon event 10 log owner RMONevent
rmon event 11 log owner RMONevent

event #10 = rising-threshold – in my example: >1 Packet has been dropped forwarded
event #11 = the falling-threshold – no packets have been…

Than, instruct the Router to have a look at a QoS-counter:

rmon alarm 10001 enterprises.9.9.166.1.15.1.1.2.18.65536 300 delta rising-threshold 1 11 falling-threshold 0 10 owner RMONevent

In the upcoming post I’ll discover the RMON-MIB to illustrate where the „enterprise.9….65536“-Parameter comes from.

This alarm #10001 monitors:

  • the value the QoS-counter with OID „enterprises.9.9.166.1.15.1.1.2.18.65536“ (Pkt-Counter of the RTP-Queue).
  • every 300s
  • watch for delta-values (not for absolute counters which might be interesting when monitoring temperatures, fan-speed etc…)
  • define a hysteresis:
    • rising: if the last counter-delta „was <1" and "is now >=1″ – it raises event#11.
    • falling: if the last counter-delta „was >=1“ and „is now <1" - it raises event#10.

Both events instruct the router to generate a syslog-message.
In production event 10 will be configured without the „log“-option [to do nothing]. This config is for demonstration purpose.

Forward some Traffic

Generate some Traffic (TOS 184 = DSCP 46 = Expedited Forwarding (EF).

IOS-RTR#ping 192.168.2.1 tos 184
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.2.1, timeout is 2 seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 1/2/5 ms

So – the Queue wasn’t used before:

  • the old-counter has been „0“
  • now 5 Packets have been forwarded

This delta-counter „5“ has exceeded rising-threshold „1“:

  • event#11 should be raised.

When?

  • we’ll have to wait between 0..300s for the cyclic 300s-alarm-interval to fire
IOS-RTR#
*Nov 20 14:54:39.015: %RMON-5-RISINGTRAP: Rising threshold has been crossed because the value of cbQosCMStatsEntry.2.18.65536 exceeded the rising-threshold value 1

The current counter is „5“:

  • whithout rtp-data, the next delta-counter will be 0.

Wait for the next 300s interval:

  • the falling-event#10 should get raised.
IOS-RTR#
*Nov 20 14:59:38.837: %RMON-5-FALLINGTRAP: Falling threshold has been crossed because the value of cbQosCMStatsEntry.2.18.65536 has fallen below the falling-threshold value 0

Works, perfect!

WAN MACsec – Encrypting Ethernet-Frames in the WAN

Not talking about ATM, PPP, HDLC, Frame Relay WAN. Ethernet is the new WAN.

Sometimes you need an easy to use encryption, which is just added to a Link between two devices – and you don’t want to invent IPSec-VPNs which at least add complexity to the design.

MACsec solved this problem bringing absolutely transparent encryption, but – since MACsec-Sessions are sent to a „link-local“ ethernet destination address (01-80-c2-00-00-03) they aren’t allowed to be forwarded by any device. A typical „non-dark-fiber“ WAN-Link can’t be encrypted using MACsec.

http://www.ieee802.org/1/files/public/docs2013/ae-seaman-macsec-hops-0213-v02.pdf discussed years ago the elegant technical solution.

Years later these thoughts are moving into production: Cisco invented „WAN MACsec“:

  • simply allowing the network-designer to change destination-mac-address and the ethernet-frametype.

This makes it possible for active provider devices to forward MACsec-Sessions through the WAN so the WAN-Customers could create a virtual encrypted link between sites, completely transparent
https://www.cisco.com/c/en/us/products/collateral/ios-nx-os-software/identity-based-networking-services/white-paper-c11-737544.html

This example shows the most simple suggestion:

  • change the destination-mac from link-local-multicast to broadcast 😉

Router A

key chain KC_WAN macsec
 key 1
   key-string PASSWORD
interface GigabitEthernet0/0/0
  ip address 10.0.0.1 255.255.255.252 
  eapol destination-address broadcast
  mka pre-shared-key key-chain KC_WAN
  macsec

Router B

key chain KC_WAN macsec
 key 1
   key-string PASSWORD
interface GigabitEthernet0/0/0
  ip address 10.0.0.2 255.255.255.252
  eapol destination-address broadcast 
  mka pre-shared-key key-chain KC_WAN
  macsec

Might make sense to change the cipher, increase the anti-reply-window etc…

Cisco UCSD: Custom Workflow Task

this is maybe just a note for myself as i found out that the UCSD-Documentation is in polite wording „somehow incomplete“.

After re-engineering some existing workflow task-scripts and playing around a bit, the good news:

  • it’s simple and straightforward to create custom workflow tasks on your own.

Background

I wanted to create the name of newly provisioned VLANs automatically:

  • the IP-Subnet was provided by an IPAM, e.g. „10.2.3.0“,
  • the VLAN-Names should have fixed length, had to be padded („010.002.003.000“).

The workflow takes two task inputs:

  • the „network“,
  • the char to be used to pad the network-string to the fixed length
    • as special feature, this parameter is optional („Mandatory: false“)
UCSD - Custom Task Inputs
Custom Task Inputs

A javascript will build the padded vlan-name – the task output:

UCSD Custom Task Output
Custom Task Output

The Script fetches the inputs:

input.<Input Field Name>

a) mandatory input:

var subnet = input.subnet;

b) optional (with default-value „0“) input:

  • length „0“ => optional parameter not set

var pad = "0";
if (input.pad.length==1) {
  pad = input.pad;
}

The padded result is published to

output.<Output Field Name>

c) padded subnet

output.paddedSubnet = paddedSubnet;

 

The Script itself is straightforward:

function expand(num, size, pad) {
  if (num.length<size) {
    return expand(pad+num, size, pad);
  }
  return num;
}

var subnet = input.subnet;
var paddedSubnet = "";

var pad = "0";
if (input.pad.length==1) {
  pad = input.pad;
}

logger.addInfo("---------------------------------");
logger.addInfo("input.subnet = "+subnet);
logger.addInfo("using pad = ["+pad+"]");
//
var parts = subnet.split('.');
for(var j=0; j<parts.length; j++) {
  if (paddedSubnet) {
    paddedSubnet += ".";
  }
  paddedSubnet = paddedSubnet + expand(parts[j], 3, pad);
}

output.paddedSubnet = paddedSubnet;

logger.addInfo("output.subnet = "+paddedSubnet);
logger.addInfo("---------------------------------");

 

Cisco UCS Director (UCSD) as Unified Infrastructure Controller

Yes, I agree: this product name can’t get worse.

What’s not good with the product name „UCS Director“? It needs explanation!

Nobody in this world could guess it’s feature-set, everybody thinks it’s some additional umbrella-management on top of the UCS-Manager or UCS Central.

„Unified Infrastructure Controller“ would fit much better, since the UCSD not only automates UCS-Components, but the whole Datacenter (and more) including LAN/SAN-Switches, Firewalls and the virtualization environment like vSphere or Hyper-V.