morpheus-cli 5.4.0 → 5.4.1

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/account_users_interface.rb +68 -0
  4. data/lib/morpheus/api/api_client.rb +51 -9
  5. data/lib/morpheus/api/audit_interface.rb +9 -0
  6. data/lib/morpheus/api/instances_interface.rb +21 -0
  7. data/lib/morpheus/api/load_balancer_monitors_interface.rb +9 -0
  8. data/lib/morpheus/api/load_balancer_pools_interface.rb +4 -4
  9. data/lib/morpheus/api/load_balancer_profiles_interface.rb +4 -5
  10. data/lib/morpheus/api/load_balancer_virtual_servers_interface.rb +13 -4
  11. data/lib/morpheus/api/load_balancers_interface.rb +5 -0
  12. data/lib/morpheus/api/network_routers_interface.rb +9 -0
  13. data/lib/morpheus/api/network_static_routes_interface.rb +36 -0
  14. data/lib/morpheus/api/read_interface.rb +4 -3
  15. data/lib/morpheus/api/rest_interface.rb +3 -3
  16. data/lib/morpheus/api/secondary_read_interface.rb +1 -1
  17. data/lib/morpheus/api/secondary_rest_interface.rb +19 -19
  18. data/lib/morpheus/api/storage_server_types_interface.rb +14 -0
  19. data/lib/morpheus/api/storage_servers_interface.rb +9 -0
  20. data/lib/morpheus/api/storage_volume_types_interface.rb +9 -0
  21. data/lib/morpheus/api/storage_volumes_interface.rb +9 -0
  22. data/lib/morpheus/api/users_interface.rb +16 -63
  23. data/lib/morpheus/cli/cli_command.rb +253 -5
  24. data/lib/morpheus/cli/cli_registry.rb +1 -1
  25. data/lib/morpheus/cli/commands/alias_command.rb +1 -1
  26. data/lib/morpheus/cli/commands/apps.rb +14 -78
  27. data/lib/morpheus/cli/commands/audit.rb +188 -0
  28. data/lib/morpheus/cli/commands/blueprints_command.rb +1 -1
  29. data/lib/morpheus/cli/commands/change_password_command.rb +4 -4
  30. data/lib/morpheus/cli/commands/clusters.rb +37 -12
  31. data/lib/morpheus/cli/commands/hosts.rb +15 -15
  32. data/lib/morpheus/cli/commands/instances.rb +109 -2
  33. data/lib/morpheus/cli/commands/load_balancer_monitors.rb +71 -0
  34. data/lib/morpheus/cli/commands/load_balancer_pools.rb +30 -50
  35. data/lib/morpheus/cli/commands/load_balancer_profiles.rb +65 -0
  36. data/lib/morpheus/cli/commands/load_balancer_types.rb +9 -4
  37. data/lib/morpheus/cli/commands/load_balancer_virtual_servers.rb +77 -57
  38. data/lib/morpheus/cli/commands/load_balancers.rb +93 -6
  39. data/lib/morpheus/cli/commands/network_firewalls_command.rb +22 -5
  40. data/lib/morpheus/cli/commands/network_routers_command.rb +96 -45
  41. data/lib/morpheus/cli/commands/network_static_routes_command.rb +446 -0
  42. data/lib/morpheus/cli/commands/network_transport_zones_command.rb +4 -4
  43. data/lib/morpheus/cli/commands/open_command.rb +30 -0
  44. data/lib/morpheus/cli/commands/options.rb +98 -0
  45. data/lib/morpheus/cli/commands/policies_command.rb +1 -1
  46. data/lib/morpheus/cli/commands/prices_command.rb +7 -7
  47. data/lib/morpheus/cli/commands/remote.rb +4 -2
  48. data/lib/morpheus/cli/commands/roles.rb +1 -1
  49. data/lib/morpheus/cli/commands/shell.rb +2 -2
  50. data/lib/morpheus/cli/commands/storage_server_types.rb +50 -0
  51. data/lib/morpheus/cli/commands/storage_servers.rb +122 -0
  52. data/lib/morpheus/cli/commands/storage_volume_types.rb +50 -0
  53. data/lib/morpheus/cli/commands/storage_volumes.rb +103 -0
  54. data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
  55. data/lib/morpheus/cli/commands/user_groups_command.rb +1 -1
  56. data/lib/morpheus/cli/commands/user_settings_command.rb +2 -1
  57. data/lib/morpheus/cli/commands/user_sources_command.rb +1 -1
  58. data/lib/morpheus/cli/commands/users.rb +28 -28
  59. data/lib/morpheus/cli/commands/view.rb +102 -0
  60. data/lib/morpheus/cli/mixins/accounts_helper.rb +5 -5
  61. data/lib/morpheus/cli/mixins/load_balancers_helper.rb +24 -4
  62. data/lib/morpheus/cli/mixins/print_helper.rb +50 -18
  63. data/lib/morpheus/cli/mixins/processes_helper.rb +1 -2
  64. data/lib/morpheus/cli/mixins/provisioning_helper.rb +15 -5
  65. data/lib/morpheus/cli/mixins/rest_command.rb +145 -73
  66. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +174 -81
  67. data/lib/morpheus/cli/mixins/storage_servers_helper.rb +156 -0
  68. data/lib/morpheus/cli/mixins/storage_volumes_helper.rb +119 -0
  69. data/lib/morpheus/cli/option_types.rb +45 -24
  70. data/lib/morpheus/cli/version.rb +1 -1
  71. data/lib/morpheus/cli.rb +1 -0
  72. data/lib/morpheus/ext/string.rb +29 -6
  73. data/lib/morpheus/routes.rb +238 -0
  74. data/lib/morpheus/util.rb +6 -1
  75. metadata +29 -8
