morpheus-cli 5.5.3.2 → 6.0.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +8 -0
  4. data/lib/morpheus/api/cloud_resource_pools_interface.rb +28 -3
  5. data/lib/morpheus/api/containers_interface.rb +10 -0
  6. data/lib/morpheus/api/doc_interface.rb +1 -10
  7. data/lib/morpheus/api/jobs_interface.rb +2 -2
  8. data/lib/morpheus/api/key_pairs_interface.rb +9 -0
  9. data/lib/morpheus/api/network_floating_ips_interface.rb +37 -0
  10. data/lib/morpheus/api/resource_pool_groups_interface.rb +51 -0
  11. data/lib/morpheus/cli/cli_command.rb +17 -11
  12. data/lib/morpheus/cli/commands/appliance_settings_command.rb +5 -0
  13. data/lib/morpheus/cli/commands/apps.rb +12 -6
  14. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +44 -12
  15. data/lib/morpheus/cli/commands/clusters.rb +23 -2
  16. data/lib/morpheus/cli/commands/containers_command.rb +129 -4
  17. data/lib/morpheus/cli/commands/doc.rb +14 -13
  18. data/lib/morpheus/cli/commands/hosts.rb +2 -0
  19. data/lib/morpheus/cli/commands/instances.rb +9 -3
  20. data/lib/morpheus/cli/commands/jobs_command.rb +50 -3
  21. data/lib/morpheus/cli/commands/key_pairs.rb +94 -33
  22. data/lib/morpheus/cli/commands/network_floating_ips.rb +109 -0
  23. data/lib/morpheus/cli/commands/reports_command.rb +8 -1
  24. data/lib/morpheus/cli/commands/resource_pool_groups_command.rb +586 -0
  25. data/lib/morpheus/cli/commands/roles.rb +10 -10
  26. data/lib/morpheus/cli/commands/service_catalog_command.rb +40 -2
  27. data/lib/morpheus/cli/commands/service_plans_command.rb +51 -22
  28. data/lib/morpheus/cli/commands/shell.rb +1 -1
  29. data/lib/morpheus/cli/commands/tasks.rb +130 -35
  30. data/lib/morpheus/cli/commands/workflows.rb +109 -23
  31. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +148 -0
  32. data/lib/morpheus/cli/mixins/jobs_helper.rb +30 -3
  33. data/lib/morpheus/cli/mixins/processes_helper.rb +2 -26
  34. data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -1
  35. data/lib/morpheus/cli/option_types.rb +2 -2
  36. data/lib/morpheus/cli/version.rb +1 -1
  37. data/test/cli/doc_test.rb +1 -1
  38. metadata +6 -2
@@ -594,11 +594,15 @@ EOT
594
594
  type_id = nil
595
595
  workflow_context = nil
596
596
  workflow_target = nil
597
+ quantity = nil
597
598
  optparse = Morpheus::Cli::OptionParser.new do |opts|
598
599
  opts.banner = subcommand_usage("[type] [options]")
599
600
  opts.on('-t', '--type TYPE', String, "Catalog Item Type Name or ID") do |val|
600
601
  type_id = val.to_s
601
602
  end
603
+ opts.on('--quantity QUANTITY', String, "Quantity for this catalog item. Will be overridden to 1 if quantity not allowed.") do |val|
604
+ quantity = val.to_s
605
+ end
602
606
  opts.on('--validate','--validate', "Validate Only. Validates the configuration and skips adding the item.") do
603
607
  options[:validate_only] = true
604
608
  end
@@ -648,6 +652,16 @@ EOT
648
652
  payload[add_item_object_key]['type'] = {'name' => catalog_item_type['name']}
649
653
  #payload[add_item_object_key]['type'] = {'id' => catalog_item_type['id']}
650
654
 
