XMPP: A Power Tool in Your Tool Box

Extensive Message Protocol (XMPP) is an open standard protocol based on XML (Extensible Markup Language). XMPP is designed to transport instant messages (IM) between entities and to detect online presence. It supports authentication of IM application and secure transport of messages over SSL/TLS. In XMPP entities can be bots, physical users, servers, devices or components. It’s really a powerful tool that has great potential for system administrators to add to their toolbox because:

XMPP is powerful

XMPP with Python is only 12 lines of code – trust me, it’s easy!

XMPP only requires a single query for multiple nodes

Status message can be used to track host presence

The Power of XMPP

For those of you that are not familiar with XMPP, it not only supports one-to-one messaging between entities but it also supports multi-party messaging (which enables an entity to join a chat room for the exchange of messages with several participants). The messages can be text messages embedded in XML format but XML can also be used to send control messages between entities as we will see with the presence stanza in a bit.

XMPP is widely used; Google uses it (for its Hangout application – formerly google chat) and so does Yahoo and MSN. At Cisco, we use Cisco Jabber extensively to communicate internally. The XMPP client function is now integrated in the Cisco Nexus 5000 series with the release 5.2(1)N1(7) and the Nexus 6000 series with the release of 7.0(0)N1(1). XMPP is an integral part of the single console access for Dynamic Fabric Automation (DFA) which is a powerful framework described in my previous blog.

The new Data Center Network Manager (DCNM) 7.0(1) is delivered as an OVA file that can be deployed quickly on an existing VMware-enabled server. Although DCNM comes with a lot of features that simplify the deployment of the Data Center fabric, we can pick and choose any service we want to use independently – which is great since DCNM comes with Cisco Jabber XCP and is license free. If you already have a XMPP service installed (like Openfire or ejabberd), it will not be a problem because everything discussed here is valid on any standard XMPP implementation.

On NX-OS devices, the XMPP feature is activated by configuring ‘feature fabric access’ and is part of the Enhanced L2 license (ENHANCED_LAYER2_PKG). Once activated, the switch becomes a XMPP client that needs to be registered on the server. In order to register it, XMPP requires the use of fully qualified domain names (FQDNs) to identify the domain server. If the switch does not have access to a DNS service, I recommend that you use the switch management network for messaging and a static host–to–IP address mapping in the switch configuration.

The switch will use its hostname to login to the XMPP service. If your XMPP server does not support auto-registration, you will need to register the switch and the rooms in the XMPP database beforehand. The DCNM OVA requires users and groups to be created via the CLI, and example of this user and group creation is:

[root@dcnm-ova ~]# appmgr add_user xmpp -u leaf0 -p cisco123
User added.
[root@dcnm-ova ~]# appmgr add_user xmpp -u leaf1 -p cisco123
User added.
…
User added.
[root@dcnm-ova ~]# appmgr add_user xmpp -u python -p cisco123
User added.
[root@dcnm-ova ~]# appmgr add_group xmpp -u admin -p C1sc0123 -g all-nodes
Group added.
[root@dcnm-ova ~]# appmgr add_group xmpp -u admin -p C1sc0123 -g leaf-nodes
Group added.

Here is an example switch CLI that connects the XMPP server as well as includes the switch in two different chat rooms.

ip host dcnm-ova.cisco.com 100.100.100.121
fabric access server dcnm-ova.cisco.com vrf management password cisco123
fabric access group all-nodes leaf-nodes

I can now use any XMPP client and have a chat with my switches! I use Pidgin since it is implemented on multiple desktop OS including Linux. You can request response messages to be formatted in XML to ease the breakdown of the various fields (e.g.: show interface e2/1 status | xml) but keep in mind that Pidgin may not display switch CLI output in XML format correctly. In order to resolve this issue, you need to enable the Conversation Colors plugin.

Once configured, the benefits are immediate! As soon as I joined the chat room all-nodes; I can issue the show command to all of my switches or I can apply the same configuration to the group in one single command since XMPP supports privilege 15. As you can see below, a reply will always end with a return value that notifies the sender of the status of the response, a value of 0 means success. The reply includes the switch name in jid format and the host-id, which could be very convenient to verify licenses for example.

Some important return value are described below:

Value | Description
2 | Invalid component name
6 | Invalid argument
7 | Command name not found
16 | No match
19 | Incomplete command
20 | Invalid command
28 | Ambiguous command
30 | Permission denied

To prevent CPU hogging, switches will not return output that are bigger than 64kB, they will in place write it on the bootflash and send a XMPP message describing the file name.

Hello World