@@ -85,6 +85,37 @@ module Morpheus::Cli::SecondaryRestCommand
85
85
 
86
86
  alias :set_rest_parent_arg :rest_parent_arg=
87
87
 
88
+ def rest_parent_param
89
+ @rest_parent_param || default_rest_parent_param
90
+ end
91
+
92
+ def default_rest_parent_param
93
+ param = rest_parent_key.to_s.split('_').collect(&:capitalize).join
94
+ "#{param[0].downcase}#{param[1..-1]}Id"
95
+ end
96
+
97
+ def rest_parent_param=(v)
98
+ @rest_parent_param = v.to_s
99
+ end
100
+
101
+ alias :set_rest_parent_param :rest_parent_param=
102
+
103
+ # rest_parent_has_name indicates a resource has a name and can be retrieved by name or id
104
+ # true by default, set to false for lookups by only id
105
+ def rest_parent_has_name
106
+ @rest_parent_has_name != nil ? @rest_parent_has_name : default_rest_parent_has_name
107
+ end
108
+
109
+ def default_rest_parent_has_name
110
+ true
111
+ end
112
+
113
+ def rest_parent_has_name=(v)
114
+ @rest_parent_has_name = !!v
115
+ end
116
+
117
+ alias :set_rest_parent_has_name :rest_parent_has_name=
118
+
88
119
  # rest_parent_label is the capitalized resource label eg. "Neat Thing"
89
120
  def rest_parent_label
90
121
  @rest_parent_label || default_rest_parent_label
@@ -147,6 +178,14 @@ module Morpheus::Cli::SecondaryRestCommand
147
178
  self.class.rest_parent_arg
148
179
  end
149
180
 
181
+ def rest_parent_param
182
+ self.class.rest_parent_param
183
+ end
184
+
185
+ def rest_parent_has_name
186
+ self.class.rest_parent_has_name
187
+ end
188
+
150
189
  def rest_parent_label
151
190
  self.class.rest_parent_label
152
191
  end
@@ -164,23 +203,56 @@ module Morpheus::Cli::SecondaryRestCommand
164
203
  end
165
204
 
166
205
  def rest_parent_object_key
167
- self.send("#{rest_parent_key}_object_key")
206
+ send("#{rest_parent_key}_object_key")
168
207
  end
169
208
 
170
209
  def rest_parent_list_key
171
- self.send("#{rest_parent_key}_list_key")
210
+ send("#{rest_parent_key}_list_key")
172
211
  end
173
212
 
174
- def rest_parent_column_definitions
175
- self.send("#{rest_parent_key}_column_definitions")
213
+ def rest_parent_column_definitions(options)
214
+ send("#{rest_parent_key}_column_definitions", options)
176
215
  end
177
216
 
