diff --git a/fibrechannel/README.mkdn b/fibrechannel/README.mkdn new file mode 100644 index 00000000..15df7ceb --- /dev/null +++ b/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> + diff --git a/fibrechannel/fibrechannel.py b/fibrechannel/fibrechannel.py new file mode 100755 index 00000000..ce192292 --- /dev/null +++ b/fibrechannel/fibrechannel.py @@ -0,0 +1,265 @@ +#!/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), + } + +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), +# '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('/','_') + 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) + inerrors = str(t[t.index(line)][3][1]) + builtdict[switch+'_'+name+'_inerrors'] = int(inerrors) + outerrors = str(t[t.index(line)][6][1]) + builtdict[switch+'_'+name+'_outerrors'] = int(outerrors) + + #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 + #pprint.pprint(aliasdict.keys()) + + 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, + })) + elif "inerrors" in key: + descriptors.append(create_desc(Desc_Skel, { + "name" : key, + "units" : "errors", + "description" : "inbound packet errors", + "groups" : "Packets", + "spoof_host" : spoof_string, + })) + elif "outerrors" in key: + descriptors.append(create_desc(Desc_Skel, { + "name" : key, + "units" : "errors", + "description" : "outbound packet errors", + "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) diff --git a/fibrechannel/fibrechannel.pyconf b/fibrechannel/fibrechannel.pyconf new file mode 100644 index 00000000..9a0990a4 --- /dev/null +++ b/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" + } + } diff --git a/recoverpoint/README.mkdn b/recoverpoint/README.mkdn new file mode 100644 index 00000000..2a0ba382 --- /dev/null +++ b/recoverpoint/README.mkdn @@ -0,0 +1,18 @@ +EMC RecoverPoint +=============== + +This is a GMOND Python Module that gets metrics from EMC RecoverPoint replication appliances. + +## DEPENDS + * python YAML + * paramiko modules + * ssh access to the recoverpoint appliance (paramiko can use ssh keys if required) + +## USAGE + * Save the recoverpoint.pyconf into /etc/ganglia/conf.d directory and update the management IP and sitenames (the sitenames have been lowercase'd) + * Save the recoverpoint.py into your ganglia python module dir eg: /usr/lib/ganglia/python_modules. Update the username/passwords if necessary. + * Restart gmond and a "recoverpoint" host should appear in ganglia. + +## AUTHOR + +Author: Evan Fraser <evan.fraser@trademe.co.nz> \ No newline at end of file diff --git a/recoverpoint/recoverpoint.py b/recoverpoint/recoverpoint.py new file mode 100755 index 00000000..8e3486bb --- /dev/null +++ b/recoverpoint/recoverpoint.py @@ -0,0 +1,142 @@ +#!/usr/bin/python +# Name: recoverpoint.py +# Desc: Ganglia Python module for gathering EMC recoverpoint statistics via SSH +# Author: Evan Fraser (evan.fraser@trademe.co.nz) +# Date: 01/08/2012 + + +import yaml +import warnings +import pprint +import time +import re + +with warnings.catch_warnings(): + warnings.simplefilter("ignore") + import paramiko + +descriptors = list() +NIMETRICS = { + 'time' : 0, + 'data' : {} +} +#This is the minimum interval between querying the RPA for metrics. +#Each ssh query takes 1.6s so we limit the interval between getting metrics to this interval. +NIMETRICS_CACHE_MAX = 5 + +ipaddr = '' + +#Example of data structure: +#{'RPA statistics': {'Site 1 RPA 1': {'Compression CPU usage': '0.00%', +# 'Latency (ms)': 12, +# 'Packet loss': '0.00%', +# 'Traffic': {'Application': {'SAN': '0 bps', +# 'WAN': '432 bps'}, +# 'Application (writes)': 0, +# 'Compression': 0}}, + +def define_metrics(Desc_Skel, statsDict): + for rpa in statsDict['RPA statistics']: + #pprint.pprint(statsDict['RPA statistics'][rpa]) + for metric in statsDict['RPA statistics'][rpa].keys(): + if "Latency (ms)" in metric: + descriptors.append(create_desc(Desc_Skel, { + "name" : (rpa.lower()).replace(' ','_') + '_latency', + "units" : "ms", + "description" : "latency in ms", + "groups" : "Latency" + })) + if "Traffic" in metric: + #define the Application/[SAN|WAN] metrics + for net in statsDict['RPA statistics'][rpa]['Traffic']['Application'].keys(): + #print net + descriptors.append(create_desc(Desc_Skel, { + "name" : (rpa.lower()).replace(' ','_') + '_' + net.lower(), + "units" : "bits/sec", + "description" : net + ' traffic', + "groups" : net + " Traffic", + })) + + return descriptors + +def create_desc(skel, prop): + d = skel.copy() + for k,v in prop.iteritems(): + d[k] = v + return d + +def get_metrics(name): + global NIMETRICS,ipaddr + # if interval since last check > NIMETRICS_CACHE_MAX get metrics again + metrics = {} + if (time.time() - NIMETRICS['time']) > NIMETRICS_CACHE_MAX: + + sshcon = paramiko.SSHClient() + sshcon.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + sshcon.connect(ipaddr, username='monitor',password='monitor',look_for_keys='False') + stdin, stdout, sterr = sshcon.exec_command("get_system_statistics") + rawmetrics = yaml.load(stdout) + for rpa in rawmetrics['RPA statistics']: + for metric in rawmetrics['RPA statistics'][rpa]: + if "Latency (ms)" in metric: + metrics[(rpa.lower()).replace(' ','_') + '_latency'] = rawmetrics['RPA statistics'][rpa]['Latency (ms)'] + if "Traffic" in metric: + #store the Application/[SAN|WAN] metrics + for net in rawmetrics['RPA statistics'][rpa]['Traffic']['Application'].keys(): + traffic,junk = rawmetrics['RPA statistics'][rpa]['Traffic']['Application'][net].split() + metrics[(rpa.lower()).replace(' ','_') + '_' + net.lower()] = int(traffic) + + + NIMETRICS = { + 'time': time.time(), + 'data': metrics + } + else: + metrics = NIMETRICS['data'] + return metrics[name] + + + +def metric_init(params): + global descriptors, Desc_Skel, ipaddr + print '[recoverpoint] Recieved the following parameters' + print params + ipaddr = params['mgmtip'] + print ipaddr + spoof_string = ipaddr + ':recoverpoint' + Desc_Skel = { + 'name' : 'XXX', + 'call_back' : get_metrics, + 'time_max' : 60, + 'value_type' : 'double', + 'format' : '%0f', + 'units' : 'XXX', + 'slope' : 'both', + 'description' : 'XXX', + 'groups' : 'netiron', + 'spoof_host' : spoof_string + } + + sshcon = paramiko.SSHClient() + sshcon.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + sshcon.connect(ipaddr, username='monitor',password='monitor',look_for_keys='False') + stdin, stdout, sterr = sshcon.exec_command("get_system_statistics") + statsDict = yaml.load(stdout) + sshcon.close() + descriptors = define_metrics(Desc_Skel, statsDict) + + return descriptors + +# For CLI Debuging: +if __name__ == '__main__': + params = { + 'mgmtip' : '192.168.1.100', + } + descriptors = metric_init(params) + 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) diff --git a/recoverpoint/recoverpoint.pyconf b/recoverpoint/recoverpoint.pyconf new file mode 100644 index 00000000..448b2a62 --- /dev/null +++ b/recoverpoint/recoverpoint.pyconf @@ -0,0 +1,28 @@ +# Name: recoverpoint.pyconf +# Author: Evan Fraser (evan.fraser@trademe.co.nz) +# Desc: Config file for the ganglia gmond recoverpoint module. +# Date: 03/08/2012 +# To use: Save this file in /etc/ganglia/conf.d/, update the mgmtip value to the IP address of one of your RecoverPoint management IP's and change the name_match lines below to match your site names. + +modules { + module { + name = "recoverpoint" + language = "python" + param mgmtip { + value = '192.168.1.100' + } + } +} +#/* Collection groups for the +# example python module */ +collection_group { + collect_every = 20 + time_threshold = 50 + metric { + name_match = "site1(.+)" + } + metric { + name_match = "site2(.+)" + } + } +