At my last job many fellow enigneers would made the claim that “I like the idea, and I like the info you can provide, but I am not going to build an entire application, with the proper code and prerequistes, for this use case.” I ended up building a webpage, but with all the security concerns, and limited resources it wasn’t a good fit for production use. However, it was adopted in an unofficial manner and that in turn expanded who could use my programs significantly. Not just other network engineers, but it also made life easier for the system admins. For example: when a system admin wanted to know what VLANs were configured to which VM servers they could just go to a webpage, view the simplified layout, and it would provide them the information in a format they were comfortable with. External users didn’t have to put in tickets and wait a week, network engineering didn’t have to stop what we were doing and look at those tickets. So, I love the idea, but my implementation was not ready for production with a proper support model. I have seen several other developers doing something along these lines, but they often seem to hit the same issues I did. I had see a few of the bigger brain developers I know build some Slack integrations, and I thought “Hey that’s cool, but I bet it’s REALLY complex”…. I was wrong: it isn’t hard at all.
Let’s take a look at what we do to build a Slack integrated program. After you get your Slack account and channels setup you need to get your token which should look something like this “xoxp-XXXXXXXX-XXXXXXXX-XXXXX”
Figuring out where the channel’s ID is can be a bit difficult. All you need to do though is right click the channel you want to interact with, and select copy link paste that into your text editor, and pull out the ID.
To get your token just go to https://api.slack.com/custom-integrations/legacy-tokens Scroll down to the “Legacy information” section and click Issue token/Re-issue token
This is all the Python code needed to build a function send a message in Slack.
def post_to_slack(message, channel, token):
slack_token = token
client = slack.WebClient(token=slack_token)
client.chat_postMessage(
channel = channel,
text=message)
Similar amount of code for building a function to pull a message in Slack.
def get_last_message(token, channel):
url = 'https://slack.com/api/conversations.history?token={}&channel={}&limit=1&pretty=1'.format(token, channel)
r = requests.get(url)
all_response = r.json()
last_message = all_response['messages'][0]['text']
return last_message
You can test this mini Slack application without worry about Python here: This particular API tests obtaining Slack conversations. Please note when it asks for a channel it is looking for a unique identifier like CP02W0ABQ, not #general. https://api.slack.com/methods/conversations.history/test
So we post in some text, and that causes our program to kick off it’s work.
Lets take a quick look at the final code used.
from pprint import pprint
import requests
import os
import slack
import json
import time
import netmiko
token = "xoxp-fake-token"
# The account that will be used to SSH into the devices
username = 'username'
password = 'password'
#Fake channel
channel ="BAR2FUFM"
#SSH or Telnet to the Cisco device
#If it fails to SSH and Telnet add an entry to Issues.csv
def make_connection(ip, username, password):
try:
net_connect = netmiko.ConnectHandler(device_type = 'cisco_ios', ip = ip, username = username, password = password)
return net_connect
except:
try:
return netmiko.ConnectHandler(device_type = 'cisco_ios_telnet', ip = ip, username = username, password = password)
except:
issue = ip + ", can't be ssh/telneted to"
to_doc_a("Issues.csv", issue)
to_doc_a("Issues.csv", '\n')
return None
#Send a command to the device that has been SSHed to
def send_command(net_connect,command):
return net_connect.send_command_expect(command)
#In this case you pass in the output from show interfaces but split into a list based on line return
#This splits the interfaces up into their own list
#So all[0] will give you a list of lines of the first interface
#all[0][0] will give you the first line of the first interface
def find_child_text (file, text):
all = []
parse = CiscoConfParse(file)
for obj in parse.find_objects(text):
each_obj = []
each_obj.append(obj.text)
for each in obj.all_children:
each_obj.append(each.text)
all.append(each_obj)
return all
#Post new message to slack channel in question
def post_to_slack(message, channel, token):
slack_token = token
client = slack.WebClient(token=slack_token)
client.chat_postMessage(
channel=channel,
text=message)
#Pull last 1 message from slack channel in question
def get_last_message(token, channel):
url = 'https://slack.com/api/conversations.history?token={}&channel={}&limit=1&pretty=1'.format(token, channel)
r = requests.get(url)
all_response = r.json()
last_message = all_response['messages'][0]['text']
return last_message
#All the devices in the lab, and they should all be eigrp neighbors
def check_lab():
lab_devices=[
'192.168.0.12',
'192.168.0.13',
'192.168.0.14',
'192.168.0.104',
#An IP I know won't be there so there are issues
'1.1.1.1'
]
#Hit each device in the lab
for ip in lab_devices:
info_output = "*checking {}*".format(ip)
#Post updates to slack so they know what's happening
post_to_slack(info_output, channel, token)
#SSH to device
net_connect = make_connection(ip, username, password)
#If it fails to connect post the error to slack and move to the next one
if net_connect == None:
issue = " can't connect to {}".format(ip)
post_to_slack(issue, channel, token)
continue
command = 'show ip eigrp nei'
#Get the output from the command sent
output = send_command(net_connect,command)
#Chek and make sure each device is listed as a neighbor, or is the one we SSHed to
for neighbor in lab_devices:
if ip == neighbor:
continue
if neighbor not in output:
issue = ' {} is not found in the active neighbors'.format(neighbor)
#If it's not there post the issue to slack
post_to_slack(issue, channel, token)
#Pull the interface data, and look for collisions
command = 'show interfaces'
output = send_command(net_connect,command)
output = output.split("\n")
interfaces = find_child_text (output, 'GigabitEthernet')
for interface in interfaces:
for line in interface:
if "collisions" in line:
if '0 collisions' not in line:
interface_name = interface[0].split(' ')[0]
issue = ' {} has collisions on {}'.format (interface_name, ip)
post_to_slack(issue, channel, token)
#Let the user know it's done
post_to_slack("done", channel, token)
#Name of things people will type in to kick off an action
functions=['say hi', 'check lab']
def say_hi():
post_to_slack("hi", channel, token)
post_to_slack("done", channel, token)
#Run forever, check back every second to see if it wants you to do anything.
while 1 == 1:
last_message = get_last_message(token, channel)
if last_message in functions:
#Start it off by posting something. That way if something bad happens you don't have
#An infinate loop of it trying to do the same thing because the last message posted was
#A kick off prompt
post_to_slack("processing", channel, token)
if last_message == 'say hi':
say_hi()
if last_message == 'check lab':
check_lab()
time.sleep(1)
Using Slack’s native integrations, and a Python library, there wasn’t actually that much code or logic required. You can see everything bundled up to a single file with around 100 lines of code. It’s also important to realize the user experience for people who are not developers or engineers, and an integration such as this can provide a reasonable middle ground.
Share details about yourself & someone from our team will reach out to you ASAP!