178
- def rest_parent_list_column_definitions
179
- self.send("#{rest_parent_key}_list_column_definitions")
217
+ def rest_parent_list_column_definitions(options)
218
+ send("#{rest_parent_key}_list_column_definitions", options)
219
+ end
220
+
221
+ def rest_parent_find_by_name_or_id(val)
222
+ # use explicitly defined finders
223
+ # else default to new generic CliCommand find_by methods
224
+ if rest_parent_has_name
225
+ if respond_to?("find_#{rest_parent_key}_by_name_or_id", true)
226
+ send("find_#{rest_parent_key}_by_name_or_id", val)
227
+ else
228
+ find_by_name_or_id(rest_parent_key, val)
229
+ end
230
+ else
231
+ if respond_to?("find_#{rest_parent_key}_by_id", true)
232
+ send("find_#{rest_parent_key}_by_id", val)
233
+ else
234
+ find_by_id(rest_parent_key, val)
235
+ end
236
+ end
180
237
  end
181
238
 
182
- def rest_parent_find_by_name_or_id(name)
183
- return self.send("find_#{rest_parent_key}_by_name_or_id", name)
239
+ # override RestCommand method to include parent_id parameter
240
+ def rest_find_by_name_or_id(parent_id, val)
241
+ # use explicitly defined finders
242
+ # else default to new generic CliCommand find_by methods
243
+ if rest_has_name
244
+ if respond_to?("find_#{rest_key}_by_name_or_id", true)
245
+ send("find_#{rest_key}_by_name_or_id", parent_id, val)
246
+ else
247
+ find_by_name_or_id(rest_key, parent_id, val)
248
+ end
249
+ else
250
+ if respond_to?("find_#{rest_key}_by_id", true)
251
+ send("find_#{rest_key}_by_id", parent_id, val)
252
+ else
253
+ find_by_id(rest_key, parent_id, val)
254
+ end
255
+ end
184
256
  end
185
257
 
186
258
  def registered_interfaces
@@ -193,29 +265,23 @@ module Morpheus::Cli::SecondaryRestCommand
193
265
  options = {}
194
266
  optparse = Morpheus::Cli::OptionParser.new do |opts|
195
267
  opts.banner = subcommand_usage("[#{rest_parent_arg}] [search]")
196
- build_standard_list_options(opts, options)
268
+ parse_list_options!(args, options, params)
197
269
  opts.footer = <<-EOT
198
270
  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}.
271
+ [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
200
272
  [search] is optional. This is a search phrase to filter the results.
201
273
  EOT
202
274
  end
203
275
  optparse.parse!(args)
204
- parent_id = args[0]
205
- if args[1] # && rest_has_name
206
- record_name = args[1]
207
- end
208
276
  verify_args!(args:args, optparse:optparse, min:1)
209
- if args.count > 1
210
- options[:phrase] = args[1..-1].join(" ")
211
- end
212
277
  connect(options)
278
+ parent_id = args[0]
213
279
  parent_record = rest_parent_find_by_name_or_id(parent_id)
214
280
  if parent_record.nil?
215
- raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
281
+ return 1, "#{rest_parent_label} not found for '#{parent_id}"
216
282
  end
217
283
  parent_id = parent_record['id']
218
- params.merge!(parse_list_options(options))
284
+ parse_list_options!(args.count > 1 ? args[1..-1] : [], options, params)
219
285
  rest_interface.setopts(options)
220
286
  if options[:dry_run]
221
287
  print_dry_run rest_interface.dry.list(parent_id, params)
@@ -228,7 +294,7 @@ EOT
228
294
  if records.nil? || records.empty?
229
295
  print cyan,"No #{rest_label_plural.downcase} found.",reset,"\n"
230
296
  else
231
- print as_pretty_table(records, rest_list_column_definitions.upcase_keys!, options)
297
+ print as_pretty_table(records, rest_list_column_definitions(options).upcase_keys!, options)
232
298
  print_results_pagination(json_response) if json_response['meta']
233
299
  end
234
300
  print reset,"\n"
@@ -241,43 +307,41 @@ EOT
241
307
  options = {}
242
308
  optparse = Morpheus::Cli::OptionParser.new do |opts|
243
309
  opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}]")
244
- build_standard_get_options(opts, options)
310
+ build_get_options(opts, options, params)
245
311
  opts.footer = <<-EOT
246
312
  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}.
