morpheus-cli 5.4.2 → 5.4.4

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +12 -1
  4. data/lib/morpheus/api/catalog_item_types_interface.rb +17 -0
  5. data/lib/morpheus/api/clusters_interface.rb +12 -0
  6. data/lib/morpheus/api/credential_types_interface.rb +9 -0
  7. data/lib/morpheus/api/credentials_interface.rb +9 -0
  8. data/lib/morpheus/api/instances_interface.rb +28 -0
  9. data/lib/morpheus/api/monitoring_apps_interface.rb +12 -4
  10. data/lib/morpheus/api/monitoring_checks_interface.rb +12 -4
  11. data/lib/morpheus/api/monitoring_groups_interface.rb +13 -5
  12. data/lib/morpheus/api/monitoring_incidents_interface.rb +12 -4
  13. data/lib/morpheus/api/options_interface.rb +8 -1
  14. data/lib/morpheus/api/ping_interface.rb +2 -0
  15. data/lib/morpheus/api/power_schedules_interface.rb +2 -2
  16. data/lib/morpheus/api/service_plans_interface.rb +6 -0
  17. data/lib/morpheus/api/setup_interface.rb +4 -0
  18. data/lib/morpheus/api/snapshots_interface.rb +19 -0
  19. data/lib/morpheus/cli/cli_command.rb +10 -17
  20. data/lib/morpheus/cli/commands/catalog_item_types_command.rb +156 -1
  21. data/lib/morpheus/cli/commands/clusters.rb +177 -50
  22. data/lib/morpheus/cli/commands/credential_types_command.rb +36 -0
  23. data/lib/morpheus/cli/commands/credentials_command.rb +124 -0
  24. data/lib/morpheus/cli/commands/hosts.rb +32 -2
  25. data/lib/morpheus/cli/commands/instances.rb +255 -2
  26. data/lib/morpheus/cli/commands/library_instance_types_command.rb +3 -0
  27. data/lib/morpheus/cli/commands/monitoring_apps_command.rb +8 -8
  28. data/lib/morpheus/cli/commands/monitoring_checks_command.rb +8 -8
  29. data/lib/morpheus/cli/commands/monitoring_groups_command.rb +8 -8
  30. data/lib/morpheus/cli/commands/monitoring_incidents_command.rb +8 -8
  31. data/lib/morpheus/cli/commands/network_static_routes_command.rb +5 -0
  32. data/lib/morpheus/cli/commands/networks_command.rb +2 -2
  33. data/lib/morpheus/cli/commands/ping.rb +3 -5
  34. data/lib/morpheus/cli/commands/policies_command.rb +1 -1
  35. data/lib/morpheus/cli/commands/power_schedules_command.rb +189 -258
  36. data/lib/morpheus/cli/commands/provisioning_settings_command.rb +1 -0
  37. data/lib/morpheus/cli/commands/remote.rb +16 -10
  38. data/lib/morpheus/cli/commands/security_groups.rb +2 -2
  39. data/lib/morpheus/cli/commands/service_plans_command.rb +52 -5
  40. data/lib/morpheus/cli/commands/setup.rb +1 -1
  41. data/lib/morpheus/cli/commands/snapshots.rb +139 -0
  42. data/lib/morpheus/cli/commands/storage_server_types.rb +0 -5
  43. data/lib/morpheus/cli/commands/storage_servers.rb +0 -6
  44. data/lib/morpheus/cli/commands/storage_volume_types.rb +0 -5
  45. data/lib/morpheus/cli/commands/storage_volumes.rb +0 -6
  46. data/lib/morpheus/cli/commands/tasks.rb +5 -5
  47. data/lib/morpheus/cli/commands/user_settings_command.rb +1 -1
  48. data/lib/morpheus/cli/commands/virtual_images.rb +4 -1
  49. data/lib/morpheus/cli/mixins/provisioning_helper.rb +117 -27
  50. data/lib/morpheus/cli/mixins/rest_command.rb +20 -4
  51. data/lib/morpheus/cli/mixins/storage_servers_helper.rb +0 -63
  52. data/lib/morpheus/cli/mixins/storage_volumes_helper.rb +0 -43
  53. data/lib/morpheus/cli/option_types.rb +27 -11
  54. data/lib/morpheus/cli/version.rb +1 -1
  55. data/lib/morpheus/routes.rb +13 -3
  56. metadata +13 -7
