morpheus-cli 3.5.2 → 3.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/morpheus/api/api_client.rb +16 -0
- data/lib/morpheus/api/blueprints_interface.rb +84 -0
- data/lib/morpheus/api/execution_request_interface.rb +33 -0
- data/lib/morpheus/api/instances_interface.rb +21 -0
- data/lib/morpheus/api/packages_interface.rb +25 -5
- data/lib/morpheus/api/processes_interface.rb +34 -0
- data/lib/morpheus/api/roles_interface.rb +7 -0
- data/lib/morpheus/api/servers_interface.rb +8 -0
- data/lib/morpheus/api/user_settings_interface.rb +76 -0
- data/lib/morpheus/cli.rb +5 -1
- data/lib/morpheus/cli/alias_command.rb +1 -1
- data/lib/morpheus/cli/app_templates.rb +2 -1
- data/lib/morpheus/cli/apps.rb +173 -19
- data/lib/morpheus/cli/blueprints_command.rb +2134 -0
- data/lib/morpheus/cli/cli_command.rb +3 -1
- data/lib/morpheus/cli/clouds.rb +4 -10
- data/lib/morpheus/cli/coloring_command.rb +14 -8
- data/lib/morpheus/cli/containers_command.rb +92 -5
- data/lib/morpheus/cli/execution_request_command.rb +313 -0
- data/lib/morpheus/cli/hosts.rb +188 -7
- data/lib/morpheus/cli/instances.rb +472 -9
- data/lib/morpheus/cli/login.rb +1 -1
- data/lib/morpheus/cli/mixins/print_helper.rb +8 -0
- data/lib/morpheus/cli/mixins/processes_helper.rb +134 -0
- data/lib/morpheus/cli/option_types.rb +21 -16
- data/lib/morpheus/cli/packages_command.rb +469 -17
- data/lib/morpheus/cli/processes_command.rb +313 -0
- data/lib/morpheus/cli/remote.rb +20 -9
- data/lib/morpheus/cli/roles.rb +186 -6
- data/lib/morpheus/cli/shell.rb +10 -1
- data/lib/morpheus/cli/tasks.rb +4 -1
- data/lib/morpheus/cli/user_settings_command.rb +431 -0
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/whoami.rb +1 -1
- data/lib/morpheus/formatters.rb +14 -0
- data/lib/morpheus/morpkg.rb +119 -0
- data/morpheus-cli.gemspec +1 -0
- metadata +26 -2
@@ -0,0 +1,313 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
require 'morpheus/cli/mixins/processes_helper'
|
3
|
+
|
4
|
+
class Morpheus::Cli::Processes
|
5
|
+
include Morpheus::Cli::CliCommand
|
6
|
+
include Morpheus::Cli::ProcessesHelper
|
7
|
+
|
8
|
+
set_command_name :'process'
|
9
|
+
|
10
|
+
register_subcommands :list, :get, {:'get-event' => :event_details}
|
11
|
+
|
12
|
+
# alias_subcommand :details, :get
|
13
|
+
# set_default_subcommand :list
|
14
|
+
|
15
|
+
def initialize()
|
16
|
+
#@appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
17
|
+
end
|
18
|
+
|
19
|
+
def connect(opts)
|
20
|
+
@api_client = establish_remote_appliance_connection(opts)
|
21
|
+
@processes_interface = @api_client.processes
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle(args)
|
25
|
+
handle_subcommand(args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def list(args)
|
29
|
+
params = {}
|
30
|
+
options = {}
|
31
|
+
#options[:show_output] = true
|
32
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
33
|
+
opts.banner = subcommand_usage()
|
34
|
+
opts.on( nil, '--events', "Display sub processes (events)." ) do
|
35
|
+
options[:show_events] = true
|
36
|
+
end
|
37
|
+
opts.on( nil, '--output', "Display process output." ) do
|
38
|
+
options[:show_output] = true
|
39
|
+
end
|
40
|
+
opts.on('--instance ID', String, "Limit results to specific instance(s).") do |val|
|
41
|
+
params['instanceIds'] = val.split(',').collect {|it| it.to_s.strip }.reject { |it| it.empty? }
|
42
|
+
end
|
43
|
+
opts.on('--container ID', String, "Limit results to specific container(s).") do |val|
|
44
|
+
params['containerIds'] = val.split(',').collect {|it| it.to_s.strip }.reject { |it| it.empty? }
|
45
|
+
end
|
46
|
+
opts.on('--host ID', String, "Limit results to specific host(s).") do |val|
|
47
|
+
params['serverIds'] = val.split(',').collect {|it| it.to_s.strip }.reject { |it| it.empty? }
|
48
|
+
end
|
49
|
+
opts.on('--cloud ID', String, "Limit results to specific cloud(s).") do |val|
|
50
|
+
params['zoneIds'] = val.split(',').collect {|it| it.to_s.strip }.reject { |it| it.empty? }
|
51
|
+
end
|
52
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
53
|
+
opts.footer = "List historical processes."
|
54
|
+
end
|
55
|
+
optparse.parse!(args)
|
56
|
+
|
57
|
+
if args.count != 0
|
58
|
+
puts optparse
|
59
|
+
return 1
|
60
|
+
end
|
61
|
+
connect(options)
|
62
|
+
begin
|
63
|
+
params.merge!(parse_list_options(options))
|
64
|
+
# params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
65
|
+
if options[:dry_run]
|
66
|
+
print_dry_run @processes_interface.dry.list(params)
|
67
|
+
return
|
68
|
+
end
|
69
|
+
json_response = @processes_interface.list(params)
|
70
|
+
if options[:json]
|
71
|
+
puts as_json(json_response, options, "processes")
|
72
|
+
return 0
|
73
|
+
elsif options[:yaml]
|
74
|
+
puts as_yaml(json_response, options, "processes")
|
75
|
+
return 0
|
76
|
+
elsif options[:csv]
|
77
|
+
puts records_as_csv(json_response['processes'], options)
|
78
|
+
return 0
|
79
|
+
else
|
80
|
+
|
81
|
+
title = "Process List"
|
82
|
+
subtitles = []
|
83
|
+
if params[:query]
|
84
|
+
subtitles << "Search: #{params[:query]}".strip
|
85
|
+
end
|
86
|
+
subtitles += parse_list_subtitles(options)
|
87
|
+
print_h1 title, subtitles
|
88
|
+
if json_response['processes'].empty?
|
89
|
+
print "#{cyan}No process history found.#{reset}\n\n"
|
90
|
+
return 0
|
91
|
+
else
|
92
|
+
history_records = []
|
93
|
+
json_response["processes"].each do |process|
|
94
|
+
row = {
|
95
|
+
id: process['id'],
|
96
|
+
eventId: nil,
|
97
|
+
uniqueId: process['uniqueId'],
|
98
|
+
name: process['displayName'],
|
99
|
+
description: process['description'],
|
100
|
+
processType: process['processType'] ? (process['processType']['name'] || process['processType']['code']) : process['processTypeName'],
|
101
|
+
createdBy: process['createdBy'] ? (process['createdBy']['displayName'] || process['createdBy']['username']) : '',
|
102
|
+
startDate: format_local_dt(process['startDate']),
|
103
|
+
duration: format_process_duration(process),
|
104
|
+
status: format_process_status(process),
|
105
|
+
error: format_process_error(process),
|
106
|
+
output: format_process_output(process)
|
107
|
+
}
|
108
|
+
history_records << row
|
109
|
+
process_events = process['events'] || process['processEvents']
|
110
|
+
if options[:show_events]
|
111
|
+
if process_events
|
112
|
+
process_events.each do |process_event|
|
113
|
+
event_row = {
|
114
|
+
id: process['id'],
|
115
|
+
eventId: process_event['id'],
|
116
|
+
uniqueId: process_event['uniqueId'],
|
117
|
+
name: process_event['displayName'], # blank like the UI
|
118
|
+
description: process_event['description'],
|
119
|
+
processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
|
120
|
+
createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
|
121
|
+
startDate: format_local_dt(process_event['startDate']),
|
122
|
+
duration: format_process_duration(process_event),
|
123
|
+
status: format_process_status(process_event),
|
124
|
+
error: format_process_error(process_event),
|
125
|
+
output: format_process_output(process_event)
|
126
|
+
}
|
127
|
+
history_records << event_row
|
128
|
+
end
|
129
|
+
else
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
columns = [
|
135
|
+
{:id => {:display_name => "PROCESS ID"} },
|
136
|
+
:name,
|
137
|
+
:description,
|
138
|
+
{:processType => {:display_name => "PROCESS TYPE"} },
|
139
|
+
{:createdBy => {:display_name => "CREATED BY"} },
|
140
|
+
{:startDate => {:display_name => "START DATE"} },
|
141
|
+
{:duration => {:display_name => "ETA/DURATION"} },
|
142
|
+
:status,
|
143
|
+
:error
|
144
|
+
]
|
145
|
+
if options[:show_events]
|
146
|
+
columns.insert(1, {:eventId => {:display_name => "EVENT ID"} })
|
147
|
+
end
|
148
|
+
if options[:show_output]
|
149
|
+
columns << :output
|
150
|
+
end
|
151
|
+
# custom pretty table columns ...
|
152
|
+
if options[:include_fields]
|
153
|
+
columns = options[:include_fields]
|
154
|
+
end
|
155
|
+
print cyan
|
156
|
+
print as_pretty_table(history_records, columns, options)
|
157
|
+
#print_results_pagination(json_response)
|
158
|
+
if options[:show_events]
|
159
|
+
print_results_pagination({size: history_records.size, total: history_records.size}, {:label => "process", :n_label => "processes"})
|
160
|
+
else
|
161
|
+
print_results_pagination(json_response, {:label => "process", :n_label => "processes"})
|
162
|
+
end
|
163
|
+
print reset, "\n"
|
164
|
+
return 0
|
165
|
+
end
|
166
|
+
end
|
167
|
+
rescue RestClient::Exception => e
|
168
|
+
print_rest_exception(e, options)
|
169
|
+
exit 1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def get(args)
|
174
|
+
options = {}
|
175
|
+
params = {}
|
176
|
+
process_id = nil
|
177
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
178
|
+
opts.banner = subcommand_usage("[id]")
|
179
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
180
|
+
opts.footer = "Display details for a specific process.\n"
|
181
|
+
"[id] is required. This is the id of the process."
|
182
|
+
end
|
183
|
+
optparse.parse!(args)
|
184
|
+
if args.count != 1
|
185
|
+
puts_error optparse
|
186
|
+
return 1
|
187
|
+
end
|
188
|
+
connect(options)
|
189
|
+
begin
|
190
|
+
process_id = args[0]
|
191
|
+
params.merge!(parse_list_options(options))
|
192
|
+
params[:query] = params.delete(:phrase) unless params[:phrase].nil?
|
193
|
+
if options[:dry_run]
|
194
|
+
print_dry_run @processes_interface.dry.get(process_id, params)
|
195
|
+
return
|
196
|
+
end
|
197
|
+
json_response = @processes_interface.get(process_id, params)
|
198
|
+
if options[:json]
|
199
|
+
puts as_json(json_response, options, "process")
|
200
|
+
return 0
|
201
|
+
elsif options[:yaml]
|
202
|
+
puts as_yaml(json_response, options, "process")
|
203
|
+
return 0
|
204
|
+
elsif options[:csv]
|
205
|
+
puts records_as_csv(json_response['process'], options)
|
206
|
+
return 0
|
207
|
+
else
|
208
|
+
process = json_response["process"]
|
209
|
+
title = "Process Details"
|
210
|
+
subtitles = []
|
211
|
+
subtitles << " Process ID: #{process_id}"
|
212
|
+
subtitles += parse_list_subtitles(options)
|
213
|
+
print_h1 title, subtitles
|
214
|
+
print_process_details(process)
|
215
|
+
|
216
|
+
print_h2 "Process Events"
|
217
|
+
process_events = process['events'] || process['processEvents'] || []
|
218
|
+
history_records = []
|
219
|
+
if process_events.empty?
|
220
|
+
puts "#{cyan}No events found.#{reset}"
|
221
|
+
else
|
222
|
+
process_events.each do |process_event|
|
223
|
+
event_row = {
|
224
|
+
id: process_event['id'],
|
225
|
+
eventId: process_event['id'],
|
226
|
+
uniqueId: process_event['uniqueId'],
|
227
|
+
name: process_event['displayName'], # blank like the UI
|
228
|
+
description: process_event['description'],
|
229
|
+
processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
|
230
|
+
createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
|
231
|
+
startDate: format_local_dt(process_event['startDate']),
|
232
|
+
duration: format_process_duration(process_event),
|
233
|
+
status: format_process_status(process_event),
|
234
|
+
error: format_process_error(process_event),
|
235
|
+
output: format_process_output(process_event)
|
236
|
+
}
|
237
|
+
history_records << event_row
|
238
|
+
end
|
239
|
+
columns = [
|
240
|
+
{:id => {:display_name => "EVENT ID"} },
|
241
|
+
:name,
|
242
|
+
:description,
|
243
|
+
{:processType => {:display_name => "PROCESS TYPE"} },
|
244
|
+
{:createdBy => {:display_name => "CREATED BY"} },
|
245
|
+
{:startDate => {:display_name => "START DATE"} },
|
246
|
+
{:duration => {:display_name => "ETA/DURATION"} },
|
247
|
+
:status,
|
248
|
+
:error,
|
249
|
+
:output
|
250
|
+
]
|
251
|
+
print cyan
|
252
|
+
print as_pretty_table(history_records, columns, options)
|
253
|
+
print_results_pagination({size: process_events.size, total: process_events.size})
|
254
|
+
print reset, "\n"
|
255
|
+
return 0
|
256
|
+
end
|
257
|
+
end
|
258
|
+
rescue RestClient::Exception => e
|
259
|
+
print_rest_exception(e, options)
|
260
|
+
exit 1
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def event_details(args)
|
265
|
+
options = {}
|
266
|
+
params = {}
|
267
|
+
process_event_id = nil
|
268
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
269
|
+
opts.banner = subcommand_usage("[event-id]")
|
270
|
+
build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
271
|
+
opts.footer = "Display details for a specific process event.\n" +
|
272
|
+
"[event-id] is required. This is the id of the process event."
|
273
|
+
end
|
274
|
+
optparse.parse!(args)
|
275
|
+
if args.count != 1
|
276
|
+
puts_error optparse
|
277
|
+
return 1
|
278
|
+
end
|
279
|
+
connect(options)
|
280
|
+
begin
|
281
|
+
process_event_id = args[0]
|
282
|
+
params.merge!(parse_list_options(options))
|
283
|
+
if options[:dry_run]
|
284
|
+
print_dry_run @processes_interface.dry.get_event(process_event_id, params)
|
285
|
+
return
|
286
|
+
end
|
287
|
+
json_response = @processes_interface.get_event(process_event_id, params)
|
288
|
+
if options[:json]
|
289
|
+
puts as_json(json_response, options, "processEvent")
|
290
|
+
return 0
|
291
|
+
elsif options[:yaml]
|
292
|
+
puts as_yaml(json_response, options, "processEvent")
|
293
|
+
return 0
|
294
|
+
elsif options[:csv]
|
295
|
+
puts records_as_csv(json_response['processEvent'], options)
|
296
|
+
return 0
|
297
|
+
else
|
298
|
+
process_event = json_response['processEvent'] || json_response['event']
|
299
|
+
title = "Process Event Details"
|
300
|
+
subtitles = []
|
301
|
+
subtitles += parse_list_subtitles(options)
|
302
|
+
print_h1 title, subtitles
|
303
|
+
print_process_event_details(process_event)
|
304
|
+
print reset, "\n"
|
305
|
+
return 0
|
306
|
+
end
|
307
|
+
rescue RestClient::Exception => e
|
308
|
+
print_rest_exception(e, options)
|
309
|
+
exit 1
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
end
|
data/lib/morpheus/cli/remote.rb
CHANGED
@@ -75,7 +75,7 @@ EOT
|
|
75
75
|
columns = [
|
76
76
|
{:active => {:display_name => "", :display_method => lambda {|it| it[:active] ? "=>" : "" } } },
|
77
77
|
{:name => {:width => 16} },
|
78
|
-
{:
|
78
|
+
{:url => {display_method: lambda {|it| it[:host] || it[:url] }, :width => 40 } },
|
79
79
|
{:version => lambda {|it| it[:build_version] } },
|
80
80
|
{:status => lambda {|it| format_appliance_status(it, cyan) } },
|
81
81
|
:username,
|
@@ -234,6 +234,9 @@ EOT
|
|
234
234
|
if ::Morpheus::Cli::OptionTypes::confirm("Would you like to login now?", options.merge({default: true}))
|
235
235
|
login_result = ::Morpheus::Cli::Login.new.handle(["--remote", appliance[:name].to_s])
|
236
236
|
keep_trying = true
|
237
|
+
if login_result == 0
|
238
|
+
keep_trying = false
|
239
|
+
end
|
237
240
|
while keep_trying do
|
238
241
|
if ::Morpheus::Cli::OptionTypes::confirm("Login was unsuccessful. Would you like to try again?", options.merge({default: true}))
|
239
242
|
login_result = ::Morpheus::Cli::Login.new.handle(["--remote", appliance[:name].to_s])
|
@@ -246,6 +249,13 @@ EOT
|
|
246
249
|
end
|
247
250
|
|
248
251
|
end
|
252
|
+
|
253
|
+
if !appliance[:active]
|
254
|
+
if ::Morpheus::Cli::OptionTypes::confirm("Would you like to switch to using this remote now?", options.merge({default: true}))
|
255
|
+
use([appliance[:name]])
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
249
259
|
else
|
250
260
|
#puts "Status is #{format_appliance_status(appliance)}"
|
251
261
|
end
|
@@ -487,7 +497,7 @@ EOT
|
|
487
497
|
print cyan
|
488
498
|
description_cols = {
|
489
499
|
"Name" => :name,
|
490
|
-
"
|
500
|
+
"URL" => :host,
|
491
501
|
"Secure" => lambda {|it| format_appliance_secure(it) },
|
492
502
|
"Version" => lambda {|it| it[:build_version] ? "#{it[:build_version]}" : 'unknown' },
|
493
503
|
"Status" => lambda {|it| format_appliance_status(it, cyan) },
|
@@ -507,11 +517,10 @@ EOT
|
|
507
517
|
|
508
518
|
if appliance[:active]
|
509
519
|
# print cyan
|
510
|
-
print cyan, "
|
520
|
+
print cyan, " => This is the current appliance.", reset, "\n\n"
|
511
521
|
end
|
512
522
|
|
513
|
-
|
514
|
-
|
523
|
+
return 0
|
515
524
|
rescue RestClient::Exception => e
|
516
525
|
print_rest_exception(e, options)
|
517
526
|
exit 1
|
@@ -865,7 +874,8 @@ EOT
|
|
865
874
|
def format_appliance_secure(app_map, return_color=cyan)
|
866
875
|
return "" if !app_map
|
867
876
|
out = ""
|
868
|
-
|
877
|
+
app_url = (app_map[:host] || app_map[:url]).to_s
|
878
|
+
is_ssl = app_url =~ /^https/
|
869
879
|
if !is_ssl
|
870
880
|
out << "No (no SSL)"
|
871
881
|
else
|
@@ -960,10 +970,11 @@ EOT
|
|
960
970
|
return nil, nil
|
961
971
|
end
|
962
972
|
app_name, app_map = self.appliances.find {|k,v| v[:active] == true }
|
973
|
+
app_url = app_map ? (app_map[:host] || app_map[:url]).to_s : nil
|
963
974
|
if app_name
|
964
|
-
return app_name,
|
975
|
+
return app_name, app_url
|
965
976
|
else
|
966
|
-
return
|
977
|
+
return nil, nil
|
967
978
|
end
|
968
979
|
end
|
969
980
|
|
@@ -1141,7 +1152,7 @@ EOT
|
|
1141
1152
|
app_name = app_name.to_sym
|
1142
1153
|
cur_appliances = self.appliances
|
1143
1154
|
app_map = cur_appliances[app_name] || {}
|
1144
|
-
app_url = app_map[:host]
|
1155
|
+
app_url = (app_map[:host] || app_map[:url]).to_s
|
1145
1156
|
|
1146
1157
|
if !app_url
|
1147
1158
|
raise "appliance config is missing url!" # should not need this
|
data/lib/morpheus/cli/roles.rb
CHANGED
@@ -10,7 +10,7 @@ require 'json'
|
|
10
10
|
class Morpheus::Cli::Roles
|
11
11
|
include Morpheus::Cli::CliCommand
|
12
12
|
include Morpheus::Cli::AccountsHelper
|
13
|
-
register_subcommands :list, :get, :add, :update, :remove, :'update-feature-access', :'update-global-group-access', :'update-group-access', :'update-global-cloud-access', :'update-cloud-access', :'update-global-instance-type-access', :'update-instance-type-access'
|
13
|
+
register_subcommands :list, :get, :add, :update, :remove, :'update-feature-access', :'update-global-group-access', :'update-group-access', :'update-global-cloud-access', :'update-cloud-access', :'update-global-instance-type-access', :'update-instance-type-access', :'update-global-blueprint-access', :'update-blueprint-access'
|
14
14
|
alias_subcommand :details, :get
|
15
15
|
set_default_subcommand :list
|
16
16
|
|
@@ -24,6 +24,7 @@ class Morpheus::Cli::Roles
|
|
24
24
|
@options_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).options
|
25
25
|
#@clouds_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
|
26
26
|
@instance_types_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).instance_types
|
27
|
+
@blueprints_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).blueprints
|
27
28
|
@active_group_id = Morpheus::Cli::Groups.active_group
|
28
29
|
end
|
29
30
|
|
@@ -87,23 +88,27 @@ class Morpheus::Cli::Roles
|
|
87
88
|
options = {}
|
88
89
|
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
89
90
|
opts.banner = subcommand_usage("[name]")
|
90
|
-
opts.on(
|
91
|
+
opts.on('-f','--feature-access', "Display Feature Access") do |val|
|
91
92
|
options[:include_feature_access] = true
|
92
93
|
end
|
93
|
-
opts.on(
|
94
|
+
opts.on('-g','--group-access', "Display Group Access") do
|
94
95
|
options[:include_group_access] = true
|
95
96
|
end
|
96
|
-
opts.on(
|
97
|
+
opts.on('-c','--cloud-access', "Display Cloud Access") do
|
97
98
|
options[:include_cloud_access] = true
|
98
99
|
end
|
99
|
-
opts.on(
|
100
|
+
opts.on('-i','--instance-type-access', "Display Instance Type Access") do
|
100
101
|
options[:include_instance_type_access] = true
|
101
102
|
end
|
102
|
-
opts.on(
|
103
|
+
opts.on('-b','--blueprint-access', "Display Blueprint Access") do
|
104
|
+
options[:include_blueprint_access] = true
|
105
|
+
end
|
106
|
+
opts.on('-a','--all-access', "Display All Access Lists") do
|
103
107
|
options[:include_feature_access] = true
|
104
108
|
options[:include_group_access] = true
|
105
109
|
options[:include_cloud_access] = true
|
106
110
|
options[:include_instance_type_access] = true
|
111
|
+
options[:include_blueprint_access] = true
|
107
112
|
end
|
108
113
|
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
109
114
|
opts.footer = "Get details about a role.\n" +
|
@@ -250,6 +255,25 @@ class Morpheus::Cli::Roles
|
|
250
255
|
end
|
251
256
|
end
|
252
257
|
|
258
|
+
blueprint_global_access = json_response['globalAppTemplateAccess'] || json_response['globalBlueprintAccess']
|
259
|
+
blueprint_permissions = json_response['appTemplatePermissions'] || json_response['blueprintPermissions'] || []
|
260
|
+
print_h2 "Blueprint Access"
|
261
|
+
print cyan
|
262
|
+
puts "Global Blueprint Access: #{get_access_string(json_response['globalAppTemplateAccess'])}\n\n"
|
263
|
+
if blueprint_global_access == 'custom'
|
264
|
+
if options[:include_blueprint_access]
|
265
|
+
rows = blueprint_permissions.collect do |it|
|
266
|
+
{
|
267
|
+
name: it['name'],
|
268
|
+
access: get_access_string(it['access']),
|
269
|
+
}
|
270
|
+
end
|
271
|
+
tp rows, [:name, :access]
|
272
|
+
else
|
273
|
+
puts "Use --blueprint-access to list custom access"
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
253
277
|
print reset,"\n"
|
254
278
|
return 0
|
255
279
|
rescue RestClient::Exception => e
|
@@ -971,6 +995,162 @@ class Morpheus::Cli::Roles
|
|
971
995
|
end
|
972
996
|
end
|
973
997
|
|
998
|
+
def update_global_blueprint_access(args)
|
999
|
+
usage = "Usage: morpheus roles update-global-blueprint-access [name] [full|custom|none]"
|
1000
|
+
options = {}
|
1001
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1002
|
+
opts.banner = subcommand_usage("[name] [full|custom|none]")
|
1003
|
+
build_common_options(opts, options, [:json, :dry_run, :remote])
|
1004
|
+
end
|
1005
|
+
optparse.parse!(args)
|
1006
|
+
|
1007
|
+
if args.count < 2
|
1008
|
+
puts optparse
|
1009
|
+
exit 1
|
1010
|
+
end
|
1011
|
+
name = args[0]
|
1012
|
+
access_value = args[1].to_s.downcase
|
1013
|
+
if !['full', 'custom', 'none'].include?(access_value)
|
1014
|
+
puts optparse
|
1015
|
+
exit 1
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
|
1019
|
+
connect(options)
|
1020
|
+
begin
|
1021
|
+
account = find_account_from_options(options)
|
1022
|
+
account_id = account ? account['id'] : nil
|
1023
|
+
role = find_role_by_name_or_id(account_id, name)
|
1024
|
+
exit 1 if role.nil?
|
1025
|
+
|
1026
|
+
params = {permissionCode: 'AppTemplate', access: access_value}
|
1027
|
+
if options[:dry_run]
|
1028
|
+
print_dry_run @roles_interface.dry.update_permission(account_id, role['id'], params)
|
1029
|
+
return
|
1030
|
+
end
|
1031
|
+
json_response = @roles_interface.update_permission(account_id, role['id'], params)
|
1032
|
+
|
1033
|
+
if options[:json]
|
1034
|
+
print JSON.pretty_generate(json_response)
|
1035
|
+
print "\n"
|
1036
|
+
else
|
1037
|
+
print_green_success "Role #{role['authority']} global blueprint access updated"
|
1038
|
+
end
|
1039
|
+
rescue RestClient::Exception => e
|
1040
|
+
print_rest_exception(e, options)
|
1041
|
+
exit 1
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
def update_blueprint_access(args)
|
1046
|
+
options = {}
|
1047
|
+
blueprint_id = nil
|
1048
|
+
access_value = nil
|
1049
|
+
do_all = false
|
1050
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1051
|
+
opts.banner = subcommand_usage("[name]")
|
1052
|
+
opts.on( '--blueprint ID', String, "Blueprint ID or Name" ) do |val|
|
1053
|
+
blueprint_id = val
|
1054
|
+
end
|
1055
|
+
opts.on( nil, '--all', "Update all blueprints at once." ) do
|
1056
|
+
do_all = true
|
1057
|
+
end
|
1058
|
+
opts.on( '--access VALUE', String, "Access value [full|read|none]" ) do |val|
|
1059
|
+
access_value = val
|
1060
|
+
end
|
1061
|
+
build_common_options(opts, options, [:json, :dry_run, :remote])
|
1062
|
+
opts.footer = "Update role access for an blueprint or all blueprints.\n" +
|
1063
|
+
"[name] is required. This is the name or id of a role.\n" +
|
1064
|
+
"--blueprint or --all is required. This is the name or id of a blueprint.\n" +
|
1065
|
+
"--access is required. This is the new access value."
|
1066
|
+
end
|
1067
|
+
optparse.parse!(args)
|
1068
|
+
|
1069
|
+
if args.count < 1
|
1070
|
+
puts optparse
|
1071
|
+
return 1
|
1072
|
+
end
|
1073
|
+
name = args[0]
|
1074
|
+
# support old usage: [name] [blueprint] [access]
|
1075
|
+
blueprint_id ||= args[1]
|
1076
|
+
access_value ||= args[2]
|
1077
|
+
|
1078
|
+
if (!blueprint_id && !do_all) || !access_value
|
1079
|
+
puts_error optparse
|
1080
|
+
return 1
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
access_value = access_value.to_s.downcase
|
1084
|
+
|
1085
|
+
if !['full', 'none'].include?(access_value)
|
1086
|
+
puts optparse
|
1087
|
+
return 1
|
1088
|
+
end
|
1089
|
+
|
1090
|
+
connect(options)
|
1091
|
+
begin
|
1092
|
+
account = find_account_from_options(options)
|
1093
|
+
account_id = account ? account['id'] : nil
|
1094
|
+
role = find_role_by_name_or_id(account_id, name)
|
1095
|
+
return 1 if role.nil?
|
1096
|
+
|
1097
|
+
role_json = @roles_interface.get(account_id, role['id'])
|
1098
|
+
blueprint_global_access = role_json['globalAppTemplateAccess'] || role_json['globalBlueprintAccess']
|
1099
|
+
blueprint_permissions = role_json['appTemplatePermissions'] || role_json['blueprintPermissions'] || []
|
1100
|
+
if blueprint_global_access != 'custom'
|
1101
|
+
print "\n", red, "Global Blueprint Access is currently: #{blueprint_global_access.to_s.capitalize}"
|
1102
|
+
print "\n", "You must first set it to Custom via `morpheus roles update-global-blueprint-access \"#{name}\" custom`"
|
1103
|
+
print "\n\n", reset
|
1104
|
+
return 1
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
# hacky, but support name or code lookup via the list returned in the show payload
|
1108
|
+
blueprint = nil
|
1109
|
+
if !do_all
|
1110
|
+
if blueprint_id.to_s =~ /\A\d{1,}\Z/
|
1111
|
+
blueprint = blueprint_permissions.find {|b| b['id'] == blueprint_id.to_i }
|
1112
|
+
else
|
1113
|
+
blueprint = blueprint_permissions.find {|b| b['name'] == blueprint_id || b['code'] == blueprint_id }
|
1114
|
+
end
|
1115
|
+
if blueprint.nil?
|
1116
|
+
print_red_alert "Blueprint not found: '#{blueprint_id}'"
|
1117
|
+
return 1
|
1118
|
+
end
|
1119
|
+
end
|
1120
|
+
|
1121
|
+
params = {}
|
1122
|
+
if do_all
|
1123
|
+
params['allAppTemplates'] = true
|
1124
|
+
#params['allBlueprints'] = true
|
1125
|
+
else
|
1126
|
+
params['appTemplateId'] = blueprint['id']
|
1127
|
+
# params['blueprintId'] = blueprint['id']
|
1128
|
+
end
|
1129
|
+
params['access'] = access_value
|
1130
|
+
|
1131
|
+
if options[:dry_run]
|
1132
|
+
print_dry_run @roles_interface.dry.update_blueprint(account_id, role['id'], params)
|
1133
|
+
return
|
1134
|
+
end
|
1135
|
+
json_response = @roles_interface.update_blueprint(account_id, role['id'], params)
|
1136
|
+
|
1137
|
+
if options[:json]
|
1138
|
+
print JSON.pretty_generate(json_response)
|
1139
|
+
print "\n"
|
1140
|
+
else
|
1141
|
+
if do_all
|
1142
|
+
print_green_success "Role #{role['authority']} access updated for all blueprints"
|
1143
|
+
else
|
1144
|
+
print_green_success "Role #{role['authority']} access updated for blueprint #{blueprint['name']}"
|
1145
|
+
end
|
1146
|
+
end
|
1147
|
+
return 0
|
1148
|
+
rescue RestClient::Exception => e
|
1149
|
+
print_rest_exception(e, options)
|
1150
|
+
exit 1
|
1151
|
+
end
|
1152
|
+
end
|
1153
|
+
|
974
1154
|
private
|
975
1155
|
# def get_access_string(val)
|
976
1156
|
# val ||= 'none'
|