morpheus-cli 5.3.0.3 → 5.3.1

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/README.md +1 -3
  4. data/lib/morpheus/api/api_client.rb +48 -14
  5. data/lib/morpheus/api/certificate_types_interface.rb +14 -0
  6. data/lib/morpheus/api/certificates_interface.rb +9 -0
  7. data/lib/morpheus/api/integration_types_interface.rb +14 -0
  8. data/lib/morpheus/api/integrations_interface.rb +7 -22
  9. data/lib/morpheus/api/network_services_interface.rb +14 -0
  10. data/lib/morpheus/api/read_interface.rb +23 -0
  11. data/lib/morpheus/api/rest_interface.rb +12 -10
  12. data/lib/morpheus/api/roles_interface.rb +7 -0
  13. data/lib/morpheus/api/servers_interface.rb +7 -0
  14. data/lib/morpheus/api/user_settings_interface.rb +38 -18
  15. data/lib/morpheus/api/vdi_allocations_interface.rb +9 -0
  16. data/lib/morpheus/api/vdi_apps_interface.rb +9 -0
  17. data/lib/morpheus/api/vdi_gateways_interface.rb +9 -0
  18. data/lib/morpheus/api/vdi_interface.rb +28 -0
  19. data/lib/morpheus/api/vdi_pools_interface.rb +19 -0
  20. data/lib/morpheus/cli.rb +9 -2
  21. data/lib/morpheus/cli/apps.rb +59 -75
  22. data/lib/morpheus/cli/catalog_item_types_command.rb +13 -13
  23. data/lib/morpheus/cli/certificates_command.rb +575 -0
  24. data/lib/morpheus/cli/cli_command.rb +61 -6
  25. data/lib/morpheus/cli/clouds.rb +1 -0
  26. data/lib/morpheus/cli/clusters.rb +1 -1
  27. data/lib/morpheus/cli/commands/standard/man_command.rb +4 -5
  28. data/lib/morpheus/cli/hosts.rb +245 -224
  29. data/lib/morpheus/cli/instances.rb +150 -167
  30. data/lib/morpheus/cli/integrations_command.rb +588 -41
  31. data/lib/morpheus/cli/login.rb +7 -0
  32. data/lib/morpheus/cli/mixins/print_helper.rb +33 -18
  33. data/lib/morpheus/cli/mixins/provisioning_helper.rb +3 -3
  34. data/lib/morpheus/cli/mixins/vdi_helper.rb +246 -0
  35. data/lib/morpheus/cli/network_routers_command.rb +22 -9
  36. data/lib/morpheus/cli/networks_command.rb +2 -2
  37. data/lib/morpheus/cli/option_types.rb +34 -33
  38. data/lib/morpheus/cli/remote.rb +1 -1
  39. data/lib/morpheus/cli/reports_command.rb +4 -1
  40. data/lib/morpheus/cli/roles.rb +215 -55
  41. data/lib/morpheus/cli/subnets_command.rb +11 -2
  42. data/lib/morpheus/cli/user_settings_command.rb +268 -57
  43. data/lib/morpheus/cli/vdi_allocations_command.rb +159 -0
  44. data/lib/morpheus/cli/vdi_apps_command.rb +317 -0
  45. data/lib/morpheus/cli/vdi_command.rb +359 -0
  46. data/lib/morpheus/cli/vdi_gateways_command.rb +290 -0
  47. data/lib/morpheus/cli/vdi_pools_command.rb +571 -0
  48. data/lib/morpheus/cli/version.rb +1 -1
  49. data/lib/morpheus/rest_client.rb +30 -0
  50. data/lib/morpheus/terminal.rb +15 -7
  51. metadata +18 -2
@@ -521,10 +521,10 @@ class Morpheus::Cli::NetworksCommand
521
521
  if options['cidr']
522
522
  payload['network']['cidr'] = options['cidr']
523
523
  else
524
- #if network_type['cidrEditable']
524
+ if network_type['cidrEditable']
525
525
  v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cidr', 'fieldLabel' => 'CIDR', 'type' => 'text', 'required' => network_type['cidrRequired'], 'description' => ''}], options)
526
526
  payload['network']['cidr'] = v_prompt['cidr']
527
- #end
527
+ end
528
528
  end
529
529
 
530
530
  # Gateway
@@ -48,13 +48,23 @@ module Morpheus
48
48
  end
49
49
  end
50
50
  # puts "Options Prompt #{options}"
