morpheus-cli 5.4.2 → 5.4.4

Sign up to get free protection for your applications and to get access to all the features.
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: []