morpheus-cli 8.0.1 → 8.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -797,7 +797,8 @@ module Morpheus::Cli::ProvisioningHelper
797
797
  resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([resource_pool_option_type],options[:options],api_client,{}, no_prompt, true)
798
798
  resource_pool_prompt.deep_compact!
799
799
  payload.deep_merge!(resource_pool_prompt)
800
- 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()
801
802
  if resource_pool_option_type['fieldContext'] && resource_pool_prompt[resource_pool_option_type['fieldContext']]
802
803
  pool_id = resource_pool_prompt[resource_pool_option_type['fieldContext']][resource_pool_option_type['fieldName']]
803
804
  elsif resource_pool_prompt[resource_pool_option_type['fieldName']]
@@ -834,38 +835,13 @@ module Morpheus::Cli::ProvisioningHelper
834
835
  option_type_list = option_type_list.reject {|opt| ['provisionServerId'].include?(opt['fieldName'])}
835
836
  end
836
837
 
837
- # 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'
838
841
  if options[:select_datastore]
839
- begin
840
- selectable_datastores = datastores_interface.list({'zoneId' => cloud_id, 'siteId' => group_id, 'poolId' => resource_pool['id'], 'resourcePoolId' => resource_pool['id']})
841
- service_plan['datastores'] = {'clusters' => [], 'datastores' => []}
842
- ['clusters', 'datastores'].each do |type|
843
- service_plan['datastores'][type] ||= []
844
- selectable_datastores[type].reject { |ds| service_plan['datastores'][type].find {|it| it['id'] == ds['id']} }.each { |ds|
845
- service_plan['datastores'][type] << ds
846
- }
847
- end
848
- rescue => error
849
- Morpheus::Logging::DarkPrinter.puts "Unable to load available data-stores, using datastores option source instead." if Morpheus::Logging.debug?
850
- end
851
-
852
- if provision_type && provision_type['supportsAutoDatastore']
853
- service_plan['supportsAutoDatastore'] = true
854
- service_plan['autoOptions'] ||= []
855
- if service_plan['datastores'] && service_plan['datastores']['clusters']
856
- if service_plan['datastores']['clusters'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'autoCluster'}
857
- service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
858
- end
859
- else
860
- service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
861
- end
862
- if service_plan['datastores'] && service_plan['datastores']['datastores']
863
- if service_plan['datastores']['datastores'].count > 0 && !service_plan['autoOptions'].find {|it| it['id'] == 'auto'}
864
- service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
865
- end
866
- else
867
- service_plan['autoOptions'] << {'id' => 'auto', 'name' => 'Auto - Datastore'}
868
- 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)
869
845
  end
870
846
  end
871
847
  end
@@ -1090,7 +1066,7 @@ module Morpheus::Cli::ProvisioningHelper
1090
1066
  no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1091
1067
  volumes = []
1092
1068
  plan_size = nil
1093
- if plan_info['maxStorage']
1069
+ if plan_info['maxStorage'].to_i > 0
1094
1070
  plan_size = plan_info['maxStorage'].to_i / (1024 * 1024 * 1024)
1095
1071
  end
1096
1072
  root_storage_types = []
@@ -1163,7 +1139,7 @@ module Morpheus::Cli::ProvisioningHelper
1163
1139
  storage_type_id = nil
1164
1140
  storage_type = nil
1165
1141
  else
1166
- 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}
1167
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])
1168
1144
  storage_type_id = v_prompt[field_context]['storageType']
1169
1145
  storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
@@ -1199,6 +1175,11 @@ module Morpheus::Cli::ProvisioningHelper
1199
1175
  volume['size'] = nil #volume.delete('size')
1200
1176
  end
1201
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
1202
1183
  else
1203
1184
  # might need different logic here ? =o
1204
1185
  #volume['size'] = plan_size
@@ -1218,29 +1199,254 @@ module Morpheus::Cli::ProvisioningHelper
1218
1199
  has_another_volume = (options[:options] && options[:options]["dataVolume#{volume_index}"]) || (options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index])