51
- # only sort if displayOrder is set
52
- sorted_option_types = (option_types[0] && option_types[0]['displayOrder']) ? option_types.sort { |x,y| x['displayOrder'].to_i <=> y['displayOrder'].to_i } : option_types
53
- sorted_option_types.each do |option_type|
51
+ # Sort options by default, group, advanced
52
+ cur_field_group = 'default'
53
+ (
54
+ option_types.reject {|it| (it['fieldGroup'] || 'default') != 'default'}.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i} +
55
+ option_types.reject {|it| ['default', 'advanced'].include?(it['fieldGroup'] || 'default')}.sort{|a,b| a['displayOrder'] <=> b['displayOrder']}.group_by{|it| it['fieldGroup']}.values.collect { |it| it.sort{|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}}.flatten +
56
+ option_types.reject {|it| it['fieldGroup'] != 'advanced'}.sort {|a,b| a['displayOrder'].to_i <=> b['displayOrder'].to_i}
57
+ ).each do |option_type|
54
58
  context_map = results
55
59
  value = nil
56
60
  value_found=false
57
61
 
62
+ if cur_field_group != (option_type['fieldGroup'] || 'default')
63
+ cur_field_group = option_type['fieldGroup']
64
+ cur_field_group = cur_field_group.to_s.sub(/options\Z/i, "").strip # avoid "ADVANCED OPTION OPTIONS"
65
+ print "\n#{cur_field_group.upcase} OPTIONS\n#{"=" * ("#{cur_field_group} OPTIONS".length)}\n\n"
66
+ end
67
+
58
68
  # How about this instead?
59
69
  # option_type = option_type.clone
60
70
  # field_key = [option_type['fieldContext'], option_type['fieldName']].select {|it| it && it != '' }.join('.')
@@ -78,6 +88,7 @@ module Morpheus
78
88
  if !option_type['visibleOnCode'].to_s.empty?
79
89
  visible_option_check_value = option_type['visibleOnCode']
80
90
  end
91
+
81
92
  if !visible_option_check_value.to_s.empty?
82
93
  # support formats code=value or code:value OR code:(value|value2|value3)
83
94
  # OR fieldContext.fieldName=value
@@ -101,8 +112,9 @@ module Morpheus
101
112
  end
102
113
  if depends_on_option_type
103
114
  # dependent option type has a different value
104
- depends_on_field_key = depends_on_option_type['fieldContext'] ? "#{depends_on_option_type['fieldContext']}.#{depends_on_option_type['fieldName']}" : "#{depends_on_option_type['fieldName']}"
115
+ depends_on_field_key = depends_on_option_type['fieldContext'].nil? || depends_on_option_type['fieldContext'].empty? ? "#{depends_on_option_type['fieldName']}" : "#{depends_on_option_type['fieldContext']}.#{depends_on_option_type['fieldName']}"
105
116
  found_dep_value = get_object_value(results, depends_on_field_key) || get_object_value(options, depends_on_field_key)
117
+
106
118
  if depends_on_values.size > 0
107
119
  # must be in the specified values
108
120
  # todo: uhh this actually needs to change to parse regex
@@ -139,7 +151,9 @@ module Morpheus
139
151
  value = cur_namespace[field_name]
140
152
  input_value = ['select', 'multiSelect','typeahead', 'multiTypeahead'].include?(option_type['type']) && option_type['fieldInput'] ? cur_namespace[option_type['fieldInput']] : nil
141
153
  if option_type['type'] == 'number'
142
- value = value.to_s.include?('.') ? value.to_f : value.to_i
154
+ if !value.to_s.empty?
155
+ value = value.to_s.include?('.') ? value.to_f : value.to_i
156
+ end
143
157
  # these select prompts should just fall down through below, with the extra params no_prompt, use_value
144
158
  elsif option_type['type'] == 'select'
145
159
  value = select_prompt(option_type.merge({'defaultValue' => value, 'defaultInputValue' => input_value}), api_client, (option_type['noParams'] ? {} : (api_params || {}).merge(results)), true)
@@ -273,22 +287,6 @@ module Morpheus
273
287
  results
274
288
  end
275
289
 
276
- def self.grails_params(data, context=nil)
277
- params = {}
278
- data.each do |k,v|
279
- if v.is_a?(Hash)
280
- params.merge!(grails_params(v, context ? "#{context}.#{k.to_s}" : k))
281
- else
282
- if context
283
- params["#{context}.#{k.to_s}"] = v
284
- else
285
- params[k.to_s] = v
286
- end
287
- end
288
- end
289
- return params
290
- end
291
-
292
290
  def self.radio_prompt(option_type)
293
291
  value_found = false
294
292
  value = nil
@@ -330,7 +328,9 @@ module Morpheus
330
328
  print "#{option_type['fieldLabel']}#{option_type['fieldAddOn'] ? ('(' + option_type['fieldAddOn'] + ') ') : '' }#{!option_type['required'] ? ' (optional)' : ''}#{!option_type['defaultValue'].to_s.empty? ? ' ['+option_type['defaultValue'].to_s+']' : ''}: "
331
329
  input = $stdin.gets.chomp!
332
330
  value = input.empty? ? option_type['defaultValue'] : input
333
- value = value.to_s.include?('.') ? value.to_f : value.to_i
331
+ if !value.to_s.empty?
332
+ value = value.to_s.include?('.') ? value.to_f : value.to_i
333
+ end
334
334
  if input == '?'
