morpheus-cli 5.5.0 → 5.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/scale_thresholds_interface.rb +9 -0
- data/lib/morpheus/cli/cli_command.rb +39 -20
- data/lib/morpheus/cli/commands/apps.rb +1 -1
- data/lib/morpheus/cli/commands/cloud_resource_pools_command.rb +33 -2
- data/lib/morpheus/cli/commands/clouds.rb +12 -6
- data/lib/morpheus/cli/commands/clusters.rb +66 -5
- data/lib/morpheus/cli/commands/hosts.rb +5 -1
- data/lib/morpheus/cli/commands/instances.rb +1 -1
- 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_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/roles.rb +475 -70
- data/lib/morpheus/cli/commands/scale_thresholds.rb +103 -0
- data/lib/morpheus/cli/commands/tasks.rb +19 -12
- 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 +1 -3
- data/lib/morpheus/cli/mixins/rest_command.rb +41 -14
- data/lib/morpheus/cli/option_types.rb +68 -37
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/logging.rb +6 -8
- 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
|
@@ -830,6 +831,7 @@ class Morpheus::Cli::Tasks
|
|
830
831
|
instances = []
|
831
832
|
server_ids = []
|
832
833
|
servers = []
|
834
|
+
default_refresh_interval = 10
|
833
835
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
834
836
|
opts.banner = subcommand_usage("[task] --instance [instance] [options]")
|
835
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|
|
@@ -864,6 +866,12 @@ class Morpheus::Cli::Tasks
|
|
864
866
|
opts.on('--config [TEXT]', String, "Custom config") do |val|
|
865
867
|
params['customConfig'] = val.to_s
|
866
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
|
867
875
|
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
868
876
|
end
|
869
877
|
optparse.parse!(args)
|
@@ -936,10 +944,7 @@ class Morpheus::Cli::Tasks
|
|
936
944
|
return
|
937
945
|
end
|
938
946
|
json_response = @tasks_interface.run(task['id'], payload)
|
939
|
-
|
940
|
-
puts as_json(json_response, options)
|
941
|
-
return json_response['success'] ? 0 : 1
|
942
|
-
else
|
947
|
+
render_response(json_response, options) do
|
943
948
|
target_desc = nil
|
944
949
|
if instances.size() > 0
|
945
950
|
target_desc = (instances.size() == 1) ? "instance #{instances[0]['name']}" : "#{instances.size()} instances"
|
@@ -951,17 +956,19 @@ class Morpheus::Cli::Tasks
|
|
951
956
|
else
|
952
957
|
print_green_success "Executing task #{task['name']}"
|
953
958
|
end
|
954
|
-
# todo: refresh, use get processId and load process record isntead? err
|
955
959
|
if json_response["jobExecution"] && json_response["jobExecution"]["id"]
|
956
|
-
|
957
|
-
|
958
|
-
|
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
|
959
969
|
end
|
960
|
-
return json_response['success'] ? 0 : 1
|
961
970
|
end
|
962
|
-
|
963
|
-
print_rest_exception(e, options)
|
964
|
-
return 1
|
971
|
+
return 0, nil
|
965
972
|
end
|
966
973
|
end
|
967
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)
|
@@ -2,6 +2,7 @@ require 'morpheus/cli/cli_command'
|
|
2
2
|
|
3
3
|
class Morpheus::Cli::Workflows
|
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
|
set_default_subcommand :list
|
@@ -538,6 +539,7 @@ class Morpheus::Cli::Workflows
|
|
538
539
|
instances = []
|
539
540
|
server_ids = []
|
540
541
|
servers = []
|
542
|
+
default_refresh_interval = 10
|
541
543
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
542
544
|
opts.banner = subcommand_usage("[workflow] --instance [instance] [options]")
|
543
545
|
opts.on('--instance INSTANCE', String, "Instance name or id to execute the workflow on. This option can be passed more than once.") do |val|
|
@@ -572,6 +574,12 @@ class Morpheus::Cli::Workflows
|
|
572
574
|
opts.on('--config [TEXT]', String, "Custom config") do |val|
|
573
575
|
params['customConfig'] = val.to_s
|
574
576
|
end
|
577
|
+
opts.on('--refresh [SECONDS]', String, "Refresh until execution is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
|
578
|
+
options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
|
579
|
+
end
|
580
|
+
opts.on(nil, '--no-refresh', "Do not refresh" ) do
|
581
|
+
options[:no_refresh] = true
|
582
|
+
end
|
575
583
|
build_common_options(opts, options, [:payload, :options, :json, :dry_run, :remote])
|
576
584
|
end
|
577
585
|
optparse.parse!(args)
|
@@ -580,7 +588,7 @@ class Morpheus::Cli::Workflows
|
|
580
588
|
end
|
581
589
|
workflow_name = args[0]
|
582
590
|
connect(options)
|
583
|
-
|
591
|
+
|
584
592
|
workflow = find_workflow_by_name_or_id(workflow_name)
|
585
593
|
return 1 if workflow.nil?
|
586
594
|
|
@@ -642,10 +650,8 @@ class Morpheus::Cli::Workflows
|
|
642
650
|
return 0
|
643
651
|
end
|
644
652
|
json_response = @task_sets_interface.run(workflow['id'], payload)
|
645
|
-
|
646
|
-
|
647
|
-
return json_response['success'] ? 0 : 1
|
648
|
-
else
|
653
|
+
|
654
|
+
render_response(json_response, options) do
|
649
655
|
target_desc = nil
|
650
656
|
if instances.size() > 0
|
651
657
|
target_desc = (instances.size() == 1) ? "instance #{instances[0]['name']}" : "#{instances.size()} instances"
|
@@ -657,18 +663,19 @@ class Morpheus::Cli::Workflows
|
|
657
663
|
else
|
658
664
|
print_green_success "Executing workflow #{workflow['name']}"
|
659
665
|
end
|
660
|
-
# todo: refresh, use get processId and load process record isntead? err
|
661
666
|
if json_response["jobExecution"] && json_response["jobExecution"]["id"]
|
662
|
-
|
663
|
-
|
664
|
-
|
667
|
+
job_execution_id = json_response["jobExecution"]["id"]
|
668
|
+
if options[:no_refresh]
|
669
|
+
get_args = [json_response["jobExecution"]["id"], "--details"] + (options[:remote] ? ["-r",options[:remote]] : [])
|
670
|
+
Morpheus::Logging::DarkPrinter.puts((['jobs', 'get-execution'] + get_args).join(' ')) if Morpheus::Logging.debug?
|
671
|
+
::Morpheus::Cli::JobsCommand.new.handle(['get-execution'] + get_args)
|
672
|
+
else
|
673
|
+
#Morpheus::Cli::JobsCommand.new.handle(["get-execution", job_execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
|
674
|
+
job_execution_results = wait_for_job_execution(job_execution_id, options.merge({:details => true}))
|
675
|
+
end
|
665
676
|
end
|
666
|
-
return json_response['success'] ? 0 : 1
|
667
677
|
end
|
668
|
-
|
669
|
-
print_rest_exception(e, options)
|
670
|
-
return 1
|
671
|
-
end
|
678
|
+
return 0, nil
|
672
679
|
end
|
673
680
|
|
674
681
|
private
|
@@ -44,8 +44,13 @@ class Morpheus::Cli::ErrorHandler
|
|
44
44
|
# exit_code = 127
|
45
45
|
when Morpheus::Cli::CommandArgumentsError
|
46
46
|
puts_angry_error err.message
|
47
|
-
|
48
|
-
|
47
|
+
if err.args.include?("--help") || err.args.include?("--help")
|
48
|
+
@stderr.puts err.optparse
|
49
|
+
else
|
50
|
+
@stderr.puts err.optparse.banner if err.optparse && err.optparse.banner
|
51
|
+
@stderr.puts "Try --help for more usage information"
|
52
|
+
end
|
53
|
+
|
49
54
|
do_print_stacktrace = false
|
50
55
|
if err.exit_code
|
51
56
|
exit_code = err.exit_code
|
@@ -60,8 +65,12 @@ class Morpheus::Cli::ErrorHandler
|
|
60
65
|
if !message_lines.empty?
|
61
66
|
@stderr.puts message_lines.join("\n") unless message_lines.empty?
|
62
67
|
else
|
63
|
-
|
64
|
-
|
68
|
+
if err.args.include?("--help") || err.args.include?("--help")
|
69
|
+
@stderr.puts err.optparse
|
70
|
+
else
|
71
|
+
@stderr.puts err.optparse.banner if err.optparse && err.optparse.banner && message_lines.empty?
|
72
|
+
@stderr.puts "Try --help for more usage information"
|
73
|
+
end
|
65
74
|
end
|
66
75
|
do_print_stacktrace = false
|
67
76
|
if err.exit_code
|
@@ -136,7 +136,7 @@ module Morpheus::Cli::AccountsHelper
|
|
136
136
|
"Multitenant" => lambda {|it|
|
137
137
|
format_boolean(it['multitenant']).to_s + (it['multitenantLocked'] ? " (LOCKED)" : "")
|
138
138
|
},
|
139
|
-
"Default Persona" => lambda {|it| it['defaultPersona'] ? it['defaultPersona']['name'] : '
|
139
|
+
"Default Persona" => lambda {|it| it['defaultPersona'] ? it['defaultPersona']['name'] : '' },
|
140
140
|
"Owner" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
|
141
141
|
#"Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
142
142
|
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'morpheus/cli/mixins/print_helper'
|
2
2
|
# Mixin for Morpheus::Cli command classes
|
3
|
-
# Provides
|
3
|
+
# Provides refreshing execution-request records by unique id (uuid)
|
4
4
|
module Morpheus::Cli::ExecutionRequestHelper
|
5
5
|
|
6
6
|
def self.included(klass)
|
@@ -111,9 +111,9 @@ module Morpheus::Cli::InfrastructureHelper
|
|
111
111
|
return cloud
|
112
112
|
end
|
113
113
|
|
114
|
-
def get_available_cloud_types(refresh=false)
|
114
|
+
def get_available_cloud_types(refresh=false, params = {})
|
115
115
|
if !@available_cloud_types || refresh
|
116
|
-
@available_cloud_types = clouds_interface.cloud_types({max:1000})['zoneTypes']
|
116
|
+
@available_cloud_types = clouds_interface.cloud_types({max:1000}.deep_merge(params))['zoneTypes']
|
117
117
|
end
|
118
118
|
return @available_cloud_types
|
119
119
|
end
|
@@ -130,7 +130,7 @@ module Morpheus::Cli::InfrastructureHelper
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def cloud_type_for_name(name)
|
133
|
-
return get_available_cloud_types().find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
|
133
|
+
return get_available_cloud_types(true, {'name' => name}).find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
|
134
134
|
end
|
135
135
|
|
136
136
|
|