noms-client 1.9.0
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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/LICENSE +191 -0
- data/README.rst +16 -0
- data/Rakefile +21 -0
- data/TODO +8 -0
- data/bin/ansible-cmdb +70 -0
- data/bin/noms +774 -0
- data/control.m4 +8 -0
- data/etc/noms.conf +11 -0
- data/lib/ncc/client.rb +108 -0
- data/lib/noms/client/version.rb +7 -0
- data/lib/noms/cmdb.rb +136 -0
- data/lib/noms/errors.rb +11 -0
- data/lib/noms/httpclient.rb +508 -0
- data/lib/noms/nagui.rb +57 -0
- data/lib/spec_helper.rb +28 -0
- data/noms-client.gemspec +26 -0
- data/spec/01nomscmdb_spec.rb +21 -0
- data/spec/02noms.sh +8 -0
- data/spec/03ncc_spec.rb +90 -0
- data/spec/04cmdb-mock_spec.rb +214 -0
- data/spec/05restmock-persist_spec.rb +83 -0
- data/spec/06noms-mock.sh +35 -0
- data/spec/07noms-admin.sh +91 -0
- metadata +148 -0
@@ -0,0 +1,508 @@
|
|
1
|
+
#!ruby
|
2
|
+
# /* Copyright 2014 Evernote Corporation. All rights reserved.
|
3
|
+
# Copyright 2013 Proofpoint, Inc. All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
# */
|
17
|
+
|
18
|
+
|
19
|
+
require 'net/http'
|
20
|
+
require 'net/https'
|
21
|
+
require 'rubygems'
|
22
|
+
require 'noms/errors'
|
23
|
+
require 'rexml/document'
|
24
|
+
require 'json'
|
25
|
+
|
26
|
+
class Hash
|
27
|
+
|
28
|
+
# The mkdir -p of []=
|
29
|
+
def set_deep(keypath, value)
|
30
|
+
if keypath.length == 1
|
31
|
+
self[keypath[0]] = value
|
32
|
+
else
|
33
|
+
self[keypath[0]] ||= Hash.new
|
34
|
+
self[keypath[0]].set_deep(keypath[1 .. -1], value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
class NOMS
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
class NOMS::XmlHash < Hash
|
45
|
+
|
46
|
+
attr_accessor :element, :name
|
47
|
+
|
48
|
+
def initialize(el)
|
49
|
+
super
|
50
|
+
@element = el
|
51
|
+
@name = el.name
|
52
|
+
self['text'] = el.text
|
53
|
+
el.attributes.each do |attr, value|
|
54
|
+
self[attr] = value
|
55
|
+
end
|
56
|
+
self['children'] = []
|
57
|
+
el.elements.each do |child|
|
58
|
+
self['children'] << NOMS::XmlHash.new(child)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_xml(name=nil)
|
63
|
+
el = REXML::Element.new(name || self.name)
|
64
|
+
el.text = self['text'] if self.has_key? 'text'
|
65
|
+
self.each do |key, val|
|
66
|
+
next if ['children', 'text'].include? key
|
67
|
+
el.add_attribute(key, val)
|
68
|
+
end
|
69
|
+
if self.has_key? 'children'
|
70
|
+
self['children'].each do |child|
|
71
|
+
el.add_element child.to_xml
|
72
|
+
end
|
73
|
+
end
|
74
|
+
el
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
class NOMS::HttpClient
|
80
|
+
|
81
|
+
@@mocked = false
|
82
|
+
|
83
|
+
def self.mock!(datafile=nil)
|
84
|
+
@@mocked = true
|
85
|
+
@@mockdata = datafile
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.mockery
|
89
|
+
NOMS::HttpClient::RestMock
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize(opt)
|
93
|
+
@opt = opt
|
94
|
+
@delegate = (@@mocked ? self.class.mockery.new(self) :
|
95
|
+
NOMS::HttpClient::Real.new(self))
|
96
|
+
end
|
97
|
+
|
98
|
+
def opt
|
99
|
+
@opt
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_mock(method, url, opt)
|
103
|
+
false
|
104
|
+
end
|
105
|
+
|
106
|
+
def config_key
|
107
|
+
'httpclient'
|
108
|
+
end
|
109
|
+
|
110
|
+
def myconfig(key=nil)
|
111
|
+
unless @opt.has_key? config_key and @opt[config_key]
|
112
|
+
raise NOMS::Error::Config, "Configuration provided to #{self.class} doesn't have a '#{config_key}' key"
|
113
|
+
end
|
114
|
+
|
115
|
+
cfg = @opt[config_key]
|
116
|
+
|
117
|
+
if key
|
118
|
+
unless cfg.has_key? key
|
119
|
+
msg = "#{config_key} configuration doesn't have a '#{key}' property"
|
120
|
+
msg += " (#{@opt[config_key].inspect})"
|
121
|
+
raise NOMS::Error::Config, msg
|
122
|
+
end
|
123
|
+
cfg[key]
|
124
|
+
else
|
125
|
+
cfg
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Used mostly for mocking behavior
|
130
|
+
def allow_partial_updates
|
131
|
+
# Replace during PUT
|
132
|
+
false
|
133
|
+
end
|
134
|
+
|
135
|
+
def default_content_type
|
136
|
+
'application/json'
|
137
|
+
end
|
138
|
+
|
139
|
+
def ignore_content_type
|
140
|
+
false
|
141
|
+
end
|
142
|
+
|
143
|
+
def dbg(msg)
|
144
|
+
if @opt.has_key? 'debug' and @opt['debug'] > 1
|
145
|
+
puts "DBG(#{self.class}): #{msg}"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def trim(s, c='/', dir=:both)
|
150
|
+
case dir
|
151
|
+
when :both
|
152
|
+
trim(trim(s, c, :right), c, :left)
|
153
|
+
when :right
|
154
|
+
s.sub(Regexp.new(c + '+/'), '')
|
155
|
+
when :left
|
156
|
+
s.sub(Regexp.new('^' + c + '+'), '')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def ltrim(s, c='/')
|
161
|
+
trim(s, c, :left)
|
162
|
+
end
|
163
|
+
|
164
|
+
def rtrim(s, c='/')
|
165
|
+
trim(s, c, :right)
|
166
|
+
end
|
167
|
+
|
168
|
+
def method_missing(meth, *args, &block)
|
169
|
+
@delegate.send(meth, *args, &block)
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
class NOMS::HttpClient::RestMock < NOMS::HttpClient
|
175
|
+
|
176
|
+
def initialize(delegator)
|
177
|
+
@delegator = delegator
|
178
|
+
@data = { }
|
179
|
+
@opt = @delegator.opt
|
180
|
+
@opt['return-hash'] = true unless @opt.has_key? 'return-hash'
|
181
|
+
self.dbg "Initialized with options: #{opt.inspect}"
|
182
|
+
end
|
183
|
+
|
184
|
+
def allow_partial_updates
|
185
|
+
@delegator.allow_partial_updates
|
186
|
+
end
|
187
|
+
|
188
|
+
def config_key
|
189
|
+
@delegator.config_key
|
190
|
+
end
|
191
|
+
|
192
|
+
def default_content_type
|
193
|
+
@delegator.default_content_type
|
194
|
+
end
|
195
|
+
|
196
|
+
def ignore_content_type
|
197
|
+
@delegator.ignore_content_type
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
def id_field(dummy=nil)
|
202
|
+
'id'
|
203
|
+
end
|
204
|
+
|
205
|
+
def maybe_save
|
206
|
+
if @@mockdata
|
207
|
+
File.open(@@mockdata, 'w') { |fh| fh << JSON.pretty_generate(@data) }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def maybe_read
|
212
|
+
if @@mockdata and File.exist? @@mockdata
|
213
|
+
@data = File.open(@@mockdata, 'r') { |fh| JSON.load(fh) }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def all_data
|
218
|
+
maybe_read
|
219
|
+
@data
|
220
|
+
end
|
221
|
+
|
222
|
+
def do_request(opt={})
|
223
|
+
maybe_read
|
224
|
+
method = [:GET, :PUT, :POST, :DELETE].find do |m|
|
225
|
+
opt.has_key? m
|
226
|
+
end
|
227
|
+
method ||= :GET
|
228
|
+
opt[method] ||= ''
|
229
|
+
|
230
|
+
rel_uri = opt[method]
|
231
|
+
dbg "relative URI is #{rel_uri}"
|
232
|
+
url = URI.parse(myconfig 'url')
|
233
|
+
unless opt[method] == ''
|
234
|
+
url.path = rtrim(url.path) + '/' + ltrim(rel_uri) unless opt[:absolute]
|
235
|
+
end
|
236
|
+
url.query = opt[:query] if opt.has_key? :query
|
237
|
+
dbg "url=#{url}"
|
238
|
+
|
239
|
+
handled = handle_mock(method, url, opt)
|
240
|
+
return handled if handled
|
241
|
+
|
242
|
+
# We're not mocking absolute URLs specifically
|
243
|
+
case method
|
244
|
+
|
245
|
+
when :PUT
|
246
|
+
# Upsert - whole objects only
|
247
|
+
dbg "Processing PUT"
|
248
|
+
@data[url.host] ||= { }
|
249
|
+
collection_path_components = url.path.split('/')
|
250
|
+
id = collection_path_components.pop
|
251
|
+
collection_path = collection_path_components.join('/')
|
252
|
+
@data[url.host][collection_path] ||= [ ]
|
253
|
+
object_index =
|
254
|
+
@data[url.host][collection_path].index { |el| el[id_field(collection_path)] == id }
|
255
|
+
if object_index.nil?
|
256
|
+
object = opt[:body].merge({ id_field(collection_path) => id })
|
257
|
+
dbg "creating in collection #{collection_path}: #{object.inspect}"
|
258
|
+
@data[url.host][collection_path] << opt[:body].merge({ id_field(collection_path) => id })
|
259
|
+
else
|
260
|
+
if allow_partial_updates
|
261
|
+
object = @data[url.host][collection_path][object_index].merge(opt[:body])
|
262
|
+
dbg "updating in collection #{collection_path}: to => #{object.inspect}"
|
263
|
+
else
|
264
|
+
object = opt[:body].merge({ id_field(collection_path) => id })
|
265
|
+
dbg "replacing in collection #{collection_path}: #{object.inspect}"
|
266
|
+
end
|
267
|
+
@data[url.host][collection_path][object_index] = object
|
268
|
+
end
|
269
|
+
maybe_save
|
270
|
+
object
|
271
|
+
|
272
|
+
when :POST
|
273
|
+
# Insert/Create
|
274
|
+
dbg "Processing POST"
|
275
|
+
@data[url.host] ||= { }
|
276
|
+
collection_path = url.path
|
277
|
+
@data[url.host][collection_path] ||= [ ]
|
278
|
+
id = opt[:body][id_field(collection_path)] || opt[:body].object_id
|
279
|
+
object = opt[:body].merge({id_field(collection_path) => id})
|
280
|
+
dbg "creating in collection #{collection_path}: #{object.inspect}"
|
281
|
+
@data[url.host][collection_path] << object
|
282
|
+
maybe_save
|
283
|
+
object
|
284
|
+
|
285
|
+
when :DELETE
|
286
|
+
dbg "Processing DELETE"
|
287
|
+
if @data[url.host]
|
288
|
+
if @data[url.host].has_key? url.path
|
289
|
+
# DELETE on a collection
|
290
|
+
@data[url.host].delete url.path
|
291
|
+
true
|
292
|
+
elsif @data[url.host].has_key? url.path.split('/')[0 .. -2].join('/')
|
293
|
+
# DELETE on an object by Id
|
294
|
+
path_components = url.path.split('/')
|
295
|
+
id = path_components.pop
|
296
|
+
collection_path = path_components.join('/')
|
297
|
+
object_index = @data[url.host][collection_path].index { |obj| obj[id_field(collection_path)] == id }
|
298
|
+
if object_index.nil?
|
299
|
+
raise NOMS::Error, "Error (#{self.class} making #{config_key} request " +
|
300
|
+
"(404): No such object id (#{id_field(collection_path)} == #{id}) in #{collection_path}"
|
301
|
+
else
|
302
|
+
@data[url.host][collection_path].delete_at object_index
|
303
|
+
end
|
304
|
+
maybe_save
|
305
|
+
true
|
306
|
+
else
|
307
|
+
raise NOMS::Error, "Error (#{self.class}) making #{config_key} request " +
|
308
|
+
"(404): No objects at location or in collection #{url.path}"
|
309
|
+
end
|
310
|
+
else
|
311
|
+
raise NOMS::Error, "Error (#{self.class}) making #{config_key} request " +
|
312
|
+
"(404): No objects on #{url.host}"
|
313
|
+
end
|
314
|
+
|
315
|
+
when :GET
|
316
|
+
dbg "Performing GET"
|
317
|
+
if @data[url.host]
|
318
|
+
dbg "we store data for #{url.host}"
|
319
|
+
if @data[url.host].has_key? url.path
|
320
|
+
# GET on a collection
|
321
|
+
# TODO get on the query string
|
322
|
+
dbg "returning collection #{url.path}"
|
323
|
+
@data[url.host][url.path]
|
324
|
+
elsif @data[url.host].has_key? url.path.split('/')[0 .. -2].join('/')
|
325
|
+
# GET on an object by Id
|
326
|
+
path_components = url.path.split('/')
|
327
|
+
id = path_components.pop
|
328
|
+
collection_path = path_components.join('/')
|
329
|
+
dbg "searching in collection #{collection_path}: id=#{id}"
|
330
|
+
dbg "data: #{@data[url.host][collection_path].inspect}"
|
331
|
+
object = @data[url.host][collection_path].find { |obj| obj[id_field(collection_path)] == id }
|
332
|
+
if object.nil?
|
333
|
+
raise NOMS::Error, "Error (#{self.class} making #{config_key} request " +
|
334
|
+
"(404): No such object id (#{id_field(collection_path)} == #{id}) in #{collection_path}"
|
335
|
+
end
|
336
|
+
dbg " found #{object.inspect}"
|
337
|
+
object
|
338
|
+
else
|
339
|
+
raise NOMS::Error, "Error (#{self.class}) making #{config_key} request " +
|
340
|
+
"(404): No objects at location or in collection #{url.path}"
|
341
|
+
end
|
342
|
+
else
|
343
|
+
raise NOMS::Error, "Error (#{self.class}) making #{config_key} request " +
|
344
|
+
"(404): No objects on #{url.host}"
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
class NOMS::HttpClient::Real < NOMS::HttpClient
|
352
|
+
|
353
|
+
def initialize(delegator)
|
354
|
+
@delegator = delegator
|
355
|
+
@opt = @delegator.opt
|
356
|
+
@opt['return-hash'] = true unless @opt.has_key? 'return-hash'
|
357
|
+
self.dbg "Initialized with options: #{opt.inspect}"
|
358
|
+
end
|
359
|
+
|
360
|
+
def config_key
|
361
|
+
@delegator.config_key
|
362
|
+
end
|
363
|
+
|
364
|
+
def default_content_type
|
365
|
+
@delegator.default_content_type
|
366
|
+
end
|
367
|
+
|
368
|
+
def ignore_content_type
|
369
|
+
@delegator.ignore_content_type
|
370
|
+
end
|
371
|
+
|
372
|
+
|
373
|
+
def do_request(opt={})
|
374
|
+
method = [:GET, :PUT, :POST, :DELETE].find do |m|
|
375
|
+
opt.has_key? m
|
376
|
+
end
|
377
|
+
if method == nil
|
378
|
+
method = :GET
|
379
|
+
opt[method] = ''
|
380
|
+
end
|
381
|
+
rel_uri = opt[method]
|
382
|
+
opt[:redirect_limit] ||= 10
|
383
|
+
if opt[:absolute]
|
384
|
+
url = URI.parse(rel_uri)
|
385
|
+
else
|
386
|
+
url = URI.parse(myconfig 'url')
|
387
|
+
unless opt[method] == ''
|
388
|
+
url.path = rtrim(url.path) + '/' + ltrim(rel_uri) unless opt[:absolute]
|
389
|
+
end
|
390
|
+
url.query = opt[:query] if opt.has_key? :query
|
391
|
+
end
|
392
|
+
self.dbg("#{method.inspect} => #{url.to_s}")
|
393
|
+
http = Net::HTTP.new(url.host, url.port)
|
394
|
+
http.read_timeout = myconfig.has_key?('timeout') ? myconfig['timeout'] : 120
|
395
|
+
http.use_ssl = true if url.scheme == 'https'
|
396
|
+
if http.use_ssl?
|
397
|
+
self.dbg("using SSL/TLS")
|
398
|
+
if myconfig.has_key? 'verify-with-ca'
|
399
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
400
|
+
http.ca_file = myconfig['verify-with-ca']
|
401
|
+
else
|
402
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
403
|
+
end
|
404
|
+
else
|
405
|
+
self.dbg("NOT using SSL/TLS")
|
406
|
+
end
|
407
|
+
reqclass = case method
|
408
|
+
when :GET
|
409
|
+
Net::HTTP::Get
|
410
|
+
when :PUT
|
411
|
+
Net::HTTP::Put
|
412
|
+
when :POST
|
413
|
+
Net::HTTP::Post
|
414
|
+
when :DELETE
|
415
|
+
Net::HTTP::Delete
|
416
|
+
end
|
417
|
+
request = reqclass.new(url.request_uri)
|
418
|
+
self.dbg request.to_s
|
419
|
+
if myconfig.has_key? 'username'
|
420
|
+
self.dbg "will do basic authentication as #{myconfig['username']}"
|
421
|
+
request.basic_auth(myconfig['username'], myconfig['password'])
|
422
|
+
else
|
423
|
+
self.dbg "no authentication"
|
424
|
+
end
|
425
|
+
if opt.has_key? :body
|
426
|
+
content_type = opt[:content_type] || default_content_type
|
427
|
+
request['Content-Type'] = content_type
|
428
|
+
request.body = case content_type
|
429
|
+
when /json$/
|
430
|
+
opt[:body].to_json
|
431
|
+
when /xml$/
|
432
|
+
opt[:body].to_xml
|
433
|
+
else
|
434
|
+
opt[:body]
|
435
|
+
end
|
436
|
+
end
|
437
|
+
response = http.request(request)
|
438
|
+
self.dbg response.to_s
|
439
|
+
if response.is_a? Net::HTTPRedirection
|
440
|
+
if opt[:redirect_limit] == 0
|
441
|
+
raise "Error (#{self.class}) making #{config_key} request " +
|
442
|
+
"(redirect limit exceeded): on #{response['location']}"
|
443
|
+
end
|
444
|
+
# TODO check if really absolute or make sure it is
|
445
|
+
self.dbg "Redirect to #{response['location']}"
|
446
|
+
do_request opt.merge({ :GET => response['location'],
|
447
|
+
:absolute => true,
|
448
|
+
:redirect_limit => opt[:redirect_limit] - 1
|
449
|
+
})
|
450
|
+
end
|
451
|
+
|
452
|
+
unless response.is_a? Net::HTTPSuccess
|
453
|
+
raise "Error (#{self.class}) making #{config_key} request " +
|
454
|
+
"(#{response.code}): " + error_body(response.body)
|
455
|
+
end
|
456
|
+
|
457
|
+
if response.body
|
458
|
+
type = ignore_content_type ? default_content_type :
|
459
|
+
(response.content_type || default_content_type)
|
460
|
+
self.dbg "Response body is type #{type}"
|
461
|
+
case type
|
462
|
+
when /xml$/
|
463
|
+
doc = REXML::Document.new response.body
|
464
|
+
if @opt['return-hash']
|
465
|
+
_xml_to_hash doc
|
466
|
+
else
|
467
|
+
doc
|
468
|
+
end
|
469
|
+
when /json$/
|
470
|
+
# Ruby JSON doesn't like bare values in JSON, we'll try to wrap these as
|
471
|
+
# one-element array
|
472
|
+
bodytext = response.body
|
473
|
+
bodytext = '[' + bodytext + ']' unless ['{', '['].include? response.body[0].chr
|
474
|
+
JSON.parse(bodytext)
|
475
|
+
else
|
476
|
+
response.body
|
477
|
+
end
|
478
|
+
else
|
479
|
+
true
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def _xml_to_hash(rexml)
|
484
|
+
NOMS::XmlHash.new rexml.root
|
485
|
+
end
|
486
|
+
|
487
|
+
def error_body(body_text, content_type=nil)
|
488
|
+
content_type ||= default_content_type
|
489
|
+
begin
|
490
|
+
extracted_message = case content_type
|
491
|
+
when /json$/
|
492
|
+
structure = JSON.parse(body_text)
|
493
|
+
structure['message']
|
494
|
+
when /xml$/
|
495
|
+
REXML::Document.new(body_text).root.elements["//message"].first.text
|
496
|
+
else
|
497
|
+
Hash.new
|
498
|
+
end
|
499
|
+
['message', 'error', 'error_message'].each do |key|
|
500
|
+
return structure[key].to_s if structure.has_key? key
|
501
|
+
end
|
502
|
+
body_text.to_s
|
503
|
+
rescue
|
504
|
+
body_text.to_s
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
end
|
data/lib/noms/nagui.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!ruby
|
2
|
+
# /* Copyright 2014 Evernote Corporation. All rights reserved.
|
3
|
+
# Copyright 2013 Proofpoint, Inc. All rights reserved.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
# */
|
17
|
+
|
18
|
+
require 'noms/httpclient'
|
19
|
+
require 'uri'
|
20
|
+
|
21
|
+
class NOMS
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
class NOMS::Nagui < NOMS::HttpClient
|
26
|
+
def dbg(msg)
|
27
|
+
if @opt.has_key? 'debug' and @opt['debug'] > 2
|
28
|
+
puts "DBG(#{self.class}): #{msg}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
def initialize(opt)
|
32
|
+
@opt = opt
|
33
|
+
self.dbg "Initialized with options: #{opt.inspect}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def config_key
|
37
|
+
'nagui'
|
38
|
+
end
|
39
|
+
|
40
|
+
def system(hostname)
|
41
|
+
results = do_request(:GET => '/nagui/nagios_live.cgi', :query => URI.encode("query=GET hosts|Filter: name ~~ #{hostname}"))
|
42
|
+
if results.kind_of?(Array) && results.length > 0
|
43
|
+
results[0]
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
def check_host_online(host)
|
49
|
+
@opt['host_up_command'] = 'check-host-alive' unless @opt.has_key?('host_up_command')
|
50
|
+
nagcheck=do_request(:GET => "/nagcheck/command/#{host}/#{@opt['host_up_command']}")
|
51
|
+
if nagcheck['state'] == 0
|
52
|
+
true
|
53
|
+
else
|
54
|
+
false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!ruby
|
2
|
+
|
3
|
+
require 'rspec/collection_matchers'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.expect_with :rspec do |c|
|
7
|
+
c.syntax = [:should, :expect]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def init_test
|
12
|
+
if ENV['TEST_DEBUG'] and ENV['TEST_DEBUG'].length > 0
|
13
|
+
$debug = (ENV['TEST_DEBUG'].to_i == 0 ? 2 : ENV['TEST_DEBUG'].to_i)
|
14
|
+
else
|
15
|
+
$debug = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
$server = 'noms-server'
|
19
|
+
$cmdbapi = '/cmdb_api/v1'
|
20
|
+
$api = '/ncc_api/v2'
|
21
|
+
$opt = {
|
22
|
+
'ncc' => { 'url' => "http://#{$server}#{$api}" },
|
23
|
+
'debug' => $debug,
|
24
|
+
'cmdb' => { 'url' => "http://#{$server}#{$cmdbapi}" }
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
init_test
|
data/noms-client.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'noms/client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "noms-client"
|
8
|
+
spec.version = NOMS::Client::VERSION
|
9
|
+
spec.authors = ["Jeremy Brinkley"]
|
10
|
+
spec.email = ["jbrinkley@evernote.com"]
|
11
|
+
spec.summary = %q{Client libraries and command-line tool for NOMS components}
|
12
|
+
spec.homepage = "http://github.com/evernote/noms-client"
|
13
|
+
spec.license = "Apache-2"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "rspec-collection_matchers"
|
24
|
+
|
25
|
+
spec.add_runtime_dependency "optconfig"
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env rspec
|
2
|
+
|
3
|
+
require 'noms/cmdb'
|
4
|
+
require 'spec_helper'
|
5
|
+
|
6
|
+
describe NOMS::CMDB do
|
7
|
+
|
8
|
+
before(:all) { init_test }
|
9
|
+
|
10
|
+
describe "#new" do
|
11
|
+
|
12
|
+
it "creates a new CMDB client object" do
|
13
|
+
|
14
|
+
obj = NOMS::CMDB.new($opt)
|
15
|
+
obj.should be_a NOMS::CMDB
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|