335
335
  help_prompt(option_type)
336
336
  elsif !value.nil? || option_type['required'] != true
@@ -363,7 +363,7 @@ module Morpheus
363
363
  if option_type['selectOptions']
364
364
  # calculate from inline lambda
365
365
  if option_type['selectOptions'].is_a?(Proc)
366
- select_options = option_type['selectOptions'].call(api_client, grails_params(api_params || {}))
366
+ select_options = option_type['selectOptions'].call(api_client, api_params || {})
367
367
  else
368
368
  # todo: better type validation
369
369
  select_options = option_type['selectOptions']
@@ -371,13 +371,13 @@ module Morpheus
371
371
  elsif option_type['optionSource']
372
372
  # calculate from inline lambda
373
373
  if option_type['optionSource'].is_a?(Proc)
374
- select_options = option_type['optionSource'].call(api_client, grails_params(api_params || {}))
374
+ select_options = option_type['optionSource'].call(api_client, api_params || {})
375
375
  elsif option_type['optionSource'] == 'list'
376
376
  # /api/options/list is a special action for custom OptionTypeLists, just need to pass the optionTypeId parameter
377
- select_options = load_source_options(option_type['optionSource'], api_client, grails_params(api_params || {}).merge({'optionTypeId' => option_type['id']}))
377
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {}.merge({'optionTypeId' => option_type['id']}))
378
378
  else
379
379
  # remote optionSource aka /api/options/$optionSource?
380
- select_options = load_source_options(option_type['optionSource'], api_client, grails_params(api_params || {}))
380
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
381
381
  end
382
382
  else
383
383
  raise "option '#{field_key}' is type: 'select' and missing selectOptions or optionSource!"
@@ -571,6 +571,7 @@ module Morpheus
571
571
  # looking for help with this input
572
572
  if input == '?'
573
573
  help_prompt(option_type)
574
+ select_options = load_options(option_type, api_client, api_params)
574
575
  display_select_options(option_type, select_options) unless select_options.empty?
575
576
  next
576
577
  end
@@ -888,7 +889,7 @@ module Morpheus
888
889
  if option_type['selectOptions']
889
890
  # calculate from inline lambda
890
891
  if option_type['selectOptions'].is_a?(Proc)
891
- select_options = option_type['selectOptions'].call(api_client, grails_params(api_params || {}))
892
+ select_options = option_type['selectOptions'].call(api_client, api_params || {})
892
893
  else
893
894
  select_options = option_type['selectOptions']
894
895
  end
@@ -903,13 +904,13 @@ module Morpheus
903
904
  elsif option_type['optionSource']
904
905
  # calculate from inline lambda
905
906
  if option_type['optionSource'].is_a?(Proc)
906
- select_options = option_type['optionSource'].call(api_client, grails_params(api_params || {}))
907
+ select_options = option_type['optionSource'].call(api_client, api_params || {})
907
908
  elsif option_type['optionSource'] == 'list'
908
909
  # /api/options/list is a special action for custom OptionTypeLists, just need to pass the optionTypeId parameter
909
- select_options = load_source_options(option_type['optionSource'], api_client, grails_params(api_params || {}).merge({'optionTypeId' => option_type['id']}))
910
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {}.merge({'optionTypeId' => option_type['id']}))
910
911
  else
911
912
  # remote optionSource aka /api/options/$optionSource?
912
- select_options = load_source_options(option_type['optionSource'], api_client, grails_params(api_params || {}))
913
+ select_options = load_source_options(option_type['optionSource'], option_type['optionSourceType'], api_client, api_params || {})
913
914
  end
914
915
  else
915
916
  raise "option '#{field_key}' is type: 'typeahead' and missing selectOptions or optionSource!"
@@ -935,8 +936,8 @@ module Morpheus
935
936
  end
936
937
 
937
938
 