313
+ [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
314
+ [#{rest_arg}] is required. This is the #{rest_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_label)} #{rest_label.downcase}.
249
315
  EOT
250
316
  end
251
317
  optparse.parse!(args)
252
318
  verify_args!(args:args, optparse:optparse, min:2)
253
319
  connect(options)
320
+ parse_get_options!(args.count > 1 ? args[1..-1] : [], options, params)
254
321
  parent_id = args[0]
255
322
  parent_record = rest_parent_find_by_name_or_id(parent_id)
256
323
  if parent_record.nil?
257
- raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
324
+ return 1, "#{rest_parent_label} not found for '#{parent_id}"
258
325
  end
259
326
  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
327
+ id = args[1..-1].join(" ")
328
+ _get(parent_id, id, params, options)
265
329
  end
266
330
 
267
331
  def _get(parent_id, id, params, options)
268
332
  if id !~ /\A\d{1,}\Z/
269
- record = rest_find_by_name_or_id(id)
333
+ record = rest_find_by_name_or_id(parent_id, id)
270
334
  if record.nil?
271
- raise_command_error "#{rest_label} not found for name '#{id}'"
335
+ return 1, "#{rest_label} not found for '#{id}"
272
336
  end
273
337
  id = record['id']
274
338
  end
275
339
  rest_interface.setopts(options)
276
340
  if options[:dry_run]
277
- print_dry_run rest_interface.dry.get(id, params)
341
+ print_dry_run rest_interface.dry.get(parent_id, id, params)
278
342
  return
279
343
  end
280
- json_response = rest_interface.get(id, params)
344
+ json_response = rest_interface.get(parent_id, id, params)
281
345
  render_response_for_get(json_response, options)
282
346
  return 0, nil
283
347
  end
@@ -287,7 +351,7 @@ EOT
287
351
  record = json_response[rest_object_key]
288
352
  print_h1 rest_label, [], options
289
353
  print cyan
290
- print_description_list(rest_column_definitions, record, options)
354
+ print_description_list(rest_column_definitions(options), record, options)
291
355
  # show config settings...
292
356
  if record['optionTypes'] && record['optionTypes'].size > 0
293
357
  print_h2 "Option Types", options
@@ -301,22 +365,22 @@ EOT
301
365
  parent_id, parent_record = nil, nil
302
366
  record_type_id = nil
303
367
  options = {}
368
+ option_types = respond_to?("add_#{rest_key}_option_types", true) ? send("add_#{rest_key}_option_types") : []
369
+ advanced_option_types = respond_to?("add_#{rest_key}_advanced_option_types", true) ? send("add_#{rest_key}_advanced_option_types") : []
370
+ type_option_type = option_types.find {|it| it['fieldName'] == 'type'}
304
371
  optparse = Morpheus::Cli::OptionParser.new do |opts|
305
- if rest_has_type
372
+ opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}]")
373
+ if rest_has_type && type_option_type.nil?
306
374
  opts.on( '-t', "--#{rest_type_arg} TYPE", "#{rest_type_label}" ) do |val|
307
375
  record_type_id = val
308
376
  end
309
377
  end
310
- if self.class.method_defined?("add_#{rest_key}_option_types")
311
- build_option_type_options(opts, options, self.send("add_#{rest_key}_option_types"))
312
- end
313
- if self.class.method_defined?("add_#{rest_key}_advanced_option_types")
314
- build_option_type_options(opts, options, self.send("add_#{rest_key}_advanced_option_types"))
315
- end
378
+ build_option_type_options(opts, options, option_types)
379
+ build_option_type_options(opts, options, advanced_option_types)
316
380
  build_standard_add_options(opts, options)
317
381
  opts.footer = <<-EOT
318
382
  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}.
383
+ [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
320
384
  [#{rest_arg}] is required. This is the name of the new #{rest_label.downcase}.
321
385
  EOT
322
386
  end
@@ -326,31 +390,39 @@ EOT
326
390
  # for now args[0] is assumed to be the 'name'
327
391
  record_name = nil
328
392
  parent_id = args[0]
329
- if args[1] # && rest_has_name
330
- record_name = args[1]
393
+ if rest_has_name
394
+ if args[1]
395
+ record_name = args[1]
396
+ end
397
+ verify_args!(args:args, optparse:optparse, min:1, max: 2)
398
+ else
399
+ verify_args!(args:args, optparse:optparse, count: 1)
331
400
  end
332
401
  connect(options)
333
402
  # load parent record
334
403
  # todo: prompt instead of error
335
404
  parent_record = rest_parent_find_by_name_or_id(parent_id)
336
405
  if parent_record.nil?
337
- raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
406
+ return 1, "#{rest_parent_label} not found for '#{parent_id}"
338
407
  end
339
408
  parent_id = parent_record['id']
340
409
  # load or prompt for type
341
- if rest_has_type
410
+ if rest_has_type && type_option_type.nil?
342
411
  if record_type_id.nil?
343
412
  #raise_command_error "#{rest_type_label} is required.\n#{optparse}"
344
- type_list = rest_type_interface.list({max:10000})[rest_type_list_key]
413
+ type_list = rest_type_interface.list({max:10000, creatable: true})[rest_type_list_key]
345
414
  type_dropdown_options = type_list.collect {|it| {'name' => it['name'], 'value' => it['code']} }
346
415
  record_type_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => rest_type_label, 'type' => 'select', 'selectOptions' => type_dropdown_options, 'required' => true}], options[:options], @api_client)['type']
