morpheus-cli 8.0.0 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -508,16 +508,17 @@ module Morpheus::Cli::ProvisioningHelper
508
508
 
509
509
  while instance_name.nil? do
510
510
  name_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Instance Name', 'type' => 'text', 'required' => options[:name_required], 'defaultValue' => options[:default_name]}], options[:options])
511
-
512
- if name_prompt['name'].nil? && !options[:name_required]
511
+ name = name_prompt['name']
512
+ if name.nil? && !options[:name_required]
513
513
  break
514
514
  else
515
- if instances_interface.list({name: name_prompt['name']})['instances'].empty?
516
- instance_name = name_prompt['name']
515
+ if instances_interface.list({name: name})['instances'].empty?
516
+ instance_name = name
517
517
  else
518
- print_red_alert "Name must be unique"
518
+ print_red_alert "Name must be unique, #{name} already exists"
519
+ options[:options].delete('name')
519
520
  exit 1 if no_prompt
520
- if options[:default_name] == name_prompt['name']
521
+ if options[:default_name] == name
521
522
  options[:default_name] += '-2'
522
523
  end
523
524
  end
@@ -796,7 +797,8 @@ module Morpheus::Cli::ProvisioningHelper
796
797
  resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([resource_pool_option_type],options[:options],api_client,{}, no_prompt, true)
797
798
  resource_pool_prompt.deep_compact!
798
799
  payload.deep_merge!(resource_pool_prompt)
799
- resource_pool = Morpheus::Cli::OptionTypes.get_last_select()
800
+ # this results in the layout, not the resourcePool for some reason...
801
+ #resource_pool = Morpheus::Cli::OptionTypes.get_last_select()
800
802
  if resource_pool_option_type['fieldContext'] && resource_pool_prompt[resource_pool_option_type['fieldContext']]
801
803
  pool_id = resource_pool_prompt[resource_pool_option_type['fieldContext']][resource_pool_option_type['fieldName']]
802
804
  elsif resource_pool_prompt[resource_pool_option_type['fieldName']]
@@ -833,38 +835,13 @@ module Morpheus::Cli::ProvisioningHelper
833
835
  option_type_list = option_type_list.reject {|opt| ['provisionServerId'].include?(opt['fieldName'])}
834
836
  end
835
837
 
836
- # add selectable datastores for resource pool
838
+ # add selectable datastores
839
+ # no longer use the plan datastores info, always load from data-stores api instead
840
+ # note: this modifies service_plan in place with 'datastores' and 'autoOptions'
837
841
  if options[:select_datastore]
838
- begin
839
- selectable_datastores = datastores_interface.list({'zoneId' => cloud_id, 'siteId' => group_id, 'poolId' => resource_pool['id'], 'resourcePoolId' => resource_pool['id']})
840
- service_plan['datastores'] = {'clusters' => [], 'datastores' => []}
841
- ['clusters', 'datastores'].each do |type|
842
- service_plan['datastores'][type] ||= []
843
- selectable_datastores[type].reject { |ds| service_plan['datastores'][type].find {|it| it['id'] == ds['id']} }.each { |ds|
844
- service_plan['datastores'][type] << ds
845
- }
846
- end
847
- rescue => error
848
- Morpheus::Logging::DarkPrinter.puts "Unable to load available data-stores, using datastores option source instead." if Morpheus::Logging.debug?
849
- end
850
-
851
- if provision_type && provision_type['supportsAutoDatastore']
852
- service_plan['supportsAutoDatastore'] = true
853
- service_plan['autoOptions'] ||= []
854
- if service_plan['datastores'] && service_plan['datastores']['clusters']
855
- if service_plan['datastores']['clusters'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'autoCluster'}
856
- service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
857
- end
858
- else
859
- service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
860
- end
861
- if service_plan['datastores'] && service_plan['datastores']['datastores']
862
- if service_plan['datastores']['datastores'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'auto'}
863
- service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
864
- end
865
- else
866
- service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
867
- end
842
+ if provision_type['hasDatastore']
843
+ datastore_params = {'zoneId' => cloud_id, 'siteId' => group_id, 'resourcePoolId' => resource_pool['id']}
844
+ load_service_plan_datastores(service_plan, provision_type, datastore_params)
868
845
  end
869
846
  end
870
847
  end
@@ -1089,7 +1066,7 @@ module Morpheus::Cli::ProvisioningHelper
1089
1066
  no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1090
1067
  volumes = []
1091
1068
  plan_size = nil
1092
- if plan_info['maxStorage']
1069
+ if plan_info['maxStorage'].to_i > 0
1093
1070
  plan_size = plan_info['maxStorage'].to_i / (1024 * 1024 * 1024)
1094
1071
  end
1095
1072
  root_storage_types = []
@@ -1162,7 +1139,7 @@ module Morpheus::Cli::ProvisioningHelper
1162
1139
  storage_type_id = nil
1163
1140
  storage_type = nil
1164
1141
  else
1165
- default_storage_type = root_storage_types.find {|t| t['value'].to_s == volume['storageType'].to_s}
1142
+ default_storage_type = root_storage_types.find {|t| t['value'].to_s == (volume['typeId'] || volume['storageType']).to_s}
1166
1143
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => default_storage_type ? default_storage_type['name'] : volume['storageType']}], options[:options])
1167
1144
  storage_type_id = v_prompt[field_context]['storageType']
1168
1145
  storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
@@ -1198,6 +1175,11 @@ module Morpheus::Cli::ProvisioningHelper
1198
1175
  volume['size'] = nil #volume.delete('size')
1199
1176
  end
1200
1177
  end
1178
+ # root volume storage controller mount input is disabled in the UI.
1179
+ # if plan_info['customizeVolume'] # && storage_type['customStorageController']
1180
+ # cmp = prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options, server)
1181
+ # volume['controllerMountPoint'] = cmp if !cmp.to_s.empty?
1182
+ # end
1201
1183
  else
1202
1184
  # might need different logic here ? =o
1203
1185
  #volume['size'] = plan_size