@@ -864,12 +864,18 @@ module Morpheus::Cli::ProvisioningHelper
864
864
  if locked_fields.include?('volumes')
865
865
  payload['volumes'] = options[:options]['volumes'] if options[:options]['volumes']
866
866
  else
867
- volumes = prompt_volumes(service_plan, options, api_client, {zoneId: cloud_id, layoutId: layout['id'], siteId: group_id})
867
+ volumes = prompt_volumes(service_plan, provision_type, options, api_client, {zoneId: cloud_id, layoutId: layout['id'], siteId: group_id})
868
868
  if !volumes.empty?
869
869
  payload['volumes'] = volumes
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']
@@ -906,7 +912,7 @@ module Morpheus::Cli::ProvisioningHelper
906
912
  has_security_groups = !!sg_option_type
907
913
  available_security_groups = []
908
914
  if sg_option_type && sg_option_type['type'] == 'select' && sg_option_type['optionSource']
909
- sg_option_results = options_interface.options_for_source(sg_option_type['optionSource'], sg_api_params)
915
+ sg_option_results = options_interface.options_for_source(sg_option_type['optionSource'], sg_api_params, sg_option_type['optionSourceType'])
910
916
  available_security_groups = sg_option_results['data'].collect do |it|
911
917
  {"id" => it["value"] || it["id"], "name" => it["name"], "value" => it["value"] || it["id"]}
912
918
  end
@@ -1033,7 +1039,7 @@ module Morpheus::Cli::ProvisioningHelper
1033
1039
 
1034
1040
  # This recreates the behavior of multi_disk.js
1035
1041
  # returns array of volumes based on service plan options (plan_info)
1036
- def prompt_volumes(plan_info, options={}, api_client=nil, api_params={})
1042
+ def prompt_volumes(plan_info, provision_type, options={}, api_client=nil, api_params={})
1037
1043
  #puts "Configure Volumes:"
1038
1044
  # return [] if plan_info['noDisks']
1039
1045
 
@@ -1135,14 +1141,19 @@ module Morpheus::Cli::ProvisioningHelper
1135
1141
  volume['name'] = v_prompt[field_context]['name']
1136
1142
  end
1137
1143
  if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customSize']
1138
- if root_custom_size_options.empty?
1139
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => 'Root Volume Size (GB)', 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1140
- volume['size'] = v_prompt[field_context]['size']
1141
- volume['sizeId'] = nil #volume.delete('sizeId')
1144
+ # provision_type['rootDiskSizeKnown'] == false means size cannot be changed
1145
+ if provision_type['rootDiskSizeKnown'] == false
1146
+ # volume['size'] = plan_size if plan_size.to_i != 0
1142
1147
  else
1143
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => 'Root Volume Size', 'selectOptions' => root_custom_size_options, 'required' => true, 'description' => 'Choose a volume size.', 'defaultValue' => volume['sizeId']}], options[:options])
1144
- volume['sizeId'] = v_prompt[field_context]['sizeId']
1145
- volume['size'] = nil #volume.delete('size')
1148
+ if root_custom_size_options.empty?
1149
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => 'Root Volume Size (GB)', 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1150
+ volume['size'] = v_prompt[field_context]['size']
1151
+ volume['sizeId'] = nil #volume.delete('sizeId')
1152
+ else
1153
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => 'Root Volume Size', 'selectOptions' => root_custom_size_options, 'required' => true, 'description' => 'Choose a volume size.', 'defaultValue' => volume['sizeId']}], options[:options])
1154
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1155
+ volume['size'] = nil #volume.delete('size')
1156
+ end
1146
1157
  end
1147
1158
  else
1148
1159
  # might need different logic here ? =o
