morpheus-cli 4.2.6 → 4.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/lib/morpheus/api/api_client.rb +4 -0
  4. data/lib/morpheus/api/clouds_interface.rb +14 -0
  5. data/lib/morpheus/api/guidance_interface.rb +47 -0
  6. data/lib/morpheus/api/users_interface.rb +7 -0
  7. data/lib/morpheus/cli.rb +1 -0
  8. data/lib/morpheus/cli/account_groups_command.rb +1 -1
  9. data/lib/morpheus/cli/approvals_command.rb +2 -2
  10. data/lib/morpheus/cli/apps.rb +26 -30
  11. data/lib/morpheus/cli/blueprints_command.rb +1 -1
  12. data/lib/morpheus/cli/budgets_command.rb +2 -2
  13. data/lib/morpheus/cli/change_password_command.rb +0 -1
  14. data/lib/morpheus/cli/cli_command.rb +19 -9
  15. data/lib/morpheus/cli/clouds.rb +107 -10
  16. data/lib/morpheus/cli/clusters.rb +12 -12
  17. data/lib/morpheus/cli/commands/standard/curl_command.rb +7 -0
  18. data/lib/morpheus/cli/deployments.rb +2 -2
  19. data/lib/morpheus/cli/environments_command.rb +1 -1
  20. data/lib/morpheus/cli/execution_request_command.rb +1 -1
  21. data/lib/morpheus/cli/groups.rb +1 -1
  22. data/lib/morpheus/cli/guidance_command.rb +529 -0
  23. data/lib/morpheus/cli/hosts.rb +2 -10
  24. data/lib/morpheus/cli/instances.rb +31 -13
  25. data/lib/morpheus/cli/integrations_command.rb +1 -1
  26. data/lib/morpheus/cli/jobs_command.rb +2 -2
  27. data/lib/morpheus/cli/library_container_types_command.rb +4 -4
  28. data/lib/morpheus/cli/library_instance_types_command.rb +3 -3
  29. data/lib/morpheus/cli/library_spec_templates_command.rb +1 -1
  30. data/lib/morpheus/cli/load_balancers.rb +2 -2
  31. data/lib/morpheus/cli/mixins/print_helper.rb +43 -3
  32. data/lib/morpheus/cli/mixins/provisioning_helper.rb +251 -165
  33. data/lib/morpheus/cli/network_routers_command.rb +1 -1
  34. data/lib/morpheus/cli/price_sets_command.rb +2 -2
  35. data/lib/morpheus/cli/provisioning_licenses_command.rb +1 -1
  36. data/lib/morpheus/cli/remote.rb +6 -1
  37. data/lib/morpheus/cli/reports_command.rb +1 -1
  38. data/lib/morpheus/cli/security_group_rules.rb +1 -1
  39. data/lib/morpheus/cli/security_groups.rb +13 -5
  40. data/lib/morpheus/cli/service_plans_command.rb +2 -2
  41. data/lib/morpheus/cli/user_groups_command.rb +2 -6
  42. data/lib/morpheus/cli/user_settings_command.rb +31 -5
  43. data/lib/morpheus/cli/user_sources_command.rb +3 -3
  44. data/lib/morpheus/cli/users.rb +117 -90
  45. data/lib/morpheus/cli/version.rb +1 -1
  46. data/lib/morpheus/cli/virtual_images.rb +2 -2
  47. data/lib/morpheus/cli/whitelabel_settings_command.rb +95 -15
  48. data/lib/morpheus/cli/wiki_command.rb +2 -2
  49. data/lib/morpheus/cli/workflows.rb +2 -3
  50. data/lib/morpheus/formatters.rb +14 -5
  51. metadata +4 -2
@@ -203,7 +203,7 @@ class Morpheus::Cli::Hosts
203
203
  subtitles += parse_list_subtitles(options)
204
204
  print_h1 title, subtitles, options
205
205
  if servers.empty?
206
- print yellow,"No hosts found.",reset,"\n"
206
+ print cyan,"No hosts found.",reset,"\n"
207
207
  else
208
208
  # print_servers_table(servers)
209
209
  # server returns stats in a separate key stats => {"id" => {} }
@@ -762,14 +762,6 @@ class Morpheus::Cli::Hosts
762
762
  })
763
763
 
764
764
  option_type_list = server_type['optionTypes']
765
- # remove volume options if volumes were configured
766
- if !payload['volumes'].empty?
767
- option_type_list = reject_volume_option_types(option_type_list)
768
- end
769
- # remove networkId option if networks were configured above
770
- if !payload['networkInterfaces'].empty?
771
- option_type_list = reject_networking_option_types(option_type_list)
772
- end
773
765
 
774
766
  # remove cpu and memory option types, which now come from the plan
775
767
  option_type_list = reject_service_plan_option_types(option_type_list)
@@ -1466,7 +1458,7 @@ class Morpheus::Cli::Hosts
1466
1458
  else
1467
1459
  print_h1 "Morpheus Server Types - Cloud: #{zone['name']}", [], options
1468
1460
  if cloud_server_types.nil? || cloud_server_types.empty?
1469
- print yellow,"No server types found for the selected cloud",reset,"\n"
1461
+ print cyan,"No server types found for the selected cloud",reset,"\n"
1470
1462
  else
1471
1463
  cloud_server_types.each do |server_type|
1472
1464
  print cyan, "[#{server_type['code']}]".ljust(20), " - ", "#{server_type['name']}", "\n"
@@ -158,7 +158,7 @@ class Morpheus::Cli::Instances
158
158
  subtitles += parse_list_subtitles(options)
159
159
  print_h1 title, subtitles, options
160
160
  if instances.empty?
161
- print yellow,"No instances found.",reset,"\n"
161
+ print cyan,"No instances found.",reset,"\n"
162
162
  else
163
163
  # print_instances_table(instances)
164
164
  # server returns stats in a separate key stats => {"id" => {} }
@@ -208,10 +208,11 @@ class Morpheus::Cli::Instances
208
208
  :type, :version, :environment,
209
209
  {:user => {:display_name => "CREATED BY", :max_width => 20}},
210
210
  :nodes, {:connection => {:max_width => 30}}, :status, :cpu, :memory, :storage]
211
- # custom pretty table columns ...
212
- if options[:include_fields]
213
- columns = options[:include_fields]
214
- end
211
+ # custom pretty table columns ... this is handled in as_pretty_table now(),
212
+ # todo: remove all these.. and try to always pass rows as the json data itself..
213
+ # if options[:include_fields]
214
+ # columns = options[:include_fields]
215
+ # end
215
216
  print cyan
216
217
  print as_pretty_table(rows, columns, options)
217
218
  print reset
@@ -319,6 +320,18 @@ class Morpheus::Cli::Instances
319
320
  opts.on("--environment ENV", String, "Environment code") do |val|
320
321
  options[:environment] = val.to_s