@@ -1217,29 +1199,254 @@ module Morpheus::Cli::ProvisioningHelper
1217
1199
  has_another_volume = (options[:options] && options[:options]["dataVolume#{volume_index}"]) || (options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index])
1218
1200
  add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add data volume?", {:default => (options[:defaultAddFirstDataVolume] == true && volume_index == 1)}))
1219
1201
  while add_another_volume do
1220
- #puts "Configure Data #{volume_index} Volume"
1202
+ #puts "Configure Data #{volume_index} Volume"
1203
+
1204
+ field_context = "dataVolume#{volume_index}"
1205
+
1206
+ #volume_label = (volume_index == 1 ? 'data' : "data #{volume_index}")
1207
+ volume_label = "data-#{volume_index}"
1208
+ volume = {
1209
+ #'id' => -1,
1210
+ 'rootVolume' => false,
1211
+ 'name' => volume_label,
1212
+ 'size' => plan_size,
1213
+ 'sizeId' => nil,
1214
+ 'storageType' => nil,
1215
+ 'datastoreId' => nil
1216
+ }
1217
+ if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index]
1218
+ volume = options[:options]['volumes'][volume_index]
1219
+ end
1220
+
1221
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => volume['storageType']}], options[:options])
1222
+ storage_type_id = v_prompt[field_context]['storageType']
1223
+ volume['storageType'] = storage_type_id
1224
+ storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1225
+
1226
+ # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1227
+ custom_size_options = []
1228
+ if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1229
+ plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1230
+ if !opt.nil?
1231
+ custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1232
+ end
1233
+ end
1234
+ end
1235
+
1236
+ if plan_info['customizeVolume'] && storage_type['customLabel']
1237
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Disk #{volume_index} Volume Label", 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1238
+ volume['name'] = v_prompt[field_context]['name']
1239
+ end
1240
+ if plan_info['customizeVolume'] && storage_type['customSize']
1241
+ if custom_size_options.empty?
1242
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => "Disk #{volume_index} Volume Size (GB)", 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1243
+ volume['size'] = v_prompt[field_context]['size']
1244
+ volume['sizeId'] = nil #volume.delete('sizeId')
1245
+ else
1246
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.', 'defaultValue' => volume['sizeId']}], options[:options])
1247
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1248
+ volume['size'] = nil #volume.delete('size')
1249
+ end
1250
+ else
1251
+ # might need different logic here ? =o
1252
+ volume['size'] = plan_size
1253
+ volume['sizeId'] = nil #volume.delete('sizeId')
1254
+ end
1255
+ #todo: Pass in virtual_image to prompt based on its storage_controllers (and volume configuration)
1256
+ if plan_info['customizeVolume'] # && storage_type['customStorageController']
1257
+ cmp = prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options, nil)
1258
+ volume['controllerMountPoint'] = cmp if !cmp.to_s.empty?
1259
+ end
1260
+ if !datastore_options.empty?
1261
+ 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.', 'defaultValue' => volume['datastoreId']}], options[:options])
1262
+ volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1263
+ end
1264
+
1265
+ volumes << volume
1266
+
1267
+ volume_index += 1
1268
+ if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index]
1269
+ add_another_volume = true
1270
+ elsif plan_info['maxDisk'] && volume_index >= plan_info['maxDisk']
1271
+ # todo: should maxDisk check consider the root volume too?
1272
+ add_another_volume = false
1273
+ else
1274
+ has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1275
+ add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another data volume?", {:default => false}))
1276
+ end
1277
+
1278
+ end
1279
+
1280
+ end
1281
+
1282
+ return volumes
1283
+ end
1284
+
1285
+
1286
+ # This recreates the behavior of multi_disk.js
1287
+ # returns array of volumes based on service plan options (plan_info)
1288
+ def prompt_resize_volumes(current_volumes, plan_info, provision_type, options={}, server=nil)
1289
+ #puts "Configure Volumes:"
1290
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1291
+
1292
+ current_root_volume = current_volumes[0]
1293
+
1294
+ volumes = []
1295
+
1296
+ plan_size = nil
1297
+ if plan_info['maxStorage'].to_i > 0
1298
+ plan_size = plan_info['maxStorage'].to_i / (1024 * 1024 * 1024)
1299
+ end
1300
+
1301
+ root_storage_types = []
1302
+ if plan_info['rootStorageTypes']
1303
+ plan_info['rootStorageTypes'].each do |opt|
1304
+ if !opt.nil?
1305
+ root_storage_types << {'name' => opt['name'], 'value' => opt['id']}
1306
+ end
1307
+ end
1308
+ end
1309
+
1310
+ storage_types = []
1311
+ if plan_info['storageTypes']
1312
+ plan_info['storageTypes'].each do |opt|
1313
+ if !opt.nil?
1314
+ storage_types << {'name' => opt['name'], 'value' => opt['id']}
1315
+ end
1316
+ end
1317
+ end
1318
+
1319
+ # no longer use the plan datastores info, always load from data-stores api instead
1320
+ # note: this modifies service_plan in place with 'datastores' and 'autoOptions'
1321
+ group_id = cloud_id = options['siteId']
1322
+ cloud_id = options['zoneId']
1323
+ server_resource_pool_id = server ? (server['resourcePoolId'] || (server['resourcePool'] ? server['resourcePool']['id'] : nil)) : nil
1324
+ resource_pool_id = options['resourcePoolId'] || server_resource_pool_id
1325
+ if provision_type['hasDatastore']
1326
+ datastore_params = {'zoneId' => cloud_id, 'siteId' => group_id, 'resourcePoolId' => resource_pool_id}
1327
+ load_service_plan_datastores(plan_info, provision_type, datastore_params)
1328
+ end
1329
+ datastore_options = []
1330
+ if plan_info['supportsAutoDatastore']
1331
+ if plan_info['autoOptions']
1332
+ plan_info['autoOptions'].each do |opt|
1333
+ if !opt.nil?
1334
+ datastore_options << {'name' => opt['name'], 'value' => opt['id']}
1335
+ end
1336
+ end
1337
+ end
1338
+ end
1339
+ if plan_info['datastores']
1340
+ plan_info['datastores'].each do |k, v|
1341
+ v.each do |opt|
1342
+ if !opt.nil?
1343
+ datastore_options << {'name' => "#{k}: #{opt['name']}", 'value' => opt['id']}
1344
+ end
1345
+ end
1346
+ end
1347
+ end
1348
+
1349
+ #puts "Configure Root Volume"
1350
+
1351
+ field_context = "rootVolume"
1352
+ storage_type = nil
1353
+ storage_type_id = nil
1354
+ if root_storage_types.empty?
1355
+ # this means there's no configuration, just send a single root volume to the server
1356
+ storage_type_id = nil
1357
+ storage_type = nil
1358
+ else
1359
+ #v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1360
+ #storage_type_id = v_prompt[field_context]['storageType']
1361
+ storage_type_id = (current_root_volume['typeId'] || current_root_volume['storageType'] || (current_root_volume['type']['id'] rescue nil))
1362
+ storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1363
+ end
1364
+
1365
+ # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1366
+ root_custom_size_options = []
1367
+ if plan_info['rootCustomSizeOptions'] && plan_info['rootCustomSizeOptions'][storage_type_id.to_s]
1368
+ plan_info['rootCustomSizeOptions'][storage_type_id.to_s].each do |opt|
1369
+ if !opt.nil?
1370
+ root_custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1371
+ end
1372
+ end
1373
+ end
1374
+ volume = {
1375
+ 'id' => current_root_volume['id'],
1376
+ 'rootVolume' => true,
1377
+ 'name' => current_root_volume['name'],
1378
+ 'size' => get_volume_size(current_root_volume) || plan_size,
1379
+ 'sizeId' => nil,
1380
+ 'storageType' => storage_type_id,
1381
+ 'datastoreId' => current_root_volume['datastoreId']
1382
+ }
1383
+ if !current_root_volume['controllerMountPoint'].to_s.empty?
1384
+ volume['controllerMountPoint'] = current_root_volume['controllerMountPoint']
1385
+ end
1386
+ if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customLabel']
1387
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Root Volume Label', 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1388
+ volume['name'] = v_prompt[field_context]['name']
1389
+ end
1390
+ if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customSize']
1391
+ # provision_type['rootDiskSizeKnown'] == false means size cannot be changed
1392
+ if provision_type['rootDiskSizeKnown'] == false
1393
+ # volume['size'] = plan_size if plan_size.to_i != 0
1394
+ else
1395
+ if root_custom_size_options.empty?
1396
+ 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])
1397
+ volume['size'] = v_prompt[field_context]['size']
1398
+ volume['sizeId'] = nil #volume.delete('sizeId')
1399
+ else
1400
+ 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])
1401
+ volume['sizeId'] = v_prompt[field_context]['sizeId']
1402
+ volume['size'] = nil #volume.delete('size')
1403
+ end
1404
+ end
1405
+ else
1406
+ # might need different logic here ? =o
1407
+ # volume['size'] = plan_size
1408
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1409
+ end
1410
+ # if !datastore_options.empty?
1411
+ # 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])
1412
+ # volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1413
+ # end
1414
+
1415
+ volumes << volume
1416
+
1417
+ # modify or delete existing data volumes
1418
+ (1..(current_volumes.size-1)).each do |volume_index|
1419
+ current_volume = current_volumes[volume_index]
1420
+ if current_volume
1221
1421
 