@@ -1242,7 +1253,7 @@ module Morpheus::Cli::ProvisioningHelper
1242
1253
 
1243
1254
  # This recreates the behavior of multi_disk.js
1244
1255
  # returns array of volumes based on service plan options (plan_info)
1245
- def prompt_resize_volumes(current_volumes, plan_info, options={})
1256
+ def prompt_resize_volumes(current_volumes, plan_info, provision_type, options={})
1246
1257
  #puts "Configure Volumes:"
1247
1258
  no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1248
1259
 
@@ -1322,7 +1333,7 @@ module Morpheus::Cli::ProvisioningHelper
1322
1333
  'id' => current_root_volume['id'],
1323
1334
  'rootVolume' => true,
1324
1335
  'name' => current_root_volume['name'],
1325
- 'size' => current_root_volume['size'] > plan_size ? current_root_volume['size'] : plan_size,
1336
+ 'size' => current_root_volume['size'] > (plan_size || 0) ? current_root_volume['size'] : plan_size,
1326
1337
  'sizeId' => nil,
1327
1338
  'storageType' => storage_type_id,
1328
1339
  'datastoreId' => current_root_volume['datastoreId']
@@ -1333,19 +1344,24 @@ module Morpheus::Cli::ProvisioningHelper
1333
1344
  volume['name'] = v_prompt[field_context]['name']
1334
1345
  end
1335
1346
  if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customSize']
1336
- if root_custom_size_options.empty?
1337
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => 'Root Volume Size (GB)', 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1338
- volume['size'] = v_prompt[field_context]['size']
1339
- volume['sizeId'] = nil #volume.delete('sizeId')
1347
+ # provision_type['rootDiskSizeKnown'] == false means size cannot be changed
1348
+ if provision_type['rootDiskSizeKnown'] == false
1349
+ # volume['size'] = plan_size if plan_size.to_i != 0
1340
1350
  else
1341
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => 'Root Volume Size', 'selectOptions' => root_custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1342
- volume['sizeId'] = v_prompt[field_context]['sizeId']
1343
- volume['size'] = nil #volume.delete('size')
1351
+ if root_custom_size_options.empty?
1352
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => 'Root Volume Size (GB)', 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1353
+ volume['size'] = v_prompt[field_context]['size']
1354
+ volume['sizeId'] = nil #volume.delete('sizeId')
1355
+ else
1356
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => 'Root Volume Size', 'selectOptions' => root_custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1357
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1358
+ volume['size'] = nil #volume.delete('size')
1359
+ end
1344
1360
  end
1345
1361
  else
1346
1362
  # might need different logic here ? =o
1347
- volume['size'] = plan_size
1348
- volume['sizeId'] = nil #volume.delete('sizeId')
1363
+ # volume['size'] = plan_size
1364
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1349
1365
  end
1350
1366
  # if !datastore_options.empty?
1351
1367
  # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => 'Root Datastore', 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.'}], options[:options])
@@ -1373,7 +1389,7 @@ module Morpheus::Cli::ProvisioningHelper
1373
1389
  'id' => current_volume['id'].to_i,
1374
1390
  'rootVolume' => false,
1375
1391
  'name' => current_volume['name'],
1376
- 'size' => current_volume['size'] > plan_size ? current_volume['size'] : plan_size,
1392
+ 'size' => current_volume['size'] > (plan_size || 0) ? current_volume['size'] : plan_size,
1377
1393
  'sizeId' => nil,
1378
1394
  'storageType' => (current_volume['type'] || current_volume['storageType']),
1379
1395
  'datastoreId' => current_volume['datastoreId']
@@ -1398,7 +1414,7 @@ module Morpheus::Cli::ProvisioningHelper
1398
1414
  'id' => current_volume['id'].to_i,
1399
1415
  'rootVolume' => false,
1400
1416
  'name' => current_volume['name'],
1401
- 'size' => current_volume['size'] > plan_size ? current_volume['size'] : plan_size,
1417
+ 'size' => current_volume['size'] > (plan_size || 0) ? current_volume['size'] : plan_size,
1402
1418
  'sizeId' => nil,
