Skip to content

Commit

Permalink
Adding Fibrechannel switch module
Browse files Browse the repository at this point in the history
  • Loading branch information
evanjfraser committed Aug 3, 2012
1 parent bd11905 commit 4ffa92a
Show file tree
Hide file tree
Showing 3 changed files with 324 additions and 0 deletions.
28 changes: 28 additions & 0 deletions fibrechannel/README.mkdn
@@ -0,0 +1,28 @@
Brocade FibreChannel
This is gmond python module that allows SNMP polling of Fibrechannel switches to get interface packet and throughput metrics.

* It works for Brocade FC switches, and probably for any other SNMP enabled switch.
* It requires pysnmp (available in debian repositorys)
* Handles polling multiple switches from a single gmond.
* Spoofs the switch hostname, so each switch shows up separately in ganglia

## DEPENDS
* python pysnmp

## USAGE
* Save the fibrechannel.pyconf into directory and update the switch(s) name & IP's
* Save the fibrechannel.py into your ganglia python module dir eg: /usr/lib/ganglia/python_modules
* Update SNMP community / ports if necessary

If you're handling a large number of metrics, you may wish to set your sysctl settings as below:

net.core.rmem_max=104857600
net.core.rmem_default=104857600
vm.dirty_ratio=100
vm.dirty_background_ratio=100
vm.dirty_expire_centisecs=720000

## AUTHOR

Author: Evan Fraser <evan.fraser@trademe.co.nz>

271 changes: 271 additions & 0 deletions fibrechannel/fibrechannel.py
@@ -0,0 +1,271 @@
#!/usr/bin/python
# Name: fibrechannel.py
# Desc: Ganglia module for polling Brocade Fibrechannel switches via snmnp (probably work with any snmp capable device)
# Author: Evan Fraser evan.fraser@trademe.co.nz
# Date: August 2012
# Copyright: GPL

import sys
import os
import re
import time
import pprint
from pysnmp.entity.rfc3413.oneliner import cmdgen
NIPARAMS = {}

NIMETRICS = {
'time' : 0,
'data' : {}
}
LAST_NIMETRICS = dict(NIMETRICS)
NIMETRICS_CACHE_MAX = 5

descriptors = list()

oidDict = {
'ifIndex' : (1,3,6,1,2,1,2,2,1,1),
'ifDescr' : (1,3,6,1,2,1,2,2,1,2),
'ifInOctets' : (1,3,6,1,2,1,2,2,1,10),
'ifInUcastPkts' : (1,3,6,1,2,1,2,2,1,11),
'ifInErrors' : (1,3,6,1,2,1,2,2,1,14),
'ifOutOctets' : (1,3,6,1,2,1,2,2,1,16),
'ifOutUcastPkts' : (1,3,6,1,2,1,2,2,1,17),
'ifOutErrors' : (1,3,6,1,2,1,2,2,1,20),
}
#oidDict = {
# 'ifIndex' : (1,3,6,1,2,1,2,2,1,1),
# 'ifName' : (1,3,6,1,2,1,31,1,1,1,1),
# 'ifAlias' : (1,3,6,1,2,1,31,1,1,1,18),
# 'ifHCInOctets' : (1,3,6,1,2,1,31,1,1,1,6),
# 'ifHCOutOctets' : (1,3,6,1,2,1,31,1,1,1,10),
# 'ifInUcastPkts' : (1,3,6,1,2,1,2,2,1,11),
# 'ifOutUcastPkts' : (1,3,6,1,2,1,2,2,1,17),
# }

def get_metrics():
"""Return all metrics"""

global NIMETRICS, LAST_NIMETRICS

# if interval since last check > NIMETRICS_CACHE_MAX get metrics again
if (time.time() - NIMETRICS['time']) > NIMETRICS_CACHE_MAX:
metrics = {}
for para in NIPARAMS.keys():
if para.startswith('switch_'):
ipaddr,name = NIPARAMS[para].split(':')
snmpTable = runSnmp(oidDict,ipaddr)
newmetrics = buildDict(oidDict,snmpTable,name)
metrics = dict(newmetrics, **metrics)

