morpheus-cli 5.5.1.4 → 5.5.2

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +25 -0
  4. data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
  5. data/lib/morpheus/api/body_io.rb +22 -0
  6. data/lib/morpheus/api/catalog_item_types_interface.rb +5 -1
  7. data/lib/morpheus/api/clients_interface.rb +41 -0
  8. data/lib/morpheus/api/clouds_interface.rb +21 -0
  9. data/lib/morpheus/api/instances_interface.rb +8 -1
  10. data/lib/morpheus/api/integrations_interface.rb +30 -0
  11. data/lib/morpheus/api/library_instance_types_interface.rb +15 -3
  12. data/lib/morpheus/api/network_pool_server_types_interface.rb +9 -0
  13. data/lib/morpheus/api/plugins_interface.rb +22 -0
  14. data/lib/morpheus/api/roles_interface.rb +20 -1
  15. data/lib/morpheus/api/security_package_types_interface.rb +9 -0
  16. data/lib/morpheus/api/security_packages_interface.rb +9 -0
  17. data/lib/morpheus/api/security_scans_interface.rb +9 -0
  18. data/lib/morpheus/api/servers_interface.rb +17 -17
  19. data/lib/morpheus/api/storage_providers_interface.rb +1 -1
  20. data/lib/morpheus/api/virtual_images_interface.rb +1 -23
  21. data/lib/morpheus/cli/cli_command.rb +81 -7
  22. data/lib/morpheus/cli/commands/apps.rb +28 -2
  23. data/lib/morpheus/cli/commands/archives_command.rb +2 -2
  24. data/lib/morpheus/cli/commands/blueprints_command.rb +16 -0
  25. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +34 -2
  26. data/lib/morpheus/cli/commands/clients_command.rb +338 -0
  27. data/lib/morpheus/cli/commands/clouds.rb +127 -1
  28. data/lib/morpheus/cli/commands/clusters.rb +42 -12
  29. data/lib/morpheus/cli/commands/curl_command.rb +114 -135
  30. data/lib/morpheus/cli/commands/hosts.rb +108 -11
  31. data/lib/morpheus/cli/commands/instances.rb +115 -14
  32. data/lib/morpheus/cli/commands/integrations_command.rb +215 -4
  33. data/lib/morpheus/cli/commands/invoices_command.rb +20 -11
  34. data/lib/morpheus/cli/commands/jobs_command.rb +299 -190
  35. data/lib/morpheus/cli/commands/library_cluster_layouts_command.rb +16 -2
  36. data/lib/morpheus/cli/commands/library_container_scripts_command.rb +14 -0
  37. data/lib/morpheus/cli/commands/library_container_templates_command.rb +131 -48
  38. data/lib/morpheus/cli/commands/library_container_types_command.rb +17 -4
  39. data/lib/morpheus/cli/commands/library_instance_types_command.rb +85 -7
  40. data/lib/morpheus/cli/commands/library_layouts_command.rb +32 -1
  41. data/lib/morpheus/cli/commands/library_option_lists_command.rb +30 -18
  42. data/lib/morpheus/cli/commands/library_option_types_command.rb +31 -14
  43. data/lib/morpheus/cli/commands/library_spec_templates_command.rb +14 -0
  44. data/lib/morpheus/cli/commands/library_upgrades_command.rb +2 -2
  45. data/lib/morpheus/cli/commands/network_pool_server_types.rb +20 -0
  46. data/lib/morpheus/cli/commands/network_pool_servers_command.rb +55 -158
  47. data/lib/morpheus/cli/commands/network_pools_command.rb +49 -23
  48. data/lib/morpheus/cli/commands/networks_command.rb +262 -45
  49. data/lib/morpheus/cli/commands/plugins.rb +213 -0
  50. data/lib/morpheus/cli/commands/price_sets_command.rb +40 -10
  51. data/lib/morpheus/cli/commands/prices_command.rb +17 -5
  52. data/lib/morpheus/cli/commands/processes_command.rb +2 -1
  53. data/lib/morpheus/cli/commands/remote.rb +7 -10
  54. data/lib/morpheus/cli/commands/roles.rb +924 -335
  55. data/lib/morpheus/cli/commands/search_command.rb +2 -0
  56. data/lib/morpheus/cli/commands/security_groups.rb +72 -84
  57. data/lib/morpheus/cli/commands/security_package_types.rb +32 -0
  58. data/lib/morpheus/cli/commands/security_packages.rb +84 -0
  59. data/lib/morpheus/cli/commands/security_scans.rb +107 -0
  60. data/lib/morpheus/cli/commands/service_plans_command.rb +16 -14
  61. data/lib/morpheus/cli/commands/subnets_command.rb +15 -1
  62. data/lib/morpheus/cli/commands/tasks.rb +34 -1
  63. data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
  64. data/lib/morpheus/cli/commands/user_settings_command.rb +11 -2
  65. data/lib/morpheus/cli/commands/users.rb +50 -9
  66. data/lib/morpheus/cli/commands/virtual_images.rb +14 -0
  67. data/lib/morpheus/cli/commands/workflows.rb +14 -0
  68. data/lib/morpheus/cli/mixins/accounts_helper.rb +6 -5
  69. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +79 -0
  70. data/lib/morpheus/cli/mixins/jobs_helper.rb +4 -5
  71. data/lib/morpheus/cli/mixins/library_helper.rb +2 -0
  72. data/lib/morpheus/cli/mixins/logs_helper.rb +3 -0
  73. data/lib/morpheus/cli/mixins/monitoring_helper.rb +1 -1
  74. data/lib/morpheus/cli/mixins/print_helper.rb +29 -4
  75. data/lib/morpheus/cli/mixins/provisioning_helper.rb +38 -9
  76. data/lib/morpheus/cli/mixins/rest_command.rb +106 -8
  77. data/lib/morpheus/cli/mixins/secondary_rest_command.rb +6 -2
  78. data/lib/morpheus/cli/option_types.rb +94 -25
  79. data/lib/morpheus/cli/version.rb +1 -1
  80. data/lib/morpheus/formatters.rb +10 -1
  81. metadata +15 -2