1219
1200
  add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add data volume?", {:default => (options[:defaultAddFirstDataVolume] == true && volume_index == 1)}))
1220
1201
  while add_another_volume do
1221
- #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
1222
1348
 
1223
- field_context = "dataVolume#{volume_index}"
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
1224
1364
 
1225
- volume_label = (volume_index == 1 ? 'data' : "data #{volume_index}")
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
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'
1226
1432
  volume = {
1227
- #'id' => -1,
1433
+ 'id' => current_volume['id'].to_i,
1228
1434
  'rootVolume' => false,
1229
- 'name' => volume_label,
1230
- 'size' => plan_size,
1435
+ 'name' => current_volume['name'],
1436
+ 'size' => get_volume_size(current_volume) || plan_size,
1231
1437
  'sizeId' => nil,
1232
- 'storageType' => nil,
1233
- 'datastoreId' => nil
1438
+ 'storageType' => (current_volume['typeId'] || current_volume['storageType'] || (current_volume['type']['id'] rescue nil)),
1439
+ 'datastoreId' => current_volume['datastoreId']
1234
1440
  }
1235
- if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index]
1236
- volume = options[:options]['volumes'][volume_index]
1441
+ if !current_volume['controllerMountPoint'].to_s.empty?
1442
+ volume['controllerMountPoint'] = current_volume['controllerMountPoint']
1237
1443
  end
1238
-
1239
- 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])
1240
- storage_type_id = v_prompt[field_context]['storageType']
1241
- 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))
1242
1449
  storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1243
-
1244
1450
  # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1245
1451
  custom_size_options = []
1246
1452
  if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
@@ -1251,6 +1457,19 @@ module Morpheus::Cli::ProvisioningHelper
1251
1457
  end
1252
1458
  end
1253
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
+
1254
1473
  if plan_info['customizeVolume'] && storage_type['customLabel']
1255
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])
1256
1475
  volume['name'] = v_prompt[field_context]['name']
@@ -1261,331 +1480,323 @@ module Morpheus::Cli::ProvisioningHelper
1261
1480
  volume['size'] = v_prompt[field_context]['size']
1262
1481
  volume['sizeId'] = nil #volume.delete('sizeId')
1263
1482
  else
1264
- 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])
1265
1484
  volume['sizeId'] = v_prompt[field_context]['sizeId']
1266
1485
  volume['size'] = nil #volume.delete('size')
1267
1486
  end
1268
1487
  else
1269
1488
  # might need different logic here ? =o
1270
- volume['size'] = plan_size
1271
- volume['sizeId'] = nil #volume.delete('sizeId')
1272
- end
1273
- if !datastore_options.empty?
1274
- 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])
1275
- volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1489
+ # volume['size'] = plan_size
1490
+ # volume['sizeId'] = nil #volume.delete('sizeId')
1276
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
1277
1500
 
1278
1501
  volumes << volume
1279
1502
 
1280
- volume_index += 1
1281
- if options[:options] && options[:options]['volumes'] && options[:options]['volumes'][volume_index]
1282
- add_another_volume = true
1283
- elsif plan_info['maxDisk'] && volume_index >= plan_info['maxDisk']
1284
- # todo: should maxDisk check consider the root volume too?
1285
- add_another_volume = false
1286
- else
1287
- has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1288
- add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add another data volume?", {:default => false}))
1289
- end
1290
-
1291
1503
  end
1292
1504
 
1293
1505
  end
1294
-
1295
- return volumes
1296
1506
  end
1297
1507
 
1298
1508
 
