Automating your NMS build – Part 3 Changing Device Categories

In the first couple of posts in this series, we created some operators, then we added some devices.   In this post we’re going to look at something a little more complicated. In this post, we’re going to link a couple of different python functions together to meet the requirement which is to change a device from one category to another.

A bit about Device Categories

For those of you haven’t used HP’s Intelligent Management Centre before, the system automatically categorizes any discovered device, usually based upon SNMP sysobjectid. What this means is that when you run an auto discovery, the majority of your infrastructure will be properly classified right out of the box.

Screen Shot 2015 07 07 at 11 11 51 PM

This works great for devices which are SNMP enabled, as well as some other devices like ESX machines ( Virtual Devices ) which use SOAP as the management protocol, but it fails pretty miserably when dealing with devices which don’t support anything more than PING.  IMC’s default behaviour is to put anything which doesn’t respond to SNMP in the Desktop Category.

IP Phones aren’t smart

I’ve had customers who decided to discover all of their expensive IP phones and suddenly found out that none of them were classified properly. The problem with IP phones is that they are usually pretty stupid devices. Low memory, weak CPUs. Most of the processing/thinking is done by the PBX. I’ve never seen an IP phone that supports SNMP.

In this case we have two choices

  1. Manually change each IP phone from the Desktop category into the Voice category one device at a time.
  2. Use the RESTful API and a bit of code to do it wihile we go have a coffee

Filtering First

So the first thing we need to do is to identify the IP phones from the rest of the devices in the Desktop category. Thankfully, this is where having a well designed network can come in REALLY handy. Most Voice networks are designed so that the IP phones are automatically put into a voice vlan. This means that all phones SHOULD be in the same layer 3 network range.

The first piece of code we need to write is a simple piece of code which will allow the user to identify which of the categories they want to filter by. Although this might sound strange, we actually need to make sure that we only grab the DESKTOP devices in a specific subnet range. Imagine if you accidentally move the router or switch for this subnet into the voice category too. Really sucks leaving when you lose your router, right?

In this piece of code, we’re simply creating a dictionary which creates the link between the categoryId, which is the number IMC internally uses to identify the defined categories and the labels we humans use to identify them.  In a nutshell, this simply prints out the available categories.  Why you ask? Because the human running this script needs to known which categories they want to filter by.

def print_dev_category():
categories = [{"categoryId":"0", "dev_type":"router"},
{"categoryId":"1", "dev_type":"switch"},
{"categoryId":"2", "dev_type":"server"},
{"categoryId":"3", "dev_type":"security"},
{"categoryId":"4", "dev_type":'storage' },
{"categoryId":"5", "dev_type":"wireless"},
{"categoryId":"6", "dev_type": "voice"},
{"categoryId":"7", "dev_type":'printer'},
{"categoryId":"8", "dev_type":'ups'},
{"categoryId":"9", "dev_type":"desktop"},
{"categoryId":"10", "dev_type":"other"},
{"categoryId":"11", "dev_type":"surveillance"},
{"categoryId":"12", "dev_type":"video"},
{"categoryId":"13", "dev_type":"module"},
{"categoryId":"14", "dev_type":"virtualdev"},
{"categoryId":"15", "dev_type":"Load Balancer"},
{"categoryId":"16", "dev_type":"sdn_ctrl"}
for i in categories:
print ("For "+i["dev_type"]+", Please press: "+i["categoryId"])

This next performs two different functions

  1. Filters all known devices by the categories listed above
  2. Filters all known devices by an IP range.

Combining the two of them we’re able to easily fine all of the devices that have been classified in the Desktop Category in the L3 subnet of the IP phones and return that as a dictionary which contains, among other things, the device IDs which we’ll need in the next step.

def filter_dev_category():
if auth == None or url == None: # checks to see if the imc credentials are already available
global r
category = None
ip_range = None
get_dev_list_url = None
filter_by_cat = input("Do you want to filter by device category?\nY/N: ")
if filter_by_cat.lower() == "y":
category = input("Please select the device category: ")
get_dev_list_url = ("/imcrs/plat/res/device?resPrivilegeFilter=false&category="+category+"&start=0&size=10000&orderBy=id&desc=false&total=false")
#return get_dev_list_url
filter_by_ip = input("Do you want to filter by IP address network range?\nY/N: ")
if filter_by_ip.lower() == "y":
ip_range = input("What is the ip network range?\n Example: 10.101.16.\nFuzzy search is acceptible: ")
if category == None:
get_dev_list_url = ("/imcrs/plat/res/device?resPrivilegeFilter=false&ip="+ip_range+"&start=0&size=5&orderBy=id&desc=false&total=false")
get_dev_list_url = ("/imcrs/plat/res/device?resPrivilegeFilter=false&category="+category+"&ip="+ip_range+"&start=0&size=10000&orderBy=id&desc=false&total=false")
f_url = url + get_dev_list_url
payload = None
r = requests.get(f_url, auth=auth, headers=headers) #creates the URL using the payload variable as the contents
if r.status_code == 200:
dev_list = (json.loads(r.text))["device"]
return dev_list
print ("An Error has occured")

Putting it all together

So the last piece of code is where the magic actually happens.

First we assign the output of the filtering function above into the variable called dev_list.  Essentially, this is a list of devices which meet the search criteria of the filtering function.

In our case, this means the list will consists of all the IP phones in the specific subnet that we filtered.

From there, we use the items in dev_list as input into a for loop which changes them into the new category.  ( see how we use the same print_category function from above? )

def change_dev_category():
global dev_list
dev_list = filter_dev_category()
for dev in dev_list:
print ("system name: "+ dev['sysName']+'\nCurrent Category: '+dev['devCategoryImgSrc']+
'\nIP address: '+ dev['ip']+'\nSystem Description: '+dev['sysDescription']+ '\n\n')
cat_id = input("What is the category to which you want to move the selected device: ")
for i in dev_list:
dev_id = i["id"]
change_dev_cat_url = "/imcrs/plat/res/device/"+dev_id+"/updateCategory"
f_url = url + change_dev_cat_url
payload = '''{ "categoryId" : "'''+cat_id+'''" }'''
r = requests.put(f_url, data=payload, auth=auth, headers=headers) #creates the URL using the payload variable as the contents
if r.status_code == 204:
print (r.status_code)
print ("An Error has occured")

Wrapping it up

Hopefully this is a fairly useful example of how putting a few basic python functions together can help to substantially cut down on the amount of time it takes to perform what’s really a simple task. That’s the whole point of automation right?

As I continue learning, I can already see there a bunch of ways to improve this code, but hopefully having a simple working example will help people who are a couple of steps behind me on this journey take another step forward.

If you’re ahead of me on this journey and have suggestions, please feel free to comment!



Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s