1222
- field_context = "dataVolume#{volume_index}"
1422
+ field_context = "dataVolume#{volume_index}"
1223
1423
 
1224
- volume_label = (volume_index == 1 ? 'data' : "data #{volume_index}")
1424
+ action_options = [{'name' => 'Modify', 'value' => 'modify'}, {'name' => 'Keep', 'value' => 'keep'}, {'name' => 'Delete', 'value' => 'delete'}]
1425
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'action', 'type' => 'select', 'fieldLabel' => "Modify/Keep/Delete volume '#{current_volume['name']}'", 'selectOptions' => action_options, 'required' => true, 'defaultValue' => 'keep', 'description' => 'Modify, Keep or Delete existing data volume?'}], options[:options])
1426
+ volume_action = v_prompt[field_context]['action']
1427
+
1428
+ if volume_action == 'delete'
1429
+ # deleted volume is just excluded from post params
1430
+ next
1431
+ elsif volume_action == 'keep'
1225
1432
  volume = {
1226
- #'id' => -1,
1433
+ 'id' => current_volume['id'].to_i,
1227
1434
  'rootVolume' => false,
1228
- 'name' => volume_label,
1229
- 'size' => plan_size,
1435
+ 'name' => current_volume['name'],
1436
+ 'size' => get_volume_size(current_volume) || plan_size,
1230
1437
  'sizeId' => nil,
1231
- 'storageType' => nil,
1232
- 'datastoreId' => nil
1438
+ 'storageType' => (current_volume['typeId'] || current_volume['storageType'] || (current_volume['type']['id'] rescue nil)),
1439
+ 'datastoreId' => current_volume['datastoreId']
1233
1440
  }
1234
- if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index]
1235
- volume = options[:options]['volumes'][volume_index]
1441
+ if !current_volume['controllerMountPoint'].to_s.empty?
1442
+ volume['controllerMountPoint'] = current_volume['controllerMountPoint']
1236
1443
  end
1237
-
1238
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.', 'defaultValue' => volume['storageType']}], options[:options])
1239
- storage_type_id = v_prompt[field_context]['storageType']
1240
- volume['storageType'] = storage_type_id
1444
+ volumes << volume
1445
+ else
1446
+ # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1447
+ # storage_type_id = v_prompt[field_context]['storageType']
1448
+ storage_type_id = (current_volume['typeId'] || current_volume['storageType'] || (current_volume['type']['id'] rescue nil))
1241
1449
  storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1242
-
1243
1450
  # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1244
1451
  custom_size_options = []
1245
1452
  if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
@@ -1250,6 +1457,19 @@ module Morpheus::Cli::ProvisioningHelper
1250
1457
  end
1251
1458
  end
1252
1459
 