1299
- # This recreates the behavior of multi_disk.js
1300
- # returns array of volumes based on service plan options (plan_info)
1301
- def prompt_resize_volumes(current_volumes, plan_info, provision_type, options={})
1302
- #puts "Configure Volumes:"
1303
- no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
1304
-
1305
- current_root_volume = current_volumes[0]
1306
-
1307
- volumes = []
1308
-
1309
- plan_size = nil
1310
- if plan_info['maxStorage']
1311
- plan_size = plan_info['maxStorage'].to_i / (1024 * 1024 * 1024)
1312
- end
1313
-
1314
- root_storage_types = []
1315
- if plan_info['rootStorageTypes']
1316
- plan_info['rootStorageTypes'].each do |opt|
1317
- if !opt.nil?
1318
- root_storage_types << {'name' => opt['name'], 'value' => opt['id']}
1319
- end
1320
- end
1321
- end
1322
-
1323
- storage_types = []
1324
- if plan_info['storageTypes']
1325
- plan_info['storageTypes'].each do |opt|
1326
- if !opt.nil?
1327
- storage_types << {'name' => opt['name'], 'value' => opt['id']}
1328
- end
1329
- end
1330
- 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 }
1331
1523
 
1332
- datastore_options = []
1333
- if plan_info['supportsAutoDatastore']
1334
- if plan_info['autoOptions']
1335
- 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|
1336
1528
  if !opt.nil?
1337
- datastore_options << {'name' => opt['name'], 'value' => opt['id']}
1529
+ custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1338
1530
  end
1339
1531
  end
1340
1532
  end
1341
- end
1342
- if plan_info['datastores']
1343
- plan_info['datastores'].each do |k, v|
1344
- v.each do |opt|
1345
- if !opt.nil?
1346
- datastore_options << {'name' => "#{k}: #{opt['name']}", 'value' => opt['id']}
1347
- end
1348
- end
1349
- end
1350
- end
1351
-
1352
- #puts "Configure Root Volume"
1353
1533
 
1354
- field_context = "rootVolume"
1355
-
1356
- if root_storage_types.empty?
1357
- # this means there's no configuration, just send a single root volume to the server
1358
- storage_type_id = nil
1359
- storage_type = nil
1360
- else
1361
- #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])
1362
- #storage_type_id = v_prompt[field_context]['storageType']
1363
- storage_type_id = current_root_volume['type'] || current_root_volume['storageType']
1364
- storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1365
- 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
+ }
1366
1545
 
1367
- # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1368
- root_custom_size_options = []
1369
- if plan_info['rootCustomSizeOptions'] && plan_info['rootCustomSizeOptions'][storage_type_id.to_s]
1370
- plan_info['rootCustomSizeOptions'][storage_type_id.to_s].each do |opt|
1371
- if !opt.nil?
1372
- root_custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1373
- 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']
1374
1549
  end
1375
- end
1376
-
1377
- volume = {
1378
- 'id' => current_root_volume['id'],
1379
- 'rootVolume' => true,
1380
- 'name' => current_root_volume['name'],
1381
- 'size' => current_root_volume['size'] > 0 ? current_root_volume['size'] : plan_size,
1382
- 'sizeId' => nil,
1383
- 'storageType' => storage_type_id,
1384
- 'datastoreId' => current_root_volume['datastoreId']
1385
- }
1386
-
1387
- if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customLabel']
1388
- 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])
1389
- volume['name'] = v_prompt[field_context]['name']
1390
- end
1391
- if plan_info['rootDiskCustomizable'] && storage_type && storage_type['customSize']
1392
- # provision_type['rootDiskSizeKnown'] == false means size cannot be changed
1393
- if provision_type['rootDiskSizeKnown'] == false
1394
- # volume['size'] = plan_size if plan_size.to_i != 0
1395
- else
1396
- if root_custom_size_options.empty?
1397
- 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])
1398
1553
  volume['size'] = v_prompt[field_context]['size']
1399
1554
  volume['sizeId'] = nil #volume.delete('sizeId')
1400
1555
  else
1401
- 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])
1402
1557
  volume['sizeId'] = v_prompt[field_context]['sizeId']
1403
1558
  volume['size'] = nil #volume.delete('size')
