morpheus-cli 8.0.1 → 8.0.3

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