1460
+ volume = {
1461
+ 'id' => current_volume['id'].to_i,
1462
+ 'rootVolume' => false,
1463
+ 'name' => current_volume['name'],
1464
+ 'size' => get_volume_size(current_volume) || plan_size,
1465
+ 'sizeId' => nil,
1466
+ 'storageType' => (current_volume['typeId'] || current_volume['storageType'] || (current_volume['type']['id'] rescue current_volume['type'])),
1467
+ 'datastoreId' => current_volume['datastoreId']
1468
+ }
1469
+ if !current_volume['controllerMountPoint'].to_s.empty?
1470
+ volume['controllerMountPoint'] = current_volume['controllerMountPoint']
1471
+ end
1472
+
1253
1473
  if plan_info['customizeVolume'] && storage_type['customLabel']
1254
1474
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Disk #{volume_index} Volume Label", 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1255
1475
  volume['name'] = v_prompt[field_context]['name']
@@ -1260,331 +1480,323 @@ module Morpheus::Cli::ProvisioningHelper
1260
1480
  volume['size'] = v_prompt[field_context]['size']
1261
1481
  volume['sizeId'] = nil #volume.delete('sizeId')
1262
1482
  else
1263
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.', 'defaultValue' => volume['sizeId']}], options[:options])
1483
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1264
1484
  volume['sizeId'] = v_prompt[field_context]['sizeId']
1265
1485
  volume['size'] = nil #volume.delete('size')
1266
1486
  end
1267
1487
  else
1268
1488
  # might need different logic here ? =o
1269
- volume['size'] = plan_size
1270
- volume['sizeId'] = nil #volume.delete('sizeId')
1271
- end
1272
- if !datastore_options.empty?
1273
- 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.', 'defaultValue' => volume['datastoreId']}], options[:options])
1274
- volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1489
+ # volume['size'] = plan_size
1490
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1275
1491
  end
1492
+ # if plan_info['customizeVolume'] # && storage_type['customStorageController']
1493
+ # cmp = prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options, server)
1494
+ # volume['controllerMountPoint'] = cmp if !cmp.to_s.empty?
1495
+ # end
1496
+ # if !datastore_options.empty?
1497
+ # 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])
1498
+ # volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1499
+ # end
1276
1500
 
1277
1501
  volumes << volume
1278
1502
 
1279
- volume_index += 1
1280
- if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index]
1281
- add_another_volume = true
1282
- elsif plan_info['maxDisk'] && volume_index >= plan_info['maxDisk']
1283
- # todo: should maxDisk check consider the root volume too?
1284
- add_another_volume = false
1285
- else
1286
- has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1287
- add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another data volume?", {:default => false}))
1288
- end
1289
-
1290
1503
  end
1291
1504
 
1292
1505
  end
1293
-
1294
- return volumes
1295
1506
  end
1296
1507
 
1297
1508
 
1298
- # This recreates the behavior of multi_disk.js
1299
- # returns array of volumes based on service plan options (plan_info)
1300
- def prompt_resize_volumes(current_volumes, plan_info, provision_type, options={})
1301
- #puts "Configure Volumes:"
1302
- no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1303
-
1304
- current_root_volume = current_volumes[0]
1305
-
1306
- volumes = []
1307
-
1308
- plan_size = nil
1309
- if plan_info['maxStorage']
1310
- plan_size = plan_info['maxStorage'].to_i / (1024 * 1024 * 1024)
1311
- end
1312
-
1313
- root_storage_types = []
1314
- if plan_info['rootStorageTypes']
1315
- plan_info['rootStorageTypes'].each do |opt|
1316
- if !opt.nil?
1317
- root_storage_types << {'name' => opt['name'], 'value' => opt['id']}
1318
- end
1319
- end
1320
- end
1321
-
1322
- storage_types = []
1323
- if plan_info['storageTypes']
1324
- plan_info['storageTypes'].each do |opt|
1325
- if !opt.nil?
1326
- storage_types << {'name' => opt['name'], 'value' => opt['id']}
1327
- end
1328
- end
1329
- end
1509
+ if plan_info['addVolumes']
1510
+ volume_index = current_volumes.size
1511
+ has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1512
+ add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add data volume?", {:default => false}))
1513
+ while add_another_volume do
1514
+ #puts "Configure Data #{volume_index} Volume"
1515
+ previous_volume = volumes.last
1516
+ default_storage_type_id = (previous_volume['typeId'] || previous_volume['storageType'] || (previous_volume['type']['id'] rescue previous_volume['type']))
1517
+ storage_type_match = storage_types.find {|type| type['value'] == default_storage_type_id}
1518
+ default_storage_type = storage_type_match ? (storage_type_match['name'] || storage_type_match['value']) : nil
1519
+ field_context = "dataVolume#{volume_index}"
1520
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'defaultValue' => default_storage_type, 'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1521
+ storage_type_id = v_prompt[field_context]['storageType']
1522
+ storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1330
1523
 
1331
- datastore_options = []
1332
- if plan_info['supportsAutoDatastore']
1333
- if plan_info['autoOptions']
1334
- plan_info['autoOptions'].each do |opt|
1524
+ # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1525
+ custom_size_options = []
1526
+ if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1527
+ plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1335
1528
  if !opt.nil?
1336
- datastore_options << {'name' => opt['name'], 'value' => opt['id']}
1529
+ custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1337
1530
  end
1338
1531
  end
1339
1532
  end
1340
- end
1341
- if plan_info['datastores']
1342
- plan_info['datastores'].each do |k, v|
1343
- v.each do |opt|
1344
- if !opt.nil?
1345
- datastore_options << {'name' => "#{k}: #{opt['name']}", 'value' => opt['id']}
1346
- end
1347
- end
1348
- end
1349
- end
1350
-
1351
- #puts "Configure Root Volume"
1352
-
1353
- field_context = "rootVolume"
1354
1533
 
