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.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus/api/api_client.rb +4 -1
  3. data/lib/morpheus/api/catalog_item_types_interface.rb +20 -0
  4. data/lib/morpheus/api/instances_interface.rb +28 -0
  5. data/lib/morpheus/api/ping_interface.rb +2 -0
  6. data/lib/morpheus/api/setup_interface.rb +4 -0
  7. data/lib/morpheus/api/snapshots_interface.rb +19 -0
  8. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +88 -0
  9. data/lib/morpheus/cli/commands/clusters.rb +59 -46
  10. data/lib/morpheus/cli/commands/hosts.rb +12 -0
  11. data/lib/morpheus/cli/commands/instances.rb +250 -1
  12. data/lib/morpheus/cli/commands/library_instance_types_command.rb +3 -0
  13. data/lib/morpheus/cli/commands/network_static_routes_command.rb +5 -0
  14. data/lib/morpheus/cli/commands/networks_command.rb +2 -2
  15. data/lib/morpheus/cli/commands/ping.rb +3 -5
  16. data/lib/morpheus/cli/commands/policies_command.rb +1 -1
  17. data/lib/morpheus/cli/commands/provisioning_settings_command.rb +1 -0
  18. data/lib/morpheus/cli/commands/remote.rb +16 -10
  19. data/lib/morpheus/cli/commands/security_groups.rb +2 -2
  20. data/lib/morpheus/cli/commands/service_plans_command.rb +1 -1
  21. data/lib/morpheus/cli/commands/setup.rb +1 -1
  22. data/lib/morpheus/cli/commands/snapshots.rb +139 -0
  23. data/lib/morpheus/cli/commands/tasks.rb +5 -5
  24. data/lib/morpheus/cli/commands/user_settings_command.rb +1 -1
  25. data/lib/morpheus/cli/commands/virtual_images.rb +4 -1
  26. data/lib/morpheus/cli/mixins/provisioning_helper.rb +80 -0
  27. data/lib/morpheus/cli/option_types.rb +26 -11
  28. data/lib/morpheus/cli/version.rb +1 -1
  29. 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 || {}).merge(results))
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 '#{field_key}' is type: 'select' and missing selectOptions or optionSource!"
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 '#{field_key}' is type: 'typeahead' and missing selectOptions or optionSource!"
1007
+ raise "option '#{help_field_key}' is type: 'typeahead' and missing selectOptions or optionSource!"
993
1008
  end
994
1009
 
995
1010
  return select_options
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "5.4.2"
4
+ VERSION = "5.4.3"
5
5
  end
6
6
  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.4.2
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-01-11 00:00:00.000000000 Z
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