morpheus-cli 5.4.2 → 5.4.3
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/lib/morpheus/api/api_client.rb +4 -1
- data/lib/morpheus/api/catalog_item_types_interface.rb +20 -0
- data/lib/morpheus/api/instances_interface.rb +28 -0
- data/lib/morpheus/api/ping_interface.rb +2 -0
- data/lib/morpheus/api/setup_interface.rb +4 -0
- data/lib/morpheus/api/snapshots_interface.rb +19 -0
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +88 -0
- data/lib/morpheus/cli/commands/clusters.rb +59 -46
- data/lib/morpheus/cli/commands/hosts.rb +12 -0
- data/lib/morpheus/cli/commands/instances.rb +250 -1
- data/lib/morpheus/cli/commands/library_instance_types_command.rb +3 -0
- data/lib/morpheus/cli/commands/network_static_routes_command.rb +5 -0
- data/lib/morpheus/cli/commands/networks_command.rb +2 -2
- data/lib/morpheus/cli/commands/ping.rb +3 -5
- data/lib/morpheus/cli/commands/policies_command.rb +1 -1
- data/lib/morpheus/cli/commands/provisioning_settings_command.rb +1 -0
- data/lib/morpheus/cli/commands/remote.rb +16 -10
- data/lib/morpheus/cli/commands/security_groups.rb +2 -2
- data/lib/morpheus/cli/commands/service_plans_command.rb +1 -1
- data/lib/morpheus/cli/commands/setup.rb +1 -1
- data/lib/morpheus/cli/commands/snapshots.rb +139 -0
- data/lib/morpheus/cli/commands/tasks.rb +5 -5
- data/lib/morpheus/cli/commands/user_settings_command.rb +1 -1
- data/lib/morpheus/cli/commands/virtual_images.rb +4 -1
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +80 -0
- data/lib/morpheus/cli/option_types.rb +26 -11
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +4 -2
@@ -803,7 +803,7 @@ EOT
|
|
803
803
|
{'fieldName' => 'windowsPassword', 'fieldLabel' => 'Windows Password', 'type' => 'password'},
|
804
804
|
{'fieldName' => 'defaultGroup', 'fieldLabel' => 'Default Group ID', 'type' => 'text'},
|
805
805
|
{'fieldName' => 'defaultCloud', 'fieldLabel' => 'Default Cloud ID', 'type' => 'text'},
|
806
|
-
{'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona Name or Code or ID', 'type' => 'text'},
|
806
|
+
{'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona Name or Code or ID eg. standard, serviceCatalog or vdi', 'type' => 'text'},
|
807
807
|
{'switch' => 'change-password', 'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => 'Change user credentials to use a new password'},
|
808
808
|
{'fieldName' => 'avatar', 'fieldLabel' => 'Avatar', 'type' => 'file', 'description' => 'Local filepath of image file to upload as user avatar'},
|
809
809
|
{'fieldName' => 'desktopBackground', 'fieldLabel' => 'Desktop Background', 'type' => 'file', 'description' => 'Local filepath of image file to upload as user desktop background'},
|
@@ -38,6 +38,9 @@ class Morpheus::Cli::VirtualImages
|
|
38
38
|
end
|
39
39
|
opts.on('--system', "System Images" ) do
|
40
40
|
options[:filterType] = 'System'
|
41
|
+
end
|
42
|
+
opts.on('--synced', "Synced Images" ) do
|
43
|
+
options[:filterType] = 'Synced'
|
41
44
|
end
|
42
45
|
opts.on('--tags Name=Value',String, "Filter by tags (metadata name value pairs).") do |val|
|
43
46
|
val.split(",").each do |value_pair|
|
@@ -51,7 +54,7 @@ class Morpheus::Cli::VirtualImages
|
|
51
54
|
options[:details] = true
|
52
55
|
end
|
53
56
|
build_standard_list_options(opts, options)
|
54
|
-
opts.footer = "List virtual images."
|
57
|
+
opts.footer = "List virtual images. Default list applies User filter"
|
55
58
|
end
|
56
59
|
optparse.parse!(args)
|
57
60
|
connect(options)
|
@@ -870,6 +870,12 @@ module Morpheus::Cli::ProvisioningHelper
|
|
870
870
|
end
|
871
871
|
end
|
872
872
|
|
873
|
+
# plan customizations
|
874
|
+
plan_opts = prompt_service_plan_options(service_plan, options, api_client, {zoneId: cloud_id, layoutId: layout['id'], siteId: group_id})
|
875
|
+
if plan_opts && !plan_opts.empty?
|
876
|
+
payload['servicePlanOptions'] = plan_opts
|
877
|
+
end
|
878
|
+
|
873
879
|
# prompt networks
|
874
880
|
if locked_fields.include?('networks')
|
875
881
|
# payload['networkInterfaces'] = options[:options]['networkInterfaces'] if options[:options]['networkInterfaces']
|
@@ -2240,6 +2246,80 @@ module Morpheus::Cli::ProvisioningHelper
|
|
2240
2246
|
return ports
|
2241
2247
|
end
|
2242
2248
|
|
2249
|
+
def prompt_service_plan_options(plan_info, options={}, api_client=nil, api_params={}, instance=nil)
|
2250
|
+
plan_opts = {}
|
2251
|
+
# provisioning with blueprint can lock fields
|
2252
|
+
locked_fields = options[:locked_fields] || []
|
2253
|
+
if options[:options]['servicePlanOptions']
|
2254
|
+
plan_opts = options[:options]['servicePlanOptions']
|
2255
|
+
end
|
2256
|
+
default_max_cores = plan_info['maxCores'].to_i != 0 ? plan_info['maxCores'] : 1
|
2257
|
+
default_cores_per_socket = plan_info['coresPerSocket'].to_i != 0 ? plan_info['coresPerSocket'] : 1
|
2258
|
+
default_max_memory = plan_info['maxMemory'].to_i != 0 ? plan_info['maxMemory'] : nil
|
2259
|
+
# use defaults from the instance/server
|
2260
|
+
if instance
|
2261
|
+
default_max_cores = instance["maxCores"] if instance["maxCores"]
|
2262
|
+
default_cores_per_socket = instance["coresPerSocket"] if instance["coresPerSocket"]
|
2263
|
+
default_max_memory = instance["maxMemory"] if instance["maxMemory"]
|
2264
|
+
end
|
2265
|
+
# Core Count
|
2266
|
+
if plan_info["customCores"]
|
2267
|
+
if locked_fields.include?('servicePlanOptions.maxCores')
|
2268
|
+
if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxCores']
|
2269
|
+
plan_opts['maxCores'] = options[:options]['servicePlanOptions']['maxCores'].to_i
|
2270
|
+
end
|
2271
|
+
else
|
2272
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'servicePlanOptions', 'fieldName' => 'maxCores', 'type' => 'number', 'fieldLabel' => "Core Count", 'required' => true, 'defaultValue' => default_max_cores, 'description' => "Customize service plan options Core Count"}], options[:options])
|
2273
|
+
if v_prompt['servicePlanOptions'] && v_prompt['servicePlanOptions']['maxCores']
|
2274
|
+
plan_opts['maxCores'] = v_prompt['servicePlanOptions']['maxCores'].to_i
|
2275
|
+
end
|
2276
|
+
end
|
2277
|
+
end
|
2278
|
+
# Cores Per Socket
|
2279
|
+
if plan_info["customCoresPerSocket"]
|
2280
|
+
if locked_fields.include?('servicePlanOptions.coresPerSocket')
|
2281
|
+
if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['coresPerSocket']
|
2282
|
+
plan_opts['coresPerSocket'] = options[:options]['servicePlanOptions']['coresPerSocket'].to_i
|
2283
|
+
end
|
2284
|
+
else
|
2285
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'servicePlanOptions', 'fieldName' => 'coresPerSocket', 'type' => 'number', 'fieldLabel' => "Cores Per Socket", 'required' => true, 'defaultValue' => default_cores_per_socket, 'description' => "Customize service plan options Cores Per Socket"}], options[:options])
|
2286
|
+
if v_prompt['servicePlanOptions'] && v_prompt['servicePlanOptions']['coresPerSocket']
|
2287
|
+
plan_opts['coresPerSocket'] = v_prompt['servicePlanOptions']['coresPerSocket'].to_i
|
2288
|
+
end
|
2289
|
+
end
|
2290
|
+
end
|
2291
|
+
# Memory
|
2292
|
+
if plan_info["customMaxMemory"]
|
2293
|
+
if locked_fields.include?('servicePlanOptions.maxMemory')
|
2294
|
+
if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxMemory']
|
2295
|
+
plan_opts['maxMemory'] = options[:options]['servicePlanOptions']['maxMemory'].to_i
|
2296
|
+
end
|
2297
|
+
else
|
2298
|
+
if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxMemory']
|
2299
|
+
plan_opts['maxMemory'] = options[:options]['servicePlanOptions']['maxMemory'].to_i
|
2300
|
+
else
|
2301
|
+
# prompt for "memoryMB" field as MB or "memoryGB" in GB
|
2302
|
+
# always convert maxMemory to bytes
|
2303
|
+
if plan_info["memorySizeType"] == "MB" || options[:options]["memoryMB"]
|
2304
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'memoryMB', 'type' => 'text', 'fieldLabel' => "Memory (MB)", 'required' => true, 'defaultValue' => default_max_memory / (1024 * 1024), 'description' => "Customize service plan options Memory (MB). Value is in megabytes."}], options[:options])
|
2305
|
+
if v_prompt['memoryMB'].to_s != ""
|
2306
|
+
plan_opts['maxMemory'] = v_prompt['memoryMB'].to_i * 1024 * 1024
|
2307
|
+
end
|
2308
|
+
else
|
2309
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'memoryGB', 'type' => 'text', 'fieldLabel' => "Memory (GB)", 'required' => true, 'defaultValue' => default_max_memory / (1024 * 1024 * 1024), 'description' => "Customize service plan options Memory (GB). Value is in gigabytes."}], options[:options])
|
2310
|
+
if v_prompt['memoryGB'].to_s != ""
|
2311
|
+
plan_opts['maxMemory'] = v_prompt['memoryGB'].to_i * 1024 * 1024 * 1024
|
2312
|
+
end
|
2313
|
+
end
|
2314
|
+
# remove transient memory field just used for prompting for MB or GB
|
2315
|
+
plan_opts.delete("memoryMB")
|
2316
|
+
plan_opts.delete("memoryGB")
|
2317
|
+
end
|
2318
|
+
end
|
2319
|
+
end
|
2320
|
+
return plan_opts
|
2321
|
+
end
|
2322
|
+
|
2243
2323
|
def format_instance_status(instance, return_color=cyan)
|
2244
2324
|
out = ""
|
2245
2325
|
status_string = instance['status'].to_s
|
@@ -42,7 +42,7 @@ module Morpheus
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
def self.prompt(option_types, options={}, api_client=nil, api_params={}, no_prompt=false, paging_enabled=false)
|
45
|
+
def self.prompt(option_types, options={}, api_client=nil, api_params={}, no_prompt=false, paging_enabled=false, ignore_empty=false)
|
46
46
|
paging_enabled = false if Morpheus::Cli.windows?
|
47
47
|
no_prompt = no_prompt || options[:no_prompt]
|
48
48
|
results = {}
|
@@ -57,6 +57,10 @@ module Morpheus
|
|
57
57
|
if option_type['fieldGroup'].to_s.downcase == 'options'
|
58
58
|
option_type['fieldGroup'] = 'default'
|
59
59
|
end
|
60
|
+
# apply custom templates
|
61
|
+
if option_type['fieldName'] == 'sshHosts'
|
62
|
+
option_type['type'] = 'multiText'
|
63
|
+
end
|
60
64
|
end
|
61
65
|
# puts "Options Prompt #{options}"
|
62
66
|
# Sort options by default, group, advanced
|
@@ -156,7 +160,7 @@ module Morpheus
|
|
156
160
|
end
|
157
161
|
|
158
162
|
# build parameters for option source api request
|
159
|
-
option_params = (option_type['noParams'] ? {} : (api_params || {}).
|
163
|
+
option_params = (option_type['noParams'] ? {} : (api_params || {}).deep_merge(results))
|
160
164
|
option_params.merge!(option_type['optionParams']) if option_type['optionParams']
|
161
165
|
|
162
166
|
# use the value passed in the options map
|
@@ -169,14 +173,14 @@ module Morpheus
|
|
169
173
|
end
|
170
174
|
# these select prompts should just fall down through below, with the extra params no_prompt, use_value
|
171
175
|
elsif option_type['type'] == 'select'
|
172
|
-
value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, option_params, true)
|
176
|
+
value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, option_params, true, nil, false, ignore_empty)
|
173
177
|
elsif option_type['type'] == 'multiSelect'
|
174
178
|
# support value as csv like "thing1, thing2"
|
175
179
|
value_list = value.is_a?(String) ? value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [value].flatten
|
176
180
|
input_value_list = input_value.is_a?(String) ? input_value.parse_csv.collect {|v| v ? v.to_s.strip : v } : [input_value].flatten
|
177
181
|
select_value_list = []
|
178
182
|
value_list.each_with_index do |v, i|
|
179
|
-
select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, option_params, true)
|
183
|
+
select_value_list << select_prompt(option_type.merge({'defaultValue' => v, 'defaultInputValue' => input_value_list[i]}), api_client, option_params, true, nil, false, ignore_empty)
|
180
184
|
end
|
181
185
|
value = select_value_list
|
182
186
|
elsif option_type['type'] == 'typeahead'
|
@@ -213,14 +217,14 @@ module Morpheus
|
|
213
217
|
# select type is special because it supports skipSingleOption
|
214
218
|
# and prints the available options on error
|
215
219
|
if ['select', 'multiSelect'].include?(option_type['type'])
|
216
|
-
value = select_prompt(option_type, api_client, option_params, true)
|
220
|
+
value = select_prompt(option_type, api_client, option_params, true, nil, false, ignore_empty)
|
217
221
|
value_found = !!value
|
218
222
|
end
|
219
223
|
if ['typeahead', 'multiTypeahead'].include?(option_type['type'])
|
220
224
|
value = typeahead_prompt(option_type, api_client, option_params, true)
|
221
225
|
value_found = !!value
|
222
226
|
end
|
223
|
-
if !value_found
|
227
|
+
if !value_found && !ignore_empty
|
224
228
|
if option_type['required']
|
225
229
|
print Term::ANSIColor.red, "\nMissing Required Option\n\n", Term::ANSIColor.reset
|
226
230
|
print Term::ANSIColor.red, " * #{option_type['fieldLabel']} [-O #{help_field_key}=] - #{option_type['description']}\n", Term::ANSIColor.reset
|
@@ -254,11 +258,11 @@ module Morpheus
|
|
254
258
|
# I suppose the entered value should take precedence
|
255
259
|
# api_params = api_params.merge(options) # this might be good enough
|
256
260
|
# dup it
|
257
|
-
value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled)
|
261
|
+
value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty)
|
258
262
|
if value && option_type['type'] == 'multiSelect'
|
259
263
|
value = [value]
|
260
264
|
while self.confirm("Add another #{option_type['fieldLabel']}?", {:default => false}) do
|
261
|
-
if addn_value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled)
|
265
|
+
if addn_value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty)
|
262
266
|
value << addn_value
|
263
267
|
else
|
264
268
|
break
|
@@ -370,7 +374,7 @@ module Morpheus
|
|
370
374
|
Thread.current[:_last_select]
|
371
375
|
end
|
372
376
|
|
373
|
-
def self.select_prompt(option_type, api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false)
|
377
|
+
def self.select_prompt(option_type, api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false, ignore_empty=false)
|
374
378
|
paging_enabled = false if Morpheus::Cli.windows?
|
375
379
|
field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
|
376
380
|
help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
|
@@ -406,9 +410,11 @@ module Morpheus
|
|
406
410
|
select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
|
407
411
|
end
|
408
412
|
else
|
409
|
-
raise "option '#{
|
413
|
+
raise "option '#{help_field_key}' is type: 'select' and missing selectOptions or optionSource!"
|
410
414
|
end
|
411
415
|
|
416
|
+
return nil if (select_options.nil? || select_options.count == 0) && ignore_empty
|
417
|
+
|
412
418
|
# ensure the preselected value (passed as an option) is in the dropdown
|
413
419
|
if !use_value.nil?
|
414
420
|
matched_option = select_options.find {|opt| opt[value_field].to_s == use_value.to_s || opt['name'].to_s == use_value.to_s }
|
@@ -672,6 +678,10 @@ module Morpheus
|
|
672
678
|
if select_options.empty?
|
673
679
|
print "The value '#{input}' matched 0 options.\n"
|
674
680
|
# print "Please try again.\n"
|
681
|
+
elsif select_options.size() == 1
|
682
|
+
print "The value '#{input}' matched 1 option.\n"
|
683
|
+
print "Perhaps you meant '#{select_options[0]['name']}' instead?"
|
684
|
+
# print "Please try again.\n"
|
675
685
|
else
|
676
686
|
print "The value '#{input}' matched #{select_options.size()} options.\n"
|
677
687
|
print "Perhaps you meant one of these? #{ored_list(select_options.collect {|i|i['name']}, 3)}\n"
|
@@ -686,6 +696,9 @@ module Morpheus
|
|
686
696
|
if select_options.empty?
|
687
697
|
print "The value '#{input}' matched 0 options.\n"
|
688
698
|
print "Please try again.\n"
|
699
|
+
elsif select_options.size() == 1
|
700
|
+
print "The value '#{input}' matched 1 option.\n"
|
701
|
+
print "Perhaps you meant '#{select_options[0]['name']}' instead?"
|
689
702
|
else
|
690
703
|
print "The value '#{input}' matched #{select_options.size()} options.\n"
|
691
704
|
print "Perhaps you meant one of these? #{ored_list(select_options.collect {|i|i['name']}, 3)}\n"
|
@@ -958,6 +971,8 @@ module Morpheus
|
|
958
971
|
end
|
959
972
|
|
960
973
|
def self.load_options(option_type, api_client, api_params, query_value=nil)
|
974
|
+
field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
|
975
|
+
help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
|
961
976
|
select_options = []
|
962
977
|
# local array of options
|
963
978
|
if option_type['selectOptions']
|
@@ -989,7 +1004,7 @@ module Morpheus
|
|
989
1004
|
select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
|
990
1005
|
end
|
991
1006
|
else
|
992
|
-
raise "option '#{
|
1007
|
+
raise "option '#{help_field_key}' is type: 'typeahead' and missing selectOptions or optionSource!"
|
993
1008
|
end
|
994
1009
|
|
995
1010
|
return select_options
|
data/lib/morpheus/cli/version.rb
CHANGED
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.4.
|
4
|
+
version: 5.4.3
|
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-02-08 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -300,6 +300,7 @@ files:
|
|
300
300
|
- lib/morpheus/api/service_catalog_interface.rb
|
301
301
|
- lib/morpheus/api/service_plans_interface.rb
|
302
302
|
- lib/morpheus/api/setup_interface.rb
|
303
|
+
- lib/morpheus/api/snapshots_interface.rb
|
303
304
|
- lib/morpheus/api/storage_providers_interface.rb
|
304
305
|
- lib/morpheus/api/storage_server_types_interface.rb
|
305
306
|
- lib/morpheus/api/storage_servers_interface.rb
|
@@ -455,6 +456,7 @@ files:
|
|
455
456
|
- lib/morpheus/cli/commands/setup.rb
|
456
457
|
- lib/morpheus/cli/commands/shell.rb
|
457
458
|
- lib/morpheus/cli/commands/sleep_command.rb
|
459
|
+
- lib/morpheus/cli/commands/snapshots.rb
|
458
460
|
- lib/morpheus/cli/commands/source_command.rb
|
459
461
|
- lib/morpheus/cli/commands/ssl_verification_command.rb
|
460
462
|
- lib/morpheus/cli/commands/storage_providers_command.rb
|