onering-agent 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/bin/onering +68 -0
- data/lib/etc/facter.list +19 -0
- data/lib/onering.rb +32 -0
- data/lib/onering/api.rb +322 -0
- data/lib/onering/cli.rb +92 -0
- data/lib/onering/cli/assets.rb +138 -0
- data/lib/onering/cli/automation.rb +62 -0
- data/lib/onering/cli/call.rb +53 -0
- data/lib/onering/cli/devices.rb +98 -0
- data/lib/onering/cli/fact.rb +22 -0
- data/lib/onering/cli/reporter.rb +121 -0
- data/lib/onering/config.rb +62 -0
- data/lib/onering/logger.rb +141 -0
- data/lib/onering/plugins/assets.rb +54 -0
- data/lib/onering/plugins/authentication.rb +35 -0
- data/lib/onering/plugins/automation.rb +70 -0
- data/lib/onering/plugins/reporter.rb +360 -0
- data/lib/onering/util.rb +150 -0
- data/lib/onering/version.rb +8 -0
- metadata +188 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module Onering
|
2
|
+
module CLI
|
3
|
+
class Fact < Plugin
|
4
|
+
def self.configure(global={})
|
5
|
+
@opts = ::Trollop::options do
|
6
|
+
banner <<-EOS
|
7
|
+
Return the value of an internal fact.
|
8
|
+
|
9
|
+
Usage:
|
10
|
+
onering [global] fact [name]
|
11
|
+
|
12
|
+
EOS
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.run(args)
|
17
|
+
args[0] = :hardwareid if args[0] and args[0] == 'id'
|
18
|
+
return Onering::Util.fact(args[0])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Onering
|
2
|
+
module CLI
|
3
|
+
class Report < Plugin
|
4
|
+
DEFAULT_CACHE_MAXAGE=600
|
5
|
+
|
6
|
+
def self.configure(global={})
|
7
|
+
@global_opts = global
|
8
|
+
@api = Onering::CLI.connect(@global_opts.merge({
|
9
|
+
:autoconnect => false
|
10
|
+
}))
|
11
|
+
|
12
|
+
@opts = ::Trollop::options do
|
13
|
+
banner <<-EOS
|
14
|
+
Generate a system report that can be saved or submitted to a Onering server
|
15
|
+
|
16
|
+
Subcommands:
|
17
|
+
<none>
|
18
|
+
Generate and output the system inventory report.
|
19
|
+
|
20
|
+
get <property> [fallback]
|
21
|
+
Get a specific <property> from the report, return [fallback] if not found.
|
22
|
+
|
23
|
+
save
|
24
|
+
Generate and save the system inventory report to Onering.
|
25
|
+
|
26
|
+
Usage:
|
27
|
+
onering [global] report [options] [subcommands]
|
28
|
+
|
29
|
+
Options:
|
30
|
+
EOS
|
31
|
+
opt :id, "Override the autodetected Hardware ID for this node", :short => '-I', :type => :string
|
32
|
+
opt :fields, "Set the named FIELD to equal VALUE in the format FIELD=VALUE. Can be specified multiple times", :short => '-o', :type => :string, :multi => true
|
33
|
+
opt :save, "Save the report output to the configured Onering server"
|
34
|
+
opt :timeout, "The maximum amount of time to wait for a report to be generated", :type => :integer, :default => 60
|
35
|
+
opt :plugin_timeout, "The maximum amount of time to wait for a report plugin to return data", :type => :integer, :default => 10
|
36
|
+
opt :local, "Do not attempt to contact a remote server for retrieving values not found locally", :type => :boolean, :default => false
|
37
|
+
opt :cachefile, "Use the specified file as a local report cache", :type => :string, :short => '-F'
|
38
|
+
opt :nocache, "Do not attempt to use a cache file for report generation", :type => :boolean, :default => false
|
39
|
+
opt :maxage, "Maxmimum age (in seconds) of the cache before it is automatically regenerated", :type => :integer, :default => DEFAULT_CACHE_MAXAGE
|
40
|
+
|
41
|
+
stop_on %w{get save}
|
42
|
+
end
|
43
|
+
|
44
|
+
# initialize report generator with user options
|
45
|
+
@_reporter = Onering::Reporter.new({
|
46
|
+
:id => @opts[:id],
|
47
|
+
:timeout => @opts[:timeout],
|
48
|
+
:plugin_timeout => @opts[:plugin_timeout],
|
49
|
+
:nocache => @opts[:nocache],
|
50
|
+
:cachefile => @opts[:cachefile],
|
51
|
+
:maxage => @opts[:maxage]
|
52
|
+
}.compact)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.run(args)
|
56
|
+
# saving, by default, should not use the cache (but should update it to keep it fresh)
|
57
|
+
if @opts[:save] === true or args[0] == 'save'
|
58
|
+
report = _report({
|
59
|
+
:cacheregen => true
|
60
|
+
})
|
61
|
+
else
|
62
|
+
report = _report()
|
63
|
+
end
|
64
|
+
|
65
|
+
# pull overrides from CLI arguments
|
66
|
+
@opts[:fields].each do |field|
|
67
|
+
key, value = field.split('=', 2)
|
68
|
+
Onering::Logger.debug("Override value #{key} from command line argument", "Onering::CLI::Report")
|
69
|
+
|
70
|
+
value = nil if ['null', '', '-'].include?(value.to_s.strip.chomp)
|
71
|
+
report = report.set(key, value)
|
72
|
+
end
|
73
|
+
|
74
|
+
# save if specified
|
75
|
+
if @opts[:save] === true
|
76
|
+
_save(report)
|
77
|
+
|
78
|
+
else
|
79
|
+
sc = args.shift
|
80
|
+
|
81
|
+
case (sc.downcase.to_sym rescue nil)
|
82
|
+
# -----------------------------------------------------------------------------
|
83
|
+
when :save
|
84
|
+
_save(report)
|
85
|
+
return nil
|
86
|
+
|
87
|
+
when :get
|
88
|
+
Onering::Logger.fatal!("Expected at least 1 parameter, got #{args.length}", "Onering::CLI::Report") unless args.length >= 1
|
89
|
+
return @_reporter.get(args[0], args[1], {
|
90
|
+
:data => report,
|
91
|
+
:api => @global_opts,
|
92
|
+
:local => @opts[:local]
|
93
|
+
})
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
return report
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
def self._save(report)
|
102
|
+
@api.connect()
|
103
|
+
@api.assets.save(report['id']) do
|
104
|
+
MultiJson.dump(report)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def self._report(options={})
|
109
|
+
begin
|
110
|
+
Onering::Logger.debug("Gathering local data for report", "Onering::CLI::Report")
|
111
|
+
Onering::Logger.fatal("Reporter not configured. This is a bug", "Onering::CLI::Report") if @_reporter.nil?
|
112
|
+
report = @_reporter.report(options).stringify_keys()
|
113
|
+
|
114
|
+
return report
|
115
|
+
rescue Timeout::Error
|
116
|
+
Onering::Logger.fatal!("Report took too long to generate, exiting...", "Onering::CLI::Report")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'hashlib'
|
3
|
+
|
4
|
+
module Onering
|
5
|
+
class Config
|
6
|
+
class<<self
|
7
|
+
DEFAULT_CONFIG={}
|
8
|
+
DEFAULT_OPTIONS_FILE=["~/.onering/cli.yml", "~/.onering/key.yml", "/etc/onering/cli.yml", "/etc/onering/key.yml"]
|
9
|
+
|
10
|
+
def load(configfile=nil, config={})
|
11
|
+
if configfile.nil?
|
12
|
+
configfile = []
|
13
|
+
else
|
14
|
+
# recursively grab all .yml files if directory is specified
|
15
|
+
configfile = (File.directory?(configfile) ? Dir.glob(File.join(configfile, "**", "*.yml")).sort : [configfile])
|
16
|
+
end
|
17
|
+
|
18
|
+
# list all existing config files from least specific to most
|
19
|
+
@_configfiles = (configfile + DEFAULT_OPTIONS_FILE).compact.select{|i|
|
20
|
+
(File.exists?(File.expand_path(i)) rescue false)
|
21
|
+
}.reverse
|
22
|
+
|
23
|
+
# merge all config files with more-specific values overriding less-specific ones
|
24
|
+
@_config ||= DEFAULT_CONFIG
|
25
|
+
@_configfiles_seen ||= Set.new()
|
26
|
+
|
27
|
+
# only load files we haven't seen before
|
28
|
+
(@_configfiles - @_configfiles_seen.to_a).each do |i|
|
29
|
+
c = YAML.load(File.read(File.expand_path(i))) rescue {}
|
30
|
+
|
31
|
+
if c and not c.empty?
|
32
|
+
Onering::Logger.info("Loading config file at #{File.expand_path(i) rescue i}", "Onering::Config")
|
33
|
+
@_config.deep_merge!(c)
|
34
|
+
end
|
35
|
+
|
36
|
+
@_configfiles_seen << i
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
# settings specified in the library override everything
|
41
|
+
unless config.empty?
|
42
|
+
Onering::Logger.debug("Merging settings specified in library instantiation", "Onering::Config")
|
43
|
+
@_config.deep_merge!(config.compact)
|
44
|
+
end
|
45
|
+
|
46
|
+
return @_config
|
47
|
+
end
|
48
|
+
|
49
|
+
def get(key, default=nil)
|
50
|
+
@_config.get(key, default)
|
51
|
+
end
|
52
|
+
|
53
|
+
def set(key, value)
|
54
|
+
@_config.set(key, value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def unset(key)
|
58
|
+
@_config.unset(key)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'rainbow'
|
3
|
+
require 'onering/version'
|
4
|
+
|
5
|
+
module Onering
|
6
|
+
class Logger
|
7
|
+
def self.setup(options={})
|
8
|
+
outputfn = options[:destination]
|
9
|
+
@_verbosity = 1
|
10
|
+
|
11
|
+
if options[:destination] == /STDOUT/i
|
12
|
+
options[:destination] = STDOUT
|
13
|
+
|
14
|
+
elsif options[:destination] == /STDERR/i or
|
15
|
+
options[:destination].nil?
|
16
|
+
options[:destination] = STDERR
|
17
|
+
|
18
|
+
elsif File.writable?(options[:destination])
|
19
|
+
options[:destination] = File.open(options[:destination], 'a')
|
20
|
+
|
21
|
+
else
|
22
|
+
options[:destination] = STDERR
|
23
|
+
end
|
24
|
+
|
25
|
+
@_logger = ::Logger.new(options[:destination])
|
26
|
+
@_logger.formatter = (options[:formatter] || proc{|severity, datetime, source, msg|
|
27
|
+
case severity.downcase.to_sym
|
28
|
+
when :fatal then sevtag = '!!'
|
29
|
+
when :error then sevtag = 'EE'
|
30
|
+
when :warn then sevtag = 'WW'
|
31
|
+
when :info then sevtag = 'II'
|
32
|
+
when :debug then sevtag = 'DD'
|
33
|
+
when :output then sevtag = ''
|
34
|
+
else sevtag = '??'
|
35
|
+
end
|
36
|
+
|
37
|
+
if options[:sevtag] == false
|
38
|
+
logline = [(source.nil? ? nil : "[#{source}]"), msg].compact.join(' ')
|
39
|
+
else
|
40
|
+
logline = ["#{sevtag} ", (source.nil? ? nil : "[#{source}]"), msg].compact.join(' ')
|
41
|
+
end
|
42
|
+
|
43
|
+
if options[:destination] === STDOUT or options[:destination] === STDERR
|
44
|
+
case severity.downcase.to_sym
|
45
|
+
when :fatal, :error
|
46
|
+
logline = logline.foreground(:red)
|
47
|
+
when :warn
|
48
|
+
logline = logline.foreground(:yellow)
|
49
|
+
when :info
|
50
|
+
logline = logline.foreground(:green)
|
51
|
+
when :debug, :debug2, :debug3
|
52
|
+
logline = logline.foreground(:blue)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
options[:destination].puts(logline)
|
57
|
+
})
|
58
|
+
|
59
|
+
self.level = (options[:threshold] || :info)
|
60
|
+
|
61
|
+
self.debug("onering-client v#{Onering::Client::VERSION} started") if defined?(Onering::FULL_CLIENT)
|
62
|
+
self.debug("Logger is initialized. Output is #{outputfn || 'stderr'}, threshold: #{options[:threshold]} or worse", (options[:name] || "Onering::Logger"))
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def self.logger()
|
67
|
+
return @_logger
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.level=(severity)
|
71
|
+
@_logger.sev_threshold = _get_level(severity)
|
72
|
+
@_verbosity = (Integer(severity.to_s[-1]) rescue 1)
|
73
|
+
|
74
|
+
@_logger.sev_threshold
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def self.log(severity, message, source=nil, verbosity=1)
|
79
|
+
if defined?(@_logger)
|
80
|
+
if @_verbosity >= verbosity
|
81
|
+
@_logger.add(_get_level(severity), message, source)
|
82
|
+
return true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
return false
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.fatal(message, source=nil, raise_error=false)
|
90
|
+
self.log(:fatal, message, source)
|
91
|
+
raise Onering::Client::FatalError.new(message) if raise_error === true
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.fatal!(message, source=nil)
|
95
|
+
self.fatal(message, source, true)
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.error(message, source=nil)
|
99
|
+
self.log(:error, message, source)
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.warn(message, source=nil)
|
103
|
+
self.log(:warn, message, source)
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.info(message, source=nil)
|
107
|
+
self.log(:info, message, source)
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.debug(message, source=nil)
|
111
|
+
self.log(:debug, message, source)
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.debug2(message, source=nil)
|
115
|
+
self.log(:debug, message, source, 2)
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.debug3(message, source=nil)
|
119
|
+
self.log(:debug, message, source, 3)
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.output(message, source=nil)
|
123
|
+
self.log(:output, message, source)
|
124
|
+
end
|
125
|
+
|
126
|
+
def self._get_level(severity)
|
127
|
+
case severity.to_sym
|
128
|
+
when :fatal
|
129
|
+
return ::Logger::FATAL
|
130
|
+
when :error
|
131
|
+
return ::Logger::ERROR
|
132
|
+
when :warn
|
133
|
+
return ::Logger::WARN
|
134
|
+
when :info
|
135
|
+
return ::Logger::INFO
|
136
|
+
else
|
137
|
+
return ::Logger::DEBUG
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Onering
|
2
|
+
class API
|
3
|
+
class Assets < API
|
4
|
+
def show(id)
|
5
|
+
get("/devices/#{id}").parsed_response
|
6
|
+
end
|
7
|
+
|
8
|
+
def get_field(id, field, fallback=nil)
|
9
|
+
rv = get("/devices/#{id}/get/#{field}")
|
10
|
+
rv = (rv.parsed_response rescue rv.response.body)
|
11
|
+
return fallback if rv.nil? or (rv.is_a?(String) and rv.strip.chomp.empty?)
|
12
|
+
return rv
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_field(id, field, value)
|
16
|
+
rv = get("/devices/#{id}/set/#{field}/#{value}")
|
17
|
+
rv.parsed_response rescue rv.response.body
|
18
|
+
end
|
19
|
+
|
20
|
+
def list(field, options={
|
21
|
+
:unique => true,
|
22
|
+
:sort => true,
|
23
|
+
:filter => nil
|
24
|
+
})
|
25
|
+
qs = {
|
26
|
+
:q => make_filter(options[:filter])
|
27
|
+
} if options[:filter]
|
28
|
+
|
29
|
+
rv = get("/devices/list/#{[*field].join('/')}", {
|
30
|
+
:query => qs
|
31
|
+
}).parsed_response
|
32
|
+
|
33
|
+
rv = rv.uniq if options[:unique]
|
34
|
+
rv = rv.sort if options[:sort]
|
35
|
+
|
36
|
+
return rv
|
37
|
+
end
|
38
|
+
|
39
|
+
def find(filter, options={})
|
40
|
+
get("/devices/find/#{make_filter(filter)}").parsed_response
|
41
|
+
end
|
42
|
+
|
43
|
+
def save(id, data=nil, &block)
|
44
|
+
if block_given?
|
45
|
+
post("/devices/#{id}", {}, &block)
|
46
|
+
else
|
47
|
+
post("/devices/#{id}", {
|
48
|
+
:body => data
|
49
|
+
})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Onering
|
2
|
+
class API
|
3
|
+
class Auth < API
|
4
|
+
def _check_type(type)
|
5
|
+
raise "Invalid authentication module object '#{type}'" unless %w{
|
6
|
+
users groups capabilities
|
7
|
+
}.include?(type.to_s)
|
8
|
+
end
|
9
|
+
|
10
|
+
def show(type, id='current')
|
11
|
+
_check_type(type)
|
12
|
+
get("#{type}/#{id}").parsed_response
|
13
|
+
end
|
14
|
+
|
15
|
+
def list(type, field='id', options={
|
16
|
+
:unique => true,
|
17
|
+
:sort => true,
|
18
|
+
:filter => nil
|
19
|
+
})
|
20
|
+
_check_type(type)
|
21
|
+
rv = get("#{type}/list").parsed_response.collect{|i| i[field.to_s] }
|
22
|
+
rv = rv.uniq if options[:unique]
|
23
|
+
rv = rv.sort if options[:sort]
|
24
|
+
rv
|
25
|
+
end
|
26
|
+
|
27
|
+
def save(type, id, data)
|
28
|
+
_check_type(type)
|
29
|
+
post("#{type}/#{id}", {
|
30
|
+
:body => data
|
31
|
+
}).parsed_response
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Onering
|
2
|
+
class API
|
3
|
+
class AutomationRequests < API
|
4
|
+
def summary(fields)
|
5
|
+
get("/automation/requests/summary/#{[*fields].join('/')}").parsed_response
|
6
|
+
end
|
7
|
+
|
8
|
+
def find_by_status(status)
|
9
|
+
get("/automation/requests/status/#{status}").parsed_response
|
10
|
+
end
|
11
|
+
|
12
|
+
def show(id)
|
13
|
+
get("/automation/requests/#{id}").parsed_response
|
14
|
+
end
|
15
|
+
|
16
|
+
def requeue(id)
|
17
|
+
get("/automation/requests/#{id}/requeue").parsed_response
|
18
|
+
end
|
19
|
+
|
20
|
+
def flush_queue()
|
21
|
+
get("/automation/requests/flush").parsed_response
|
22
|
+
end
|
23
|
+
|
24
|
+
def purge()
|
25
|
+
get("/automation/requests/purge").parsed_response
|
26
|
+
end
|
27
|
+
|
28
|
+
def requeue_all_failed()
|
29
|
+
get("/automation/requests/requeue").parsed_response
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# -----------------------------------------------------------------------------
|
35
|
+
class AutomationJobs < API
|
36
|
+
def list()
|
37
|
+
get("/automation/jobs/list").parsed_response
|
38
|
+
end
|
39
|
+
|
40
|
+
def show(name)
|
41
|
+
get("/automation/jobs/#{name}").parsed_response
|
42
|
+
end
|
43
|
+
|
44
|
+
def requests_waiting(name)
|
45
|
+
get("/automation/jobs/#{name}/waiting").parsed_response
|
46
|
+
end
|
47
|
+
|
48
|
+
def run(name, options={}, &block)
|
49
|
+
if block_given?
|
50
|
+
post("/automation/jobs/#{name}/run", {
|
51
|
+
:query => options
|
52
|
+
}, &block).parsed_response
|
53
|
+
else
|
54
|
+
get("/automation/jobs/#{name}/run", {
|
55
|
+
:query => options
|
56
|
+
}).parsed_response
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# -----------------------------------------------------------------------------
|
62
|
+
class AutomationTasks < API
|
63
|
+
def run(name, options={})
|
64
|
+
get("/automation/tasks/#{name}/run", {
|
65
|
+
:query => options
|
66
|
+
}).parsed_response
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|