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.
- checksums.yaml +4 -4
- data/Dockerfile +1 -1
- data/README.md +1 -3
- data/lib/morpheus/api/api_client.rb +48 -14
- data/lib/morpheus/api/certificate_types_interface.rb +14 -0
- data/lib/morpheus/api/certificates_interface.rb +9 -0
- data/lib/morpheus/api/integration_types_interface.rb +14 -0
- data/lib/morpheus/api/integrations_interface.rb +7 -22
- data/lib/morpheus/api/network_services_interface.rb +14 -0
- data/lib/morpheus/api/read_interface.rb +23 -0
- data/lib/morpheus/api/rest_interface.rb +12 -10
- data/lib/morpheus/api/roles_interface.rb +7 -0
- data/lib/morpheus/api/servers_interface.rb +7 -0
- data/lib/morpheus/api/user_settings_interface.rb +38 -18
- data/lib/morpheus/api/vdi_allocations_interface.rb +9 -0
- data/lib/morpheus/api/vdi_apps_interface.rb +9 -0
- data/lib/morpheus/api/vdi_gateways_interface.rb +9 -0
- data/lib/morpheus/api/vdi_interface.rb +28 -0
- data/lib/morpheus/api/vdi_pools_interface.rb +19 -0
- data/lib/morpheus/cli.rb +9 -2
- data/lib/morpheus/cli/apps.rb +59 -75
- data/lib/morpheus/cli/catalog_item_types_command.rb +13 -13
- data/lib/morpheus/cli/certificates_command.rb +575 -0
- data/lib/morpheus/cli/cli_command.rb +61 -6
- data/lib/morpheus/cli/clouds.rb +1 -0
- data/lib/morpheus/cli/clusters.rb +1 -1
- data/lib/morpheus/cli/commands/standard/man_command.rb +4 -5
- data/lib/morpheus/cli/hosts.rb +245 -224
- data/lib/morpheus/cli/instances.rb +150 -167
- data/lib/morpheus/cli/integrations_command.rb +588 -41
- data/lib/morpheus/cli/login.rb +7 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +33 -18
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +3 -3
- data/lib/morpheus/cli/mixins/vdi_helper.rb +246 -0
- data/lib/morpheus/cli/network_routers_command.rb +22 -9
- data/lib/morpheus/cli/networks_command.rb +2 -2
- data/lib/morpheus/cli/option_types.rb +34 -33
- data/lib/morpheus/cli/remote.rb +1 -1
- data/lib/morpheus/cli/reports_command.rb +4 -1
- data/lib/morpheus/cli/roles.rb +215 -55
- data/lib/morpheus/cli/subnets_command.rb +11 -2
- data/lib/morpheus/cli/user_settings_command.rb +268 -57
- data/lib/morpheus/cli/vdi_allocations_command.rb +159 -0
- data/lib/morpheus/cli/vdi_apps_command.rb +317 -0
- data/lib/morpheus/cli/vdi_command.rb +359 -0
- data/lib/morpheus/cli/vdi_gateways_command.rb +290 -0
- data/lib/morpheus/cli/vdi_pools_command.rb +571 -0
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/rest_client.rb +30 -0
- data/lib/morpheus/terminal.rb +15 -7
- 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
|
-
|
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
|
-
|
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
|
-
#
|
52
|
-
|
53
|
-
|
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']
|
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
|
-
|
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
|
-
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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 << "
|
955
|
+
out << "#{'=' * header.length}\n"
|
955
956
|
select_options.each do |option|
|
956
957
|
out << " * #{option['name']} [#{option['value']}]\n"
|
957
958
|
end
|
data/lib/morpheus/cli/remote.rb
CHANGED
@@ -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 => "
|
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}"
|
data/lib/morpheus/cli/roles.rb
CHANGED
@@ -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[:
|
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[:
|
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
|
-
|
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[:
|
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' =>
|
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
|
-
|
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
|
-
|
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(
|
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
|
-
|
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
|
-
|
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
|
-
|
1002
|
-
group_id = nil
|
1003
|
-
cloud_id = nil
|
1023
|
+
cloud = nil
|
1004
1024
|
if !do_all
|
1005
|
-
|
1006
|
-
if
|
1007
|
-
|
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
|
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
|
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' =>
|
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
|