morpheus-cli 0.9.10 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/morpheus +48 -33
- data/lib/morpheus/api/api_client.rb +12 -0
- data/lib/morpheus/api/custom_instance_types.rb +55 -0
- data/lib/morpheus/api/custom_instance_types_interface.rb +133 -0
- data/lib/morpheus/api/dashboard_interface.rb +37 -0
- data/lib/morpheus/api/users_interface.rb +17 -0
- data/lib/morpheus/api/whoami_interface.rb +20 -0
- data/lib/morpheus/cli.rb +13 -1
- data/lib/morpheus/cli/app_templates.rb +1 -1
- data/lib/morpheus/cli/cli_command.rb +22 -1
- data/lib/morpheus/cli/cli_registry.rb +1 -1
- data/lib/morpheus/cli/credentials.rb +33 -11
- data/lib/morpheus/cli/dashboard_command.rb +74 -0
- data/lib/morpheus/cli/instance_types.rb +4 -2
- data/lib/morpheus/cli/key_pairs.rb +1 -1
- data/lib/morpheus/cli/library.rb +539 -0
- data/lib/morpheus/cli/login.rb +57 -0
- data/lib/morpheus/cli/logout.rb +61 -0
- data/lib/morpheus/cli/mixins/accounts_helper.rb +52 -10
- data/lib/morpheus/cli/mixins/print_helper.rb +23 -16
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -1
- data/lib/morpheus/cli/mixins/whoami_helper.rb +34 -0
- data/lib/morpheus/cli/option_types.rb +15 -1
- data/lib/morpheus/cli/recent_activity_command.rb +83 -0
- data/lib/morpheus/cli/remote.rb +71 -28
- data/lib/morpheus/cli/roles.rb +89 -24
- data/lib/morpheus/cli/shell.rb +24 -11
- data/lib/morpheus/cli/users.rb +166 -24
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/version_command.rb +45 -0
- data/lib/morpheus/cli/whoami.rb +139 -0
- data/lib/morpheus/logging.rb +78 -0
- metadata +15 -2
data/lib/morpheus/cli/remote.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'io/console'
|
3
3
|
require 'rest_client'
|
4
|
-
require 'term/ansicolor'
|
5
4
|
require 'optparse'
|
5
|
+
require 'morpheus/cli/cli_command'
|
6
6
|
|
7
7
|
|
8
8
|
class Morpheus::Cli::Remote
|
9
|
-
include
|
9
|
+
include Morpheus::Cli::CliCommand
|
10
|
+
|
10
11
|
def initialize()
|
11
12
|
@appliances = ::Morpheus::Cli::Remote.load_appliance_file
|
12
13
|
end
|
@@ -32,35 +33,56 @@ class Morpheus::Cli::Remote
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def list(args)
|
36
|
+
options = {}
|
37
|
+
optparse = OptionParser.new do|opts|
|
38
|
+
opts.banner = "Usage: morpheus remote list"
|
39
|
+
build_common_options(opts, options, [])
|
40
|
+
end
|
41
|
+
optparse.parse(args)
|
42
|
+
|
35
43
|
print "\n" ,cyan, bold, "Morpheus Appliances\n","==================", reset, "\n\n"
|
36
44
|
# print red, bold, "red bold", reset, "\n"
|
37
45
|
if @appliances == nil || @appliances.empty?
|
46
|
+
puts yellow,"No remote appliances configured.",reset
|
38
47
|
else
|
48
|
+
rows = @appliances.collect do |app_name, v|
|
49
|
+
{
|
50
|
+
active: (v[:active] ? "=>" : ""),
|
51
|
+
name: app_name,
|
52
|
+
host: v[:host]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
print cyan
|
56
|
+
tp rows, {:active => {:display_name => ""}}, {:name => {:width => 16}}, {:host => {:width => 40}}
|
57
|
+
print reset
|
58
|
+
|
59
|
+
# @appliances.each do |app_name, v|
|
60
|
+
# print cyan
|
61
|
+
# if v[:active] == true
|
62
|
+
# print bold, "=> #{app_name}\t#{v[:host]}",reset,"\n"
|
63
|
+
# else
|
64
|
+
# print "= #{app_name}\t#{v[:host]}",reset,"\n"
|
65
|
+
# end
|
66
|
+
# end
|
67
|
+
|
68
|
+
print "\n\n# => - current\n\n"
|
39
69
|
end
|
40
|
-
@appliances.each do |app_name, v|
|
41
|
-
print cyan
|
42
|
-
if v[:active] == true
|
43
|
-
print bold, "=> #{app_name}\t#{v[:host]}",reset,"\n"
|
44
|
-
else
|
45
|
-
print "= #{app_name}\t#{v[:host]}",reset,"\n"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
print "\n\n# => - current\n\n"
|
49
70
|
end
|
50
71
|
|
51
72
|
def add(args)
|
52
|
-
|
53
|
-
puts "\nUsage: morpheus remote add [name] [host] [--default]\n\n"
|
54
|
-
return
|
55
|
-
end
|
56
|
-
params = {}
|
73
|
+
options = {}
|
57
74
|
optparse = OptionParser.new do|opts|
|
58
|
-
|
59
|
-
opts
|
60
|
-
|
75
|
+
opts.banner = "Usage: morpheus remote add [name] [host] [--default]"
|
76
|
+
build_common_options(opts, options, [])
|
77
|
+
opts.on( '-d', '--default', "Make this the default remote appliance" ) do
|
78
|
+
options[:default] = true
|
61
79
|
end
|
62
80
|
end
|
63
81
|
optparse.parse(args)
|
82
|
+
if args.count < 2
|
83
|
+
puts "\n#{optparse.banner}\n\n"
|
84
|
+
return
|
85
|
+
end
|
64
86
|
|
65
87
|
name = args[0].to_sym
|
66
88
|
if @appliances[name] != nil
|
@@ -70,7 +92,7 @@ class Morpheus::Cli::Remote
|
|
70
92
|
host: args[1],
|
71
93
|
active: false
|
72
94
|
}
|
73
|
-
if
|
95
|
+
if options[:default] == true
|
74
96
|
set_active_appliance name
|
75
97
|
end
|
76
98
|
end
|
@@ -79,10 +101,20 @@ class Morpheus::Cli::Remote
|
|
79
101
|
end
|
80
102
|
|
81
103
|
def remove(args)
|
104
|
+
options = {}
|
105
|
+
optparse = OptionParser.new do|opts|
|
106
|
+
opts.banner = "Usage: morpheus remote remove [name]"
|
107
|
+
build_common_options(opts, options, [])
|
108
|
+
opts.on( '-d', '--default', "Make this the default remote appliance" ) do
|
109
|
+
options[:default] = true
|
110
|
+
end
|
111
|
+
end
|
112
|
+
optparse.parse(args)
|
82
113
|
if args.empty?
|
83
|
-
puts "\
|
114
|
+
puts "\n#{optparse.banner}\n\n"
|
84
115
|
return
|
85
116
|
end
|
117
|
+
|
86
118
|
name = args[0].to_sym
|
87
119
|
if @appliances[name] == nil
|
88
120
|
print red, "Remote appliance not configured for #{args[0]}", reset, "\n"
|
@@ -101,10 +133,20 @@ class Morpheus::Cli::Remote
|
|
101
133
|
end
|
102
134
|
|
103
135
|
def use(args)
|
136
|
+
options = {}
|
137
|
+
optparse = OptionParser.new do|opts|
|
138
|
+
opts.banner = "Usage: morpheus remote use [name]"
|
139
|
+
build_common_options(opts, options, [])
|
140
|
+
opts.on( '-d', '--default', "Make this the default remote appliance. This does the same thing as remote use." ) do
|
141
|
+
options[:default] = true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
optparse.parse(args)
|
104
145
|
if args.empty?
|
105
|
-
puts "
|
146
|
+
puts "\n#{optparse.banner}\n\n"
|
106
147
|
return
|
107
148
|
end
|
149
|
+
|
108
150
|
name = args[0].to_sym
|
109
151
|
if @appliances[name] == nil
|
110
152
|
print red, "Remote appliance not configured for #{args[0]}", reset, "\n"
|
@@ -141,12 +183,13 @@ class Morpheus::Cli::Remote
|
|
141
183
|
if File.exist? remote_file
|
142
184
|
return YAML.load_file(remote_file)
|
143
185
|
else
|
144
|
-
return {
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
}
|
186
|
+
return {}
|
187
|
+
# return {
|
188
|
+
# morpheus: {
|
189
|
+
# host: 'https://api.gomorpheus.com',
|
190
|
+
# active: true
|
191
|
+
# }
|
192
|
+
# }
|
150
193
|
end
|
151
194
|
end
|
152
195
|
|
data/lib/morpheus/cli/roles.rb
CHANGED
@@ -23,6 +23,7 @@ class Morpheus::Cli::Roles
|
|
23
23
|
exit 1
|
24
24
|
end
|
25
25
|
@api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
|
26
|
+
@whoami_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).whoami
|
26
27
|
@users_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).users
|
27
28
|
@accounts_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).accounts
|
28
29
|
@roles_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).roles
|
@@ -84,6 +85,8 @@ class Morpheus::Cli::Roles
|
|
84
85
|
connect(options)
|
85
86
|
begin
|
86
87
|
|
88
|
+
load_whoami()
|
89
|
+
|
87
90
|
account = find_account_from_options(options)
|
88
91
|
account_id = account ? account['id'] : nil
|
89
92
|
|
@@ -103,7 +106,7 @@ class Morpheus::Cli::Roles
|
|
103
106
|
if roles.empty?
|
104
107
|
puts yellow,"No roles found.",reset
|
105
108
|
else
|
106
|
-
print_roles_table(roles)
|
109
|
+
print_roles_table(roles, {is_master_account: @is_master_account})
|
107
110
|
end
|
108
111
|
print reset,"\n\n"
|
109
112
|
end
|
@@ -168,6 +171,8 @@ class Morpheus::Cli::Roles
|
|
168
171
|
puts "Name: #{role['authority']}"
|
169
172
|
puts "Description: #{role['description']}"
|
170
173
|
puts "Scope: #{role['scope']}"
|
174
|
+
puts "Type: #{format_role_type(role)}"
|
175
|
+
puts "Multitenant: #{role['multitenant'] ? 'Yes' : 'No'}"
|
171
176
|
puts "Owner: #{role['owner'] ? role['owner']['name'] : nil}"
|
172
177
|
puts "Date Created: #{format_local_dt(role['dateCreated'])}"
|
173
178
|
puts "Last Updated: #{format_local_dt(role['lastUpdated'])}"
|
@@ -268,25 +273,55 @@ class Morpheus::Cli::Roles
|
|
268
273
|
connect(options)
|
269
274
|
begin
|
270
275
|
|
276
|
+
load_whoami()
|
277
|
+
|
271
278
|
account = find_account_from_options(options)
|
272
279
|
account_id = account ? account['id'] : nil
|
273
280
|
|
274
|
-
|
281
|
+
# argh, some options depend on others here...eg. multitenant is only available when roleType == 'user'
|
282
|
+
#prompt_option_types = update_role_option_types()
|
275
283
|
|
276
|
-
|
277
|
-
|
278
|
-
role_payload =
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
+
role_payload = {}
|
285
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'authority', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1}], options[:options])
|
286
|
+
role_payload['authority'] = v_prompt['authority']
|
287
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2}], options[:options])
|
288
|
+
role_payload['description'] = v_prompt['description']
|
289
|
+
|
290
|
+
if @is_master_account
|
291
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'roleType', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => role_type_options, 'defaultValue' => 'user', 'displayOrder' => 3}], options[:options])
|
292
|
+
role_payload['roleType'] = v_prompt['roleType']
|
293
|
+
else
|
294
|
+
role_payload['roleType'] = 'user'
|
284
295
|
end
|
285
|
-
|
286
|
-
|
296
|
+
|
297
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'text', 'displayOrder' => 4}], options[:options])
|
298
|
+
if v_prompt['baseRole'].to_s != ''
|
299
|
+
base_role = find_role_by_name(account_id, v_prompt['baseRole'])
|
287
300
|
exit 1 if base_role.nil?
|
288
301
|
role_payload['baseRoleId'] = base_role['id']
|
289
302
|
end
|
303
|
+
|
304
|
+
if @is_master_account
|
305
|
+
if role_payload['roleType'] == 'user'
|
306
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use', 'displayOrder' => 5}], options[:options])
|
307
|
+
role_payload['multitenant'] = ['on','true'].include?(v_prompt['multitenant'].to_s)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
role_payload['instanceLimits'] = {}
|
312
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instanceLimits.maxStorage', 'fieldLabel' => 'Max Storage (bytes)', 'type' => 'text', 'displayOrder' => 8}], options[:options])
|
313
|
+
if v_prompt['instanceLimits.maxStorage'].to_s.strip != ''
|
314
|
+
role_payload['instanceLimits']['maxStorage'] = v_prompt['instanceLimits.maxStorage'].to_i
|
315
|
+
end
|
316
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instanceLimits.maxMemory', 'fieldLabel' => 'Max Memory (bytes)', 'type' => 'text', 'displayOrder' => 9}], options[:options])
|
317
|
+
if v_prompt['instanceLimits.maxMemory'].to_s.strip != ''
|
318
|
+
role_payload['instanceLimits']['maxMemory'] = v_prompt['instanceLimits.maxMemory'].to_i
|
319
|
+
end
|
320
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instanceLimits.maxCpu', 'fieldLabel' => 'CPU Count', 'type' => 'text', 'displayOrder' => 10}], options[:options])
|
321
|
+
if v_prompt['instanceLimits.maxCpu'].to_s.strip != ''
|
322
|
+
role_payload['instanceLimits']['maxCpu'] = v_prompt['instanceLimits.maxCpu'].to_i
|
323
|
+
end
|
324
|
+
|
290
325
|
request_payload = {role: role_payload}
|
291
326
|
response = @roles_interface.create(account_id, request_payload)
|
292
327
|
|
@@ -327,18 +362,27 @@ class Morpheus::Cli::Roles
|
|
327
362
|
|
328
363
|
begin
|
329
364
|
|
365
|
+
load_whoami()
|
366
|
+
|
330
367
|
account = find_account_from_options(options)
|
331
368
|
account_id = account ? account['id'] : nil
|
332
369
|
|
333
370
|
role = find_role_by_name(account_id, name)
|
334
371
|
exit 1 if role.nil?
|
335
372
|
|
336
|
-
|
373
|
+
prompt_option_types = update_role_option_types()
|
374
|
+
if !@is_master_account
|
375
|
+
prompt_option_types = prompt_option_types.reject {|it| ['roleType', 'multitenant'].include?(it['fieldName']) }
|
376
|
+
end
|
377
|
+
if role['roleType'] != 'user'
|
378
|
+
prompt_option_types = prompt_option_types.reject {|it| ['multitenant'].include?(it['fieldName']) }
|
379
|
+
end
|
380
|
+
#params = Morpheus::Cli::OptionTypes.prompt(prompt_option_types, options[:options], @api_client, options[:params])
|
337
381
|
params = options[:options] || {}
|
338
382
|
|
339
383
|
if params.empty?
|
340
384
|
puts "\n#{usage}\n\n"
|
341
|
-
option_lines =
|
385
|
+
option_lines = prompt_option_types.collect {|it| "\t-O #{it['fieldName']}=\"value\"" }.join("\n")
|
342
386
|
puts "\nAvailable Options:\n#{option_lines}\n\n"
|
343
387
|
exit 1
|
344
388
|
end
|
@@ -352,6 +396,10 @@ class Morpheus::Cli::Roles
|
|
352
396
|
role_payload['instanceLimits']['maxMemory'] = params['instanceLimits.maxMemory'].to_i if params['instanceLimits.maxMemory'].to_s.strip != ''
|
353
397
|
role_payload['instanceLimits']['maxCpu'] = params['instanceLimits.maxCpu'].to_i if params['instanceLimits.maxCpu'].to_s.strip != ''
|
354
398
|
end
|
399
|
+
|
400
|
+
if params['multitenant'].to_s != ''
|
401
|
+
role_payload['multitenant'] = ['on','true'].include?(v_prompt['multitenant'].to_s)
|
402
|
+
end
|
355
403
|
request_payload = {role: role_payload}
|
356
404
|
response = @roles_interface.update(account_id, role['id'], request_payload)
|
357
405
|
|
@@ -785,28 +833,31 @@ class Morpheus::Cli::Roles
|
|
785
833
|
|
786
834
|
private
|
787
835
|
|
788
|
-
def get_access_string(val)
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
|
794
|
-
|
795
|
-
end
|
836
|
+
# def get_access_string(val)
|
837
|
+
# val ||= 'none'
|
838
|
+
# if val == 'none'
|
839
|
+
# "#{white}#{val.to_s.capitalize}#{cyan}"
|
840
|
+
# else
|
841
|
+
# "#{green}#{val.to_s.capitalize}#{cyan}"
|
842
|
+
# end
|
843
|
+
# end
|
796
844
|
|
797
845
|
def add_role_option_types
|
798
846
|
[
|
799
847
|
{'fieldName' => 'authority', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
800
848
|
{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'displayOrder' => 2},
|
801
|
-
{'fieldName' => '
|
849
|
+
{'fieldName' => 'roleType', 'fieldLabel' => 'Role Type', 'type' => 'select', 'selectOptions' => [{'name' => 'User Role', 'value' => 'user'}, {'name' => 'Account Role', 'value' => 'account'}], 'defaultValue' => 'user', 'displayOrder' => 3},
|
850
|
+
{'fieldName' => 'baseRole', 'fieldLabel' => 'Copy From Role', 'type' => 'text', 'displayOrder' => 4},
|
851
|
+
{'fieldName' => 'multitenant', 'fieldLabel' => 'Multitenant', 'type' => 'checkbox', 'defaultValue' => 'off', 'description' => 'A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use', 'displayOrder' => 5},
|
802
852
|
{'fieldName' => 'instanceLimits.maxStorage', 'fieldLabel' => 'Max Storage (bytes)', 'type' => 'text', 'displayOrder' => 8},
|
803
853
|
{'fieldName' => 'instanceLimits.maxMemory', 'fieldLabel' => 'Max Memory (bytes)', 'type' => 'text', 'displayOrder' => 9},
|
804
854
|
{'fieldName' => 'instanceLimits.maxCpu', 'fieldLabel' => 'CPU Count', 'type' => 'text', 'displayOrder' => 10},
|
805
855
|
]
|
806
856
|
end
|
807
857
|
|
858
|
+
"A Multitenant role is automatically copied into all existing subaccounts as well as placed into a subaccount when created. Useful for providing a set of predefined roles a Customer can use"
|
808
859
|
def update_role_option_types
|
809
|
-
add_role_option_types.reject {|it| ['baseRole'].include?(it['fieldName']) }
|
860
|
+
add_role_option_types.reject {|it| ['roleType', 'baseRole'].include?(it['fieldName']) }
|
810
861
|
end
|
811
862
|
|
812
863
|
|
@@ -852,4 +903,18 @@ private
|
|
852
903
|
return results['instanceTypes'][0]
|
853
904
|
end
|
854
905
|
|
906
|
+
def load_whoami
|
907
|
+
whoami_response = @whoami_interface.get()
|
908
|
+
@current_user = whoami_response["user"]
|
909
|
+
if @current_user.empty?
|
910
|
+
print_red_alert "Unauthenticated. Please login."
|
911
|
+
exit 1
|
912
|
+
end
|
913
|
+
@is_master_account = whoami_response["isMasterAccount"]
|
914
|
+
end
|
915
|
+
|
916
|
+
def role_type_options
|
917
|
+
[{'name' => 'User Role', 'value' => 'user'}, {'name' => 'Account Role', 'value' => 'account'}]
|
918
|
+
end
|
919
|
+
|
855
920
|
end
|
data/lib/morpheus/cli/shell.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# require 'yaml'
|
2
2
|
require 'io/console'
|
3
3
|
require 'rest_client'
|
4
|
-
require 'term/ansicolor'
|
5
4
|
require 'optparse'
|
6
5
|
require 'table_print'
|
7
6
|
require 'morpheus/cli/cli_command'
|
@@ -13,7 +12,7 @@ require 'fileutils'
|
|
13
12
|
|
14
13
|
class Morpheus::Cli::Shell
|
15
14
|
include Morpheus::Cli::CliCommand
|
16
|
-
|
15
|
+
|
17
16
|
def initialize()
|
18
17
|
@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
19
18
|
@auto_complete = proc do |s|
|
@@ -55,7 +54,6 @@ class Morpheus::Cli::Shell
|
|
55
54
|
@history_logger.info "shell started" if @history_logger
|
56
55
|
load_history_from_log_file()
|
57
56
|
|
58
|
-
remote_handler = Morpheus::Cli::Remote.new()
|
59
57
|
exit = false
|
60
58
|
while !exit do
|
61
59
|
Readline.completion_append_character = " "
|
@@ -122,13 +120,18 @@ class Morpheus::Cli::Shell
|
|
122
120
|
if @command_options[:nocolor]
|
123
121
|
argv.push "--nocolor"
|
124
122
|
end
|
123
|
+
|
124
|
+
# set global log level to debug (print stack trace for bubbled exceptions)
|
125
|
+
if argv.find {|arg| arg == '-V' || arg == '--debug'}
|
126
|
+
@return_to_log_level = Morpheus::Logging.log_level
|
127
|
+
Morpheus::Logging.set_log_level(Morpheus::Logging::Logger::DEBUG)
|
128
|
+
end
|
129
|
+
argv = argv.find_all {|arg| arg != '-V' && arg != '--debug'}
|
130
|
+
|
125
131
|
#puts "cmd: #{argv.join(' ')}"
|
126
132
|
|
127
133
|
if argv[0] == 'shell'
|
128
|
-
puts "
|
129
|
-
elsif argv[0] == 'remote'
|
130
|
-
log_history_command(input)
|
131
|
-
remote_handler.handle(argv[1..-1])
|
134
|
+
puts "You are already in a shell."
|
132
135
|
elsif Morpheus::Cli::CliRegistry.has_command?(argv[0])
|
133
136
|
log_history_command(input)
|
134
137
|
Morpheus::Cli::CliRegistry.exec(argv[0], argv[1..-1])
|
@@ -145,13 +148,23 @@ class Morpheus::Cli::Shell
|
|
145
148
|
rescue SystemExit
|
146
149
|
# nothing to do
|
147
150
|
print "\n"
|
151
|
+
rescue OptionParser::InvalidOption => e
|
152
|
+
print Term::ANSIColor.red, "\n", "#{e.message}", "", Term::ANSIColor.reset
|
153
|
+
print "\n", "Try -h for help with this command.", "\n\n"
|
148
154
|
rescue => e
|
149
|
-
|
150
155
|
@history_logger.error "#{e.message}" if @history_logger
|
151
|
-
print red, "\n",
|
152
|
-
|
156
|
+
print Term::ANSIColor.red, "\n", "Unexpected Error", "\n\n", Term::ANSIColor.reset
|
157
|
+
if Morpheus::Logging.print_stacktrace?
|
158
|
+
print Term::ANSIColor.red, "\n", "#{e.class}: #{e.message}", "\n", Term::ANSIColor.reset
|
159
|
+
print e.backtrace.join("\n"), "\n\n"
|
160
|
+
end
|
153
161
|
end
|
154
|
-
|
162
|
+
|
163
|
+
if @return_to_log_level
|
164
|
+
Morpheus::Logging.set_log_level(@return_to_log_level)
|
165
|
+
@return_to_log_level = nil
|
166
|
+
end
|
167
|
+
|
155
168
|
end
|
156
169
|
end
|
157
170
|
end
|
data/lib/morpheus/cli/users.rb
CHANGED
@@ -23,6 +23,7 @@ class Morpheus::Cli::Users
|
|
23
23
|
exit 1
|
24
24
|
end
|
25
25
|
@api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
|
26
|
+
@whoami_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).whoami
|
26
27
|
@users_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).users
|
27
28
|
@accounts_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).accounts
|
28
29
|
@roles_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).roles
|
@@ -46,6 +47,8 @@ class Morpheus::Cli::Users
|
|
46
47
|
update(args[1..-1])
|
47
48
|
when 'remove'
|
48
49
|
remove(args[1..-1])
|
50
|
+
when 'feature-permissions'
|
51
|
+
feature_permissions(args[1..-1])
|
49
52
|
else
|
50
53
|
puts "\n#{usage}\n\n"
|
51
54
|
exit 127
|
@@ -84,7 +87,7 @@ class Morpheus::Cli::Users
|
|
84
87
|
else
|
85
88
|
print_users_table(users)
|
86
89
|
end
|
87
|
-
print reset,"\n
|
90
|
+
print reset,"\n"
|
88
91
|
end
|
89
92
|
rescue RestClient::Exception => e
|
90
93
|
print_rest_exception(e, options)
|
@@ -98,6 +101,24 @@ class Morpheus::Cli::Users
|
|
98
101
|
optparse = OptionParser.new do|opts|
|
99
102
|
opts.banner = usage
|
100
103
|
build_common_options(opts, options, [:account, :json])
|
104
|
+
opts.on(nil,'--feature-access', "Display Feature Access") do |val|
|
105
|
+
options[:include_feature_access] = true
|
106
|
+
end
|
107
|
+
# opts.on(nil,'--group-access', "Display Group Access") do
|
108
|
+
# options[:include_group_access] = true
|
109
|
+
# end
|
110
|
+
# opts.on(nil,'--cloud-access', "Display Cloud Access") do
|
111
|
+
# options[:include_cloud_access] = true
|
112
|
+
# end
|
113
|
+
# opts.on(nil,'--instance-type-access', "Display Instance Type Access") do
|
114
|
+
# options[:include_instance_type_access] = true
|
115
|
+
# end
|
116
|
+
opts.on(nil,'--all-access', "Display All Access Lists") do
|
117
|
+
options[:include_feature_access] = true
|
118
|
+
options[:include_group_access] = true
|
119
|
+
options[:include_cloud_access] = true
|
120
|
+
options[:include_instance_type_access] = true
|
121
|
+
end
|
101
122
|
end
|
102
123
|
optparse.parse(args)
|
103
124
|
|
@@ -118,18 +139,31 @@ class Morpheus::Cli::Users
|
|
118
139
|
user = find_user_by_username(account_id, username)
|
119
140
|
exit 1 if user.nil?
|
120
141
|
|
142
|
+
# meh, this should just always be returned with GET /api/users/:id
|
143
|
+
user_feature_permissions_json = nil
|
144
|
+
user_feature_permissions = nil
|
145
|
+
if options[:include_feature_access]
|
146
|
+
user_feature_permissions_json = @users_interface.feature_permissions(account_id, user['id'])
|
147
|
+
user_feature_permissions = user_feature_permissions_json['featurePermissions']
|
148
|
+
end
|
149
|
+
|
121
150
|
if options[:json]
|
122
151
|
print JSON.pretty_generate({user:user})
|
123
152
|
print "\n"
|
153
|
+
if (user_feature_permissions_json)
|
154
|
+
print JSON.pretty_generate(user_feature_permissions_json)
|
155
|
+
print "\n"
|
156
|
+
end
|
124
157
|
else
|
125
158
|
print "\n" ,cyan, bold, "User Details\n","==================", reset, "\n\n"
|
126
159
|
print cyan
|
127
160
|
puts "ID: #{user['id']}"
|
128
161
|
puts "Account: #{user['account'] ? user['account']['name'] : nil}"
|
129
162
|
puts "First Name: #{user['firstName']}"
|
130
|
-
puts "Last Name: #{user['
|
163
|
+
puts "Last Name: #{user['lastName']}"
|
131
164
|
puts "Username: #{user['username']}"
|
132
|
-
puts "
|
165
|
+
puts "Email: #{user['email']}"
|
166
|
+
puts "Role: #{format_user_role_names(user)}"
|
133
167
|
puts "Date Created: #{format_local_dt(user['dateCreated'])}"
|
134
168
|
puts "Last Updated: #{format_local_dt(user['lastUpdated'])}"
|
135
169
|
print "\n" ,cyan, bold, "User Instance Limits\n","==================", reset, "\n\n"
|
@@ -137,8 +171,22 @@ class Morpheus::Cli::Users
|
|
137
171
|
puts "Max Storage (bytes): #{user['instanceLimits'] ? user['instanceLimits']['maxStorage'] : 0}"
|
138
172
|
puts "Max Memory (bytes): #{user['instanceLimits'] ? user['instanceLimits']['maxMemory'] : 0}"
|
139
173
|
puts "CPU Count: #{user['instanceLimits'] ? user['instanceLimits']['maxCpu'] : 0}"
|
174
|
+
|
175
|
+
if options[:include_feature_access] && user_feature_permissions
|
176
|
+
if user_feature_permissions
|
177
|
+
print "\n" ,cyan, bold, "Feature Permissions\n","==================", reset, "\n\n"
|
178
|
+
print cyan
|
179
|
+
rows = user_feature_permissions.collect do |code, access|
|
180
|
+
{code: code, access: get_access_string(access) }
|
181
|
+
end
|
182
|
+
tp rows, [:code, :access]
|
183
|
+
else
|
184
|
+
puts yellow,"No permissions found.",reset
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
140
188
|
print cyan
|
141
|
-
print reset,"\n
|
189
|
+
print reset,"\n"
|
142
190
|
end
|
143
191
|
rescue RestClient::Exception => e
|
144
192
|
print_rest_exception(e, options)
|
@@ -152,6 +200,11 @@ class Morpheus::Cli::Users
|
|
152
200
|
optparse = OptionParser.new do|opts|
|
153
201
|
opts.banner = usage
|
154
202
|
build_common_options(opts, options, [:account, :options, :json])
|
203
|
+
opts.on('-h', '--help', "Prints this help" ) do
|
204
|
+
puts opts
|
205
|
+
puts Morpheus::Cli::OptionTypes.display_option_types_help(add_user_option_types)
|
206
|
+
exit
|
207
|
+
end
|
155
208
|
end
|
156
209
|
optparse.parse(args)
|
157
210
|
|
@@ -162,7 +215,10 @@ class Morpheus::Cli::Users
|
|
162
215
|
account = find_account_from_options(options)
|
163
216
|
account_id = account ? account['id'] : nil
|
164
217
|
|
165
|
-
|
218
|
+
# remove role option_type, it is just for help display, the role prompt is separate down below
|
219
|
+
prompt_option_types = add_user_option_types().reject {|it| ['role'].include?(it['fieldName']) }
|
220
|
+
|
221
|
+
params = Morpheus::Cli::OptionTypes.prompt(prompt_option_types, options[:options], @api_client, options[:params])
|
166
222
|
|
167
223
|
#puts "parsed params is : #{params.inspect}"
|
168
224
|
user_keys = ['username', 'firstName', 'lastName', 'email', 'password', 'passwordConfirmation', 'instanceLimits']
|
@@ -173,11 +229,12 @@ class Morpheus::Cli::Users
|
|
173
229
|
user_payload['instanceLimits']['maxMemory'] = params['instanceLimits.maxMemory'].to_i if params['instanceLimits.maxMemory'].to_s.strip != ''
|
174
230
|
user_payload['instanceLimits']['maxCpu'] = params['instanceLimits.maxCpu'].to_i if params['instanceLimits.maxCpu'].to_s.strip != ''
|
175
231
|
end
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
user_payload['
|
232
|
+
|
233
|
+
roles = prompt_user_roles(account_id, nil, options)
|
234
|
+
if !roles.empty?
|
235
|
+
user_payload['roles'] = roles.collect {|r| {id: r['id']} }
|
180
236
|
end
|
237
|
+
|
181
238
|
request_payload = {user: user_payload}
|
182
239
|
json_response = @users_interface.create(account_id, request_payload)
|
183
240
|
|
@@ -210,11 +267,17 @@ class Morpheus::Cli::Users
|
|
210
267
|
optparse = OptionParser.new do|opts|
|
211
268
|
opts.banner = usage
|
212
269
|
build_common_options(opts, options, [:account, :options, :json])
|
270
|
+
opts.on('-h', '--help', "Prints this help" ) do
|
271
|
+
puts opts
|
272
|
+
puts Morpheus::Cli::OptionTypes.display_option_types_help(update_user_option_types)
|
273
|
+
exit
|
274
|
+
end
|
213
275
|
end
|
214
276
|
optparse.parse(args)
|
215
277
|
|
216
278
|
if args.count < 1
|
217
|
-
puts "
|
279
|
+
puts "#{usage}\n\n"
|
280
|
+
puts Morpheus::Cli::OptionTypes.display_option_types_help(update_user_option_types)
|
218
281
|
exit 1
|
219
282
|
end
|
220
283
|
username = args[0]
|
@@ -231,16 +294,19 @@ class Morpheus::Cli::Users
|
|
231
294
|
|
232
295
|
#params = Morpheus::Cli::OptionTypes.prompt(update_user_option_types, options[:options], @api_client, options[:params])
|
233
296
|
params = options[:options] || {}
|
234
|
-
|
297
|
+
roles = prompt_user_roles(account_id, user['id'], options.merge(no_prompt: true))
|
298
|
+
if !roles.empty?
|
299
|
+
params['roles'] = roles.collect {|r| {id: r['id']} }
|
300
|
+
end
|
301
|
+
|
235
302
|
if params.empty?
|
236
303
|
puts "\n#{usage}\n\n"
|
237
|
-
|
238
|
-
puts "\nAvailable Options:\n#{option_lines}\n\n"
|
304
|
+
puts Morpheus::Cli::OptionTypes.display_option_types_help(update_user_option_types)
|
239
305
|
exit 1
|
240
306
|
end
|
241
307
|
|
242
308
|
#puts "parsed params is : #{params.inspect}"
|
243
|
-
user_keys = ['username', 'firstName', 'lastName', 'email', 'password', 'instanceLimits']
|
309
|
+
user_keys = ['username', 'firstName', 'lastName', 'email', 'password', 'instanceLimits', 'roles']
|
244
310
|
user_payload = params.select {|k,v| user_keys.include?(k) }
|
245
311
|
if !user_payload['instanceLimits']
|
246
312
|
user_payload['instanceLimits'] = {}
|
@@ -248,11 +314,7 @@ class Morpheus::Cli::Users
|
|
248
314
|
user_payload['instanceLimits']['maxMemory'] = params['instanceLimits.maxMemory'].to_i if params['instanceLimits.maxMemory'].to_s.strip != ''
|
249
315
|
user_payload['instanceLimits']['maxCpu'] = params['instanceLimits.maxCpu'].to_i if params['instanceLimits.maxCpu'].to_s.strip != ''
|
250
316
|
end
|
251
|
-
|
252
|
-
role = find_role_by_name(account_id, params['role'])
|
253
|
-
exit 1 if role.nil?
|
254
|
-
user_payload['role'] = {id: role['id']}
|
255
|
-
end
|
317
|
+
|
256
318
|
request_payload = {user: user_payload}
|
257
319
|
json_response = @users_interface.update(account_id, user['id'], request_payload)
|
258
320
|
|
@@ -320,21 +382,101 @@ private
|
|
320
382
|
|
321
383
|
def add_user_option_types
|
322
384
|
[
|
323
|
-
{'fieldName' => '
|
324
|
-
{'fieldName' => '
|
325
|
-
{'fieldName' => '
|
385
|
+
{'fieldName' => 'firstName', 'fieldLabel' => 'First Name', 'type' => 'text', 'required' => false, 'displayOrder' => 1},
|
386
|
+
{'fieldName' => 'lastName', 'fieldLabel' => 'Last Name', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
387
|
+
{'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'required' => true, 'displayOrder' => 3},
|
326
388
|
{'fieldName' => 'email', 'fieldLabel' => 'Email', 'type' => 'text', 'required' => true, 'displayOrder' => 4},
|
327
|
-
{'fieldName' => 'role', 'fieldLabel' => 'Role', 'type' => 'text', 'displayOrder' => 5},
|
328
389
|
{'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'required' => true, 'displayOrder' => 6},
|
329
390
|
{'fieldName' => 'passwordConfirmation', 'fieldLabel' => 'Confirm Password', 'type' => 'password', 'required' => true, 'displayOrder' => 7},
|
330
391
|
{'fieldName' => 'instanceLimits.maxStorage', 'fieldLabel' => 'Max Storage (bytes)', 'type' => 'text', 'displayOrder' => 8},
|
331
392
|
{'fieldName' => 'instanceLimits.maxMemory', 'fieldLabel' => 'Max Memory (bytes)', 'type' => 'text', 'displayOrder' => 9},
|
332
393
|
{'fieldName' => 'instanceLimits.maxCpu', 'fieldLabel' => 'CPU Count', 'type' => 'text', 'displayOrder' => 10},
|
394
|
+
{'fieldName' => 'role', 'fieldLabel' => 'Role', 'type' => 'text', 'displayOrder' => 11, 'description' => "Role names (comma separated)"},
|
333
395
|
]
|
334
396
|
end
|
335
397
|
|
336
398
|
def update_user_option_types
|
337
|
-
|
399
|
+
[
|
400
|
+
{'fieldName' => 'firstName', 'fieldLabel' => 'First Name', 'type' => 'text', 'required' => false, 'displayOrder' => 1},
|
401
|
+
{'fieldName' => 'lastName', 'fieldLabel' => 'Last Name', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
402
|
+
{'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'required' => false, 'displayOrder' => 3},
|
403
|
+
{'fieldName' => 'email', 'fieldLabel' => 'Email', 'type' => 'text', 'required' => false, 'displayOrder' => 4},
|
404
|
+
{'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'required' => false, 'displayOrder' => 6},
|
405
|
+
{'fieldName' => 'passwordConfirmation', 'fieldLabel' => 'Confirm Password', 'type' => 'password', 'required' => false, 'displayOrder' => 7},
|
406
|
+
{'fieldName' => 'instanceLimits.maxStorage', 'fieldLabel' => 'Max Storage (bytes)', 'type' => 'text', 'displayOrder' => 8},
|
407
|
+
{'fieldName' => 'instanceLimits.maxMemory', 'fieldLabel' => 'Max Memory (bytes)', 'type' => 'text', 'displayOrder' => 9},
|
408
|
+
{'fieldName' => 'instanceLimits.maxCpu', 'fieldLabel' => 'CPU Count', 'type' => 'text', 'displayOrder' => 10},
|
409
|
+
{'fieldName' => 'role', 'fieldLabel' => 'Role', 'type' => 'text', 'displayOrder' => 11, 'description' => "Role names (comma separated)"},
|
410
|
+
]
|
411
|
+
end
|
412
|
+
|
413
|
+
# prompt user to select roles for a new or existing user
|
414
|
+
# options['role'] can be passed as comma separated role names
|
415
|
+
# if so, it will be used instead of prompting
|
416
|
+
# returns array of role objects
|
417
|
+
def prompt_user_roles(account_id, user_id, options={})
|
418
|
+
|
419
|
+
passed_role_string = nil
|
420
|
+
if options['role'] || (options[:options] && (options[:options]['role'] || options[:options]['roles']))
|
421
|
+
passed_role_string = options['role'] || (options[:options] && (options[:options]['role'] || options[:options]['roles']))
|
422
|
+
end
|
423
|
+
passed_role_names = []
|
424
|
+
if !passed_role_string.empty?
|
425
|
+
passed_role_names = passed_role_string.split(',').uniq.compact.collect {|r| r.strip}
|
426
|
+
end
|
427
|
+
|
428
|
+
available_roles = @users_interface.available_roles(account_id, user_id)['roles']
|
429
|
+
|
430
|
+
if available_roles.empty?
|
431
|
+
print_red_alert "No available roles found."
|
432
|
+
exit 1
|
433
|
+
end
|
434
|
+
role_options = available_roles.collect {|role|
|
435
|
+
{'name' => role['authority'], 'value' => role['id']}
|
436
|
+
}
|
437
|
+
|
438
|
+
# found_roles = []
|
439
|
+
roles = []
|
440
|
+
|
441
|
+
if !passed_role_names.empty?
|
442
|
+
invalid_role_names = []
|
443
|
+
passed_role_names.each do |role_name|
|
444
|
+
found_role = available_roles.find {|ar| ar['authority'] == role_name}
|
445
|
+
if found_role
|
446
|
+
# found_roles << found_role
|
447
|
+
roles << found_role
|
448
|
+
else
|
449
|
+
invalid_role_names << role_name
|
450
|
+
end
|
451
|
+
end
|
452
|
+
if !invalid_role_names.empty?
|
453
|
+
print_red_alert "Invalid Roles: #{invalid_role_names.join(', ')}"
|
454
|
+
exit 1
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
if roles.empty?
|
459
|
+
no_prompt = (options[:no_prompt] || (options[:options] && options[:options][:no_prompt]))
|
460
|
+
if !no_prompt
|
461
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'roleId', 'fieldLabel' => 'Role', 'type' => 'select', 'selectOptions' => role_options, 'required' => true}], options[:options])
|
462
|
+
role_id = v_prompt['roleId']
|
463
|
+
roles << available_roles.find {|r| r['id'].to_i == role_id.to_i }
|
464
|
+
add_another_role = true
|
465
|
+
while add_another_role do
|
466
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'roleId', 'fieldLabel' => 'Another Role', 'type' => 'select', 'selectOptions' => role_options, 'required' => false}], options[:options])
|
467
|
+
if v_prompt['roleId'].to_s.empty?
|
468
|
+
add_another_role = false
|
469
|
+
else
|
470
|
+
role_id = v_prompt['roleId']
|
471
|
+
roles << available_roles.find {|r| r['id'].to_i == role_id.to_i }
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
roles = roles.compact
|
478
|
+
return roles
|
479
|
+
|
338
480
|
end
|
339
481
|
|
340
482
|
end
|