1403
1419
  'storageType' => (current_volume['type'] || current_volume['storageType']),
1404
1420
  'datastoreId' => current_volume['datastoreId']
@@ -1420,8 +1436,8 @@ module Morpheus::Cli::ProvisioningHelper
1420
1436
  end
1421
1437
  else
1422
1438
  # might need different logic here ? =o
1423
- volume['size'] = plan_size
1424
- volume['sizeId'] = nil #volume.delete('sizeId')
1439
+ # volume['size'] = plan_size
1440
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1425
1441
  end
1426
1442
  # if !datastore_options.empty?
1427
1443
  # 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])
@@ -1486,8 +1502,8 @@ module Morpheus::Cli::ProvisioningHelper
1486
1502
  end
1487
1503
  else
1488
1504
  # might need different logic here ? =o
1489
- volume['size'] = plan_size
1490
- volume['sizeId'] = nil #volume.delete('sizeId')
1505
+ # volume['size'] = plan_size
1506
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1491
1507
  end
1492
1508
  if !datastore_options.empty?
1493
1509
  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])
@@ -2240,6 +2256,80 @@ module Morpheus::Cli::ProvisioningHelper
2240
2256
  return ports
2241
2257
  end
2242
2258
 
2259
+ def prompt_service_plan_options(plan_info, options={}, api_client=nil, api_params={}, instance=nil)
2260
+ plan_opts = {}
2261
+ # provisioning with blueprint can lock fields
2262
+ locked_fields = options[:locked_fields] || []
2263
+ if options[:options]['servicePlanOptions']
2264
+ plan_opts = options[:options]['servicePlanOptions']
2265
+ end
2266
+ default_max_cores = plan_info['maxCores'].to_i != 0 ? plan_info['maxCores'] : 1
2267
+ default_cores_per_socket = plan_info['coresPerSocket'].to_i != 0 ? plan_info['coresPerSocket'] : 1
2268
+ default_max_memory = plan_info['maxMemory'].to_i != 0 ? plan_info['maxMemory'] : nil
2269
+ # use defaults from the instance/server
2270
+ if instance
2271
+ default_max_cores = instance["maxCores"] if instance["maxCores"]
2272
+ default_cores_per_socket = instance["coresPerSocket"] if instance["coresPerSocket"]
2273
+ default_max_memory = instance["maxMemory"] if instance["maxMemory"]
2274
+ end
2275
+ # Core Count
2276
+ if plan_info["customCores"]
2277
+ if locked_fields.include?('servicePlanOptions.maxCores')
2278
+ if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxCores']
2279
+ plan_opts['maxCores'] = options[:options]['servicePlanOptions']['maxCores'].to_i
2280
+ end
2281
+ else
2282
+ 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])
2283
+ if v_prompt['servicePlanOptions'] && v_prompt['servicePlanOptions']['maxCores']
2284
+ plan_opts['maxCores'] = v_prompt['servicePlanOptions']['maxCores'].to_i
2285
+ end
2286
+ end
2287
+ end
2288
+ # Cores Per Socket
2289
+ if plan_info["customCoresPerSocket"]
2290
+ if locked_fields.include?('servicePlanOptions.coresPerSocket')
2291
+ if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['coresPerSocket']
2292
+ plan_opts['coresPerSocket'] = options[:options]['servicePlanOptions']['coresPerSocket'].to_i
2293
+ end
2294
+ else
2295
+ 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])
2296
+ if v_prompt['servicePlanOptions'] && v_prompt['servicePlanOptions']['coresPerSocket']
2297
+ plan_opts['coresPerSocket'] = v_prompt['servicePlanOptions']['coresPerSocket'].to_i
2298
+ end
2299
+ end
2300
+ end
2301
+ # Memory
2302
+ if plan_info["customMaxMemory"]
2303
+ if locked_fields.include?('servicePlanOptions.maxMemory')
2304
+ if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxMemory']
2305
+ plan_opts['maxMemory'] = options[:options]['servicePlanOptions']['maxMemory'].to_i
2306
+ end
2307
+ else
2308
+ if options[:options]['servicePlanOptions'] && options[:options]['servicePlanOptions']['maxMemory']
2309
+ plan_opts['maxMemory'] = options[:options]['servicePlanOptions']['maxMemory'].to_i
2310
+ else
2311
+ # prompt for "memoryMB" field as MB or "memoryGB" in GB
2312
+ # always convert maxMemory to bytes
2313
+ if plan_info["memorySizeType"] == "MB" || options[:options]["memoryMB"]
2314
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'memoryMB', 'type' => 'text', 'fieldLabel' => "Memory (MB)", 'required' => true, 'defaultValue' => default_max_memory ? (default_max_memory / (1024 * 1024)) : nil, 'description' => "Customize service plan options Memory (MB). Value is in megabytes."}], options[:options])
2315
+ if v_prompt['memoryMB'].to_s != ""
2316
+ plan_opts['maxMemory'] = v_prompt['memoryMB'].to_i * 1024 * 1024
2317
+ end
2318
+ else
2319
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'memoryGB', 'type' => 'text', 'fieldLabel' => "Memory (GB)", 'required' => true, 'defaultValue' => default_max_memory ? (default_max_memory / (1024 * 1024 * 1024)) : nil, 'description' => "Customize service plan options Memory (GB). Value is in gigabytes."}], options[:options])
2320
+ if v_prompt['memoryGB'].to_s != ""
2321
+ plan_opts['maxMemory'] = v_prompt['memoryGB'].to_i * 1024 * 1024 * 1024
2322
+ end
2323
+ end
2324
+ # remove transient memory field just used for prompting for MB or GB
2325
+ plan_opts.delete("memoryMB")
2326
+ plan_opts.delete("memoryGB")
2327
+ end
2328
+ end
2329
+ end
2330
+ return plan_opts
2331
+ end
2332
+
2243
2333
  def format_instance_status(instance, return_color=cyan)
