#!/usr/bin/env ruby # SNMP Interface Metrics # === # # Collect device network interface metrics using the IF-MIB (RFC 2863) interface. # # This script uses the 64-bit "HC" (high capacity) metrics which I am assuming most # modern devices probably support. If there is no HC support, use --low-capacity # flag. # # DEPENDENCIES: # gem: sensu-plugin # gem: snmp # # USAGE: # # snmp-if-metrics.rb -C community -h host # # Only in/out octets are displayed by default to keep the number of metrics low. Additional # Metrics can be generated: # # - `--include-down`: Output metrics for interfaces not marked up (ifOperStatus != 1). # By default only metrics for interfaces in the up state are printed. # - `--include-errors`: Output error metrics such as in/out errors, discards, etc. # - `--include-name`: Include the interface name with the interface index number when generating # the metric name. This can help identify interfaces in graphite when browsing. # - `--include-packet-counts`: Output packet metrics. # - `--include-speed`: Output a metric for the interface's speed. Useful when constructing # a view of an interfaces capacity in graphite. # - `--version`: Set SNMP protocol version (SNMPv2c or SNMPv1) # - `--low-capacity`: Use low capacity counters # # LICENSE: # Copyright (c) 2013 Joe Miller # # Released under the same terms as Sensu (the MIT license); see LICENSE # for details. # rubocop:disable VariableName require 'sensu-plugin/metric/cli' require 'snmp' def graphite_safe_name(name) name.gsub(/\W/, '_') end # Class that collects SNMP if metrics and outputs them in graphite format. class SNMPIfStatsGraphite < Sensu::Plugin::Metric::CLI::Graphite option :host, short: '-h host', boolean: true, default: '127.0.0.1', required: true option :community, short: '-C snmp community', boolean: true, default: 'public' option :scheme, short: '-s SCHEME', long: '--scheme SCHEME', default: 'snmp.interfaces', description: 'prefix to attach to graphite path' option :include_pkt_metrics, short: '-k', long: '--include-packet-counts', boolean: true, default: false, description: 'Include packets metrics, eg: IfInUcastPkts, IfOutUcastPkts, IfInBroadcastPkts, etc' option :include_speed, short: '-s', long: '--include-speed', boolean: true, default: false, description: 'Output interface max speed (ifSpeed) metric' option :include_name, short: '-n', long: '--include-name', boolean: true, default: false, description: 'append ifName to ifIndex when generating metric name, eg: "1__OUTSIDE"' option :include_error_metrics, short: '-e', long: '--include-errors', boolean: true, default: false, description: 'Include error metrics in output' option :include_down_interfaces, short: '-d', long: '--include-down', boolean: true, default: false, description: 'output metrics for all interfaces including those marked down' option :verbose, short: '-v', long: '--verbose', boolean: true, default: false, description: 'verbose output for debugging' option :version, short: '-V VERSION', long: '--version VERSION', default: :SNMPv2c, description: 'SNMP version (SNMPv1 or SNMPv2c)', proc: proc(&:intern) option :low_capacity, long: '--low-capacity', boolean: true, default: false, description: 'Use low capacity counters' def run # rubocop:disable Metrics/AbcSize if_table_HC_columns = %w( ifHCInOctets ifHCOutOctets ifHCInUcastPkts ifHCOutUcastPkts ifHCInMulticastPkts ifHCOutMulticastPkts ifHCInBroadcastPkts ifHCOutBroadcastPkts ) if_table_LC_columns = %w( ifInOctets ifOutOctets ifInUcastPkts ifOutUcastPkts ifInMulticastPkts ifOutMulticastPkts ifInBroadcastPkts ifOutBroadcastPkts ) if_table_common_columns = %w( ifIndex ifOperStatus ifName ifDescr ifInErrors ifOutErrors ifInDiscards ifOutDiscards ifSpeed ) if_table_columns = if_table_common_columns + (config[:low_capacity] ? if_table_LC_columns : if_table_HC_columns) SNMP::Manager.open(host: config[:host].to_s, community: config[:community].to_s, version: config[:version]) do |manager| manager.walk(if_table_columns) do |row_array| # turn row (an array) into a hash for eaiser access to the columns row = Hash[*if_table_columns.zip(row_array).flatten] puts row.inspect if config[:verbose] if_name = config[:include_name] ? "#{row['ifIndex'].value}__#{graphite_safe_name(row['ifName'].value.to_s)}" : row['ifIndex'].value.to_s next if row['ifOperStatus'].value != 1 && !config[:include_down_interfaces] in_octets = config[:low_capacity] ? 'ifInOctets' : 'ifHCInOctets' out_octets = config[:low_capacity] ? 'ifOutOctets' : 'ifHCOutOctets' output "#{config[:scheme]}.#{if_name}.#{in_octets}", row[in_octets].value output "#{config[:scheme]}.#{if_name}.#{out_octets}", row[out_octets].value output "#{config[:scheme]}.#{if_name}.ifSpeed", row['ifSpeed'].value if config[:include_speed] if config[:include_error_metrics] output "#{config[:scheme]}.#{if_name}.ifInErrors", row['ifInErrors'].value output "#{config[:scheme]}.#{if_name}.ifOutErrors", row['ifOutErrors'].value output "#{config[:scheme]}.#{if_name}.ifInDiscards", row['ifInDiscards'].value output "#{config[:scheme]}.#{if_name}.ifOutDiscards", row['ifOutDiscards'].value end if config[:include_pkt_metrics] in_ucast_pkts = config[:low_capacity] ? 'ifInUcastPkts' : 'ifHCInUcastPkts' out_ucast_pkts = config[:low_capacity] ? 'ifOutUcastPkts' : 'ifHCOutUcastPkts' in_mcast_pkts = config[:low_capacity] ? 'ifInMulticastPkts' : 'ifHCInMulticastPkts' out_mcast_pkts = config[:low_capacity] ? 'ifOutMulticastPkts' : 'ifHCOutMulticastPkts' in_bcast_pkts = config[:low_capacity] ? 'ifInBroadcastPkts' : 'ifHCInBroadcastPkts' out_bcast_pkts = config[:low_capacity] ? 'ifOutBroadcastPkts' : 'ifHCOutBroadcastPkts' output "#{config[:scheme]}.#{if_name}.#{in_ucast_pkts}", row[in_ucast_pkts].value output "#{config[:scheme]}.#{if_name}.#{out_ucast_pkts}", row[out_ucast_pkts].value output "#{config[:scheme]}.#{if_name}.#{in_mcast_pkts}", row[in_mcast_pkts].value output "#{config[:scheme]}.#{if_name}.#{out_mcast_pkts}", row[out_mcast_pkts].value output "#{config[:scheme]}.#{if_name}.#{in_bcast_pkts}", row[in_bcast_pkts].value output "#{config[:scheme]}.#{if_name}.#{out_bcast_pkts}", row[out_bcast_pkts].value end end end ok end end