# update cache
LAST_NIMETRICS = dict(NIMETRICS)
NIMETRICS = {
'time': time.time(),
'data': metrics
}

return [NIMETRICS, LAST_NIMETRICS]

def get_delta(name):
"""Return change over time for the requested metric"""

# get metrics
[curr_metrics, last_metrics] = get_metrics()
try:
delta = float(curr_metrics['data'][name] - last_metrics['data'][name])/(curr_metrics['time'] - last_metrics['time'])
#print delta
if delta < 0:
print "Less than 0"
delta = 0
except StandardError:
delta = 0

return delta

# Separate routine to perform SNMP queries and returns table (dict)
def runSnmp(oidDict,ip):

# cmdgen only takes tuples, oid strings don't work
## ifIndex = (1,3,6,1,2,1,2,2,1,1)
## ifName = (1,3,6,1,2,1,31,1,1,1,1)
## ifAlias = (1,3,6,1,2,1,31,1,1,1,18)
## ifHCInOctets = (1,3,6,1,2,1,31,1,1,1,6)
## ifHCOutOctets = (1,3,6,1,2,1,31,1,1,1,10)

# 'ifIndex' : (1,3,6,1,2,1,2,2,1,1),
# 'ifDescr' : (1,3,6,1,2,1,2,2,1,2),
# 'ifInOctets' : (1,3,6,1,2,1,2,2,1,10),
# 'ifInUcastPkts' : (1,3,6,1,2,1,2,2,1,11),
# 'ifInErrors' : (1,3,6,1,2,1,2,2,1,14),
# 'ifOutOctets' : (1,3,6,1,2,1,2,2,1,16),
# 'ifOutUcastPkts' : (1,3,6,1,2,1,2,2,1,17),
# 'ifOutErrors' : (1,3,6,1,2,1,2,2,1,20),

#Runs the SNMP query, The order that oid's are passed determines the order in the results
errorIndication, errorStatus, errorIndex, varBindTable = cmdgen.CommandGenerator().nextCmd(
# SNMP v2
cmdgen.CommunityData('test-agent', 'public'),
cmdgen.UdpTransportTarget((ip, 161)),
oidDict['ifIndex'],
oidDict['ifDescr'],
oidDict['ifInOctets'],
oidDict['ifInErrors'],
oidDict['ifInUcastPkts'],
oidDict['ifOutOctets'],
oidDict['ifOutErrors'],
oidDict['ifOutUcastPkts'],
)
#pprint.pprint(varBindTable)
# Check for SNMP errors
if errorIndication:
print errorIndication
else:
if errorStatus:
print '%s at %s\n' % (
errorStatus.prettyPrint(), errorIndex and varBindTable[-1][int(errorIndex)-1] or '?'
)
else:
return(varBindTable)

def buildDict(oidDict,t,switch): # passed a list of tuples, build's a dict based on the alias name
builtdict = {}

for line in t:
# if t[t.index(line)][2][1] != '':
string = str(t[t.index(line)][1][1]) # this is the ifDescr
#print string
match = re.search(r'FC port', string)
if match and t[t.index(line)][0][1] != '':
#alias = str(t[t.index(line)][0][1])
index = str(t[t.index(line)][0][1])
temp = str(t[t.index(line)][1][1]) #(use ifDescr)
#lowercase the name, change spaces + '/' to '_'
name = ((temp.lower()).replace(' ','_')).replace('/','_')
#print name
inoct = str(t[t.index(line)][2][1])
builtdict[switch+'_'+name+'_bitsin'] = int(inoct) * 8
outoct = str(t[t.index(line)][5][1])
builtdict[switch+'_'+name+'_bitsout'] = int(outoct) * 8
inpkt = str(t[t.index(line)][4][1])
builtdict[switch+'_'+name+'_pktsin'] = int(inpkt)
outpkt = str(t[t.index(line)][7][1])
builtdict[switch+'_'+name+'_pktsout'] = int(outpkt)
#if match and t[t.index(line)][0][1] != '':
# alias = str(t[t.index(line)][0][1])
# index = str(t[t.index(line)][1][1])
# name = str(t[t.index(line)][2][1])
# hcinoct = str(t[t.index(line)][3][1])
# builtdict[switch+'_'+alias+'_bitsin'] = int(hcinoct) * 8
# hcoutoct = str(t[t.index(line)][4][1])
# builtdict[switch+'_'+alias+'_bitsout'] = int(hcoutoct) * 8
# hcinpkt = str(t[t.index(line)][5][1])
# builtdict[switch+'_'+alias+'_pktsin'] = int(hcinpkt)
# hcoutpkt = str(t[t.index(line)][6][1])
# builtdict[switch+'_'+alias+'_pktsout'] = int(hcoutpkt)

