morpheus-cli 5.5.3.2 → 6.0.1

Sign up to get free protection for your applications and to get access to all the features.
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