noms-client 1.10.0 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
data/ROADMAP.rst ADDED
@@ -0,0 +1,215 @@
1
+ noms
2
+ ====
3
+
4
+ This is the roadmap for a second-generation of the 'noms' general purpose interface.
5
+
6
+ **noms** is a remote command-line interface interpreter. It's designed to be a stable runtime environment for interpreting server-defined command-line interfaces for (principally) rest-like data stores (or for commands that are side-effect free, but *not* commands that change any state on the system on which the command runs).
7
+
8
+ The web browser is a platform in which the operator of a web service can implement a graphical user interface to data it controls. For example, it's a common pattern to offer access to services and data via a ReST or ReST-like interface, making http requests against a remote API understanding the HTTP verbs and returning results in the form of HTTP responses, header metadata and response bodies containing serialized data (e.g. JSON). Such interfaces are generally implemented in a combination of HTML documents and Javascript which modifies the document model of the HTML page(s).
9
+
10
+ **noms** enables an author to offer a command-line interface designed along the same pattern: structured documents modified by javascript programs, interpreted and rendered on the client. **noms** has sandboxing similar to web browsers (no modifying of local storage outside of restricted, application-specific local storage and automatic caching of javascript files).
11
+
12
+ **noms** is *not* a web browser and is not designed to offer terminal user interfaces like lynx or elinks. It is also *not* an interactive shell--it's designed to be used from a shell. It maintains authenticated sessions state when necessary.
13
+
14
+ Syntax
15
+ ------
16
+
17
+ The basic way of invoking an **noms** command is as follows::
18
+
19
+ noms *url* *options* *arguments*
20
+
21
+ **noms** invokes the app at *url* with the given options and arguments, displaying the results.
22
+
23
+ Special URLs
24
+ ~~~~~~~~~~~~
25
+
26
+ Certain invalid "URLs" are interpreted specially:
27
+
28
+ * ``noms login *url*``
29
+
30
+ Normally **noms** handles user authentication implicitly. With this command, it does a HEAD request against the application URL, forcing login if required.
31
+
32
+ * ``noms logout *url*``
33
+
34
+ Causes **noms** to forget its session state for the given application URL.
35
+
36
+ * ``noms *bookmark*[/arg] ...``
37
+
38
+ The **noms** command itself has a configuration file (``~/.noms``, ``/usr/local/etc/noms.conf``, ``/etc/noms.conf`` in that order) which defines bookmarks to different URLs. For example, given the following in ``/etc/noms.conf``::
39
+
40
+ {
41
+ "cmdb": "https://cmdb.noms-example.com/cmdb.json",
42
+ "instance": "https://ncc-api.noms-example.com/ncc-api.json",
43
+ "nagios": "https://nagios.noms-example.com/nagui.json",
44
+ "nag": "https://nagios.noms-exmaple.com/nagui.json"
45
+ }
46
+
47
+ When invoked in the following ways, it's the equivalent to the command on the right:
48
+
49
+ ================================= ==================================================================
50
+ Command given Equivalent command
51
+ ================================= ==================================================================
52
+ ``noms cmdb query fqdn~^m00`` ``noms https://cmdb.noms-example.com/cmdb.json query fqdn~^m00``
53
+ (``document.argv[0]`` set to ``cmdb``)
54
+ ``noms cmdb/env list`` ``noms https://cmdb.noms-example.com/cmdb.json list``
55
+ (``document.argv[0]`` set to ``cmdb/env``)
56
+ ``noms nag alerts`` ``noms https://cmdb.noms-example.com/nagui.json alerts``
57
+ (``document.argv[0]`` set to ``nag``)
58
+ ================================= ==================================================================
59
+
60
+ Implementation
61
+ --------------
62
+
63
+ If the type is ``text/plain``, it's simply displayed.
64
+
65
+ If the type is a recognized data serialization format (JSON or YAML):
66
+
67
+ * application/json
68
+ * application/x-json
69
+ * text/json
70
+ * application/yaml
71
+ * application/x-yaml
72
+ * text/yaml
73
+
74
+ If the fetched content is a single object and the object has the top-level key '$doctype', it may be interpreted according to "Dynamic Doctypes", below. Otherwise, it is assumed to be either a single object to display or a list of such. Otherwise **noms** will render the object or array using its default format (usually YAML).
75
+
76
+ Dynamic Doctypes
77
+ ~~~~~~~~~~~~~~~~
78
+
79
+ The principle dynamic doctype is the ``noms-v2``, which is an object with the following top-level attributes:
80
+
81
+ ``$doctype``
82
+ Must be ``noms-v2``. In future, backwards-incompatible extensions may be implemented in ``noms-v3`` or higher doctypes.
83
+
84
+ ``$script``
85
+ An ordered array of scripts to fetch and evaluate.
86
+
87
+ ``$format-fields``
88
+ An array of objects, each having (at least) a ``name`` and ``width`` attribute. May also include a ``label`` attribute
89
+ for the column heading.
90
+
91
+ ``$body``
92
+ The body of the document is the data to display. See `Output Description Notation`_ below.
93
+
94
+ Output Description Notation
95
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
96
+
97
+ The following entities are allowed in the body of a **noms=2** document.
98
+
99
+ * Arrays - Each item in the array is concatenated with a line-break between them.
100
+ * Strings and numbers - A string or number is just displayed
101
+ * Raw objects - Raw objects are rendered using **noms** default formatting (usually YAML)
102
+ * Described objects - Described objects are data along with information on how to render them. A described object
103
+ has a top-level attribute called **$type** which defines how the described object is rendered.
104
+
105
+ * ``$type``: **object-list** An object list is a (usually) tabular list of objects with information on how
106
+ wide to make the fields or how to otherwise serialize the objects. It has the following attributes:
107
+
108
+ * **render**: The format in which to render, one of: **json**, **yaml**, **text** (default **text**)
109
+ * **fields**: Field names, headings and widths
110
+ * **objects**: The objects to render
111
+
112
+ * ``$type``: **object** An object described-object has the following attributes:
113
+
114
+ * **render**: The format in which to render, one of: **json**, **yaml**, **text** (default **yaml**)
115
+ * **object**: The object data
116
+
117
+ Putting it all together
118
+ -----------------------
119
+
120
+ Example **noms** conversation::
121
+
122
+ bash$ noms https://cmdb.noms-example.com/cmdb.json --format=csv system fqdn~^m00
123
+
124
+ noms >> GET https://cmdb.noms-example.com/cmdb.json
125
+ noms << set 'document' to retrieved object:
126
+ { "$doctype": "appdoc",
127
+ "$script": ["lib/optconfig.js", "noms/cmdb.js", "noms/cli.js"],
128
+ "$body": null
129
+ }
130
+ noms << set 'document.argv' to ["https://cmdb.noms-example.com/cmdb.json", "--format=csv", "system", "fqdn~^m00"]
131
+ noms << set 'document.exitcode' to 0
132
+ noms >> GET https://cmdb.noms-example.com/lib/optconfig.js
133
+ noms << evaluate javascript option-parsing library optconfig.js
134
+ noms >> GET https://cmdb.noms-example.com/noms/cmdb.js
135
+ noms << evaluate noms cmdb client library
136
+ noms >> GET https://cmdb.noms-example.com/noms/cli.js
137
+ noms << evaluate noms cli library
138
+ cli.js << calls optconfig().parse with optspec
139
+ optconfig.js << sets document.argv to ["system", "fqdn~^m00"]
140
+ optconfig.js << sets document.options to { "format": "csv" }
141
+ cli.js << call noms_cmdb().query("system", "fqdn~^m00")
142
+ noms/cmdb.js << http.request("https://cmdb.noms-example.com/cmdb_api/v1/system/?fqdn~^m00")
143
+ cli.js << sets document.body to return objects to render
144
+ { "$doctype": "appdoc",
145
+ "$script": ["lib/optconfig.js", "noms/cmdb.js", "noms/cli.js"],
146
+ "$body": [{
147
+ "$type": "object-list",
148
+ "render": "csv",
149
+ "fields": [
150
+ { "name": "fqdn", "width": 36 },
151
+ { "name": "environment_name", "width": 16, "heading": "environment" },
152
+ { "name": "status", "width": 15 },
153
+ { "name": "roles", "width": 15 },
154
+ { "name": "ipaddress", "width": 15 },
155
+ { "name": "data_center_code": 11, "heading": "datacenter" } ],
156
+ "objects": [
157
+ { "fqdn": "m001.noms-example.com",
158
+ "environment_name": "production",
159
+ "status": "production",
160
+ "roles": "build",
161
+ "ipaddress": "10.8.9.10",
162
+ "data_center_code": "US2" },
163
+ { "fqdn": "m002.noms-example.com",
164
+ "environment_name": "testing",
165
+ "status": "allocated",
166
+ "roles": "webserver",
167
+ "ipaddress": "10.8.9.11",
168
+ "data_center_code": "US2" }
169
+ ]
170
+ }
171
+ ]
172
+ }
173
+
174
+ noms >> print output
175
+ fqdn,environment,status,roles,ipaddress,datacenter
176
+ "m001.noms-example.com",production,production,build,10.8.9.10,US2
177
+ "m002.noms-example.com",allocated,testing,webserver,10.8.9.11,US2
178
+
179
+ bash$ noms https://ncc-api.noms-example.com/ncc.json show m002.noms-example.com
180
+
181
+ { "$doctype": "appdoc",
182
+ "$script": ["noms/optconfig.js",
183
+ { "name": "name", "width": 36 },
184
+ { "name": "status", "width": 10 },
185
+ { "name": "size", "width": 10 },
186
+ { "name": "image", "width": 15 },
187
+ { "name": "id", "width": 37 }
188
+ ]
189
+ "$body": null
190
+ }
191
+
192
+ name status size image id
193
+ m0000291.noms-example.net active m1.small deb6 d8c4c29e-785f-49ef-9d31-e4a71e9954fc
194
+ m0000290.noms-example.net active m1.small deb7 33a88a1d-49a4-4c26-9a0c-b699703f5e64
195
+ m0000289.noms-example.net active m1.small deb7 fd82f522-f305-4150-a969-1b8b9fd2d91d
196
+ m0000288.noms-example.net error m1.small deb6 9d7f1c55-5f8f-4f98-9bf8-c1156a0506d2
197
+ m0000287.noms-example.net active m1.small deb6 c4a6310d-4927-4e79-8170-443172eb9a7c
198
+ m0000286.noms-example.net active m1.small centos6.2 88c654b6-77f2-4995-affb-c3a3bac16bd0
199
+ m0000277.noms-example.net active m1.small deb6 e34e4a8f-81ef-42a3-a9c0-40933be7595f
200
+
201
+ Invoked scripts have access to the following global objects:
202
+
203
+ * **window** - This has information about the terminal environment in which **noms** is being invoked. It has the following attributes:
204
+ * **height** - Height (if known)
205
+ * **width** - Width (if known)
206
+ * **isatty** - true if the output stream is a terminal
207
+ * **document** - The document global object
208
+ * **document** - The document object is the current document being rendered by **noms**. In addition to the attributes of the document itself, it has the following:
209
+ * **argv** - The arguments being invoked. The first element of this array is the first argument passed to **noms** itself (not the script it ultimately fetches, but how it's invoked, similar to ``$1``
210
+ * **exitcode** - The numeric exit code with which **noms** will exit. Initially 0.
211
+
212
+ Web 1.0 vs Web 2.0
213
+ ------------------
214
+
215
+ Like the "real web", **noms** commands can choose to do some calculation on the server and some on the client: **noms** doesn't care. You can use no ``$script`` tag at all and just calculate the entire document to be rendered in the client (though this currently odoesn't allow for argument interpretation, in the future the arguments may be passed in request headers or **noms** may allow a way for them to show up in a query string or POST request--but **noms** is not really a command-line http client either). This is up to the application designer.
data/bin/noms CHANGED
@@ -63,7 +63,13 @@
63
63
  # show type name - Show information for named type [hostgroup,host,service]
