morpheus-cli 5.5.0 → 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/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
|
|