321
322
  end
323
+ opts.on('--metadata LIST', String, "Metadata tags in the format 'name:value, name:value'") do |val|
324
+ options[:metadata] = val
325
+ end
326
+ opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
327
+ #options[:tags] = val
328
+ options[:tags] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
329
+ end
330
+ opts.on('--tags LIST', String, "Tags") do |val|
331
+ #options[:tags] = val
332
+ options[:tags] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
333
+ end
334
+ opts.add_hidden_option('--tags')
322
335
  opts.on("--copies NUMBER", Integer, "Number of copies to provision") do |val|
323
336
  options[:copies] = val.to_i
324
337
  end
@@ -361,7 +374,7 @@ class Morpheus::Cli::Instances
361
374
  opts.on("--create-backup [on|off]", String, "Automation: Create Backups.") do |val|
362
375
  options[:create_backup] = ['on','true','1',''].include?(val.to_s.downcase) ? 'on' : 'off'
363
376
  end
364
- opts.on("--security-groups LIST", Integer, "Security Groups, comma sepearated list of security group IDs") do |val|
377
+ opts.on("--security-groups LIST", String, "Security Groups, comma sepearated list of security group IDs") do |val|
365
378
  options[:security_groups] = val.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
366
379
  end
367
380
  opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
@@ -490,13 +503,18 @@ class Morpheus::Cli::Instances
490
503
  opts.on('--group GROUP', String, "Group Name or ID") do |val|
491
504
  options[:group] = val
492
505
  end
493
- opts.on('--metadata LIST', String, "Metadata in the format 'name:value, name:value'") do |val|
506
+ opts.on('--metadata LIST', String, "Metadata tags in the format 'name:value, name:value'") do |val|
494
507
  options[:metadata] = val
495
508
  end
509
+ opts.on('--labels LIST', String, "Labels (keywords) in the format 'foo, bar'") do |val|
510
+ #params['tags'] = val
511
+ params['tags'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
512
+ end
496
513
  opts.on('--tags LIST', String, "Tags") do |val|
497
- params['tags'] = val
498
- # params['tags'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq
514
+ #params['tags'] = val
515
+ params['tags'] = val.split(',').collect {|it| it.to_s.strip }.compact.uniq.join(',')
499
516
  end
517
+ opts.add_hidden_option('--tags')
500
518
  opts.on('--power-schedule-type ID', String, "Power Schedule Type ID") do |val|
501
519
  params['powerScheduleType'] = val == "null" ? nil : val
502
520
  end
@@ -1286,7 +1304,7 @@ class Morpheus::Cli::Instances
1286
1304
  "Version" => lambda {|it| it['instanceVersion'] },
1287
1305
  "Plan" => lambda {|it| it['plan'] ? it['plan']['name'] : '' },
1288
1306
  "Environment" => 'instanceContext',
1289
- "Tags" => lambda {|it| it['tags'] ? it['tags'].join(',') : '' },
1307
+ "Labels" => lambda {|it| it['tags'] ? it['tags'].join(',') : '' },
1290
1308
  "Metadata" => lambda {|it| it['metadata'] ? it['metadata'].collect {|m| "#{m['name']}: #{m['value']}" }.join(', ') : '' },
1291
1309
  "Power Schedule" => lambda {|it| (it['powerSchedule'] && it['powerSchedule']['type']) ? it['powerSchedule']['type']['name'] : '' },
1292
1310
  "Created By" => lambda {|it| it['createdBy'] ? (it['createdBy']['username'] || it['createdBy']['id']) : '' },
@@ -1399,7 +1417,7 @@ class Morpheus::Cli::Instances
1399
1417
  if options[:include_scaling]
1400
1418
  print_h2 "Instance Scaling", options
1401
1419
  if instance_threshold.nil? || instance_threshold.empty?
1402
- print yellow,"No scaling settings applied to this instance.",reset,"\n"
1420
+ print cyan,"No scaling settings applied to this instance.",reset,"\n"
1403
1421
  else
1404
1422
  print cyan
1405
1423
  print_instance_threshold_description_list(instance_threshold)
@@ -1476,7 +1494,7 @@ class Morpheus::Cli::Instances
1476
1494
  title = "Instance Containers: #{instance['name']} (#{instance['instanceType']['name']})"
1477
1495
  print_h1 title, [], options
1478
1496
  if containers.empty?
1479
- print yellow,"No containers found for instance.",reset,"\n"
1497
+ print cyan,"No containers found for instance.",reset,"\n"
1480
1498
  else
1481
1499
 
1482
1500
  rows = containers.collect {|container|
@@ -3008,7 +3026,7 @@ class Morpheus::Cli::Instances
3008
3026
  title = "Instance Scaling: [#{instance['id']}] #{instance['name']} (#{instance['instanceType']['name']})"
3009
3027
  print_h1 title, [], options
3010
3028
  if instance_threshold.empty?
3011
- print yellow,"No scaling settings applied to this instance.",reset,"\n"
3029
+ print cyan,"No scaling settings applied to this instance.",reset,"\n"
3012
3030
  else
3013
3031
  # print_h1 "Threshold Settings", [], options
3014
3032
  print cyan
@@ -53,7 +53,7 @@ class Morpheus::Cli::IntegrationsCommand
53
53
  integrations = json_response['integrations']
54
54
 
55
55
  if integrations.empty?
56
- print yellow,"No integrations found.",reset,"\n"
56
+ print cyan,"No integrations found.",reset,"\n"
57
57
  else
58
58
  rows = integrations.collect do |it|
59
59
  {
@@ -79,7 +79,7 @@ class Morpheus::Cli::JobsCommand
79
79
  jobs = json_response['jobs']
80
80
 
81
81
  if jobs.empty?
82
- print yellow,"No jobs found.",reset,"\n"
82
+ print cyan,"No jobs found.",reset,"\n"
83
83
  else
84
84
  rows = jobs.collect do |job|
85
85
  {
@@ -944,7 +944,7 @@ class Morpheus::Cli::JobsCommand
944
944
 
945
945
  def print_job_executions(execs, options={})
946
946
  if execs.empty?
947
- print yellow,"No job executions found.",reset,"\n"
947
+ print cyan,"No job executions found.",reset,"\n"
948
948
  else
949
949
  rows = execs.collect do |ex|
950
950
  {
@@ -189,7 +189,7 @@ class Morpheus::Cli::LibraryContainerTypesCommand
189
189
  ]
190
190
  print as_pretty_table(evars, evar_columns)
191
191
  else
192
- # print yellow,"No environment variables found for this node type.","\n",reset
192
+ # print cyan,"No environment variables found for this node type.","\n",reset
193
193
  end
194
194
 
195
195
  exposed_ports = container_type['containerPorts']
@@ -203,7 +203,7 @@ class Morpheus::Cli::LibraryContainerTypesCommand
203
203
  ]
204
204
  print as_pretty_table(exposed_ports, columns)
205
205
  else
206
- # print yellow,"No exposed ports found for this node type.","\n",reset
206
+ # print cyan,"No exposed ports found for this node type.","\n",reset
207
207
  end
208
208
 
209
209
  container_scripts = container_type['containerScripts'] || container_type['scripts']
@@ -215,7 +215,7 @@ class Morpheus::Cli::LibraryContainerTypesCommand
215
215
  ]
216
216
  print as_pretty_table(container_scripts, columns)
217
217
  else
218
- # print yellow,"No scripts found for this node type.","\n",reset
218
+ # print cyan,"No scripts found for this node type.","\n",reset
219
219
  end
220
220
 
221
221
  container_file_templates = container_type['containerTemplates'] || container_type['templates']
@@ -227,7 +227,7 @@ class Morpheus::Cli::LibraryContainerTypesCommand
227
227
  ]
228
228
  print as_pretty_table(container_file_templates, columns)
229
229
  else
230
- # print yellow,"No scripts found for this node type.","\n",reset
230
+ # print cyan,"No scripts found for this node type.","\n",reset
231
231
  end
232
232
 
233
233
  print reset,"\n"
@@ -178,7 +178,7 @@ class Morpheus::Cli::LibraryInstanceTypesCommand
178
178
  ]