1355
- if root_storage_types.empty?
1356
- # this means there's no configuration, just send a single root volume to the server
1357
- storage_type_id = nil
1358
- storage_type = nil
1359
- else
1360
- #v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => 'Root Storage Type', 'selectOptions' => root_storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1361
- #storage_type_id = v_prompt[field_context]['storageType']
1362
- storage_type_id = current_root_volume['type'] || current_root_volume['storageType']
1363
- storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1364
- end
1534
+ #volume_label = (volume_index == 1 ? 'data' : "data #{volume_index}")
1535
+ volume_label = "data-#{volume_index}"
1536
+ volume = {
1537
+ 'id' => -1,
1538
+ 'rootVolume' => false,
1539
+ 'name' => volume_label,
1540
+ 'size' => plan_size,
1541
+ 'sizeId' => nil,
1542
+ 'storageType' => storage_type_id,
1543
+ 'datastoreId' => nil
1544
+ }
1365
1545
 
1366
- # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1367
- root_custom_size_options = []
1368
- if plan_info['rootCustomSizeOptions'] && plan_info['rootCustomSizeOptions'][storage_type_id.to_s]
1369
- plan_info['rootCustomSizeOptions'][storage_type_id.to_s].each do |opt|
1370
- if !opt.nil?
1371
- root_custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1372
- end
1546
+ if plan_info['customizeVolume'] && storage_type['customLabel']
1547
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Disk #{volume_index} Volume Label", 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume_label}], options[:options])
1548
+ volume['name'] = v_prompt[field_context]['name']
1373
1549
  end
1374
- end
1375
-
1376
- volume = {
1377
- 'id' => current_root_volume['id'],
1378
- 'rootVolume' => true,
1379
- 'name' => current_root_volume['name'],
1380
- 'size' => current_root_volume['size'] > 0 ? current_root_volume['size'] : plan_size,
1381
- 'sizeId' => nil,
1382
- 'storageType' => storage_type_id,
1383
- 'datastoreId' => current_root_volume['datastoreId']
1384
- }
1385
-
1386
- if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customLabel']
1387
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Root Volume Label', 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1388
- volume['name'] = v_prompt[field_context]['name']
1389
- end
1390
- if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customSize']
1391
- # provision_type['rootDiskSizeKnown'] == false means size cannot be changed
1392
- if provision_type['rootDiskSizeKnown'] == false
1393
- # volume['size'] = plan_size if plan_size.to_i != 0
1394
- else
1395
- if root_custom_size_options.empty?
1396
- 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])
1550
+ if plan_info['customizeVolume'] && storage_type['customSize']
1551
+ if custom_size_options.empty?
1552
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => "Disk #{volume_index} Volume Size (GB)", 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1397
1553
  volume['size'] = v_prompt[field_context]['size']
1398
1554
  volume['sizeId'] = nil #volume.delete('sizeId')
1399
1555
  else
1400
- 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])
1556
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1401
1557
  volume['sizeId'] = v_prompt[field_context]['sizeId']
1402
1558
  volume['size'] = nil #volume.delete('size')
1403
1559
  end
1560
+ else
1561
+ # might need different logic here ? =o
1562
+ # volume['size'] = plan_size
1563
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1564
+ end
1565
+ if plan_info['customizeVolume'] # && storage_type['customStorageController']
1566
+ cmp = prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options, server)
1567
+ volume['controllerMountPoint'] = cmp if !cmp.to_s.empty?
1568
+ end
1569
+ if !datastore_options.empty?
1570
+ 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.', 'defaultValue' => current_root_volume['datastoreId']}], options[:options])
1571
+ volume['datastoreId'] = v_prompt[field_context]['datastoreId'] unless v_prompt[field_context]['datastoreId'].to_s.empty?
1404
1572
  end
1405
- else
1406
- # might need different logic here ? =o
1407
- # volume['size'] = plan_size
1408
- # volume['sizeId'] = nil #volume.delete('sizeId')
1409
- end
1410
- # if !datastore_options.empty?
1411
- # 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])
1412
- # volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1413
- # end
1414
1573
 
1415
- volumes << volume
1416
-
1417
- # modify or delete existing data volumes
1418
- (1..(current_volumes.size-1)).each do |volume_index|
1419
- current_volume = current_volumes[volume_index]
1420
- if current_volume
1421
-
1422
- field_context = "dataVolume#{volume_index}"
1423
-
1424
- action_options = [{'name' => 'Modify', 'value' => 'modify'}, {'name' => 'Keep', 'value' => 'keep'}, {'name' => 'Delete', 'value' => 'delete'}]
1425
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'action', 'type' => 'select', 'fieldLabel' => "Modify/Keep/Delete volume '#{current_volume['name']}'", 'selectOptions' => action_options, 'required' => true, 'defaultValue' => 'keep', 'description' => 'Modify, Keep or Delete existing data volume?'}], options[:options])
1426
- volume_action = v_prompt[field_context]['action']
1427
-
1428
- if volume_action == 'delete'
1429
- # deleted volume is just excluded from post params
1430
- next
1431
- elsif volume_action == 'keep'
1432
- volume = {
1433
- 'id' => current_volume['id'].to_i,
1434
- 'rootVolume' => false,
1435
- 'name' => current_volume['name'],
1436
- 'size' => current_volume['size'] > (plan_size || 0) ? current_volume['size'] : plan_size,
1437
- 'sizeId' => nil,
1438
- 'storageType' => (current_volume['type'] || current_volume['storageType']),
1439
- 'datastoreId' => current_volume['datastoreId']
1440
- }
1441
- volumes << volume
1442
- else
1443
- # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1444
- # storage_type_id = v_prompt[field_context]['storageType']
1445
- storage_type_id = current_volume['type'] || current_volume['storageType']
1446
- storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1447
- # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1448
- custom_size_options = []
1449
- if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1450
- plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1451
- if !opt.nil?
1452
- custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1453
- end
1454
- end
1455
- end
1574
+ volumes << volume
1456
1575
 
