morpheus-cli 4.2.6 → 4.2.7

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