There are more advantages in using XMPP on NX-OS, as Python can be enhanced with the xmpppy library (http://xmpppy.sourceforge.net) and I can quickly write a script that will send the infamous “Hello world” message to the console of the switch.
import xmpp

cmd="send session console Hello World \n"
jid="admin@dcnm-ova.cisco.com"
pwd="cisco123"
to="leaf0@dcnm-ova.cisco.com"
jid=xmpp.protocol.JID(jid)
cl=xmpp.Client(jid.getDomain(), debug=[])

cl.connect()
cl.auth(jid.getNode(),pwd)
cl.sendInitPresence()

message=xmpp.Message(to, cmd)
message.setAttr('type', 'chat')

cl.send(message)

Although this example is not very secure nor does it take in account connection and authentication problem, it demonstrates just how easy it is to interact with a switch using XMPP and Python. This program does not wait or expect an answer from the client, it just send a command message and the switch display it on the console.

Are You in the Room?

Multi User Chat room (MUC) offer several advantages, the most obvious is the capability to configure all switches participating in the chat in one shot. If you configure all the switches to join a common room, it also offer the possibility to monitor their presence inside that room. That means, you do not need to query the individual nodes or the room because, as soon as the script signal its presence to the room, all the participants (including the bot itself) will automatically respond with a presence stanza. Receiving message is a little bit more complex because, you need an event loop and a callback handler for incoming messages, here is the example:

import xmpp, re, time

jid=”python@dcnm-ova.cisco.com”
pwd="cisco123"
room="all-nodes@conference.dcnm-ova.cisco.com"

def messageHandler(conn, iq_node):
node = str(iq_node)
if “http://jabber.org/protocol/muc#user” in node:
njid=re.findall("jid=\"(.*?)/", node)
nrole=re.findall("role=\"(.*?)\"", node)
if (str(nrole[0]) == "none"):
print "%s %s left the room" %(time.strftime('%X'),str(njid[0]))
else:
print "%s %s is %s" %(time.strftime('%X'),str(njid[0]), str(nrole[0]))

def StepOn(conn):
try:
conn.Process(1)
except KeyboardInterrupt:
return 0
return 1

def GoOn(conn):
while StepOn(conn):
pass

jid=xmpp.protocol.JID(jid)
cl=xmpp.Client(jid.getDomain(), debug=[])
cl.connect()
cl.auth(jid.getNode(),pwd)
cl.RegisterHandler('presence',messageHandler)
cl.sendInitPresence()
cl.send(xmpp.Presence(to='{0}/{1}'.format(room, jid)))

GoOn(cl)

The script loop endlessly in the event handler routine until the user presses “ctrl + c”. Thanks to the endless loop you dynamically monitor the change in the nodes presence inside the room. I am including the output of the script below; you can observe that I rebooted leaf0 after I launched the script.

patrick@lnx-42:~/xmpp# python xmpp-presence.py
15:35:47 leaf4@dcnm-ova.cisco.com is participant
15:35:47 leaf1@dcnm-ova.cisco.com is participant
15:35:47 spine0@dcnm-ova.cisco.com is participant
15:35:47 leaf0@dcnm-ova.cisco.com is participant
15:35:47 spine1@dcnm-ova.cisco.com is participant
15:35:47 admin@dcnm-ova.cisco.com is moderator
15:35:47 leaf2@dcnm-ova.cisco.com is participant
15:35:47 leaf3@dcnm-ova.cisco.com is participant
15:35:47 python@dcnm-ova.cisco.com is participant
15:36:40 leaf0@dcnm-ova.cisco.com left the room

The user python@dcnm-ova.cisco.com is the jid of our python bot, it is important to note that jid are unique so while the script is running no other script using the same jid can be executed.

Dude, Where is My VM?

One of the key advantages for Dynamic Fabric Automation (DFA) is to provide workload automation, this capability guides configuration of fabric components based on the requirements and location of a physical or virtual server. This functionality enables the deployment of any workload, anywhere, dynamically. Now that VMs can moved freely inside the infrastructure, we may need to locate the payload quickly for troubleshooting purposes, DCNM provides this function via the DFA Dashboard but with a simple script I also can identify the DFA leaf where the VM is located.
The example below use the “groupchat” capability and query the “leaf-nodes” group for a specific Virtual Machine and interprets the result. With a little bit of parsing I can quickly return the jid of the leaf hosting the VM and display the result.

import xmpp, re

vm="Merlot_VM1"
cmd="show evb host name " + vm
jid='python@dcnm-ova.cisco.com'
pwd="cisco123"
room="leaf-nodes@conference.dcnm-ova.cisco.com"

def messageHandler(conn, msg):
if msg.getType() == "groupchat":
result=re.findall("No. of Hosts: [1-9]", str(msg.getBody()))
if result:
sw=re.findall("/(.*?)/",str(msg.getFrom()))
print vm + " was found on:\n"
print sw[0] + "\n"
print str(msg.getBody()) + "\n"

def StepOn(conn):
try:
conn.Process(1)
except KeyboardInterrupt:
return 0
return 1

def GoOn(conn):
while StepOn(conn):
pass

jid=xmpp.protocol.JID(jid)
cl=xmpp.Client(jid.getDomain(), debug=[])

if cl.connect() == "":
print "connection failed"
sys.exit(0)

if cl.auth(jid.getNode(),pwd) == None:
print "authentication failed"
sys.exit(0)

cl.RegisterHandler('message',messageHandler)
cl.sendInitPresence()
cl.send(xmpp.Presence(to='{0}/{1}'.format(room, jid)))
message = xmpp.Message(room, cmd)
message.setAttr('type', 'groupchat')
cl.send(message)

GoOn(cl)

The result message can be divided into 2 main fields, the first contain the source jid and the second contain the output from the CLI. Here is the output:

Merlot_VM1 was found on:
leaf3@dcnm-ova.cisco.com

EVB Host table

No. of Hosts: 1
No. of VSIs: 1
Flags: + - Multiple addresses

Host Name   VNI    Vlan  BD   Mac-address    IP-Address  Interface
----------- ------ ----- ---- -------------- ----------- ---------
Merlot_VM1  30001  500   500  fa16.3ec7.7ba5 7.7.7.2     Eth2/17

Results returned :: 0

The graphic below illustrate the result we collected from the leaf.

I hope you can see that XMPP is a powerful addition to the NX-OS toolbox. It has the capability to support end-to-end secure transport (TLS will be supported in an upcoming release), one-to-many communication, server query, and is using the standard NX-OS CLI. You can also increase your efficiency with the Python library and get away from expect/tcl scripts and move to a more modern efficient and feature rich scripting language. Python is well supported on the NX-OS switches; they already possess a Python interpreter and can automate their configuration using a Python Power On Auto Provisioning (POAP) script.

Have you written a script to share? Do not hesitate to send me your comments!