938
- def self.load_source_options(source,api_client,params)
939
- api_client.options.options_for_source(source,params)['data']
939
+ def self.load_source_options(source,sourceType,api_client,params)
940
+ api_client.options.options_for_source("#{sourceType ? "#{sourceType}/" : ''}#{source}",params)['data']
940
941
  end
941
942
 
942
943
  def self.format_select_options_help(opt, select_options = [], paging = nil)
@@ -951,7 +952,7 @@ module Morpheus
951
952
  out = ""
952
953
  out << "\n"
953
954
  out << "#{header}\n"
954
- out << "===============\n"
955
+ out << "#{'=' * header.length}\n"
955
956
  select_options.each do |option|
956
957
  out << " * #{option['name']} [#{option['value']}]\n"
957
958
  end
@@ -1337,7 +1337,7 @@ EOT
1337
1337
  include Term::ANSIColor
1338
1338
 
1339
1339
  # for caching the the contents of YAML file $home/appliances
1340
- # it is structured like :appliance_name => {:host => "htt[://api.gomorpheus.com", :active => true}
1340
+ # it is structured like :appliance_name => {:host => "https://api.gomorpheus.com", :active => true}
1341
1341
  # not named @@appliances to avoid confusion with the instance variable . This is also a command class...
1342
1342
  @@appliance_config = nil
1343
1343
 
@@ -368,7 +368,6 @@ class Morpheus::Cli::ReportsCommand
368
368
  do_mkdir = false
369
369
  optparse = Morpheus::Cli::OptionParser.new do |opts|
370
370
  opts.banner = subcommand_usage("[id] [file]")
371
- build_common_options(opts, options, [:dry_run, :remote])
372
371
  opts.on( '--format VALUE', String, "Report Format for exported file, json or csv. Default is json." ) do |val|
373
372
  report_format = val
374
373
  end
@@ -379,6 +378,7 @@ class Morpheus::Cli::ReportsCommand
379
378
  opts.on( '-p', '--mkdir', "Create missing directories for [local-file] if they do not exist." ) do
380
379
  do_mkdir = true
381
380
  end
381
+ build_common_options(opts, options, [:dry_run, :remote])
382
382
  opts.footer = "Export a report result as json or csv." + "\n" +
383
383
  "[id] is required. This is id of the report result." + "\n" +
384
384
  "[file] is required. This is local destination for the downloaded file."
@@ -394,6 +394,9 @@ class Morpheus::Cli::ReportsCommand
394
394
 
395
395
  outfile = args[1]
396
396
  outfile = File.expand_path(outfile)
397
+ if outfile =~ /\.csv\Z/i
398
+ report_format = "csv"
399
+ end
397
400
 
398
401
  if Dir.exists?(outfile)
399
402
  print_red_alert "[file] is invalid. It is the name of an existing directory: #{outfile}"
@@ -13,7 +13,7 @@ class Morpheus::Cli::Roles
13
13
  include Morpheus::Cli::AccountsHelper
14
14
  include Morpheus::Cli::ProvisioningHelper
15
15
  include Morpheus::Cli::WhoamiHelper
16
- register_subcommands :list, :get, :add, :update, :remove, :'list-permissions', :'update-feature-access', :'update-global-group-access', :'update-group-access', :'update-global-cloud-access', :'update-cloud-access', :'update-global-instance-type-access', :'update-instance-type-access', :'update-global-blueprint-access', :'update-blueprint-access', :'update-global-catalog-item-type-access', :'update-catalog-item-type-access', :'update-persona-access'
16
+ register_subcommands :list, :get, :add, :update, :remove, :'list-permissions', :'update-feature-access', :'update-global-group-access', :'update-group-access', :'update-global-cloud-access', :'update-cloud-access', :'update-global-instance-type-access', :'update-instance-type-access', :'update-global-blueprint-access', :'update-blueprint-access', :'update-global-catalog-item-type-access', :'update-catalog-item-type-access', :'update-persona-access', :'update-global-vdi-pool-access', :'update-vdi-pool-access'
17
17
  alias_subcommand :details, :get
18
18
  set_default_subcommand :list
19
19
 
@@ -102,19 +102,23 @@ class Morpheus::Cli::Roles
102
102
  options[:include_blueprint_access] = true
103
103
  end
104
104
  opts.on(nil,'--catalog-item-type-access', "Display Catalog Item Type Access") do
105
- options[:include_catalog_item_types_access] = true
105
+ options[:include_catalog_item_type_access] = true
106
106
  end
107
107
  opts.on(nil,'--personas', "Display Persona Access") do
108
108
  options[:include_personas_access] = true
109
109
  end
110
+ opts.on(nil,'--vdi-pool-access', "Display VDI Pool Access") do
111
+ options[:include_vdi_pool_access] = true
112
+ end
110
113
  opts.on('-a','--all', "Display All Access Lists") do
111
114
  options[:include_feature_access] = true
112
115
  options[:include_group_access] = true
113
116
  options[:include_cloud_access] = true
114
117
  options[:include_instance_type_access] = true
115
118
  options[:include_blueprint_access] = true
116
- options[:include_catalog_item_types_access] = true
119
+ options[:include_catalog_item_type_access] = true
117
120
  options[:include_personas_access] = true
121
+ options[:include_vdi_pool_access] = true
118
122
  end
119
123
  build_standard_get_options(opts, options)
120
124
  opts.footer = <<-EOT
@@ -206,9 +210,9 @@ EOT
206
210
  rows = rows.select {|row| row[:code].to_s =~ phrase_regexp || row[:name].to_s =~ phrase_regexp }
207
211
  end
208
212
  print as_pretty_table(rows, [:code, :name, :access], options)
209
- print reset,"\n"
213
+ # print reset,"\n"
210
214
  else
211
- print cyan,"Use --permissions to list permissions","\n"
215
+ print cyan,"Use --permissions to list feature permissions","\n"
212
216
  end
213
217
 
214
218
  has_group_access = true
@@ -220,6 +224,7 @@ EOT
220
224
  "Instance Types" => lambda {|it| get_access_string(it['globalInstanceTypeAccess']) },
221
225
  "Blueprints" => lambda {|it| get_access_string(it['globalAppTemplateAccess'] || it['globalBlueprintAccess']) },
222
226
  "Catalog Item Types" => lambda {|it| get_access_string(it['globalCatalogItemTypeAccess']) },
227
+ "VDI Pools" => lambda {|it| get_access_string(it['globalVdiPoolAccess']) },
223
228
  }