179
179
  print as_pretty_table(instance_type_option_types, columns)
180
180
  else
181
- # print yellow,"No option types found for this layout.","\n",reset
181
+ # print cyan,"No option types found for this layout.","\n",reset
182
182
  end
183
183
 
184
184
  instance_type_evars = instance_type['environmentVariables']
@@ -193,7 +193,7 @@ class Morpheus::Cli::LibraryInstanceTypesCommand
193
193
  ]
194
194
  print as_pretty_table(instance_type_evars, evar_columns)
195
195
  else
196
- # print yellow,"No environment variables found for this instance type.","\n",reset
196
+ # print cyan,"No environment variables found for this instance type.","\n",reset
197
197
  end
198
198
 
199
199
  print_h2 "Layouts"
@@ -229,7 +229,7 @@ class Morpheus::Cli::LibraryInstanceTypesCommand
229
229
  ]
230
230
  print as_pretty_table(instance_type_layouts, layout_columns)
231
231
  else
232
- print yellow,"No layouts found for this instance type.","\n",reset
232
+ print cyan,"No layouts found for this instance type.","\n",reset
233
233
  end
234
234
 
235
235
  print reset,"\n"
@@ -163,7 +163,7 @@ class Morpheus::Cli::LibrarySpecTemplatesCommand
163
163
  puts "Path: #{file_content['contentPath']}"
164
164
  end
165
165
  else
166
- print yellow,"No file content.",reset,"\n"
166
+ print cyan,"No file content.",reset,"\n"
167
167
  end
168
168
  end
169
169
 
@@ -56,7 +56,7 @@ class Morpheus::Cli::LoadBalancers
56
56
  lbs = json_response['loadBalancers']
57
57
  print_h1 "Morpheus Load Balancers"
58
58
  if lbs.empty?
59
- print yellow,"No load balancers found.",reset,"\n"
59
+ print cyan,"No load balancers found.",reset,"\n"
60
60
  else
61
61
  columns = [
62
62
  {"ID" => 'id'},
@@ -243,7 +243,7 @@ class Morpheus::Cli::LoadBalancers
243
243
  lb_types = json_response['loadBalancerTypes']
244
244
  print_h1 "Morpheus Load Balancer Types"
245
245
  if lb_types.nil? || lb_types.empty?
246
- print yellow,"No lb types currently exist on this appliance. This could be a seed issue.",reset,"\n"
246
+ print cyan,"No load balancer types found.",reset,"\n"
247
247
  else
248
248
  print cyan
249
249
  lb_table_data = lb_types.collect do |lb_type|
@@ -538,7 +538,7 @@ module Morpheus::Cli::PrintHelper
538
538
  value = value.to_s
539
539
  if do_wrap && value && Morpheus::Cli::PrintHelper.terminal_width
540
540
  value_width = Morpheus::Cli::PrintHelper.terminal_width - label_width
541
- if value_width > 0 && value.to_s.size > value_width
541
+ if value_width > 0 && value.gsub(/\e\[(\d+)m/, '').to_s.size > value_width
542
542
  wrap_indent = label_width + 1 # plus 1 needs to go away
543
543
  value = wrap(value, value_width, wrap_indent)
544
544
  end
@@ -628,6 +628,44 @@ module Morpheus::Cli::PrintHelper
628
628
  #
629
629
  def as_pretty_table(data, columns, options={})
630
630
  data = [data].flatten
631
+
632
+ # support --fields x,y,z and --all-fields or --fields all
633
+ all_fields = data.first ? data.first.keys : []
634
+
635
+ if options[:include_fields]
636
+ if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
637
+ columns = all_fields
638
+ else
639
+ # so let's use the passed in column definitions instead of the raw data properties
640
+ # columns = options[:include_fields]
641
+ new_columns = []
642
+ options[:include_fields].each do |f|
643
+ matching_column = nil
644
+ # column definitions vary right now, array of symbols/strings/hashes or perhaps a single hash
645
+ if columns.is_a?(Array) && columns[0] && columns[0].is_a?(Hash)
646
+ matching_column = columns.find {|c|
647
+ if c.is_a?(Hash)
648
+ c.keys[0].to_s.downcase == f.to_s.downcase
649
+ else
650
+ c && c.to_s.downcase == f.to_s.downcase
651
+ end
652
+ }
653
+ elsif columns.is_a?(Hash)
654
+ matching_key = columns.keys.find {|k| k.to_s.downcase == f.to_s.downcase }
655
+ if matching_key
656
+ matching_column = columns[matching_key]
657
+ end
658
+ end
659
+ new_columns << (matching_column ? matching_column : f)
660
+ end
661
+ columns = new_columns
662
+ end
663
+ elsif options[:all_fields]
664
+ columns = all_fields
665
+ else
666
+ columns = columns
667
+ end
668
+
631
669
  columns = build_column_definitions(columns)
632
670
 
633
671
  table_color = options[:color] || cyan
@@ -690,7 +728,7 @@ module Morpheus::Cli::PrintHelper
690
728
  # could use some options[:preferred_columns] logic here to throw away in some specified order
691
729
  # --all fields disables this
692
730
  trimmed_columns = []
693
- if options[:responsive_table] != false && options[:include_fields].nil? && options[:all_fields] != true
731
+ if options[:responsive_table] != false # && options[:include_fields].nil? && options[:all_fields] != true
694
732
 
695
733
  begin
696
734
  term_width = current_terminal_width()
@@ -1027,11 +1065,13 @@ module Morpheus::Cli::PrintHelper
1027
1065
  cols = []
1028
1066
  all_fields = records.first ? records.first.keys : []
1029
1067
  if options[:include_fields]
1030
- if options[:include_fields] == 'all' || options[:include_fields].include?('all')
1068
+ if (options[:include_fields].is_a?(Array) && options[:include_fields].size == 1 && options[:include_fields][0] == 'all') || options[:include_fields] == 'all'
1031
1069
  cols = all_fields
1032
1070
  else
1033
1071
  cols = options[:include_fields]
1034
1072
  end
1073
+ elsif options[:all_fields]
1074
+ cols = all_fields
1035
1075
  elsif default_columns
1036
1076
  cols = default_columns
1037
1077
  else
@@ -298,10 +298,12 @@ module Morpheus::Cli::ProvisioningHelper
298
298
  # prompts user for all the configuartion options for a particular instance
299
299
  # returns payload of data for a new instance
300
300
  def prompt_new_instance(options={})
301
+ no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
301
302
  #puts "prompt_new_instance() #{options}"
302
303
  print reset # clear colors
303
304
  options[:options] ||= {}
304
-
305
+ # provisioning with blueprint can lock fields
306
+ locked_fields = options[:locked_fields] || []
305
307
  # Group
306
308
  default_group = find_group_by_name_or_id_for_provisioning(options[:default_group] || @active_group_id) if options[:default_group] || @active_group_id
307
309
 
@@ -365,7 +367,7 @@ module Morpheus::Cli::ProvisioningHelper
365
367
  instance_name = name_prompt['name']
366
368
  else
367
369
  print_red_alert "Name must be unique"
368
-
370
+ exit 1 if no_prompt
369
371
  if options[:default_name] == name_prompt['name']
370
372
  options[:default_name] += '-2'
371
373
  end
@@ -421,6 +423,7 @@ module Morpheus::Cli::ProvisioningHelper
421
423
  arbitrary_options.delete('environment')
422
424
  arbitrary_options.delete('instanceContext')
423
425
  arbitrary_options.delete('tags')
426
+ arbitrary_options.delete('lockedFields')
424
427
  # arbitrary_options.delete('ports')
425
428
  payload.deep_merge!(arbitrary_options)
426
429
  end
@@ -441,68 +444,85 @@ module Morpheus::Cli::ProvisioningHelper
441
444
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'select', 'required' => false, 'selectOptions' => get_available_environments()}], options[:options])
442
445
  payload['instance']['instanceContext'] = v_prompt['environment'] if !v_prompt['environment'].empty?
