noms-client 1.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/noms ADDED
@@ -0,0 +1,774 @@
1
+ #!/usr/bin/env 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
+ # = NAME
19
+ #
20
+ # noms - CLI for New Operations Management Stack
21
+ #
22
+ # = SYNOPSIS
23
+ #
24
+ # noms command [options] [arguments]
25
+ # cmdb subcommand
26
+ # query [condition [...]] - Query for systems
27
+ # set fqdn field=value [field=value [...]] -
28
+ # Update system attributes
29
+ # show fqdn - Show fields for one system
30
+ # waitfor expr cmdb query - Waitfor CMDB query to have
31
+ # specified number of results
32
+ # instance subcommand - Operate on NOMS instances
33
+ # add [cloud] [attribute=value [...]] -
34
+ # Add a new NOMS instance
35
+ # list [cloud] - List NOMS instances (in cloud)
36
+ # show [cloud/]instanceid
37
+ # show [cloud/]name - Show details of cloud instance
38
+ # (can include --fields console)
39
+ # remove [cloud/]instanceid
40
+ # remove [cloud/]name - Terminate a cloud instance
41
+ # environment subcommand - Operate on NOMS environment
42
+ # add name [environment_name=value] [note=value] -
43
+ # Add a new environment
44
+ # list - List environments
45
+ # tree - Show environments in a tree
46
+ # show name - Show details on <environment>
47
+ # remove name - Remove environment
48
+ # svcinst subcommand - Operate on NOMS service instances
49
+ # add [environment/]service - Add service instance
50
+ # set [environment/]service property=value -
51
+ # Set property on service instance
52
+ # show [environment/]service- Show details on service instance
53
+ # list [environment] - List service instances for environment
54
+ # remove [environment/]service -
55
+ # Remove a service instance from an
56
+ # environment
57
+ # describe item - Describe meta-data for NOMS
58
+ # clouds - List clouds
59
+ # cloud name - Describe named cloud
60
+ # ncc - Describe NCC API endpoint
61
+ # cmdb - Describe system fields
62
+ # Options:
63
+ # --names - Just print entity names, equivalent to
64
+ # --fields name --noheader --nofeedback --nolabel
65
+ # --fields field[,field[,...]]
66
+ # --format {text | json | csv} - Print output in specified format
67
+ # --timeout - Timeout for network operations (especially
68
+ # relevant for waitfor)
69
+ # --noheader
70
+ # --nofeedback
71
+ # --nolabel
72
+ # options as described in Optconfig
73
+ #
74
+ #
75
+ # = DESCRIPTION
76
+ #
77
+ # The +noms+ command is used to set up and manage virtual and physical environments.
78
+ #
79
+ # == Commands
80
+ #
81
+ # * describe
82
+ #
83
+ # The +describe+ command (+desc+ is a synonym) is used to describe different
84
+ # things about the NOMS environment.
85
+ #
86
+ # ** commands
87
+ #
88
+ # List the different commands available
89
+ #
90
+ # * instance
91
+ #
92
+ # The +instance+ command is used for operating on individual instances in the
93
+ # NOMS environment. Use it by invoking subcommands to add, remove and list
94
+ # instances.
95
+ #
96
+ # ** list
97
+ #
98
+ # List instances. Use the *--verbose* option to list more fields
99
+ #
100
+ # Example: +noms instance list us-east-1+
101
+ #
102
+ # ** show
103
+ #
104
+ # Show information about the named instance.
105
+ #
106
+ # Example: +noms instance show us-east-1/webserver1.ops-lab.net+
107
+ #
108
+ # * cmdb
109
+ #
110
+ # Perform operations on the CMDB.
111
+ #
112
+ # ** show
113
+ #
114
+ # Show field values for the specified system (identified by fqdn).
115
+ #
116
+ # Example: +noms cmdb show webserver1.ops-lab.net+
117
+ #
118
+ # ** query
119
+ #
120
+ # Perform a query and display tabular results (see the *--fields*
121
+ # and *--format* option).
122
+ #
123
+ # Example: +noms cmdb query status=allocated data_center_code=XDC1 'tags!~backup'+
124
+ #
125
+ # ** set
126
+ #
127
+ # Set field values for the system in the CMDB.
128
+ #
129
+ # Example: +noms cmdb set webserver1.ops-lab.net status=decommissioned+
130
+ #
131
+ # * waitfor
132
+ #
133
+ # Wait for a CMDB query to have a specified number of result rows. Possible value of
134
+ # the result row count expression is a number signifying a number of rows to exactly match,
135
+ # or '>n' indicating that more than _n_ rows must match. The following syntax is 'cmdb query
136
+ # field-expression', just as with the 'cmdb query' command.
137
+ #
138
+ # Example: +noms waitfor >1 cmdb query status=idle roles=xenserver+
139
+ #
140
+ # == Options
141
+ #
142
+ # * --names
143
+ #
144
+ # Just print the names of the entries. (Synonymous with --noheader --nofeedback --fields=fqdn
145
+ # or --fields=name)
146
+ #
147
+ # Example: +noms cmdb query status=idle --names+
148
+ #
149
+ # Example: +noms instance list --names+
150
+ #
151
+ # * --fields
152
+ #
153
+ # Use the specified comma-separated list of fields for printing results.
154
+ #
155
+ # Example: +noms cmdb query 'status!=decommissioned' --fields=fqdn,operatingsystem,status+
156
+ #
157
+ # * --format
158
+ #
159
+ # Specify the output format. One of: text, csv or json (default is 'text').
160
+ #
161
+ # Example: +noms cmdb show webserver1.ops-lab.net --format=json+
162
+ #
163
+ # * --noheader
164
+ #
165
+ # Don't print column headings (in text or CSV output).
166
+ #
167
+ # * --nofeedback
168
+ #
169
+ # Don't print object counts.
170
+ #
171
+ # = AUTHOR
172
+ #
173
+ # Jeremy Brinkley, <jbrinkley@evernote.com>
174
+ #
175
+
176
+ require 'rubygems'
177
+ require 'optconfig'
178
+ require 'etc'
179
+ require 'csv'
180
+ require 'noms/client/version'
181
+ require 'ncc/client'
182
+ require 'noms/cmdb'
183
+
184
+ $VERSION = NOMS::Client::VERSION
185
+ $me = 'noms'
186
+
187
+ $opt = Optconfig.new('noms', {
188
+ 'ncc=s%' => {
189
+ 'url' => 'http://noms/ncc_api/v2' },
190
+ 'cmdb=s%' => {
191
+ 'url' => 'http://cmdb/cmdb_api/v1' },
192
+ 'fields=s' => '',
193
+ 'mock=s' => nil,
194
+ 'format=s' => 'text',
195
+ 'names!' => false,
196
+ 'label!' => true,
197
+ 'header!' => true,
198
+ 'feedback!' => true,
199
+ 'waitfor-interval=i' => 5,
200
+ 'timeout=i' => 600,
201
+ 'default-environment' => 'production',
202
+ 'default-cloud' => 'ec2',
203
+ 'format-field=s%' => {
204
+ 'svcinst' => {
205
+ 'fields' => [
206
+ 'name', 'type', 'note'
207
+ ],
208
+ 'length' => {
209
+ 'name' => 20,
210
+ 'type' => 20,
211
+ 'note' => 30
212
+ }
213
+ },
214
+ 'environment' => {
215
+ 'fields' => [
216
+ 'name', 'environment_name', 'note',
217
+ ],
218
+ 'length' => {
219
+ 'name' => 20,
220
+ 'environment_name' => 20,
221
+ 'note' => 30
222
+ }
223
+ },
224
+ 'instance' => {
225
+ 'fields' => [
226
+ 'name', 'status', 'size', 'image',
227
+ 'id'],
228
+ 'length' => {
229
+ 'name' => 36,
230
+ 'status' => 10,
231
+ 'size' => 10,
232
+ 'id' => 36,
233
+ 'image' => 15
234
+ }
235
+ },
236
+ 'system' => {
237
+ 'fields' => [
238
+ 'fqdn', 'environment_name', 'status', 'roles', 'ipaddress',
239
+ 'data_center_code'],
240
+ 'length' => {
241
+ 'fqdn' => 36,
242
+ 'environment_name' => 16,
243
+ 'status' => 15,
244
+ 'ipaddress' => 15,
245
+ 'data_center_code' => 11,
246
+ 'svc_id' => 10,
247
+ 'cloud' => 10,
248
+ 'serial_number' => 36,
249
+ 'tags' => 20
250
+ }
251
+ }
252
+ }
253
+ })
254
+
255
+ if $opt['mock']
256
+ NCC::Client.mock! $opt['mock']
257
+ NOMS::CMDB.mock! $opt['mock']
258
+ end
259
+
260
+ case $opt['format']
261
+ when 'csv'
262
+ $opt['feedback'] = false
263
+ when 'json'
264
+ $opt['feedback'] = false
265
+ $opt['header'] = false
266
+ when 'text'
267
+ else
268
+ fatal "Don't know how to produce #{$opt['format']} output"
269
+ end
270
+
271
+ if $opt['names']
272
+ $opt['header'] = false
273
+ $opt['feedback'] = false
274
+ end
275
+
276
+ $opt['cmdb']['timeout'] ||= $opt['timeout']
277
+ $opt['ncc']['timeout'] ||= $opt['timeout']
278
+
279
+ $cmdb = NOMS::CMDB.new($opt)
280
+ $ncc = NCC::Client.new($opt)
281
+
282
+ def parse_format(fields, type)
283
+ dbg "Specified fields: #{fields.inspect}"
284
+ if fields.nil? or fields.empty?
285
+ dbg " no specification"
286
+ elsif fields.respond_to? :to_ary
287
+ dbg " fields are already array"
288
+ else
289
+ dbg " splitting on ','"
290
+ fields = fields.split(',')
291
+ end
292
+ if ! fields.empty?
293
+ fields.map do |fieldname|
294
+ if m = /([^=]+)=(\d+)/.match(fieldname)
295
+ fieldname = m[1]
296
+ $opt['format-field'][type]['length'][fieldname] = m[2].to_i
297
+ end
298
+ fieldname
299
+ end
300
+ else
301
+ nil
302
+ end
303
+ end
304
+
305
+ def fmt_string(type='instance', fields=nil)
306
+ fields = fmt_fields(type, fields)
307
+ fields.map do |field|
308
+ len = $opt['format-field'][type]['length'][field]
309
+ len = field.length unless (len and len > 0)
310
+ "%-#{len}s"
311
+ end.join(" ")
312
+ end
313
+
314
+ def fmt_fields(type='instance', fields=nil)
315
+ if fields.nil?
316
+ fields = parse_format($opt['fields'], type) || $opt['format-field'][type]['fields']
317
+ end
318
+ fields
319
+ end
320
+
321
+ def fmt_line(obj, type='instance', fields=nil)
322
+ fields = fmt_fields(type, fields)
323
+ case $opt['format']
324
+ when 'csv'
325
+ CSV.generate_line(fields.map { |f| obj[f] }).chomp
326
+ else
327
+ fmt_string(type, fields) % fields.map { |f| obj[f] }
328
+ end
329
+ end
330
+
331
+ def fmt_header(type='instance', fields=nil)
332
+ fields = fmt_fields(type, fields)
333
+ case $opt['format']
334
+ when 'csv'
335
+ CSV.generate_line(fields).chomp
336
+ else
337
+ fmt_string(type, fields) % fields
338
+ end
339
+ end
340
+
341
+ def formatted_output(objects, type='instance', fields=nil)
342
+ puts fmt_header(type, fields) if $opt['header']
343
+ results =
344
+ case $opt['format']
345
+ when 'json'
346
+ fields = fmt_fields(type, fields)
347
+ fieldlist = parse_format($opt['fields'], type) || fields
348
+ JSON.pretty_generate(objects.map do |o|
349
+ Hash[fieldlist.map { |f| [f, o[f]] }]
350
+ end)
351
+ else
352
+ objects.map do |obj|
353
+ fmt_line(obj, type, fields)
354
+ end.join("\n")
355
+ end
356
+ results += "\n#{objects.length} objects" if $opt['feedback']
357
+ results
358
+ end
359
+
360
+ def record_formatted_output(object, type='instance', fields=nil)
361
+ fields = fmt_fields(type, fields)
362
+ all_fields = object.keys
363
+ fields = fields & all_fields
364
+ fieldlist = parse_format($opt['fields'], type) || fields + (all_fields - fields)
365
+ case $opt['format']
366
+ when 'json'
367
+ JSON.pretty_generate(Hash[fieldlist.map { |f| [f, object[f]] }])
368
+ when 'csv'
369
+ ($opt['label'] ? CSV.generate_line(fieldlist) : '') +
370
+ CSV.generate_line(fieldlist.map { |f| object[f] })
371
+ else
372
+ fieldlist.map { |f|
373
+ $opt['label'] ? "#{f}: #{object[f]}" : object[f]
374
+ }.join("\n")
375
+ end
376
+ end
377
+
378
+ def extractkvs(args)
379
+ args.inject([{}, []]) do |a, arg|
380
+ if /^(\w+)=/.match arg
381
+ key, value = arg.split('=')
382
+ [a[0].merge({ key.to_sym => value }), a[1]]
383
+ else
384
+ [a[0], a[1] << arg]
385
+ end
386
+ end
387
+ end
388
+
389
+ def parsekvs(args)
390
+ args.inject({}) do |h, arg|
391
+ key, value = arg.split('=')
392
+ h.merge({ key.to_sym => value })
393
+ end
394
+ end
395
+
396
+ def get_username
397
+ Etc.getpwuid(Process.uid).name
398
+ end
399
+
400
+ def fatal(msg)
401
+ $stderr.puts "noms error: #{msg}"
402
+ Process.exit(1)
403
+ end
404
+
405
+ def dbg(msg)
406
+ if $opt.has_key? 'debug' and $opt['debug'] > 0
407
+ puts "DBG(noms): #{msg}"
408
+ end
409
+ end
410
+
411
+ def hash_from_array(a, field='id')
412
+ Hash[a.map { |e| [e[field], e] }]
413
+ end
414
+
415
+ def generic_describe(h)
416
+ h.map do |k, v|
417
+ "#{k}: #{v}"
418
+ end.join("\n")
419
+ end
420
+
421
+ def desc(args)
422
+ what = args.shift
423
+ case what
424
+ when 'commands', nil
425
+ generic_describe({
426
+ 'describe' => 'describe things',
427
+ 'commands' => 'list commands',
428
+ 'cmdb' => 'interact with NOMS CMDB (Inventory)',
429
+ 'instance' => 'interact with clouds (NCC-API)',
430
+ 'waitfor' => 'wait for CMDB query to be satisfied',
431
+ 'environment' => 'interact with NOMS CMDB environments',
432
+ 'svcinst' => 'manage CMDB service instances'
433
+ })
434
+ when 'clouds'
435
+ generic_describe Hash[$ncc.clouds.map { |e| [e['name'],
436
+ e['provider']] }]
437
+ when 'cloud'
438
+ generic_describe $ncc.clouds args.first
439
+ when 'ncc'
440
+ generic_describe $ncc.info
441
+ when 'systems', 'cmdb'
442
+ help = $cmdb.help 'system'
443
+ puts help.inspect
444
+ generic_describe(help.select { |f|
445
+ f.respond_to? :has_key? and f.has_key? 'label' }.map { |f| f['label'] })
446
+ when 'describe', 'desc'
447
+ generic_describe ({
448
+ 'describe' => 'describe resources',
449
+ 'clouds' => 'describe available clouds',
450
+ 'cloud' => 'describe cloud',
451
+ 'ncc' => 'describe NCC API',
452
+ 'systems|cmdb' => 'describe CMDB entries (system)'
453
+ })
454
+ else
455
+ $stderr.puts "Can't describe '#{what}'"
456
+ end
457
+ end
458
+
459
+ def cmdb_query(args)
460
+ $cmdb.query('system', args)
461
+ end
462
+
463
+ def cmdb_show(args)
464
+ fqdn = args.shift
465
+ system = $cmdb.query('system', 'fqdn=' + fqdn).first
466
+ record = if ! args.empty?
467
+ system.keys.inject({}) do |h, k|
468
+ if args.include? k
469
+ h.merge({k => system[k]})
470
+ else
471
+ h
472
+ end
473
+ end
474
+ else
475
+ system
476
+ end
477
+ record
478
+ end
479
+
480
+ def cmdb(args)
481
+ $opt['fields'] = ['fqdn'] if $opt['names']
482
+ cmd = args.shift
483
+ opt, argv = extractkvs args
484
+ case cmd
485
+ when 'query'
486
+ # Note, args, not argv
487
+ results = cmdb_query args
488
+ formatted_output(results, 'system')
489
+ when 'show','info'
490
+ record = cmdb_show args
491
+ record_formatted_output(record, 'system')
492
+ when 'set'
493
+ fqdn = args.shift
494
+ updated = $cmdb.update('system', opt, fqdn)
495
+ ''
496
+ else
497
+ $stderr.puts "Unknown cmdb command '#{cmd}'"
498
+ Process.exit(2)
499
+ end
500
+ end
501
+
502
+ def eval_conditions(count, results)
503
+ expr =
504
+ case count
505
+ when "0", 0
506
+ lambda { |r| r.nil? or r.length == 0 }
507
+ when /^>/
508
+ m = /^>(\d+)/.match count
509
+ ct = m[1].to_i
510
+ lambda { |r| !r.nil? and r.length > ct }
511
+ else
512
+ ct = count.to_i
513
+ lambda { |r| !r.nil? and r.length == ct }
514
+ end
515
+
516
+ expr.call results
517
+ end
518
+
519
+ # TODO: Needs to be factored into general waiting lib
520
+ def waitfor(args)
521
+ count = args.shift
522
+ commands = ['cmdb']
523
+ conditions = []
524
+
525
+ command = args.shift
526
+ subcommand = args.shift
527
+ continuing = true
528
+
529
+ startts = Time.now
530
+ while continuing
531
+ results = case command
532
+ when 'cmdb'
533
+ case subcommand
534
+ when 'query'
535
+ cmdb_query args
536
+ else
537
+ fatal "Can only wait for cmdb query or show"
538
+ end
539
+ else
540
+ fatal "Can only wait for cmdb"
541
+ end
542
+ if eval_conditions(count, results)
543
+ dbg "Conditions satisfied (#{count} records)"
544
+ Process.exit(0)
545
+ continuing = false
546
+ else
547
+ dbg "Conditions not satisfied"
548
+ if (Time.now - startts) > $opt['timeout']
549
+ $stderr.puts "Timed out waiting for #{count} records of " +
550
+ "#{command} #{subcommand} #{args}"
551
+ Process.exit(4)
552
+ end
553
+ dbg "Waiting #{$opt['waitfor-interval']}"
554
+ sleep $opt['waitfor-interval']
555
+ end
556
+ end
557
+ end
558
+
559
+ def find_key(h, k, p=nil)
560
+ puts " - looking for #{k} at path #{p.inspect}"
561
+ if h.has_key? k
562
+ return (p || []) + [k]
563
+ else
564
+ h.keys.each do |nk|
565
+ find_key(h[nk], k, (p || []) + [nk])
566
+ end
567
+ end
568
+ return nil
569
+ end
570
+
571
+ def make_tree(envs, tree={})
572
+ puts "make_tree(#{envs.inspect}, #{tree.inspect})"
573
+ return tree if envs.length == 0
574
+ # Kind of ugly using select for side effects
575
+ pruned_envs = envs.reject do |env|
576
+ puts " checking #{env['name']} < #{env['environment_name']}"
577
+ if env['environment_name'].nil? or env['name'] == env['environment_name']
578
+ puts " root environment, setting"
579
+ tree[env['name']] ||= { }
580
+ true
581
+ elsif path = find_key(tree, env['environment_name'])
582
+ puts " found parent #{env['environment_name']} at #{path.inspect}"
583
+ path_set(tree, path, { env['name'] => { } })
584
+ true
585
+ else
586
+ puts " couldn't find #{env['environment_name']}, save for later"
587
+ false
588
+ end
589
+ end
590
+ make_tree(pruned_envs, tree)
591
+ end
592
+
593
+ def path_set(hash, keypath, value)
594
+ if keypath.length == 1
595
+ hash[keypath[0]] = value
596
+ else
597
+ next_key = keypath.shift
598
+ path_set(hash[next_key], keypath, value)
599
+ end
600
+ end
601
+
602
+ def env(args)
603
+ $opt['fields'] = ['name'] if $opt['names']
604
+ command = args.shift
605
+ case command
606
+ when 'list'
607
+ formatted_output $cmdb.environments, 'environment'
608
+ when 'show', 'info'
609
+ env_name = args.shift
610
+ record_formatted_output($cmdb.environment(env_name), 'environment')
611
+ when 'tree'
612
+ make_tree($cmdb.environments).to_yaml
613
+ when 'add'
614
+ name = args.shift
615
+ attrs = { :environment_name => 'production' }
616
+ attrs.update(args.empty? ? { } : parsekvs(args))
617
+ record_formatted_output($cmdb.create_environment(name, attrs))
618
+ when 'remove'
619
+ name = args.shift
620
+ result = $cmdb.delete_environment name
621
+ # cmdb-api returns '1'. ugh.
622
+ nil unless result != 1
623
+ else
624
+ $stderr.puts "Unknown env command '#{command}'"
625
+ Process.exit(2)
626
+ end
627
+ end
628
+
629
+ def parse_qualified_name(s, default_qualifier)
630
+ qualifier, name = s.split('/', 2)
631
+ if name.nil?
632
+ name = qualifier
633
+ qualifier = default_qualifier
634
+ end
635
+ return qualifier, name
636
+ end
637
+
638
+ def parse_svcinst(s)
639
+ parse_qualified_name(s, $opt['default-environment'])
640
+ end
641
+
642
+ def svcinst(args)
643
+ $opt['fields'] = ['name'] if $opt['names']
644
+ command = args.shift
645
+ case command
646
+ when 'list'
647
+ formatted_output($cmdb.services(args[0] || $opt['default-environment']), 'svcinst')
648
+ when 'show', 'info'
649
+ env, svc = parse_svcinst(args[0])
650
+ record_formatted_output($cmdb.service(env, svc), 'svcinst')
651
+ when 'add'
652
+ env, svc = parse_svcinst(args.shift)
653
+ attrs = parsekvs(args)
654
+ $cmdb.create_service(env, svc, attrs)
655
+ nil
656
+ when 'remove'
657
+ env, svc = parse_svcinst(args.shift)
658
+ $cmdb.delete_service(env, svc)
659
+ when 'set'
660
+ env, svc = parse_svcinst(args.shift)
661
+ attrs = parsekvs(args)
662
+ $cmdb.update_service(env, svc, attrs)
663
+ nil
664
+ else
665
+ $stderr.puts "Unknown svcinst '#{command}'"
666
+ end
667
+ end
668
+
669
+ def outs(output)
670
+ unless [nil, true, false].include? output
671
+ puts output.to_s if (output.to_s and output.to_s.length != 0)
672
+ end
673
+ end
674
+
675
+ def dispatch_command(argv)
676
+ command = argv.shift
677
+ case command
678
+ when 'help'
679
+ $stderr.puts "Use noms --help"
680
+ Process.exit(1)
681
+ when 'describe', 'desc'
682
+ outs desc argv
683
+ when 'cmdb', 'inv'
684
+ outs cmdb argv
685
+ when 'svc', 'svcinst'
686
+ outs svcinst argv
687
+ when 'environment', 'env'
688
+ outs env argv
689
+ when 'instance'
690
+ outs instance argv
691
+ when 'waitfor'
692
+ waitfor argv
693
+ when nil
694
+ $stderr.puts "Usage:\n noms --help\n noms {desc|cmdb|instance|waitfor|...} [arg]"
695
+ Process.exit(1)
696
+ else
697
+ $stderr.puts "Unknown command '#{command}'"
698
+ Process.exit(1)
699
+ end
700
+ end
701
+
702
+ def parse_instance(s)
703
+ parse_qualified_name(s, $opt['default-cloud'])
704
+ end
705
+
706
+ def is_name(n)
707
+ true if /\./.match n
708
+ end
709
+
710
+ def instance(args)
711
+ $opt['fields'] = ['name'] if $opt['names']
712
+ cmd = args.shift
713
+ case cmd
714
+ when 'list', 'ls'
715
+ if args.length < 1
716
+ args.unshift $opt['default-cloud']
717
+ end
718
+ results = $ncc.list *args
719
+ formatted_output(results, 'instance')
720
+ when 'info', 'show'
721
+ if args.length == 2
722
+ cloud, id = *args
723
+ elsif args.length == 1
724
+ cloud, id = parse_instance(args.first)
725
+ else
726
+ fatal "Usage: noms instance show [cloud/]instance"
727
+ end
728
+ if is_name(id)
729
+ instance = $ncc.find_by_name cloud, id
730
+ else
731
+ instance = $ncc.instance cloud, id
732
+ end
733
+ if $opt['fields'].include? 'console'
734
+ console = $ncc.console cloud, instance['id']
735
+ instance['console'] = console['url'] if (console and console.has_key? 'url')
736
+ end
737
+ record_formatted_output instance
738
+ when 'add'
739
+ instanceopt, argv = extractkvs args
740
+ if argv.length == 0
741
+ cloud = $opt['default-cloud']
742
+ elsif argv.length == 1
743
+ cloud = argv.first
744
+ else
745
+ fatal "Usage: noms instance add [cloud] [param=value [...]]"
746
+ end
747
+ attrs = { :username => get_username, :size => 'm1.small' }
748
+ attrs.update(instanceopt.empty? ? { } : instanceopt)
749
+ dbg attrs.inspect
750
+ inst = $ncc.create(cloud, attrs)
751
+ record_formatted_output(inst) unless inst.nil?
752
+ when 'remove', 'rm'
753
+ if args.length == 2
754
+ cloud, id = *args
755
+ elsif args.length == 1
756
+ cloud, id = parse_instance(args.first)
757
+ else
758
+ fatal "Usage: noms instance remove [cloud.]instance"
759
+ end
760
+ fatal("No id to remove") if id.nil?
761
+ if is_name(id)
762
+ attrs = { :name => id }
763
+ else
764
+ attrs = { :id => id }
765
+ end
766
+ $ncc.delete(cloud, attrs)
767
+ else
768
+ $stderr.puts "Unknown instance command #{cmd}"
769
+ Process.exit(3)
770
+ end
771
+ end
772
+
773
+ dispatch_command ARGV
774
+