2244
2334
  out = ""
2245
2335
  status_string = instance['status'].to_s
@@ -336,11 +336,19 @@ module Morpheus::Cli::RestCommand
336
336
  end
337
337
 
338
338
  def rest_object_key
339
- send("#{rest_key}_object_key")
339
+ if respond_to?("#{rest_key}_object_key", true)
340
+ send("#{rest_key}_object_key")
341
+ else
342
+ rest_name.camelcase.singularize
343
+ end
340
344
  end
341
345
 
342
346
  def rest_list_key
343
- send("#{rest_key}_list_key")
347
+ if respond_to?("#{rest_key}_list_key", true)
348
+ send("#{rest_key}_list_key")
349
+ else
350
+ rest_name.camelcase
351
+ end
344
352
  end
345
353
 
346
354
  def rest_column_definitions(options)
@@ -408,11 +416,19 @@ module Morpheus::Cli::RestCommand
408
416
  end
409
417
 
410
418
  def rest_type_object_key
411
- send("#{rest_type_key}_object_key")
419
+ if respond_to?("#{rest_type_key}_object_key", true)
420
+ send("#{rest_type_key}_object_key")
421
+ else
422
+ rest_type_name.camelcase.singularize
423
+ end
412
424
  end
413
425
 
414
426
  def rest_type_list_key
415
- send("#{rest_type_key}_list_key")
427
+ if respond_to?("#{rest_type_key}_list_key", true)
428
+ send("#{rest_type_key}_list_key")
429
+ else
430
+ rest_type_name.camelcase
431
+ end
416
432
  end
417
433
 
418
434
  def rest_type_column_definitions(options)
@@ -90,67 +90,4 @@ module Morpheus::Cli::StorageServersHelper
90
90
  end
91
91
  end
92
92
 