224
229
  if role['roleType'].to_s.downcase == 'account'
225
230
  global_access_columns.delete("Groups")
@@ -228,7 +233,7 @@ EOT
228
233
  global_access_columns.delete("Clouds")
229
234
  has_cloud_access = false
230
235
  end
231
- puts as_pretty_table([json_response], global_access_columns, options)
236
+ print as_pretty_table([json_response], global_access_columns, options)
232
237
 
233
238
  if has_group_access
234
239
  #print_h2 "Group Access: #{get_access_string(json_response['globalSiteAccess'])}", options
@@ -246,7 +251,7 @@ EOT
246
251
  else
247
252
  print cyan,"Use -g, --group-access to list custom access","\n"
248
253
  end
249
- print reset,"\n"
254
+ # print reset,"\n"
250
255
  else
251
256
  # print "\n"
252
257
  # print cyan,bold,"Group Access: #{get_access_string(json_response['globalSiteAccess'])}",reset,"\n"
@@ -270,7 +275,7 @@ EOT
270
275
  else
271
276
  print cyan,"Use -c, --cloud-access to list custom access","\n"
272
277
  end
273
- print reset,"\n"
278
+ # print reset,"\n"
274
279
  else
275
280
  # print "\n"
276
281
  # print cyan,bold,"Cloud Access: #{get_access_string(json_response['globalZoneAccess'])}",reset,"\n"
@@ -293,7 +298,7 @@ EOT
293
298
  else
294
299
  print cyan,"Use -i, --instance-type-access to list custom access","\n"
295
300
  end
296
- print reset,"\n"
301
+ # print reset,"\n"
297
302
  else
298
303
  # print "\n"
299
304
  # print cyan,bold,"Instance Type Access: #{get_access_string(json_response['globalInstanceTypeAccess'])}",reset,"\n"
@@ -317,7 +322,7 @@ EOT
317
322
  else
318
323
  print cyan,"Use -b, --blueprint-access to list custom access","\n"
319
324
  end
320
- print reset,"\n"
325
+ # print reset,"\n"
321
326
  else
322
327
  # print "\n"
323
328
  # print cyan,bold,"Blueprint Access: #{get_access_string(json_response['globalAppTemplateAccess'])}",reset,"\n"
@@ -331,7 +336,7 @@ EOT
331
336
  # print "\n"
332
337
  if catalog_item_type_global_access == 'custom'
333
338
  print_h2 "Catalog Item Type Access", options
334
- if options[:include_catalog_item_types_access]
339
+ if options[:include_catalog_item_type_access]
335
340
  rows = catalog_item_type_permissions.collect do |it|
336
341
  {
337
342
  name: it['name'],
@@ -350,6 +355,7 @@ EOT
350
355
 
351
356
  persona_permissions = json_response['personaPermissions'] || json_response['personas'] || []
352
357
  # if options[:include_personas_access]
358
+ print cyan
353
359
  if persona_permissions
354
360
  print_h2 "Persona Access", options
355
361
  rows = persona_permissions.collect do |it|
@@ -358,13 +364,34 @@ EOT
358
364
  access: format_access_string(it['access'], ["none","read","full"]),
359
365
  }
360
366
  end
361
- print as_pretty_table(rows, [:name, :access], options)
362
- print reset,"\n"
367
+ print as_pretty_table(rows, [:name, :access], options)
363
368
  end
364
369
 
365
370
  # print reset,"\n"
366
371
 
372
+ vdi_pool_global_access = json_response['globalVdiPoolAccess']
373
+ vdi_pool_permissions = json_response['vdiPoolPermissions'] || []
374
+ print cyan
375
+ if vdi_pool_global_access == 'custom'
376
+ print_h2 "VDI Pool Access", options
377
+ if options[:include_vdi_pool_access]
378
+ rows = vdi_pool_permissions.collect do |it|
379
+ {
380
+ name: it['name'],
381
+ access: format_access_string(it['access'], ["none","read","full"]),
382
+ }
383
+ end
384
+ print as_pretty_table(rows, [:name, :access], options)
385
+ else
386
+ print cyan,"Use --vdi-pool-access to list custom access","\n"
387
+ end
388
+ else
389
+ # print "\n"
390
+ # print cyan,bold,"VDI Pool Access: #{get_access_string(json_response['globalVdiPoolAccess'])}",reset,"\n"
391
+ end
392
+
367
393
  end
