morpheus-cli 3.3.1.4 → 3.3.2
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/lib/morpheus/api/api_client.rb +28 -0
- data/lib/morpheus/api/instance_types_interface.rb +12 -10
- data/lib/morpheus/api/instances_interface.rb +4 -0
- data/lib/morpheus/api/library_container_scripts_interface.rb +49 -0
- data/lib/morpheus/api/library_container_templates_interface.rb +49 -0
- data/lib/morpheus/api/library_container_types_interface.rb +65 -0
- data/lib/morpheus/api/library_container_upgrades_interface.rb +66 -0
- data/lib/morpheus/api/library_instance_types_interface.rb +59 -0
- data/lib/morpheus/api/library_layouts_interface.rb +65 -0
- data/lib/morpheus/api/servers_interface.rb +4 -0
- data/lib/morpheus/api/user_sources_interface.rb +120 -0
- data/lib/morpheus/api/virtual_images_interface.rb +7 -0
- data/lib/morpheus/cli.rb +12 -1
- data/lib/morpheus/cli/accounts.rb +35 -9
- data/lib/morpheus/cli/cli_command.rb +82 -2
- data/lib/morpheus/cli/curl_command.rb +1 -1
- data/lib/morpheus/cli/echo_command.rb +1 -1
- data/lib/morpheus/cli/hosts.rb +40 -14
- data/lib/morpheus/cli/instance_types.rb +106 -64
- data/lib/morpheus/cli/instances.rb +39 -15
- data/lib/morpheus/cli/library.rb +1 -1184
- data/lib/morpheus/cli/library_container_scripts_command.rb +437 -0
- data/lib/morpheus/cli/library_container_templates_command.rb +397 -0
- data/lib/morpheus/cli/library_container_types_command.rb +653 -0
- data/lib/morpheus/cli/library_instance_types_command.rb +491 -0
- data/lib/morpheus/cli/library_layouts_command.rb +650 -0
- data/lib/morpheus/cli/library_option_lists_command.rb +476 -0
- data/lib/morpheus/cli/library_option_types_command.rb +549 -0
- data/lib/morpheus/cli/library_upgrades_command.rb +604 -0
- data/lib/morpheus/cli/mixins/library_helper.rb +123 -0
- data/lib/morpheus/cli/mixins/print_helper.rb +21 -22
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +56 -11
- data/lib/morpheus/cli/network_services_command.rb +1 -1
- data/lib/morpheus/cli/option_types.rb +12 -2
- data/lib/morpheus/cli/power_scheduling_command.rb +1 -1
- data/lib/morpheus/cli/shell.rb +120 -22
- data/lib/morpheus/cli/sleep_command.rb +45 -0
- data/lib/morpheus/cli/user_sources_command.rb +963 -0
- data/lib/morpheus/cli/users.rb +33 -2
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/cli/version_command.rb +1 -1
- data/lib/morpheus/cli/virtual_images.rb +93 -39
- data/lib/morpheus/formatters.rb +37 -27
- data/lib/morpheus/terminal.rb +1 -1
- metadata +20 -2
@@ -0,0 +1,437 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
|
3
|
+
class Morpheus::Cli::LibraryContainerScriptsCommand
|
4
|
+
include Morpheus::Cli::CliCommand
|
5
|
+
|
6
|
+
set_command_name :'library-scripts'
|
7
|
+
|
8
|
+
register_subcommands :list, :get, :add, :update, :remove
|
9
|
+
|
10
|
+
def connect(opts)
|
11
|
+
@api_client = establish_remote_appliance_connection(opts)
|
12
|
+
@container_scripts_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).library_container_scripts
|
13
|
+
end
|
14
|
+
|
15
|
+
def handle(args)
|
16
|
+
handle_subcommand(args)
|
17
|
+
end
|
18
|
+
|
19
|
+
def list(args)
|
20
|
+
options = {}
|
21
|
+
params = {}
|
22
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
23
|
+
opts.banner = subcommand_usage()
|
24
|
+
build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
25
|
+
opts.footer = "List container scripts."
|
26
|
+
end
|
27
|
+
optparse.parse!(args)
|
28
|
+
connect(options)
|
29
|
+
if args.count > 0
|
30
|
+
print_error Morpheus::Terminal.angry_prompt
|
31
|
+
puts_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
32
|
+
return 1
|
33
|
+
end
|
34
|
+
begin
|
35
|
+
# construct payload
|
36
|
+
params.merge!(parse_list_options(options))
|
37
|
+
# dry run?
|
38
|
+
if options[:dry_run]
|
39
|
+
print_dry_run @container_scripts_interface.dry.list(params)
|
40
|
+
return
|
41
|
+
end
|
42
|
+
# do it
|
43
|
+
json_response = @container_scripts_interface.list(params)
|
44
|
+
container_scripts = json_response['containerScripts']
|
45
|
+
if options[:include_fields]
|
46
|
+
json_response = {"containerScripts" => filter_data(json_response["containerScripts"], options[:include_fields]) }
|
47
|
+
end
|
48
|
+
# print result and return output
|
49
|
+
if options[:json]
|
50
|
+
puts as_json(json_response, options)
|
51
|
+
return 0
|
52
|
+
elsif options[:csv]
|
53
|
+
puts records_as_csv(json_response['containerScripts'], options)
|
54
|
+
return 0
|
55
|
+
elsif options[:yaml]
|
56
|
+
puts as_yaml(json_response, options)
|
57
|
+
return 0
|
58
|
+
end
|
59
|
+
container_scripts = json_response['containerScripts']
|
60
|
+
title = "Morpheus Library - Scripts"
|
61
|
+
subtitles = []
|
62
|
+
subtitles += parse_list_subtitles(options)
|
63
|
+
print_h1 title, subtitles
|
64
|
+
if container_scripts.empty?
|
65
|
+
print cyan,"No container scripts found.",reset,"\n"
|
66
|
+
else
|
67
|
+
print_container_scripts_table(container_scripts, options)
|
68
|
+
print_results_pagination(json_response, {:label => "container script", :n_label => "container scripts"})
|
69
|
+
end
|
70
|
+
print reset,"\n"
|
71
|
+
rescue RestClient::Exception => e
|
72
|
+
print_rest_exception(e, options)
|
73
|
+
return 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def get(args)
|
78
|
+
options = {}
|
79
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
80
|
+
opts.banner = subcommand_usage("[name]")
|
81
|
+
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
82
|
+
end
|
83
|
+
optparse.parse!(args)
|
84
|
+
if args.count < 1
|
85
|
+
puts optparse
|
86
|
+
return 1
|
87
|
+
end
|
88
|
+
connect(options)
|
89
|
+
id_list = parse_id_list(args)
|
90
|
+
return run_command_for_each_arg(id_list) do |arg|
|
91
|
+
_get(arg, options)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def _get(id, options)
|
96
|
+
|
97
|
+
begin
|
98
|
+
container_script = find_container_script_by_name_or_id(id)
|
99
|
+
if container_script.nil?
|
100
|
+
return 1
|
101
|
+
end
|
102
|
+
if options[:dry_run]
|
103
|
+
print_dry_run @container_scripts_interface.dry.get(container_script['id'])
|
104
|
+
return
|
105
|
+
end
|
106
|
+
json_response = @container_scripts_interface.get(container_script['id'])
|
107
|
+
container_script = json_response['containerScript']
|
108
|
+
instances = json_response['instances'] || []
|
109
|
+
servers = json_response['servers'] || []
|
110
|
+
if options[:include_fields]
|
111
|
+
json_response = {"containerScript" => filter_data(json_response["containerScript"], options[:include_fields]) }
|
112
|
+
end
|
113
|
+
if options[:json]
|
114
|
+
puts as_json(json_response, options)
|
115
|
+
return 0
|
116
|
+
elsif options[:yaml]
|
117
|
+
puts as_yaml(json_response, options)
|
118
|
+
return 0
|
119
|
+
elsif options[:csv]
|
120
|
+
puts records_as_csv([json_response['containerScript']], options)
|
121
|
+
return 0
|
122
|
+
end
|
123
|
+
|
124
|
+
print_h1 "Container Script Details"
|
125
|
+
print cyan
|
126
|
+
description_cols = {
|
127
|
+
"ID" => lambda {|it| it['id'] },
|
128
|
+
"Name" => lambda {|it| it['name'] },
|
129
|
+
"Type" => lambda {|it| format_container_script_type(it['scriptType']) },
|
130
|
+
"Phase" => lambda {|it| format_container_script_phase(it['scriptPhase']) },
|
131
|
+
"Owner" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
132
|
+
# "Enabled" => lambda {|it| format_boolean it['enabled'] },
|
133
|
+
# "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
134
|
+
# "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
135
|
+
}
|
136
|
+
print_description_list(description_cols, container_script)
|
137
|
+
|
138
|
+
print_h2 "Script"
|
139
|
+
|
140
|
+
puts container_script['script']
|
141
|
+
|
142
|
+
|
143
|
+
|
144
|
+
print reset,"\n"
|
145
|
+
|
146
|
+
rescue RestClient::Exception => e
|
147
|
+
print_rest_exception(e, options)
|
148
|
+
return 1
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def add(args)
|
153
|
+
options = {}
|
154
|
+
params = {'scriptType' => 'bash', 'scriptPhase' => 'postProvision'}
|
155
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
156
|
+
opts.banner = subcommand_usage("[name]")
|
157
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
158
|
+
params['name'] = val
|
159
|
+
end
|
160
|
+
opts.on('--type [bash|powershell]', String, "Script Type. Default is 'bash'") do |val|
|
161
|
+
params['scriptType'] = val
|
162
|
+
end
|
163
|
+
opts.on('--phase [start|stop|postProvision]', String, "Script Phase. Default is 'postProvision'") do |val|
|
164
|
+
params['scriptPhase'] = val
|
165
|
+
end
|
166
|
+
opts.on('--category VALUE', String, "Category") do |val|
|
167
|
+
params['category'] = val
|
168
|
+
end
|
169
|
+
opts.on('--script TEXT', String, "Contents of the script.") do |val|
|
170
|
+
params['script'] = val
|
171
|
+
end
|
172
|
+
opts.on('--file FILE', "File containing the script. This can be used instead --script" ) do |filename|
|
173
|
+
full_filename = File.expand_path(filename)
|
174
|
+
if File.exists?(full_filename)
|
175
|
+
params['script'] = File.read(full_filename)
|
176
|
+
else
|
177
|
+
print_red_alert "File not found: #{full_filename}"
|
178
|
+
exit 1
|
179
|
+
end
|
180
|
+
# use the filename as the name by default.
|
181
|
+
if !params['name']
|
182
|
+
params['name'] = File.basename(full_filename)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
# opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
|
186
|
+
# options['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
|
187
|
+
# end
|
188
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
189
|
+
opts.footer = "Create a new container script." + "\n" +
|
190
|
+
"[name] is required and can be passed as --name instead."
|
191
|
+
end
|
192
|
+
optparse.parse!(args)
|
193
|
+
# support [name] as first argument
|
194
|
+
if args[0]
|
195
|
+
params['name'] = args[0]
|
196
|
+
end
|
197
|
+
if !params['name']
|
198
|
+
print_error Morpheus::Terminal.angry_prompt
|
199
|
+
puts_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
200
|
+
return 1
|
201
|
+
end
|
202
|
+
connect(options)
|
203
|
+
begin
|
204
|
+
# construct payload
|
205
|
+
payload = nil
|
206
|
+
if options[:payload]
|
207
|
+
payload = options[:payload]
|
208
|
+
else
|
209
|
+
# merge -O options into normally parsed options
|
210
|
+
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
211
|
+
# todo: prompt?
|
212
|
+
payload = {'containerScript' => params}
|
213
|
+
end
|
214
|
+
if options[:dry_run]
|
215
|
+
print_dry_run @container_scripts_interface.dry.create(payload)
|
216
|
+
return
|
217
|
+
end
|
218
|
+
json_response = @container_scripts_interface.create(payload)
|
219
|
+
if options[:json]
|
220
|
+
puts as_json(json_response, options)
|
221
|
+
elsif !options[:quiet]
|
222
|
+
container_script = json_response['containerScript']
|
223
|
+
print_green_success "Added container script #{container_script['name']}"
|
224
|
+
_get(container_script['id'], {})
|
225
|
+
end
|
226
|
+
return 0
|
227
|
+
rescue RestClient::Exception => e
|
228
|
+
print_rest_exception(e, options)
|
229
|
+
return 1
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
def update(args)
|
235
|
+
options = {}
|
236
|
+
params = {}
|
237
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
238
|
+
opts.banner = subcommand_usage("[name]")
|
239
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
240
|
+
params['name'] = val
|
241
|
+
end
|
242
|
+
# opts.on('--code VALUE', String, "Code") do |val|
|
243
|
+
# params['code'] = val
|
244
|
+
# end
|
245
|
+
# opts.on('--description VALUE', String, "Description") do |val|
|
246
|
+
# params['description'] = val
|
247
|
+
# end
|
248
|
+
opts.on('--type [bash|powershell]', String, "Script Type") do |val|
|
249
|
+
params['scriptType'] = val
|
250
|
+
end
|
251
|
+
opts.on('--phase [start|stop]', String, "Script Phase") do |val|
|
252
|
+
params['scriptPhase'] = val
|
253
|
+
end
|
254
|
+
opts.on('--category VALUE', String, "Category") do |val|
|
255
|
+
params['category'] = val
|
256
|
+
end
|
257
|
+
opts.on('--script TEXT', String, "Contents of the script.") do |val|
|
258
|
+
params['script'] = val
|
259
|
+
end
|
260
|
+
opts.on('--file FILE', "File containing the script. This can be used instead --script" ) do |filename|
|
261
|
+
full_filename = File.expand_path(filename)
|
262
|
+
if File.exists?(full_filename)
|
263
|
+
params['script'] = File.read(full_filename)
|
264
|
+
else
|
265
|
+
print_red_alert "File not found: #{full_filename}"
|
266
|
+
exit 1
|
267
|
+
end
|
268
|
+
# use the filename as the name by default.
|
269
|
+
if !params['name']
|
270
|
+
params['name'] = File.basename(full_filename)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
opts.on('--file FILE', "File containing the script. This can be used instead --script" ) do |filename|
|
274
|
+
if File.exists?(File.expand_path(filename))
|
275
|
+
params['script'] = File.read(File.expand_path(filename))
|
276
|
+
else
|
277
|
+
print_red_alert "File not found: #{filename}"
|
278
|
+
exit 1
|
279
|
+
end
|
280
|
+
end
|
281
|
+
# opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
|
282
|
+
# options['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
|
283
|
+
# end
|
284
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
285
|
+
opts.footer = "Update a container script." + "\n" +
|
286
|
+
"[name] is required. This is the name or id of a container script."
|
287
|
+
end
|
288
|
+
optparse.parse!(args)
|
289
|
+
if args.count != 1
|
290
|
+
print_error Morpheus::Terminal.angry_prompt
|
291
|
+
puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
292
|
+
return 1
|
293
|
+
end
|
294
|
+
connect(options)
|
295
|
+
begin
|
296
|
+
container_script = find_container_script_by_name_or_id(args[0])
|
297
|
+
if container_script.nil?
|
298
|
+
return 1
|
299
|
+
end
|
300
|
+
# construct payload
|
301
|
+
payload = nil
|
302
|
+
if options[:payload]
|
303
|
+
payload = options[:payload]
|
304
|
+
else
|
305
|
+
# merge -O options into normally parsed options
|
306
|
+
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
307
|
+
payload = {'containerScript' => params}
|
308
|
+
end
|
309
|
+
if options[:dry_run]
|
310
|
+
print_dry_run @container_scripts_interface.dry.update(container_script["id"], payload)
|
311
|
+
return
|
312
|
+
end
|
313
|
+
json_response = @container_scripts_interface.update(container_script["id"], payload)
|
314
|
+
if options[:json]
|
315
|
+
puts as_json(json_response, options)
|
316
|
+
elsif !options[:quiet]
|
317
|
+
print_green_success "Updated container script #{container_script['name']}"
|
318
|
+
_get(container_script['id'], {})
|
319
|
+
end
|
320
|
+
return 0
|
321
|
+
rescue RestClient::Exception => e
|
322
|
+
print_rest_exception(e, options)
|
323
|
+
return 1
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def remove(args)
|
328
|
+
options = {}
|
329
|
+
params = {}
|
330
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
331
|
+
opts.banner = subcommand_usage("[name]")
|
332
|
+
build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
|
333
|
+
end
|
334
|
+
optparse.parse!(args)
|
335
|
+
if args.count < 1
|
336
|
+
puts optparse
|
337
|
+
return 127
|
338
|
+
end
|
339
|
+
connect(options)
|
340
|
+
|
341
|
+
begin
|
342
|
+
container_script = find_container_script_by_name_or_id(args[0])
|
343
|
+
if container_script.nil?
|
344
|
+
return 1
|
345
|
+
end
|
346
|
+
|
347
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete container script '#{container_script['name']}'?", options)
|
348
|
+
return false
|
349
|
+
end
|
350
|
+
|
351
|
+
# payload = {
|
352
|
+
# 'containerScript' => {id: container_script["id"]}
|
353
|
+
# }
|
354
|
+
# payload['containerScript'].merge!(container_script)
|
355
|
+
payload = params
|
356
|
+
|
357
|
+
if options[:dry_run]
|
358
|
+
print_dry_run @container_scripts_interface.dry.destroy(container_script["id"])
|
359
|
+
return
|
360
|
+
end
|
361
|
+
|
362
|
+
json_response = @container_scripts_interface.destroy(container_script["id"])
|
363
|
+
if options[:json]
|
364
|
+
puts as_json(json_response, options)
|
365
|
+
elsif !options[:quiet]
|
366
|
+
print_green_success "Deleted container script #{container_script['name']}"
|
367
|
+
end
|
368
|
+
return 0, nil
|
369
|
+
rescue RestClient::Exception => e
|
370
|
+
print_rest_exception(e, options)
|
371
|
+
return 1
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
|
376
|
+
private
|
377
|
+
|
378
|
+
def find_container_script_by_name_or_id(val)
|
379
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
380
|
+
return find_container_script_by_id(val)
|
381
|
+
else
|
382
|
+
return find_container_script_by_name(val)
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def find_container_script_by_id(id)
|
387
|
+
begin
|
388
|
+
json_response = @container_scripts_interface.get(id.to_i)
|
389
|
+
return json_response['containerScript']
|
390
|
+
rescue RestClient::Exception => e
|
391
|
+
if e.response && e.response.code == 404
|
392
|
+
print_red_alert "Container Script not found by id #{id}"
|
393
|
+
else
|
394
|
+
raise e
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def find_container_script_by_name(name)
|
400
|
+
container_scripts = @container_scripts_interface.list({name: name.to_s})['containerScripts']
|
401
|
+
if container_scripts.empty?
|
402
|
+
print_red_alert "Container Script not found by name #{name}"
|
403
|
+
return nil
|
404
|
+
elsif container_scripts.size > 1
|
405
|
+
print_red_alert "#{container_scripts.size} container scripts found by name #{name}"
|
406
|
+
print_container_scripts_table(container_scripts, {color: red})
|
407
|
+
print_red_alert "Try using ID instead"
|
408
|
+
print reset,"\n"
|
409
|
+
return nil
|
410
|
+
else
|
411
|
+
return container_scripts[0]
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def print_container_scripts_table(container_scripts, opts={})
|
416
|
+
columns = [
|
417
|
+
{"ID" => lambda {|container_script| container_script['id'] } },
|
418
|
+
{"NAME" => lambda {|container_script| container_script['name'] } },
|
419
|
+
{"TYPE" => lambda {|container_script| format_container_script_type(container_script['scriptType']) } },
|
420
|
+
{"PHASE" => lambda {|container_script| format_container_script_phase(container_script['scriptPhase']) } },
|
421
|
+
{"OWNER" => lambda {|container_script| container_script['account'] ? container_script['account']['name'] : '' } },
|
422
|
+
]
|
423
|
+
if opts[:include_fields]
|
424
|
+
columns = opts[:include_fields]
|
425
|
+
end
|
426
|
+
print as_pretty_table(container_scripts, columns, opts)
|
427
|
+
end
|
428
|
+
|
429
|
+
def format_container_script_type(val)
|
430
|
+
val.to_s # .capitalize
|
431
|
+
end
|
432
|
+
|
433
|
+
def format_container_script_phase(val)
|
434
|
+
val.to_s # .capitalize
|
435
|
+
end
|
436
|
+
|
437
|
+
end
|
@@ -0,0 +1,397 @@
|
|
1
|
+
require 'morpheus/cli/cli_command'
|
2
|
+
require 'morpheus/cli/mixins/library_helper'
|
3
|
+
|
4
|
+
class Morpheus::Cli::LibraryContainerTemplatesCommand
|
5
|
+
include Morpheus::Cli::CliCommand
|
6
|
+
include Morpheus::Cli::LibraryHelper
|
7
|
+
|
8
|
+
set_command_name :'library-file-templates'
|
9
|
+
|
10
|
+
register_subcommands :list, :get, :add, :update, :remove
|
11
|
+
|
12
|
+
def connect(opts)
|
13
|
+
@api_client = establish_remote_appliance_connection(opts)
|
14
|
+
@container_templates_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).library_container_templates
|
15
|
+
end
|
16
|
+
|
17
|
+
def handle(args)
|
18
|
+
handle_subcommand(args)
|
19
|
+
end
|
20
|
+
|
21
|
+
def list(args)
|
22
|
+
options = {}
|
23
|
+
params = {}
|
24
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
25
|
+
opts.banner = subcommand_usage()
|
26
|
+
build_common_options(opts, options, [:list, :json, :yaml, :csv, :fields, :dry_run, :remote])
|
27
|
+
end
|
28
|
+
optparse.parse!(args)
|
29
|
+
connect(options)
|
30
|
+
begin
|
31
|
+
[:phrase, :offset, :max, :sort, :direction, :lastUpdated].each do |k|
|
32
|
+
params[k] = options[k] unless options[k].nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
if options[:dry_run]
|
36
|
+
print_dry_run @container_templates_interface.dry.list(params)
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
json_response = @container_templates_interface.list(params)
|
41
|
+
if options[:include_fields]
|
42
|
+
json_response = {"containerTemplates" => filter_data(json_response["containerTemplates"], options[:include_fields]) }
|
43
|
+
end
|
44
|
+
if options[:json]
|
45
|
+
puts as_json(json_response, options)
|
46
|
+
return 0
|
47
|
+
elsif options[:csv]
|
48
|
+
puts records_as_csv(json_response['containerTemplates'], options)
|
49
|
+
return 0
|
50
|
+
elsif options[:yaml]
|
51
|
+
puts as_yaml(json_response, options)
|
52
|
+
return 0
|
53
|
+
end
|
54
|
+
container_templates = json_response['containerTemplates']
|
55
|
+
title = "Morpheus Library - File Templates"
|
56
|
+
subtitles = []
|
57
|
+
# if group
|
58
|
+
# subtitles << "Group: #{group['name']}".strip
|
59
|
+
# end
|
60
|
+
# if cloud
|
61
|
+
# subtitles << "Cloud: #{cloud['name']}".strip
|
62
|
+
# end
|
63
|
+
if params[:phrase]
|
64
|
+
subtitles << "Search: #{params[:phrase]}".strip
|
65
|
+
end
|
66
|
+
print_h1 title, subtitles
|
67
|
+
if container_templates.empty?
|
68
|
+
print cyan,"No container file templates found.",reset,"\n"
|
69
|
+
else
|
70
|
+
print_container_templates_table(container_templates, options)
|
71
|
+
print_results_pagination(json_response, {:label => "container file template", :n_label => "container file templates"})
|
72
|
+
# print_results_pagination(json_response)
|
73
|
+
end
|
74
|
+
print reset,"\n"
|
75
|
+
rescue RestClient::Exception => e
|
76
|
+
print_rest_exception(e, options)
|
77
|
+
return 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def get(args)
|
82
|
+
options = {}
|
83
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
84
|
+
opts.banner = subcommand_usage("[name]")
|
85
|
+
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
86
|
+
end
|
87
|
+
optparse.parse!(args)
|
88
|
+
if args.count < 1
|
89
|
+
puts optparse
|
90
|
+
return 1
|
91
|
+
end
|
92
|
+
connect(options)
|
93
|
+
id_list = parse_id_list(args)
|
94
|
+
return run_command_for_each_arg(id_list) do |arg|
|
95
|
+
_get(arg, options)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def _get(id, options)
|
100
|
+
|
101
|
+
begin
|
102
|
+
container_template = find_container_template_by_name_or_id(id)
|
103
|
+
if container_template.nil?
|
104
|
+
return 1
|
105
|
+
end
|
106
|
+
if options[:dry_run]
|
107
|
+
print_dry_run @container_templates_interface.dry.get(container_template['id'])
|
108
|
+
return
|
109
|
+
end
|
110
|
+
json_response = @container_templates_interface.get(container_template['id'])
|
111
|
+
container_template = json_response['containerTemplate']
|
112
|
+
instances = json_response['instances'] || []
|
113
|
+
servers = json_response['servers'] || []
|
114
|
+
if options[:include_fields]
|
115
|
+
json_response = {"containerTemplate" => filter_data(json_response["containerTemplate"], options[:include_fields]) }
|
116
|
+
end
|
117
|
+
if options[:json]
|
118
|
+
puts as_json(json_response, options)
|
119
|
+
return 0
|
120
|
+
elsif options[:yaml]
|
121
|
+
puts as_yaml(json_response, options)
|
122
|
+
return 0
|
123
|
+
elsif options[:csv]
|
124
|
+
puts records_as_csv([json_response['containerTemplate']], options)
|
125
|
+
return 0
|
126
|
+
end
|
127
|
+
|
128
|
+
print_h1 "File Template Details"
|
129
|
+
print cyan
|
130
|
+
description_cols = {
|
131
|
+
"ID" => lambda {|it| it['id'] },
|
132
|
+
"Name" => lambda {|it| it['name'] },
|
133
|
+
"File Name" => lambda {|it| it['fileName'] },
|
134
|
+
"File Path" => lambda {|it| it['filePath'] },
|
135
|
+
"Setting Category" => lambda {|it| it['settingCategory'] },
|
136
|
+
"Setting Name" => lambda {|it| it['settingName'] },
|
137
|
+
"Phase" => lambda {|it| it['templatePhase'] },
|
138
|
+
"Owner" => lambda {|it| it['account'] ? it['account']['name'] : '' },
|
139
|
+
# "Enabled" => lambda {|it| format_boolean it['enabled'] },
|
140
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
141
|
+
"Updated" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
142
|
+
}
|
143
|
+
print_description_list(description_cols, container_template)
|
144
|
+
|
145
|
+
print_h2 "Template"
|
146
|
+
|
147
|
+
puts container_template['template']
|
148
|
+
|
149
|
+
|
150
|
+
|
151
|
+
print reset,"\n"
|
152
|
+
|
153
|
+
rescue RestClient::Exception => e
|
154
|
+
print_rest_exception(e, options)
|
155
|
+
return 1
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def add(args)
|
160
|
+
options = {}
|
161
|
+
params = {'templatePhase' => 'provision'}
|
162
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
163
|
+
opts.banner = subcommand_usage("[name]")
|
164
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
165
|
+
params['name'] = val
|
166
|
+
end
|
167
|
+
# opts.on('--code VALUE', String, "Code") do |val|
|
168
|
+
# params['code'] = val
|
169
|
+
# end
|
170
|
+
# opts.on('--description VALUE', String, "Description") do |val|
|
171
|
+
# params['description'] = val
|
172
|
+
# end
|
173
|
+
opts.on('--phase [start|stop|postProvision]', String, "Template Phase. Default is 'provision'") do |val|
|
174
|
+
params['scriptPhase'] = val
|
175
|
+
end
|
176
|
+
opts.on('--category VALUE', String, "Category") do |val|
|
177
|
+
params['category'] = val
|
178
|
+
end
|
179
|
+
# opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
|
180
|
+
# options['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
|
181
|
+
# end
|
182
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
183
|
+
opts.footer = "Create a new file template." + "\n" +
|
184
|
+
"[name] is required and can be passed as --name instead."
|
185
|
+
end
|
186
|
+
optparse.parse!(args)
|
187
|
+
if args.count > 1
|
188
|
+
print_error Morpheus::Terminal.angry_prompt
|
189
|
+
puts_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
190
|
+
return 1
|
191
|
+
end
|
192
|
+
# support [name] as first argument
|
193
|
+
if args[0]
|
194
|
+
params['name'] = args[0]
|
195
|
+
end
|
196
|
+
connect(options)
|
197
|
+
begin
|
198
|
+
# construct payload
|
199
|
+
payload = nil
|
200
|
+
if options[:payload]
|
201
|
+
payload = options[:payload]
|
202
|
+
else
|
203
|
+
# merge -O options into normally parsed options
|
204
|
+
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
205
|
+
# todo: prompt?
|
206
|
+
payload = {'containerTemplate' => params}
|
207
|
+
end
|
208
|
+
if options[:dry_run]
|
209
|
+
print_dry_run @container_templates_interface.dry.create(payload)
|
210
|
+
return
|
211
|
+
end
|
212
|
+
json_response = @container_templates_interface.create(payload)
|
213
|
+
if options[:json]
|
214
|
+
puts as_json(json_response, options)
|
215
|
+
elsif !options[:quiet]
|
216
|
+
container_template = json_response['containerTemplate']
|
217
|
+
print_green_success "Added file template #{container_template['name']}"
|
218
|
+
_get(container_template['id'], {})
|
219
|
+
end
|
220
|
+
return 0
|
221
|
+
rescue RestClient::Exception => e
|
222
|
+
print_rest_exception(e, options)
|
223
|
+
return 1
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
def update(args)
|
229
|
+
options = {}
|
230
|
+
params = {}
|
231
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
232
|
+
opts.banner = subcommand_usage("[name]")
|
233
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
234
|
+
params['name'] = val
|
235
|
+
end
|
236
|
+
# opts.on('--code VALUE', String, "Code") do |val|
|
237
|
+
# params['code'] = val
|
238
|
+
# end
|
239
|
+
# opts.on('--description VALUE', String, "Description") do |val|
|
240
|
+
# params['description'] = val
|
241
|
+
# end
|
242
|
+
opts.on('--phase [start|stop]', String, "Template Phase") do |val|
|
243
|
+
params['scriptPhase'] = val
|
244
|
+
end
|
245
|
+
|
246
|
+
# opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
|
247
|
+
# options['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
|
248
|
+
# end
|
249
|
+
build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
|
250
|
+
opts.footer = "Update a file template." + "\n" +
|
251
|
+
"[name] is required. This is the name or id of a file template."
|
252
|
+
end
|
253
|
+
optparse.parse!(args)
|
254
|
+
if args.count != 1
|
255
|
+
print_error Morpheus::Terminal.angry_prompt
|
256
|
+
puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
|
257
|
+
return 1
|
258
|
+
end
|
259
|
+
connect(options)
|
260
|
+
begin
|
261
|
+
container_template = find_container_template_by_name_or_id(args[0])
|
262
|
+
if container_template.nil?
|
263
|
+
return 1
|
264
|
+
end
|
265
|
+
# construct payload
|
266
|
+
payload = nil
|
267
|
+
if options[:payload]
|
268
|
+
payload = options[:payload]
|
269
|
+
else
|
270
|
+
# merge -O options into normally parsed options
|
271
|
+
params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
|
272
|
+
payload = {'containerTemplate' => params}
|
273
|
+
end
|
274
|
+
if options[:dry_run]
|
275
|
+
print_dry_run @container_templates_interface.dry.update(container_template["id"], payload)
|
276
|
+
return
|
277
|
+
end
|
278
|
+
json_response = @container_templates_interface.update(container_template["id"], payload)
|
279
|
+
if options[:json]
|
280
|
+
puts as_json(json_response, options)
|
281
|
+
elsif !options[:quiet]
|
282
|
+
print_green_success "Updated file template #{container_template['name']}"
|
283
|
+
_get(container_template['id'], {})
|
284
|
+
end
|
285
|
+
return 0
|
286
|
+
rescue RestClient::Exception => e
|
287
|
+
print_rest_exception(e, options)
|
288
|
+
return 1
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def remove(args)
|
293
|
+
options = {}
|
294
|
+
params = {}
|
295
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
296
|
+
opts.banner = subcommand_usage("[name]")
|
297
|
+
build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
|
298
|
+
end
|
299
|
+
optparse.parse!(args)
|
300
|
+
if args.count < 1
|
301
|
+
puts optparse
|
302
|
+
return 127
|
303
|
+
end
|
304
|
+
connect(options)
|
305
|
+
|
306
|
+
begin
|
307
|
+
container_template = find_container_template_by_name_or_id(args[0])
|
308
|
+
if container_template.nil?
|
309
|
+
return 1
|
310
|
+
end
|
311
|
+
|
312
|
+
unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete file template '#{container_template['name']}'?", options)
|
313
|
+
return false
|
314
|
+
end
|
315
|
+
|
316
|
+
# payload = {
|
317
|
+
# 'containerTemplate' => {id: container_template["id"]}
|
318
|
+
# }
|
319
|
+
# payload['containerTemplate'].merge!(container_template)
|
320
|
+
payload = params
|
321
|
+
|
322
|
+
if options[:dry_run]
|
323
|
+
print_dry_run @container_templates_interface.dry.destroy(container_template["id"])
|
324
|
+
return
|
325
|
+
end
|
326
|
+
|
327
|
+
json_response = @container_templates_interface.destroy(container_template["id"])
|
328
|
+
if options[:json]
|
329
|
+
puts as_json(json_response, options)
|
330
|
+
elsif !options[:quiet]
|
331
|
+
print_green_success "Deleted file template #{container_template['name']}"
|
332
|
+
end
|
333
|
+
return 0, nil
|
334
|
+
rescue RestClient::Exception => e
|
335
|
+
print_rest_exception(e, options)
|
336
|
+
return 1
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
private
|
342
|
+
|
343
|
+
def find_container_template_by_name_or_id(val)
|
344
|
+
if val.to_s =~ /\A\d{1,}\Z/
|
345
|
+
return find_container_template_by_id(val)
|
346
|
+
else
|
347
|
+
return find_container_template_by_name(val)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
def find_container_template_by_id(id)
|
352
|
+
begin
|
353
|
+
json_response = @container_templates_interface.get(id.to_i)
|
354
|
+
return json_response['containerTemplate']
|
355
|
+
rescue RestClient::Exception => e
|
356
|
+
if e.response && e.response.code == 404
|
357
|
+
print_red_alert "File Template not found by id #{id}"
|
358
|
+
else
|
359
|
+
raise e
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
def find_container_template_by_name(name)
|
365
|
+
container_templates = @container_templates_interface.list({name: name.to_s})['containerTemplates']
|
366
|
+
if container_templates.empty?
|
367
|
+
print_red_alert "File Template not found by name #{name}"
|
368
|
+
return nil
|
369
|
+
elsif container_templates.size > 1
|
370
|
+
print_red_alert "#{container_templates.size} file templates found by name #{name}"
|
371
|
+
print_container_templates_table(container_templates, {color: red})
|
372
|
+
print_red_alert "Try using ID instead"
|
373
|
+
print reset,"\n"
|
374
|
+
return nil
|
375
|
+
else
|
376
|
+
return container_templates[0]
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
def print_container_templates_table(container_templates, opts={})
|
381
|
+
columns = [
|
382
|
+
{"ID" => lambda {|container_template| container_template['id'] } },
|
383
|
+
{"NAME" => lambda {|container_template| container_template['name'] } },
|
384
|
+
{"FILE NAME" => lambda {|container_template| container_template['fileName'] } },
|
385
|
+
{"FILE PATH" => lambda {|container_template| container_template['filePath'] } },
|
386
|
+
{"SETTING CATEGORY" => lambda {|container_template| container_template['settingCategory'] } },
|
387
|
+
{"SETTING NAME" => lambda {|container_template| container_template['settingName'] } },
|
388
|
+
{"OWNER" => lambda {|container_template| container_template['account'] ? container_template['account']['name'] : '' } },
|
389
|
+
]
|
390
|
+
if opts[:include_fields]
|
391
|
+
columns = opts[:include_fields]
|
392
|
+
end
|
393
|
+
print as_pretty_table(container_templates, columns, opts)
|
394
|
+
end
|
395
|
+
|
396
|
+
|
397
|
+
end
|