@@ -57,6 +57,10 @@ module Morpheus::Cli::ProvisioningHelper
57
57
  @api_client.accounts
58
58
  end
59
59
 
60
+ def datastores_interface
61
+ @api_client.datastores
62
+ end
63
+
60
64
  def get_available_groups(params = {}, refresh=false)
61
65
  if !@available_groups || refresh
62
66
  option_results = options_interface.options_for_source('groups', params)
@@ -601,11 +605,13 @@ module Morpheus::Cli::ProvisioningHelper
601
605
  payload['instance']['instanceContext'] = v_prompt['environment'] if !v_prompt['environment'].empty?
602
606
 
603
607
  # Labels (used to be called tags)
604
- if options[:labels]
605
- payload['instance']['labels'] = options[:labels].is_a?(Array) ? options[:labels] : options[:labels].to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq
606
- else
607
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'fieldLabel' => 'Labels', 'type' => 'text', 'required' => false, 'defaultValue' => options[:default_labels]}], options[:options])
608
- payload['instance']['labels'] = v_prompt['labels'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['labels'].empty?
608
+ unless options[:skip_labels_prompt]
609
+ if options[:labels]
610
+ payload['instance']['labels'] = options[:labels].is_a?(Array) ? options[:labels] : options[:labels].to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq
611
+ else
612
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'fieldLabel' => 'Labels', 'type' => 'text', 'required' => false, 'defaultValue' => options[:default_labels]}], options[:options])
613
+ payload['instance']['labels'] = v_prompt['labels'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['labels'].empty?
614
+ end
609
615
  end
610
616
 
611
617
  # Version and Layout
@@ -634,6 +640,8 @@ module Morpheus::Cli::ProvisioningHelper
634
640
  version_value = options[:options]['version']
635
641
  else
636
642
  available_versions = options_interface.options_for_source('instanceVersions',{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})['data']
643
+ # filter versions with no layouts.. api should probably do that too eh?
644
+ available_versions.reject! { |available_version| available_version["layouts"].nil? || available_version["layouts"].empty? }
637
645
  default_version_value = payload['instance']['version'] ? payload['instance']['version'] : payload['version']
638
646
  #default_layout_value = options[:layout]
639
647
  if default_layout_value.nil?
@@ -953,6 +961,14 @@ module Morpheus::Cli::ProvisioningHelper
953
961
  api_params['config'] = payload['config'] if payload['config']
954
962
  api_params['poolId'] = payload['config']['resourcePoolId'] if payload['config'] && payload['config']['resourcePoolId']
955
963
  api_params['resourcePoolId'] = api_params['poolId']
964
+ # some option sources expect networkIntefaces passed as networkInterfaceIds[] eg. oraclecloudAvailabilityDomains
965
+ if payload['networkInterfaces']
966
+ begin
967
+ api_params['networkInterfaceIds[]'] = payload['networkInterfaces'].collect {|it| it['network']['id'] }
968
+ rescue => netex
969
+ Morpheus::Logging::DarkPrinter.puts "Unable to parse networkInterfaces parameter" if Morpheus::Logging.debug?
970
+ end
971
+ end
956
972
 
957
973
  # set option type defaults from config
958
974
  if options[:default_config]
@@ -1338,7 +1354,7 @@ module Morpheus::Cli::ProvisioningHelper
1338
1354
  'id' => current_root_volume['id'],
1339
1355
  'rootVolume' => true,
1340
1356
  'name' => current_root_volume['name'],
1341
- 'size' => current_root_volume['size'] > (plan_size || 0) ? current_root_volume['size'] : plan_size,
1357
+ 'size' => current_root_volume['size'] > 0 ? current_root_volume['size'] : plan_size,
1342
1358
  'sizeId' => nil,
1343
1359
  'storageType' => storage_type_id,
1344
1360
  'datastoreId' => current_root_volume['datastoreId']
@@ -1464,9 +1480,11 @@ module Morpheus::Cli::ProvisioningHelper
1464
1480
  while add_another_volume do
1465
1481
  #puts "Configure Data #{volume_index} Volume"
1466
1482
 
1483
+ current_root_volume_type = current_root_volume['type']
1484
+ storage_type_match = storage_types.find {|type| type['value'] == current_root_volume_type}
1485
+ default_storage_type = storage_type_match ? current_root_volume_type : storage_types[0]['value']
1467
1486
  field_context = "dataVolume#{volume_index}"
1468
-
1469
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1487
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'defaultValue' => default_storage_type, 'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1470
1488
  storage_type_id = v_prompt[field_context]['storageType']
1471
1489
  storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1472
1490
 
@@ -1510,8 +1528,19 @@ module Morpheus::Cli::ProvisioningHelper
1510
1528
  # volume['size'] = plan_size
1511
1529
  # volume['sizeId'] = nil #volume.delete('sizeId')
1512
1530
  end
1531
+
1532
+ if datastore_options.empty? && storage_type['hasDatastore'] != false
1533
+ begin
1534
+ datastore_res = datastores_interface.list({'resourcePoolId' => current_root_volume['resourcePoolId'], 'zoneId' => options['zoneId'], 'siteId' => options['siteId']})['datastores']
1535
+ datastore_res.each do |opt|
1536
+ datastore_options << {'name' => opt['name'], 'value' => opt['id']}
1537
+ end
1538
+ rescue
1539
+ datastore_options = []
1540
+ end
1541
+ end
1513
1542
  if !datastore_options.empty?
1514
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Datastore", 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.'}], options[:options])
1543
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'defaultValue' => current_root_volume['datastoreId'],'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Datastore", 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.'}], options[:options])
1515
1544
  volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1516