1457
- volume = {
1458
- 'id' => current_volume['id'].to_i,
1459
- 'rootVolume' => false,
1460
- 'name' => current_volume['name'],
1461
- 'size' => current_volume['size'] ? current_volume['size'] : (plan_size || 0),
1462
- 'sizeId' => nil,
1463
- 'storageType' => (current_volume['type'] || current_volume['storageType']),
1464
- 'datastoreId' => current_volume['datastoreId']
1465
- }
1466
-
1467
- if plan_info['customizeVolume'] && storage_type['customLabel']
1468
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Disk #{volume_index} Volume Label", 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume['name']}], options[:options])
1469
- volume['name'] = v_prompt[field_context]['name']
1470
- end
1471
- if plan_info['customizeVolume'] && storage_type['customSize']
1472
- if custom_size_options.empty?
1473
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => "Disk #{volume_index} Volume Size (GB)", 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => volume['size']}], options[:options])
1474
- volume['size'] = v_prompt[field_context]['size']
1475
- volume['sizeId'] = nil #volume.delete('sizeId')
1476
- else
1477
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1478
- volume['sizeId'] = v_prompt[field_context]['sizeId']
1479
- volume['size'] = nil #volume.delete('size')
1480
- end
1481
- else
1482
- # might need different logic here ? =o
1483
- # volume['size'] = plan_size
1484
- # volume['sizeId'] = nil #volume.delete('sizeId')
1485
- end
1486
- # if !datastore_options.empty?
1487
- # 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])
1488
- # volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1489
- # end
1576
+ # todo: should maxDisk check consider the root volume too?
1577
+ if plan_info['maxDisk'] && volume_index >= plan_info['maxDisk']
1578
+ add_another_volume = false
1579
+ else
1580
+ volume_index += 1
1581
+ has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1582
+ add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another data volume?", {default:false}))
1583
+ end
1490
1584
 
1491
- volumes << volume
1585
+ end
1492
1586
 
1493
- end
1587
+ end
1588
+
1589
+ return volumes
1590
+ end
1494
1591
 
1592
+ # loads datastores and modifies service_plan in place
1593
+ # populating datastores: {datastores: [],clusters: []}, supportsAutoDatastore: true, autoOptions: []
1594
+ def load_service_plan_datastores(service_plan, provision_type, params)
1595
+ begin
1596
+ selectable_datastores = datastores_interface.list(params)
1597
+ service_plan['datastores'] = {'clusters' => [], 'datastores' => []}
1598
+ ['clusters', 'datastores'].each do |type|
1599
+ service_plan['datastores'][type] ||= []
1600
+ selectable_datastores[type].reject { |ds| service_plan['datastores'][type].find {|it| it['id'] == ds['id']} }.each { |ds|
1601
+ service_plan['datastores'][type] << ds
1602
+ }
1603
+ end
1604
+ rescue => error
1605
+ Morpheus::Logging::DarkPrinter.puts "Unable to load available data-stores, using datastores option source instead." if Morpheus::Logging.debug?
1606
+ end
1607
+
1608
+ if provision_type && provision_type['supportsAutoDatastore']
1609
+ service_plan['supportsAutoDatastore'] = true
1610
+ service_plan['autoOptions'] ||= []
1611
+ if service_plan['datastores'] && service_plan['datastores']['clusters']
1612
+ if service_plan['datastores']['clusters'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'autoCluster'}
1613
+ service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
1495
1614
  end
1615
+ else
1616
+ service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
1496
1617
  end
1618
+ if service_plan['datastores'] && service_plan['datastores']['datastores']
1619
+ if service_plan['datastores']['datastores'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'auto'}
1620
+ service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
1621
+ end
1622
+ else
1623
+ service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
1624
+ end
1625
+ end
1626
+ return service_plan
1627
+ end
1497
1628
 
1629
+ def get_volume_size(volume)
1630
+ if volume['size'].to_i > 0
1631
+ volume['size']
1632
+ elsif volume['maxStorage'].to_i > 0
1633
+ volume['maxStorage'].to_i / (1024**3)
1634
+ else
1635
+ nil
1636
+ end
1637
+ end
1498
1638
 
1499
- if plan_info['addVolumes']
1500
- volume_index = current_volumes.size
1501
- has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1502
- add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add data volume?", {:default => false}))
1503
- while add_another_volume do
1504
- #puts "Configure Data #{volume_index} Volume"
1505
-
1506
- current_root_volume_type = current_root_volume['type']
1507
- storage_type_match = storage_types.find {|type| type['value'] == current_root_volume_type}
1508
- default_storage_type = storage_type_match ? current_root_volume_type : storage_types[0]['value']
1509
- field_context = "dataVolume#{volume_index}"
1510
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'defaultValue' => default_storage_type, 'fieldContext' => field_context, 'fieldName' => 'storageType', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Type", 'selectOptions' => storage_types, 'required' => true, 'defaultFirstOption' => true, 'skipSingleOption' => true, 'description' => 'Choose a storage type.'}], options[:options])
1511
- storage_type_id = v_prompt[field_context]['storageType']
1512
- storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1513
-
1514
- # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1515
- custom_size_options = []
1516
- if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1517
- plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1518
- if !opt.nil?
1519
- custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1520
- end
1521
- end
1639
+ def prompt_volume_controller_mount_point(volumes, field_context, volume_index, volume, plan_info, provision_type, options={}, server=nil)
1640
+ begin
1641
+ # The /api/provision-types has always returned all the controllerTypes, yay
1642
+ # It did not return hasStorageControllers: true|false though, so fallback to checking if controllerTypes.size > 0.
1643
+ # has_storage_controllers = provision_type['hasStorageControllers'] || ['vmware'].include?(provision_type['code'])
1644
+ # storage_controller_types = has_storage_controllers ? options_interface.options_for_source('storageControllerTypes',{provisionTypeId:provision_type['id']})['data'] : []
1645
+ storage_controller_types = provision_type['controllerTypes']
1646
+ has_storage_controllers = provision_type['hasStorageControllers'] || (storage_controller_types && !storage_controller_types.empty?)
1647
+ storage_controllers = server ? server['controllers'] : []
1648
+ if has_storage_controllers
1649
+ # existing storage controllers from server
1650
+ storage_controller_options = []
1651
+ storage_controller_options.push({'name' => 'Auto', 'value' => 'auto'})
1652
+ storage_controller_options.push({'name' => 'New', 'value' => -1})
1653
+ storage_controllers.each_with_index do |sc, index|
1654
+ # storage_controller_options.push({'name' => sc['name'], 'value' => sc['id']})
1655
+ # API did not always return controller busNumber so extract from the volume mountPoint if needed
1656
+ if sc['busNumber'].nil?
1657
+ matching_volume = volumes.find {|v| v['controllerMountPoint'] && v['controllerMountPoint'].split(':')[0] == sc['id'].to_s }
1658
+ if matching_volume
1659
+ sc['busNumber'] = matching_volume['controllerMountPoint'].split(":")[1]
1522
1660
  end