443
446
 
444
- # Tags
445
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tags', 'fieldLabel' => 'Tags', 'type' => 'text', 'required' => false}], options[:options])
446
- payload['instance']['tags'] = v_prompt['tags'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['tags'].empty?
447
+ # Labels (tags)
448
+ if options[:tags]
449
+ payload['instance']['tags'] = options[:tags].is_a?(Array) ? options[:tags] : options[:tags].to_s.split(',').collect {|it| it.to_s.strip }.compact.uniq
450
+ else
451
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tags', 'fieldLabel' => 'Labels', 'type' => 'text', 'required' => false}], options[:options])
452
+ payload['instance']['tags'] = v_prompt['tags'].split(',').collect {|it| it.to_s.strip }.compact.uniq if !v_prompt['tags'].empty?
453
+ end
447
454
 
448
455
  # Version and Layout
449
- if options[:version]
450
- version_value = options[:version]
456
+ layout_id = nil
457
+ if locked_fields.include?('instance.layout.id')
458
+ layout_id = options[:options]['instance']['layout'] rescue options[:options]['layout']
459
+ if layout_id.is_a?(Hash)
460
+ layout_id = layout_id['id'] || layout_id['code'] || layout_id['name']
461
+ end
451
462
  else
452
- available_versions = options_interface.options_for_source('instanceVersions',{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})['data']
453
- default_version_value = payload['instance']['version'] ? payload['instance']['version'] : payload['version']
454
- default_layout_value = payload['instance']['layout'] ? payload['instance']['layout'] : payload['layout']
455
- if default_layout_value && default_layout_value.is_a?(Hash)
456
- default_layout_value = default_layout_value['id']
457
- end
458
- # JD: version is always nil because it is not stored in the blueprint or config !!
459
- # so for now, infer the version from the layout
460
- # requires api 3.6.2 to get "layouts" from /options/versions
461
- if default_layout_value && default_version_value.to_s.empty?
462
- available_versions.each do |available_version|
463
- if available_version["layouts"]
464
- selected_layout = available_version["layouts"].find {|it| it["value"].to_s == default_layout_value.to_s || it["id"].to_s == default_layout_value.to_s || it["code"].to_s == default_layout_value.to_s }
465
- if selected_layout
466
- default_version_value = available_version["value"]
467
- break
463
+ layout_id = nil
464
+ if options[:layout]
465
+ layout_id = options[:layout]
466
+ end
467
+ if layout_id.is_a?(Hash)
468
+ layout_id = layout_id['id'] || layout_id['code'] || layout_id['name']
469
+ end
470
+ if layout_id.nil?
471
+ version_value = nil
472
+ default_layout_value = nil
473
+ if options[:version]
474
+ version_value = options[:version]
475
+ else
476
+ available_versions = options_interface.options_for_source('instanceVersions',{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})['data']
477
+ default_version_value = payload['instance']['version'] ? payload['instance']['version'] : payload['version']
478
+ #default_layout_value = options[:layout]
479
+ if default_layout_value.nil?
480
+ default_layout_value = payload['instance']['layout'] ? payload['instance']['layout'] : payload['layout']
481
+ end
482
+ if default_layout_value && default_layout_value.is_a?(Hash)
483
+ default_layout_value = default_layout_value['id']
484
+ end
485
+ # JD: version is always nil because it is not stored in the blueprint or config !!
486
+ # so for now, infer the version from the layout
487
+ # requires api 3.6.2 to get "layouts" from /options/versions
488
+ if default_layout_value && default_version_value.to_s.empty?
489
+ available_versions.each do |available_version|
490
+ if available_version["layouts"]
491
+ selected_layout = available_version["layouts"].find {|it| it["value"].to_s == default_layout_value.to_s || it["id"].to_s == default_layout_value.to_s || it["code"].to_s == default_layout_value.to_s }
492
+ if selected_layout
493
+ default_version_value = available_version["value"]
494
+ break
495
+ end
496
+ end
468
497
  end
469
498
  end
499
+
500
+ # do not require version if a layout is passed
501
+ version_value = default_version_value
502
+ version_is_required = default_layout_value.nil?
503
+ if default_layout_value.nil? && options[:options]["layout"].nil? && options[:always_prompt] != true
504
+ #version_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'version', 'type' => 'select', 'fieldLabel' => 'Version', 'optionSource' => 'instanceVersions', 'required' => true, 'skipSingleOption' => true, 'autoPickOption' => true, 'description' => 'Select which version of the instance type to be provisioned.', 'defaultValue' => default_version_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})
505
+ version_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'version', 'type' => 'select', 'fieldLabel' => 'Version', 'selectOptions' => available_versions, 'required' => version_is_required, 'skipSingleOption' => true, 'autoPickOption' => true, 'description' => 'Select which version of the instance type to be provisioned.', 'defaultValue' => default_version_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})
506
+ version_value = version_prompt['version']
507
+ end
470
508
  end