347
416
  end
348
417
  record_type = rest_type_find_by_name_or_id(record_type_id)
349
418
  if record_type.nil?
350
- raise_command_error "#{rest_type_label} not found for '#{record_type_id}'.\n#{optparse}"
419
+ return 1, "#{rest_type_label} not found for '#{record_type_id}"
351
420
  end
352
421
  end
353
422
  passed_options = parse_passed_options(options)
423
+ options[:params] ||= {}
424
+ options[:params][rest_parent_param] = parent_id
425
+ options[:options]['_object_key'] = rest_object_key
354
426
  payload = {}
355
427
  if options[:payload]
356
428
  payload = options[:payload]
@@ -360,25 +432,41 @@ EOT
360
432
  if record_name
361
433
  record_payload['name'] = record_name
362
434
  options[:options]['name'] = record_name # injected for prompt
435
+ options[:options][rest_arg] = record_name
363
436
  end
364
437
  if rest_has_type && record_type
365
438
  # record_payload['type'] = {'code' => record_type['code']}
366
439
  record_payload['type'] = record_type['code']
367
440
  options[:options]['type'] = record_type['code'] # injected for prompt
368
441
  # initialize params for loading optionSource data
369
- options[:params] ||= {}
370
442
  options[:params]['type'] = record_type['code']
371
443
  end
372
444
  record_payload.deep_merge!(passed_options)
373
- if self.class.method_defined?("add_#{rest_key}_option_types")
374
- add_option_types = self.send("add_#{rest_key}_option_types")
375
- v_prompt = Morpheus::Cli::OptionTypes.prompt(add_option_types, options[:options], @api_client, options[:params])
445
+ if option_types && !option_types.empty?
446
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(option_types, options[:options], @api_client, options[:params])
376
447
  v_prompt.deep_compact!
377
448
  v_prompt.booleanize! # 'on' => true
378
449
  record_payload.deep_merge!(v_prompt)
379
450
  end
380
451
  # options by type
381
- my_option_types = record_type ? record_type['optionTypes'] : nil
452
+ if rest_has_type && record_type.nil?
453
+ type_value = record_payload['type'].is_a?(Hash) ? record_payload['type']['id'] : record_payload['type']
454
+ if type_value
455
+ record_type = rest_type_find_by_name_or_id(type_value)
456
+ if record_type.nil?
457
+ return 1, "#{rest_type_label} not found for '#{type_value}"
458
+ end
459
+ end
460
+ # reload the type by id to get all the details ie. optionTypes
461
+ if record_type && record_type['optionTypes'].nil?
462
+ record_type = rest_type_find_by_name_or_id(record_type['id'])
463
+ end
464
+ end
465
+ if respond_to?("load_option_types_for_#{rest_key}", true)
466
+ my_option_types = send("load_option_types_for_#{rest_key}", record_type, parent_record)
467
+ else
468
+ my_option_types = record_type ? record_type['optionTypes'] : nil
469
+ end
382
470
  if my_option_types && !my_option_types.empty?
383
471
  # remove redundant fieldContext
384
472
  my_option_types.each do |option_type|
@@ -386,15 +474,15 @@ EOT
386
474
  option_type['fieldContext'] = nil
387
475
  end
388
476
  end
389
- v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, options[:params])
477
+ api_params = (options[:params] || {}).merge(record_payload)
478
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(my_option_types, options[:options], @api_client, api_params)
390
479
  v_prompt.deep_compact!
391
480
  v_prompt.booleanize! # 'on' => true
392
481
  record_payload.deep_merge!(v_prompt)
393
482
  end
394
483
  # advanced options (uses no_prompt)