1545
  end
1517
1546
 
@@ -425,10 +425,6 @@ module Morpheus::Cli::RestCommand
425
425
  self.class.rest_type_arg
426
426
  end
427
427
 
428
- def rest_has_name
429
- self.class.rest_type_arg
430
- end
431
-
432
428
  def rest_type_label
433
429
  self.class.rest_type_label
434
430
  end
@@ -524,7 +520,10 @@ EOT
524
520
  json_response = rest_interface.list(params)
525
521
  render_response(json_response, options, rest_list_key) do
526
522
  records = json_response[rest_list_key]
527
- print_h1 "Morpheus #{rest_label_plural}"
523
+ title = "Morpheus #{rest_label_plural}"
524
+ subtitles = []
525
+ subtitles += parse_list_subtitles(options)
526
+ print_h1 title, subtitles, options
528
527
  if records.nil? || records.empty?
529
528
  print cyan,"No #{rest_label_plural.downcase} found.",reset,"\n"
530
529
  else
@@ -586,7 +585,7 @@ EOT
586
585
  # print_description_list(config.keys, config)
587
586
  # end
588
587
  # Option Types
589
- if record['optionTypes'] && record['optionTypes'].size > 0
588
+ if record['optionTypes'] && record['optionTypes'].sort { |x,y| x['displayOrder'].to_i <=> y['displayOrder'].to_i }.size > 0
590
589
  print_h2 "Option Types", options
591
590
  print format_option_types_table(record['optionTypes'], options, rest_object_key)
592
591
  end
@@ -603,7 +602,11 @@ EOT
603
602
  advanced_option_types = respond_to?("add_#{rest_key}_advanced_option_types", true) ? send("add_#{rest_key}_advanced_option_types") : []
604
603
  type_option_type = option_types.find {|it| it['fieldName'] == 'type'}
605
604
  optparse = Morpheus::Cli::OptionParser.new do |opts|
606
- opts.banner = subcommand_usage("[#{rest_arg}]")
605
+ if rest_has_name
606
+ opts.banner = subcommand_usage("[name]")
607
+ else
608
+ opts.banner = subcommand_usage()
609
+ end
607
610
  if rest_has_type && type_option_type.nil?
608
611
  opts.on( '-t', "--#{rest_type_arg} TYPE", "#{rest_type_label}" ) do |val|
609
612
  record_type_id = val
@@ -614,7 +617,7 @@ EOT
614
617
  build_standard_add_options(opts, options)
615
618
  opts.footer = <<-EOT
616
619
  Create a new #{rest_label.downcase}.
