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.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/lib/morpheus/api/api_client.rb +25 -0
- data/lib/morpheus/api/archive_buckets_interface.rb +1 -1
- data/lib/morpheus/api/body_io.rb +22 -0
- data/lib/morpheus/api/catalog_item_types_interface.rb +5 -1
- data/lib/morpheus/api/clients_interface.rb +41 -0
- data/lib/morpheus/api/clouds_interface.rb +21 -0
- data/lib/morpheus/api/instances_interface.rb +8 -1
- data/lib/morpheus/api/integrations_interface.rb +30 -0
- data/lib/morpheus/api/library_instance_types_interface.rb +15 -3
- data/lib/morpheus/api/network_pool_server_types_interface.rb +9 -0
- data/lib/morpheus/api/plugins_interface.rb +22 -0
- data/lib/morpheus/api/roles_interface.rb +20 -1
- data/lib/morpheus/api/security_package_types_interface.rb +9 -0
- data/lib/morpheus/api/security_packages_interface.rb +9 -0
- data/lib/morpheus/api/security_scans_interface.rb +9 -0
- data/lib/morpheus/api/servers_interface.rb +17 -17
- data/lib/morpheus/api/storage_providers_interface.rb +1 -1
- data/lib/morpheus/api/virtual_images_interface.rb +1 -23
- data/lib/morpheus/cli/cli_command.rb +81 -7
- data/lib/morpheus/cli/commands/apps.rb +28 -2
- data/lib/morpheus/cli/commands/archives_command.rb +2 -2
- data/lib/morpheus/cli/commands/blueprints_command.rb +16 -0
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +34 -2
- data/lib/morpheus/cli/commands/clients_command.rb +338 -0
- data/lib/morpheus/cli/commands/clouds.rb +127 -1
- data/lib/morpheus/cli/commands/clusters.rb +42 -12
- data/lib/morpheus/cli/commands/curl_command.rb +114 -135
- data/lib/morpheus/cli/commands/hosts.rb +108 -11
- data/lib/morpheus/cli/commands/instances.rb +115 -14
- data/lib/morpheus/cli/commands/integrations_command.rb +215 -4
- data/lib/morpheus/cli/commands/invoices_command.rb +20 -11
- data/lib/morpheus/cli/commands/jobs_command.rb +299 -190
- data/lib/morpheus/cli/commands/library_cluster_layouts_command.rb +16 -2
- data/lib/morpheus/cli/commands/library_container_scripts_command.rb +14 -0
- data/lib/morpheus/cli/commands/library_container_templates_command.rb +131 -48
- data/lib/morpheus/cli/commands/library_container_types_command.rb +17 -4
- data/lib/morpheus/cli/commands/library_instance_types_command.rb +85 -7
- data/lib/morpheus/cli/commands/library_layouts_command.rb +32 -1
- data/lib/morpheus/cli/commands/library_option_lists_command.rb +30 -18
- data/lib/morpheus/cli/commands/library_option_types_command.rb +31 -14
- data/lib/morpheus/cli/commands/library_spec_templates_command.rb +14 -0
- data/lib/morpheus/cli/commands/library_upgrades_command.rb +2 -2
- data/lib/morpheus/cli/commands/network_pool_server_types.rb +20 -0
- data/lib/morpheus/cli/commands/network_pool_servers_command.rb +55 -158
- data/lib/morpheus/cli/commands/network_pools_command.rb +49 -23
- data/lib/morpheus/cli/commands/networks_command.rb +262 -45
- data/lib/morpheus/cli/commands/plugins.rb +213 -0
- data/lib/morpheus/cli/commands/price_sets_command.rb +40 -10
- data/lib/morpheus/cli/commands/prices_command.rb +17 -5
- data/lib/morpheus/cli/commands/processes_command.rb +2 -1
- data/lib/morpheus/cli/commands/remote.rb +7 -10
- data/lib/morpheus/cli/commands/roles.rb +924 -335
- data/lib/morpheus/cli/commands/search_command.rb +2 -0
- data/lib/morpheus/cli/commands/security_groups.rb +72 -84
- data/lib/morpheus/cli/commands/security_package_types.rb +32 -0
- data/lib/morpheus/cli/commands/security_packages.rb +84 -0
- data/lib/morpheus/cli/commands/security_scans.rb +107 -0
- data/lib/morpheus/cli/commands/service_plans_command.rb +16 -14
- data/lib/morpheus/cli/commands/subnets_command.rb +15 -1
- data/lib/morpheus/cli/commands/tasks.rb +34 -1
- data/lib/morpheus/cli/commands/tenants_command.rb +1 -1
- data/lib/morpheus/cli/commands/user_settings_command.rb +11 -2
- data/lib/morpheus/cli/commands/users.rb +50 -9
- data/lib/morpheus/cli/commands/virtual_images.rb +14 -0
- data/lib/morpheus/cli/commands/workflows.rb +14 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +6 -5
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +79 -0
- data/lib/morpheus/cli/mixins/jobs_helper.rb +4 -5
- data/lib/morpheus/cli/mixins/library_helper.rb +2 -0
- data/lib/morpheus/cli/mixins/logs_helper.rb +3 -0
- data/lib/morpheus/cli/mixins/monitoring_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +29 -4
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +38 -9
- data/lib/morpheus/cli/mixins/rest_command.rb +106 -8
- data/lib/morpheus/cli/mixins/secondary_rest_command.rb +6 -2
- data/lib/morpheus/cli/option_types.rb +94 -25
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/formatters.rb +10 -1
- 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
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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'] >
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
[
|
|
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
|
-
|
|
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
|
-
[
|
|
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
|
-
|
|
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 {|
|
|
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?
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
|
|
581
|
-
if
|
|
582
|
-
|
|
583
|
-
|
|
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
|
-
|
|
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']} (
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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[
|
|
1254
|
+
out << (option['isGroup'] ? "- #{option['name']}\n" : " * #{option['name']} [#{option[value_field]}]\n")
|
|
1186
1255
|
end
|
|
1187
1256
|
return out
|
|
1188
1257
|
end
|
data/lib/morpheus/cli/version.rb
CHANGED
data/lib/morpheus/formatters.rb
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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-
|
|
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
|