morpheus-cli 0.9.10 → 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|