394
+ print reset,"\n"
368
395
 
369
396
  return 0, nil
370
397
  end
@@ -520,7 +547,7 @@ EOT
520
547
  end
521
548
 
522
549
  # v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'optionSource' => 'personas', 'description' => 'Default Persona'}], options[:options], @api_client)
523
- v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'selectOptions' => [{'name'=>'Service Catalog','value'=>'serviceCatalog'},{'name'=>'Standard','value'=>'standard'}], 'description' => 'Default Persona'}], options[:options], @api_client)
550
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'selectOptions' => get_persona_select_options(), 'description' => 'Default Persona'}], options[:options], @api_client)
524
551
  role_payload['defaultPersona'] = {'code' => v_prompt['defaultPersona']} unless v_prompt['defaultPersona'].to_s.strip.empty?
525
552
 
526
553
  payload = {"role" => role_payload}
@@ -782,14 +809,14 @@ EOT
782
809
  def update_group_access(args)
783
810
  options = {}
784
811
  name = nil
785
- group_name = nil
812
+ group_id = nil
786
813
  access_value = nil
787
814
  do_all = false
788
815
  allowed_access_values = ['full', 'read', 'none']
789
816
  optparse = Morpheus::Cli::OptionParser.new do |opts|
790
817
  opts.banner = subcommand_usage("[role] [group] [access]")
791
818
  opts.on( '-g', '--group GROUP', "Group name or id" ) do |val|
792
- group_name = val
819
+ group_id = val
793
820
  end
794
821
  opts.on( nil, '--all', "Update all groups at once." ) do
795
822
  do_all = true
@@ -845,9 +872,8 @@ EOT
845
872
  end
846
873
 
847
874
  group = nil
848
- group_id = nil
849
875
  if !do_all
850
- group = find_group_by_name_or_id_for_provisioning(group_name)
876
+ group = find_group_by_name_or_id_for_provisioning(group_id)
851
877
  return 1 if group.nil?
852
878
  group_id = group['id']
853
879
  end
@@ -932,15 +958,14 @@ EOT
932
958
 
933
959
  def update_cloud_access(args)
934
960
  options = {}
935
- cloud_name = nil
961
+ cloud_id = nil
936
962
  access_value = nil
937
963
  do_all = false
938
964
  allowed_access_values = ['full', 'read', 'none']
939
965
  optparse = Morpheus::Cli::OptionParser.new do |opts|
940
966
  opts.banner = subcommand_usage("[name]")
941
967
  opts.on( '-c', '--cloud CLOUD', "Cloud name or id" ) do |val|
942
- puts "val is : #{val}"
943
- cloud_name = val
968
+ cloud_id = val
944
969
  end
945
970
  opts.on( nil, '--all', "Update all clouds at once." ) do
946
971
  do_all = true
@@ -948,9 +973,6 @@ EOT
948
973
  opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
949
974
  access_value = val
950
975
  end
951
- opts.on( '-g', '--group GROUP', "Group to find cloud in" ) do |val|
952
- options[:group] = val
953
- end
954
976
  build_common_options(opts, options, [:json, :dry_run, :remote])
955
977
  opts.footer = "Update role access for a cloud or all clouds.\n" +
956
978
  "[role] is required. This is the name or id of a role.\n" +
@@ -998,23 +1020,11 @@ EOT
998
1020
  exit 1
999
1021
  end
1000
1022
 
1001
- # crap, group_id is needed for this api, maybe just use infrastructure or some other optionSource instead.
1002
- group_id = nil
1003
- cloud_id = nil
1023
+ cloud = nil
1004
1024
  if !do_all
1005
- group_id = nil
1006
- if !options[:group].nil?
1007
- group = find_group_by_name_or_id_for_provisioning(options[:group])
1008
- group_id = group['id']
1009
- else
1010
- group_id = @active_group_id
1011
- end
1012
- if group_id.nil?
1013
- print_red_alert "Group not found or specified!"
1014
- return 1
1015
- end
1016
- cloud_id = find_cloud_id_by_name(group_id, cloud_name)
1017
- return 1 if cloud_id.nil?
1025
+ cloud = find_cloud_by_name_or_id_for_provisioning(nil, cloud_id)
1026
+ return 1 if cloud.nil?
1027
+ cloud_id = cloud['id']
1018
1028
  end
1019
1029
  params = {}
1020
1030
  if do_all
@@ -1037,7 +1047,7 @@ EOT
1037
1047
  if do_all
1038
1048
  print_green_success "Role #{role['authority']} access updated for all clouds"
1039
1049
  else
1040
- print_green_success "Role #{role['authority']} access updated for cloud id #{cloud_id}"
1050
+ print_green_success "Role #{role['authority']} access updated for cloud #{cloud['name']}"
1041
1051
  end
1042
1052
  end
