Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Added EMC RecoverPoint gmond module #75

Merged
merged 3 commits into from

2 participants

@evanjfraser

Remembered a README this time :)
Requires paramiko & YAML python modules as well as SSH access to the Recoverpoint appliance.

@evanjfraser

Added a Brocade Fibrechannel switch module as well.

@jbuchbinder jbuchbinder merged commit be072b7 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 2, 2012
  1. Added EMC RecoverPoint module

    efraser authored
Commits on Aug 3, 2012
  1. @evanjfraser
Commits on Aug 6, 2012
  1. @evanjfraser

    Added FC error metrics

    evanjfraser authored
This page is out of date. Refresh to see the latest.
View
28 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>
+
View
265 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)
View
25 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"
+ }
+ }
View
18 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 &lt;evan.fraser@trademe.co.nz&gt;
View
142 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)
View
28 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(.+)"
+ }
+ }
+
Something went wrong with that request. Please try again.