morpheus-cli 5.4.5 → 5.5.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/lib/morpheus/api/api_client.rb +4 -0
- data/lib/morpheus/api/clusters_interface.rb +12 -0
- data/lib/morpheus/api/network_pool_servers_interface.rb +7 -0
- data/lib/morpheus/api/prices_interface.rb +6 -0
- data/lib/morpheus/api/scale_thresholds_interface.rb +9 -0
- data/lib/morpheus/cli/cli_command.rb +55 -23
- data/lib/morpheus/cli/commands/apps.rb +2 -2
- data/lib/morpheus/cli/commands/cloud_resource_pools_command.rb +33 -2
- data/lib/morpheus/cli/commands/clouds.rb +61 -31
- data/lib/morpheus/cli/commands/clusters.rb +66 -5
- data/lib/morpheus/cli/commands/cypher_command.rb +22 -23
- data/lib/morpheus/cli/commands/hosts.rb +5 -1
- data/lib/morpheus/cli/commands/instances.rb +12 -12
- data/lib/morpheus/cli/commands/integrations_command.rb +1 -1
- data/lib/morpheus/cli/commands/invoices_command.rb +8 -1
- data/lib/morpheus/cli/commands/jobs_command.rb +45 -225
- data/lib/morpheus/cli/commands/library_container_types_command.rb +52 -3
- data/lib/morpheus/cli/commands/library_option_lists_command.rb +18 -8
- data/lib/morpheus/cli/commands/library_option_types_command.rb +56 -62
- data/lib/morpheus/cli/commands/load_balancers.rb +11 -19
- data/lib/morpheus/cli/commands/network_pool_servers_command.rb +5 -2
- data/lib/morpheus/cli/commands/prices_command.rb +25 -11
- data/lib/morpheus/cli/commands/roles.rb +475 -70
- data/lib/morpheus/cli/commands/scale_thresholds.rb +103 -0
- data/lib/morpheus/cli/commands/tasks.rb +64 -22
- data/lib/morpheus/cli/commands/user_sources_command.rb +107 -39
- data/lib/morpheus/cli/commands/users.rb +10 -10
- data/lib/morpheus/cli/commands/view.rb +1 -0
- data/lib/morpheus/cli/commands/workflows.rb +21 -14
- data/lib/morpheus/cli/error_handler.rb +13 -4
- data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/execution_request_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/infrastructure_helper.rb +3 -3
- data/lib/morpheus/cli/mixins/jobs_helper.rb +173 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +120 -38
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +2 -3
- data/lib/morpheus/cli/mixins/rest_command.rb +41 -14
- data/lib/morpheus/cli/option_types.rb +69 -13
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/logging.rb +6 -8
- data/lib/morpheus/routes.rb +3 -5
- metadata +6 -4
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
|
3
|
+
class Morpheus::Cli::ScaleThresholds
|
4
|
+
include Morpheus::Cli::CliCommand
|
5
|
+
include Morpheus::Cli::RestCommand
|
6
|
+
|
7
|
+
set_command_name :'scale-thresholds'
|
8
|
+
set_command_description "View and manage scale thresholds."
|
9
|
+
register_subcommands :list, :get, :add, :update, :remove
|
10
|
+
|
11
|
+
# using convention matching /scale-thresholds now
|
12
|
+
|
13
|
+
# RestCommand settings
|
14
|
+
#set_rest_interface_name :thresholds
|
15
|
+
# register_interfaces :thresholds
|
16
|
+
# set_rest_interface_name :thresholds
|
17
|
+
|
18
|
+
# set_rest_name :threshold
|
19
|
+
# set_rest_label "Scale Threshold"
|
20
|
+
# set_rest_label_plural "Scale Thresholds"
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
# def threshold_object_key
|
25
|
+
# 'scaleThreshold'
|
26
|
+
# end
|
27
|
+
|
28
|
+
# def threshold_list_key
|
29
|
+
# 'scaleThresholds'
|
30
|
+
# end
|
31
|
+
|
32
|
+
# def threshold_label
|
33
|
+
# 'Scale Threshold'
|
34
|
+
# end
|
35
|
+
|
36
|
+
# def threshold_label_plural
|
37
|
+
# 'Scale Thresholds'
|
38
|
+
# end
|
39
|
+
|
40
|
+
def scale_threshold_list_column_definitions(options)
|
41
|
+
{
|
42
|
+
"ID" => 'id',
|
43
|
+
"Name" => 'name',
|
44
|
+
"CPU" => lambda {|it| it['cpuEnabled'] ? "✔" : '' },
|
45
|
+
"Memory" => lambda {|it| it['memoryEnabled'] ? "✔" : '' },
|
46
|
+
"Disk" => lambda {|it| it['diskEnabled'] ? "✔" : '' },
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def scale_threshold_column_definitions(options)
|
51
|
+
{
|
52
|
+
"ID" => 'id',
|
53
|
+
"Name" => 'name',
|
54
|
+
"Auto Upscale" => lambda {|it| format_boolean(it['autoUp']) },
|
55
|
+
"Auto Downscale" => lambda {|it| format_boolean(it['autoDown']) },
|
56
|
+
"Min Count" => lambda {|it| it['minCount'] },
|
57
|
+
"Max Count" => lambda {|it| it['maxCount'] },
|
58
|
+
"Enable CPU Threshold" => lambda {|it| format_boolean(it['cpuEnabled']) },
|
59
|
+
"Min CPU" => lambda {|it| it['minCpu'] },
|
60
|
+
"Max CPU" => lambda {|it| it['maxCpu'] },
|
61
|
+
"Enable Memory Threshold" => lambda {|it| format_boolean(it['memoryEnabled']) },
|
62
|
+
"Min Memory" => lambda {|it| it['minMemory'] },
|
63
|
+
"Max Memory" => lambda {|it| it['maxMemory'] },
|
64
|
+
"Enable Disk Threshold" => lambda {|it| format_boolean(it['diskEnabled']) },
|
65
|
+
"Min Disk" => lambda {|it| it['minDisk'] },
|
66
|
+
"Max Disk" => lambda {|it| it['maxDisk'] },
|
67
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
68
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def add_scale_threshold_option_types()
|
73
|
+
[
|
74
|
+
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 10},
|
75
|
+
{'fieldName' => 'autoUp', 'fieldLabel' => 'Auto Upscale', 'type' => 'checkbox', 'required' => true, 'defaultValue' => false, 'displayOrder' => 30},
|
76
|
+
{'fieldName' => 'autoDown', 'fieldLabel' => 'Auto Downscale', 'type' => 'checkbox', 'required' => true, 'defaultValue' => false, 'displayOrder' => 40},
|
77
|
+
{'fieldName' => 'minCount', 'fieldLabel' => 'Min Count', 'type' => 'number', 'required' => true, 'defaultValue' => 1, 'displayOrder' => 41},
|
78
|
+
{'fieldName' => 'maxCount', 'fieldLabel' => 'Max Count', 'type' => 'number', 'required' => true, 'defaultValue' => 2, 'displayOrder' => 42},
|
79
|
+
{'fieldName' => 'cpuEnabled', 'fieldLabel' => 'Enable CPU Threshold', 'type' => 'checkbox', 'required' => false, 'defaultValue' => false, 'displayOrder' => 50},
|
80
|
+
{'fieldName' => 'minCpu', 'fieldLabel' => 'Min CPU', 'type' => 'number', 'required' => false, 'defaultValue' => 0, 'displayOrder' => 60},
|
81
|
+
{'fieldName' => 'maxCpu', 'fieldLabel' => 'Max CPU', 'type' => 'number', 'required' => false, 'defaultValue' => 0, 'displayOrder' => 70},
|
82
|
+
{'fieldName' => 'memoryEnabled', 'fieldLabel' => 'Enable Memory Threshold', 'type' => 'checkbox', 'required' => false, 'defaultValue' => false, 'displayOrder' => 80},
|
83
|
+
{'fieldName' => 'minMemory', 'fieldLabel' => 'Min Memory', 'type' => 'number', 'required' => false, 'defaultValue' => 0, 'displayOrder' => 90},
|
84
|
+
{'fieldName' => 'maxMemory', 'fieldLabel' => 'Max Memory', 'type' => 'number', 'required' => false, 'defaultValue' => 0, 'displayOrder' => 100},
|
85
|
+
{'fieldName' => 'diskEnabled', 'fieldLabel' => 'Enable Disk Threshold', 'type' => 'checkbox', 'required' => false, 'defaultValue' => false, 'displayOrder' => 110},
|
86
|
+
{'fieldName' => 'minDisk', 'fieldLabel' => 'Min Disk', 'type' => 'number', 'required' => false, 'defaultValue' => 0, 'displayOrder' => 120},
|
87
|
+
{'fieldName' => 'maxDisk', 'fieldLabel' => 'Max Disk', 'type' => 'number', 'required' => false, 'defaultValue' => 0, 'displayOrder' => 130},
|
88
|
+
]
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_scale_threshold_advanced_option_types()
|
92
|
+
[]
|
93
|
+
end
|
94
|
+
|
95
|
+
def update_scale_threshold_option_types()
|
96
|
+
add_scale_threshold_option_types.collect {|it| it.delete('required'); it.delete('defaultValue'); it.delete('dependsOnCode'); it }
|
97
|
+
end
|
98
|
+
|
99
|
+
def update_scale_threshold_advanced_option_types()
|
100
|
+
add_scale_threshold_advanced_option_types.collect {|it| it.delete('required'); it.delete('defaultValue'); it.delete('dependsOnCode'); it }
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -2,6 +2,7 @@ require 'morpheus/cli/cli_command'
|
|
2
2
|
|
3
3
|
class Morpheus::Cli::Tasks
|
4
4
|
include Morpheus::Cli::CliCommand
|
5
|
+
include Morpheus::Cli::JobsHelper
|
5
6
|
|
6
7
|
register_subcommands :list, :get, :add, :update, :remove, :execute
|
7
8
|
register_subcommands :'list-types' => :list_task_types
|
@@ -297,6 +298,9 @@ class Morpheus::Cli::Tasks
|
|
297
298
|
opts.on('--execute-target VALUE', String, "Execute Target" ) do |val|
|
298
299
|
options[:options]['executeTarget'] = val
|
299
300
|
end
|
301
|
+
opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
|
302
|
+
options[:options]['credential'] = val
|
303
|
+
end
|
300
304
|
opts.on('--target-host VALUE', String, "Target Host" ) do |val|
|
301
305
|
options[:options]['taskOptions'] ||= {}
|
302
306
|
options[:options]['taskOptions']['host'] = val
|
@@ -313,6 +317,10 @@ class Morpheus::Cli::Tasks
|
|
313
317
|
options[:options]['taskOptions'] ||= {}
|
314
318
|
options[:options]['taskOptions']['password'] = val
|
315
319
|
end
|
320
|
+
opts.on('--target-ssh-key VALUE', String, "Target SSH Key" ) do |val|
|
321
|
+
options[:options]['taskOptions'] ||= {}
|
322
|
+
options[:options]['taskOptions']['sshKey'] = val
|
323
|
+
end
|
316
324
|
opts.on('--git-repo VALUE', String, "Git Repo ID" ) do |val|
|
317
325
|
options[:options]['taskOptions'] ||= {}
|
318
326
|
options[:options]['taskOptions']['localScriptGitId'] = val
|
@@ -521,17 +529,38 @@ class Morpheus::Cli::Tasks
|
|
521
529
|
payload['task']['taskOptions'] ||= {}
|
522
530
|
payload['task']['taskOptions']['port'] = v_prompt['taskOptions']['port']
|
523
531
|
end
|
524
|
-
#
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
532
|
+
# Credentials
|
533
|
+
credential_code = "credential"
|
534
|
+
credential_option_type = {'code' => credential_code, 'fieldName' => credential_code, 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Enter an existing credential ID or choose "local"', 'defaultValue' => "local", 'required' => true}
|
535
|
+
supported_credential_types = ['username-keypair', 'username-password', 'username-password-keypair'].compact.flatten.join(",").split(",").collect {|it| it.strip }
|
536
|
+
credential_params = {"new" => false, "credentialTypes" => supported_credential_types}
|
537
|
+
credential_value = Morpheus::Cli::OptionTypes.select_prompt(credential_option_type, @api_client, credential_params, options[:no_prompt], options[:options][credential_code])
|
538
|
+
if !credential_value.to_s.empty?
|
539
|
+
if credential_value == "local"
|
540
|
+
payload['task'][credential_code] = {"type" => credential_value}
|
541
|
+
elsif credential_value.to_s =~ /\A\d{1,}\Z/
|
542
|
+
payload['task'][credential_code] = {"id" => credential_value.to_i}
|
543
|
+
end
|
529
544
|
end
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
545
|
+
if credential_value == "local"
|
546
|
+
# Username
|
547
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => 'Username for remote execution'}], options[:options], @api_client)
|
548
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['username'].to_s.empty?
|
549
|
+
payload['task']['taskOptions'] ||= {}
|
550
|
+
payload['task']['taskOptions']['username'] = v_prompt['taskOptions']['username']
|
551
|
+
end
|
552
|
+
# Password
|
553
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => 'Password for remote execution'}], options[:options], @api_client)
|
554
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['password'].to_s.empty?
|
555
|
+
payload['task']['taskOptions'] ||= {}
|
556
|
+
payload['task']['taskOptions']['password'] = v_prompt['taskOptions']['password']
|
557
|
+
end
|
558
|
+
# SSH Key
|
559
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'sshKey', 'fieldLabel' => 'Key', 'type' => 'select', 'optionSource' => 'keyPairs', 'description' => 'SSH Key for remote execution'}], options[:options], @api_client)
|
560
|
+
if v_prompt['taskOptions'] && !v_prompt['taskOptions']['sshKey'].to_s.empty?
|
561
|
+
payload['task']['taskOptions'] ||= {}
|
562
|
+
payload['task']['taskOptions']['sshKey'] = v_prompt['taskOptions']['sshKey']
|
563
|
+
end
|
535
564
|
end
|
536
565
|
end
|
537
566
|
|
@@ -639,6 +668,9 @@ class Morpheus::Cli::Tasks
|
|
639
668
|
opts.on('--execute-target VALUE', String, "Execute Target" ) do |val|
|
640
669
|
options[:options]['executeTarget'] = val
|
641
670
|
end
|
671
|
+
opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
|
672
|
+
options[:options]['credential'] = val
|
673
|
+
end
|
642
674
|
opts.on('--target-host VALUE', String, "Target Host" ) do |val|
|
643
675
|
options[:options]['taskOptions'] ||= {}
|
644
676
|
options[:options]['taskOptions']['host'] = val
|
@@ -655,6 +687,10 @@ class Morpheus::Cli::Tasks
|
|
655
687
|
options[:options]['taskOptions'] ||= {}
|
656
688
|
options[:options]['taskOptions']['password'] = val
|
657
689
|
end
|
690
|
+
opts.on('--target-ssh-key VALUE', String, "Target SSH Key" ) do |val|
|
691
|
+
options[:options]['taskOptions'] ||= {}
|
692
|
+
options[:options]['taskOptions']['sshKey'] = val
|
693
|
+
end
|
658
694
|
opts.on('--git-repo VALUE', String, "Git Repo ID" ) do |val|
|
659
695
|
options[:options]['taskOptions'] ||= {}
|
660
696
|
options[:options]['taskOptions']['localScriptGitId'] = val
|
@@ -795,6 +831,7 @@ class Morpheus::Cli::Tasks
|
|
795
831
|
instances = []
|
796
832
|
server_ids = []
|
797
833
|
servers = []
|
834
|
+
default_refresh_interval = 10
|
798
835
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
799
836
|
opts.banner = subcommand_usage("[task] --instance [instance] [options]")
|
800
837
|
opts.on('--instance INSTANCE', String, "Instance name or id to execute the task on. This option can be passed more than once.") do |val|
|
@@ -829,6 +866,12 @@ class Morpheus::Cli::Tasks
|
|
829
866
|
opts.on('--config [TEXT]', String, "Custom config") do |val|
|
830
867
|
params['customConfig'] = val.to_s
|
831
868
|
end
|
869
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until execution is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
|
870
|
+
options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
|
871
|
+
end
|
872
|
+
opts.on(nil, '--no-refresh', "Do not refresh" ) do
|
873
|
+
options[:no_refresh] = true
|
874
|
+
end
|
832
875
|
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
833
876
|
end
|
834
877
|
optparse.parse!(args)
|
@@ -901,10 +944,7 @@ class Morpheus::Cli::Tasks
|
|
901
944
|
return
|
902
945
|
end
|
903
946
|
json_response = @tasks_interface.run(task['id'], payload)
|
904
|
-
|
905
|
-
puts as_json(json_response, options)
|
906
|
-
return json_response['success'] ? 0 : 1
|
907
|
-
else
|
947
|
+
render_response(json_response, options) do
|
908
948
|
target_desc = nil
|
909
949
|
if instances.size() > 0
|
910
950
|
target_desc = (instances.size() == 1) ? "instance #{instances[0]['name']}" : "#{instances.size()} instances"
|
@@ -916,17 +956,19 @@ class Morpheus::Cli::Tasks
|
|
916
956
|
else
|
917
957
|
print_green_success "Executing task #{task['name']}"
|
918
958
|
end
|
919
|
-
# todo: refresh, use get processId and load process record isntead? err
|
920
959
|
if json_response["jobExecution"] && json_response["jobExecution"]["id"]
|
921
|
-
|
922
|
-
|
923
|
-
|
960
|
+
job_execution_id = json_response["jobExecution"]["id"]
|
961
|
+
if options[:no_refresh]
|
962
|
+
get_args = [json_response["jobExecution"]["id"], "--details"] + (options[:remote] ? ["-r",options[:remote]] : [])
|
963
|
+
Morpheus::Logging::DarkPrinter.puts((['jobs', 'get-execution'] + get_args).join(' ')) if Morpheus::Logging.debug?
|
964
|
+
::Morpheus::Cli::JobsCommand.new.handle(['get-execution'] + get_args)
|
965
|
+
else
|
966
|
+
#Morpheus::Cli::JobsCommand.new.handle(["get-execution", job_execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
|
967
|
+
job_execution_results = wait_for_job_execution(job_execution_id, options.merge({:details => true}))
|
968
|
+
end
|
924
969
|
end
|
925
|
-
return json_response['success'] ? 0 : 1
|
926
970
|
end
|
927
|
-
|
928
|
-
print_rest_exception(e, options)
|
929
|
-
return 1
|
971
|
+
return 0, nil
|
930
972
|
end
|
931
973
|
end
|
932
974
|
|
@@ -261,7 +261,7 @@ EOT
|
|
261
261
|
end
|
262
262
|
end
|
263
263
|
|
264
|
-
opts.on('--default-role ID', String, "Default Role ID") do |val|
|
264
|
+
opts.on('--default-role ID', String, "Default Role ID or Authority") do |val|
|
265
265
|
default_role_id = val
|
266
266
|
end
|
267
267
|
#build_option_type_options(opts, options, add_user_source_option_types())
|
@@ -285,24 +285,62 @@ EOT
|
|
285
285
|
|
286
286
|
|
287
287
|
|
288
|
-
# find the account first, or just prompt for that too please.
|
289
|
-
if !account_id
|
290
|
-
|
291
|
-
|
292
|
-
|
288
|
+
# # find the account first, or just prompt for that too please.
|
289
|
+
# if !account_id
|
290
|
+
# print_error Morpheus::Terminal.angry_prompt
|
291
|
+
# puts_error "missing required argument [account]\n#{optparse}"
|
292
|
+
# return 1
|
293
|
+
# end
|
294
|
+
|
295
|
+
# tenant is optional, it is expected in the url right now instead of in the payload...this sets both
|
296
|
+
account = nil
|
297
|
+
if account_id
|
298
|
+
options[:options]['tenant'] = account_id
|
293
299
|
end
|
294
|
-
|
295
|
-
|
296
|
-
|
300
|
+
account_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tenant', 'fieldLabel' => 'Tenant', 'type' => 'select', 'optionSource' => 'tenants', 'required' => false, 'description' => 'Tenant'}], options[:options], @api_client)
|
301
|
+
if account_id
|
302
|
+
options[:options].delete('tenant')
|
303
|
+
end
|
304
|
+
account_id = account_prompt['tenant']
|
305
|
+
if !account_id.to_s.empty?
|
306
|
+
# reload tenant by id, sure why not..
|
307
|
+
account = find_account_by_name_or_id(account_id)
|
308
|
+
return 1 if account.nil?
|
309
|
+
account_id = account['id']
|
310
|
+
else
|
311
|
+
account_id = nil
|
312
|
+
end
|
313
|
+
|
297
314
|
|
298
315
|
# construct payload
|
299
316
|
payload = {}
|
300
317
|
if options[:payload]
|
301
318
|
payload = options[:payload]
|
302
319
|
payload.deep_merge!({'userSource' => parse_passed_options(options)})
|
320
|
+
|
321
|
+
# JD: should apply options on top of payload, but just do these two for now
|
322
|
+
|
323
|
+
# Tenant
|
324
|
+
if account
|
325
|
+
payload['userSource']['account'] = {'id' => account['id'] }
|
326
|
+
end
|
327
|
+
|
328
|
+
# Name
|
329
|
+
if params['name']
|
330
|
+
payload['userSource']['name'] = params['name']
|
331
|
+
end
|
332
|
+
|
303
333
|
else
|
304
334
|
payload.deep_merge!({'userSource' => parse_passed_options(options)})
|
305
335
|
|
336
|
+
# support old -O options
|
337
|
+
payload['userSource'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
338
|
+
|
339
|
+
# Tenant
|
340
|
+
if account
|
341
|
+
payload['userSource']['account'] = {'id' => account['id'] }
|
342
|
+
end
|
343
|
+
|
306
344
|
# Identity Source Type
|
307
345
|
user_source_types = @user_sources_interface.list_types({userSelectable: true})['userSourceTypes']
|
308
346
|
if user_source_types.empty?
|
@@ -338,16 +376,15 @@ EOT
|
|
338
376
|
payload['userSource'].deep_merge!(v_prompt)
|
339
377
|
|
340
378
|
# Default Account Role
|
341
|
-
#
|
342
|
-
if !default_role_id
|
343
|
-
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'defaultAccountRole', 'fieldName' => 'id', 'type' => 'text', 'fieldLabel' => 'Default Account Role ID', 'required' => true}], options[:options])
|
344
|
-
if v_prompt['defaultAccountRole'] && v_prompt['defaultAccountRole']['id']
|
345
|
-
default_role_id = v_prompt['defaultAccountRole']['id']
|
346
|
-
end
|
347
|
-
end
|
379
|
+
# always prompt for role to lookup id from name
|
348
380
|
if default_role_id
|
349
|
-
|
381
|
+
options[:options]['defaultAccountRole'] = {'id' => default_role_id }
|
382
|
+
end
|
383
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'defaultAccountRole', 'fieldName' => 'id', 'type' => 'select', 'selectOptions' => get_available_role_options(account_id), 'fieldLabel' => 'Default Role', 'required' => false }], options[:options])
|
384
|
+
if v_prompt['defaultAccountRole'] && v_prompt['defaultAccountRole']['id']
|
385
|
+
default_role_id = v_prompt['defaultAccountRole']['id']
|
350
386
|
end
|
387
|
+
payload['userSource']['defaultAccountRole'] = {'id' => default_role_id }
|
351
388
|
|
352
389
|
# Allow Custom Mappings
|
353
390
|
if !params['allowCustomMappings'].nil?
|
@@ -364,9 +401,6 @@ EOT
|
|
364
401
|
if role_mapping_names
|
365
402
|
payload['roleMappingNames'] = role_mapping_names
|
366
403
|
end
|
367
|
-
|
368
|
-
# support old -O options
|
369
|
-
payload['userSource'].deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
370
404
|
|
371
405
|
|
372
406
|
end
|
@@ -378,14 +412,12 @@ EOT
|
|
378
412
|
# do it
|
379
413
|
json_response = @user_sources_interface.create(account_id, payload)
|
380
414
|
# print and return result
|
381
|
-
|
382
|
-
|
383
|
-
|
415
|
+
render_response(json_response, options, 'userSource') do
|
416
|
+
user_source = json_response['userSource']
|
417
|
+
print_green_success "Added Identity Source #{user_source['name']}"
|
418
|
+
_get(user_source['id'], {}, options)
|
384
419
|
end
|
385
|
-
|
386
|
-
print_green_success "Added Identity Source #{user_source['name']}"
|
387
|
-
get([user_source['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
|
388
|
-
return 0
|
420
|
+
return 0, nil
|
389
421
|
end
|
390
422
|
|
391
423
|
def update(args)
|
@@ -394,6 +426,7 @@ EOT
|
|
394
426
|
account_id = nil
|
395
427
|
role_mappings = nil
|
396
428
|
role_mapping_names = nil
|
429
|
+
default_role_id = nil
|
397
430
|
optparse = Morpheus::Cli::OptionParser.new do|opts|
|
398
431
|
opts.banner = subcommand_usage("[name] [options]")
|
399
432
|
opts.on('--name VALUE', String, "Name for this identity source") do |val|
|
@@ -427,6 +460,9 @@ EOT
|
|
427
460
|
end
|
428
461
|
end
|
429
462
|
end
|
463
|
+
opts.on('--default-role ROLE', String, "Default Role ID or Authority") do |val|
|
464
|
+
default_role_id = val
|
465
|
+
end
|
430
466
|
build_standard_update_options(opts, options)
|
431
467
|
opts.footer = "Update an identity source." + "\n" +
|
432
468
|
"[name] is required. This is the name or id of an identity source."
|
@@ -470,22 +506,33 @@ EOT
|
|
470
506
|
payload['roleMappingNames'] = role_mapping_names
|
471
507
|
end
|
472
508
|
|
509
|
+
# Default Account Role
|
510
|
+
if default_role_id
|
511
|
+
if default_role_id == 'null'
|
512
|
+
payload['userSource']['defaultAccountRole'] = {'id' => nil }
|
513
|
+
else
|
514
|
+
# use no_prompt to convert name to id
|
515
|
+
options[:options]['defaultAccountRole'] = {'id' => default_role_id }
|
516
|
+
v_prompt = Morpheus::Cli::OptionTypes.no_prompt([{'fieldContext' => 'defaultAccountRole', 'fieldName' => 'id', 'type' => 'select', 'selectOptions' => get_available_role_options(user_source['account']['id']), 'fieldLabel' => 'Default Role', 'required' => false }], options[:options])
|
517
|
+
if v_prompt['defaultAccountRole'] && v_prompt['defaultAccountRole']['id']
|
518
|
+
default_role_id = v_prompt['defaultAccountRole']['id']
|
519
|
+
end
|
520
|
+
payload['userSource']['defaultAccountRole'] = {'id' => default_role_id }
|
521
|
+
end
|
522
|
+
end
|
473
523
|
end
|
474
524
|
@user_sources_interface.setopts(options)
|
475
525
|
if options[:dry_run]
|
476
526
|
print_dry_run @user_sources_interface.dry.update(nil, user_source['id'], payload)
|
477
527
|
return
|
478
528
|
end
|
479
|
-
|
480
529
|
json_response = @user_sources_interface.update(nil, user_source['id'], payload)
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
530
|
+
render_response(json_response, options, 'userSource') do
|
531
|
+
user_source = json_response['userSource'] || user_source
|
532
|
+
print_green_success "Updated Identity Source #{user_source['name']}"
|
533
|
+
_get(user_source['id'], {}, options)
|
485
534
|
end
|
486
|
-
|
487
|
-
print_green_success "Updated Identity Source #{params['name'] || user_source['name']}"
|
488
|
-
get([user_source['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
|
535
|
+
return 0, nil
|
489
536
|
rescue RestClient::Exception => e
|
490
537
|
print_rest_exception(e, options)
|
491
538
|
exit 1
|
@@ -941,10 +988,20 @@ EOT
|
|
941
988
|
]
|
942
989
|
elsif type_code == 'saml'
|
943
990
|
[
|
944
|
-
{'fieldContext' => 'config', 'fieldName' => 'url', 'type' => 'text', 'fieldLabel' => 'Login Redirect URL', 'required' => true, 'description' => ''},
|
945
|
-
{'fieldContext' => 'config', 'fieldName' => 'doNotIncludeSAMLRequest', 'type' => 'checkbox', 'fieldLabel' => 'Exclude SAMLRequest Parameter', 'required' => true, 'description' => 'Do not include SAMLRequest parameter', 'defaultValue' => false},
|
946
|
-
{'fieldContext' => 'config', 'fieldName' => 'logoutUrl', 'type' => 'text', 'fieldLabel' => 'Logout Post URL', 'required' => true, 'description' => ''},
|
947
|
-
{'fieldContext' => 'config', 'fieldName' => '
|
991
|
+
{'fieldContext' => 'config', 'fieldName' => 'url', 'type' => 'text', 'fieldLabel' => 'Login Redirect URL', 'required' => true, 'description' => '', 'fieldGroup' => 'SAML SSO Configuration', 'displayOrder' => 1},
|
992
|
+
{'fieldContext' => 'config', 'fieldName' => 'doNotIncludeSAMLRequest', 'type' => 'checkbox', 'fieldLabel' => 'Exclude SAMLRequest Parameter', 'required' => true, 'description' => 'Do not include SAMLRequest parameter', 'defaultValue' => false, 'fieldGroup' => 'SAML SSO Configuration', 'displayOrder' => 2},
|
993
|
+
{'fieldContext' => 'config', 'fieldName' => 'logoutUrl', 'type' => 'text', 'fieldLabel' => 'Logout Post URL', 'required' => true, 'description' => '', 'fieldGroup' => 'SAML SSO Configuration', 'displayOrder' => 3},
|
994
|
+
{'code' => 'saml.SAMLSignatureMode', 'fieldContext' => 'config', 'fieldName' => 'SAMLSignatureMode', 'type' => 'select', 'selectOptions' => [{'name' => 'No Signature', 'value' => 'NoSignature'},{'name' => 'Self Signed', 'value' => 'SelfSigned'},{'name' => 'Custom RSA Signature', 'value' => 'CustomSignature'}], 'fieldLabel' => 'SAML Request', 'required' => true, 'description' => '', 'fieldGroup' => 'SAML SSO Configuration', 'defaultValue' => 'NoSignature', 'displayOrder' => 4},
|
995
|
+
{'dependsOnCode' => 'saml.SAMLSignatureMode:CustomSignature', 'fieldContext' => 'config', 'fieldName' => 'request509Certificate', 'type' => 'textarea', 'fieldLabel' => 'X.509 Certificate', 'required' => false, 'description' => '', 'fieldGroup' => 'SAML SSO Configuration', 'displayOrder' => 5},
|
996
|
+
{'dependsOnCode' => 'saml.SAMLSignatureMode:CustomSignature', 'fieldContext' => 'config', 'fieldName' => 'requestPrivateKey', 'type' => 'textarea', 'fieldLabel' => 'RSA Private Key', 'required' => false, 'description' => '', 'fieldGroup' => 'SAML SSO Configuration', 'displayOrder' => 6},
|
997
|
+
{'code' => 'saml.doNotValidateSignature', 'fieldContext' => 'config', 'fieldName' => 'doNotValidateSignature', 'type' => 'select', 'selectOptions' => [{'name' => 'Do Not Validate Assertion Signature', 'value' => 'true'},{'name' => 'Validate Assertion Signature', 'value' => 'false'}], 'fieldLabel' => 'SAML Response', 'required' => true, 'description' => '', 'fieldGroup' => 'SAML SSO Configuration', 'defaultValue' => 'Do Not Validate Assertion Signature', 'displayOrder' => 7},
|
998
|
+
{'dependsOnCode' => 'saml.doNotValidateSignature:false', 'fieldContext' => 'config', 'fieldName' => 'publicKey', 'type' => 'textarea', 'fieldLabel' => 'Signing Public Key', 'required' => false, 'description' => '', 'fieldGroup' => 'SAML SSO Configuration', 'displayOrder' => 8},
|
999
|
+
{'fieldContext' => 'config', 'fieldName' => 'privateKey', 'type' => 'textarea', 'fieldLabel' => 'Encryption RSA Private Key', 'required' => false, 'description' => '', 'fieldGroup' => 'SAML SSO Configuration', 'displayOrder' => 9},
|
1000
|
+
{'fieldContext' => 'config', 'fieldName' => 'givenNameAttribute', 'type' => 'text', 'fieldLabel' => 'Given Name Attribute Name', 'required' => false, 'description' => '', 'fieldGroup' => 'Assertion Attribute Mappings', 'displayOrder' => 10},
|
1001
|
+
{'fieldContext' => 'config', 'fieldName' => 'surnameAttribute', 'type' => 'text', 'fieldLabel' => 'Surname Attribute Name', 'required' => false, 'description' => '', 'fieldGroup' => 'Assertion Attribute Mappings', 'displayOrder' => 11},
|
1002
|
+
{'fieldContext' => 'config', 'fieldName' => 'roleAttributeName', 'type' => 'text', 'fieldLabel' => 'Role Attribute Name', 'required' => false, 'description' => '', 'fieldGroup' => 'Role Mappings', 'displayOrder' => 12},
|
1003
|
+
{'fieldContext' => 'config', 'fieldName' => 'requiredAttributeValue', 'type' => 'text', 'fieldLabel' => 'Required Role Attribute Value', 'required' => false, 'description' => '', 'fieldGroup' => 'Role Mappings', 'displayOrder' => 13},
|
1004
|
+
|
948
1005
|
]
|
949
1006
|
elsif type_code == 'customExternal'
|
950
1007
|
[
|
@@ -966,4 +1023,15 @@ EOT
|
|
966
1023
|
[]
|
967
1024
|
end
|
968
1025
|
end
|
1026
|
+
|
1027
|
+
def get_available_role_options(account_id)
|
1028
|
+
available_roles = @account_users_interface.available_roles(account_id)['roles']
|
1029
|
+
# if available_roles.empty?
|
1030
|
+
# print_red_alert "No available roles found."
|
1031
|
+
# exit 1
|
1032
|
+
# end
|
1033
|
+
role_options = available_roles.collect {|role|
|
1034
|
+
{'name' => role['authority'], 'value' => role['id']}
|
1035
|
+
}
|
1036
|
+
end
|
969
1037
|
end
|
@@ -172,8 +172,8 @@ class Morpheus::Cli::Users
|
|
172
172
|
options[:include_personas_access] = true
|
173
173
|
params['includeAccess'] = true
|
174
174
|
end
|
175
|
-
opts.on(
|
176
|
-
options[:
|
175
|
+
opts.on(nil, '--hide-none-access', "Hide records with 'none' access") do
|
176
|
+
options[:hide_none_access] = true
|
177
177
|
end
|
178
178
|
build_standard_get_options(opts, options, [:account])
|
179
179
|
opts.footer = <<-EOT
|
@@ -245,11 +245,11 @@ EOT
|
|
245
245
|
end
|
246
246
|
else
|
247
247
|
available_field_options = {'features' => 'Feature', 'sites' => 'Group', 'zones' => 'Cloud', 'instance_types' => 'Instance Type',
|
248
|
-
'app_templates' => 'Blueprint', 'catalog_item_types' => 'Catalog Item
|
248
|
+
'app_templates' => 'Blueprint', 'catalog_item_types' => 'Catalog Item Type', 'personas' => 'Persona', 'vdi_pools' => 'VDI Pool', 'report_types' => 'Report Type'}
|
249
249
|
available_field_options.each do |field, label|
|
250
250
|
if !(field == 'sites' && is_tenant_account) && options["include_#{field}_access".to_sym]
|
251
251
|
access = user['access'][field.split('_').enum_for(:each_with_index).collect {|word, idx| idx == 0 ? word : word.capitalize}.join]
|
252
|
-
access = access.reject {|it| it['access'] == 'none'} if
|
252
|
+
access = access.reject {|it| it['access'] == 'none'} if options[:hide_none_access]
|
253
253
|
|
254
254
|
if field == "features"
|
255
255
|
# print_h2 "Permissions", options
|
@@ -291,10 +291,10 @@ EOT
|
|
291
291
|
opts.on('-g','--global', "Global (All Tenants). Find users across all tenants. Default is your own tenant only.") do
|
292
292
|
options[:global] = true
|
293
293
|
end
|
294
|
-
|
295
|
-
|
296
|
-
options[:display_none_access] = true
|
294
|
+
opts.on('--hide-none-access', "Hide records with 'none' access") do
|
295
|
+
options[:hide_none_access] = true
|
297
296
|
end
|
297
|
+
build_common_options(opts, options, [:account, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
298
298
|
opts.footer = "Display Access for a user." + "\n" +
|
299
299
|
"[user] is required. This is the username or id of a user."
|
300
300
|
end
|
@@ -366,11 +366,11 @@ EOT
|
|
366
366
|
|
367
367
|
print_h1 "User Permissions: #{user['username']}", options
|
368
368
|
|
369
|
-
available_field_options = {'features' => 'Feature', 'sites' => 'Group', 'zones' => 'Cloud', 'instance_types' => 'Instance Type', 'app_templates' => 'Blueprint'}
|
369
|
+
available_field_options = {'features' => 'Feature', 'sites' => 'Group', 'zones' => 'Cloud', 'instance_types' => 'Instance Type', 'app_templates' => 'Blueprint', 'catalog_item_types' => 'Catalog Item Type', 'vdi_pools' => 'VDI Pool', 'report_types' => 'Report Type','personas' => 'Persona'}
|
370
370
|
available_field_options.each do |field, label|
|
371
371
|
if !(field == 'sites' && is_tenant_account)
|
372
372
|
access = json_response['access'][field.split('_').enum_for(:each_with_index).collect {|word, idx| idx == 0 ? word : word.capitalize}.join]
|
373
|
-
access = access.reject {|it| it['access'] == 'none'} if
|
373
|
+
access = access.reject {|it| it['access'] == 'none'} if options[:hide_none_access]
|
374
374
|
|
375
375
|
print_h2 "#{label} Access", options
|
376
376
|
print cyan
|
@@ -381,7 +381,7 @@ EOT
|
|
381
381
|
if access.count > 0
|
382
382
|
access.each {|it| it['access'] = format_access_string(it['access'], available_access_levels)}
|
383
383
|
|
384
|
-
if ['features', 'instance_types'].include?(field)
|
384
|
+
if ['features', 'instance_types', 'report_types'].include?(field)
|
385
385
|
print as_pretty_table(access, [:name, :code, :access], options)
|
386
386
|
else
|
387
387
|
print as_pretty_table(access, [:name, :access], options)
|
@@ -74,6 +74,7 @@ EOT
|
|
74
74
|
if id.to_s !~ /\A\d{1,}\Z/
|
75
75
|
# assume the last part of path is the type
|
76
76
|
record_type = path.split("/").last
|
77
|
+
record_type.sub!('#!', '')
|
77
78
|
record = find_by_name(record_type, id)
|
78
79
|
if record.nil?
|
79
80
|
raise_command_error("[id] is invalid. No #{record_type} found for '#{id}'", args, optparse)
|