655
+ if quantity
656
+ payload[add_item_object_key].deep_merge!({'quantity' => quantity})
657
+ else
658
+ if catalog_item_type['allowQuantity']
659
+ quantity_option_type = {'fieldName' => 'quantity', 'fieldLabel' => 'Quantity', 'type' => 'number', 'defaultValue' => 1, 'required' => true, 'displayOrder' => 1}
660
+ quantity_prompt = Morpheus::Cli::OptionTypes.prompt( [quantity_option_type], options[:options], @api_client, options[:params])['quantity']
661
+ payload[add_item_object_key].deep_merge!({'quantity' => quantity_prompt})
662
+ end
663
+ end
664
+
651
665
  # this is silly, need to load by id to get optionTypes
652
666
  # maybe do ?name=foo&includeOptionTypes=true
653
667
  if catalog_item_type['optionTypes'].nil?
@@ -926,11 +940,15 @@ EOT
926
940
  type_id = nil
927
941
  workflow_context = nil
928
942
  workflow_target = nil
943
+ quantity = nil
929
944
  optparse = Morpheus::Cli::OptionParser.new do |opts|
930
945
  opts.banner = subcommand_usage("[type] [options]")
931
946
  opts.on('-t', '--type TYPE', String, "Catalog Item Type Name or ID") do |val|
932
947
  type_id = val.to_s
933
948
  end
949
+ opts.on('--quantity QUANTITY', String, "Quantity for this catalog item. Will be overridden to 1 if quantity not allowed.") do |val|
950
+ quantity = val.to_s
951
+ end
934
952
  opts.on('--validate','--validate', "Validate Only. Validates the configuration and skips creating the order.") do
935
953
  options[:validate_only] = true
936
954
  end
@@ -991,6 +1009,17 @@ EOT
991
1009
  item_payload['type'] = {'name' => catalog_item_type['name']}
992
1010
  #payload[add_item_object_key]['type'] = {'id' => catalog_item_type['id']}
993
1011
 
1012
+ if quantity
1013
+ item_payload.deep_merge!({'quantity' => quantity})
1014
+ else
1015
+ if catalog_item_type['allowQuantity']
1016
+ quantity_option_type = {'fieldName' => 'quantity', 'fieldLabel' => 'Quantity', 'type' => 'number', 'defaultValue' => 1, 'required' => true, 'displayOrder' => 1}
1017
+ quantity_prompt = Morpheus::Cli::OptionTypes.prompt( [quantity_option_type], options[:options], @api_client, options[:params])['quantity']
1018
+ item_payload.deep_merge!({'quantity' => quantity_prompt})
1019
+ end
1020
+ end
1021
+
1022
+
994
1023
  # this is silly, need to load by id to get optionTypes
995
1024
  # maybe do ?name=foo&includeOptionTypes=true
996
1025
  if catalog_item_type['optionTypes'].nil?
@@ -1126,6 +1155,14 @@ EOT
1126
1155
  opts.on('--releaseEIPs [true|false]', String, "Release EIPs. Default is on. Applies to Amazon only.") do |val|
1127
1156
  params[:releaseEIPs] = ['true','on','1',''].include?(val.to_s.downcase)
1128
1157
  end
1158
+ opts.on('--release-ips [on|off]', ['on','off'], "Release Floating IPs. Default is on. Applies to certain types only.") do |val|
1159
+ params[:releaseFloatingIps] = ['true','on','1',''].include?(val.to_s.downcase)
1160
+ params[:releaseEIPs] = params[:releaseFloatingIps] # old parameter before 6.0
1161
+ end
1162
+ opts.on('--releaseEIPs [on|off]', ['on','off'], "Alias for Release Floating IPs") do |val|
1163
+ params[:releaseFloatingIps] = ['true','on','1',''].include?(val.to_s.downcase)
1164
+ params[:releaseEIPs] = params[:releaseFloatingIps] # old parameter before 6.0
1165
+ end
1129
1166
  opts.on( '-f', '--force', "Force Delete" ) do
1130
1167
  params[:force] = true
1131
1168
  end
@@ -1192,6 +1229,7 @@ EOT
1192
1229
  # "Blueprint" => lambda {|it| it['blueprint'] ? it['blueprint']['name'] : nil },
1193
1230
  # "Enabled" => lambda {|it| format_boolean(it['enabled']) },
