summaryrefslogtreecommitdiff
path: root/extra
diff options
context:
space:
mode:
authorSaku Ytti <saku@ytti.fi>2013-04-20 00:41:23 +0300
committerSaku Ytti <saku@ytti.fi>2013-04-20 00:41:23 +0300
commitf1287a7925901bf3518eb69079304bfb97f7434d (patch)
tree3dd2779e4d087b73b99d7136f37db88c76091d8d /extra
parent5a393d6102655f575549311e6b250534b4dcbb59 (diff)
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.
Diffstat (limited to 'extra')
-rw-r--r--extra/rest_client.rb25
-rwxr-xr-xextra/syslog.rb111
2 files changed, 136 insertions, 0 deletions
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'