1404
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?
1405
1572
  end
1406
- else
1407
- # might need different logic here ? =o
1408
- # volume['size'] = plan_size
1409
- # volume['sizeId'] = nil #volume.delete('sizeId')
1410
- end
1411
- # if !datastore_options.empty?
1412
- # 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])
1413
- # volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1414
- # end
1415
1573
 
1416
- volumes << volume
1417
-
1418
- # modify or delete existing data volumes
1419
- (1..(current_volumes.size-1)).each do |volume_index|
1420
- current_volume = current_volumes[volume_index]
1421
- if current_volume
1422
-
1423
- field_context = "dataVolume#{volume_index}"
1424
-
1425
- action_options = [{'name' => 'Modify', 'value' => 'modify'}, {'name' => 'Keep', 'value' => 'keep'}, {'name' => 'Delete', 'value' => 'delete'}]
1426
- 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])
1427
- volume_action = v_prompt[field_context]['action']
1428
-
1429
- if volume_action == 'delete'
1430
- # deleted volume is just excluded from post params
1431
- next
1432
- elsif volume_action == 'keep'
1433
- volume = {
1434
- 'id' => current_volume['id'].to_i,
1435
- 'rootVolume' => false,
1436
- 'name' => current_volume['name'],
1437
- 'size' => current_volume['size'] > (plan_size || 0) ? current_volume['size'] : plan_size,
1438
- 'sizeId' => nil,
1439
- 'storageType' => (current_volume['type'] || current_volume['storageType']),
1440
- 'datastoreId' => current_volume['datastoreId']
1441
- }
1442
- volumes << volume
1443
- else
1444
- # 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])
1445
- # storage_type_id = v_prompt[field_context]['storageType']
1446
- storage_type_id = current_volume['type'] || current_volume['storageType']
1447
- storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1448
- # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1449
- custom_size_options = []
1450
- if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1451
- plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1452
- if !opt.nil?
1453
- custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1454
- end
1455
- end
1456
- end
1574
+ volumes << volume
1457
1575
 
1458
- volume = {
1459
- 'id' => current_volume['id'].to_i,
1460
- 'rootVolume' => false,
1461
- 'name' => current_volume['name'],
1462
- 'size' => current_volume['size'] ? current_volume['size'] : (plan_size || 0),
1463
- 'sizeId' => nil,
1464
- 'storageType' => (current_volume['type'] || current_volume['storageType']),
1465
- 'datastoreId' => current_volume['datastoreId']
1466
- }
1467
-
1468
- if plan_info['customizeVolume'] && storage_type['customLabel']
1469
- 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])
1470
- volume['name'] = v_prompt[field_context]['name']
1471
- end
1472
- if plan_info['customizeVolume'] && storage_type['customSize']
1473
- if custom_size_options.empty?
1474
- 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])
1475
- volume['size'] = v_prompt[field_context]['size']
1476
- volume['sizeId'] = nil #volume.delete('sizeId')
1477
- else
1478
- 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])
1479
- volume['sizeId'] = v_prompt[field_context]['sizeId']
1480
- volume['size'] = nil #volume.delete('size')
1481
- end
1482
- else
1483
- # might need different logic here ? =o
1484
- # volume['size'] = plan_size
1485
- # volume['sizeId'] = nil #volume.delete('sizeId')
1486
- end
1487
- # if !datastore_options.empty?
1488
- # 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])
1489
- # volume['datastoreId'] = v_prompt[field_context]['datastoreId']
1490
- # 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
1584
+
1585
+ end
1491
1586
 
1492
- volumes << volume
1587
+ end
1493
1588
 
1494
- end
1589
+ return volumes
1590
+ end
1495
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'}
1496
1614
  end
1615
+ else
1616
+ service_plan['autoOptions'] << {'id' => 'autoCluster', 'name' => 'Auto - Cluster'}
1497
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
1498
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
1499
1638
 