93
- def get_available_storage_server_types(refresh=false)
94
- if !@available_storage_server_types || refresh
95
- @available_storage_server_types = storage_server_types_interface.list({max:1000})[storage_server_type_list_key]
96
- end
97
- return @available_storage_server_types
98
- end
99
-
100
- def storage_server_type_for_name_or_id(val)
101
- if val.to_s =~ /\A\d{1,}\Z/
102
- return storage_server_type_for_id(val)
103
- else
104
- return storage_server_type_for_name(val)
105
- end
106
- end
107
-
108
- def storage_server_type_for_id(id)
109
- return get_available_storage_server_types().find { |z| z['id'].to_i == id.to_i}
110
- end
111
-
112
- def storage_server_type_for_name(name)
113
- return get_available_storage_server_types().find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
114
- end
115
-
116
- def find_storage_server_type_by_name_or_id(val)
117
- if val.to_s =~ /\A\d{1,}\Z/
118
- return find_storage_server_type_by_id(val)
119
- else
120
- return find_storage_server_type_by_name(val)
121
- end
122
- end
123
-
124
- def find_storage_server_type_by_id(id)
125
- begin
126
- json_response = storage_server_types_interface.get(id.to_i)
127
- return json_response[storage_server_type_object_key]
128
- rescue RestClient::Exception => e
129
- if e.response && e.response.code == 404
130
- print_red_alert "Storage Server Type not found by id #{id}"
131
- return nil
132
- else
133
- raise e
134
- end
135
- end
136
- end
137
-
138
- def find_storage_server_type_by_name(name)
139
- json_response = storage_server_types_interface.list({name: name.to_s})
140
- storage_server_types = json_response[storage_server_type_list_key]
141
- if storage_server_types.empty?
142
- print_red_alert "Storage Server Type not found by name #{name}"
143
- return storage_server_types
144
- elsif storage_server_types.size > 1
145
- print_red_alert "#{storage_server_types.size} storage server types found by name #{name}"
146
- rows = storage_server_types.collect do |it|
147
- {id: it['id'], name: it['name']}
148
- end
149
- puts as_pretty_table(rows, [:id, :name], {color:red})
150
- return nil
151
- else
152
- return storage_server_types[0]
153
- end
154
- end
155
-
156
93
  end
@@ -54,49 +54,6 @@ module Morpheus::Cli::StorageVolumesHelper
54
54
  'Storage Volume Types'
55
55
  end
56
56
 
57
- def get_available_storage_volume_types(refresh=false)
58
- if !@available_storage_volume_types || refresh
59
- @available_storage_volume_types = storage_volume_types_interface.list({max:1000})[storage_volume_type_list_key]
60
- end
61
- return @available_storage_volume_types
62
- end
63
-
64
- def storage_volume_type_for_name_or_id(val)
65
- if val.to_s =~ /\A\d{1,}\Z/
66
- return storage_volume_type_for_id(val)
67
- else
68
- return storage_volume_type_for_name(val)
69
- end
70
- end
71
-
72
- def storage_volume_type_for_id(val)
73
- record = get_available_storage_volume_types().find { |z| z['id'].to_i == val.to_i}
74
- label = "Storage Volume Type"
75
- if record.nil?
76
- print_red_alert "#{label} not found by id #{val}"
77
- return nil
78
- end
79
- return record
80
- end
81
-
82
- def storage_volume_type_for_name(val)
83
- records = get_available_storage_volume_types().select { |z| z['name'].downcase == val.downcase || z['code'].downcase == val.downcase}
84
- label = "Storage Volume Type"
85
- if records.empty?
86
- print_red_alert "#{label} not found by name '#{val}'"
87
- return nil
88
- elsif records.size > 1
89
- print_red_alert "More than one #{label.downcase} found by name '#{val}'"
90
- print_error "\n"
91
- puts_error as_pretty_table(records, [:id, :name], {color:red})
92
- print_red_alert "Try using ID instead"
93
- print_error reset,"\n"
94
- return nil
95
- else
96
- return records[0]
97
- end
98
- end
99
-
100
57
  def format_storage_volume_status(record, return_color=cyan)
101
58
  out = ""
102
59
  status_string = record['status']
@@ -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,20 +217,21 @@ 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
227
231
  print "\n"
228
232
  exit 1
229
233
  else
