#!/usr/bin/env ruby #onering # list [options] field [key:value ..] # search [options] [key:value .. ] # provision [options] [key:value .. ] [pxe profile | ] require 'multi_json' require 'rubygems' require 'subcommander' require 'onering' require 'pp' require 'rainbow' include Subcommander def print_format(data, format=nil) format = :text unless format case format.to_sym when :text if data.is_a?(Hash) pp data else unless data.empty? if data.is_a?(Array) puts data.join("\n") else puts data.to_s end end end when :yaml require 'yaml' puts YAML.dump(data) when :json require 'json' puts MultiJson.dump(data) else raise "Unknown output format #{format}" end end begin subcommander.version = ::Gem.loaded_specs['onering-client'].version.to_s subcommander.desc = ::Gem.loaded_specs['onering-client'].description subcommand :devices, "Operations related to Onering's assets database" do |devices| api = Onering::API::Devices api.connect({ :host => ENV['ONERING_URL'], :pemfile => ENV['ONERING_PEM'] }) def _field(action, field, value, opts={}) rv = [] ids = [] # append IDs from filter ids += Onering::API::Devices.list('id', { :filter => opts[:filter] }) if opts[:filter] # add specific ID ids << opts[:id] if opts[:id] ids.each do |id| case action when :get rv << Onering::API::Devices.get_field(id, field) when :set rv << Onering::API::Devices.set_field(id, field, value) end end rv end # SHOW devices.subcommand :show, "Print out a single node by ID" do |sc| sc.usage = "onering devices show ID" sc.opt :format, '-t', '--format FORMAT', "Return the results as FORMAT" sc.exec do id = sc[:args].first print_format(api.get(id), sc[:format]) end end # GET [FIELD] devices.subcommand :get, "Get a named field from one or more devices" do |sc| sc.usage = "onering devices get FIELD" sc.opt :filter, '-f', '--filter FILTER', "A urlquery filter string" sc.opt :format, '-t', '--format FORMAT', "Return the results as FORMAT" sc.opt :id, '-i', '--id ID', "A specific node ID" sc.exec do rv = _field(:get, sc[:args].first, nil, sc) print_format(rv, sc[:format]) end end # SET [FIELD] devices.subcommand :set, "Set a named field for one or more devices" do |sc| sc.usage = "onering devices set FIELD VALUE" sc.opt :filter, '-f', '--filter FILTER', "A urlquery filter string" sc.opt :format, '-t', '--format FORMAT', "Return the results as FORMAT" sc.opt :id, '-i', '--id ID', "A specific node ID" sc.exec do rv = _field(:set, sc[:args].first, sc[:args][1], sc) print_format(rv, sc[:format]) end end # LIST devices.subcommand :list, "List field values" do |sc| sc.usage = "onering devices list [-f FILTER] FIELD" sc.opt :filter, '-f', '--filter FILTER', "A urlquery filter string" sc.opt :format, '-t', '--format FORMAT', "Return the results as FORMAT" sc.exec do field = sc[:args].first filter = sc[:filter] print_format(api.list(field, { :filter => filter }), sc[:format]) end end # FIND devices.subcommand :find, "Finds all nodes that match a urlquery filter string" do |sc| sc.arity = 1 sc.usage = "onering devices find FILTER" sc.opt :format, '-t', '--format FORMAT', "Return the results as FORMAT" sc.exec do print_format(api.find(sc[:args].first), sc[:format]) end end # SAVE devices.subcommand :save, "Creates or updates a new device in Onering, reading a JSON document from standard input" do |sc| sc.usage = "onering report | onering devices save [ID]" sc.exec do unless STDIN.tty? begin json = ::MultiJson.load(STDIN.read) raise "Input document must specify an ID" if sc[:args].empty? and not json['id'] rv = api.save((sc[:args].first || json['id']), json) print_format(rv, :json) if rv rescue Exception => e STDERR.puts "#{e.class.name}: #{e.message}" exit 1 end end end end end subcommand :users, "Manage Onering users" do |users| api = Onering::API::Auth api.connect({ :host => ENV['ONERING_URL'], :pemfile => ENV['ONERING_PEM'] }) # SHOW users.subcommand :show, "Print out a single user by ID" do |sc| sc.usage = "onering users show ID" sc.exec do id = sc[:args].first print_format(api.get(:users, id)) end end # LIST users.subcommand :list, "List users" do |sc| sc.usage = "onering users list FIELD" sc.opt :format, '-t', '--format FORMAT', "Return the results as FORMAT" sc.exec do field = sc[:args].first filter = sc[:filter] print_format(api.list(:users, field, { :filter => filter }), sc[:format]) end end # SAVE users.subcommand :save, "Creates or updates a new device in Onering, reading a JSON document from standard input" do |sc| sc.usage = "cat user.json | onering users save [ID]" sc.exec do unless STDIN.tty? begin json = ::MultiJson.load(STDIN.read) raise "Input document must specify an ID" if sc[:args].empty? and not json['id'] print_format(api.save((sc[:args].first || json['id']), json)) rescue Exception => e STDERR.puts "#{e.class.name}: #{e.message}" exit 1 end end end end end subcommand :call, "Call generic Onering API endpoints" do |call| api = Onering::API::Base api.connect({ :host => ENV['ONERING_URL'], :pemfile => ENV['ONERING_PEM'] }) call.usage = "onering call path/to/endpoint" call.opt :format, '-t', '--format FORMAT', "Return the results as FORMAT" call.opt :method, '-m', '--method VERB', "The HTTP method to use for the call (default: GET)" call.opt :opts, '-o', '--option KEY:VALUE', Array, "A comma-separated list of key:value querystrings to pass with the request" call.exec do rv = api.request(call[:args].first.to_sym, { :method => (call[:method] || :get), :data => (STDIN.tty? ? {} : ::MultiJson.load(STDIN.read)), :fields => (Hash[call[:opts].collect{|i| i.split(':',2) }] rescue {}) }) print_format(rv, call[:format] || :json) unless rv.nil? or rv.to_s.strip.chomp.empty? end end subcommand :fact, "Retrieve a system fact" do |fact| fact.usage = "onering fact NAME [DEFAULT] [NAME2 [DEFAULT2] ..]" fact.opt :format, '-t', '--format FORMAT', "Return the results as FORMAT" fact.exec do Onering::Reporter.setup() rv = [] fact[:args].each_index do |i| if i.even? name = fact[:args][i] else default = fact[:args][i] end rv << Onering::Reporter.fact(name, default) end rv.compact! rv = rv.first if rv.length == 1 print_format(rv, fact[:format]) unless rv.nil? or rv.to_s.empty? end end subcommand :report, "Collect and output system information" do |report| report.usage = "onering report" report.opt :plugin_path, '-p', '--plugin PATH', 'Add the named path to the plugin search path' report.opt :status, '-S', '--status STATUS', 'Set the status to report' report.opt :tags, '-T', '--tag TAG[,...]', 'Add a tag to the report output' report.opt :aliases, '-A', '--alias ALIAS[,...]', 'Add a alias to the report output' report.opt :id, '-I', '--id ID', 'Override the auto-detected hardware ID' report.opt :format, '-t', '--format FORMAT', 'Format the output' report.exec do config = {} %w{ plugin_path status tags aliases id }.each do |a| a = a.to_sym next if report[a].nil? if [:tags, :aliases].include?(a) config[a] = report[a].split(',') else config[a] = report[a] end end Onering::Reporter.setup(config) rv = Onering::Reporter.report() print_format(rv, report[:format] || :json) unless rv.nil? end end subcommander.go! rescue Onering::API::Errors::ClientError => e STDERR.puts("#{e.message}".foreground(:red)) exit 1 rescue Exception => e STDERR.puts("#{e.class.name}: #{e.message}".foreground(:red)) e.backtrace.each do |b| STDERR.puts(" #{b}") end exit 1 end