|
#!/usr/bin/ruby |
|
###################################################################################################### |
|
# |
|
# Description: |
|
# Handle SNMP Traps for IETF LinkUp and LinkDown |
|
# * Post All Traps to Slack Channel provided by SLACK_WEBHOOK_URL |
|
# |
|
###################################################################################################### |
|
# |
|
# Author: Nick Peelman |
|
# |
|
###################################################################################################### |
|
# |
|
# Usage: |
|
# interface_trap.rb -H <hostname> -c <community> -i <index> -a <admin-status> -o <oper-status> |
|
# Parameters: |
|
# * <hostname>: Hostname or IP that sent the trap |
|
# * <community>: SNMP Community with Read privileges to the IF-MIB |
|
# * <index>: the index of the interface that changed state |
|
# * <admin-status>: the admin status as sent by the trap |
|
# * <oper-status>: the oper status as sent by the trap |
|
# |
|
###################################################################################################### |
|
# |
|
# Requirements |
|
# * gem install slack-notifier |
|
# * gem install snmp |
|
# |
|
###################################################################################################### |
|
|
|
require 'optparse' |
|
require 'slack-notifier' |
|
require 'snmp' |
|
###################################################################################################### |
|
# CONFIGURATION Parameters |
|
# |
|
SLACK_WEBHOOK_URL = "<slack-webhook-url>" |
|
SLACK_CHANNEL = "#port-notifications" |
|
|
|
###################################################################################################### |
|
|
|
OID_IFDESCR = SNMP::ObjectName.new "1.3.6.1.2.1.2.2.1.2" |
|
OID_IFALIAS = SNMP::ObjectName.new "1.3.6.1.2.1.31.1.1.1.18" |
|
OID_IFADMIN = SNMP::ObjectName.new "1.3.6.1.2.1.2.2.1.7" |
|
OID_IFOPER = SNMP::ObjectName.new "1.3.6.1.2.1.2.2.1.8" |
|
OID_IFSPEED = SNMP::ObjectName.new "1.3.6.1.2.1.31.1.1.1.15" |
|
OID_IFMTU = SNMP::ObjectName.new "1.3.6.1.2.1.2.2.1.4" |
|
OID_SYSNAME = SNMP::ObjectName.new "1.3.6.1.2.1.1.5.0" |
|
|
|
OID_ARRAY = [ |
|
OID_IFDESCR, |
|
OID_IFALIAS, |
|
OID_IFADMIN, |
|
OID_IFOPER, |
|
OID_IFSPEED, |
|
OID_IFMTU, |
|
OID_SYSNAME |
|
] |
|
|
|
|
|
OID_VALUES_IFSTATUS = { |
|
1 => "Up", |
|
2 => "Down", |
|
3 => "Testing", |
|
4 => "Unknown", |
|
5 => "Dormant", |
|
6 => "Not Present", |
|
7 => "Lower Layer Down" |
|
} |
|
|
|
@sysname = '' |
|
@ifdescr = '' |
|
@ifalias = '' |
|
@ifadminstatus = '' |
|
@ifoperstatus = '' |
|
@ifspeed |
|
@ifmtu |
|
|
|
def parse_args(args = {}) |
|
@print = args[:print].nil? ? true : args[:print] |
|
@exit = args[:exit].nil? ? true : args[:exit] |
|
|
|
# now, handle the arguments... |
|
@opts = OptionParser.new |
|
@opts.on("-h", "--host HOSTNAME", "Hostname or IP") do |h| |
|
@hostname = h |
|
end |
|
@opts.on("-c", "--community COMMUNITY", "snmp community") do |c| |
|
@community = c |
|
end |
|
@opts.on("-i", "--index INDEX", "ifIndex") do |i| |
|
@ifindex = i |
|
end |
|
@opts.on("-a", "--admin STATUS", "ifAdminStatus") do |a| |
|
@ifadminstatus = a |
|
end |
|
@opts.on("-o", "--oper STATUS", "ifOperStatus") do |o| |
|
@ifoperstatus = o |
|
end |
|
@opts.on("-d", "--debug", "print output, don't send to slack") do |d| |
|
@debug = true |
|
end |
|
@opts.on_tail("-h", "--help", "Show this message") do |
|
puts @opts |
|
exit |
|
end |
|
@opts.parse! |
|
end |
|
|
|
def number_to_ifstatus(number) |
|
if number.to_i == 0 |
|
return "Down" |
|
elsif number.to_i == 1 |
|
return "Up" |
|
else |
|
return "Unknown" |
|
end |
|
end |
|
UNITS = ['Kbps', 'Mbps', 'Gbps', 'Tbps', 'Pbps', 'Ebps', 'Zbps', 'Ybps'] |
|
|
|
def safe_vb_to_int(number, safe = 0) |
|
Integer(number) rescue safe |
|
end |
|
|
|
def number_to_human_bps(number) |
|
pos = 1 |
|
n = number |
|
while (n > 999) |
|
n = n/1000 |
|
pos = pos+1 |
|
end |
|
return "#{n}#{UNITS[pos]}" |
|
end |
|
|
|
def fetch_oids(host, community) |
|
SNMP::Manager.open(host: host, community: community) do |manager| |
|
response = manager.get(OID_ARRAY) |
|
response.each_varbind do |vb| |
|
case vb.name.to_str |
|
when OID_SYSNAME.to_str |
|
@sysname = vb.value |
|
break |
|
when OID_IFDESCR.to_str |
|
next if vb.value.class === SNMP::NoSuchInstance |
|
@ifdescr = vb.value |
|
when OID_IFALIAS.to_str |
|
next if vb.value.class === SNMP::NoSuchInstance |
|
@ifalias = vb.value |
|
when OID_IFADMIN.to_str |
|
@ifadminstatus = safe_vb_to_int(vb.value, 6) |
|
when OID_IFOPER.to_str |
|
@ifoperstatus = safe_vb_to_int(vb.value, 6) |
|
when OID_IFSPEED.to_str |
|
@ifspeed = number_to_human_bps(safe_vb_to_int(vb.value, 0)) |
|
when OID_IFMTU.to_str |
|
@ifmtu = safe_vb_to_int(vb.value, 0) |
|
else |
|
puts "Error: unknown VarBind returned: #{vb}" |
|
end |
|
end |
|
end |
|
end |
|
|
|
def send_slack_message(message, attachments) |
|
notifier = Slack::Notifier.new SLACK_WEBHOOK_URL |
|
notifier.post({text:"#{message}", attachments: attachments}) |
|
end |
|
|
|
def get_address_array(peer_group) |
|
addresses = [] |
|
peer_group.each_value do |v| |
|
addresses << v.fetch(:address) |
|
end |
|
return addresses |
|
end |
|
|
|
def ip_to_peer_name(ip_address, peer_group) |
|
peer_group.each do |k,v| |
|
return k if v.fetch(:address) == ip_address |
|
end |
|
return nil |
|
end |
|
|
|
def main |
|
parse_args |
|
|
|
@sysname = @hostname |
|
@ifdescr = '' |
|
@ifalias = '' |
|
@ifadminstatus = '' |
|
@ifoperstatus = '' |
|
@ifspeed = '' |
|
@ifmtu = '' |
|
@msgcolor = "#cccccc" |
|
|
|
OID_IFDESCR.push(@ifindex) |
|
OID_IFALIAS.push(@ifindex) |
|
OID_IFOPER.push(@ifindex) |
|
OID_IFADMIN.push(@ifindex) |
|
OID_IFSPEED.push(@ifindex) |
|
OID_IFMTU.push(@ifindex) |
|
|
|
fetch_oids(@hostname, @community) |
|
|
|
case @ifoperstatus |
|
when 1 |
|
message_head = ":white_check_mark: Port Up" |
|
@msgcolor = 'good' |
|
when 2 |
|
message_head = ":x: Port Down" |
|
@msgcolor = 'danger' |
|
when 3 |
|
message_head = ":construction: Port Testing" |
|
@msgcolor ='warning' |
|
when 4 |
|
message_head = ":game_die: Port Unknown" |
|
@msgcolor = 'warning' |
|
when 5 |
|
message_head = ":mountain: Port Dormant" |
|
@msgcolor = 'warning' |
|
when 6 |
|
message_head = ":ghost: Not Present" |
|
@msgcolor = 'warning' |
|
when 7 |
|
message_head = ":x: Lower Layer Down" |
|
@msgcolor = 'warning' |
|
else |
|
message_head = ":interrobang: Unknown Oper Status" |
|
@msgcolor = 'warning' |
|
end |
|
|
|
if (@ifalias.match(/((G|g)enerator)|(.*#?trap-?disable.*)|(.*#?disable-?trap.*)|(.*#?no-?trap.*)/)) |
|
puts "Ignoring Trap due to ifAlias: \"#{@ifalias}\"" |
|
exit |
|
end |
|
|
|
ifindex_friendly = "Index: #{@ifindex}" |
|
message = "#{message_head}: #{@sysname} [#{@ifalias.empty? ? @ifdescr : @ifalias}]" |
|
attachments = [{ |
|
'color': @msgcolor, |
|
'fallback': message, |
|
'fields': [ |
|
{ |
|
'title': "Device", |
|
'value': "#{@sysname}", |
|
'short': true |
|
}, |
|
{ |
|
'title': "Description", |
|
'value': "#{@ifalias.empty? ? @ifdescr : @ifalias}", |
|
'short': true |
|
}, |
|
{ |
|
'title': "Details", |
|
'value': "#{@ifalias.empty? ? ifindex_friendly : @ifdescr}", |
|
'short': false |
|
}, |
|
{ |
|
'title': "Admin Status", |
|
'value': "#{OID_VALUES_IFSTATUS[@ifadminstatus]}", |
|
'short': true |
|
}, |
|
{ |
|
'title': "Oper Status", |
|
'value': "#{OID_VALUES_IFSTATUS[@ifoperstatus]}", |
|
'short': true |
|
}, |
|
{ |
|
'title': "Speed", |
|
'value': "#{@ifspeed}", |
|
'short': true |
|
}, |
|
{ |
|
'title': "MTU", |
|
'value': "#{@ifmtu}", |
|
'short': true |
|
}, |
|
|
|
] |
|
}] |
|
send_slack_message('', attachments) unless @debug |
|
puts attachments if @debug |
|
end |
|
|
|
main |