Cisco Jabber Network Bot

I wanted to try and create a Jabber bot that allowed some very basic troubleshooting through a chat window. I did find a page on Cisco.com about creating a Jabber bots, however it seemed out of date and I was not able to get it the code working as well as I wanted. After searching the Cisco community forums and the internet, I found an python 3 xmmp library called slixmpp and started hacking at the code.

I am putting this here in hopes it will help someone else. If you find this helpful or have suggestions please let me know. I would love to hear from you.

Setup:

First you will need a working Python 3 environment. I am running Redhat Enterprise and know these commands / code work on it.

Install the following Python dependencies: logging, paramiko, time, sys, slixmpp, getpass, and argparse. I install these using pip, however you maybe able to install them using yum or apt.

Next create the username account you will be using for the Jabber Bot. This account will be used to login to your Jabber server. The account creation can depend heavily on you setup. If you are unsure on how to do this I suggest ask the person who manages your Cisco Unified Communications Manager.

Supported Commands

Right now the bot only supports a few commands, however it should not be very difficult to modify the code to add more.

!help = Lists the help
!check ip [IP] switch stack – Show the output of ‘show switch stack-ports’
!check ip [IP] switch boot – Show the output of ‘show boot’
!check ip [IP] router vrrp – Show the output of ‘show vrrp’

Starting the Bot:

Start the bot from CLI by using the following command:

python3 bot_test.py -j [JabberBotUsername]@[Domain] -p [JabberBotPassword]

Code:

You will need to edit the variables below.
[BOT MANAGER JABBER ID] – The Jabber user who manages the bot.
[SSH_USERNAME] – Username used to login to the Cisco devices.
[SSH_PASSWORD] – Password used to login to the Cisco devices.
[JABBER SERVER IP] – IP address of the messaging server.

#!/bin/env python3
# -*- coding: utf-8 -*-

import logging, paramiko, time, sys, slixmpp
from getpass import getpass
from argparse import ArgumentParser

class LazyBot(slixmpp.ClientXMPP):
    def __init__(self, jid, password):
        slixmpp.ClientXMPP.__init__(self, jid, password)
        self.add_event_handler("session_start", self.start)
        self.add_event_handler("message", self.message)

    async def start(self, event):
        self.send_presence()
        self.send_message(mto='[BOT MANAGER JABBER ID]', mbody='Bot Online', mtype='chat')
        await self.get_roster()

    def message(self, msg):
        if msg['type'] in ('chat', 'normal'):
            if ("!check" in msg['body']):
                # Split Message 
                parts = (msg['body'].split())
                method = parts[1]
                site = parts[2]
                device = parts[3]
                check = parts[4]
				
				# Username and Password for SSH to Devices
				# We use Cisco ISE and have this account allowed into the devices from specific IP addresses
                username = "[SSH_USERNAME]"
                password = "[SSH_PASSWORD]"

				#Future Use
                if (method == "site"):
                    #Add code here to look up hostname or check SQL database for IP information

                # Check Switch Stack Ports
                if (method == "ip" and device == "switch" and check == "stack"):
                    remote_conn_pre=paramiko.SSHClient()
                    remote_conn_pre.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                    remote_conn_pre.connect(site, port=22, username=username, password=password, look_for_keys=False, allow_agent=False)
                    remote_conn = remote_conn_pre.invoke_shell()
                    remote_conn.send("show switch stack-ports\n")
                    time.sleep(1)
                    output = remote_conn.recv(65535)
                    msg.reply(output.decode('UTF-8')).send()

				# Check Switch Boot
                if (method == "ip" and device == "switch" and check == "boot"):
                    remote_conn_pre=paramiko.SSHClient()
                    remote_conn_pre.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                    remote_conn_pre.connect(site, port=22, username=username, password=password, look_for_keys=False, allow_agent=False)
                    remote_conn = remote_conn_pre.invoke_shell()
                    remote_conn.send("term length 0\n")
                    remote_conn.send("show boot\n")
                    time.sleep(1)
                    output = remote_conn.recv(65535)
                    msg.reply(output.decode('UTF-8')).send()

                # Check Router VRRP
                if (method == "ip" and device == "router" and check == "vrrp"):
                    remote_conn_pre=paramiko.SSHClient()
                    remote_conn_pre.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                    remote_conn_pre.connect(site, port=22, username=username, password=password, look_for_keys=False, allow_agent=False)
					remote_conn = remote_conn_pre.invoke_shell()
                    remote_conn.send("show vrrp\n")
                    time.sleep(1)
                    output = remote_conn.recv(65535)
                    msg.reply(output.decode('UTF-8')).send()

			# Ping Pong
            elif (msg['body'] == "!ping"):
                msg.reply("pong").send()
            
			# Help
            elif (msg['body'] == "!help"):
                text = '-------- Lazy Bot Version .001 Help -------- \n !check ip <ip_address> <device> <check>\n Example: !check ip 10.253.67.5 switch stack \n Supported Devices: switch, router \n Supported Checks: stack, vrrp\n Example: \t !check ip 10.253.67.5 switch stack \n'
                msg.reply(text).send()
				
            # Echo Everything Else
            else:
                msg.reply("You said %(body)s" % msg).send()

if __name__ == '__main__':
    # Slixmpp Setup (Mostly from the default Slixmpp Echo Bot Code) 
    # Setup the command line arguments.
    parser = ArgumentParser(description=LazyBot.__doc__)

    # Output verbosity options.
    parser.add_argument("-q", "--quiet", help="set logging to ERROR", action="store_const", dest="loglevel", const=logging.ERROR, default=logging.INFO)
    parser.add_argument("-d", "--debug", help="set logging to DEBUG", action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.INFO)

    # JID and password options.
    parser.add_argument("-j", "--jid", dest="jid", help="JID to use")
    parser.add_argument("-p", "--password", dest="password", help="password to use")
    args = parser.parse_args()

    # Setup logging.
    logging.basicConfig(level=args.loglevel, format='%(levelname)-8s %(message)s')

    if args.jid is None:
        args.jid = input("Username: ")
    if args.password is None:
        args.password = getpass("Password: ")

    xmpp = LazyBot(args.jid, args.password)
    xmpp.register_plugin('xep_0030') # Service Discovery
    xmpp.register_plugin('xep_0004') # Data Forms
    xmpp.register_plugin('xep_0060') # PubSub
    xmpp.register_plugin('xep_0199') # XMPP Ping

    # Connect to the XMPP server and start processing XMPP stanzas.
    xmpp.connect(address=('[JABBER SERVER IP]', 5222))
    xmpp.process()

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.