#pprint.pprint(builtdict)
return builtdict

# define_metrics will run an snmp query on an ipaddr, find interfaces, build descriptors and set spoof_host
# define_metrics is called from metric_init
def define_metrics(Desc_Skel, ipaddr, switch):
snmpTable = runSnmp(oidDict,ipaddr)
aliasdict = buildDict(oidDict,snmpTable,switch)
spoof_string = ipaddr + ':' + switch
#print newdict

for key in aliasdict.keys():
if "bitsin" in key:
descriptors.append(create_desc(Desc_Skel, {
"name" : key,
"units" : "bits/sec",
"description" : "received bits per sec",
"groups" : "Throughput",
"spoof_host" : spoof_string,
}))
elif "bitsout" in key:
descriptors.append(create_desc(Desc_Skel, {
"name" : key,
"units" : "bits/sec",
"description" : "transmitted bits per sec",
"groups" : "Throughput",
"spoof_host" : spoof_string,
}))
elif "pktsin" in key:
descriptors.append(create_desc(Desc_Skel, {
"name" : key,
"units" : "pkts/sec",
"description" : "received packets per sec",
"groups" : "Packets",
"spoof_host" : spoof_string,
}))
elif "pktsout" in key:
descriptors.append(create_desc(Desc_Skel, {
"name" : key,
"units" : "pkts/sec",
"description" : "transmitted packets per sec",
"groups" : "Packets",
"spoof_host" : spoof_string,
}))


return descriptors

def metric_init(params):
global descriptors, Desc_Skel, _Worker_Thread, Debug, newdict

print '[switch] Received the following parameters'
print params

#Import the params into the global NIPARAMS
for key in params:
NIPARAMS[key] = params[key]

Desc_Skel = {
'name' : 'XXX',
'call_back' : get_delta,
'time_max' : 60,
'value_type' : 'double',
'format' : '%0f',
'units' : 'XXX',
'slope' : 'both',
'description' : 'XXX',
'groups' : 'switch',
}

# Find all the switch's passed in params
for para in params.keys():
if para.startswith('switch_'):
#Get ipaddr + name of switchs from params
ipaddr,name = params[para].split(':')
# pass skel, ip and name to define_metrics to create descriptors
descriptors = define_metrics(Desc_Skel, ipaddr, name)
#Return the descriptors back to gmond
return descriptors

def create_desc(skel, prop):
d = skel.copy()
for k,v in prop.iteritems():
d[k] = v
return d


def metric_cleanup():
'''Clean up the metric module.'''
pass

# For CLI Debuging:
if __name__ == '__main__':
params = {
'switch_1' : '192.168.1.1:switch1',
#'switch_2' : '192.168.1.2:switch2',
}
descriptors = metric_init(params)
print len(descriptors)
while True:
for d in descriptors:
v = d['call_back'](d['name'])
print 'value for %s is %u' % (d['name'], v)
print 'Sleeping 5 seconds'
time.sleep(5)
#exit(0)
25 changes: 25 additions & 0 deletions fibrechannel/fibrechannel.pyconf
@@ -0,0 +1,25 @@
modules {
module {
name = "fibrechannel"
language = "python"
param switch_1 {
# ip:hostname
value = '192.168.1.1:switch1'
}
#param switch_2 {
# value = '192.168.1.2:switch2'
#}
}
}
#/* Collection groups for the
# example python module */
collection_group {
collect_every = 20
time_threshold = 50
metric {
name_match = "(.+)in"
}
metric {
name_match = "(.+)out"
}
}

0 comments on commit 4ffa92a

Please sign in to comment.