morpheus-cli 5.3.3 → 5.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +12 -0
  4. data/lib/morpheus/api/clouds_interface.rb +4 -11
  5. data/lib/morpheus/api/load_balancer_pools_interface.rb +4 -4
  6. data/lib/morpheus/api/load_balancer_profiles_interface.rb +10 -0
  7. data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +4 -4
  8. data/lib/morpheus/api/network_routers_interface.rb +21 -0
  9. data/lib/morpheus/api/network_servers_interface.rb +42 -0
  10. data/lib/morpheus/api/rest_interface.rb +2 -1
  11. data/lib/morpheus/api/virtual_servers_interface.rb +9 -0
  12. data/lib/morpheus/cli/cli_command.rb +2 -1
  13. data/lib/morpheus/cli/cloud_resource_pools_command.rb +1 -1
  14. data/lib/morpheus/cli/clouds.rb +22 -40
  15. data/lib/morpheus/cli/hosts.rb +0 -1
  16. data/lib/morpheus/cli/instances.rb +111 -7
  17. data/lib/morpheus/cli/invoices_command.rb +42 -38
  18. data/lib/morpheus/cli/library_option_lists_command.rb +3 -3
  19. data/lib/morpheus/cli/load_balancer_pools.rb +111 -0
  20. data/lib/morpheus/cli/load_balancer_virtual_servers.rb +136 -0
  21. data/lib/morpheus/cli/load_balancers.rb +0 -155
  22. data/lib/morpheus/cli/mixins/load_balancers_helper.rb +2 -2
  23. data/lib/morpheus/cli/mixins/provisioning_helper.rb +32 -11
  24. data/lib/morpheus/cli/mixins/rest_command.rb +53 -37
  25. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +488 -0
  26. data/lib/morpheus/cli/network_routers_command.rb +291 -7
  27. data/lib/morpheus/cli/network_scopes_command.rb +442 -0
  28. data/lib/morpheus/cli/networks_command.rb +2 -2
  29. data/lib/morpheus/cli/option_types.rb +20 -0
  30. data/lib/morpheus/cli/subnets_command.rb +7 -2
  31. data/lib/morpheus/cli/tasks.rb +25 -2
  32. data/lib/morpheus/cli/version.rb +1 -1
  33. data/lib/morpheus/cli/virtual_images.rb +2 -0
  34. data/lib/morpheus/cli.rb +9 -1
  35. metadata +9 -2