471
- end
472
509
 
473
- # do not require version if a layout is passed
474
- version_value = default_version_value
475
- version_is_required = default_layout_value.nil?
476
- if default_layout_value.nil? && options[:options]["layout"].nil? && options[:always_prompt] != true
477
- #version_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'version', 'type' => 'select', 'fieldLabel' => 'Version', 'optionSource' => 'instanceVersions', 'required' => true, 'skipSingleOption' => true, 'autoPickOption' => true, 'description' => 'Select which version of the instance type to be provisioned.', 'defaultValue' => default_version_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})
478
- version_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'version', 'type' => 'select', 'fieldLabel' => 'Version', 'selectOptions' => available_versions, 'required' => version_is_required, 'skipSingleOption' => true, 'autoPickOption' => true, 'description' => 'Select which version of the instance type to be provisioned.', 'defaultValue' => default_version_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})
479
- version_value = version_prompt['version']
510
+ layout_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'layout', 'type' => 'select', 'fieldLabel' => 'Layout', 'optionSource' => 'layoutsForCloud', 'required' => true, 'description' => 'Select which configuration of the instance type to be provisioned.', 'defaultValue' => default_layout_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], version: version_value, creatable: true})['layout']
480
511
  end
481
512
  end
482
513
 
483
- # JD: there is a bug here, the version needs to be passed perhaps? or the optionSource methods need updating...
484
- # could just allow for now ...
485
- # if options[:options]["layout"]
486
- # layout_id = options[:options]["layout"]
487
- # ...
488
- # end
489
- layout_id = options[:layout].to_i if options[:layout]
490
-
491
514
  # determine layout and provision_type
492
- # provision_type = (layout && provision_type ? provision_type : nil) || get_provision_type_for_zone_type(cloud['zoneType']['id'])
493
- # need to GET layout and provision type by ID in order to get optionTypes, and other settings...
494
-
495
- if !layout_id
496
- layout_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'layout', 'type' => 'select', 'fieldLabel' => 'Layout', 'optionSource' => 'layoutsForCloud', 'required' => true, 'description' => 'Select which configuration of the instance type to be provisioned.', 'defaultValue' => default_layout_value}],options[:options],api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], version: version_value, creatable: true})['layout']
497
- end
498
515
 
499
516
  # layout = find_instance_type_layout_by_id(instance_type['id'], layout_id.to_i)
500
- layout = (instance_type['instanceTypeLayouts'] || []).find {|it| it['id'] == layout_id.to_i }
517
+ layout = (instance_type['instanceTypeLayouts'] || []).find {|it|
518
+ it['id'].to_s == layout_id.to_s || it['code'].to_s == layout_id.to_s || it['name'].to_s == layout_id.to_s
519
+ }
501
520
  if !layout
502
521
  print_red_alert "Layout not found by id #{layout_id}"
503
522
  exit 1
504
523
  end
505
- payload['instance']['layout'] = {'id' => layout['id']}
524
+ layout_id = layout['id']
525
+ payload['instance']['layout'] = {'id' => layout['id'], 'code' => layout['code']}
506
526
 
507
527
  # need to GET provision type for optionTypes, and other settings...
508
528
  provision_type_code = layout['provisionTypeCode'] || layout['provisionType']['code']
@@ -518,37 +538,47 @@ module Morpheus::Cli::ProvisioningHelper
518
538
  end
519
539
 
520
540
  # prompt for service plan
541
+ plan_id = nil
542
+ service_plan = nil
521
543
  service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, layoutId: layout['id'], siteId: group_id})
522
544
  service_plans = service_plans_json["plans"]
523
- service_plan = service_plans.find {|sp| sp['id'] == options[:service_plan].to_i} if options[:service_plan]
524
-
525
- if !service_plan
526
- service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"], 'code' => sp['code']} } # already sorted
527
- default_plan = nil
528
- if payload['plan']
529
- default_plan = payload['plan']
530
- elsif payload['instance'] && payload['instance']['plan']
531
- default_plan = payload['instance']['plan']
545
+ if locked_fields.include?('plan.id')
546
+ plan_id = options[:options]['plan']['id'] rescue nil
547
+ if plan_id.nil?
548
+ plan_id = options[:options]['instance']['plan']['id'] rescue nil
532
549
  end
550
+ service_plan = service_plans.find {|sp| sp['id'] == plan_id }
551
+ else
552
+ service_plan = service_plans.find {|sp| sp['id'] == options[:service_plan].to_i} if options[:service_plan]
533
553
 
534
- if options[:default_plan] && service_plans_dropdown.find {|sp| [sp["name"], sp["value"].to_s, sp["code"]].include?(options[:default_plan].to_s)}
535
- default_plan_value = options[:default_plan]
536
- else
537
- default_plan_value = options[:default_plan] || (default_plan.is_a?(Hash) ? default_plan['id'] : default_plan)
538
- end
539
- plan_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'servicePlan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => service_plans_dropdown, 'required' => true, 'description' => 'Choose the appropriately sized plan for this instance', 'defaultValue' => default_plan_value}],options[:options])
540
- plan_id = plan_prompt['servicePlan']
541
- service_plan = service_plans.find {|sp| sp["id"] == plan_id.to_i }
542
554
  if !service_plan
543
- print_red_alert "Plan not found by id #{plan_id}"
544
- exit 1
555
+ service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"], 'code' => sp['code']} } # already sorted
556
+ default_plan = nil
557
+ if payload['plan']
558
+ default_plan = payload['plan']
559
+ elsif payload['instance'] && payload['instance']['plan']
560
+ default_plan = payload['instance']['plan']
561
+ end
562
+
563
+ if options[:default_plan] && service_plans_dropdown.find {|sp| [sp["name"], sp["value"].to_s, sp["code"]].include?(options[:default_plan].to_s)}
564
+ default_plan_value = options[:default_plan]
565
+ else
566
+ default_plan_value = options[:default_plan] || (default_plan.is_a?(Hash) ? default_plan['id'] : default_plan)
567
+ end
568
+ plan_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'servicePlan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => service_plans_dropdown, 'required' => true, 'description' => 'Choose the appropriately sized plan for this instance', 'defaultValue' => default_plan_value}],options[:options])
569
+ plan_id = plan_prompt['servicePlan']
570
+ service_plan = service_plans.find {|sp| sp["id"] == plan_id.to_i }
571
+ if !service_plan
572
+ print_red_alert "Plan not found by id #{plan_id}"
573
+ exit 1
574
+ end
545
575
  end