1043
1053
  return 0
@@ -1534,7 +1544,7 @@ EOT
1534
1544
  build_common_options(opts, options, [:json, :dry_run, :remote])
1535
1545
  opts.footer = "Update role access for a persona or all personas.\n" +
1536
1546
  "[role] is required. This is the name or id of a role.\n" +
1537
- "--persona or --all is required. This is the code of a persona. Service Catalog or Standard\n" +
1547
+ "--persona or --all is required. This is the code of a persona. Service Catalog, Standard, or Virtual Desktop\n" +
1538
1548
  "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1539
1549
  end
1540
1550
  optparse.parse!(args)
@@ -1606,6 +1616,160 @@ EOT
1606
1616
  end
1607
1617
  end
1608
1618
 
1619
+ def update_global_vdi_pool_access(args)
1620
+ usage = "Usage: morpheus roles update-global-vdi-pool-access [role] [full|custom|none]"
1621
+ options = {}
1622
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
1623
+ opts.banner = subcommand_usage("[role] [full|custom|none]")
1624
+ build_common_options(opts, options, [:json, :dry_run, :remote])
1625
+ end
1626
+ optparse.parse!(args)
1627
+ verify_args!(args:args, optparse:optparse, count: 2)
1628
+ name = args[0]
1629
+ access_value = args[1].to_s.downcase
1630
+ if !['full', 'custom', 'none'].include?(access_value)
1631
+ raise_command_error("invalid access value: #{args[1]}", optparse)
1632
+ end
1633
+
1634
+
1635
+ connect(options)
1636
+ begin
1637
+ account = find_account_from_options(options)
1638
+ account_id = account ? account['id'] : nil
1639
+ role = find_role_by_name_or_id(account_id, name)
1640
+ exit 1 if role.nil?
1641
+ # note: VdiPools being plural is odd, the others are singular
1642
+ params = {permissionCode: 'VdiPools', access: access_value}
1643
+ @roles_interface.setopts(options)
1644
+ if options[:dry_run]
1645
+ print_dry_run @roles_interface.dry.update_permission(account_id, role['id'], params)
1646
+ return
1647
+ end
1648
+ json_response = @roles_interface.update_permission(account_id, role['id'], params)
1649
+
1650
+ if options[:json]
1651
+ print JSON.pretty_generate(json_response)
1652
+ print "\n"
1653
+ else
1654
+ print_green_success "Role #{role['authority']} global vdi pool access updated"
1655
+ end
1656
+ rescue RestClient::Exception => e
1657
+ print_rest_exception(e, options)
1658
+ exit 1
1659
+ end
1660
+ end
1661
+
1662
+ def update_vdi_pool_access(args)
1663
+ options = {}
1664
+ vdi_pool_id = nil
1665
+ access_value = nil
1666
+ do_all = false
1667
+ allowed_access_values = ['full', 'none']
1668
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
1669
+ opts.banner = subcommand_usage("[role] [vdi-pool] [access]")
1670
+ opts.on( '--vdi-pool ID', String, "VDI Pool ID or Name" ) do |val|
1671
+ vdi_pool_id = val
1672
+ end
1673
+ opts.on( nil, '--all', "Update all VDI pools at once." ) do
1674
+ do_all = true
1675
+ end
1676
+ opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
1677
+ access_value = val
1678
+ end
1679
+ build_common_options(opts, options, [:json, :dry_run, :remote])
1680
+ opts.footer = "Update role access for a VDI pool or all VDI pools.\n" +
1681
+ "[role] is required. This is the name or id of a role.\n" +
1682
+ "--vdi-pool or --all is required. This is the name or id of a VDI pool.\n" +
1683
+ "--access is required. This is the new access value. #{anded_list(allowed_access_values)}"
1684
+ end
1685
+ optparse.parse!(args)
1686
+
1687
+ # usage: update-vdi-pool-access [role] [access] --all
1688
+ # update-vdi-pool-access [role] [vdi-pool] [access]
1689
+ name = args[0]
1690
+ if do_all
1691
+ verify_args!(args:args, optparse:optparse, min:1, max:2)
1692
+ access_value = args[1] if args[1]
1693
+ else
1694
+ verify_args!(args:args, optparse:optparse, min:1, max:3)
1695
+ vdi_pool_id = args[1] if args[1]
1696
+ access_value = args[2] if args[2]
1697
+ end
1698
+ if !vdi_pool_id && !do_all
1699
+ raise_command_error("missing required argument: [vdi-pool] or --all", optparse)
1700
+ end
1701
+ if !access_value
1702
+ raise_command_error("missing required argument: [access]", optparse)
1703
+ end
1704
+ access_value = access_value.to_s.downcase
1705
+ if !allowed_access_values.include?(access_value)
1706
+ raise_command_error("invalid access value: #{access_value}", optparse)
1707
+ puts optparse
1708
+ return 1
1709
+ end
1710
+
1711
+ connect(options)
1712
+ begin
1713
+ account = find_account_from_options(options)
1714
+ account_id = account ? account['id'] : nil
1715
+ role = find_role_by_name_or_id(account_id, name)
1716
+ return 1 if role.nil?
1717
+
1718
+ role_json = @roles_interface.get(account_id, role['id'])
1719
+ vdi_pool_global_access = role_json['globalVdiPoolAccess']
1720
+ vdi_pool_permissions = role_json['vdiPoolPermissions'] || []
1721
+ if vdi_pool_global_access != 'custom'
1722
+ print "\n", red, "Global VDI Pool Access is currently: #{vdi_pool_global_access.to_s.capitalize}"
1723
+ print "\n", "You must first set it to Custom via `morpheus roles update-global-vdi-pool-access \"#{name}\" custom`"
1724
+ print "\n\n", reset
1725
+ return 1
1726
+ end
1727
+
1728
+ # hacky, but support name or code lookup via the list returned in the show payload
1729
+ vdi_pool = nil
1730
+ if !do_all
1731
+ if vdi_pool_id.to_s =~ /\A\d{1,}\Z/
1732
+ vdi_pool = vdi_pool_permissions.find {|b| b['id'] == vdi_pool_id.to_i }
1733
+ else
1734
+ vdi_pool = vdi_pool_permissions.find {|b| b['name'] == vdi_pool_id }
1735
+ end
1736
+ if vdi_pool.nil?
1737
+ print_red_alert "VDI Pool not found: '#{vdi_pool_id}'"
1738
+ return 1
1739
+ end
1740
+ end
1741
+
1742
+ params = {}
1743
+ if do_all
1744
+ params['allVdiPools'] = true
1745
+ else
1746
+ params['vdiPoolId'] = vdi_pool['id']
1747
+ end
1748
+ params['access'] = access_value
1749
+ @roles_interface.setopts(options)
1750
+ if options[:dry_run]
1751
+ print_dry_run @roles_interface.dry.update_vdi_pool(account_id, role['id'], params)
1752
+ return
1753
+ end
1754
+ json_response = @roles_interface.update_vdi_pool(account_id, role['id'], params)
1755
+
1756
+ if options[:json]
1757
+ print JSON.pretty_generate(json_response)
1758
+ print "\n"
1759
+ else
1760
+ if do_all
1761
+ print_green_success "Role #{role['authority']} access updated for all VDI pools"
1762
+ else
1763
+ print_green_success "Role #{role['authority']} access updated for VDI pool #{vdi_pool['name']}"
1764
+ end
1765
+ end
1766
+ return 0
1767
+ rescue RestClient::Exception => e
1768
+ print_rest_exception(e, options)
1769
+ exit 1
1770
+ end
1771
+ end
1772
+
1609
1773
  private