617
- [#{rest_arg}] is required. This is the name of the new #{rest_label.downcase}.
620
+ [name] is required. This is the name of the new #{rest_label.downcase}.
618
621
  EOT
619
622
  opts.footer += send "add_#{rest_key}_footer_addn" if respond_to?("add_#{rest_key}_footer_addn", true)
620
623
  end
@@ -888,5 +891,100 @@ EOT
888
891
  return 0, nil
889
892
  end
890
893
 
894
+ def list_types(args)
895
+ params = {}
896
+ options = {}
897
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
898
+ opts.banner = subcommand_usage("[search]")
899
+ build_list_options(opts, options, params)
900
+ opts.footer = <<-EOT
901
+ List #{rest_type_label_plural.downcase}.
902
+ [search] is optional. This is a search phrase to filter the results.
903
+ EOT
904
+ end
905
+ optparse.parse!(args)
906
+ connect(options)
907
+ parse_list_options!(args, options, params)
908
+ rest_type_interface.setopts(options)
909
+ if options[:dry_run]
910
+ print_dry_run rest_type_interface.dry.list(params)
911
+ return
912
+ end
913
+ json_response = rest_type_interface.list(params)
914
+ render_response(json_response, options, rest_type_list_key) do
915
+ records = json_response[rest_type_list_key]
916
+ title = "Morpheus #{rest_type_label_plural}"
917
+ subtitles = []
918
+ subtitles += parse_list_subtitles(options)
919
+ print_h1 title, subtitles, options
920
+ if records.nil? || records.empty?
921
+ print cyan,"No #{rest_type_label_plural.downcase} found.",reset,"\n"
922
+ else
923
+ print as_pretty_table(records, rest_type_list_column_definitions(options).upcase_keys!, options)
924
+ print_results_pagination(json_response) if json_response['meta']
925
+ end
926
+ print reset,"\n"
927
+ end
928
+ return 0, nil
929
+ end
930
+
931
+ def get_type(args)
932
+ params = {}
933
+ options = {}
934
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
935
+ opts.banner = subcommand_usage("[#{rest_type_arg}]")
936
+ build_get_options(opts, options, params)
937
+ opts.footer = <<-EOT
938
+ Get details about #{a_or_an(rest_type_label)} #{rest_type_label.downcase}.
939
+ [#{rest_type_arg}] is required. This is the name or id of #{a_or_an(rest_type_label)} #{rest_type_label.downcase}.
940
+ EOT
941
+ end
942
+ optparse.parse!(args)
943
+ verify_args!(args:args, optparse:optparse, min:1)
944
+ connect(options)
945
+ parse_get_options!(args, options, params)
946
+ id = args.join(" ")
947
+ _get_type(id, params, options)
948
+ end
949
+
950
+ def _get_type(id, params, options)
951
+ if id !~ /\A\d{1,}\Z/ # && rest_type_has_name
952
+ record = rest_type_find_by_name_or_id(id)
953
+ if record.nil?
954
+ return 1, "#{rest_type_label} not found for '#{id}'"
955
+ end
956
+ id = record['id']
957
+ end
958
+ rest_type_interface.setopts(options)
959
+ if options[:dry_run]
960
+ print_dry_run rest_type_interface.dry.get(id, params)
961
+ return
962
+ end
963
+ json_response = rest_type_interface.get(id, params)
964
+ render_response_for_get_type(json_response, options)
965
+ return 0, nil
966
+ end
967
+
968
+ def render_response_for_get_type(json_response, options)
969
+ render_response(json_response, options, rest_type_object_key) do
970
+ record = json_response[rest_type_object_key]
971
+ print_h1 rest_type_label, [], options
972
+ print cyan
973
+ print_description_list(rest_type_column_definitions(options), record, options)
974
+ # # could always show config eh? or maybe only with --config if that is nicer.
975
+ # # config = record['config'].is_a?(Hash) && !record['config'].empty?
976
+ # if config && !config.empty?
977
+ # print_h2 "Configuration"
978
+ # print_description_list(config.keys, config)
979
+ # end
980
+ # Option Types
981
+ if record['optionTypes'] && record['optionTypes'].sort { |x,y| x['displayOrder'].to_i <=> y['displayOrder'].to_i }.size > 0
982
+ print_h2 "Option Types", options
983
+ print format_option_types_table(record['optionTypes'], options, rest_object_key)
984
+ end
985
+ print reset,"\n"
986
+ end
987
+ end
988
+
891
989
  end
892
990
 
@@ -369,7 +369,11 @@ EOT
369
369
  advanced_option_types = respond_to?("add_#{rest_key}_advanced_option_types", true) ? send("add_#{rest_key}_advanced_option_types") : []
370
370
  type_option_type = option_types.find {|it| it['fieldName'] == 'type'}
371
371
  optparse = Morpheus::Cli::OptionParser.new do |opts|
372
- opts.banner = subcommand_usage("[#{rest_parent_arg}] [#{rest_arg}]")
372
+ if rest_has_name
373
+ opts.banner = subcommand_usage("[#{rest_parent_arg}] [name]")
374
+ else
375
+ opts.banner = subcommand_usage("[#{rest_parent_arg}]")
376
+ end
373
377
  if rest_has_type && type_option_type.nil?
374
378
  opts.on( '-t', "--#{rest_type_arg} TYPE", "#{rest_type_label}" ) do |val|
375
379
  record_type_id = val
@@ -381,7 +385,7 @@ EOT
381
385
  opts.footer = <<-EOT
382
386
  Create a new #{rest_label.downcase}.
383
387
  [#{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}.
384
- [#{rest_arg}] is required. This is the name of the new #{rest_label.downcase}.
388
+ [name] is required. This is the name of the new #{rest_label.downcase}.
385
389
  EOT
386
390
  end
387
391
  optparse.parse!(args)
@@ -75,6 +75,10 @@ module Morpheus
75
75
 
76
76
  # puts "Options Prompt #{options}"
77
77
  # Sort options by default, group, advanced
78
+ # add displayOrder if it's missing, so it doesn't end up using a random order
79
+ if !option_types.find {|it| it['displayOrder'] }
80
+ option_types.each_with_index {|it, i| it['displayOrder'] = i+1 }
81
+ end
78
82
  cur_field_group = 'default'
79
83
  prompt_local_credentials = true
80
84
  self.sort_option_types(option_types.reject {|it| it[:for_help_only]}).each do |option_type|
@@ -86,7 +90,7 @@ module Morpheus
86
90
 
87
91
  if cur_field_group != field_group
88
92
  cur_field_group = field_group
89
- if !no_prompt
93
+ if !no_prompt && option_type['noPrompt'] != true
90
94
  print "\n#{cur_field_group.upcase} OPTIONS\n#{"=" * ("#{cur_field_group} OPTIONS".length)}\n\n"
91
95
  end
92
96
  end
@@ -114,8 +118,11 @@ module Morpheus
114
118
  if !option_type['visibleOnCode'].to_s.empty?
115
119
  visible_option_check_value = option_type['visibleOnCode']
116
120
  end
117
-
118
- if !visible_option_check_value.to_s.empty?
121
+ # adding a slight hack for dependencies on the network component since its not an option type
122
+ extra_option_params = {}
123
+ if visible_option_check_value == 'networkInterfaces'
124
+ # extra_option_params["networkInterfaceIds[]"] = []
125
+ elsif !visible_option_check_value.to_s.empty?
119
126
  match_type = 'any'
120
127
 
121
128
  if visible_option_check_value.include?('::')
@@ -161,6 +168,7 @@ module Morpheus
161
168
  # build parameters for option source api request
162
169
  option_params = (option_type['noParams'] ? {} : (api_params || {}).deep_merge(results))
163
170
  option_params.merge!(option_type['optionParams']) if option_type['optionParams']
171
+ # option_params.merge!(extra_option_params) if extra_option_params && !extra_option_params.empty?
164
172
 
165
173
  cur_namespace = options
166
174
  parent_context_map = context_map
@@ -243,7 +251,7 @@ module Morpheus
243
251
  end
244
252
  # no_prompt means skip prompting and instead
245
253
  # use default value or error if a required option is not present
246
- if no_prompt
254
+ if no_prompt || option_type['noPrompt'] == true
247
255
  if !value_found
248
256
  if option_type['defaultValue'] != nil && !['select', 'multiSelect','typeahead','multiTypeahead'].include?(option_type['type'])
249
257
  value = option_type['defaultValue']
@@ -356,6 +364,11 @@ module Morpheus
356
364
  end
357
365
  end
358
366
 
367
+ # --labels x,y,z uses processValue proc to convert strings to an array
368
+ if option_type['processValue'].is_a?(Proc)
369
+ value = option_type['processValue'].call(value)
370
+ end
371
+
359
372
  if option_type['type'] == 'multiSelect'
360
373
  value = [value] if !value.nil? && !value.is_a?(Array)
361
374
  elsif option_type['type'] == 'multiText'
@@ -474,7 +487,22 @@ module Morpheus
474
487
 
475
488
  # ensure the preselected value (passed as an option) is in the dropdown
476
489
  if !use_value.nil?
477
- matched_option = select_options.find {|opt| opt[value_field].to_s == use_value.to_s || opt['name'].to_s == use_value.to_s }
490
+ matched_option = select_options.find {|it| it[value_field].to_s == use_value.to_s }
491
+ if matched_option.nil?
492
+ matched_options = select_options.select {|it| opt['name'].to_s == use_value.to_s }
493
+ if matched_options.size > 1
494
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{use_value}]\n\n", Term::ANSIColor.reset
495
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
496
+ display_select_options(option_type, matched_options)
497
+ print "The value '#{input}' matched #{matched_options.size()} options.\n"
498
+ # print "Perhaps you meant one of these? #{ored_list(matched_options.collect {|i|i[value_field]}, 3)}\n"
499
+ print "Try using value instead of name.\n"
500
+ print "\n"
501
+ exit 1
502
+ elsif matched_options.size == 1
503
+ matched_option = matched_options[0]
504
+ end
505
+ end
478
506
  if !matched_option.nil?
479
507
  value = matched_option[value_field]
480
508
  value_found = true
@@ -522,10 +550,39 @@ module Morpheus
522
550
 
523
551
  if no_prompt
524
552
  if !value_found
525
- if !default_value.nil? && !select_options.nil? && select_options.find {|it| it['name'] == default_value || it[value_field].to_s == default_value.to_s}
526
- value_found = true
527
- value = select_options.find {|it| it['name'] == default_value || it[value_field].to_s == default_value.to_s}[value_field]
528
- elsif !select_options.nil? && select_options.count > 1 && option_type['autoPickOption'] == true
553
+ if !default_value.nil? && !select_options.nil?
554
+ matched_option = select_options.find {|it| it[value_field].to_s == default_value.to_s }
555
+ if matched_option.nil?
556
+ matched_options = select_options.select {|it| it['name'].to_s == default_value.to_s }
557
+ if matched_options.size > 1
558
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{default_value}]\n\n", Term::ANSIColor.reset
559
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
560
+ display_select_options(option_type, matched_options)
561
+ print "The value '#{default_value}' matched #{matched_options.size()} options.\n"
562
+ # print "Perhaps you meant one of these? #{ored_list(matched_options.collect {|i|i[value_field]}, 3)}\n"
563
+ print "Try using value instead of name.\n"
564
+ print "\n"
565
+ exit 1
566
+ elsif matched_options.size == 1
567
+ matched_option = matched_options[0]
568
+ end
569
+ end
570
+ if !matched_option.nil?
571
+ value = matched_option[value_field]
572
+ value_found = true
573
+ else
574
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{default_value}]\n\n", Term::ANSIColor.reset
575
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
576
+ if select_options && select_options.size > 10
577
+ display_select_options(option_type, select_options.first(10))
578
+ puts " (#{select_options.size-10} more)"
579
+ else
580
+ display_select_options(option_type, select_options)
581
+ end
582
+ print "\n"
583
+ exit 1
584
+ end
585
+ elsif !select_options.nil? && select_options.count > 0 && option_type['autoPickOption'] == true
529
586
  value_found = true
530
587
  value = select_options[0][value_field]
531
588
  elsif option_type['required']
@@ -577,10 +634,26 @@ module Morpheus
577
634
  if input.empty? && default_value
578
635
  input = default_value.to_s
579
636
  end
580
- select_option = select_options.find{|b| b['name'] == input || (!b['value'].nil? && b['value'].to_s == input) || (!b[value_field].nil? && b[value_field].to_s == input) || (b[value_field].nil? && input.empty?)}
581
- if select_option
582
- value = select_option[value_field]
583
- set_last_select(select_option)
637
+ matched_option = select_options.find{|it| (!it['value'].nil? && it['value'].to_s == input) || (!it[value_field].nil? && it[value_field].to_s == input) || (it[value_field].nil? && input.empty?)}
638
+ if matched_option.nil?
639
+ matched_options = select_options.select {|it| it['name'] == input } # should probably be case insensitive
640
+ if matched_options.size > 1
641
+ print Term::ANSIColor.red, "\nInvalid Option #{option_type['fieldLabel']}: [#{input}]\n\n", Term::ANSIColor.reset
642
+ print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{option_type['fieldContext'] ? (option_type['fieldContext']+'.') : ''}#{option_type['fieldName']}=] - #{option_type['description']}\n", Term::ANSIColor.reset
643
+ display_select_options(option_type, matched_options)
644
+ print "The value '#{input}' matched #{matched_options.size()} options.\n"
645
+ # print "Perhaps you meant one of these? #{ored_list(matched_options.collect {|i|i[value_field]}, 3)}\n"
646
+ print "Try using value instead of name.\n"
647
+ print "\n"
648
+ exit 1
649
+ elsif matched_options.size == 1
650
+ matched_option = matched_options[0]
651
+ end
652
+ end
653
+
654
+ if matched_option
655
+ value = matched_option[value_field]
656
+ set_last_select(matched_option)
584
657
  elsif !input.nil? && !input.to_s.empty?
585
658
  input = '?'
586
659
  end
@@ -780,32 +853,28 @@ module Morpheus
780
853
  value
781
854
  end
782
855
 
783
- # this is a funky one, the user is prompted for yes/no
784
- # but the return value is 'on','off',nil
785
- # todo: maybe make this easier to use, and have the api's be flexible too..
786
- # @param option_type [Hash] option type object with type,fieldName,fieldLabel,etc..
787
- # @return 'on', 'off' or nil
788
856
  def self.checkbox_prompt(option_type)
789
857
  value_found = false
790
858
  value = nil
791
859
  has_default = option_type['defaultValue'] != nil
792
- default_yes = has_default ? ['on', 'true', 'yes', '1'].include?(option_type['defaultValue'].to_s.downcase) : false
860
+ default_on = has_default ? ['on', 'true', 'yes', '1'].include?(option_type['defaultValue'].to_s.downcase) : false
793
861
  while !value_found do
794
- print "#{option_type['fieldLabel']} (yes/no)#{has_default ? ' ['+(default_yes ? 'yes' : 'no')+']' : ''}: "
862
+ print "#{option_type['fieldLabel']} (on/off)#{has_default ? ' ['+(default_on ? 'on' : 'off')+']' : ''}: "
795
863
  input = $stdin.gets.chomp!
796
864
  if input == '?'
797
865
  help_prompt(option_type)
798
866
  next
799
867
  end
800
- if input.downcase == 'yes' || input.downcase == 'y' || input.downcase == 'on'
868
+ check_value = input.downcase.strip
869
+ if check_value == 'yes' || check_value == 'y' || check_value == 'on' || check_value == 'true' || check_value == '1'
801
870
  value_found = true
802
871
  value = 'on'
803
- elsif input.downcase == 'no' || input.downcase == 'n' || input.downcase == 'off'
872
+ elsif check_value == 'no' || check_value == 'n' || check_value == 'off' || check_value == 'true' || check_value == '0'
804
873
  value_found = true
805
874
  value = 'off'
806
875
  elsif input == '' && has_default
807
876
  value_found = true
808
- value = default_yes ? 'on' : 'off'
877
+ value = default_on ? 'on' : 'off'
809
878
  elsif input != ""
810
879
  puts "Invalid Option... Please try again."
811
880
  next
@@ -1170,6 +1239,7 @@ module Morpheus
1170
1239
  def self.format_select_options_help(opt, select_options = [], paging = nil)
1171
1240
  out = ""
1172
1241
  header = opt['fieldLabel'] ? "#{opt['fieldLabel']} Options" : "Options"
1242
+ value_field = (opt['config'] ? opt['config']['valueField'] : nil) || 'value'
1173
1243
  if paging
1174
1244
  offset = paging[:cur_page] * paging[:page_size]
1175
1245
  limit = [offset + paging[:page_size], select_options.count].min - 1
@@ -1180,9 +1250,8 @@ module Morpheus
1180
1250
  out << "\n"
1181
1251
  out << "#{header}\n"
1182
1252
  out << "#{'=' * header.length}\n"
1183
-
1184
1253
  select_options.each do |option|
1185
- out << (option['isGroup'] ? "- #{option['name']}\n" : " * #{option['name']} [#{option['value']}]\n")
1254
+ out << (option['isGroup'] ? "- #{option['name']}\n" : " * #{option['name']} [#{option[value_field]}]\n")
1186
1255
  end
1187
1256
  return out
1188
1257
  end
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "5.5.1.4"
4
+ VERSION = "5.5.2"
5
5
  end
6
6
  end
@@ -1,6 +1,7 @@
1
1
  require 'time'
2
2
  require 'filesize'
3
3
  require 'money'
4
+ require 'cgi'
4
5
 
5
6
  DEFAULT_DATE_FORMAT = "%x"
6
7
  DEFAULT_TIME_FORMAT = "%x %I:%M %p"
@@ -366,7 +367,11 @@ def format_number(n, opts={})
366
367
  decimal = parts[1] ? parts[1..-1].join('.') : nil
367
368
  i = 0
368
369
  whole_number.reverse.each_char do |c|
369
- out = (i > 0 && i % 3 == 0) ? "#{c}#{delim}#{out}" : "#{c}#{out}"
370
+ if c == "-"
371
+ out = "#{c}#{out}"
372
+ else
373
+ out = (i > 0 && i % 3 == 0) ? "#{c}#{delim}#{out}" : "#{c}#{out}"
374
+ end
370
375
  i+= 1
371
376
  end
372
377
  if decimal
@@ -488,3 +493,7 @@ def format_ok_status(status)
488
493
  end
489
494
  "#{color}#{status.to_s.upcase}#{cyan}"
490
495
  end
496
+
497
+ def escape_filepath(filepath)
498
+ filepath.to_s.split("/").select {|it| !it.to_s.empty? }.collect {|it| CGI::escape(it) }.join("/")
499
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: morpheus-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.1.4
4
+ version: 5.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Estes
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2022-08-09 00:00:00.000000000 Z
14
+ date: 2022-11-22 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -191,10 +191,12 @@ files:
191
191
  - lib/morpheus/api/backups_interface.rb
192
192
  - lib/morpheus/api/billing_interface.rb
193
193
  - lib/morpheus/api/blueprints_interface.rb
194
+ - lib/morpheus/api/body_io.rb
194
195
  - lib/morpheus/api/budgets_interface.rb
195
196
  - lib/morpheus/api/catalog_item_types_interface.rb
196
197
  - lib/morpheus/api/certificate_types_interface.rb
197
198
  - lib/morpheus/api/certificates_interface.rb
199
+ - lib/morpheus/api/clients_interface.rb
198
200
  - lib/morpheus/api/cloud_datastores_interface.rb
199
201
  - lib/morpheus/api/cloud_folders_interface.rb
200
202
  - lib/morpheus/api/cloud_policies_interface.rb
@@ -264,6 +266,7 @@ files:
264
266
  - lib/morpheus/api/network_edge_clusters_interface.rb
265
267
  - lib/morpheus/api/network_groups_interface.rb
266
268
  - lib/morpheus/api/network_pool_ips_interface.rb
269
+ - lib/morpheus/api/network_pool_server_types_interface.rb
267
270
  - lib/morpheus/api/network_pool_servers_interface.rb
268
271
  - lib/morpheus/api/network_pools_interface.rb
269
272
  - lib/morpheus/api/network_proxies_interface.rb
@@ -280,6 +283,7 @@ files:
280
283
  - lib/morpheus/api/options_interface.rb
281
284
  - lib/morpheus/api/packages_interface.rb
282
285
  - lib/morpheus/api/ping_interface.rb
286
+ - lib/morpheus/api/plugins_interface.rb
283
287
  - lib/morpheus/api/policies_interface.rb
284
288
  - lib/morpheus/api/power_schedules_interface.rb
285
289
  - lib/morpheus/api/price_sets_interface.rb
@@ -300,6 +304,9 @@ files:
300
304
  - lib/morpheus/api/secondary_rest_interface.rb
301
305
  - lib/morpheus/api/security_group_rules_interface.rb
302
306
  - lib/morpheus/api/security_groups_interface.rb
307
+ - lib/morpheus/api/security_package_types_interface.rb
308
+ - lib/morpheus/api/security_packages_interface.rb
309
+ - lib/morpheus/api/security_scans_interface.rb
303
310
  - lib/morpheus/api/server_types_interface.rb
304
311
  - lib/morpheus/api/servers_interface.rb
305
312
  - lib/morpheus/api/service_catalog_interface.rb
@@ -355,6 +362,7 @@ files:
355
362
  - lib/morpheus/cli/commands/catalog_item_types_command.rb
356
363
  - lib/morpheus/cli/commands/certificates_command.rb
357
364
  - lib/morpheus/cli/commands/change_password_command.rb
365
+ - lib/morpheus/cli/commands/clients_command.rb
358
366
  - lib/morpheus/cli/commands/cloud_datastores_command.rb
359
367
  - lib/morpheus/cli/commands/cloud_folders_command.rb
360
368
  - lib/morpheus/cli/commands/cloud_resource_pools_command.rb
@@ -429,6 +437,7 @@ files:
429
437
  - lib/morpheus/cli/commands/network_edge_clusters_command.rb
430
438
  - lib/morpheus/cli/commands/network_firewalls_command.rb
431
439
  - lib/morpheus/cli/commands/network_groups_command.rb
440
+ - lib/morpheus/cli/commands/network_pool_server_types.rb
432
441
  - lib/morpheus/cli/commands/network_pool_servers_command.rb
433
442
  - lib/morpheus/cli/commands/network_pools_command.rb
434
443
  - lib/morpheus/cli/commands/network_proxies_command.rb
@@ -441,6 +450,7 @@ files:
441
450
  - lib/morpheus/cli/commands/options.rb
442
451
  - lib/morpheus/cli/commands/packages_command.rb
443
452
  - lib/morpheus/cli/commands/ping.rb
453
+ - lib/morpheus/cli/commands/plugins.rb
444
454
  - lib/morpheus/cli/commands/policies_command.rb
445
455
  - lib/morpheus/cli/commands/power_schedules_command.rb
446
456
  - lib/morpheus/cli/commands/preseed_scripts_command.rb
@@ -459,6 +469,9 @@ files:
459
469
  - lib/morpheus/cli/commands/search_command.rb
460
470
  - lib/morpheus/cli/commands/security_group_rules.rb
461
471
  - lib/morpheus/cli/commands/security_groups.rb
472
+ - lib/morpheus/cli/commands/security_package_types.rb
473
+ - lib/morpheus/cli/commands/security_packages.rb
474
+ - lib/morpheus/cli/commands/security_scans.rb
462
475
  - lib/morpheus/cli/commands/service_catalog_command.rb
463
476
  - lib/morpheus/cli/commands/service_plans_command.rb
464
477
  - lib/morpheus/cli/commands/set_prompt_command.rb