From f1287a7925901bf3518eb69079304bfb97f7434d Mon Sep 17 00:00:00 2001 From: Saku Ytti Date: Sat, 20 Apr 2013 00:41:23 +0300 Subject: Example of Syslog triggered fetch 'syslog.rb' listed to UDP port (or reads file). When IOS or JunOS style config change/commit message is seen, it triggers immediate update ot config It transports commit message (junos) remote host from which change was mde (ios) and who made the change (junos+ios). This is carried over to the 'output' methods, that is, 'git blame' will show IOS/JunOS user-name who made the change. --- extra/rest_client.rb | 25 ++++++++++++ extra/syslog.rb | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 extra/rest_client.rb create mode 100755 extra/syslog.rb (limited to 'extra') diff --git a/extra/rest_client.rb b/extra/rest_client.rb new file mode 100644 index 0000000..2c58d04 --- /dev/null +++ b/extra/rest_client.rb @@ -0,0 +1,25 @@ +module Oxidized + class RestClient + require 'net/http' + require 'json' + HOST = 'localhost' + PORT = 8888 + + class << self + def next node, opt={}, host=HOST, port=PORT + web = new host, port + web.next node, opt + end + end + + def initialize host=HOST, port=PORT + @web = Net::HTTP.new host, port + end + + def next node, opt={} + data = JSON.dump :node => node, :user => opt[:user], :msg => opt[:msg], :from => opt[:from] + @web.put '/nodes/next/' + node.to_s, data + end + + end +end diff --git a/extra/syslog.rb b/extra/syslog.rb new file mode 100755 index 0000000..10757bc --- /dev/null +++ b/extra/syslog.rb @@ -0,0 +1,111 @@ +#!/usr/bin/env ruby + +# IOS: +# logging discriminator CFG mnemonics includes CONFIG_I +# logging host SERVER discriminator CFG + +# JunOS: +# set system syslog host SERVER interactive-commands notice +# set system syslog host SERVER match "^mgd\[[0-9]+\]: UI_COMMIT: .*" + +# sudo setcap 'cap_net_bind_service=+ep' /usr/bin/ruby + +# exit if fork ## TODO: proper daemonize + +require 'socket' +require 'resolv' +require './rest_client' + +module Oxidized + class SyslogMonitor + NAME_MAP = { + /(.*)\.ip\.tdc\.net/ => '\1', + /(.*)\.ip\.fi/ => '\1', + } + PORT = 514 + FILE = 'messages' + MSG = { + :ios => '%SYS-5-CONFIG_I:', + :junos => 'UI_COMMIT:', + } + + class << self + def udp port=PORT, listen=0 + io = UDPSocket.new + io.bind listen, port + new io, :udp + end + def file syslog_file=FILE + io = open syslog_file, 'r' + io.seek 0, IO::SEEK_END + new io, :file + end + end + + private + + def initialize io, mode=:udp + @mode = mode + run io + end + + def rest opt + Oxidized::RestClient.next opt[:name], :user => opt[:user], :msg => opt[:msg], + :from => opt[:from] + end + + def ios ip, log, i + # TODO: we need to fetch 'ip/name' in mode == :file here + user = log[i+5] + from = log[-1][1..-2] + rest( :user => user, :from => from, :model => 'ios', :ip => ip, + :name => getname(ip) ) + end + + def jnpr ip, log, i + # TODO: we need to fetch 'ip/name' in mode == :file here + user = log[i+2][1..-2] + msg = log[(i+6)..-1].join(' ')[10..-2] + msg = nil if msg == 'none' + rest( :user => user, :msg => msg, :model => 'jnpr', :ip => ip, + :name => getname(ip) ) + end + + def handle_log log, ip + log = log.to_s.split ' ' + if i = log.index(MSG[:ios]) + ios ip, log, i + elsif i = log.index(MSG[:junos]) + jnpr ip, log, i + end + end + + def run io + while true + log = select [io] + log, ip = log.first.first, nil + if @mode == :udp + log, ip = log.recvfrom_nonblock 2000 + ip = ip.last + else + begin + log = log.read_nonblock 2000 + rescue EOFError + sleep 1 + retry + end + end + handle_log log, ip + end + end + + def getname ip + name = (Resolv.getname ip.to_s rescue ip) + NAME_MAP.each { |re, sub| name.sub! re, sub } + name + end + end +end + +Oxidized::SyslogMonitor.udp +#Oxidized::SyslogMonitor.file '/var/log/poop' -- cgit v1.2.1