1500
- if plan_info['addVolumes']
1501
- volume_index = current_volumes.size
1502
- has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1503
- add_another_volume = has_another_volume || (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Add data volume?", {:default => false}))
1504
- while add_another_volume do
1505
- #puts "Configure Data #{volume_index} Volume"
1506
-
1507
- current_root_volume_type = current_root_volume['type']
1508
- storage_type_match = storage_types.find {|type| type['value'] == current_root_volume_type}
1509
- default_storage_type = storage_type_match ? current_root_volume_type : storage_types[0]['value']
1510
- field_context = "dataVolume#{volume_index}"
1511
- 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])
1512
- storage_type_id = v_prompt[field_context]['storageType']
1513
- storage_type = plan_info['storageTypes'].find {|i| i['id'] == storage_type_id.to_i }
1514
-
1515
- # sometimes the user chooses sizeId from a list of size options (AccountPrice) and other times it is free form
1516
- custom_size_options = []
1517
- if plan_info['customSizeOptions'] && plan_info['customSizeOptions'][storage_type_id.to_s]
1518
- plan_info['customSizeOptions'][storage_type_id.to_s].each do |opt|
1519
- if !opt.nil?
1520
- custom_size_options << {'name' => opt['value'], 'value' => opt['key']}
1521
- end
1522
- 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]
1523
1660
  end
1524
-
1525
- volume_label = (volume_index == 1 ? 'data' : "data #{volume_index}")
1526
- volume = {
1527
- 'id' => -1,
1528
- 'rootVolume' => false,
1529
- 'name' => volume_label,
1530
- 'size' => plan_size,
1531
- 'sizeId' => nil,
1532
- 'storageType' => storage_type_id,
1533
- 'datastoreId' => nil
1534
- }
1535
-
1536
- if plan_info['customizeVolume'] && storage_type['customLabel']
1537
- 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])
1538
- 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
1539
1710
  end
1540
- if plan_info['customizeVolume'] && storage_type['customSize']
1541
- if custom_size_options.empty?
1542
- 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])
1543
- volume['size'] = v_prompt[field_context]['size']
1544
- volume['sizeId'] = nil #volume.delete('sizeId')
1545
- else
1546
- 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])
1547
- volume['sizeId'] = v_prompt[field_context]['sizeId']
1548
- volume['size'] = nil #volume.delete('size')
1549
- end
1550
- else
1551
- # might need different logic here ? =o
1552
- # volume['size'] = plan_size
1553
- # 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}")
1554
1717
  end
1555
-
1556
- if datastore_options.empty? && storage_type['hasDatastore'] != false
1557
- begin
1558
- datastore_res = datastores_interface.list({'poolId' => current_root_volume['resourcePoolId'], 'resourcePoolId' => current_root_volume['resourcePoolId'], 'zoneId' => options['zoneId'], 'siteId' => options['siteId']})['datastores']
1559
- datastore_res.each do |opt|
1560
- 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
1561
1730
  end
1562
- rescue
1563
- datastore_options = []
1731
+ bus_number = next_bus_number
1564
1732
  end
1565
1733
  end
1566
- if !datastore_options.empty?
1567
- 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])
1568
- 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}")
1569
1746
  end
1570
-
1571
- volumes << volume
1572
-
1573
- # todo: should maxDisk check consider the root volume too?
1574
- if plan_info['maxDisk'] && volume_index >= plan_info['maxDisk']
1575
- add_another_volume = false
1576
- else
1577
- volume_index += 1
1578
- has_another_volume = options[:options] && options[:options]["dataVolume#{volume_index}"]
1579
- 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
1580
1782
  end
1581
-
1582
1783
  end
1583
-
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
1584
1790
  end
1585
-
1586
- return volumes
1791
+ else
1792
+ # storage controllers not supported, carry on then
1587
1793
  end
1588
-
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
1589
1800
 
1590
1801
  # This recreates the behavior of multi_networks.js
1591
1802
  # This is used by both `instances add` and `hosts add`