morpheus-cli 8.0.12.2 → 8.1.0
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/bin/morpheus +7 -4
- data/lib/morpheus/api/api_client.rb +8 -0
- data/lib/morpheus/api/roles_interface.rb +14 -0
- data/lib/morpheus/api/system_types_interface.rb +13 -0
- data/lib/morpheus/api/systems_interface.rb +9 -0
- data/lib/morpheus/cli/commands/backups_command.rb +31 -0
- data/lib/morpheus/cli/commands/catalog_item_types_command.rb +8 -12
- data/lib/morpheus/cli/commands/clusters.rb +18 -14
- data/lib/morpheus/cli/commands/library_option_lists_command.rb +10 -2
- data/lib/morpheus/cli/commands/roles.rb +333 -38
- data/lib/morpheus/cli/commands/service_plans_command.rb +7 -0
- data/lib/morpheus/cli/commands/storage_providers_command.rb +6 -2
- data/lib/morpheus/cli/commands/systems.rb +270 -0
- data/lib/morpheus/cli/commands/tenants_command.rb +12 -3
- data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -0
- data/lib/morpheus/cli/mixins/backups_helper.rb +29 -1
- data/lib/morpheus/cli/version.rb +1 -1
- metadata +5 -2
|
@@ -5,7 +5,7 @@ class Morpheus::Cli::Roles
|
|
|
5
5
|
include Morpheus::Cli::AccountsHelper
|
|
6
6
|
include Morpheus::Cli::ProvisioningHelper
|
|
7
7
|
include Morpheus::Cli::WhoamiHelper
|
|
8
|
-
register_subcommands :list, :get, :add, :update, :remove,
|
|
8
|
+
register_subcommands :list, :get, :add, :update, :remove, :validate,
|
|
9
9
|
:'list-permissions', :'update-feature-access',
|
|
10
10
|
:'update-group-access', :'update-global-group-access', :'update-default-group-access',
|
|
11
11
|
:'update-global-cloud-access', :'update-cloud-access', :'update-default-cloud-access',
|
|
@@ -16,7 +16,8 @@ class Morpheus::Cli::Roles
|
|
|
16
16
|
:'update-global-vdi-pool-access', :'update-vdi-pool-access', :'update-default-vdi-pool-access',
|
|
17
17
|
:'update-global-report-type-access', :'update-report-type-access', :'update-default-report-type-access',
|
|
18
18
|
:'update-global-task-access', :'update-task-access', :'update-default-task-access',
|
|
19
|
-
:'update-global-workflow-access', :'update-workflow-access', :'update-default-workflow-access'
|
|
19
|
+
:'update-global-workflow-access', :'update-workflow-access', :'update-default-workflow-access',
|
|
20
|
+
:'update-cluster-type-access', :'update-default-cluster-type-access'
|
|
20
21
|
set_subcommands_hidden(
|
|
21
22
|
subcommands.keys.select{|c|
|
|
22
23
|
c.include?('update-global')
|
|
@@ -47,7 +48,7 @@ class Morpheus::Cli::Roles
|
|
|
47
48
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
48
49
|
opts.banner = subcommand_usage("[search phrase]")
|
|
49
50
|
opts.on( '--tenant TENANT', "Tenant Filter for list of Roles." ) do |val|
|
|
50
|
-
options[:
|
|
51
|
+
options[:account] = val
|
|
51
52
|
end
|
|
52
53
|
build_standard_list_options(opts, options)
|
|
53
54
|
opts.footer = "List roles."
|
|
@@ -67,9 +68,6 @@ class Morpheus::Cli::Roles
|
|
|
67
68
|
return 0, nil
|
|
68
69
|
end
|
|
69
70
|
load_whoami()
|
|
70
|
-
if options[:tenant]
|
|
71
|
-
params[:tenant] = options[:tenant]
|
|
72
|
-
end
|
|
73
71
|
json_response = @roles_interface.list(account_id, params)
|
|
74
72
|
|
|
75
73
|
render_response(json_response, options, "roles") do
|
|
@@ -136,6 +134,9 @@ class Morpheus::Cli::Roles
|
|
|
136
134
|
opts.on(nil,'--task-access', "Display Task Access") do
|
|
137
135
|
options[:include_task_access] = true
|
|
138
136
|
end
|
|
137
|
+
opts.on(nil,'--cluster-type-access', "Display Cluster Type Access") do
|
|
138
|
+
options[:include_cluster_type_access] = true
|
|
139
|
+
end
|
|
139
140
|
opts.on('-a','--all', "Display All Access Lists") do
|
|
140
141
|
options[:include_all_access] = true
|
|
141
142
|
end
|
|
@@ -143,9 +144,7 @@ class Morpheus::Cli::Roles
|
|
|
143
144
|
options[:include_default_access] = true
|
|
144
145
|
end
|
|
145
146
|
opts.on('--account-id ID', String, "Clarify Owner of Role") do |val|
|
|
146
|
-
|
|
147
|
-
options[:account_id] = val.to_s
|
|
148
|
-
end
|
|
147
|
+
options[:account_id] = val.to_s
|
|
149
148
|
end
|
|
150
149
|
build_standard_get_options(opts, options)
|
|
151
150
|
opts.footer = <<-EOT
|
|
@@ -246,6 +245,7 @@ EOT
|
|
|
246
245
|
"VDI Pools" => lambda {|it| get_access_string(it['globalVdiPoolAccess']) },
|
|
247
246
|
"Workflows" => lambda {|it| get_access_string(it['globalTaskSetAccess']) },
|
|
248
247
|
"Tasks" => lambda {|it| get_access_string(it['globalTaskAccess']) },
|
|
248
|
+
"Cluster Types" => lambda {|it| get_access_string(it['globalClusterTypeAccess']) },
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
if role['roleType'].to_s.downcase == 'account'
|
|
@@ -449,7 +449,7 @@ EOT
|
|
|
449
449
|
workflow_permissions = role['taskSets'] ? role['taskSets'] : (json_response['taskSetPermissions'] || [])
|
|
450
450
|
print cyan
|
|
451
451
|
if options[:include_workflow_access] || options[:include_all_access]
|
|
452
|
-
print_h2 "Workflow", options
|
|
452
|
+
print_h2 "Workflow Access", options
|
|
453
453
|
rows = workflow_permissions.collect do |it|
|
|
454
454
|
{
|
|
455
455
|
name: it['name'],
|
|
@@ -461,9 +461,30 @@ EOT
|
|
|
461
461
|
end
|
|
462
462
|
print as_pretty_table(rows, [:name, :access], options)
|
|
463
463
|
elsif workflow_permissions.find {|it| it['access'] && it['access'] != 'default'}
|
|
464
|
-
print_h2 "Workflow", options
|
|
464
|
+
print_h2 "Workflow Access", options
|
|
465
465
|
print cyan,"Use --workflow-access to list custom access","\n"
|
|
466
466
|
end
|
|
467
|
+
|
|
468
|
+
cluster_type_global_access = json_response['globalClusterTypeAccess']
|
|
469
|
+
cluster_type_permissions = role['clusterTypes'] ? role['clusterTypes'] : (json_response['clusterTypePermissions'] || [])
|
|
470
|
+
print cyan
|
|
471
|
+
if options[:include_cluster_type_access] || options[:include_all_access]
|
|
472
|
+
print_h2 "Cluster Type Access", options
|
|
473
|
+
rows = cluster_type_permissions.collect do |it|
|
|
474
|
+
{
|
|
475
|
+
name: it['name'],
|
|
476
|
+
access: format_access_string(it['access'], ["none","full"]),
|
|
477
|
+
}
|
|
478
|
+
end
|
|
479
|
+
if !options[:include_default_access]
|
|
480
|
+
rows = rows.select {|row| row[:access] && row[:access] != 'default '}
|
|
481
|
+
end
|
|
482
|
+
print as_pretty_table(rows, [:name, :access], options)
|
|
483
|
+
elsif cluster_type_permissions.find {|it| it['access'] && it['access'] != 'default'}
|
|
484
|
+
print_h2 "Cluster Type Access", options
|
|
485
|
+
print cyan,"Use --cluster-type-access to list custom access","\n"
|
|
486
|
+
end
|
|
487
|
+
|
|
467
488
|
print reset,"\n"
|
|
468
489
|
return 0, nil
|
|
469
490
|
end
|
|
@@ -471,7 +492,7 @@ EOT
|
|
|
471
492
|
|
|
472
493
|
def list_permissions(args)
|
|
473
494
|
options = {}
|
|
474
|
-
available_categories = ['feature', 'group', 'cloud', 'instance-type', 'blueprint', 'report-type', 'persona', 'catalog-item-type', 'vdi-pool', 'workflow', 'task']
|
|
495
|
+
available_categories = ['feature', 'group', 'cloud', 'instance-type', 'blueprint', 'report-type', 'persona', 'catalog-item-type', 'vdi-pool', 'workflow', 'task', 'cluster-type']
|
|
475
496
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
476
497
|
opts.banner = subcommand_usage("[role] [category]")
|
|
477
498
|
build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
|
@@ -585,7 +606,7 @@ EOT
|
|
|
585
606
|
opts.banner = subcommand_usage("[name] [options]")
|
|
586
607
|
build_option_type_options(opts, options, add_role_option_types)
|
|
587
608
|
build_role_access_options(opts, options, params)
|
|
588
|
-
opts.on('--owner ID', String, "Set the owner/tenant/account for the role by account id.
|
|
609
|
+
opts.on('--owner ID', String, "Set the owner/tenant/account for the role by account id. This option requires the admin permission to manage tenants." ) do |val|
|
|
589
610
|
params['owner'] = val
|
|
590
611
|
end
|
|
591
612
|
opts.on(nil, '--include-default-access', "Include default access levels in the response (returns all available resources)") do
|
|
@@ -631,24 +652,19 @@ EOT
|
|
|
631
652
|
params['authority'] = v_prompt['authority']
|
|
632
653
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2}], options[:options])
|
|
633
654
|
params['description'] = v_prompt['description']
|
|
634
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'landingUrl', 'fieldLabel' => '
|
|
655
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'landingUrl', 'fieldLabel' => 'Landing URL', 'type' => 'text', 'displayOrder' => 3, 'description' => 'An optional override for the default landing page after login for a user.'}], options[:options])
|
|
635
656
|
params['landingUrl'] = v_prompt['landingUrl']
|
|
636
657
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
else
|
|
641
|
-
print_red_alert "You do not have the necessary authority to use owner option"
|
|
642
|
-
return
|
|
643
|
-
end
|
|
644
|
-
elsif @is_master_account && has_complete_access
|
|
658
|
+
can_manage_accounts = @user_permissions.find { |it| it['code'] == 'admin-accounts' && it['access'] == 'full'}
|
|
659
|
+
|
|
660
|
+
if can_manage_accounts
|
|
645
661
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'owner', 'fieldLabel' => 'Owner', 'type' => 'select', 'selectOptions' => role_owner_options, 'defaultValue' => current_account['id'], 'displayOrder' => 3}], options[:options])
|
|
646
662
|
params['owner'] = v_prompt['owner']
|
|
647
663
|
else
|
|
648
664
|
params['owner'] = current_account['id']
|
|
649
665
|
end
|
|
650
666
|
|
|
651
|
-
if
|
|
667
|
+
if can_manage_accounts
|
|
652
668
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'roleType', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => role_type_options, 'defaultValue' => 'user', 'displayOrder' => 4}], options[:options])
|
|
653
669
|
params['roleType'] = v_prompt['roleType']
|
|
654
670
|
else
|
|
@@ -661,6 +677,12 @@ EOT
|
|
|
661
677
|
if options[:group_permissions] && params['roleType'] == 'account'
|
|
662
678
|
raise_command_error "The --groups option is only available for account roles, not user roles"
|
|
663
679
|
end
|
|
680
|
+
if params['globalZoneAccess'] && params['roleType'] == 'user'
|
|
681
|
+
raise_command_error "The --default-cloud-access option is only available for account roles, not user roles"
|
|
682
|
+
end
|
|
683
|
+
if params['globalSiteAccess'] && params['roleType'] == 'account'
|
|
684
|
+
raise_command_error "The --default-group-access option is only available for user roles, not account roles"
|
|
685
|
+
end
|
|
664
686
|
|
|
665
687
|
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'select', 'selectOptions' => base_role_options(params), 'displayOrder' => 5}], options[:options])
|
|
666
688
|
if v_prompt['baseRole'].to_s != ''
|
|
@@ -779,9 +801,9 @@ EOT
|
|
|
779
801
|
# merge -O options into normally parsed options
|
|
780
802
|
params.deep_merge!(passed_options)
|
|
781
803
|
prompt_option_types = update_role_option_types()
|
|
782
|
-
if
|
|
783
|
-
|
|
784
|
-
end
|
|
804
|
+
# if !has_complete_access
|
|
805
|
+
# prompt_option_types = prompt_option_types.reject {|it| ['roleType', 'multitenant','multitenantLocked'].include?(it['fieldName']) }
|
|
806
|
+
# end
|
|
785
807
|
if role['roleType'] != 'user'
|
|
786
808
|
prompt_option_types = prompt_option_types.reject {|it| ['multitenant','multitenantLocked'].include?(it['fieldName']) }
|
|
787
809
|
end
|
|
@@ -792,6 +814,12 @@ EOT
|
|
|
792
814
|
if options[:group_permissions] && role['roleType'] == 'account'
|
|
793
815
|
raise_command_error "The --groups option is only available for account roles, not user roles"
|
|
794
816
|
end
|
|
817
|
+
if params['globalZoneAccess'] && role['roleType'] == 'user'
|
|
818
|
+
raise_command_error "The --default-cloud-access option is only available for account roles, not user roles"
|
|
819
|
+
end
|
|
820
|
+
if params['globalSiteAccess'] && role['roleType'] == 'account'
|
|
821
|
+
raise_command_error "The --default-group-access option is only available for user roles, not account roles"
|
|
822
|
+
end
|
|
795
823
|
# bulk role permissions
|
|
796
824
|
parse_role_access_options(options, params)
|
|
797
825
|
|
|
@@ -872,6 +900,109 @@ EOT
|
|
|
872
900
|
end
|
|
873
901
|
end
|
|
874
902
|
|
|
903
|
+
def validate(args)
|
|
904
|
+
options = {}
|
|
905
|
+
params = {}
|
|
906
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
907
|
+
opts.banner = subcommand_usage("[role] [options]")
|
|
908
|
+
build_option_type_options(opts, options, add_role_option_types)
|
|
909
|
+
build_role_access_options(opts, options, params)
|
|
910
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
|
|
911
|
+
opts.footer = <<-EOT
|
|
912
|
+
Validate role permissions without creating or updating a role.
|
|
913
|
+
[role] is optional. This is the name (authority) or id of a role.
|
|
914
|
+
This is useful for testing permission configurations before applying them.
|
|
915
|
+
All the role permissions and access values can be validated.
|
|
916
|
+
Use --feature-access "CODE=ACCESS,CODE=ACCESS" to validate access levels for specific feature permissions.
|
|
917
|
+
Example: morpheus roles validate --authority "Test Role" --feature-access "admin=full,activity=read"
|
|
918
|
+
Example: morpheus roles validate "Existing Role" --feature-access "activity=full"
|
|
919
|
+
EOT
|
|
920
|
+
end
|
|
921
|
+
optparse.parse!(args)
|
|
922
|
+
|
|
923
|
+
# allow 0-1 arguments
|
|
924
|
+
verify_args!(args:args, optparse:optparse, max:1)
|
|
925
|
+
|
|
926
|
+
connect(options)
|
|
927
|
+
begin
|
|
928
|
+
account = find_account_from_options(options)
|
|
929
|
+
account_id = account ? account['id'] : nil
|
|
930
|
+
|
|
931
|
+
# load existing role if arg passed
|
|
932
|
+
role = nil
|
|
933
|
+
if args[0]
|
|
934
|
+
role = find_role_by_name_or_id(account_id, args[0])
|
|
935
|
+
exit 1 if role.nil?
|
|
936
|
+
end
|
|
937
|
+
|
|
938
|
+
passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
|
|
939
|
+
payload = nil
|
|
940
|
+
|
|
941
|
+
if options[:payload]
|
|
942
|
+
payload = options[:payload]
|
|
943
|
+
payload.deep_merge!({'role' => passed_options}) unless passed_options.empty?
|
|
944
|
+
else
|
|
945
|
+
# merge -O options into normally parsed options
|
|
946
|
+
params.deep_merge!(passed_options)
|
|
947
|
+
|
|
948
|
+
# Parse role access options
|
|
949
|
+
parse_role_access_options(options, params)
|
|
950
|
+
|
|
951
|
+
# Validate role type constraints
|
|
952
|
+
role_type = role ? role['roleType'] : params['roleType']
|
|
953
|
+
if role_type
|
|
954
|
+
if params['globalZoneAccess'] && role_type == 'user'
|
|
955
|
+
raise_command_error "The --default-cloud-access option is only available for account roles, not user roles"
|
|
956
|
+
end
|
|
957
|
+
if params['globalSiteAccess'] && role_type == 'account'
|
|
958
|
+
raise_command_error "The --default-group-access option is only available for user roles, not account roles"
|
|
959
|
+
end
|
|
960
|
+
end
|
|
961
|
+
|
|
962
|
+
if params.empty? && passed_options.empty? && role.nil?
|
|
963
|
+
raise_command_error "Specify at least one role configuration option to validate.\n#{optparse}"
|
|
964
|
+
end
|
|
965
|
+
|
|
966
|
+
payload = {"role" => params}
|
|
967
|
+
end
|
|
968
|
+
|
|
969
|
+
if role
|
|
970
|
+
payload['role']['id'] = role['id']
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
query_params = parse_query_options(options)
|
|
974
|
+
@roles_interface.setopts(options)
|
|
975
|
+
|
|
976
|
+
if options[:dry_run]
|
|
977
|
+
print_dry_run @roles_interface.dry.validate(account_id, payload, query_params)
|
|
978
|
+
return 0, nil
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
json_response = @roles_interface.validate(account_id, payload, query_params)
|
|
982
|
+
|
|
983
|
+
render_response(json_response, options) do
|
|
984
|
+
if json_response['success'] && json_response['valid']
|
|
985
|
+
print_green_success json_response['msg'] || "Role permissions are valid"
|
|
986
|
+
else
|
|
987
|
+
print_red_alert "Validation failed: #{json_response['msg'] || 'Invalid role permissions'}"
|
|
988
|
+
if json_response['errors'] && !json_response['errors'].empty?
|
|
989
|
+
print_h2 "Validation Errors", options
|
|
990
|
+
json_response['errors'].each do |key, msg|
|
|
991
|
+
print red, " #{key}: #{msg}", reset, "\n"
|
|
992
|
+
end
|
|
993
|
+
end
|
|
994
|
+
end
|
|
995
|
+
end
|
|
996
|
+
|
|
997
|
+
# Return exit code based on validation result
|
|
998
|
+
return json_response['success'] && json_response['valid'] ? 0 : 1
|
|
999
|
+
|
|
1000
|
+
rescue RestClient::Exception => e
|
|
1001
|
+
print_rest_exception(e, options)
|
|
1002
|
+
return 1
|
|
1003
|
+
end
|
|
1004
|
+
end
|
|
1005
|
+
|
|
875
1006
|
def update_feature_access(args)
|
|
876
1007
|
options = {}
|
|
877
1008
|
allowed_access_values = ["full", "full_decrypted", "group", "listfiles", "managerules", "no", "none", "provision", "read", "rolemappings", "user", "view", "yes"]
|
|
@@ -979,6 +1110,10 @@ EOT
|
|
|
979
1110
|
role = find_role_by_name_or_id(account_id, name)
|
|
980
1111
|
exit 1 if role.nil?
|
|
981
1112
|
|
|
1113
|
+
if role['roleType'] == 'account'
|
|
1114
|
+
raise_command_error "The default-group-access command is only available for user roles, not account roles"
|
|
1115
|
+
end
|
|
1116
|
+
|
|
982
1117
|
params = {permissionCode: 'ComputeSite', access: access_value}
|
|
983
1118
|
@roles_interface.setopts(options)
|
|
984
1119
|
if options[:dry_run]
|
|
@@ -1132,6 +1267,10 @@ EOT
|
|
|
1132
1267
|
role = find_role_by_name_or_id(account_id, name)
|
|
1133
1268
|
exit 1 if role.nil?
|
|
1134
1269
|
|
|
1270
|
+
if role['roleType'] == 'user'
|
|
1271
|
+
raise_command_error "The default-cloud-access command is only available for account roles, not user roles"
|
|
1272
|
+
end
|
|
1273
|
+
|
|
1135
1274
|
params = {permissionCode: 'ComputeZone', access: access_value}
|
|
1136
1275
|
@roles_interface.setopts(options)
|
|
1137
1276
|
if options[:dry_run]
|
|
@@ -2475,6 +2614,153 @@ Update default workflow access for a role.
|
|
|
2475
2614
|
end
|
|
2476
2615
|
end
|
|
2477
2616
|
|
|
2617
|
+
def update_default_cluster_type_access(args)
|
|
2618
|
+
options = {}
|
|
2619
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
2620
|
+
opts.banner = subcommand_usage("[role] [access]")
|
|
2621
|
+
build_common_options(opts, options, [:json, :dry_run, :remote])
|
|
2622
|
+
opts.footer = <<-EOT
|
|
2623
|
+
Update default cluster type access for a role.
|
|
2624
|
+
[role] is required. This is the id of a role.
|
|
2625
|
+
[access] is required. This is the access level to assign: full or none.
|
|
2626
|
+
EOT
|
|
2627
|
+
end
|
|
2628
|
+
optparse.parse!(args)
|
|
2629
|
+
verify_args!(args:args, optparse:optparse, count: 2)
|
|
2630
|
+
name = args[0]
|
|
2631
|
+
access_value = args[1].to_s.downcase
|
|
2632
|
+
if !['full', 'none', 'custom'].include?(access_value)
|
|
2633
|
+
raise_command_error("invalid access value: #{args[1]}", args, optparse)
|
|
2634
|
+
end
|
|
2635
|
+
|
|
2636
|
+
connect(options)
|
|
2637
|
+
begin
|
|
2638
|
+
account = find_account_from_options(options)
|
|
2639
|
+
account_id = account ? account['id'] : nil
|
|
2640
|
+
role = find_role_by_name_or_id(account_id, name)
|
|
2641
|
+
exit 1 if role.nil?
|
|
2642
|
+
params = {permissionCode: 'ServerGroupType', access: access_value}
|
|
2643
|
+
@roles_interface.setopts(options)
|
|
2644
|
+
if options[:dry_run]
|
|
2645
|
+
print_dry_run @roles_interface.dry.update_permission(account_id, role['id'], params)
|
|
2646
|
+
return
|
|
2647
|
+
end
|
|
2648
|
+
json_response = @roles_interface.update_permission(account_id, role['id'], params)
|
|
2649
|
+
|
|
2650
|
+
if options[:json]
|
|
2651
|
+
print JSON.pretty_generate(json_response)
|
|
2652
|
+
print "\n"
|
|
2653
|
+
else
|
|
2654
|
+
print_green_success "Role #{role['authority']} default cluster type access updated"
|
|
2655
|
+
end
|
|
2656
|
+
rescue RestClient::Exception => e
|
|
2657
|
+
print_rest_exception(e, options)
|
|
2658
|
+
exit 1
|
|
2659
|
+
end
|
|
2660
|
+
end
|
|
2661
|
+
|
|
2662
|
+
def update_cluster_type_access(args)
|
|
2663
|
+
options = {}
|
|
2664
|
+
cluster_type_id = nil
|
|
2665
|
+
access_value = nil
|
|
2666
|
+
do_all = false
|
|
2667
|
+
allowed_access_values = ['full', 'none', 'default']
|
|
2668
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
2669
|
+
opts.banner = subcommand_usage("[role] [cluster-type] [access]")
|
|
2670
|
+
opts.on( '--cluster-type ID', String, "Cluster Type ID, code or Name" ) do |val|
|
|
2671
|
+
cluster_type_id = val
|
|
2672
|
+
end
|
|
2673
|
+
opts.on( nil, '--all', "Update all cluster types at once." ) do
|
|
2674
|
+
do_all = true
|
|
2675
|
+
end
|
|
2676
|
+
opts.on( '--access VALUE', String, "Access value [#{allowed_access_values.join('|')}]" ) do |val|
|
|
2677
|
+
access_value = val
|
|
2678
|
+
end
|
|
2679
|
+
build_common_options(opts, options, [:json, :dry_run, :remote])
|
|
2680
|
+
opts.footer = "Update role access for a cluster type or all cluster types.\n" +
|
|
2681
|
+
"[role] is required. This is the name or id of a role.\n" +
|
|
2682
|
+
"--cluster-type or --all is required. This is the name, code or id of a cluster type.\n" +
|
|
2683
|
+
"--access is required. This is the new access value: #{ored_list(allowed_access_values)}"
|
|
2684
|
+
end
|
|
2685
|
+
optparse.parse!(args)
|
|
2686
|
+
|
|
2687
|
+
name = args[0]
|
|
2688
|
+
if do_all
|
|
2689
|
+
verify_args!(args:args, optparse:optparse, min:1, max:2)
|
|
2690
|
+
access_value = args[1] if args[1]
|
|
2691
|
+
else
|
|
2692
|
+
verify_args!(args:args, optparse:optparse, min:1, max:3)
|
|
2693
|
+
cluster_type_id = args[1] if args[1]
|
|
2694
|
+
access_value = args[2] if args[2]
|
|
2695
|
+
end
|
|
2696
|
+
if !cluster_type_id && !do_all
|
|
2697
|
+
raise_command_error("missing required argument: [cluster-type] or --all", args, optparse)
|
|
2698
|
+
end
|
|
2699
|
+
if !access_value
|
|
2700
|
+
raise_command_error("missing required argument: [access]", args, optparse)
|
|
2701
|
+
end
|
|
2702
|
+
access_value = access_value.to_s.downcase
|
|
2703
|
+
if !allowed_access_values.include?(access_value)
|
|
2704
|
+
raise_command_error("invalid access value: #{access_value}", args, optparse)
|
|
2705
|
+
puts optparse
|
|
2706
|
+
return 1
|
|
2707
|
+
end
|
|
2708
|
+
|
|
2709
|
+
connect(options)
|
|
2710
|
+
begin
|
|
2711
|
+
account = find_account_from_options(options)
|
|
2712
|
+
account_id = account ? account['id'] : nil
|
|
2713
|
+
role = find_role_by_name_or_id(account_id, name)
|
|
2714
|
+
return 1 if role.nil?
|
|
2715
|
+
|
|
2716
|
+
role_json = @roles_interface.get(account_id, role['id'], {'includeDefaultAccess' => true})
|
|
2717
|
+
cluster_type_permissions = role_json['clusterTypePermissions'] || role_json['clusterTypes'] || []
|
|
2718
|
+
|
|
2719
|
+
# hacky, but support name or code lookup via the list returned in the show payload
|
|
2720
|
+
cluster_type = nil
|
|
2721
|
+
if !do_all
|
|
2722
|
+
if cluster_type_id.to_s =~ /\A\d{1,}\Z/
|
|
2723
|
+
cluster_type = cluster_type_permissions.find {|b| b['id'] == cluster_type_id.to_i }
|
|
2724
|
+
else
|
|
2725
|
+
cluster_type = cluster_type_permissions.find {|b| b['name'] == cluster_type_id }
|
|
2726
|
+
end
|
|
2727
|
+
if cluster_type.nil?
|
|
2728
|
+
print_red_alert "Cluster Type not found: '#{cluster_type_id}'"
|
|
2729
|
+
return 1
|
|
2730
|
+
end
|
|
2731
|
+
end
|
|
2732
|
+
|
|
2733
|
+
params = {}
|
|
2734
|
+
if do_all
|
|
2735
|
+
params['allClusterTypes'] = true
|
|
2736
|
+
else
|
|
2737
|
+
params['clusterTypeId'] = cluster_type['id']
|
|
2738
|
+
end
|
|
2739
|
+
params['access'] = access_value == 'default' ? nil : access_value
|
|
2740
|
+
@roles_interface.setopts(options)
|
|
2741
|
+
if options[:dry_run]
|
|
2742
|
+
print_dry_run @roles_interface.dry.update_cluster_type(account_id, role['id'], params)
|
|
2743
|
+
return
|
|
2744
|
+
end
|
|
2745
|
+
json_response = @roles_interface.update_cluster_type(account_id, role['id'], params)
|
|
2746
|
+
|
|
2747
|
+
if options[:json]
|
|
2748
|
+
print JSON.pretty_generate(json_response)
|
|
2749
|
+
print "\n"
|
|
2750
|
+
else
|
|
2751
|
+
if do_all
|
|
2752
|
+
print_green_success "Role #{role['authority']} access updated for all cluster types"
|
|
2753
|
+
else
|
|
2754
|
+
print_green_success "Role #{role['authority']} access updated for cluster type #{cluster_type['name']}"
|
|
2755
|
+
end
|
|
2756
|
+
end
|
|
2757
|
+
return 0
|
|
2758
|
+
rescue RestClient::Exception => e
|
|
2759
|
+
print_rest_exception(e, options)
|
|
2760
|
+
exit 1
|
|
2761
|
+
end
|
|
2762
|
+
end
|
|
2763
|
+
|
|
2478
2764
|
private
|
|
2479
2765
|
|
|
2480
2766
|
def add_role_option_types
|
|
@@ -2511,21 +2797,10 @@ Update default workflow access for a role.
|
|
|
2511
2797
|
end
|
|
2512
2798
|
|
|
2513
2799
|
def base_role_options(role_payload)
|
|
2514
|
-
params = {"tenantId" => role_payload['owner'], "
|
|
2800
|
+
params = {"tenantId" => role_payload['owner'], "roleType" => role_payload['roleType'] }
|
|
2515
2801
|
@options_interface.options_for_source("copyFromRole", params)['data']
|
|
2516
2802
|
end
|
|
2517
2803
|
|
|
2518
|
-
def has_complete_access
|
|
2519
|
-
has_access = false
|
|
2520
|
-
if @is_master_account
|
|
2521
|
-
admin_accounts = @user_permissions.select { |it| it['code'] == 'admin-accounts' && it['access'] == 'full'}
|
|
2522
|
-
admin_roles = @user_permissions.select { |it| it['code'] == 'admin-roles' && it['access'] == 'full' }
|
|
2523
|
-
if admin_accounts != nil && admin_roles != nil
|
|
2524
|
-
has_access = true
|
|
2525
|
-
end
|
|
2526
|
-
end
|
|
2527
|
-
has_access
|
|
2528
|
-
end
|
|
2529
2804
|
|
|
2530
2805
|
def parse_access_csv(output, val)
|
|
2531
2806
|
output ||= {}
|
|
@@ -2664,6 +2939,13 @@ Update default workflow access for a role.
|
|
|
2664
2939
|
options[:workflow_permissions] ||= {}
|
|
2665
2940
|
parse_access_csv(options[:workflow_permissions], val)
|
|
2666
2941
|
end
|
|
2942
|
+
opts.on('--default-cluster-type-access ACCESS', String, "Set the default cluster type access: [none|full]" ) do |val|
|
|
2943
|
+
params['globalTaskSetAccess'] = val.to_s.downcase
|
|
2944
|
+
end
|
|
2945
|
+
opts.on('--cluster-types CODE=ACCESS', String, "Set cluster type to a custom access by cluster type code. Example: kubernetes-cluster=none,mvm-cluster=full" ) do |val|
|
|
2946
|
+
options[:cluster_type_permissions] ||= {}
|
|
2947
|
+
parse_access_csv(options[:cluster_type_permissions], val)
|
|
2948
|
+
end
|
|
2667
2949
|
opts.on('--reset-permissions', "Reset all feature permission access to none. This can be used in conjunction with --permissions to recreate the feature permission access for the role." ) do
|
|
2668
2950
|
options[:reset_permissions] = true
|
|
2669
2951
|
end
|
|
@@ -2814,6 +3096,19 @@ Update default workflow access for a role.
|
|
|
2814
3096
|
end
|
|
2815
3097
|
params['taskSets'] = perms_array
|
|
2816
3098
|
end
|
|
3099
|
+
if options[:cluster_type_permissions]
|
|
3100
|
+
perms_array = []
|
|
3101
|
+
options[:cluster_type_permissions].each do |k,v|
|
|
3102
|
+
cluster_type_code = k
|
|
3103
|
+
access_value = v.to_s.empty? ? "none" : v.to_s
|
|
3104
|
+
if cluster_type_code =~ /\A\d{1,}\Z/
|
|
3105
|
+
perms_array << {"id" => cluster_type_code.to_i, "access" => access_value}
|
|
3106
|
+
else
|
|
3107
|
+
perms_array << {"code" => cluster_type_code, "access" => access_value}
|
|
3108
|
+
end
|
|
3109
|
+
end
|
|
3110
|
+
params['clusterTypes'] = perms_array
|
|
3111
|
+
end
|
|
2817
3112
|
if options[:reset_permissions]
|
|
2818
3113
|
params["resetPermissions"] = true
|
|
2819
3114
|
end
|
|
@@ -168,6 +168,7 @@ class Morpheus::Cli::ServicePlanCommand
|
|
|
168
168
|
description_cols['Core Count'] = lambda {|it| it['maxCores']}
|
|
169
169
|
description_cols['Custom Cores'] = lambda {|it| format_boolean(it['customCores'])}
|
|
170
170
|
description_cols['Cores Per Socket'] = lambda {|it| it['coresPerSocket']} if provision_type['hasConfigurableCpuSockets'] && service_plan['customCores']
|
|
171
|
+
description_cols['Custom CPU'] = lambda {|it| format_boolean(it['customCpu'])}
|
|
171
172
|
|
|
172
173
|
ranges = (service_plan['config'] ? service_plan['config']['ranges'] : nil) || {}
|
|
173
174
|
|
|
@@ -279,6 +280,9 @@ class Morpheus::Cli::ServicePlanCommand
|
|
|
279
280
|
opts.on('--custom-cores [on|off]', String, "Can be used to enable / disable customizable cores. Default is on") do |val|
|
|
280
281
|
params['customCores'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
|
281
282
|
end
|
|
283
|
+
opts.on('--custom-cpu [on|off]', String, "Can be used to enable / disable customizable CPUs. Default is on") do |val|
|
|
284
|
+
params['customCpu'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
|
285
|
+
end
|
|
282
286
|
opts.on('--custom-storage [on|off]', String, "Can be used to enable / disable customizable storage. Default is on") do |val|
|
|
283
287
|
params['customMaxStorage'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
|
284
288
|
end
|
|
@@ -551,6 +555,9 @@ class Morpheus::Cli::ServicePlanCommand
|
|
|
551
555
|
opts.on('--custom-cores [on|off]', String, "Can be used to enable / disable customizable cores. Default is on") do |val|
|
|
552
556
|
params['customCores'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
|
553
557
|
end
|
|
558
|
+
opts.on('--custom-cpu [on|off]', String, "Can be used to enable / disable customizable CPUs. Default is on") do |val|
|
|
559
|
+
params['customCpu'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
|
560
|
+
end
|
|
554
561
|
opts.on('--custom-storage [on|off]', String, "Can be used to enable / disable customizable storage. Default is on") do |val|
|
|
555
562
|
params['customMaxStorage'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
|
|
556
563
|
end
|
|
@@ -513,8 +513,12 @@ class Morpheus::Cli::StorageProvidersCommand
|
|
|
513
513
|
|
|
514
514
|
def remove(args)
|
|
515
515
|
options = {}
|
|
516
|
+
params = {:removeResources => 'on'}
|
|
516
517
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
|
517
518
|
opts.banner = subcommand_usage("[storage-bucket]")
|
|
519
|
+
opts.on('--remove-resources [on|off]', ['on','off'], "Remove From Server. Default is on.") do |val|
|
|
520
|
+
params[:removeResources] = val.nil? ? 'on' : val
|
|
521
|
+
end
|
|
518
522
|
build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
|
|
519
523
|
opts.footer = "Delete a storage bucket." + "\n" +
|
|
520
524
|
"[storage-bucket] is required. This is the name or id of a storage bucket."
|
|
@@ -537,10 +541,10 @@ class Morpheus::Cli::StorageProvidersCommand
|
|
|
537
541
|
end
|
|
538
542
|
@storage_providers_interface.setopts(options)
|
|
539
543
|
if options[:dry_run]
|
|
540
|
-
print_dry_run @storage_providers_interface.dry.destroy(storage_provider['id'])
|
|
544
|
+
print_dry_run @storage_providers_interface.dry.destroy(storage_provider['id'], params)
|
|
541
545
|
return 0
|
|
542
546
|
end
|
|
543
|
-
json_response = @storage_providers_interface.destroy(storage_provider['id'])
|
|
547
|
+
json_response = @storage_providers_interface.destroy(storage_provider['id'], params)
|
|
544
548
|
if options[:json]
|
|
545
549
|
print JSON.pretty_generate(json_response)
|
|
546
550
|
print "\n"
|