64
64
  # detail host - Show object detail for named host
65
65
  # query type [condition [...]] - Query for object by type [hostgroup,host,service]
66
- # check <host> [<service>] - Run service check for host (or host check) synchronously
66
+ # check host [service] - Run service check for host (or host check) synchronously
67
+ # ack host [service] <comment - acknowledge a warning or critical host/service
68
+ # comment host [service] comment - add a comment to a host or service
69
+ # downtime host [service] duration comment - put a host or service into maintenance
70
+ # undowntime host [service] - Remove downtime from a host or service
71
+ # alerts - List all unacknowledged problems that are in a hard non-OK
72
+ # state and are not in a scheduled downtime period
67
73
  #
68
74
  # Options:
69
75
  # --names - Just print entity names, equivalent to
@@ -242,7 +248,7 @@ $opt = Optconfig.new('noms', {
242
248
  'state' => 5,
243
249
  'plugin_output' => 36
244
250
  }
245
- },
251
+ },
246
252
  'service' => {
247
253
  'fields' => [
248
254
  'host_name','description','state', 'plugin_output'
@@ -517,7 +523,7 @@ end
517
523
 
518
524
  def cmdb_show(args)
519
525
  fqdn = args.shift
520
- system = $cmdb.query('system', 'fqdn=' + fqdn).first
526
+ system = $cmdb.system(fqdn)
521
527
  record = if ! args.empty?
522
528
  system.keys.inject({}) do |h, k|
523
529
  if args.include? k
@@ -618,6 +624,52 @@ def nagios(args)
618
624
  end
619
625
  when 'check'
620
626
  record_formatted_output(nagcheck(args),'nagcheck')
627
+ when 'ack'
628
+ host=args.shift
629
+ if args.length == 1
630
+ comment=args.shift
631
+ $nagui.ack_host(host,get_username,comment)
632
+ else
633
+ service=args.shift
634
+ if(args.length > 1)
635
+ comment = args.join(' ')
636
+ else
637
+ comment = args.shift
638
+ end
639
+ $nagui.ack_service(host,service,get_username,comment)
640
+ end
641
+ when 'alerts'
642
+ formatted_output($nagui.query('service',['acknowledged=0','state!=0','state_type=1',"scheduled_downtime_depth=0","host_scheduled_downtime_depth=0"]),'service')
643
+ when 'comment'
644
+ host=args.shift
645
+ if args.length == 1
646
+ comment=args.shift
647
+ $nagui.comment(host,nil,get_username,comment)
648
+ else
649
+ service=args.shift
650
+ if(args.length > 1)
651
+ comment = args.join(' ')
652
+ else
653
+ comment = args.shift
654
+ end
655
+ $nagui.comment(host,service,get_username,comment)
656
+ end
657
+ when 'downtime','down'
658
+ host = args.shift
659
+ if args.length == 2
660
+ length = args.shift
661
+ comment = args.shift
662
+ $nagui.downtime_host(host,length,get_username,comment)
663
+ else
664
+ service = args.shift
665
+ length = args.shift
666
+ comment = args.join(' ')
667
+ $nagui.downtime_service(host,service,length,get_username,comment)
668
+ end
669
+ when 'undowntime','undown'
670
+ host = args.shift
671
+ service = args.shift if !args.empty? || nil
672
+ $nagui.undowntime(host,service)
621
673
  else
622
674
  $stderr.puts "Unknown nagios command '#{cmd}'"
623
675
  Process.exit(2)
@@ -640,6 +692,10 @@ def cmdb(args)
640
692
  fqdn = args.shift
641
693
  updated = $cmdb.update('system', opt, fqdn)
642
694
  ''
695
+ when 'add'
696
+ fqdn = args.shift
697
+ created = $cmdb.create('system', opt, fqdn)
698
+ ''
643
699
  else
644
700
  $stderr.puts "Unknown cmdb command '#{cmd}'"
645
701
  Process.exit(2)
@@ -3,5 +3,5 @@ class NOMS
3
3
  end
4
4
 
5
5
  class NOMS::Client
6
- VERSION = '1.10.0'
6
+ VERSION = '1.12.0'
7
7
  end
data/lib/noms/cmdb.rb CHANGED
@@ -61,6 +61,20 @@ class NOMS::CMDB < NOMS::HttpClient
61
61
  do_request(:GET => "pcmsystemname/#{serial}")
62
62
  end
63
63
 
64
+ def create(type, obj, key=nil)
65
+ keyfield = key_field_of(type)
66
+ objkey = obj[keyfield]
67
+
68
+ if key.nil? and objkey.nil?
69
+ raise NOMS::Error, "Must specify a key value or imply it in the object's #{key_field_of(type)} value"
70
+ end
71
+ if (obj.has_key?(keyfield) and (! key.nil?) and obj[keyfield] != key)
72
+ raise NOMS::Error, "You specified different keys for a new #{type} object: #{objkey} in the object vs. #{key}"
73
+ end
74
+ newobj = { keyfield => (objkey || key) }.merge obj
75
+ do_request(:POST => type, :body => newobj)
76
+ end
77
+
64
78
  def update(type, obj, key=nil)
65
79
  key ||= obj[key_field_of(type)]
66
80
  do_request(:PUT => "#{type}/#{key}", :body => obj)
@@ -119,12 +133,25 @@ class NOMS::CMDB::Mock < NOMS::HttpClient::RestMock
119
133
 
120
134
  @@machine_id = 0
121
135
 
136
+ def allow_put_to_create
137
+ false
138
+ end
139
+
140
+ def id_field(path)
141
+ case path
142
+ when %r{system$}
143
+ 'fqdn'
144
+ else
145
+ 'id'
146
+ end
147
+ end
148
+
122
149
  def handle_mock(method, uri, opt)
123
150
  if m = Regexp.new('/pcmsystemname/([^/]+)').match(uri.path)
124
151
  serial = m[1]
125
152
  @@machine_id += 1
126
153
  name = "m-%03d.mock" % @@machine_id
127
- do_request :PUT => "system/#{name}",
154
+ do_request :POST => "system",
128
155
  :body => {
129
156
  'serial' => serial,
130
157
  'fqdn' => name
@@ -132,6 +132,10 @@ class NOMS::HttpClient
132
132
  false
133
133
  end
134
134
 
135
+ def allow_put_to_create
136
+ true
137
+ end
138
+
135
139
  def default_content_type
136
140
  'application/json'
137
141
  end
@@ -253,9 +257,14 @@ class NOMS::HttpClient::RestMock < NOMS::HttpClient
253
257
  object_index =
254
258
  @data[url.host][collection_path].index { |el| el[id_field(collection_path)] == id }
255
259
  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 })
260
+ if allow_put_to_create
261
+ object = opt[:body].merge({ id_field(collection_path) => id })
262
+ dbg "creating in collection #{collection_path}: #{object.inspect}"
263
+ @data[url.host][collection_path] << opt[:body].merge({ id_field(collection_path) => id })
264
+ else
265
+ dbg "not creating in collection #{collection_path}: #{id} (allow_put_to_create is false)"
266
+ raise NOMS::Error, "There is no resource at this location"
267
+ end
259
268
  else
260
269
  if allow_partial_updates
261
270
  object = @data[url.host][collection_path][object_index].merge(opt[:body])
data/lib/noms/nagui.rb CHANGED
@@ -16,7 +16,7 @@
16
16
  # */
17
17
 
18
18
  require 'noms/httpclient'
19
- require 'uri'
19
+ require 'cgi'
20
20
 
21
21
  class NOMS
22
22
 
@@ -80,10 +80,11 @@ class NOMS::Nagui < NOMS::HttpClient
80
80
  Process.exit(1)
81
81
  end
82
82
  query_string = make_lql(type,queries)
83
- results = do_request(:GET => '/nagui/nagios_live.cgi', :query => URI.encode("query=#{query_string}"))
83
+ results = do_request(:GET => '/nagui/nagios_live.cgi', :query => "query=#{CGI.escape(query_string)}")
84
84
  end
85
85
  def hostgroup(name)
86
- results = do_request(:GET => '/nagui/nagios_live.cgi', :query => URI.encode("query=GET hostgroups|Filter: name = #{name}"))
86
+ lql = "GET hostgroups|Filter: name = #{name}"
87
+ results = do_request(:GET => '/nagui/nagios_live.cgi', :query => "query=#{CGI.escape(lql)}")
87
88
  if results.kind_of?(Array) && results.length > 0
88
89
  results[0]
89
90
  else
@@ -91,7 +92,8 @@ class NOMS::Nagui < NOMS::HttpClient
91
92
  end
92
93
  end
93
94
  def service(host,description)
94
- results = do_request(:GET => '/nagui/nagios_live.cgi', :query => URI.encode("query=GET services|Filter: host_name = #{host}|Filter: description = #{description}"))
95
+ lql = "GET services|Filter: host_name = #{host}|Filter: description = #{description}"
96
+ results = do_request(:GET => '/nagui/nagios_live.cgi', :query => "query=#{CGI.escape(lql)}")
95
97
  if results.kind_of?(Array) && results.length > 0
96
98
  results[0]
97
99
  else
@@ -99,7 +101,8 @@ class NOMS::Nagui < NOMS::HttpClient
99
101
  end
100
102
  end
101
103
  def servicegroup(name)
102
- results = do_request(:GET => '/nagui/nagios_live.cgi', :query => URI.encode("query=GET hosts|Filter: name = #{name}"))
104
+ lql = "query=GET hosts|Filter: name = #{name}"
105
+ results = do_request(:GET => '/nagui/nagios_live.cgi', :query => "query=#{CGI.escape(lql)}")
103
106
  if results.kind_of?(Array) && results.length > 0
104
107
  results[0]
105
108
  else
@@ -107,7 +110,8 @@ class NOMS::Nagui < NOMS::HttpClient
107
110
  end
108
111
  end
109
112
  def host(hostname)
110
- results = do_request(:GET => '/nagui/nagios_live.cgi', :query => URI.encode("query=GET hosts|Filter: name = #{hostname}"))
113
+ lql = "GET hosts|Filter: name = #{hostname}"
114
+ results = do_request(:GET => '/nagui/nagios_live.cgi', :query => "query=#{CGI.escape(lql)}")
111
115
  if results.kind_of?(Array) && results.length > 0
112
116
  results[0]
113
117
  else
@@ -115,6 +119,73 @@ class NOMS::Nagui < NOMS::HttpClient
115
119
  end
116
120
  end
117
121
 
122
+ def calc_time(str)
123
+ case str
124
+ when /(\d+)m/
125
+ #minutes
126
+ $1.to_i * 60
127
+ when /(\d+)h/
128
+ #hours
129
+ $1.to_i * 3600
130
+ when /(\d+)d/
131
+ #days
132
+ $1.to_i * 86400
133
+ else
134
+ str.to_i
135
+ end
136
+ end
137
+
138
+ def process_command(cmd)
139
+ do_request(:POST => '/nagui/nagios_live.cgi', :body => "query=#{CGI.escape(cmd)}", :content_type => 'application/x-www-form-urlencoded')
140
+ end
141
+
142
+ def downtime_host(host,length,user,comment)
143
+ starttime=Time.now.to_i
144
+ endtime = starttime + calc_time(length)
145
+ cmd="COMMAND [#{Time.now.to_i}] SCHEDULE_HOST_DOWNTIME;#{host};#{starttime};#{endtime};1;0;0;#{user};#{comment}"
146
+ process_command(cmd)
147
+ end
148
+ def downtime_service(host,service,length,user,comment)
149
+ starttime=Time.now.to_i
150
+ endtime = starttime + calc_time(length)
151
+ cmd="COMMAND [#{Time.now.to_i}] SCHEDULE_SVC_DOWNTIME;#{host};#{service};#{starttime};#{endtime};1;0;0;#{user};#{comment}"
152
+ process_command(cmd)
153
+ end
154
+ def undowntime(host,service=nil)
155
+ if service == nil
156
+ host_record = host(host)
157
+ host_record['downtimes'].each do |id|
158
+ cmd = "COMMAND [#{Time.now.to_i}] DEL_HOST_DOWNTIME;#{id}"
159
+ process_command(cmd)
160
+ end
161
+ else
162
+ service_record = service(host,service)
163
+ service_record['downtimes'].each do |id|
164
+ cmd = "COMMAND [#{Time.now.to_i}] DEL_SVC_DOWNTIME;#{id}"
165
+ process_command(cmd)
166
+ end
167
+ end
168
+ end
169
+
170
+ def comment(host,service,user,comment)
171
+ if service == nil
172
+ cmd="COMMAND [#{Time.now.to_i}] ADD_HOST_COMMENT;#{host};1;#{user};#{comment}"
173
+ else
174
+ cmd="COMMAND [#{Time.now.to_i}] ADD_SVC_COMMENT;#{host};#{service};1;#{user};#{comment}"
175
+ end
176
+ process_command(cmd)
177
+ end
178
+
179
+ def ack_host(host,user,comment)
180
+ cmd="COMMAND [#{Time.now.to_i}] ACKNOWLEDGE_HOST_PROBLEM;#{host};0;1;1;#{user};#{comment}"
181
+ process_command(cmd)
182
+ end
183
+
184
+ def ack_service(host,service,user,comment)
185
+ cmd="COMMAND [#{Time.now.to_i}] ACKNOWLEDGE_SVC_PROBLEM;#{host};#{service};0;1;1;#{user};#{comment}"
186
+ process_command(cmd)
187
+ end
188
+
118
189
  def nagcheck_host(host)
119
190
  url = "/nagcheck/host/#{host}"
120
191
  dbg("nagcheck url= #{url}")
@@ -5,7 +5,11 @@ require 'spec_helper'
5
5
 
6
6
  describe NOMS::CMDB do
7
7
 
8
- before(:all) { init_test }
8
+ before(:all) do
9
+ init_test
10
+
11
+ NOMS::CMDB.mock! nil
12
+ end
9
13
 
10
14
  describe "#new" do
11
15
 
@@ -18,4 +22,41 @@ describe NOMS::CMDB do
18
22
 
19
23
  end
20
24
 
25
+ describe "#create" do
26
+
27
+ before(:each) { @cmdb = NOMS::CMDB.new $opt }
28
+
29
+ it 'creates an entry' do
30
+ obj = @cmdb.create('system', { 'fqdn' => 'test0', 'inventory_component_type' => 'system'})
31
+ expect(obj).to have_key 'fqdn'
32
+ expect(obj).to have_key 'inventory_component_type'
33
+ expect(obj['fqdn']).to eq 'test0'
34
+ end
35
+
36
+ it 'finds the same entry' do
37
+ @cmdb.create('system', { 'fqdn' => 'test2', 'inventory_component_type' => 'system'})
38
+ obj = @cmdb.system('test2')
39
+ expect(obj).to have_key 'fqdn'
40
+ expect(obj).to include 'inventory_component_type' => 'system'
41
+ end
42
+
43
+ it 'creates an entry under the specified key' do
44
+ @cmdb.create('system', { 'inventory_component_type' => 'system' }, 'test3')
45
+ obj = @cmdb.system('test3')
46
+ expect(obj).to include 'fqdn' => 'test3'
47
+ expect(obj).to include 'inventory_component_type' => 'system'
48
+ end
49
+
50
+ it 'fails to create a keyless entry' do
51
+ expect { @cmdb.create('system', { 'environment_name' => 'random' }) }.to raise_error(NOMS::Error)
52
+ end
53
+
54
+ it 'fails when different keys are specified' do
55
+ expect { @cmdb.create('system',
56
+ { 'fqdn' => 'test3', 'environment_name' => 'random' },
57
+ 'test4') }.to raise_error(NOMS::Error)
58
+ end
59
+
60
+ end
61
+
21
62
  end
@@ -3,6 +3,8 @@
3
3
  require 'noms/cmdb'
4
4
  require 'spec_helper'
5
5
 
6
+ # The NOMS::CMDB::RestMock has
7
+ # a non-upserting PUT
6
8
  describe NOMS::CMDB::RestMock do
7
9
 
8
10
  before(:all) do
@@ -62,39 +64,28 @@ describe NOMS::CMDB::RestMock do
62
64
  it 'synthesizes an id' do
63
65
  result = @cmdb.do_request :POST => '/environments', :body => { 'name' => 'production' }
64
66
  expect(result).to have_key 'id'
65
- expect(@cmdb.all_data[$server][$cmdbapi + '/environments']).to include { |o| o['id'] = result['id'] }
67
+ expect(@cmdb.all_data[$server][$cmdbapi + '/environments']).to include { |o| o['id'] == result['id'] }
68
+ end
69
+
70
+ it 'understands alternate id fields' do
71
+ result = @cmdb.do_request :POST => '/system', :body => {
72
+ 'fqdn' => 'test0',
73
+ 'environment_name' => 'production' }
74
+ expect(result).to have_key 'fqdn'
75
+ expect(@cmdb.all_data[$server][$cmdbapi + '/system']).to include { |o| o['fqdn'] == 'test0' }
66
76
  end
67
77
 
68
78
  end
69
79
 
70
80
  context :PUT do
71
81
 
72
- it 'creates a new entry' do
73
- @cmdb.do_request :PUT => '/environments/1', :body => {
74
- 'name' => 'production', 'environment_name' => 'production' }
75
- expect(@cmdb.all_data[$server][$cmdbapi + '/environments']).to include { |o| o['name'] == 'production' }
76
- end
77
-
78
- it 'creates several new entries' do
79
- @cmdb.do_request :PUT => '/environments/1', :body => {
80
- 'name' => 'production', 'environment_name' => 'production' }
81
- 10.times do |i|
82
- @cmdb.do_request :PUT => "/environments/#{100 + i}", :body => {
83
- 'name' => "environment-#{i}", 'environment_name' => 'production' }
84
- end
85
- expect(@cmdb.all_data[$server][$cmdbapi + '/environments']).to have(11).items
86
- expect(@cmdb.all_data[$server][$cmdbapi + '/environments']).to include { |o| o['name'] == 'production' }
87
- end
88
-
89
- it 'infers the id' do
90
- result = @cmdb.do_request :PUT => '/environments/1', :body => { 'name' => 'production' }
91
- expect(result).to have_key 'id'
92
- expect(@cmdb.all_data[$server][$cmdbapi + '/environments'][0]).to have_key 'id'
93
- expect(@cmdb.all_data[$server][$cmdbapi + '/environments'][0]['id']).to eq '1'
82
+ it 'fails to create a new entry' do
83
+ expect { @cmdb.do_request(:PUT => '/environments/1', :body => {
84
+ 'name' => 'production', 'environment_name' => 'production' }) }.to raise_error(NOMS::Error)
94
85
  end
95
86
 
96
87
  it 'replaces an existing object' do
97
- @cmdb.do_request :PUT => '/environments/1', :body => {
88
+ @cmdb.do_request :POST => '/environments', :body => { 'id' => '1',
98
89
  'name' => 'production', 'environment_name' => 'production' }
99
90
  @cmdb.do_request :PUT => '/environments/1', :body => {
100
91
  'name' => 'testing', 'note' => 'testing environment' }
@@ -110,7 +101,7 @@ describe NOMS::CMDB::RestMock do
110
101
  def @cmdb.allow_partial_updates
111
102
  true
112
103
  end
113
- @cmdb.do_request :PUT => '/environments/1', :body => {
104
+ @cmdb.do_request :POST => '/environments', :body => { 'id' => '1',
114
105
  'name' => 'production', 'environment_name' => 'production' }
115
106
  @cmdb.do_request :PUT => '/environments/1', :body => {
116
107
  'name' => 'testing', 'note' => 'testing environment' }
@@ -134,6 +125,16 @@ describe NOMS::CMDB::RestMock do
134
125
  expect(result).to include 'name' => 'production'
135
126
  end
136
127
 
128
+ it 'finds an existing entry by alternate id' do
129
+ @cmdb.do_request :POST => '/system', :body => {
130
+ 'fqdn' => 'test0', 'environment_name' => 'production'
131
+ }
132
+ result = @cmdb.do_request :GET => '/system/test0'
133
+ expect(result).to be_a Hash
134
+ expect(result).to include 'fqdn' => 'test0'
135
+ expect(result).to include 'environment_name' => 'production'
136
+ end
137
+
137
138
  it 'retrieves all entries' do
138
139
  @cmdb.do_request :POST => '/environments', :body => {
139
140
  'name' => 'production', 'environment_name' => 'production'
@@ -151,7 +152,7 @@ describe NOMS::CMDB::RestMock do
151
152
 
152
153
  it 'raises an exception for missing entries' do
153
154
  expect { @cmdb.do_request :GET => '/environments/1' }.to raise_error
154
- @cmdb.do_request :PUT => '/environments/1', :body => { 'name' => 'production' }
155
+ @cmdb.do_request :POST => '/environments', :body => { 'id' => '1', 'name' => 'production' }
155
156
  expect { @cmdb.do_request :GET => '/chorizo' }.to raise_error
156
157
  expect { @cmdb.do_request :GET => '/environments/2' }.to raise_error
157
158
  end
@@ -161,16 +162,16 @@ describe NOMS::CMDB::RestMock do
161
162
  context :DELETE do
162
163
 
163
164
  it 'deletes an existing entry' do
164
- @cmdb.do_request :PUT => '/environments/1', :body => { 'name' => 'production' }
165
+ @cmdb.do_request :POST => '/environments', :body => { 'id' => '1', 'name' => 'production' }
165
166
  result = @cmdb.do_request :DELETE => '/environments/1'
166
167
  expect(result).to be true
167
168
  expect(@cmdb.all_data[$server][$cmdbapi + '/environments']).to have(0).items
168
169
  end
169
170
 
170
171
  it 'deletes an existing entry among many' do
171
- @cmdb.do_request :PUT => '/environments/1', :body => { 'name' => 'production' }
172
+ @cmdb.do_request :POST => '/environments', :body => { 'id' => '1', 'name' => 'production' }
172
173
  10.times do |i|
173
- @cmdb.do_request :PUT => "/environments/#{100 + i}", :body => {
174
+ @cmdb.do_request :POST => "/environments", :body => { 'id' => "#{100 + i}",
174
175
  'name' => "environment-#{i}", 'environment_name' => 'production' }
175
176
  end
176
177
  @cmdb.do_request :DELETE => '/environments/103'
@@ -180,14 +181,14 @@ describe NOMS::CMDB::RestMock do
180
181
  end
181
182
 
182
183
  it 'deletes a whole collection' do
183
- @cmdb.do_request :PUT => '/environments/1', :body => {'name' => 'production' }
184
+ @cmdb.do_request :POST => '/environments', :body => {'id' => '1', 'name' => 'production' }
184
185
  @cmdb.do_request :DELETE => '/environments'
185
186
  expect(@cmdb.all_data[$server]).not_to have_key '/environments'
186
187
  end
187
188
 
188
189
  it 'raises an exception for nonexistent entries' do
189
190
  expect { @cmdb.do_request :DELETE => '/environments/2' }.to raise_error
190
- @cmdb.do_request :PUT => '/environments/1', :body => { 'name' => 'production' }
191
+ @cmdb.do_request :POST => '/environments', :body => { 'id' => '1', 'name' => 'production' }
191
192
  expect { @cmdb.do_request :DELETE => '/environments/2' }.to raise_error
192
193
  expect { @cmdb.do_request :DELETE => '/chorizo' }.to raise_error
193
194
  end
@@ -42,10 +42,11 @@ describe NOMS::CMDB::RestMock do
42
42
  end
43
43
  end
44
44
 
45
- context :PUT do
45
+ context :POST do
46
46
 
47
47
  it 'creates a new entry' do
48
- @cmdb.do_request :PUT => '/environments/production', :body => {
48
+ @cmdb.do_request :POST => '/environments', :body => {
49
+ 'id' => 'production',
49
50
  'name' => 'production', 'environment_name' => 'production' }
50
51
  expect(File.exist? $datafile).to be true
51
52
  end
data/spec/06noms-mock.sh CHANGED
@@ -33,3 +33,22 @@ teardown() {
33
33
  EOF
34
34
  noms --config=test/etc/noms.conf --mock=test/data.json cmdb show test1.example.com
35
35
  }
36
+
37
+ @test "noms-mock cmdb add" {
38
+ cat >test/data.json <<EOF
39
+ { "cmdb": {
40
+ "/cmdb_api/v1/system": [
41
+ { "id": "test1.example.com",
42
+ "fqdn": "test1.example.com",
43
+ "status": "idle",
44
+ "environment": "testing",
45
+ "data_center_code": "DC1",
46
+ "ip_address": "10.0.0.1" }
47
+ ]
48
+ }
49
+ }
50
+ EOF
51
+ noms --config=test/etc/noms.conf --mock=test/data.json cmdb add test2.example.com inventory_component_type=system
52
+ type=$(noms --config=test/etc/noms.conf --mock=test/data.json --nolabel cmdb show test2.example.com inventory_component_type)
53
+ [ "$type" = 'system' ]
54
+ }
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: noms-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.10.0
4
+ version: 1.12.0
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Jeremy Brinkley
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-02-25 00:00:00.000000000 Z
12
+ date: 2016-01-27 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,6 +30,7 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: rake
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
35
  - - ~>
32
36
  - !ruby/object:Gem::Version
@@ -34,6 +38,7 @@ dependencies:
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
43
  - - ~>
39
44
  - !ruby/object:Gem::Version
@@ -41,43 +46,49 @@ dependencies:
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: rspec
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
- - - '>='
51
+ - - ! '>='
46
52
  - !ruby/object:Gem::Version
47
53
  version: '0'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
- - - '>='
59
+ - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: rspec-collection_matchers
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
- - - '>='
67
+ - - ! '>='
60
68
  - !ruby/object:Gem::Version
61
69
  version: '0'
62
70
  type: :development
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
- - - '>='
75
+ - - ! '>='
67
76
  - !ruby/object:Gem::Version
68
77
  version: '0'
69
78
  - !ruby/object:Gem::Dependency
70
79
  name: optconfig
71
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
72
82
  requirements:
73
- - - '>='
83
+ - - ! '>='
74
84
  - !ruby/object:Gem::Version
75
85
  version: '0'
76
86
  type: :runtime
77
87
  prerelease: false
78
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
79
90
  requirements:
80
- - - '>='
91
+ - - ! '>='
81
92
  - !ruby/object:Gem::Version
82
93
  version: '0'
83
94
  description:
@@ -93,11 +104,11 @@ files:
93
104
  - Gemfile
94
105
  - LICENSE
95
106
  - README.rst
107
+ - ROADMAP.rst
96
108
  - Rakefile
97
109
  - TODO
98
110
  - bin/ansible-cmdb
99
111
  - bin/noms
100
- - control.m4
101
112
  - etc/noms.conf
102
113
  - lib/ncc/client.rb
103
114
  - lib/noms/client/version.rb
@@ -117,26 +128,27 @@ files:
117
128
  homepage: http://github.com/evernote/noms-client
118
129
  licenses:
119
130
  - Apache-2
120
- metadata: {}
121
131
  post_install_message:
122
132
  rdoc_options: []
123
133
  require_paths:
124
134
  - lib
125
135
  required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
126
137
  requirements:
127
- - - '>='
138
+ - - ! '>='
128
139
  - !ruby/object:Gem::Version
129
140
  version: '0'
130
141
  required_rubygems_version: !ruby/object:Gem::Requirement
142
+ none: false
131
143
  requirements:
132
- - - '>='
144
+ - - ! '>='
133
145
  - !ruby/object:Gem::Version
134
146
  version: '0'
135
147
  requirements: []
136
148
  rubyforge_project:
137
- rubygems_version: 2.0.14
149
+ rubygems_version: 1.8.23
138
150
  signing_key:
139
- specification_version: 4
151
+ specification_version: 3
140
152
  summary: Client libraries and command-line tool for NOMS components
141
153
  test_files:
142
154
  - spec/01nomscmdb_spec.rb
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 9e24aa514edf837a8d6d263ffe707e86aefcc76f
4
- data.tar.gz: 09e54474d3b41a4a257fa1cf98a248ad16b66cd5
5
- SHA512:
6
- metadata.gz: c6707f63d16653725c7d9e00be5421e282db4a7195a932bd793643021f70ef8da14a04d698bcd940c1fc41ffa3d11613ec24180bb54b59e357ccb9970fb7bc1e
7
- data.tar.gz: 35bf1ffa2984e3f338927060d46f1021b8c2b21b09d8656f70cd965468c920800aaf4752995cc3435f9cc7679767781d3f224a1cd32aac131946042e3c0f523f
data/control.m4 DELETED
@@ -1,8 +0,0 @@
1
- Package: noms-client
2
- Version: __VERSION__-__RELEASE__
3
- Architecture: all
4
- Section: Miscellaneous
5
- Priority: optional
6
- Maintainer: Evernote Operations <ops@evernote.com>
7
- Description: This package is the Evernote-specific noms-client build
8
-