395
- if self.class.method_defined?("add_#{rest_key}_advanced_option_types")
396
- add_advanced_option_types = self.send("add_#{rest_key}_advanced_option_types")
397
- v_prompt = Morpheus::Cli::OptionTypes.no_prompt(add_advanced_option_types, options[:options], @api_client, options[:params])
484
+ if advanced_option_types && !advanced_option_types.empty?
485
+ v_prompt = Morpheus::Cli::OptionTypes.no_prompt(advanced_option_types, options[:options], @api_client, options[:params])
398
486
  v_prompt.deep_compact!
399
487
  v_prompt.booleanize! # 'on' => true
400
488
  record_payload.deep_merge!(v_prompt)
@@ -421,31 +509,28 @@ EOT
421
509
  record_type = nil
422
510
  record_type_id = nil
423
511
  options = {}
424
- params = {}
512
+ option_types = respond_to?("update_#{rest_key}_option_types", true) ? send("update_#{rest_key}_option_types") : []
513
+ advanced_option_types = respond_to?("update_#{rest_key}_advanced_option_types", true) ? send("update_#{rest_key}_advanced_option_types") : []
425
514
  optparse = Morpheus::Cli::OptionParser.new do |opts|
426
515
  opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}] [options]")
427
- if self.class.method_defined?("update_#{rest_key}_option_types")
428
- build_option_type_options(opts, options, self.send("update_#{rest_key}_option_types"))
429
- end
430
- if self.class.method_defined?("update_#{rest_key}_advanced_option_types")
431
- build_option_type_options(opts, options, self.send("update_#{rest_key}_advanced_option_types"))
432
- end
516
+
433
517
  build_standard_update_options(opts, options)
434
518
  opts.footer = <<-EOT
435
519
  Update an existing #{rest_label.downcase}.