234
+ parent_context_map.reject! {|k,v| k == parent_ns && (v.nil? || (v.is_a?(Hash) && v.empty?))}
230
235
  next
231
236
  end
232
237
  end
@@ -254,11 +259,11 @@ module Morpheus
254
259
  # I suppose the entered value should take precedence
255
260
  # api_params = api_params.merge(options) # this might be good enough
256
261
  # dup it
257
- value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled)
262
+ value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty)
258
263
  if value && option_type['type'] == 'multiSelect'
259
264
  value = [value]
260
265
  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)
266
+ if addn_value = select_prompt(option_type, api_client, option_params, options[:no_prompt], nil, paging_enabled, ignore_empty)
262
267
  value << addn_value
263
268
  else
264
269
  break
@@ -370,7 +375,7 @@ module Morpheus
370
375
  Thread.current[:_last_select]
371
376
  end
372
377
 
373
- def self.select_prompt(option_type, api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false)
378
+ def self.select_prompt(option_type, api_client, api_params={}, no_prompt=false, use_value=nil, paging_enabled=false, ignore_empty=false)
374
379
  paging_enabled = false if Morpheus::Cli.windows?
375
380
  field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
376
381
  help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
@@ -406,9 +411,11 @@ module Morpheus
406
411
  select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
407
412
  end
408
413
  else
409
- raise "option '#{field_key}' is type: 'select' and missing selectOptions or optionSource!"
414
+ raise "option '#{help_field_key}' is type: 'select' and missing selectOptions or optionSource!"
410
415
  end
411
416
 
417
+ return nil if (select_options.nil? || select_options.count == 0) && ignore_empty
418
+
412
419
  # ensure the preselected value (passed as an option) is in the dropdown
413
420
  if !use_value.nil?
414
421
  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 +679,10 @@ module Morpheus
672
679
  if select_options.empty?
673
680
  print "The value '#{input}' matched 0 options.\n"
674
681
  # print "Please try again.\n"
682
+ elsif select_options.size() == 1
683
+ print "The value '#{input}' matched 1 option.\n"
684
+ print "Perhaps you meant '#{select_options[0]['name']}' instead?"
685
+ # print "Please try again.\n"
675
686
  else
676
687
  print "The value '#{input}' matched #{select_options.size()} options.\n"
677
688
  print "Perhaps you meant one of these? #{ored_list(select_options.collect {|i|i['name']}, 3)}\n"
@@ -686,6 +697,9 @@ module Morpheus
686
697
  if select_options.empty?
687
698
  print "The value '#{input}' matched 0 options.\n"
688
699
  print "Please try again.\n"
700
+ elsif select_options.size() == 1
701
+ print "The value '#{input}' matched 1 option.\n"
702
+ print "Perhaps you meant '#{select_options[0]['name']}' instead?"
689
703
  else
690
704
  print "The value '#{input}' matched #{select_options.size()} options.\n"
691
705
  print "Perhaps you meant one of these? #{ored_list(select_options.collect {|i|i['name']}, 3)}\n"
@@ -958,6 +972,8 @@ module Morpheus
958
972
  end
959
973
 
960
974
  def self.load_options(option_type, api_client, api_params, query_value=nil)
975
+ field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
976
+ help_field_key = option_type[:help_field_prefix] ? "#{option_type[:help_field_prefix]}.#{field_key}" : field_key
961
977
  select_options = []
962
978
  # local array of options
963
979
  if option_type['selectOptions']
@@ -989,7 +1005,7 @@ module Morpheus
989
1005
  select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
990
1006
  end
991
1007
  else
992
- raise "option '#{field_key}' is type: 'typeahead' and missing selectOptions or optionSource!"
1008
+ raise "option '#{help_field_key}' is type: 'typeahead' and missing selectOptions or optionSource!"
993
1009
  end
994
1010
 
995
1011
  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.4"
5
5
  end
6
6
  end
@@ -87,9 +87,19 @@ module Morpheus::Routes
87
87
  :'data-stores' => {}, # ugh, should be datastores
88
88
  servers: {}, # Storage Servers
89
89
  },