1194
1231
  "Featured" => lambda {|it| format_boolean(it['featured']) },
1232
+ "Allow Quantity" => lambda {|it| format_boolean(it['allowQuantity']) },
1195
1233
  #"Config" => lambda {|it| it['config'] },
1196
1234
  # "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
1197
1235
  # "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
@@ -1400,7 +1438,7 @@ EOT
1400
1438
  {"ID" => lambda {|it| it['id'] } },
1401
1439
  #{"NAME" => lambda {|it| it['name'] } },
1402
1440
  {"Type" => lambda {|it| it['type']['name'] rescue '' } },
1403
- #{"Qty" => lambda {|it| it['quantity'] } },
1441
+ {"Qty" => lambda {|it| it['quantity'] } },
1404
1442
  {"Price" => lambda {|it| it['price'] ? format_money(it['price'] , it['currency'], {sigdig:options[:sigdig] || default_sigdig}) : "No pricing configured" } },
1405
1443
  {"Status" => lambda {|it|
1406
1444
  status_string = format_catalog_item_status(it)
@@ -1430,7 +1468,7 @@ EOT
1430
1468
  {"ID" => lambda {|it| it['id'] } },
1431
1469
  #{"NAME" => lambda {|it| it['name'] } },
1432
1470
  {"TYPE" => lambda {|it| it['type']['name'] rescue '' } },
1433
- #{"QTY" => lambda {|it| it['quantity'] } },
1471
+ {"QTY" => lambda {|it| it['quantity'] } },
1434
1472
  {"PRICE" => lambda {|it| it['price'] ? format_money(it['price'] , it['currency'], {sigdig:options[:sigdig] || default_sigdig}) : "No pricing configured" } },
1435
1473
  {"STATUS" => lambda {|it|
1436
1474
  status_string = format_catalog_item_status(it)
@@ -171,13 +171,21 @@ class Morpheus::Cli::ServicePlanCommand
171
171
  ranges = (service_plan['config'] ? service_plan['config']['ranges'] : nil) || {}
172
172
 
173
173
  if (ranges['minStorage'] && ranges['minStorage'] != '') || (ranges['maxStorage'] && ranges['maxStorage'] != '')
174
- description_cols['Custom Storage Range'] = lambda {|it|
174
+ description_cols['Custom Total Storage Range'] = lambda {|it|
175
175
  get_range(
176
176
  ranges['minStorage'] && ranges['minStorage'] != '' ? "#{ranges['minStorage']} #{(it['config'] && it['config']['storageSizeType'] ? it['config']['storageSizeType'] : 'GB').upcase}" : nil,
177
177
  ranges['maxStorage'] && ranges['maxStorage'] != '' ? "#{ranges['maxStorage']} #{(it['config'] && it['config']['storageSizeType'] ? it['config']['storageSizeType'] : 'GB').upcase}" : nil,
178
178
  )
179
179
  }
180
180
  end
181
+ if (ranges['minPerDiskSize'] && ranges['minPerDiskSize'] != '') || (ranges['maxPerDiskSize'] && ranges['maxPerDiskSize'] != '')
182
+ description_cols['Custom Per Disk Range'] = lambda {|it|
183
+ get_range(
184
+ ranges['minPerDiskSize'] && ranges['minPerDiskSize'] != '' ? "#{ranges['minPerDiskSize']} GB" : nil,
185
+ ranges['maxPerDiskSize'] && ranges['maxPerDiskSize'] != '' ? "#{ranges['maxPerDiskSize']} GB" : nil
186
+ )
187
+ }
188
+ end
181
189
  if (ranges['minMemory'] && ranges['minMemory'] != '') || (ranges['maxMemory'] && ranges['maxMemory'] != '')
182
190
  description_cols['Custom Memory Range'] = lambda {|it|
183
191
  get_range(
@@ -264,6 +272,9 @@ class Morpheus::Cli::ServicePlanCommand
264
272
  opts.on('--disks [NUMBER]', Integer, "Max disks allowed" ) do |val|
265
273
  params['maxDisks'] = val.to_i || 1
266
274
  end
275
+ opts.on('--cores-per-socket [NUMBER]', Integer, "Cores Per Socket") do |val|
276
+ params['coresPerSocket'] = val.to_i || 1
277
+ end
267
278
  opts.on('--custom-cores [on|off]', String, "Can be used to enable / disable customizable cores. Default is on") do |val|
268
279
  params['customCores'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
269
280
  end
@@ -285,17 +296,17 @@ class Morpheus::Cli::ServicePlanCommand
285
296
  opts.on('--price-sets [LIST]', Array, 'Price set(s), comma separated list of price set IDs') do |list|
286
297
  params['priceSets'] = list.collect {|it| it.to_s.strip.empty? || !it.to_i ? nil : it.to_s.strip}.compact.uniq.collect {|it| {'id' => it.to_i}}
287
298
  end
288
- opts.on('--min-storage NUMBER', String, "Min storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
289
- # Storage doesn't get converted to bytes
290
- bytes = parse_bytes_param(val, '--min-storage', 'GB', true)
291
- ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = bytes[:number]
292
- (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
299
+ opts.on('--min-storage NUMBER', String, "Min total storage in GB.") do |val|
300
+ ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = val.to_i
293
301
  end
294
- opts.on('--max-storage NUMBER', String, "Max storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
295
- # Storage doesn't get converted to bytes
296
- bytes = parse_bytes_param(val.to_s, '--max-storage', 'GB', true)
297
- ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = bytes[:number]
298
- (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
302
+ opts.on('--max-storage NUMBER', String, "Max total storage in GB.") do |val|
303
+ ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = val.to_i
304
+ end
305
+ opts.on('--min-per-disk-size NUMBER', String, "Min per disk size in GB.") do |val|
306
+ ((params['config'] ||= {})['ranges'] ||= {})['minPerDiskSize'] = val.to_i
307
+ end
308
+ opts.on('--max-per-disk-size NUMBER', String, "Max per disk size in GB.") do |val|
309
+ ((params['config'] ||= {})['ranges'] ||= {})['maxPerDiskSize'] = val.to_i
299
310
  end
300
311
  opts.on('--min-memory NUMBER', String, "Min memory. Assumes MB unless optional modifier specified, ex: 1GB") do |val|
301
312
  # Memory does get converted to bytes
@@ -396,13 +407,31 @@ class Morpheus::Cli::ServicePlanCommand
396
407
  true
397
408
  )
398
409
  params['maxMemory'] = bytes[:bytes]
399
- # (params['config'] ||= {})['memorySizeType'] = bytes[:unit].downcase
400
410
  rescue
401
411
  print "Invalid Value... Please try again.\n"
402
412
  end
413
+ params['customMaxMemory'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'customMaxMemory', 'type' => 'checkbox', 'fieldLabel' => 'Custom Max Memory', 'required' => false, 'description' => 'Custom Max Memory', 'defaultValue' => false}],options[:options],@api_client,{}, options[:no_prompt])['customMaxMemory']
403
414
  end
404
415
  end
405
416
 
417
+ # add'n options
418
+ addn_options = [
419
+ {'fieldName' => 'maxCores', 'fieldLabel' => 'Core Count', 'type' => 'number', 'required' => true, 'defaultValue' => 1, 'displayOrder' => 1},
420
+ {'fieldName' => 'customCores', 'fieldLabel' => 'Custom Cores', 'type' => 'checkbox', 'defaultValue' => false, 'displayOrder' => 2},
421
+ {'fieldName' => 'coresPerSocket', 'fieldLabel' => 'Cores Per Socket', 'type' => 'number', 'required' => true, 'defaultValue' => 1, 'displayOrder' => 3},
422
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minStorage', 'fieldLabel' => 'Min Total Storage (GB)', 'type' => 'number', 'displayOrder' => 1},
423
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxStorage', 'fieldLabel' => 'Max Total Storage (GB)', 'type' => 'number', 'displayOrder' => 2},
424
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minPerDiskSize', 'fieldLabel' => 'Min Per Disk Size (GB)', 'type' => 'number', 'displayOrder' => 3},
425
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxPerDiskSize', 'fieldLabel' => 'Max Per Disk Size (GB)', 'type' => 'number', 'displayOrder' => 4},
426
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minMemory', 'fieldLabel' => 'Min Memory (GB)', 'type' => 'number', 'displayOrder' => 5},
427
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxMemory', 'fieldLabel' => 'Max Memory (GB)', 'type' => 'number', 'displayOrder' => 6},
428
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'minCores', 'fieldLabel' => 'Min Cores', 'type' => 'number', 'displayOrder' => 7},
429
+ {'fieldContext' => 'config.ranges', 'fieldGroup' => 'Custom Ranges', 'fieldName' => 'maxCores', 'fieldLabel' => 'Max Cores', 'type' => 'number', 'displayOrder' => 8}
430
+ ]
431
+
432
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(addn_options, options[:options], @api_client, params)
433
+ params.deep_merge!(v_prompt)
434
+
406
435
  # price sets
407
436
  if params['priceSets'].nil? && !options[:no_prompt]
408
437
  price_sets = []
@@ -517,17 +546,17 @@ class Morpheus::Cli::ServicePlanCommand
517
546
  opts.on('--price-sets [LIST]', Array, 'Price set(s), comma separated list of price set IDs') do |list|
518
547
  params['priceSets'] = list.collect {|it| it.to_s.strip.empty? || !it.to_i ? nil : it.to_s.strip}.compact.uniq.collect {|it| {'id' => it.to_i}}
519
548
  end
520
- opts.on('--min-storage NUMBER', String, "Min storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
521
- # Storage doesn't get converted to bytes
522
- bytes = parse_bytes_param(val, '--min-storage', 'GB')
523
- ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = bytes[:number]
524
- (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
549
+ opts.on('--min-storage NUMBER', String, "Min total storage in GB.") do |val|
550
+ ((params['config'] ||= {})['ranges'] ||= {})['minStorage'] = val.to_i
525
551
  end
526
- opts.on('--max-storage NUMBER', String, "Max storage. Assumes GB unless optional modifier specified, ex: 512MB") do |val|
527
- # Storage doesn't get converted to bytes
528
- bytes = parse_bytes_param(val, '--max-storage', 'GB', true)
529
- ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = bytes[:number]
530
- (params['config'] ||= {})['storageSizeType'] = bytes[:unit].downcase
552
+ opts.on('--max-storage NUMBER', String, "Max total storage in GB.") do |val|
553
+ ((params['config'] ||= {})['ranges'] ||= {})['maxStorage'] = val.to_i
554
+ end
555
+ opts.on('--min-per-disk-size NUMBER', String, "Min per disk size in GB.") do |val|
556
+ ((params['config'] ||= {})['ranges'] ||= {})['minPerDiskSize'] = val.to_i
557
+ end
558
+ opts.on('--max-per-disk-size NUMBER', String, "Max per disk size in GB.") do |val|
559
+ ((params['config'] ||= {})['ranges'] ||= {})['maxPerDiskSize'] = val.to_i
531
560
  end
532
561
  opts.on('--min-memory NUMBER', String, "Min memory. Assumes MB unless optional modifier specified, ex: 1GB") do |val|
533
562
  # Memory does get converted to bytes
@@ -359,7 +359,7 @@ class Morpheus::Cli::Shell
359
359
  end
360
360
  out << "See the available commands below.\n"
361
361
 
362
- out << "\nCommands:"
362
+ out << "\nCommands:\n"
363
363
  # commands = @morpheus_commands + @shell_commands
364
364
  # @morpheus_commands.sort.each {|cmd|
365
365
  # out << "\t#{cmd.to_s}\n"
@@ -442,7 +442,7 @@ class Morpheus::Cli::Tasks
442
442
  # Result Type
443
443
  if options[:options]['resultType']
444
444
  payload['task']['resultType'] = options[:options]['resultType']
445
- else
445
+ elsif task_type['hasResults']
446
446
  result_types_dropdown = [{"name" => "Value", "value" => "value"}, {"name" => "Exit Code", "value" => "exitCode"}, {"name" => "Key Value", "value" => "keyValue"}, {"name" => "JSON", "value" => "json"}]
447
447
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resultType', 'fieldLabel' => 'Result Type', 'type' => 'select', 'selectOptions' => result_types_dropdown}], options[:options], @api_client)
448
448
  payload['task']['resultType'] = v_prompt['resultType'] unless v_prompt['resultType'].to_s.empty?
@@ -471,6 +471,7 @@ class Morpheus::Cli::Tasks
471
471
  end
472
472
  end
473
473
  end
474
+ process_special_task_option_typeaheads(task_option_types)
474
475
  # inject file_params into options for file-content prompt
475
476
  # or into taskOptions.script for types not yet using file-content
476
477
  unless file_params.empty?
@@ -859,38 +860,99 @@ class Morpheus::Cli::Tasks
859
860
  target_type = nil
860
861
  instance_ids = []
861
862
  instances = []
863
+ instance_label = nil
862
864
  server_ids = []
863
865
  servers = []
864
- default_refresh_interval = 10
866
+ server_label = nil
867
+ default_refresh_interval = 5
868
+ all_target_types = ['appliance', 'instance', 'instance-label', 'server', 'server-label']
865
869
  optparse = Morpheus::Cli::OptionParser.new do |opts|
866
- opts.banner = subcommand_usage("[task] --instance [instance] [options]")
867
- opts.on('--instance INSTANCE', String, "Instance name or id to execute the task on. This option can be passed more than once.") do |val|
870
+ opts.banner = subcommand_usage("[task] [options]")
871
+ opts.on('--context-type VALUE', String, "Context Type, #{ored_list(all_target_types)}") do |val|
872
+ val = val.downcase
873
+ val = 'appliance' if val == 'none'
874
+ if target_type && target_type != val
875
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
876
+ end
877
+ if !all_target_types.include?(val)
878
+ raise ::OptionParser::InvalidOption.new("'#{val}' is invalid. It must be one of the following: instance, instance-label, server, server-label or appliance")
879
+ end
880
+ target_type = val
881
+ end
882
+ opts.on('--target-type VALUE', String, "alias for context-type") do |val|
883
+ val = val.downcase
884
+ val = 'appliance' if val == 'none'
885
+ if target_type && target_type != val
886
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
887
+ end
888
+ if !all_target_types.include?(val)
889
+ raise ::OptionParser::InvalidOption.new("'#{val}' is invalid. It must be one of the following: instance, instance-label, server, server-label or appliance")
890
+ end
891
+ target_type = val
892
+ end
893
+ opts.add_hidden_option('--target-type')
894
+ opts.on('--instance INSTANCE', String, "Instance name or id to target for execution. This option can be passed more than once.") do |val|
895
+ if target_type && target_type != 'instance'
896
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
897
+ end
868
898
  target_type = 'instance'
869
899
  instance_ids << val
870
900
  end
871
- opts.on('--instances [LIST]', Array, "Instances, comma separated list of instance names or IDs.") do |list|
901
+ opts.on('--instances LIST', Array, "Instances, comma separated list of instance names or IDs.") do |list|
902
+ if target_type && target_type != 'instance'
903
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
904
+ end
872
905
  target_type = 'instance'
873
906
  instance_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
874
907
  end
875
- opts.on('--host HOST', String, "Host name or id to execute the task on. This option can be passed more than once.") do |val|
908
+ opts.on('--instance-label LABEL', String, "Instance Label") do |val|
909
+ if target_type && target_type != 'instance-label'
910
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
911
+ end
912
+ target_type = 'instance-label'
913
+ instance_label = val
914
+ end
915
+ opts.on('--server SERVER', String, "Server name or id to target for execution. This option can be passed more than once.") do |val|
916
+ if target_type && target_type != 'server'
917
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
918
+ end
876
919
  target_type = 'server'
877
920
  server_ids << val
878
921
  end
879
- opts.on('--hosts [LIST]', Array, "Hosts, comma separated list of host names or IDs.") do |list|
922
+ opts.on('--servers LIST', Array, "Servers, comma separated list of host names or IDs.") do |list|
923
+ if target_type && target_type != 'server'
924
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
925
+ end
880
926
  target_type = 'server'
881
927
  server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
882
928
  end
883
- opts.on('--server HOST', String, "alias for --host") do |val|
929
+ opts.on('--server-label LABEL', String, "Server Label") do |val|
930
+ if target_type && target_type != 'server-label'
931
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
932
+ end
933
+ target_type = 'server-label'
934
+ server_label = val
935
+ end
936
+ opts.on('--host HOST', String, "alias for --server") do |val|
937
+ if target_type && target_type != 'server'
938
+ raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
939
+ end
884
940
  target_type = 'server'
885
941
  server_ids << val
886
942
  end
887
- opts.on('--servers [LIST]', Array, "alias for --hosts") do |list|
943
+ opts.add_hidden_option('--host')
944
+ opts.on('--hosts HOSTS', Array, "alias for --servers") do |list|
945
+ if target_type && target_type != 'server'
946
+ raise ::OptionParser::InvalidOption.new("The --hosts option cannot be combined with another context (#{target_type})")
947
+ end
888
948
  target_type = 'server'
889
949
  server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
890
950
  end
891
- opts.add_hidden_option('--server')
892
- opts.add_hidden_option('--servers')
951
+ opts.add_hidden_option('--hosts')
893
952
  opts.on('-a', '--appliance', "Execute on the appliance, the target is the appliance itself.") do
953
+ if target_type && target_type != 'appliance'
954
+ raise ::OptionParser::InvalidOption.new("The --appliance option cannot be combined with another context (#{target_type})")
955
+ end
894
956
  target_type = 'appliance'
895
957
  end
896
958
  opts.on('--config [TEXT]', String, "Custom config") do |val|
@@ -902,7 +964,7 @@ class Morpheus::Cli::Tasks
902
964
  opts.on(nil, '--no-refresh', "Do not refresh" ) do
903
965
  options[:no_refresh] = true
904
966
  end
905
- build_common_options(opts, options, [:options, :json, :dry_run, :remote])
967
+ build_standard_post_options(opts, options)
906
968
  end
907
969
  optparse.parse!(args)
908
970
  if args.count != 1
@@ -921,46 +983,59 @@ class Morpheus::Cli::Tasks
921
983
  payload = options[:payload]
922
984
  payload.deep_merge!({'job' => passed_options}) unless passed_options.empty?
923
985
  else
924
- # always parse instances and/or hosts
925
- if instance_ids.size > 0 && server_ids.size > 0
926
- raise_command_error "Pass --instance or --host, not both.\n#{optparse}"
927
- elsif instance_ids.size > 0
986
+ # prompt for target type and target
987
+ if target_type.nil?
988
+ # todo: Need api to fetch available Context Types for taskId/workflowId
989
+ available_target_types = get_available_contexts_for_task(task)
990
+ default_target_type = available_target_types.first ? available_target_types.first['name'] : nil
991
+ if !available_target_types.empty?
992
+ default_target_type = available_target_types.first ? available_target_types.first['name'] : nil
993
+ target_type = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'context-type', 'fieldName' => 'targetType', 'fieldLabel' => 'Context Type', 'type' => 'select', 'selectOptions' => available_target_types, 'defaultValue' => default_target_type, 'required' => true, 'description' => 'Context Type determines the type of target(s) for the execution'}], options[:options], @api_client)['targetType']
994
+ end
995
+ end
996
+ if target_type
997
+ params['targetType'] = target_type
998
+ end
999
+ if target_type == 'instance'
1000
+ if instance_ids.empty?
1001
+ instance_ids_value = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instances', 'fieldName' => 'instances', 'fieldLabel' => 'Instance(s)', 'type' => 'text', 'required' => true, 'description' => 'Instances, comma separated list of instance names or IDs.'}], options[:options], @api_client)['instances']
1002
+ instance_ids = parse_array(instance_ids_value)
1003
+ end
928
1004
  instance_ids.each do |instance_id|
929
1005
  instance = find_instance_by_name_or_id(instance_id)
930
1006
  return 1 if instance.nil?
931
1007
  instances << instance
932
1008
  end
933
1009
  params['instances'] = instances.collect {|it| it['id'] }
934
- elsif server_ids.size > 0
1010
+ elsif target_type == 'instance-label'
1011
+ if instance_label.nil?
1012
+ instance_label = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instance-label', 'fieldName' => 'instanceLabel', 'fieldLabel' => 'Instance Label', 'type' => 'text', 'required' => true, 'description' => 'Instance Label'}], options[:options], @api_client)['instanceLabel']
1013
+ end
1014
+ # params['config'] ||= {}
1015
+ # params['config']['instanceLabel'] = instance_label
1016
+ params['instanceLabel'] = instance_label
1017
+ elsif target_type == 'server'
1018
+ if server_ids.empty?
1019
+ server_ids_value = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'servers', 'fieldName' => 'servers', 'fieldLabel' => 'Server(s)', 'type' => 'text', 'required' => true, 'description' => 'Servers, comma separated list of server names or IDs.'}], options[:options], @api_client)['servers']
1020
+ server_ids = parse_array(server_ids_value)
1021
+ end
935
1022
  server_ids.each do |server_id|
936
1023
  server = find_server_by_name_or_id(server_id)
937
1024
  return 1 if server.nil?
938
1025
  servers << server
939
1026
  end
940
1027
  params['servers'] = servers.collect {|it| it['id'] }
941
- end
942
- # validate requires inputs based on task executeTarget
943
- if task['executeTarget'] == 'resource'
944
- if instance_ids.empty? && server_ids.empty?
945
- # todo: prompt for Context: None,Instance,Server and then Instance(s) or Server(s)
946
- raise_command_error "missing required option: --instance or --host\n#{optparse}"
1028
+ elsif target_type == 'server-label'
1029
+ if server_label.nil?
1030
+ server_label = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'server-label', 'fieldName' => 'serverLabel', 'fieldLabel' => 'Server Label', 'type' => 'text', 'required' => true, 'description' => 'Server Label'}], options[:options], @api_client)['serverLabel']
947
1031
  end
948
- elsif task['executeTarget'] == 'local'
949
- # no targetType required for local
950
- elsif task['executeTarget'] == 'remote'
951
- # not sure about this one
952
- else
953
- # unknown executeTarget
1032
+ # params['config'] ||= {}
1033
+ # params['config']['serverLabel'] = server_label
1034
+ params['serverLabel'] = server_label
954
1035
  end
955
-
956
-
957
-
958
1036
  # todo: prompt to task optionTypes for customOptions
959
1037
  if task['optionTypes']
960
1038
 
961
- end
962
- if target_type
963
- params['targetType'] = target_type
964
1039
  end
965
1040
  job_payload = {}
966
1041
  job_payload.deep_merge!(params)
@@ -1293,4 +1368,24 @@ class Morpheus::Cli::Tasks
1293
1368
  end
1294
1369
  end
1295
1370
 
1371
+ def process_special_task_option_typeaheads(option_types)
1372
+ # massage special task typeaheads options
1373
+ # this makes us all sad
1374
+ option_types.each do |option_type|
1375
+ if option_type['type'] == 'typeahead'
1376
+ if ['operationalWorkflowName','containerScript','containerTemplate'].include?(option_type['code'])
1377
+ option_type.deep_merge!({'config' => {'valueField' => 'name'}})
1378
+ end
1379
+ elsif option_type['type'] == 'hidden'
1380
+ if ['operationalWorkflowId','containerScriptId','containerTemplateId'].include?(option_type['code'])
1381
+ option_type['processValue'] = lambda {|val|
1382
+ if val.to_s.empty?
1383
+ selected_option = Morpheus::Cli::OptionTypes.get_last_select()
1384
+ selected_option ? selected_option['value'] : nil
1385
+ end
1386
+ }
1387
+ end
1388
+ end
1389
+ end
1390
+ end
1296
1391
  end