436
- [#{rest_parent_arg}] is required. This is the name or id of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
437
- [#{rest_arg}] is required. This is the name or id of #{a_or_an(rest_label)} #{rest_label.downcase}.
520
+ [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
521
+ [#{rest_arg}] is required. This is the #{rest_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_label)} #{rest_label.downcase}.
438
522
  EOT
439
523
  end
440
524
  optparse.parse!(args)
441
525
  verify_args!(args:args, optparse:optparse, count:2)
526
+ connect(options)
442
527
  parent_record = rest_parent_find_by_name_or_id(parent_id)
443
528
  if parent_record.nil?
444
- raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
529
+ return 1, "#{rest_parent_label} not found for '#{parent_id}"
445
530
  end
446
531
  parent_id = parent_record['id']
447
532
  connect(options)
448
- record = rest_find_by_name_or_id(id)
533
+ record = rest_find_by_name_or_id(parent_id, id)
449
534
  if record.nil?
450
535
  return 1, "#{rest_name} not found for '#{id}'"
451
536
  end
@@ -454,7 +539,11 @@ EOT
454
539
  record_type_id = record['type']['id']
455
540
  record_type = rest_type_find_by_name_or_id(record_type_id)
456
541
  if record_type.nil?
457
- raise_command_error "#{rest_type_label} not found for '#{record_type_id}'.\n#{optparse}"
542
+ return 1, "#{rest_type_label} not found for '#{record_type_id}"
543
+ end
544
+ # reload the type by id to get all the details ie. optionTypes
545
+ if record_type['optionTypes'].nil?
546
+ record_type = rest_type_find_by_name_or_id(record_type['id'])
458
547
  end
459
548
  end
460
549
  passed_options = parse_passed_options(options)
@@ -473,15 +562,20 @@ EOT
473
562
  options[:params]['type'] = record_type['code']
474
563
  end
475
564
  # update options without prompting by default
476
- if self.class.method_defined?("update_#{rest_key}_option_types")
477
- update_option_types = self.send("update_#{rest_key}_option_types")
478
- v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_option_types, options[:options], @api_client, options[:params])
565
+ if option_types && !option_types.empty?
566
+ api_params = (options[:params] || {}).merge(record_payload) # need to merge in values from record too, ughhh
567
+ v_prompt = Morpheus::Cli::OptionTypes.no_prompt(option_types, options[:options], @api_client, api_params)
479
568
  v_prompt.deep_compact!
480
569
  v_prompt.booleanize! # 'on' => true
481
570
  record_payload.deep_merge!(v_prompt)
482
571
  end
483
572
  # options by type
484
- my_option_types = record_type ? record_type['optionTypes'] : nil
573
+ my_option_types = nil
574
+ if respond_to?("load_option_types_for_#{rest_key}", true)
575
+ my_option_types = send("load_option_types_for_#{rest_key}", record_type, parent_record)
576
+ else
577
+ my_option_types = record_type ? record_type['optionTypes'] : nil
578
+ end
485
579
  if my_option_types && !my_option_types.empty?
486
580
  # remove redundant fieldContext
487
581
  # make them optional for updates
@@ -500,9 +594,8 @@ EOT
500
594
  record_payload.deep_merge!(v_prompt)
501
595
  end
502
596
  # advanced options
503
- if self.class.method_defined?("update_#{rest_key}_advanced_option_types")
504
- update_advanced_option_types = self.send("update_#{rest_key}_advanced_option_types")
505
- v_prompt = Morpheus::Cli::OptionTypes.no_prompt(update_advanced_option_types, options[:options], @api_client, options[:params])
597
+ if advanced_option_types && !advanced_option_types.empty?
598
+ v_prompt = Morpheus::Cli::OptionTypes.no_prompt(advanced_option_types, options[:options], @api_client, options[:params])
506
599
  v_prompt.deep_compact!
507
600
  v_prompt.booleanize! # 'on' => true
508
601
  record_payload.deep_merge!(v_prompt)
@@ -540,8 +633,8 @@ EOT
540
633
  build_standard_remove_options(opts, options)
541
634
  opts.footer = <<-EOT
542
635
  Delete an existing #{rest_label.downcase}.
543
- [#{rest_parent_arg}] is required. This is the name or id of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
544
- [#{rest_arg}] is required. This is the name or id of #{a_or_an(rest_label)} #{rest_label.downcase}.
636
+ [#{rest_parent_arg}] is required. This is the #{rest_parent_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_parent_label)} #{rest_parent_label.downcase}.
637
+ [#{rest_arg}] is required. This is the #{rest_has_name ? 'name or id' : 'id'} of #{a_or_an(rest_label)} #{rest_label.downcase}.
545
638
  EOT
546
639
  end
547
640
  optparse.parse!(args)
@@ -549,9 +642,9 @@ EOT
549
642
  connect(options)
550
643
  parent_record = rest_parent_find_by_name_or_id(parent_id)
551
644
  if parent_record.nil?
552
- raise_command_error "#{rest_parent_label} not found for '#{parent_id}'.\n#{optparse}"
645
+ return 1, "#{rest_parent_label} not found for '#{parent_id}"
553
646
  end
554
- record = rest_find_by_name_or_id(id)
647
+ record = rest_find_by_name_or_id(parent_id, id)
555
648
  if record.nil?
556
649
  return 1, "#{rest_name} not found for '#{id}'"
557
650
  end
@@ -561,7 +654,7 @@ EOT
561
654
  params.merge!(parse_query_options(options))
562
655
  rest_interface.setopts(options)
563
656
  if options[:dry_run]
564
- print_dry_run rest_interface.dry.destroy(parent_id, record['id'])
657
+ print_dry_run rest_interface.dry.destroy(parent_id, record['id'], params)
565
658
  return 0, nil
566
659
  end
567
660
  json_response = rest_interface.destroy(parent_id, record['id'], params)
@@ -0,0 +1,156 @@
1
+ require 'morpheus/cli/mixins/print_helper'
2
+ require 'morpheus/cli/option_types'
3
+ require 'morpheus/rest_client'
4
+ # Mixin for Morpheus::Cli command classes
5
+ # Provides common methods for storage server management
6
+ # including storage servers and storage server types
7
+ module Morpheus::Cli::StorageServersHelper
8
+
9
+ def self.included(klass)
10
+ klass.send :include, Morpheus::Cli::PrintHelper
11
+ end
12
+
13
+ def storage_servers_interface
14
+ # @api_client.storage_servers
15
+ raise "#{self.class} has not defined @storage_servers_interface" if @storage_servers_interface.nil?
16
+ @storage_servers_interface
17
+ end
18
+
19
+ def storage_server_types_interface
20
+ # @api_client.storage_server_types
21
+ raise "#{self.class} has not defined @storage_server_types_interface" if @storage_server_types_interface.nil?
22
+ @storage_server_types_interface
23
+ end
24
+
25
+ def storage_server_object_key
26
+ 'storageServer'
27
+ end
28
+
29
+ def storage_server_list_key
30
+ 'storageServers'
31
+ end
32
+
33
+ def storage_server_label
34
+ 'Storage Server'
35
+ end
36
+
37
+ def storage_server_label_plural
38
+ 'Storage Server'
39
+ end
40
+
41
+ def storage_server_type_object_key
42
+ 'storageServerType'
43
+ end
44
+
45
+ def storage_server_type_list_key
46
+ 'storageServerTypes'
47
+ end
48
+
49
+ def storage_server_type_label
50
+ 'Storage Server Type'
51
+ end
52
+
53
+ def storage_server_type_label_plural
54
+ 'Storage Server Types'
55
+ end
56
+
57
+ def find_storage_server_by_name_or_id(val)
58
+ if val.to_s =~ /\A\d{1,}\Z/
59
+ return find_storage_server_by_id(val)
60
+ else
61
+ return find_storage_server_by_name(val)
62
+ end
63
+ end
64
+
65
+ def find_storage_server_by_id(id)
66
+ begin
67
+ json_response = storage_servers_interface.get(id.to_i)
68
+ return json_response[storage_server_object_key]
69
+ rescue RestClient::Exception => e
70
+ if e.response && e.response.code == 404
71
+ print_red_alert "Storage Server not found by id #{id}"
72
+ else
73
+ raise e
74
+ end
75
+ end
76
+ end
77
+
78
+ def find_storage_server_by_name(name)
79
+ lbs = storage_servers_interface.list({name: name.to_s})[storage_server_list_key]
80
+ if lbs.empty?
81
+ print_red_alert "Storage Server not found by name #{name}"
82
+ return nil
83
+ elsif lbs.size > 1
84
+ print_red_alert "#{lbs.size} storage servers found by name #{name}"
85
+ #print_lbs_table(lbs, {color: red})
86
+ print reset,"\n\n"
87
+ return nil
88
+ else
89
+ return lbs[0]
90
+ end
91
+ end
92
+
93
+ def get_available_storage_server_types(refresh=false)
94
+ if !@available_storage_server_types || refresh
95
+ @available_storage_server_types = storage_server_types_interface.list({max:1000})[storage_server_type_list_key]
96
+ end
97
+ return @available_storage_server_types
98
+ end
99
+
100
+ def storage_server_type_for_name_or_id(val)
101
+ if val.to_s =~ /\A\d{1,}\Z/
102
+ return storage_server_type_for_id(val)
103
+ else
104
+ return storage_server_type_for_name(val)
105
+ end
106
+ end
107
+
108
+ def storage_server_type_for_id(id)
109
+ return get_available_storage_server_types().find { |z| z['id'].to_i == id.to_i}
110
+ end
111
+
112
+ def storage_server_type_for_name(name)
113
+ return get_available_storage_server_types().find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
114
+ end
115
+
116
+ def find_storage_server_type_by_name_or_id(val)
117
+ if val.to_s =~ /\A\d{1,}\Z/
118
+ return find_storage_server_type_by_id(val)
119
+ else
120
+ return find_storage_server_type_by_name(val)
121
+ end
122
+ end
123
+
124
+ def find_storage_server_type_by_id(id)
125
+ begin
126
+ json_response = storage_server_types_interface.get(id.to_i)
127
+ return json_response[storage_server_type_object_key]
128
+ rescue RestClient::Exception => e
129
+ if e.response && e.response.code == 404
130
+ print_red_alert "Storage Server Type not found by id #{id}"
131
+ return nil
132
+ else
133
+ raise e
134
+ end
135
+ end
136
+ end
137
+
138
+ def find_storage_server_type_by_name(name)
139
+ json_response = storage_server_types_interface.list({name: name.to_s})
140
+ storage_server_types = json_response[storage_server_type_list_key]
141
+ if storage_server_types.empty?
142
+ print_red_alert "Storage Server Type not found by name #{name}"
143
+ return storage_server_types
144
+ elsif storage_server_types.size > 1
145
+ print_red_alert "#{storage_server_types.size} storage server types found by name #{name}"
146
+ rows = storage_server_types.collect do |it|
147
+ {id: it['id'], name: it['name']}
148
+ end
149
+ puts as_pretty_table(rows, [:id, :name], {color:red})
150
+ return nil
151
+ else
152
+ return storage_server_types[0]
153
+ end
154
+ end
155
+
156
+ end