576
+ #todo: consolidate these, instances api looks for instance.plan.id and apps looks for plan.id
577
+ payload['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
578
+ payload['instance']['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
546
579
  end
547
- #todo: consolidate these, instances api looks for instance.plan.id and apps looks for plan.id
548
- payload['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
549
- payload['instance']['plan'] = {'id' => service_plan["id"], 'code' => service_plan["code"], 'name' => service_plan["name"]}
550
580
 
551
- # build option types
581
+ # build config option types
552
582
  option_type_list = []
553
583
  if !layout['optionTypes'].nil? && !layout['optionTypes'].empty?
554
584
  option_type_list += layout['optionTypes']
@@ -559,42 +589,43 @@ module Morpheus::Cli::ProvisioningHelper
559
589
  if !provision_type.nil? && !provision_type['optionTypes'].nil? && !provision_type['optionTypes'].empty?
560
590
  option_type_list += provision_type['optionTypes']
561
591
  end
562
- if !payload['volumes'].empty?
563
- option_type_list = reject_volume_option_types(option_type_list)
564
- end
565
- # remove networkId option if networks were configured above
566
- if !payload['networkInterfaces'].empty?
567
- option_type_list = reject_networking_option_types(option_type_list)
568
- end
569
592
 
570
593
  # prompt for resource pool
571
594
  pool_id = nil
572
595
  resource_pool = nil
573
- has_zone_pools = provision_type && provision_type["id"] && provision_type["hasZonePools"]
574
- if has_zone_pools
575
- # pluck out the resourcePoolId option type to prompt for
576
- resource_pool_option_type = option_type_list.find {|opt| ['resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
577
- option_type_list = option_type_list.reject {|opt| ['resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
578
- resource_pool_options = @options_interface.options_for_source('zonePools', {groupId: group_id, siteId: group_id, zoneId: cloud_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], planId: service_plan["id"], layoutId: layout["id"]})['data']
579
- resource_pool = resource_pool_options.find {|opt| opt['id'] == options[:resource_pool].to_i} if options[:resource_pool]
580
-
581
- if resource_pool
582
- pool_id = resource_pool['id']
583
- else
584
- if options[:default_resource_pool]
585
- default_resource_pool = resource_pool_options.find {|rp| rp['id'] == options[:default_resource_pool]}
586
- end
587
- resource_pool_option_type ||= {'fieldContext' => 'config', 'fieldName' => 'resourcePoolId', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.', 'defaultValue' => default_resource_pool ? default_resource_pool['name'] : nil}
588
- resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([resource_pool_option_type],options[:options],api_client,{})
589
- resource_pool_prompt.deep_compact!
590
- payload.deep_merge!(resource_pool_prompt)
591
- resource_pool = Morpheus::Cli::OptionTypes.get_last_select()
592
- if resource_pool_option_type['fieldContext'] && resource_pool_prompt[resource_pool_option_type['fieldContext']]
593
- pool_id = resource_pool_prompt[resource_pool_option_type['fieldContext']][resource_pool_option_type['fieldName']]
594
- elsif resource_pool_prompt[resource_pool_option_type['fieldName']]
595
- pool_id = resource_pool_prompt[resource_pool_option_type['fieldName']]
596
+ if locked_fields.include?('config.resourcePoolId')
597
+ pool_id = payload['config']['resourcePoolId'] rescue nil
598
+ elsif locked_fields.include?('config.resourcePool')
599
+ pool_id = payload['config']['resourcePool'] rescue nil
600
+ elsif locked_fields.include?('config.azureResourceGroupId')
601
+ pool_id = payload['config']['azureResourceGroupId'] rescue nil
602
+ else
603
+ has_zone_pools = provision_type && provision_type["id"] && provision_type["hasZonePools"]
604
+ if has_zone_pools
605
+ # pluck out the resourcePoolId option type to prompt for
606
+ resource_pool_option_type = option_type_list.find {|opt| ['resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
607
+ option_type_list = option_type_list.reject {|opt| ['resourcePool','resourcePoolId','azureResourceGroupId'].include?(opt['fieldName']) }
608
+ resource_pool_options = @options_interface.options_for_source('zonePools', {groupId: group_id, siteId: group_id, zoneId: cloud_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], planId: service_plan["id"], layoutId: layout["id"]})['data']
609
+ resource_pool = resource_pool_options.find {|opt| opt['id'] == options[:resource_pool].to_i} if options[:resource_pool]
610
+
611
+ if resource_pool
612
+ pool_id = resource_pool['id']
613
+ else
614
+ if options[:default_resource_pool]
615
+ default_resource_pool = resource_pool_options.find {|rp| rp['id'] == options[:default_resource_pool]}
616
+ end
617
+ resource_pool_option_type ||= {'fieldContext' => 'config', 'fieldName' => 'resourcePoolId', 'type' => 'select', 'fieldLabel' => 'Resource Pool', 'selectOptions' => resource_pool_options, 'required' => true, 'skipSingleOption' => true, 'description' => 'Select resource pool.', 'defaultValue' => default_resource_pool ? default_resource_pool['name'] : nil}
618
+ resource_pool_prompt = Morpheus::Cli::OptionTypes.prompt([resource_pool_option_type],options[:options],api_client,{})
619
+ resource_pool_prompt.deep_compact!
620
+ payload.deep_merge!(resource_pool_prompt)
621
+ resource_pool = Morpheus::Cli::OptionTypes.get_last_select()
622
+ if resource_pool_option_type['fieldContext'] && resource_pool_prompt[resource_pool_option_type['fieldContext']]
623
+ pool_id = resource_pool_prompt[resource_pool_option_type['fieldContext']][resource_pool_option_type['fieldName']]
624
+ elsif resource_pool_prompt[resource_pool_option_type['fieldName']]
625
+ pool_id = resource_pool_prompt[resource_pool_option_type['fieldName']]
626
+ end
627
+ resource_pool ||= resource_pool_options.find {|it| it['id'] == pool_id}
596
628
  end
597
- resource_pool ||= resource_pool_options.find {|it| it['id'] == pool_id}
598
629
  end
599
630
  end
600
631
 
@@ -652,43 +683,81 @@ module Morpheus::Cli::ProvisioningHelper
652
683
  end
653
684
 
654
685
  # prompt for volumes
655
- volumes = prompt_volumes(service_plan, options, api_client, {zoneId: cloud_id, layoutId: layout['id'], siteId: group_id})
656
- if !volumes.empty?
657
- payload['volumes'] = volumes
686
+ if locked_fields.include?('volumes')
687
+ payload['volumes'] = options[:options]['volumes'] if options[:options]['volumes']
688
+ else
689
+ volumes = prompt_volumes(service_plan, options, api_client, {zoneId: cloud_id, layoutId: layout['id'], siteId: group_id})
690
+ if !volumes.empty?
691
+ payload['volumes'] = volumes
692
+ end
658
693
  end
659
694
 
660
695
  # prompt networks
661
- if provision_type && provision_type["hasNetworks"]
662
- # prompt for network interfaces (if supported)
663
- begin
664
- network_interfaces = prompt_network_interfaces(cloud_id, provision_type["id"], pool_id, options)
665
- if !network_interfaces.empty?
666
- payload['networkInterfaces'] = network_interfaces
696
+ if locked_fields.include?('networks')
697
+ # payload['networkInterfaces'] = options[:options]['networkInterfaces'] if options[:options]['networkInterfaces']
698
+ else
699
+ if provision_type && provision_type["hasNetworks"]
700
+ # prompt for network interfaces (if supported)
701
+ begin
702
+ network_interfaces = prompt_network_interfaces(cloud_id, provision_type["id"], pool_id, options)
703
+ if !network_interfaces.empty?
704
+ payload['networkInterfaces'] = network_interfaces
705
+ end
706
+ rescue RestClient::Exception => e
707
+ print yellow,"Unable to load network options. Proceeding...",reset,"\n"
708
+ print_rest_exception(e, options) if Morpheus::Logging.debug?
667
709
  end
668
- rescue RestClient::Exception => e
669
- print yellow,"Unable to load network options. Proceeding...",reset,"\n"
670
- print_rest_exception(e, options) if Morpheus::Logging.debug?
671
710
  end
672
711
  end
673
- # end
674
712
 
675
713
  # Security Groups
676
- # prompt for multiple security groups
714
+ # look for securityGroups option type... this is old and goofy
677
715
  sg_option_type = option_type_list.find {|opt| ((opt['code'] == 'provisionType.amazon.securityId') || (opt['name'] == 'securityId')) }
678
716
  option_type_list = option_type_list.reject {|opt| ((opt['code'] == 'provisionType.amazon.securityId') || (opt['name'] == 'securityId')) }
679
- # ok.. seed data has changed and serverTypes do not have this optionType anymore...
680
- if sg_option_type.nil?
681
- if provision_type && (provision_type["code"] == 'amazon')
682
- sg_option_type = {'fieldContext' => 'config', 'fieldName' => 'securityId', 'type' => 'select', 'fieldLabel' => 'Security Group', 'optionSource' => 'amazonSecurityGroup', 'required' => true, 'description' => 'Select security group.', 'defaultValue' => options[:default_security_group]}
717
+ if locked_fields.include?('securityGroups')
718
+ # payload['securityGroups'] = options[:options]['securityGroups'] if options[:options]['securityGroups']
719
+ else
720
+ # prompt for multiple security groups
721
+ # ok.. seed data has changed and serverTypes do not have this optionType anymore...
722
+ if sg_option_type.nil?
723
+ if provision_type && (provision_type["code"] == 'amazon')
724
+ sg_option_type = {'fieldContext' => 'config', 'fieldName' => 'securityId', 'type' => 'select', 'fieldLabel' => 'Security Group', 'optionSource' => 'amazonSecurityGroup', 'required' => true, 'description' => 'Select security group.', 'defaultValue' => options[:default_security_group]}
725
+ end
683
726
  end
684
- end
685
- has_security_groups = !!sg_option_type
686
- if options[:security_groups]
687
- payload['securityGroups'] = options[:security_groups].collect {|sg_id| {'id' => sg_id} }
688
- elsif has_security_groups
689
- security_groups_array = prompt_security_groups(sg_option_type, {zoneId: cloud_id, poolId: pool_id}, options)
690
- if !security_groups_array.empty?
691
- payload['securityGroups'] = security_groups_array.collect {|sg_id| {'id' => sg_id} }
727
+ sg_api_params = {zoneId: cloud_id, poolId: pool_id}
728
+ has_security_groups = !!sg_option_type
729
+ available_security_groups = []
730
+ if sg_option_type && sg_option_type['type'] == 'select' && sg_option_type['optionSource']
731
+ sg_option_results = options_interface.options_for_source(sg_option_type['optionSource'], sg_api_params)
732
+ available_security_groups = sg_option_results['data'].collect do |it|
733
+ {"id" => it["value"] || it["id"], "name" => it["name"], "value" => it["value"] || it["id"]}
734
+ end
735
+ end
736
+ if options[:security_groups]
737
+ # work with id or names, API expects ids though.
738
+ payload['securityGroups'] = options[:security_groups].collect {|sg_id|
739
+ found_sg = available_security_groups.find {|it| sg_id && (sg_id.to_s == it['id'].to_s || sg_id.to_s == it['name'].to_s) }
740
+ if found_sg.nil?
741
+ print_red_alert "Security group not found by name or id '#{sg_id}'"
742
+ exit 1
743
+ end
744
+ {'id' => found_sg['id']}
745
+ }
746
+
747
+ elsif has_security_groups
748
+ do_prompt_sg = true
749
+ if options[:default_security_groups]
750
+ payload['securityGroups'] = options[:default_security_groups]
751
+ security_groups_value = options[:default_security_groups].collect {|sg| sg['id'] }.join(',') rescue options[:default_security_groups]
752
+ # do_prompt_sg = (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Modify security groups? (#{security_groups_value})", {:default => false}))
753
+ do_prompt_sg = (!no_prompt && Morpheus::Cli::OptionTypes.confirm("Modify security groups?", {:default => false}))
754
+ end
755
+ if do_prompt_sg
756
+ security_groups_array = prompt_security_groups(sg_option_type, sg_api_params, options)
757
+ if !security_groups_array.empty?
758
+ payload['securityGroups'] = security_groups_array.collect {|sg_id| {'id' => sg_id} }
759
+ end
760
+ end
692
761
  end
693
762
  end
694
763
 
@@ -732,10 +801,35 @@ module Morpheus::Cli::ProvisioningHelper
732
801
  payload['evars'] = evars
733
802
  end
734
803
 
735
- # prompt for metadata variables
736
- metadata = prompt_metadata(options)
737
- if !metadata.empty?
738
- payload['metadata'] = metadata
804
+ # metadata tags
805
+ if options[:options]['metadata'].is_a?(Array) && !options[:metadata]
806
+ options[:metadata] = options[:options]['metadata']
807
+ end
808
+ if options[:metadata]
809
+ if options[:metadata] == "[]" || options[:metadata] == "null"
810
+ payload['metadata'] = []
811
+ elsif options[:metadata].is_a?(Array)
812
+ payload['metadata'] = options[:metadata]
813
+ else
814
+ # parse string into format name:value, name:value
815
+ # merge IDs from current metadata
816
+ # todo: should allow quoted semicolons..
817
+ metadata_list = options[:metadata].split(",").select {|it| !it.to_s.empty? }
818
+ metadata_list = metadata_list.collect do |it|
819
+ metadata_pair = it.split(":")
820
+ row = {}
821
+ row['name'] = metadata_pair[0].to_s.strip
822
+ row['value'] = metadata_pair[1].to_s.strip
823
+ row
824
+ end
825
+ payload['metadata'] = metadata_list
826
+ end
827
+ else
828
+ # prompt for metadata tags
829
+ metadata = prompt_metadata(options)
830
+ if !metadata.empty?
831
+ payload['metadata'] = metadata
832
+ end
739
833
  end
740
834
 
741
835
  return payload
@@ -1293,6 +1387,7 @@ module Morpheus::Cli::ProvisioningHelper
1293
1387
  # end
1294
1388
  # end
1295
1389
 
1390
+ # networkInterfaces may be passed as objects from blueprints or via -O networkInterfaces='[]'
1296
1391
  field_context = interface_index == 0 ? "networkInterface" : "networkInterface#{interface_index+1}"
1297
1392
  network_interface = {}
1298
1393
  if options[:options] && options[:options]['networkInterfaces'] && options[:options]['networkInterfaces'][interface_index]
@@ -1300,14 +1395,20 @@ module Morpheus::Cli::ProvisioningHelper
1300
1395
  end
1301
1396
 
1302
1397
  default_network_id = network_interface['networkId'] || network_interface['id']
1303
-
1304
- if network_interface['network'] && network_interface['network']['id']
1305
- if network_interface['network']['subnet']
1306
- default_network_id = "subnet-#{network_interface['network']['subnet']}"
1307
- elsif network_interface['network']['group']
1308
- default_network_id = "networkGroup-#{network_interface['network']['group']}"
1309
- else
1310
- default_network_id = "network-#{network_interface['network']['id']}"
1398
+ if default_network_id.nil? && network_interface['network'].is_a?(Hash) && network_interface['network']['id']
1399
+ default_network_id = network_interface['network']['id']
1400
+ end
1401
+ # JD: this for cluster or server prompting perhaps?
1402
+ # because zoneNetworkOptions already returns foramt like "id": "network-1"
1403
+ if !default_network_id || !default_network_id.to_s.include?("-")
1404
+ if network_interface['network'] && network_interface['network']['id']
1405
+ if network_interface['network']['subnet']
1406
+ default_network_id = "subnet-#{network_interface['network']['subnet']}"
1407
+ elsif network_interface['network']['group']
1408
+ default_network_id = "networkGroup-#{network_interface['network']['group']}"
1409
+ else
1410
+ default_network_id = "network-#{network_interface['network']['id']}"
1411
+ end
1311
1412
  end
1312
1413
  end
1313
1414
 
@@ -1318,7 +1419,7 @@ module Morpheus::Cli::ProvisioningHelper
1318
1419
  network_interface['network'] = {}
1319
1420
  network_interface['network']['id'] = v_prompt[field_context]['networkId'].to_s
1320
1421
  selected_network = networks.find {|it| it["id"].to_s == network_interface['network']['id'] }
1321
- network_options.reject! {|it| it['value'] == v_prompt[field_context]['networkId']}
1422
+ #network_options.reject! {|it| it['value'] == v_prompt[field_context]['networkId']}
1322
1423
 
1323
1424
  if !selected_network
1324
1425
  print_red_alert "Network not found by id #{network_interface['network']['id']}!"
@@ -1332,10 +1433,13 @@ module Morpheus::Cli::ProvisioningHelper
1332
1433
  network_interface['networkInterfaceTypeId'] = v_prompt[field_context]['networkInterfaceTypeId'].to_i
1333
1434
  end
1334
1435
 
1335
- # choose IP unless network has a pool configured
1436
+ # choose IP if network allows it
1437
+ # allowStaticOverride is only returned in 4.2.1+, so treat null as true for now..
1438
+ ip_available = selected_network['allowStaticOverride'] == true || selected_network['allowStaticOverride'].nil?
1336
1439
  ip_required = true
1337
1440
  if selected_network['id'].to_s.include?('networkGroup')
1338
1441
  #puts "IP Address: Using network group." if !no_prompt
1442
+ ip_available = false
1339
1443
  ip_required = false
1340
1444
  elsif selected_network['pool']
1341
1445
  #puts "IP Address: Using pool '#{selected_network['pool']['name']}'" if !no_prompt
@@ -1344,9 +1448,12 @@ module Morpheus::Cli::ProvisioningHelper
1344
1448
  #puts "IP Address: Using DHCP" if !no_prompt
1345
1449
  ip_required = false
1346
1450
  end
1347
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'ipAddress', 'type' => 'text', 'fieldLabel' => "IP Address", 'required' => ip_required, 'description' => 'Enter an IP for this network interface. x.x.x.x', 'defaultValue' => network_interface['ipAddress']}], options[:options])
1348
- if v_prompt[field_context] && !v_prompt[field_context]['ipAddress'].to_s.empty?
1349
- network_interface['ipAddress'] = v_prompt[field_context]['ipAddress']
1451
+
1452
+ if ip_available
1453
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => field_context, 'fieldName' => 'ipAddress', 'type' => 'text', 'fieldLabel' => "IP Address", 'required' => ip_required, 'description' => 'Enter an IP for this network interface. x.x.x.x', 'defaultValue' => network_interface['ipAddress']}], options[:options])
1454
+ if v_prompt[field_context] && !v_prompt[field_context]['ipAddress'].to_s.empty?
1455
+ network_interface['ipAddress'] = v_prompt[field_context]['ipAddress']
1456
+ end
1350
1457
  end
1351
1458
 
1352
1459
  network_interfaces << network_interface
@@ -1493,27 +1600,6 @@ module Morpheus::Cli::ProvisioningHelper
1493
1600
  return payload
1494
1601
  end
1495
1602
 
1496
- # reject old volume option types
1497
- # these will eventually get removed from the associated optionTypes
1498
- def reject_volume_option_types(option_types)
1499
- option_types.reject {|opt|
1500
- ['osDiskSize', 'osDiskType',
1501
- 'diskSize', 'diskType',
1502
- 'datastoreId', 'storagePodId'
1503
- ].include?(opt['fieldName'])
1504
- }
1505
- end
1506
-
1507
- # reject old networking option types
1508
- # these will eventually get removed from the associated optionTypes
1509
- def reject_networking_option_types(option_types)
1510
- option_types.reject {|opt|
1511
- ['networkId', 'networkType', 'ipAddress', 'netmask', 'gateway', 'nameservers',
1512
- 'vmwareNetworkType', 'vmwareIpAddress', 'vmwareNetmask', 'vmwareGateway', 'vmwareNameservers',
1513
- 'subnetId'
1514
- ].include?(opt['fieldName'])
1515
- }
1516
- end
1517
1603
 
1518
1604
  # reject old option types that now come from the selected service plan
1519
1605
  # these will eventually get removed from the associated optionTypes