morpheus-cli 5.5.1.5 → 5.5.2

Sign up to get free protection for your applications and to get access to all the features.
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 +27 -8
  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.5"
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.5
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-09-06 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