From 96668cc2dc0367e3d1a3ec94265ce889b62d8ada Mon Sep 17 00:00:00 2001 From: Saku Ytti Date: Fri, 19 Apr 2013 12:36:45 +0300 Subject: Add restful API --- .rspec | 1 + README.md | 3 ++- TODO.md | 5 ----- lib/oxidized/api/domain.rb | 48 ++++++++++++++++++++++++++++++++++++++++ lib/oxidized/api/rest.rb | 43 +++++++++++++++++++++++++++++++++++ lib/oxidized/config/bootstrap.rb | 3 ++- lib/oxidized/config/defaults.rb | 1 + lib/oxidized/core.rb | 15 +++++++++++-- lib/oxidized/manager.rb | 1 + lib/oxidized/node.rb | 19 ++++++++++++++++ lib/oxidized/nodes.rb | 20 ++++++++++++----- lib/oxidized/output/git.rb | 1 - spec/nodes_spec.rb | 13 ++++++----- 13 files changed, 151 insertions(+), 22 deletions(-) create mode 100644 .rspec create mode 100644 lib/oxidized/api/domain.rb create mode 100644 lib/oxidized/api/rest.rb diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..53607ea --- /dev/null +++ b/.rspec @@ -0,0 +1 @@ +--colour diff --git a/README.md b/README.md index 5e2b584..237578b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Pitch * automatically adds/removes threads to meet configured retrieval interval - * can move node immediately to head-of-queue (maybe trigger from snmp trap or syslog), to be serviced by next spawned thread + * restful API to move node immediately to head-of-queue (maybe trigger from snmp trap or syslog), to be serviced by next spawned thread (GET /nodes/next/$node) + * restful API to reload list of nodes (GET /nodes/reload) # Install early days, but try to run it and edit ~/.config/oxidized/config diff --git a/TODO.md b/TODO.md index e2ba06d..d67a0bc 100644 --- a/TODO.md +++ b/TODO.md @@ -15,11 +15,6 @@ I don't really need it myself, since I don't have platforms where it would be ne * should we try to avoid max threads from being hit? (like maybe non-success thread is pulling average?) -# restful API (puma+sinatra): - * ask to reload node list - * move node to head of queue - - # config * save keys as strings, load as symbols? diff --git a/lib/oxidized/api/domain.rb b/lib/oxidized/api/domain.rb new file mode 100644 index 0000000..fa6a1c7 --- /dev/null +++ b/lib/oxidized/api/domain.rb @@ -0,0 +1,48 @@ +# this is not used, just added here if I want to revive it + +module Oxidized + require 'socket' + require 'json' + module API + class Domain + def initialize nodes, socket=CFG.api + puts 'here' + @nodes = nodes + File.unlink socket rescue Errno::ENOENT + @server = UNIXServer.new socket + end + def work + io = select [@server], nil, nil, Config::Sleep + process io.first.first.accept if io + end + def read + @socket.recv 1024 + end + def write data='' + begin + @socket.send JSON.dump(data), 0 + rescue Errno::EPIPE + end + end + def process socket + @socket = socket + cmd = read + cmd, data = cmd.split /\s+/, 2 + data = data.to_s.chomp + case cmd + when /next/i + @nodes.next data + write 'OK' + when /reload/i + @nodes.load if data.match /nodes/i + write 'OK' + when /list/i + write @nodes.map{|e|e.name} + when /node/i + write @nodes.show(data) + end + @socket.close + end + end + end +end diff --git a/lib/oxidized/api/rest.rb b/lib/oxidized/api/rest.rb new file mode 100644 index 0000000..ee06bc8 --- /dev/null +++ b/lib/oxidized/api/rest.rb @@ -0,0 +1,43 @@ +module Oxidized + require 'webrick' + require 'json' + module API + class Rest + def initialize nodes, listen + @nodes = nodes + addr, port = listen.to_s.split ':' + port, addr = addr, nil if not port + @web = WEBrick::HTTPServer.new :BindAddress=>addr, :Port=>port + maps + end + def work + req = select @web.listeners, nil, nil, Config::Sleep + while req + @web.run req.first.first.accept + req = select @web.listeners, nil, nil, 0 + end + end + def maps + @web.mount_proc '/nodes' do |req, res| + #script_name, #path_info + case req.path_info[1..-1] + # /nodes/reload - reloads list of nodes + when 'reload' + @nodes.load + res.body = JSON.dump 'OK' + # /nodes/next/node - moves node to head of queue + when /next\/(.*)/ + @nodes.next $1 + res.body = JSON.dump 'OK' + # /nodes/list - returns list of nodes + when 'list' + res.body = JSON.dump @nodes.list + # /nodes/show/node - returns data about node + when /show\/(.*)/ + res.body = JSON.dump @nodes.show $1 + end + end + end + end + end +end diff --git a/lib/oxidized/config/bootstrap.rb b/lib/oxidized/config/bootstrap.rb index e44dde7..2991fa3 100644 --- a/lib/oxidized/config/bootstrap.rb +++ b/lib/oxidized/config/bootstrap.rb @@ -10,6 +10,7 @@ module Oxidized CFG.threads = 10 CFG.timeout = 5 CFG.prompt = /^([\w\.\-@]{3,30}[#>]\s?)$/ + CFG.rest = 8888 CFG.input = { :default => 'ssh', } @@ -17,7 +18,7 @@ module Oxidized :default => 'git', } CFG.source = { - :default => 'ascii', + :default => 'csv', } CFG.model_map = { 'cisco' => 'ios', diff --git a/lib/oxidized/config/defaults.rb b/lib/oxidized/config/defaults.rb index a07e1a6..943d90b 100644 --- a/lib/oxidized/config/defaults.rb +++ b/lib/oxidized/config/defaults.rb @@ -5,6 +5,7 @@ module Oxidized OutputDir = File.join Directory, %w(lib oxidized output) ModelDir = File.join Directory, %w(lib oxidized model) SourceDir = File.join Directory, %w(lib oxidized source) + Sleep = 1 end class << self attr_accessor :mgr diff --git a/lib/oxidized/core.rb b/lib/oxidized/core.rb index 76aa330..59292f9 100644 --- a/lib/oxidized/core.rb +++ b/lib/oxidized/core.rb @@ -4,6 +4,7 @@ module Oxidized require 'oxidized/worker' require 'oxidized/nodes' require 'oxidized/manager' + require 'oxidized/api/rest' class << self def new *args Core.new args @@ -14,8 +15,18 @@ module Oxidized def initialize args Oxidized.mgr = Manager.new nodes = Nodes.new - worker = Worker.new nodes - loop { worker.work; sleep 1 } + @worker = Worker.new nodes + @rest = API::Rest.new nodes, CFG.rest if CFG.rest + run + end + + private + + def run + while true + @worker.work + @rest ? @rest.work : sleep(Config::Sleep) + end end end end diff --git a/lib/oxidized/manager.rb b/lib/oxidized/manager.rb index 0edf9e7..8c53d64 100644 --- a/lib/oxidized/manager.rb +++ b/lib/oxidized/manager.rb @@ -37,6 +37,7 @@ module Oxidized @model.merge! _model end def source= _source + return nil if @source.key? _source _source = Manager.load Config::SourceDir, _source return false if _source.empty? @source.merge! _source diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb index 740834a..de04c98 100644 --- a/lib/oxidized/node.rb +++ b/lib/oxidized/node.rb @@ -28,6 +28,25 @@ module Oxidized [status, config] end + def serialize + h = { + :name => @name, + :ip => @ip, + :group => @group, + :model => @model.class.to_s, + :last => nil, + } + if @last + h[:last] = { + :start => @last.start, + :end => @last.end, + :status => @last.status, + :time => @last.time, + } + end + h + end + private def resolve_prompt opt diff --git a/lib/oxidized/nodes.rb b/lib/oxidized/nodes.rb index 467d3a0..3e58348 100644 --- a/lib/oxidized/nodes.rb +++ b/lib/oxidized/nodes.rb @@ -2,7 +2,7 @@ module Oxidized require 'oxidized/node' class Nodes < Array attr_accessor :source - alias :del :delete + alias :put :unshift def initialize *args super load if args.empty? @@ -17,17 +17,25 @@ module Oxidized replace new end def list - self + map { |e| e.name } end - # @param node [String] name of the node inserted into nodes array - def put node - unshift node + def find_index node + index { |e| e.name == node } + end + def show node + i = find_index node + self[i].serialize if i + end + def del node + i = find_index node + delete_at i if i end # @param node [String] name of the node moved into the head of array - def top node + def next node n = del node put n if n end + alias :top :next # @return [String] node from the head of the array def get (self << shift).last diff --git a/lib/oxidized/output/git.rb b/lib/oxidized/output/git.rb index 77d18bb..019d83b 100644 --- a/lib/oxidized/output/git.rb +++ b/lib/oxidized/output/git.rb @@ -28,7 +28,6 @@ class Git < Output repo = File.join File.dirname(repo), opt[:group] + '.git' end begin - repo = Repo.new repo actor = Actor.new user, email update_repo repo, file, data, msg, actor diff --git a/spec/nodes_spec.rb b/spec/nodes_spec.rb index ad51525..80ed300 100644 --- a/spec/nodes_spec.rb +++ b/spec/nodes_spec.rb @@ -1,15 +1,16 @@ -require 'oxidized/nodes' +require 'oxidized' +Oxidized.mgr = Oxidized::Manager.new describe Oxidized::Nodes do before(:each) do @nodes_org = %w(ltt-pe1.hel kes2-rr1.tku tor-peer1.oul - hal-p2.tre sav-gr1-sw1.kuo psl-sec-pe1.hel) + hal-p2.tre sav-gr1-sw1.kuo psl-sec-pe1.hel).map { |e| Oxidized::Node.new(:name=>e) } @nodes = Oxidized::Nodes.new @nodes_org.dup end describe '#put' do it 'adds node to top of queue' do - node = 'kst-p1.sto' + node = Oxidized::Node.new(:name=>'kst-p1.sto') @nodes.put node expect(@nodes).to eq [node] + @nodes_org end @@ -30,15 +31,15 @@ describe Oxidized::Nodes do end end - describe '#top' do + describe '#next' do it 'moves node to top of queue' do node = @nodes[3] - @nodes.top node + @nodes.next node.name expect(@nodes).to start_with [node] end it 'does not change node count' do before = @nodes.size - @nodes.top @nodes[3] + @nodes.next @nodes[3].name expect(before).to eq @nodes.size end end -- cgit v1.2.1