@@ -0,0 +1,488 @@
1
+ # SecondaryRestCommand is a mixin for Morpheus::Cli command classes.
2
+ # for resources that are secondary to some parent resource.
3
+ # Provides basic CRUD commands: list, get, add, update, remove
4
+ # The parent resource is specified as the first argument for all the comments.
5
+ #
6
+ # Example of a SecondaryRestCommand for `morpheus load-balancer-virtual-servers`.
7
+ #
8
+ # class Morpheus::Cli::LoadBalancerVirtualServers
9
+ #
10
+ # include Morpheus::Cli::CliCommand
11
+ # include Morpheus::Cli::RestCommand
12
+ # include Morpheus::Cli::SecondaryRestCommand
13
+ # include Morpheus::Cli::LoadBalancersHelper
14
+ #
15
+ # set_command_name :'load-balancer-virtual-servers'
16
+ # register_subcommands :list, :get, :add, :update, :remove
17
+ #
18
+ # register_interfaces :load_balancer_virtual_servers,
19
+ # :load_balancers, :load_balancer_types
20
+ #
21
+ # set_rest_parent_name :load_balancers
22
+ #
23
+ # end
24
+ #
25
+ module Morpheus::Cli::SecondaryRestCommand
26
+ def self.included(base)
27
+ base.extend ClassMethods
28
+ end
29
+
30
+ module ClassMethods
31
+
32
+ ## duplicated the rest_* settings with rest_parent_*, for defining the parent resource
33
+
34
+ # rest_parent_name is the rest_name for the parent
35
+ def rest_parent_name
36
+ @rest_parent_name || default_rest_parent_name
37
+ end
38
+
39
+ def default_rest_parent_name
40
+ words = rest_name.split("_")
41
+ if words.size > 1
42
+ words.pop
43
+ return words.join("_") + "s"
44
+ else
45
+ # this wont happen, default wont make sense in this scenario
46
+ # "parent_" + rest_name
47
+ raise "Unable to determine default_rest_parent_name for rest_name: #{rest_name}, class: #{self}"
48
+ end
49
+ end
50
+
51
+ def rest_parent_name=(v)
52
+ @rest_parent_name = v.to_s
53
+ end
54
+
55
+ alias :set_rest_parent_name :rest_parent_name=
56
+ alias :set_rest_parent :rest_parent_name=
57
+ #alias :rest_parent= :rest_parent_name=
58
+
59
+ # rest_parent_key is the singular name of the resource eg. "neat_thing"
60
+ def rest_parent_key
61
+ @rest_parent_key || default_rest_parent_key
62
+ end
63
+
64
+ def default_rest_parent_key
65
+ rest_parent_name.chomp("s")
66
+ end
67
+
68
+ def rest_parent_key=(v)
69
+ @rest_parent_key = v.to_s
70
+ end
71
+
72
+ alias :set_rest_parent_key :rest_parent_key=
73
+
74
+ def rest_parent_arg
75
+ @rest_parent_arg || default_rest_parent_arg
76
+ end
77
+
78
+ def default_rest_parent_arg
79
+ rest_parent_key.to_s.gsub("_", " ")
80
+ end
81
+
82
+ def rest_parent_arg=(v)
83
+ @rest_parent_arg = v.to_s
84
+ end
85
+
86
+ alias :set_rest_parent_arg :rest_parent_arg=
87
+
88
+ # rest_parent_label is the capitalized resource label eg. "Neat Thing"
89
+ def rest_parent_label
90
+ @rest_parent_label || default_rest_parent_label
91
+ end
92
+
93
+ def default_rest_parent_label
94
+ rest_parent_key.to_s.split("_").collect {|it| it.to_s.capitalize }.join(" ")
95
+ end
96
+
97
+ def rest_parent_label=(v)
98
+ @rest_parent_label = v.to_s
99
+ end
100
+
101
+ alias :set_rest_parent_label :rest_parent_label=
102
+
103
+ # the plural version of the label eg. "Neat Things"
104
+ def rest_parent_label_plural
105
+ @rest_parent_label_plural || default_rest_parent_label_plural
106
+ end
107
+
108
+ def default_rest_parent_label_plural
109
+ #rest_parent_name.to_s.split("_").collect {|it| it.to_s.capitalize }.join(" ")
110
+ rest_parent_label.to_s.pluralize
111
+ end
112
+
113
+ def rest_parent_label_plural=(v)
114
+ @rest_parent_label_plural = v.to_s
115
+ end
116
+
117
+ alias :set_rest_parent_label_plural :rest_parent_label_plural=
118
+
119
+ # the name of the default interface, matches the rest name eg. "neat_things"
120
+ def rest_parent_interface_name
121
+ @rest_parent_interface_name || default_rest_parent_interface_name
122
+ end
123
+
124
+ def default_rest_parent_interface_name
125
+ rest_parent_name
126
+ end
127
+
128
+ def rest_parent_interface_name=(v)
129
+ @rest_parent_interface_name = v.to_s
130
+ end
131
+
132
+ alias :set_rest_parent_interface_name :rest_parent_interface_name=
133
+
134
+ end
135
+
136
+ ## duplicated the rest_* settings with rest_parent, for the parents resource
137
+
138
+ def rest_parent_name
139
+ self.class.rest_parent_name
140
+ end
141
+
142
+ def rest_parent_key
143
+ self.class.rest_parent_key
144
+ end
145
+
146
+ def rest_parent_arg
147
+ self.class.rest_parent_arg
148
+ end
149
+
150
+ def rest_parent_label
151
+ self.class.rest_parent_label
152
+ end
153
+
154
+ def rest_parent_label_plural
155
+ self.class.rest_parent_label_plural
156
+ end
157
+
158
+ def rest_parent_interface_name
159
+ self.class.rest_parent_interface_name # || "@#{rest_parent_name}_interface"
160
+ end
161
+
162
+ def rest_parent_interface
163
+ instance_variable_get("@#{rest_parent_interface_name}_interface")
164
+ end
165
+
166
+ def rest_parent_object_key
167
+ self.send("#{rest_parent_key}_object_key")
168
+ end
169
+
170
+ def rest_parent_list_key
171
+ self.send("#{rest_parent_key}_list_key")
172
+ end
173
+
174
+ def rest_parent_column_definitions
175
+ self.send("#{rest_parent_key}_column_definitions")
176
+ end
177
+
178
+ def rest_parent_list_column_definitions
179
+ self.send("#{rest_parent_key}_list_column_definitions")
180
+ end
181
+
182
+ def rest_parent_find_by_name_or_id(name)
183
+ return self.send("find_#{rest_parent_key}_by_name_or_id", name)
184
+ end
185
+
186
+ def registered_interfaces
187
+ self.class.registered_interfaces
188
+ end
189
+
190
+ def list(args)
191
+ parent_id, parent_record = nil, nil
192
+ params = {}
193
+ options = {}
194
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
195
+ opts.banner = subcommand_usage("[#{rest_parent_arg}] [search]")
196
+ build_standard_list_options(opts, options)
197
+ opts.footer = <<-EOT
198
+ List #{rest_label_plural.downcase}.
199
+ [#{rest_parent_arg}] is required. This is the name or id of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
200
+ [search] is optional. This is a search phrase to filter the results.
201
+ EOT
202
+ end
203
+ optparse.parse!(args)
204
+ parent_id = args[0]
205
+ if args[1] # && rest_has_name
206
+ record_name = args[1]
207
+ end
208
+ verify_args!(args:args, optparse:optparse, min:1)
209
+ if args.count > 1
210
+ options[:phrase] = args[1..-1].join(" ")
211
+ end
212
+ connect(options)
213
+ parent_record = rest_parent_find_by_name_or_id(parent_id)
214
+ if parent_record.nil?
215
+ raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
216
+ end
217
+ parent_id = parent_record['id']
218
+ params.merge!(parse_list_options(options))
219
+ rest_interface.setopts(options)
220
+ if options[:dry_run]
221
+ print_dry_run rest_interface.dry.list(parent_id, params)
222
+ return
223
+ end
224
+ json_response = rest_interface.list(parent_id, params)
225
+ render_response(json_response, options, rest_list_key) do
226
+ records = json_response[rest_list_key]
227
+ print_h1 "Morpheus #{rest_label_plural}"
228
+ if records.nil? || records.empty?
229
+ print cyan,"No #{rest_label_plural.downcase} found.",reset,"\n"
230
+ else
231
+ print as_pretty_table(records, rest_list_column_definitions.upcase_keys!, options)
232
+ print_results_pagination(json_response) if json_response['meta']
233
+ end
234
+ print reset,"\n"
235
+ end
236
+ return 0, nil
237
+ end
238
+
239
+ def get(args)
240
+ params = {}
241
+ options = {}
242
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
243
+ opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}]")
244
+ build_standard_get_options(opts, options)
245
+ opts.footer = <<-EOT
246
+ Get details about #{a_or_an(rest_label)} #{rest_label.downcase}.
247
+ [#{rest_parent_arg}] is required. This is the name or id of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
248
+ [#{rest_arg}] is required. This is the name or id of #{a_or_an(rest_label)} #{rest_label.downcase}.
249
+ EOT
250
+ end
251
+ optparse.parse!(args)
252
+ verify_args!(args:args, optparse:optparse, min:2)
253
+ connect(options)
254
+ parent_id = args[0]
255
+ parent_record = rest_parent_find_by_name_or_id(parent_id)
256
+ if parent_record.nil?
257
+ raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
258
+ end
259
+ parent_id = parent_record['id']
260
+ params.merge!(parse_query_options(options))
261
+ id_list = parse_id_list(args[1..-1])
262
+ return run_command_for_each_arg(id_list) do |arg|
263
+ _get(parent_id, arg, params, options)
264
+ end
265
+ end
266
+
267
+ def _get(parent_id, id, params, options)
268
+ if id !~ /\A\d{1,}\Z/
269
+ record = rest_find_by_name_or_id(id)
270
+ if record.nil?
271
+ raise_command_error "#{rest_label} not found for name '#{id}'"
272
+ end
273
+ id = record['id']
274
+ end
275
+ rest_interface.setopts(options)
276
+ if options[:dry_run]
277
+ print_dry_run rest_interface.dry.get(id, params)
278
+ return
279
+ end
280
+ json_response = rest_interface.get(id, params)
281
+ render_response_for_get(json_response, options)
282
+ return 0, nil
283
+ end
284
+
285
+ def render_response_for_get(json_response, options)
286
+ render_response(json_response, options, rest_object_key) do
287
+ record = json_response[rest_object_key]
288
+ print_h1 rest_label, [], options
289
+ print cyan
290
+ print_description_list(rest_column_definitions, record, options)
291
+ # show config settings...
292
+ if record['optionTypes'] && record['optionTypes'].size > 0
293
+ print_h2 "Option Types", options
294
+ print format_option_types_table(record['optionTypes'], options, rest_object_key)
295
+ end
296
+ print reset,"\n"
297
+ end
298
+ end
299
+
300
+ def add(args)
301
+ parent_id, parent_record = nil, nil
302
+ record_type_id = nil
303
+ options = {}
304
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
305
+ if rest_has_type
306
+ opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}] -t TYPE")
307
+ opts.on( '-t', "--#{rest_type_arg} TYPE", "#{rest_type_label}" ) do |val|
308
+ record_type_id = val
309
+ end
310
+ else
311
+ opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}]")
312
+ end
313
+ # if defined?(add_#{rest_key}_option_types)
314
+ # build_option_type_options(opts, options, add_#{rest_key}_option_types)
315
+ # end
316
+ build_standard_add_options(opts, options)
317
+ opts.footer = <<-EOT
318
+ Create a new #{rest_label.downcase}.
319
+ [#{rest_parent_arg}] is required. This is the name or id of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
320
+ [#{rest_arg}] is required. This is the name of the new #{rest_label.downcase}.
321
+ EOT
322
+ end
323
+ optparse.parse!(args)
324
+ verify_args!(args:args, optparse:optparse, min:1, max: 2)
325
+ # todo: make supporting args[0] optional and more flexible
326
+ # for now args[0] is assumed to be the 'name'
327
+ record_name = nil
328
+ parent_id = args[0]
329
+ if args[1] # && rest_has_name
330
+ record_name = args[1]
331
+ end
332
+ # todo: maybe need a flag to make this required, it could be an option type too, so
333
+ if rest_has_type
334
+ if record_type_id.nil?
335
+ raise_command_error "#{rest_type_label} is required.\n#{optparse}"
336
+ end
337
+ end
338
+ connect(options)
339
+ if rest_has_type
340
+ record_type = rest_type_find_by_name_or_id(record_type_id)
341
+ if record_type.nil?
342
+ raise_command_error "#{rest_type_label} not found for '#{record_type_id}'.\n#{optparse}"
343
+ end
344
+ end
345
+ parent_record = rest_parent_find_by_name_or_id(parent_id)
346
+ if parent_record.nil?
347
+ raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
348
+ end
349
+ parent_id = parent_record['id']
350
+ passed_options = parse_passed_options(options)
351
+ payload = {}
352
+ if options[:payload]
353
+ payload = options[:payload]
354
+ payload.deep_merge!({rest_object_key => passed_options})
355
+ else
356
+ record_payload = {}
357
+ if record_name
358
+ record_payload['name'] = record_name
359
+ options[:options]['name'] = record_name # injected for prompt
360
+ end
361
+ if rest_has_type && record_type
362
+ # record_payload['type'] = {'code' => record_type['code']}
363
+ record_payload['type'] = record_type['code']
364
+ options[:options]['type'] = record_type['code'] # injected for prompt
365
+ end
366
+ record_payload.deep_merge!(passed_options)
367
+ # options by type
368
+ my_option_types = record_type ? record_type['optionTypes'] : nil
369
+ if my_option_types && !my_option_types.empty?
370
+ # remove redundant fieldContext
371
+ my_option_types.each do |option_type|
372
+ if option_type['fieldContext'] == rest_object_key
373
+ option_type['fieldContext'] = nil
374
+ end
375
+ end
376
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, options[:params])
377
+ v_prompt.deep_compact!
378
+ v_prompt.booleanize! # 'on' => true
379
+ record_payload.deep_merge!(v_prompt)
380
+ end
381
+ payload[rest_object_key] = record_payload
382
+ end
383
+ rest_interface.setopts(options)
384
+ if options[:dry_run]
385
+ print_dry_run rest_interface.dry.create(parent_id, payload)
386
+ return
387
+ end
388
+ json_response = rest_interface.create(parent_id, payload)
389
+ render_response(json_response, options, rest_object_key) do
390
+ record = json_response[rest_object_key]
391
+ print_green_success "Added #{rest_label.downcase} #{record['name'] || record['id']}"
392
+ return _get(parent_id, record["id"], {}, options)
393
+ end
394
+ return 0, nil
395
+ end
396
+
397
+ def update(args)
398
+ parent_id = args[0]
399
+ id = args[1]
400
+ options = {}
401
+ params = {}
402
+ account_name = nil
403
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
404
+ opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}] [options]")
405
+ build_standard_update_options(opts, options)
406
+ opts.footer = <<-EOT
407
+ Update an existing #{rest_label.downcase}.
408
+ [#{rest_parent_arg}] is required. This is the name or id of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
409
+ [#{rest_arg}] is required. This is the name or id of #{a_or_an(rest_label)} #{rest_label.downcase}.
410
+ EOT
411
+ end
412
+ optparse.parse!(args)
413
+ verify_args!(args:args, optparse:optparse, count:2)
414
+ parent_record = rest_parent_find_by_name_or_id(parent_id)
415
+ if parent_record.nil?
416
+ raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
417
+ end
418
+ parent_id = parent_record['id']
419
+ connect(options)
420
+ record = rest_find_by_name_or_id(id)
421
+ passed_options = parse_passed_options(options)
422
+ payload = nil
423
+ if options[:payload]
424
+ payload = options[:payload]
425
+ payload.deep_merge!({rest_object_key => passed_options}) unless passed_options.empty?
426
+ else
427
+ record_payload = passed_options
428
+ if record_payload.empty?
429
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
430
+ end
431
+ payload[rest_object_key] = record_payload
432
+ end
433
+ rest_interface.setopts(options)
434
+ if options[:dry_run]
435
+ print_dry_run rest_interface.dry.update(parent_id, record['id'], payload)
436
+ return
437
+ end
438
+ json_response = rest_interface.update(parent_id, record['id'], payload)
439
+ render_response(json_response, options, rest_object_key) do
440
+ print_green_success "Updated #{rest_label.downcase} #{record['name'] || record['id']}"
441
+ _get(parent_id, record["id"], {}, options)
442
+ end
443
+ return 0, nil
444
+ end
445
+
446
+ def remove(args)
447
+ parent_id = args[0]
448
+ id = args[1]
449
+ params = {}
450
+ options = {}
451
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
452
+ opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}]")
453
+ build_standard_remove_options(opts, options)
454
+ opts.footer = <<-EOT
455
+ Delete an existing #{rest_label.downcase}.
456
+ [#{rest_parent_arg}] is required. This is the name or id of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
457
+ [#{rest_arg}] is required. This is the name or id of #{a_or_an(rest_label)} #{rest_label.downcase}.
458
+ EOT
459
+ end
460
+ optparse.parse!(args)
461
+ verify_args!(args:args, optparse:optparse, count:2)
462
+ connect(options)
463
+ parent_record = rest_parent_find_by_name_or_id(parent_id)
464
+ if parent_record.nil?
465
+ raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
466
+ end
467
+ record = rest_find_by_name_or_id(id)
468
+ if record.nil?
469
+ return 1, "#{rest_name} not found for '#{id}'"
470
+ end
471
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the #{rest_label.downcase} #{record['name'] || record['id']}?")
472
+ return 9, "aborted"
473
+ end
474
+ params.merge!(parse_query_options(options))
475
+ rest_interface.setopts(options)
476
+ if options[:dry_run]
477
+ print_dry_run rest_interface.dry.destroy(parent_id, record['id'])
478
+ return 0, nil
479
+ end
480
+ json_response = rest_interface.destroy(parent_id, record['id'], params)
481
+ render_response(json_response, options) do
482
+ print_green_success "Removed #{rest_label.downcase} #{record['name'] || record['id']}"
483
+ end
484
+ return 0, nil
485
+ end
486
+
487
+ end
488
+