1523
-
1524
- volume_label = (volume_index == 1 ? 'data' : "data #{volume_index}")
1525
- volume = {
1526
- 'id' => -1,
1527
- 'rootVolume' => false,
1528
- 'name' => volume_label,
1529
- 'size' => plan_size,
1530
- 'sizeId' => nil,
1531
- 'storageType' => storage_type_id,
1532
- 'datastoreId' => nil
1533
- }
1534
-
1535
- if plan_info['customizeVolume'] && storage_type['customLabel']
1536
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => "Disk #{volume_index} Volume Label", 'required' => true, 'description' => 'Enter a volume label.', 'defaultValue' => volume_label}], options[:options])
1537
- volume['name'] = v_prompt[field_context]['name']
1661
+ end
1662
+ sct = storage_controller_types.find { |t| t['id'] == sc['type']['id'] }
1663
+ storage_controller_options.push({'name' => "#{sct['category'].to_s.upcase rescue ''} #{sc['busNumber'] || index}", 'value' => sc['id']})
1664
+ end if storage_controllers
1665
+ # The controller mount point specification for this volume in the format: `id:busNumber:typeId:unitNumber"`
1666
+ # For new storage controllers the id is passed as -1, so an example value would be: `"-1:1:6:0"` which translates to id: -1 (new), busNumber: 1, storage controller type id: 6 (SCSI VMware Paravirtual), unit number: 0.
1667
+ # Split -O dataVolume1.controllerMountPoint="-1:1:6:0" into its components.
1668
+ controller_mount_point = ""
1669
+ controller_id, bus_number, controller_type_id, unit_number = nil, nil, nil, nil
1670
+ if options[:options][field_context] && options[:options][field_context]['controllerMountPoint']
1671
+ parts = options[:options][field_context]['controllerMountPoint'].split(":").collect {|it| it.to_s.strip }
1672
+ controller_id, bus_number, controller_type_id, unit_number = parts[0],parts[1],parts[2],parts[3]
1673
+ # if we dont have every part then clear the options value so it prompt down below
1674
+ if controller_id.to_s.empty? || bus_number.to_s.empty? || controller_type_id.to_s.empty? || unit_number.to_s.empty?
1675
+ options[:options][field_context].delete('controllerMountPoint')
1676
+ end
1677
+ end
1678
+ # Select Storage Controller, auto, new or existing
1679
+ # Choose Existing or Add a new controller which requires selecting type selecting type.
1680
+ if !controller_id.to_s.empty?
1681
+ controller_id = controller_id
1682
+ else
1683
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'controllerId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Controller #{volume_index}", 'selectOptions' => storage_controller_options, 'required' => true, 'description' => 'Choose a storage controller.', 'defaultValue' => (volume['controllerId'] || 'auto') }], options[:options])
1684
+ controller_id = v_prompt[field_context]['controllerId']
1685
+ end
1686
+ skip_controller_mount_point = false
1687
+ if controller_id.to_s == 'auto'
1688
+ # auto just means omit controllerMountPoint
1689
+ skip_controller_mount_point = true
1690
+ elsif controller_id.to_i < 1
1691
+ # -1 (or anything but a positive integer) means new controller
1692
+ controller_id = -1
1693
+ elsif controller_id
1694
+ controller_id = controller_id.to_i
1695
+ end
1696
+ if skip_controller_mount_point
1697
+ # skip mount point parameter
1698
+ # volume.delete('controllerMountPoint')
1699
+ # volume.delete('controllerId')
1700
+ return nil
1701
+ else
1702
+ storage_controller_type = nil
1703
+ storage_controller = nil
1704
+ # New Controller?
1705
+ if controller_id == -1
1706
+ #[field_context]['controllerTypeId'] controller_type_id = unless controller_type_id.to_s.empty?
1707
+ storage_controller_type_options = storage_controller_types.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}.collect {|it| {"name" => it["name"], "value" => it["id"]}}
1708
+ if !controller_type_id.to_s.empty?
1709
+ options[:options][field_context]['controllerTypeId'] = controller_type_id
1538
1710
  end
1539
- if plan_info['customizeVolume'] && storage_type['customSize']
1540
- if custom_size_options.empty?
1541
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'size', 'type' => 'number', 'fieldLabel' => "Disk #{volume_index} Volume Size (GB)", 'required' => true, 'description' => 'Enter a volume size (GB).', 'defaultValue' => plan_size}], options[:options])
1542
- volume['size'] = v_prompt[field_context]['size']
1543
- volume['sizeId'] = nil #volume.delete('sizeId')
1544
- else
1545
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'sizeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Volume Size", 'selectOptions' => custom_size_options, 'required' => true, 'description' => 'Choose a volume size.'}], options[:options])
1546
- volume['sizeId'] = v_prompt[field_context]['sizeId']
1547
- volume['size'] = nil #volume.delete('size')
1548
- end
1549
- else
1550
- # might need different logic here ? =o
1551
- # volume['size'] = plan_size
1552
- # volume['sizeId'] = nil #volume.delete('sizeId')
1711
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'controllerTypeId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Storage Controller Type", 'selectOptions' => storage_controller_type_options, 'required' => true, 'description' => 'Choose a storage controller type.'}], options[:options])
1712
+ controller_type_id = v_prompt[field_context]['controllerTypeId']
1713
+ # find the selected controller type
1714
+ storage_controller_type = storage_controller_types.find {|it| it['id'] == controller_type_id }
1715
+ if storage_controller_type.nil?
1716
+ raise_command_error("Storage controller type not found for id #{controller_type_id}")
1553
1717
  end