90
- # :'keys-and-certs' => {},
91
- :'key-pairs' => {},
92
- certificates: {},
90
+ trust: [
91
+ "#!credentials",
92
+ "#!certificates",
93
+ "#!keypairs",
94
+ "#!services",
95
+ ],
96
+ boot: [
97
+ "#!mappings",
98
+ "#!boot-menus",
99
+ "#!answerfiles",
100
+ "#!boot-images",
101
+ "#!macs",
102
+ ],
93
103
  },
94
104
  backups: {
95
105
 
metadata CHANGED
@@ -1,17 +1,17 @@
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.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Estes
8
8
  - Bob Whiton
9
9
  - Jeremy Michael Crosbie
10
10
  - James Dickson
11
- autorequire:
11
+ autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2022-01-11 00:00:00.000000000 Z
14
+ date: 2022-03-10 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: bundler
@@ -200,6 +200,8 @@ files:
200
200
  - lib/morpheus/api/clouds_interface.rb
201
201
  - lib/morpheus/api/clusters_interface.rb
202
202
  - lib/morpheus/api/containers_interface.rb
203
+ - lib/morpheus/api/credential_types_interface.rb
204
+ - lib/morpheus/api/credentials_interface.rb
203
205
  - lib/morpheus/api/custom_instance_types_interface.rb
204
206
  - lib/morpheus/api/cypher_interface.rb
205
207
  - lib/morpheus/api/dashboard_interface.rb
@@ -300,6 +302,7 @@ files:
300
302
  - lib/morpheus/api/service_catalog_interface.rb
301
303
  - lib/morpheus/api/service_plans_interface.rb
302
304
  - lib/morpheus/api/setup_interface.rb
305
+ - lib/morpheus/api/snapshots_interface.rb
303
306
  - lib/morpheus/api/storage_providers_interface.rb
304
307
  - lib/morpheus/api/storage_server_types_interface.rb
305
308
  - lib/morpheus/api/storage_servers_interface.rb
@@ -355,6 +358,8 @@ files:
355
358
  - lib/morpheus/cli/commands/clusters.rb
356
359
  - lib/morpheus/cli/commands/coloring_command.rb
357
360
  - lib/morpheus/cli/commands/containers_command.rb
361
+ - lib/morpheus/cli/commands/credential_types_command.rb
362
+ - lib/morpheus/cli/commands/credentials_command.rb
358
363
  - lib/morpheus/cli/commands/curl_command.rb
359
364
  - lib/morpheus/cli/commands/cypher_command.rb
360
365
  - lib/morpheus/cli/commands/dashboard_command.rb
@@ -455,6 +460,7 @@ files:
455
460
  - lib/morpheus/cli/commands/setup.rb
456
461
  - lib/morpheus/cli/commands/shell.rb
457
462
  - lib/morpheus/cli/commands/sleep_command.rb
463
+ - lib/morpheus/cli/commands/snapshots.rb
458
464
  - lib/morpheus/cli/commands/source_command.rb
459
465
  - lib/morpheus/cli/commands/ssl_verification_command.rb
460
466
  - lib/morpheus/cli/commands/storage_providers_command.rb
@@ -524,11 +530,11 @@ files:
524
530
  - lib/morpheus/terminal.rb
525
531
  - lib/morpheus/util.rb
526
532
  - morpheus-cli.gemspec
527
- homepage:
533
+ homepage:
528
534
  licenses:
529
535
  - MIT
530
536
  metadata: {}
531
- post_install_message:
537
+ post_install_message:
532
538
  rdoc_options: []
533
539
  require_paths:
534
540
  - lib
@@ -543,9 +549,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
543
549
  - !ruby/object:Gem::Version
544
550
  version: '0'
545
551
  requirements: []
546
- rubyforge_project:
552
+ rubyforge_project:
547
553
  rubygems_version: 2.7.6
548
- signing_key:
554
+ signing_key:
549
555
  specification_version: 4
550
556
  summary: Provides CLI Interface to the Morpheus Public/Private Cloud Appliance
551
557
  test_files: []