From 8880188001da23a5f0960e3970c4eb9bbd448306 Mon Sep 17 00:00:00 2001 From: Saku Ytti Date: Sun, 23 Feb 2014 19:11:29 +0200 Subject: Migrate to sinatra/puma from webrick As I can't do IO#select on sinatra/puma to run it when I have time, I have to run it on separate thread. This means Nodes container needs to be thread safe, it now has ghetto mutex locking, but I probably need to be be more focused what are the external methods that can be called and wrap those in @mutex.synchronize Provide also HTML UI not just JSON for ghetto UI to people who don't want to integrate --- bin/oxidized | 2 + lib/oxidized/api/web.rb | 19 ++++++ lib/oxidized/api/web/public/css/oxidized.css | 35 ++++++++++ lib/oxidized/api/web/views/default.haml | 4 ++ lib/oxidized/api/web/views/head.haml | 3 + lib/oxidized/api/web/views/node.haml | 6 ++ lib/oxidized/api/web/views/nodes.haml | 27 ++++++++ lib/oxidized/api/web/webapp.rb | 98 ++++++++++++++++++++++++++++ lib/oxidized/config/core.rb | 2 +- lib/oxidized/config/defaults.rb | 2 +- lib/oxidized/core.rb | 7 +- lib/oxidized/input/cli.rb | 6 +- lib/oxidized/input/ssh.rb | 2 +- lib/oxidized/job.rb | 2 +- lib/oxidized/manager.rb | 2 +- lib/oxidized/node.rb | 12 ++-- lib/oxidized/nodes.rb | 32 +++++++-- lib/oxidized/output/file.rb | 3 +- lib/oxidized/worker.rb | 2 +- oxidized.gemspec | 6 +- 20 files changed, 245 insertions(+), 27 deletions(-) create mode 100644 lib/oxidized/api/web.rb create mode 100644 lib/oxidized/api/web/public/css/oxidized.css create mode 100644 lib/oxidized/api/web/views/default.haml create mode 100644 lib/oxidized/api/web/views/head.haml create mode 100644 lib/oxidized/api/web/views/node.haml create mode 100644 lib/oxidized/api/web/views/nodes.haml create mode 100644 lib/oxidized/api/web/webapp.rb diff --git a/bin/oxidized b/bin/oxidized index 2509279..72db61a 100755 --- a/bin/oxidized +++ b/bin/oxidized @@ -2,6 +2,8 @@ require 'oxidized' +trap("INT") { exit } # sinatra will otherwise steak this from us + begin Process.daemon unless $DEBUG Oxidized.new diff --git a/lib/oxidized/api/web.rb b/lib/oxidized/api/web.rb new file mode 100644 index 0000000..9a5a507 --- /dev/null +++ b/lib/oxidized/api/web.rb @@ -0,0 +1,19 @@ +module Oxidized + module API + class Web + attr_reader :thread + def initialize nodes, listen + require 'oxidized/api/web/webapp' + addr, port = listen.to_s.split ':' + port, addr = addr, nil if not port + WebApp.set :server, %w(puma) + WebApp.set :bind, addr if addr + WebApp.set :port, port + WebApp.set :nodes, nodes + end + def run + @thread = Thread.new { WebApp.run! } + end + end + end +end diff --git a/lib/oxidized/api/web/public/css/oxidized.css b/lib/oxidized/api/web/public/css/oxidized.css new file mode 100644 index 0000000..8455513 --- /dev/null +++ b/lib/oxidized/api/web/public/css/oxidized.css @@ -0,0 +1,35 @@ +body { + background: #fdf6e3; + color: #002b36; +} + +a:link { + /* color: #268bd2; */ + color: #dc322f; + text-decoration: none; +} + +a:visited { +} + +a:hover { + color: #d33682; + text-decoration: underline; +} + +a:active { +} + +tr.odd { + background: #eee8d5; +} + +tr.even { + background: #fdf6e3; +} + +tr:hover { + background: #586e75; + color: #fdf6e3; +} + diff --git a/lib/oxidized/api/web/views/default.haml b/lib/oxidized/api/web/views/default.haml new file mode 100644 index 0000000..1ff1f4b --- /dev/null +++ b/lib/oxidized/api/web/views/default.haml @@ -0,0 +1,4 @@ +%html + !=haml :head + %body + =@data diff --git a/lib/oxidized/api/web/views/head.haml b/lib/oxidized/api/web/views/head.haml new file mode 100644 index 0000000..1619512 --- /dev/null +++ b/lib/oxidized/api/web/views/head.haml @@ -0,0 +1,3 @@ +%head + %title oxidized + %link{:rel=>'stylesheet', :href=>'/css/oxidized.css'} diff --git a/lib/oxidized/api/web/views/node.haml b/lib/oxidized/api/web/views/node.haml new file mode 100644 index 0000000..6c68e07 --- /dev/null +++ b/lib/oxidized/api/web/views/node.haml @@ -0,0 +1,6 @@ +%html + !=haml :head + %body + -out='';PP.pp(@data,out) + %pre + =out diff --git a/lib/oxidized/api/web/views/nodes.haml b/lib/oxidized/api/web/views/nodes.haml new file mode 100644 index 0000000..4bc0b14 --- /dev/null +++ b/lib/oxidized/api/web/views/nodes.haml @@ -0,0 +1,27 @@ +%html + !=haml :head + %body + %a(href="/reload") reload list of nodes + %table + %tr + %th Name + %th IP + %th Group + %th Last Status + %th Last Time + %th Config + %th Update + -trclass = %w(even odd) + -@data.sort_by{|e|e[:name]}.each do |node| + -klass = trclass.rotate!.first + %tr{:class=>klass} + %td + %a(href="/node/show/#{node[:name]}") #{node[:name]} + %td= node[:ip] + %td= node[:group] + %td= node[:status] + %td= node[:time] + %td + %a(href="/node/fetch/#{node[:full_name]}") config + %td + %a(href="/node/next/#{node[:full_name]}") update diff --git a/lib/oxidized/api/web/webapp.rb b/lib/oxidized/api/web/webapp.rb new file mode 100644 index 0000000..13bd0ab --- /dev/null +++ b/lib/oxidized/api/web/webapp.rb @@ -0,0 +1,98 @@ +require 'sinatra/base' +require 'sinatra/json' +require 'haml' +require 'pp' +module Oxidized + module API + class WebApp < Sinatra::Base + + get '/nodes.?:format?' do + @data = nodes.list.map do |node| + node[:status] = 'never' + node[:time] = 'never' + node[:group] = 'default' unless node[:group] + if node[:last] + node[:status] = node[:last][:status] + node[:time] = node[:last][:end] + end + node + end + out :nodes + end + + get '/reload.?:format?' do + nodes.load + @data = 'reloaded list of nodes' + out + end + + + get '/node/fetch/:node' do + node, @json = route_parse :node + @data = nodes.fetch node, nil + out :text + end + + get '/node/fetch/:group/:node' do + node, @json = route_parse :node + @data = nodes.fetch node, params[:group] + out :text + end + + + get '/node/next/:node' do + node, @json = route_parse :node + nodes.next node + redirect '/nodes' unless @json + @data = 'ok' + out + end + + # use this to attach author/email/message to commit + put '/node/next/:node' do + node, @json = route_parse :node + opt = JSON.load request.body.read + nodes.next node, opt + redirect '/nodes' unless @json + @data = 'ok' + out + end + + get '/node/show/:node' do + node, @json = route_parse :node + @data = nodes.show node + out :node + end + + #get '/node/:node' do + # @data = nodes.show params[:node] + # out + #end + + def out template=:default + if @json or params[:format] == 'json' + json @data + elsif template == :text + content_type :text + @data + else + haml template + end + end + + def nodes + settings.nodes + end + + def route_parse param + json = false + e = params[param].split '.' + if e.last == 'json' + e.pop + json = true + end + [e.join('.'), json] + end + end + end +end diff --git a/lib/oxidized/config/core.rb b/lib/oxidized/config/core.rb index 6c91920..c2cde58 100644 --- a/lib/oxidized/config/core.rb +++ b/lib/oxidized/config/core.rb @@ -11,7 +11,7 @@ module Oxidized # load config from file or bootstrap with built-ins def load if File.exists? @file - marshal_load YAML.load_file @file + marshal_load YAML.load_file @file else require 'oxidized/config/bootstrap' end diff --git a/lib/oxidized/config/defaults.rb b/lib/oxidized/config/defaults.rb index d189ca3..c30143f 100644 --- a/lib/oxidized/config/defaults.rb +++ b/lib/oxidized/config/defaults.rb @@ -5,7 +5,7 @@ module Oxidized InputDir = File.join Directory, %w(lib oxidized input) OutputDir = File.join Directory, %w(lib oxidized output) ModelDir = File.join Directory, %w(lib oxidized model) - SourceDir = File.join Directory, %w(lib oxidized source) + SourceDir = File.join Directory, %w(lib oxidized source) Sleep = 1 end class << self diff --git a/lib/oxidized/core.rb b/lib/oxidized/core.rb index 59292f9..70e9e42 100644 --- a/lib/oxidized/core.rb +++ b/lib/oxidized/core.rb @@ -4,7 +4,7 @@ module Oxidized require 'oxidized/worker' require 'oxidized/nodes' require 'oxidized/manager' - require 'oxidized/api/rest' + require 'oxidized/api/web' class << self def new *args Core.new args @@ -16,7 +16,8 @@ module Oxidized Oxidized.mgr = Manager.new nodes = Nodes.new @worker = Worker.new nodes - @rest = API::Rest.new nodes, CFG.rest if CFG.rest + @rest = API::Web.new nodes, CFG.rest if CFG.rest + @rest.run run end @@ -25,7 +26,7 @@ module Oxidized def run while true @worker.work - @rest ? @rest.work : sleep(Config::Sleep) + Config::Sleep end end end diff --git a/lib/oxidized/input/cli.rb b/lib/oxidized/input/cli.rb index 8daddac..ab93b06 100644 --- a/lib/oxidized/input/cli.rb +++ b/lib/oxidized/input/cli.rb @@ -1,7 +1,7 @@ module Oxidized class Input module CLI - + def initialize @post_login = [] @pre_logout = [] @@ -17,13 +17,13 @@ module Oxidized def disconnect_cli @pre_logout.each { |command, block| block ? block.call : (cmd command) } end - + def post_login _post_login=nil, &block unless @exec @post_login << [_post_login, block] end end - + def pre_logout _pre_logout=nil, &block unless @exec @pre_logout << [_pre_logout, block] diff --git a/lib/oxidized/input/ssh.rb b/lib/oxidized/input/ssh.rb index 67be180..5c9a4f0 100644 --- a/lib/oxidized/input/ssh.rb +++ b/lib/oxidized/input/ssh.rb @@ -77,7 +77,7 @@ module Oxidized expect expect_re if expect_re @output end - + def expect regexp @ssh.loop(0.1) do sleep 0.1 diff --git a/lib/oxidized/job.rb b/lib/oxidized/job.rb index 6921c2b..6fb60a8 100644 --- a/lib/oxidized/job.rb +++ b/lib/oxidized/job.rb @@ -5,7 +5,7 @@ module Oxidized @node = node @start = Time.now.utc super do |node| - @status, @config = node.run + @status, @config = node.run @end = Time.now.utc @time = @end - @start end diff --git a/lib/oxidized/manager.rb b/lib/oxidized/manager.rb index 8c53d64..5065617 100644 --- a/lib/oxidized/manager.rb +++ b/lib/oxidized/manager.rb @@ -34,7 +34,7 @@ module Oxidized def model= _model _model = Manager.load Config::ModelDir, _model return false if _model.empty? - @model.merge! _model + @model.merge! _model end def source= _source return nil if @source.key? _source diff --git a/lib/oxidized/node.rb b/lib/oxidized/node.rb index 05a926c..6c7b3aa 100644 --- a/lib/oxidized/node.rb +++ b/lib/oxidized/node.rb @@ -58,12 +58,14 @@ module Oxidized def serialize h = { - :name => @name, - :ip => @ip, - :group => @group, - :model => @model.class.to_s, - :last => nil, + :name => @name, + :full_name => @name, + :ip => @ip, + :group => @group, + :model => @model.class.to_s, + :last => nil, } + h[:full_name] = [@group, @name].join('/') if @group if @last h[:last] = { :start => @last.start, diff --git a/lib/oxidized/nodes.rb b/lib/oxidized/nodes.rb index affeea8..981e082 100644 --- a/lib/oxidized/nodes.rb +++ b/lib/oxidized/nodes.rb @@ -7,36 +7,43 @@ module Oxidized alias :put :unshift def initialize *args super + @mutex= Mutex.new # we compete for the nodes with webapi thread load if args.empty? end def load + lock new = [] @source = CFG.source[:default] Oxidized.mgr.source = @source Oxidized.mgr.source[@source].new.load.each do |node| new.push Node.new node end - replace new + unlock(replace new) end def list - map { |e| e.name } + lock + unlock(map { |e| e.serialize }) end def show node + lock i = find_node_index node - self[i].serialize + unlock(self[i].serialize) end def fetch node, group + lock i = find_node_index node output = self[i].output.new + unlock raise Oxidized::NotSupported unless output.respond_to? :fetch output.fetch node, group end def del node - delete_at find_node_index + lock + unlock(delete_at find_node_index(node)) end # @param node [String] name of the node moved into the head of array def next node, opt={} - require 'pp' + lock n = del node if n n.user = opt['user'] @@ -44,21 +51,32 @@ module Oxidized n.from = opt['from'] put n end + unlock end alias :top :next # @return [String] node from the head of the array def get - (self << shift).last + lock + unlock((self << shift).last) end private + def lock + @mutex.lock unless @mutex.owned? + end + + def unlock arg=nil + @mutex.unlock if @mutex.owned? + arg + end + def find_index node index { |e| e.name == node } end def find_node_index node - find_index node or raise Oxidized::NodeNotFound + find_index node or raise Oxidized::NodeNotFound, "unable to find '#{node}'" end end end diff --git a/lib/oxidized/output/file.rb b/lib/oxidized/output/file.rb index da778ff..ee8a9a6 100644 --- a/lib/oxidized/output/file.rb +++ b/lib/oxidized/output/file.rb @@ -25,7 +25,6 @@ class OxFile < Output open(file, 'w') { |fh| fh.write data } end - def fetch node, group cfg_dir = @cfg[:directory] if group # group is explicitly defined by user @@ -39,6 +38,6 @@ class OxFile < Output end end end - + end end diff --git a/lib/oxidized/worker.rb b/lib/oxidized/worker.rb index 14cf2a5..fcdd6f0 100644 --- a/lib/oxidized/worker.rb +++ b/lib/oxidized/worker.rb @@ -26,7 +26,7 @@ module Oxidized msg = "update #{node.name}" msg += " from #{node.from}" if node.from msg += " with message '#{node.msg}'" if node.msg - node.output.new.store node.name, job.config, + node.output.new.store node.name, job.config, :msg => msg, :user => node.user, :group => node.group node.reset else diff --git a/oxidized.gemspec b/oxidized.gemspec index cd324f1..4c6839a 100644 --- a/oxidized.gemspec +++ b/oxidized.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |s| s.name = 'oxidized' - s.version = '0.0.20' + s.version = '0.0.21' s.platform = Gem::Platform::RUBY s.authors = [ 'Saku Ytti' ] s.email = %w( saku@ytti.fi ) @@ -16,4 +16,8 @@ Gem::Specification.new do |s| s.add_dependency 'sqlite3' s.add_dependency 'net-ssh' s.add_dependency 'grit' + s.add_dependency 'sinatra' + s.add_dependency 'haml' + s.add_dependency 'puma' + end -- cgit v1.2.1