2
0
Fork 0
mirror of https://github.com/munin-monitoring/contrib.git synced 2018-11-08 00:59:34 +01:00

Port numbering in line with Brocade's UI and front plate. Made explicit that the plugin is Brocade specific.

This commit is contained in:
Troels Arvin 2011-03-06 20:07:39 +01:00 committed by Steve Schnepp
parent 577fb4b319
commit f3c8ba5fd1

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
""" """
Munin plugin which reports selected counters regarding ports on a SAN Munin plugin which reports selected counters regarding ports on a
FC-switch. Only enabled ports are considered. Brocade SAN FC-switch. Only enabled ports are considered.
The counters shown: The counters shown:
@ -25,20 +25,29 @@ rx_crcs: CRC errors detected in received frames.
Together with enc_out errors, CRC errors Together with enc_out errors, CRC errors
indicate a GBIC/SFP problem. indicate a GBIC/SFP problem.
words: FC transmission words (each word comprises bits: Number of bits transmitted(tx)/received(rx)
four 10-bit units). Reflects how busy the by the port. Inspecting this graph will help
port is. determining if the port is saturated.
When symlinking to the plugin, indicate hostname like this: When symlinking to the plugin, indicate hostname like this:
fc_switch_ports_HOSTNAME brocade_san_switch_ports_HOSTNAME
# Special requirements: # Special requirements:
# - the pysnmp module; on RHEL 6 with EPEL 6, you may simply yum- # - the pysnmp module; on RHEL 6 with EPEL 6, you may simply yum-
# install it # install it
""" """
# Note: In the SNMP output from brocade switches, the interesting
# counters are named with numbers starting with 1, while the
# ports' real names on the box and in the administration interface
# start with 0. And there doesn't seem to be a way to map between
# ifDesc and the interesting crc and enc_out counters :-(
# Therefore, this plugin is Brocade-specific, and thus some
# manipulation of port numbers are performed for the output
# of this plugin (see comments marked ARGH below).
# TODOs: # TODOs:
# - implement snmpconf # - implement snmpconf?
# Munin magic markers # Munin magic markers
#%# family=manual #%# family=manual
@ -81,16 +90,15 @@ fc_switch_ports_HOSTNAME
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# ==================================================================== # ====================================================================
# $Id: fc_switch_ports_ 15423 2011-03-01 13:21:14Z tra $ # $Id: brocade_san_switch_ports_ 15443 2011-03-03 12:23:56Z tra $
import os, sys, re import os, sys, re
from pysnmp.entity.rfc3413.oneliner import cmdgen from pysnmp.entity.rfc3413.oneliner import cmdgen
my_canonical_name = 'fc_switch_ports_' # If called as - e.g. - my_canonical_name = 'brocade_san_switch_ports_' # If called as - e.g. -
# fc_switch_ports_sansw1,then # brocade_san_switch_ports_sansw1,then
# sansw1 will be interpreted as # sansw1 will be interpreted as
# the host_name # the host_name
community = 'public'
# For reference: # For reference:
# SW-MIB::swFCPortLinkState = .1.3.6.1.4.1.1588.2.1.1.1.6.2.1.6 # SW-MIB::swFCPortLinkState = .1.3.6.1.4.1.1588.2.1.1.1.6.2.1.6
@ -116,11 +124,15 @@ descriptions = {
'rx_crcs' : 'the number of CRC errors detected for frames received', 'rx_crcs' : 'the number of CRC errors detected for frames received',
'enc_out' : 'encoding errors outside FC frame', 'enc_out' : 'encoding errors outside FC frame',
'enc_out_per_mframe': 'enc errors outside FC frame, per million frames of rx+tx trafic', 'enc_out_per_mframe': 'enc errors outside FC frame, per million frames of rx+tx trafic',
'words' : 'transmitted/received words' 'bits' : 'received(rx)/transmitted(tx) bits'
} }
# These counter types don't distinguish rrd_types = {
combined_tx_rx_countertypes = [ 'rx_crcs', 'enc_out', 'enc_out_per_mframe' ] 'rx_crcs' : 'GAUGE',
'enc_out' : 'GAUGE',
'enc_out_per_mframe': 'GAUGE',
'bits' : 'COUNTER'
}
# Some helper functions: # Some helper functions:
@ -167,68 +179,64 @@ def varBindTable2plainDict(varBindTable):
def print_config(host_name,enabled_ports): def print_config(host_name,enabled_ports):
print('host_name %s' % host_name) print('host_name %s' % host_name)
# Per-port
for counter_type in descriptions: for counter_type in descriptions:
for portnum in enabled_ports: for portnum in enabled_ports:
print('multigraph %s.port_%d' % (counter_type,portnum)) print('multigraph %s.port_%d' % (counter_type,portnum-1)) # ARGH: numbering base stuff
print('graph_title Port %d %s' % (portnum,counter_type)) print('graph_title Port %d %s' % (portnum-1,counter_type)) # ARGH: numbering base stuff
print('graph_args --base 1000 -l 0') print('graph_args --base 1000 -l 0')
print('graph_category SAN') print('graph_category SAN')
print('graph_info This graph shows the count of %s' % descriptions[counter_type]) print('graph_info This graph shows the count of %s' % descriptions[counter_type])
# for some of the graphs, there is an in/out aspect if counter_type == 'bits':
if counter_type in combined_tx_rx_countertypes: print('graph_vlabel bits rx (-) / tx (+) per ${graph_period}')
print('graph_order rx tx')
print('rx.label rx')
print('rx.graph no')
print('rx.type %s' % rrd_types[counter_type])
print('rx.max 20000000000') # initial-spike prevention: 20Gbit/s is max FC speed
print('tx.label bps')
print('tx.negative rx')
print('tx.type %s' % rrd_types[counter_type])
print('tx.max 20000000000') # initial-spike prevention: 20Gbit/s is max FC speed
else:
print('graph_vlabel count') print('graph_vlabel count')
print('count.label count') print('count.label count')
print('count.min 0') print('count.type %s' % rrd_types[counter_type])
# enc_out_per_mframe is special; others will just get the implied default (GAUGE)
if counter_type == 'enc_out_per_mframe':
print('count.type COUNTER')
else:
print('graph_vlabel units in (-) / out (+) per ${graph_period}')
print('graph_order tx rx')
print('tx.label tx')
print('tx.graph no')
print('tx.min 0')
print('rx.label tx')
print('rx.negative tx')
print('rx.min 0')
print('rx.info units transmitted/received by this interface')
# Totals
for counter_type in descriptions: for counter_type in descriptions:
print('multigraph %s' % counter_type) print('multigraph %s' % counter_type)
print('graph_title %s total %s' % (host_name,counter_type)) print('graph_title %s total %s' % (host_name,counter_type))
print('graph_args --base 1000 -l 0') print('graph_args --base 1000 -l 0')
print('graph_category SAN') print('graph_category SAN')
print('graph_info This graph shows the total count of %s across all ports' % descriptions[counter_type]) print('graph_info This graph shows the total count of %s across all ports' % descriptions[counter_type])
if counter_type in combined_tx_rx_countertypes:
if counter_type == 'bits':
print('graph_vlabel bits rx (-) / tx (+) per ${graph_period}')
print('rx.label rx')
print('rx.graph no')
print('rx.type %s' % rrd_types[counter_type])
print('rx.max 800000000000') # initial-spike prevention: Assuming a max of 40 ports with each 20Gbit/s max
print('tx.label bps')
print('tx.negative rx')
print('tx.type %s' % rrd_types[counter_type])
print('tx.max 800000000000') # initial-spike prevention: Assuming a max of 40 ports with each 20Gbit/s max
else:
print('graph_vlabel count') print('graph_vlabel count')
print('count.label count') print('count.label count')
print('count.min 0') print('count.type %s' % rrd_types[counter_type])
# enc_out_per_mframe is special; others will just get the implied default (GAUGE)
if counter_type == 'enc_out_per_mframe':
print('count.type COUNTER')
else:
print('graph_vlabel units in (-) / out (+) per ${graph_period}')
print('tx.label tx')
print('tx.graph no')
print('tx.min 0')
print('rx.label tx')
print('rx.negative tx')
print('rx.min 0')
print('rx.info units transmitted/received')
# We don't care for disabled ports # We don't care for disabled ports
def get_enabled_ports(host_name): def get_enabled_ports(host_name,community):
link_states = get_port_values(host_name,port_link_state_oidstr) link_states = get_port_values(host_name,community,port_link_state_oidstr)
# status 1 means enabled # status 1 means enabled
return [ portnum for portnum in link_states if link_states[portnum] == 1 ] return [ portnum for portnum in link_states if link_states[portnum] == 1 ]
# Talk to the SNMP agent performing the equivalent of an snmpwalk from # Talk to the SNMP agent performing the equivalent of an snmpwalk from
# the starting point indicated by the oid_start_tpl tuple. # the starting point indicated by the oid_start_tpl tuple.
# Handle potential errors. # Handle potential errors.
def pull_values(host_name,oid_start_tpl): def pull_values(host_name,community,oid_start_tpl):
try: try:
errorIndication, errorStatus, errorIndex, varBindTable = cmdgen.CommandGenerator().bulkCmd( errorIndication, errorStatus, errorIndex, varBindTable = cmdgen.CommandGenerator().bulkCmd(
cmdgen.CommunityData('whatever', community), cmdgen.CommunityData('whatever', community),
@ -249,9 +257,9 @@ def pull_values(host_name,oid_start_tpl):
# Combine oidstr2tupl, pull_values and varBindTable2plainDict. # Combine oidstr2tupl, pull_values and varBindTable2plainDict.
# Return dict of port-number => count # Return dict of port-number => count
def get_port_values(host_name,oid_start_str): def get_port_values(host_name,community,oid_start_str):
return varBindTable2plainDict( return varBindTable2plainDict(
pull_values(host_name,oidstr2tuple(oid_start_str)) pull_values(host_name,community,oidstr2tuple(oid_start_str))
) )
# Initial sanity check # Initial sanity check
@ -273,7 +281,13 @@ if match:
else: else:
bailout('Missing host_name and/or counter type') bailout('Missing host_name and/or counter type')
enabled_ports = get_enabled_ports(host_name) # Determine SNMP community
try:
community = os.environ['community']
except:
community = 'public'
enabled_ports = get_enabled_ports(host_name,community)
# See how we were called # See how we were called
if n_args == 2: if n_args == 2:
@ -291,27 +305,27 @@ if n_args == 2:
# Prepare some structures # Prepare some structures
counters = {} counters = {}
counters['rx_crcs' ] = get_port_values(host_name,oidstrs['rx_crcs' ]) counters['rx_crcs' ] = get_port_values(host_name,community,oidstrs['rx_crcs' ])
counters['enc_out' ] = get_port_values(host_name,oidstrs['enc_out' ]) counters['enc_out' ] = get_port_values(host_name,community,oidstrs['enc_out' ])
counters['tx_frames'] = get_port_values(host_name,oidstrs['tx_frames']) counters['rx_frames'] = get_port_values(host_name,community,oidstrs['rx_frames'])
counters['rx_frames'] = get_port_values(host_name,oidstrs['rx_frames']) counters['tx_frames'] = get_port_values(host_name,community,oidstrs['tx_frames'])
counters['tx_words' ] = get_port_values(host_name,oidstrs['tx_words' ]) counters['rx_words' ] = get_port_values(host_name,community,oidstrs['rx_words' ])
counters['rx_words' ] = get_port_values(host_name,oidstrs['rx_words' ]) counters['tx_words' ] = get_port_values(host_name,community,oidstrs['tx_words' ])
totals = {} totals = {}
totals['rx_crcs'] = 0 totals['rx_crcs'] = 0
totals['enc_out'] = 0 totals['enc_out'] = 0
totals['enc_out_per_mframe'] = 0 totals['enc_out_per_mframe'] = 0
totals['tx_frames'] = 0
totals['rx_frames'] = 0 totals['rx_frames'] = 0
totals['tx_words'] = 0 totals['tx_frames'] = 0
totals['rx_words'] = 0 totals['rx_bits'] = 0
totals['tx_bits'] = 0
# special handling of enc_out per million frames # special handling of enc_out per million frames
counters['enc_out_per_mframe'] = {} counters['enc_out_per_mframe'] = {}
for k in counters['tx_frames'].keys(): for k in counters['rx_frames'].keys():
if counters['tx_frames'][k] + counters['rx_frames'][k] > 0: if counters['rx_frames'][k] + counters['tx_frames'][k] > 0:
counters['enc_out_per_mframe'][k] = 1000000*counters['enc_out'][k] / (counters['tx_frames'][k] + counters['rx_frames'][k]) counters['enc_out_per_mframe'][k] = 1000000*counters['enc_out'][k] / (counters['rx_frames'][k] + counters['tx_frames'][k])
else: else:
counters['enc_out_per_mframe'][k] = 0 counters['enc_out_per_mframe'][k] = 0
@ -323,31 +337,31 @@ for k in counters['tx_frames'].keys():
# Per-port values # Per-port values
for portnum in enabled_ports: for portnum in enabled_ports:
for counter_type in descriptions: for counter_type in descriptions:
print('multigraph %s.port_%d' % (counter_type,portnum)) print('multigraph %s.port_%d' % (counter_type,portnum-1)) # ARGH: numbering base stuff
# for some of the graphs, there is an in/out aspect # For some of the graphs, there is an in/out aspect, for others
if counter_type in combined_tx_rx_countertypes: # they are combined or not applicable
if counter_type == 'bits':
rx_value = counters['rx_words'][portnum]
tx_value = counters['tx_words'][portnum]
rx_bits = rx_value * 40 # Each word consists of four
tx_bits = tx_value * 40 # 10-bit units.
print('rx.value %d' % rx_bits)
print('tx.value %d' % tx_bits)
totals['rx_bits'] += rx_bits
totals['tx_bits'] += tx_bits
else:
print('count.value %d' % counters[counter_type][portnum]) print('count.value %d' % counters[counter_type][portnum])
totals[counter_type] += counters[counter_type][portnum] totals[counter_type] += counters[counter_type][portnum]
else:
tx_key = 'tx_%s' % counter_type
rx_key = 'rx_%s' % counter_type
tx_value = counters[tx_key][portnum]
rx_value = counters[rx_key][portnum]
print('tx.value %d' % tx_value)
print('rx.value %d' % rx_value)
totals[tx_key] += tx_value
totals[rx_key] += rx_value
# Totals # Totals
for counter_type in descriptions: for counter_type in descriptions:
print('multigraph %s' % (counter_type)) print('multigraph %s' % (counter_type))
# for some of the graphs, there is an in/out aspect # For some of the graphs, there is an in/out aspect, for others
if counter_type in combined_tx_rx_countertypes: # they are combined or not applicable
print('count.value %d' % totals[counter_type]) if counter_type == 'bits':
print('rx.value %d' % totals['rx_bits'])
print('tx.value %d' % totals['tx_bits'])
else: else:
tx_key = 'tx_%s' % counter_type print('count.value %d' % totals[counter_type])
rx_key = 'rx_%s' % counter_type
print('tx.value %d' % totals[tx_key])
print('rx.value %d' % totals[rx_key])