1554
-
1555
- if datastore_options.empty? && storage_type['hasDatastore'] != false
1556
- begin
1557
- datastore_res = datastores_interface.list({'poolId' => current_root_volume['resourcePoolId'], 'resourcePoolId' => current_root_volume['resourcePoolId'], 'zoneId' => options['zoneId'], 'siteId' => options['siteId']})['datastores']
1558
- datastore_res.each do |opt|
1559
- datastore_options << {'name' => opt['name'], 'value' => opt['id']}
1718
+ storage_controller = {'id' => -1, 'type' => {'id' => storage_controller_type['id']}}
1719
+ max_devices = storage_controller_type['maxDevices'] || 1
1720
+ reserved_bus_numbers = storage_controllers.select {|it| it['category'] == storage_controller_type['category'] }.collect {|it| it['busNumber'].to_s}.uniq
1721
+ bus_number = bus_number.to_i || 0
1722
+ while true
1723
+ if !reserved_bus_numbers.include?(bus_number.to_s)
1724
+ break
1725
+ else
1726
+ next_bus_number = bus_number + 1
1727
+ if next_bus_number >= max_devices
1728
+ Morpheus::Logging::DarkPrinter.puts "Failed to find an available bus number!"
1729
+ break
1560
1730
  end
1561
- rescue
1562
- datastore_options = []
1731
+ bus_number = next_bus_number
1563
1732
  end
1564
1733
  end
1565
- if !datastore_options.empty?
1566
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'defaultValue' => current_root_volume['datastoreId'],'fieldContext' => field_context, 'fieldName' => 'datastoreId', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Datastore", 'selectOptions' => datastore_options, 'required' => true, 'description' => 'Choose a datastore.'}], options[:options])
1567
- volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1734
+ storage_controller['busNumber'] = bus_number.to_s
1735
+ storage_controller['name'] = "#{storage_controller_type['category'].to_s.upcase} #{bus_number}"
1736
+ # storage_controller['unitNumber'] = "null"
1737
+ # add new controller to the list
1738
+ storage_controllers.push(storage_controller)
1739
+ else
1740
+ # existing controller
1741
+ # controller_type_id = controller_type_id
1742
+ # find the selected controller type
1743
+ storage_controller = storage_controllers.find {|it| it['id'] == controller_id.to_i }
1744
+ if storage_controller.nil?
1745
+ raise_command_error("Storage controller type not found for id #{controller_type_id}")
1568
1746
  end
1569
-
1570
- volumes << volume
1571
-
1572
- # todo: should maxDisk check consider the root volume too?
1573
- if plan_info['maxDisk'] && volume_index >= plan_info['maxDisk']
1574
- add_another_volume = false
1575
- else
1576
- volume_index += 1
1577
- has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1578
- add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another data volume?"))
1747
+ end
1748
+ # Choose Controller Mount Point
1749
+ mount_point_options = []
1750
+ controller_list = storage_controllers
1751
+ # only show the selected controller
1752
+ controller_list = [storage_controller]
1753
+ controller_list.each do |sc|
1754
+ sct = storage_controller_types.find { |t| t['id'] == sc['type']['id'] }
1755
+ category = sct['category']
1756
+ max_devices = sct['maxDevices'] || 1
1757
+ matching_type_ids = storage_controller_types.select { |t| t['category'] == category }.collect {|t| t['id'] }
1758
+ # find available unit numbers by reversing the controllerMountPoint to find matching types, blarghhh
1759
+ reserved_unit_numbers = (volumes || []).collect {|v|
1760
+ if !v['controllerMountPoint'].to_s.empty?
1761
+ parts = v['controllerMountPoint'].split(":")
1762
+ vol_controller_id = parts[0]
1763
+ vol_bus_number = parts[1]
1764
+ vol_sct_id = parts[2]
1765
+ vol_unit_number = parts[3]
1766
+ if matching_type_ids.include?(vol_sct_id.to_i)
1767
+ vol_unit_number.to_i
1768
+ else
1769
+ nil
1770
+ end
1771
+ else
1772
+ nil
1773
+ end
1774
+ }.compact.uniq
1775
+ (0..(max_devices - 1)).each do |index|
1776
+ cmp_value = [sc['id'],sc['busNumber'],sct['id'],index].join(":")
1777
+ if reserved_unit_numbers.include?(index)
1778
+ #mount_point_options << {'name' => "#{category.to_s.upcase} #{sc['busNumber']}:#{index}", 'value' => cmp_value}
1779
+ else
1780
+ mount_point_options << {'name' => "#{category.to_s.upcase} #{sc['busNumber']}:#{index}", 'value' => cmp_value}
1781
+ end
1579
1782
  end
1580
-
1581
1783
  end
1582
-
1784
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'controllerMountPoint', 'type' => 'select', 'fieldLabel' => "Disk #{volume_index} Controller Mount Point", 'selectOptions' => mount_point_options, 'required' => true, 'description' => 'Choose a storage controller mount point.'}], options[:options])
1785
+ controller_mount_point = v_prompt[field_context]['controllerMountPoint']
1786
+ # build controllerMountPoint parameter as "id:busNumber:typeId:unitNumber"
1787
+ #controller_mount_point = [controller_id, bus_number, controller_type_id, unit_number].collect {|it| it.to_s.strip}.join(":")
1788
+ # volume['controllerMountPoint'] = controller_mount_point
1789
+ return controller_mount_point
1583
1790
  end
1584
-
1585
- return volumes
1791
+ else
1792
+ # storage controllers not supported, carry on then
1586
1793
  end
1587
-
1794
+ rescue => e
1795
+ raise e
1796
+ Morpheus::Cli::ErrorHandler.new(my_terminal.stderr).handle_error(e) if Morpheus::Logging.debug?
1797
+ Morpheus::Logging::DarkPrinter.puts "Failed to prompt for storage controller. Proceeding..."
1798
+ end
1799
+ end
1588
1800
 
1589
1801
  # This recreates the behavior of multi_networks.js
1590
1802
  # This is used by both `instances add` and `hosts add`