1610
1774
 
1611
1775
  def add_role_option_types
@@ -1616,7 +1780,7 @@ EOT
1616
1780
  {'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'text'},
1617
1781
  {'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use'},
1618
1782
  {'fieldName' => 'multitenantLocked', 'fieldLabel' => 'Multitenant Locked', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'Prevents subtenants from branching off this role/modifying it. '},
1619
- {'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'selectOptions' => [{'name'=>'Service Catalog','value'=>'serviceCatalog'},{'name'=>'Standard','value'=>'standard'}], 'description' => 'Default Persona'}
1783
+ {'fieldName' => 'defaultPersona', 'fieldLabel' => 'Default Persona', 'type' => 'select', 'selectOptions' => get_persona_select_options(), 'description' => 'Default Persona'}
1620
1784
  ]
1621
1785
  end
1622
1786
 
@@ -1624,20 +1788,16 @@ EOT
1624
1788
  add_role_option_types.reject {|it| ['roleType', 'baseRole'].include?(it['fieldName']) }
1625
1789
  end
1626
1790
 
1627
-
1628
- def find_cloud_id_by_name(group_id, name)
1629
- option_results = @options_interface.options_for_source('clouds', {groupId: group_id})
1630
- match = option_results['data'].find { |grp| grp['value'].to_s == name.to_s || grp['name'].downcase == name.downcase}
1631
- if match.nil?
1632
- print_red_alert "Cloud not found by name #{name}"
1633
- return nil
1634
- else
1635
- return match['value']
1636
- end
1637
- end
1638
-
1639
1791
  def role_type_options
1640
1792
  [{'name' => 'User Role', 'value' => 'user'}, {'name' => 'Account Role', 'value' => 'account'}]
1641
1793
  end
1642
1794
 
1795
+ def get_persona_select_options
1796
+ [
1797
+ {'name'=>'Service Catalog','value'=>'serviceCatalog'},
1798
+ {'name'=>'Standard','value'=>'standard'},
1799
+ {'name'=>'Virtual Desktop','value'=>'vdi'}
1800
+ ]
1801
+ end
1802
+
1643
1803
  end