onering-client 0.0.46 → 0.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/onering +31 -314
- data/lib/onering/api.rb +235 -180
- data/lib/onering/cli/automation.rb +59 -0
- data/lib/onering/cli/call.rb +36 -0
- data/lib/onering/cli/devices.rb +93 -0
- data/lib/onering/cli/fact.rb +23 -0
- data/lib/onering/cli/reporter.rb +61 -0
- data/lib/onering/cli.rb +56 -0
- data/lib/onering/plugins/authentication.rb +27 -31
- data/lib/onering/plugins/automation.rb +66 -0
- data/lib/onering/plugins/devices.rb +36 -36
- data/lib/onering/plugins/reporter.rb +6 -13
- data/lib/onering/util.rb +91 -1
- data/lib/onering.rb +7 -0
- metadata +31 -24
data/lib/onering/api.rb
CHANGED
@@ -1,221 +1,276 @@
|
|
1
|
-
require 'rest_client'
|
2
|
-
require 'uri'
|
3
|
-
require 'multi_json'
|
4
1
|
require 'yaml'
|
5
|
-
require '
|
2
|
+
require 'hashlib'
|
6
3
|
require 'deep_merge'
|
4
|
+
require 'addressable/uri'
|
5
|
+
require 'active_support/core_ext'
|
6
|
+
require 'httparty'
|
7
7
|
|
8
8
|
module Onering
|
9
|
-
|
9
|
+
class API
|
10
|
+
module Actions
|
11
|
+
class Retry < ::Exception; end
|
12
|
+
end
|
13
|
+
|
10
14
|
module Errors
|
15
|
+
class Exception < ::Exception; end
|
11
16
|
class NotConnected < Exception; end
|
12
17
|
class ClientError < Exception; end
|
13
18
|
class ServerError < Exception; end
|
14
19
|
class ConnectionTimeout < Exception; end
|
15
|
-
class
|
16
|
-
end
|
17
|
-
|
18
|
-
class Base
|
19
|
-
include Onering::Util
|
20
|
-
|
21
|
-
DEFAULT_BASE="https://onering"
|
22
|
-
DEFAULT_PATH="/api"
|
23
|
-
DEFAULT_OPTIONS_FILE=[
|
24
|
-
"~/.onering/cli.yml",
|
25
|
-
"/etc/onering/cli.yml"
|
26
|
-
]
|
27
|
-
|
28
|
-
DEFAULT_CLIENT_PEM=[
|
29
|
-
"~/.onering/client.pem",
|
30
|
-
"/etc/onering/client.pem"
|
31
|
-
]
|
32
|
-
|
33
|
-
DEFAULT_VALIDATION_PEM=[
|
34
|
-
"/etc/onering/validation.pem"
|
35
|
-
]
|
36
|
-
|
37
|
-
class<<self
|
38
|
-
def connect(options={})
|
39
|
-
# list all existing config files from least specific to most
|
40
|
-
@_configfiles = ([options[:config]] + DEFAULT_OPTIONS_FILE).compact.select{|i|
|
41
|
-
(File.exists?(File.expand_path(i)) rescue false)
|
42
|
-
}.reverse
|
43
|
-
|
44
|
-
# merge all config files with more-specific values overriding less-specific ones
|
45
|
-
@_config = {}
|
46
|
-
@_configfiles.each do |i|
|
47
|
-
c = YAML.load(File.read(File.expand_path(i))) rescue {}
|
48
|
-
@_config.deep_merge!(c)
|
49
|
-
end
|
20
|
+
class AuthenticationMissing < Exception; end
|
21
|
+
end
|
50
22
|
|
51
|
-
|
52
|
-
|
53
|
-
elsif options[:host].is_a?(String)
|
54
|
-
@_uri = Addressable::URI.parse("#{options[:host]}/#{DEFAULT_PATH}")
|
55
|
-
else
|
56
|
-
@_uri = Addressable::URI.parse("#{@_config['url'] || DEFAULT_BASE}/#{@_config['apiroot'] || DEFAULT_PATH}")
|
57
|
-
end
|
23
|
+
include Onering::Util
|
24
|
+
include ::HTTParty
|
58
25
|
|
59
|
-
|
60
|
-
|
61
|
-
@_pemfile = ([options[:pemfile], @_config['pemfile']]+DEFAULT_CLIENT_PEM).compact.select{|i|
|
62
|
-
(File.exists?((File.expand_path(i) rescue i)) rescue nil)
|
63
|
-
}.compact.first
|
26
|
+
attr_accessor :url
|
27
|
+
format :json
|
64
28
|
|
65
|
-
|
29
|
+
DEFAULT_CONFIG={}
|
30
|
+
DEFAULT_BASE="https://onering"
|
31
|
+
DEFAULT_PATH="/api"
|
32
|
+
DEFAULT_OPTIONS_FILE=["~/.onering/cli.yml", "/etc/onering/cli.yml"]
|
33
|
+
DEFAULT_CLIENT_PEM=["~/.onering/client.pem", "/etc/onering/client.pem"]
|
34
|
+
DEFAULT_CLIENT_KEY=["~/.onering/client.key", "/etc/onering/client.key"]
|
35
|
+
DEFAULT_VALIDATION_PEM="/etc/onering/validation.pem"
|
66
36
|
|
67
|
-
@_pem = File.read((File.expand_path(@_pemfile) rescue @_pemfile))
|
68
37
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
:ssl_client_key => OpenSSL::PKey::RSA.new(@_pem),
|
74
|
-
:verify_peer => OpenSSL::SSL::VERIFY_PEER
|
75
|
-
})
|
38
|
+
def initialize(options={})
|
39
|
+
@_config = {}
|
40
|
+
@_plugins = {}
|
41
|
+
@_connection_options = options
|
76
42
|
|
77
|
-
|
78
|
-
|
79
|
-
STDERR.puts("Onering client.pem not found, attempting automatic registration...")
|
43
|
+
# load and merge all config file sources
|
44
|
+
_load_config(@_connection_options[:configfile], @_connection_options.get(:config, {}))
|
80
45
|
|
81
|
-
|
82
|
-
|
83
|
-
(File.exists?((File.expand_path(i) rescue i)) rescue nil)
|
84
|
-
}.compact.first
|
46
|
+
# set API connectivity details
|
47
|
+
Onering::API.base_uri @_config.get(:url, DEFAULT_BASE)
|
85
48
|
|
86
|
-
|
87
|
-
|
88
|
-
|
49
|
+
Onering::Reporter.setup()
|
50
|
+
connect(options) if options.get(:autoconnect, true)
|
51
|
+
end
|
89
52
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
:timeout => 120,
|
94
|
-
:open_timeout => 30,
|
95
|
-
:ssl_client_cert => OpenSSL::X509::Certificate.new(@_validation),
|
96
|
-
:ssl_client_key => OpenSSL::PKey::RSA.new(@_validation),
|
97
|
-
:verify_peer => OpenSSL::SSL::VERIFY_PEER
|
98
|
-
})
|
99
|
-
|
100
|
-
|
101
|
-
clients = [{
|
102
|
-
:path => "/etc/onering",
|
103
|
-
:name => (@_config['id'] || File.read("/etc/hardware.id")).strip.chomp,
|
104
|
-
:keyname => 'system',
|
105
|
-
:autodelete => true
|
106
|
-
},{
|
107
|
-
:path => "~/.onering",
|
108
|
-
:name => ENV['USER'],
|
109
|
-
:keyname => 'cli',
|
110
|
-
:autodelete => false
|
111
|
-
}]
|
112
|
-
|
113
|
-
# attempt to autoregister clients from least specific to most (machine account then user account)
|
114
|
-
clients.each do |client|
|
115
|
-
# determine if we can create this client
|
116
|
-
client[:path] = (File.expand_path(client[:path]) rescue client[:path])
|
117
|
-
next unless File.writable?(File.dirname(client[:path]))
|
118
|
-
Dir.mkdir(client[:path]) unless File.directory?(client[:path])
|
119
|
-
next unless File.writable?(client[:path])
|
120
|
-
|
121
|
-
begin
|
122
|
-
response = @rest["/api/users/#{client[:name]}/keys/#{client[:keyname]}"].get({
|
123
|
-
:params => {
|
124
|
-
:cert => 'pem',
|
125
|
-
:autodelete => client[:autodelete]
|
126
|
-
}
|
127
|
-
})
|
128
|
-
|
129
|
-
rescue RestClient::Forbidden
|
130
|
-
STDERR.puts("Cannot re-download key '#{client[:keyname]}' for client #{client[:name]}. Please remove the client key from Onering and try again.")
|
131
|
-
next
|
132
|
-
|
133
|
-
rescue RestClient::Exception => e
|
134
|
-
raise Errors::ClientError.new("HTTP #{e.http_code}: #{e.message}")
|
135
|
-
end
|
136
|
-
|
137
|
-
|
138
|
-
File.open("#{client[:path]}/client.pem", "w") do |file|
|
139
|
-
file.puts(response.to_str)
|
140
|
-
STDERR.puts("Successfully registered client key #{client[:name]}:#{client[:keyname]}, key is at #{file.path}")
|
141
|
-
break
|
142
|
-
end
|
143
|
-
end
|
53
|
+
def connect(options={})
|
54
|
+
# setup authentication
|
55
|
+
_setup_auth()
|
144
56
|
|
145
|
-
|
146
|
-
|
147
|
-
end
|
57
|
+
return self
|
58
|
+
end
|
148
59
|
|
149
|
-
retry
|
150
60
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
61
|
+
def request(method, endpoint, options={})
|
62
|
+
endpoint = [@_config.get(:path, DEFAULT_PATH).strip, endpoint.sub(/^\//,'')].join('/')
|
63
|
+
|
64
|
+
case method
|
65
|
+
when :post
|
66
|
+
rv = Onering::API.post(endpoint, options)
|
67
|
+
when :put
|
68
|
+
rv = Onering::API.put(endpoint, options)
|
69
|
+
when :delete
|
70
|
+
rv = Onering::API.delete(endpoint, options)
|
71
|
+
when :head
|
72
|
+
rv = Onering::API.head(endpoint, options)
|
73
|
+
else
|
74
|
+
rv = Onering::API.get(endpoint, options)
|
75
|
+
end
|
155
76
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
77
|
+
if rv.code >= 500
|
78
|
+
raise Errors::ServerError.new("HTTP #{rv.code} - #{Onering::Util.http_status(rv.code)} #{rv.parsed_response.get('error.message','') rescue ''}")
|
79
|
+
elsif rv.code >= 400
|
80
|
+
raise Errors::ClientError.new("HTTP #{rv.code} - #{Onering::Util.http_status(rv.code)} #{rv.parsed_response.get('error.message', '') rescue ''}")
|
81
|
+
else
|
82
|
+
rv
|
83
|
+
end
|
84
|
+
end
|
160
85
|
|
161
|
-
def request(endpoint, options={})
|
162
|
-
options = @_config.merge(options)
|
163
|
-
options[:method] = (options[:method].to_s.downcase.to_sym rescue nil)
|
164
|
-
request = nil
|
165
86
|
|
166
|
-
|
167
|
-
|
87
|
+
def get(endpoint, options={})
|
88
|
+
request(:get, endpoint, options)
|
89
|
+
end
|
168
90
|
|
169
|
-
|
91
|
+
def post(endpoint, options={}, &block)
|
92
|
+
if block_given?
|
93
|
+
request(:post, endpoint, options.merge({
|
94
|
+
:body => yield
|
95
|
+
}))
|
96
|
+
else
|
97
|
+
request(:post, endpoint, options)
|
98
|
+
end
|
99
|
+
end
|
170
100
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
101
|
+
def put(endpoint, options={})
|
102
|
+
if block_given?
|
103
|
+
request(:put, endpoint, options.merge({
|
104
|
+
:body => yield
|
105
|
+
}))
|
106
|
+
else
|
107
|
+
request(:put, endpoint, options)
|
108
|
+
end
|
109
|
+
end
|
175
110
|
|
176
|
-
|
177
|
-
|
178
|
-
|
111
|
+
def delete(endpoint, options={})
|
112
|
+
request(:delete, endpoint, options)
|
113
|
+
end
|
179
114
|
|
180
|
-
when :delete
|
181
|
-
response = @rest[uri.request_uri].delete()
|
182
115
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
116
|
+
# I'm not a huge fan of what's happening here, but metaprogramming is hard...
|
117
|
+
#
|
118
|
+
# "Don't let the perfect be the enemy of the good."
|
119
|
+
#
|
120
|
+
def method_missing(method, *args, &block)
|
121
|
+
modname = method.to_s.split('_').map(&:capitalize).join
|
188
122
|
|
189
|
-
|
190
|
-
|
123
|
+
if not (plugin = (Onering::API.const_get(modname) rescue nil)).nil?
|
124
|
+
@_plugins[method] ||= plugin.new.connect(@_connection_options)
|
125
|
+
return @_plugins[method]
|
126
|
+
else
|
127
|
+
super
|
128
|
+
end
|
129
|
+
end
|
191
130
|
|
192
|
-
|
193
|
-
|
194
|
-
|
131
|
+
def opt(name, default=nil)
|
132
|
+
@_config.get(name, default)
|
133
|
+
end
|
195
134
|
|
196
|
-
begin
|
197
|
-
rv = (response.empty? ? nil : MultiJson.load(response))
|
198
|
-
rescue Exception
|
199
|
-
rv = response
|
200
|
-
end
|
201
135
|
|
202
|
-
|
203
|
-
|
136
|
+
def status()
|
137
|
+
Onering::API.get("/").parsed_response
|
138
|
+
end
|
204
139
|
|
205
|
-
def make_filter(filter)
|
206
|
-
filter = filter.collect{|k,v| "#{k}/#{v}" } if filter.is_a?(Hash)
|
207
|
-
filter = filter.collect{|i| i.sub(':','/') }.join("/") if filter.is_a?(Array)
|
208
|
-
filter
|
209
|
-
end
|
210
140
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
141
|
+
|
142
|
+
private
|
143
|
+
# -----------------------------------------------------------------------------
|
144
|
+
def _load_config(configfile, config={})
|
145
|
+
if configfile.nil?
|
146
|
+
configfile = []
|
147
|
+
else
|
148
|
+
# recursively grab all .yml files if directory is specified
|
149
|
+
configfile = (File.directory?(configfile) ? Dir.glob(File.join(configfile, "**", "*.yml")).sort : [configfile])
|
150
|
+
end
|
151
|
+
|
152
|
+
# list all existing config files from least specific to most
|
153
|
+
@_configfiles = (configfile + DEFAULT_OPTIONS_FILE).compact.select{|i|
|
154
|
+
(File.exists?(File.expand_path(i)) rescue false)
|
155
|
+
}.reverse
|
156
|
+
|
157
|
+
# merge all config files with more-specific values overriding less-specific ones
|
158
|
+
@_config = DEFAULT_CONFIG
|
159
|
+
@_configfiles.each do |i|
|
160
|
+
c = YAML.load(File.read(File.expand_path(i))) rescue {}
|
161
|
+
@_config.deep_merge!(c)
|
162
|
+
end
|
163
|
+
|
164
|
+
# settings specified in the library override everything
|
165
|
+
@_config.deep_merge!(config.compact) unless config.empty?
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
# -----------------------------------------------------------------------------
|
170
|
+
def _setup_auth()
|
171
|
+
type = @_config.get('authentication.type', :auto)
|
172
|
+
|
173
|
+
case type.to_sym
|
174
|
+
when :token
|
175
|
+
_setup_auth_token()
|
176
|
+
|
177
|
+
else
|
178
|
+
_setup_auth_ssl()
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
# -----------------------------------------------------------------------------
|
184
|
+
def _setup_auth_ssl()
|
185
|
+
begin
|
186
|
+
# get first keyfile found
|
187
|
+
key = (([@_config.get('authentication.keyfile')] + DEFAULT_CLIENT_PEM).compact.select{|i|
|
188
|
+
(File.exists?(File.expand_path(i)) rescue false)
|
189
|
+
}).first
|
190
|
+
|
191
|
+
# SSL client key not found, attempt autoregistration...
|
192
|
+
if key.nil?
|
193
|
+
if @_config.get('authentication.autoregister', true)
|
194
|
+
validation_key = @_config.get('authentication.validation_keyfile', DEFAULT_VALIDATION_PEM)
|
195
|
+
validation_key = (File.expand_path(validation_key) rescue validation_key)
|
196
|
+
|
197
|
+
# if validation key exists, autoregister
|
198
|
+
if File.size?(validation_key)
|
199
|
+
# set the authentication PEM to validation.pem
|
200
|
+
Onering::API.pem(File.read(validation_key))
|
201
|
+
|
202
|
+
# attempt to create client.pem from least-specific to most, first writable path wins
|
203
|
+
clients = [{
|
204
|
+
:path => "/etc/onering",
|
205
|
+
:name => fact('hardwareid'),
|
206
|
+
:keyname => 'system',
|
207
|
+
:autodelete => true
|
208
|
+
},{
|
209
|
+
:path => "~/.onering",
|
210
|
+
:name => ENV['USER'],
|
211
|
+
:keyname => 'cli',
|
212
|
+
:autodelete => false
|
213
|
+
}]
|
214
|
+
|
215
|
+
# for each client attempt...
|
216
|
+
clients.each do |client|
|
217
|
+
# expand and assemble path
|
218
|
+
client[:path] = (File.expand_path(client[:path]) rescue client[:path])
|
219
|
+
keyfile = File.join(client[:path], 'client.pem')
|
220
|
+
|
221
|
+
# skip this if we can't write to the parent directory
|
222
|
+
next unless File.writable?(client[:path])
|
223
|
+
Dir.mkdir(client[:path]) unless File.directory?(client[:path])
|
224
|
+
next if File.exists?(keyfile)
|
225
|
+
|
226
|
+
# attempt to create/download the keyfile
|
227
|
+
response = self.class.get("/api/users/#{client[:name].strip}/keys/#{client[:keyname]}")
|
228
|
+
|
229
|
+
# if successful, write the file
|
230
|
+
if response.code < 400 and response.body
|
231
|
+
File.open(keyfile, 'w').puts(response.body)
|
232
|
+
raise Actions::Retry.new
|
233
|
+
else
|
234
|
+
# all errors are fatal at this stage
|
235
|
+
raise Errors::ClientError.new("Cannot autoregister client: HTTP #{response.code} - #{(response.parsed_response || {}).get('error.message', 'Unknown error')}")
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# it is an error to not have created a client.pem by now
|
240
|
+
raise Errors::AuthenticationMissing.new("Cannot autoregister client: keyfile not created")
|
241
|
+
|
242
|
+
else
|
243
|
+
# cannot autoregister without a validation.pem
|
244
|
+
raise Errors::AuthenticationMissing.new("Cannot autoregister client: validation keyfile is missing")
|
215
245
|
end
|
246
|
+
else
|
247
|
+
raise Errors::AuthenticationMissing.new("Cannot find SSL key and autoregistration is disabled")
|
216
248
|
end
|
249
|
+
else
|
250
|
+
Onering::API.pem(File.read((File.expand_path(key) rescue key)))
|
217
251
|
end
|
252
|
+
|
253
|
+
rescue Actions::Retry
|
254
|
+
retry
|
218
255
|
end
|
219
256
|
end
|
257
|
+
|
258
|
+
|
259
|
+
# -----------------------------------------------------------------------------
|
260
|
+
def _setup_auth_token()
|
261
|
+
# get first keyfile found
|
262
|
+
key = @_config.get('authentication.key')
|
263
|
+
raise Errors::AuthenticationMissing.new("Cannot find an API token") if key.nil?
|
264
|
+
|
265
|
+
# set auth mechanism
|
266
|
+
Onering::API.headers({
|
267
|
+
'X-Auth-Mechanism' => 'token'
|
268
|
+
})
|
269
|
+
|
270
|
+
# set default parameters
|
271
|
+
Onering::API.default_params({
|
272
|
+
:token => key
|
273
|
+
})
|
274
|
+
end
|
220
275
|
end
|
221
|
-
end
|
276
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Onering
|
2
|
+
module CLI
|
3
|
+
module Automation
|
4
|
+
def self.configure(global={})
|
5
|
+
@requests = Onering::CLI.connect(global).automation_requests
|
6
|
+
@jobs = Onering::CLI.connect(global).automation_jobs
|
7
|
+
@tasks = Onering::CLI.connect(global).automation_tasks
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.run(args)
|
11
|
+
sc = args.shift # subcommand
|
12
|
+
ssc = args.shift # sub-subcommand
|
13
|
+
|
14
|
+
case (sc.downcase.to_sym rescue nil)
|
15
|
+
# -----------------------------------------------------------------------------
|
16
|
+
when :requests
|
17
|
+
@opts = ::Trollop::options do
|
18
|
+
banner <<-EOS
|
19
|
+
Options:
|
20
|
+
EOS
|
21
|
+
stop_on %w{show status requeue flush}
|
22
|
+
end
|
23
|
+
|
24
|
+
case (ssc.downcase.to_sym rescue nil)
|
25
|
+
when :show
|
26
|
+
return @requests.show(args[0])
|
27
|
+
|
28
|
+
when :requeue
|
29
|
+
return @requests.requeue_all_failed() if args[0].nil?
|
30
|
+
return @requests.requeue(args[0])
|
31
|
+
|
32
|
+
when :status
|
33
|
+
fields = (args.empty? ? ["status"] : args)
|
34
|
+
rv = {}
|
35
|
+
out = @requests.summary(fields)
|
36
|
+
|
37
|
+
_rejigger_hash = Proc.new do |h|
|
38
|
+
[*h].collect{|i|
|
39
|
+
[i['id'], (i['children'].nil? ? i['count'] : Hash[_rejigger_hash.call(i['children'])])]
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
return Hash[_rejigger_hash.call(out)]
|
44
|
+
|
45
|
+
when :flush
|
46
|
+
return @requests.flush_queue()
|
47
|
+
end
|
48
|
+
|
49
|
+
# -----------------------------------------------------------------------------
|
50
|
+
when :jobs
|
51
|
+
|
52
|
+
|
53
|
+
# -----------------------------------------------------------------------------
|
54
|
+
when :tasks
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Onering
|
2
|
+
module CLI
|
3
|
+
module Call
|
4
|
+
def self.configure(global={})
|
5
|
+
@api = Onering::CLI.connect(global)
|
6
|
+
|
7
|
+
@opts = ::Trollop::options do
|
8
|
+
banner <<-EOS
|
9
|
+
Call an arbitrary Onering API endpoint and return the output
|
10
|
+
|
11
|
+
Usage:
|
12
|
+
onering call [options] [endpoint]
|
13
|
+
|
14
|
+
Examples:
|
15
|
+
# Returns the API status page at path /api/
|
16
|
+
$ onering call /
|
17
|
+
|
18
|
+
# Returns details about the authenticated user
|
19
|
+
$ onering call users/current
|
20
|
+
|
21
|
+
# Delete the device called '0bf29c'
|
22
|
+
$ onering call devices/0bf29c -m delete
|
23
|
+
|
24
|
+
Options:
|
25
|
+
EOS
|
26
|
+
opt :method, "The HTTP method to use when performing the request (default: GET)", :default => 'get', :short => "-m", :type => :string
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.run(args)
|
31
|
+
rv = @api.request(@opts[:method], args.first)
|
32
|
+
return (rv.parsed_response || rv.response.body)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Onering
|
2
|
+
module CLI
|
3
|
+
module Devices
|
4
|
+
def self.configure(global={})
|
5
|
+
@api = Onering::CLI.connect(global).devices
|
6
|
+
|
7
|
+
@opts = ::Trollop::options do
|
8
|
+
banner <<-EOS
|
9
|
+
Options:
|
10
|
+
EOS
|
11
|
+
|
12
|
+
opt :query, "The Onering urlquery to filter devices by", :short => '-f', :type => :string
|
13
|
+
opt :id, "The node ID of the device to operate on", :short => '-i', :type => :string
|
14
|
+
|
15
|
+
# subcommands
|
16
|
+
stop_on %w{show get set list find save}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.run(args)
|
21
|
+
sc = args.shift
|
22
|
+
|
23
|
+
case (sc.downcase.to_sym rescue nil)
|
24
|
+
# -----------------------------------------------------------------------------
|
25
|
+
when :show
|
26
|
+
return @api.devices.show(args[0])
|
27
|
+
|
28
|
+
# -----------------------------------------------------------------------------
|
29
|
+
when :get
|
30
|
+
raise "Expected 1 parameter, got #{args.length}" unless args.length == 1
|
31
|
+
|
32
|
+
if @opts[:query_given]
|
33
|
+
# doing this until a bulk field query endpoint is built
|
34
|
+
return @api.list('id', {
|
35
|
+
:filter => @opts[:query]
|
36
|
+
}).collect{|i| @api.get_field(i, args[0])}
|
37
|
+
|
38
|
+
elsif @opts[:id_given]
|
39
|
+
return @api.get_field(@opts[:id], args[0])
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# -----------------------------------------------------------------------------
|
44
|
+
when :set
|
45
|
+
raise "Expected 2 parameters, got #{args.length}" unless args.length == 2
|
46
|
+
|
47
|
+
if @opts.get(:query)
|
48
|
+
# doing this until a bulk field set endpoint is built
|
49
|
+
return @api.list('id', {
|
50
|
+
:filter => @opts[:query]
|
51
|
+
}).collect{|i| @api.set_field(i, args[0])}
|
52
|
+
|
53
|
+
elsif @opts.get(:id)
|
54
|
+
return @api.set_field(@opts[:id], args[0], args[1])
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
# -----------------------------------------------------------------------------
|
59
|
+
when :list
|
60
|
+
raise "Expected 1 parameter, got #{args.length}" unless args.length >= 1
|
61
|
+
return @api.list(args[0], {
|
62
|
+
:filter => [@opts[:query], args[1]].compact.join('/')
|
63
|
+
}.compact)
|
64
|
+
|
65
|
+
# -----------------------------------------------------------------------------
|
66
|
+
when :find
|
67
|
+
raise "Expected 1 parameter, got #{args.length}" unless args.length == 1
|
68
|
+
return @api.find(args[0])
|
69
|
+
|
70
|
+
# -----------------------------------------------------------------------------
|
71
|
+
when :save
|
72
|
+
rv = @api.save(args[0] || @opts[:id]) do
|
73
|
+
# read from pipe
|
74
|
+
if not STDIN.tty?
|
75
|
+
STDIN.read
|
76
|
+
|
77
|
+
# read from specified file
|
78
|
+
elsif (File.readable?(args[1]) rescue false)
|
79
|
+
File.read(args[1])
|
80
|
+
|
81
|
+
else
|
82
|
+
raise "Cannot save data, no input data specified"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
rv.parsed_response
|
87
|
+
else
|
88
|
+
raise "Unknown subcommand #{sc.inspect}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Onering
|
2
|
+
module CLI
|
3
|
+
module Fact
|
4
|
+
def self.configure(global={})
|
5
|
+
Onering::Reporter.setup()
|
6
|
+
|
7
|
+
@opts = ::Trollop::options do
|
8
|
+
banner <<-EOS
|
9
|
+
Return the value of an internal fact.
|
10
|
+
|
11
|
+
Usage:
|
12
|
+
onering [global] fact [name]
|
13
|
+
|
14
|
+
EOS
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.run(args)
|
19
|
+
return Onering::Util.fact(args[0])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|