morpheus-cli 6.3.0 → 6.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.
@@ -0,0 +1,552 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::LibraryClusterPackagesCommand
4
+ include Morpheus::Cli::CliCommand
5
+ include Morpheus::Cli::LibraryHelper
6
+
7
+ set_command_name :'library-cluster-packages'
8
+
9
+ register_subcommands :list, :get, :add, :update, :remove
10
+
11
+ def initialize()
12
+ end
13
+
14
+ def connect(opts)
15
+ @api_client = establish_remote_appliance_connection(opts)
16
+ @library_cluster_packages_interface = @api_client.library_cluster_packages
17
+ end
18
+
19
+ def handle(args)
20
+ handle_subcommand(args)
21
+ end
22
+
23
+ def list(args)
24
+ options = {}
25
+ params = {}
26
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
27
+ opts.banner = subcommand_usage()
28
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
29
+ opts.footer = "List cluster packages."
30
+ end
31
+ optparse.parse!(args)
32
+ # verify_args!(args:args, optparse:optparse, count:0)
33
+ if args.count > 0
34
+ options[:phrase] = args.join(" ")
35
+ end
36
+ connect(options)
37
+ begin
38
+ # construct payload
39
+ params.merge!(parse_list_options(options))
40
+ @library_cluster_packages_interface.setopts(options)
41
+ if options[:dry_run]
42
+ print_dry_run @library_cluster_packages_interface.dry.list(params)
43
+ return
44
+ end
45
+ # do it
46
+ json_response = @library_cluster_packages_interface.list(params)
47
+ # print and/or return result
48
+ # return 0 if options[:quiet]
49
+ if options[:json]
50
+ puts as_json(json_response, options, "clusterPackages")
51
+ return 0
52
+ elsif options[:csv]
53
+ puts records_as_csv(json_response['clusterPackages'], options)
54
+ return 0
55
+ elsif options[:yaml]
56
+ puts as_yaml(json_response, options, "clusterPackages")
57
+ return 0
58
+ end
59
+ packages = json_response['clusterPackages']
60
+ title = "Morpheus Library - Cluster Packages"
61
+ subtitles = parse_list_subtitles(options)
62
+ print_h1 title, subtitles
63
+ if packages.empty?
64
+ print cyan,"No cluster packages found.",reset,"\n"
65
+ else
66
+ rows = packages.collect do |package|
67
+ {
68
+ id: package['id'],
69
+ name: package['name'],
70
+ type: package['type'],
71
+ packageType: package['packageType'],
72
+ enabled: package['enabled']
73
+
74
+ }
75
+ end
76
+ print as_pretty_table(rows, [:id, :name, :type, :packageType, :enabled], options)
77
+ print_results_pagination(json_response, {})
78
+ end
79
+ print reset,"\n"
80
+ rescue RestClient::Exception => e
81
+ print_rest_exception(e, options)
82
+ return 1
83
+ end
84
+ end
85
+
86
+ def get(args)
87
+ options = {}
88
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
89
+ opts.banner = subcommand_usage("[clusterPackage]")
90
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
91
+ opts.footer = "Display cluster package details." + "\n" +
92
+ "[clusterPackage] is required. This is the id of a cluster package."
93
+ end
94
+ optparse.parse!(args)
95
+ if args.count < 1
96
+ puts optparse
97
+ return 1
98
+ end
99
+ connect(options)
100
+ id_list = parse_id_list(args)
101
+ id_list.each do |id|
102
+
103
+ end
104
+ return run_command_for_each_arg(id_list) do |arg|
105
+ _get(arg, options)
106
+ end
107
+ end
108
+
109
+ def _get(id, options)
110
+ begin
111
+ @library_cluster_packages_interface.setopts(options)
112
+
113
+ if options[:dry_run]
114
+ print_dry_run @library_cluster_packages_interface.dry.get(id)
115
+ return
116
+ end
117
+ package = find_package_by_id(id)
118
+ if package.nil?
119
+ return 1
120
+ end
121
+
122
+ json_response = {'package' => package}
123
+
124
+ if options[:json]
125
+ puts as_json(json_response, options, "package")
126
+ return 0
127
+ elsif options[:yaml]
128
+ puts as_yaml(json_response, options, "package")
129
+ return 0
130
+ elsif options[:csv]
131
+ puts records_as_csv([json_response['package']], options)
132
+ return 0
133
+ end
134
+
135
+ print_h1 "Cluster Package Details"
136
+ print cyan
137
+ description_cols = {
138
+ "ID" => lambda {|it| it['id'] },
139
+ "Name" => lambda {|it| it['name'] },
140
+ "Code" => lambda {|it| it['code']},
141
+ "Enabled" => lambda {|it| format_boolean(it['enabled'])},
142
+ "Description" => lambda {|it|it['description']},
143
+ "Package Version" => lambda {|it| it['packageVersion']},
144
+ "Package Type" => lambda {|it| it['packageType']},
145
+ "Type" => lambda {|it| it['type'] },
146
+ "Repeat Install" => lambda {|it| format_boolean(it['repeatInstall'])},
147
+ "Logo" => lambda {|it| it['imagePath'] },
148
+ "Dark Logo" => lambda {|it| it['darkImagePath'] },
149
+ "Spec Templates" => lambda {|it|
150
+ "(#{it['specTemplates'].count}) #{it['specTemplates'].collect {|it| it['name'] }.join(', ')}"
151
+ }
152
+ }
153
+
154
+ print_description_list(description_cols, package)
155
+
156
+ if (package['optionTypes'] || []).count > 0
157
+ rows = package['optionTypes'].collect do |opt|
158
+ {
159
+ label: opt['fieldLabel'],
160
+ type: opt['type']
161
+ }
162
+ end
163
+ print_h2 "Option Types"
164
+ puts as_pretty_table(rows, [:label, :type])
165
+ end
166
+
167
+ print reset,"\n"
168
+ rescue RestClient::Exception => e
169
+ print_rest_exception(e, options)
170
+ return 1
171
+ end
172
+ end
173
+
174
+ def add(args)
175
+ options = {}
176
+ params = {}
177
+ logo_file = nil
178
+ dark_logo_file = nil
179
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
180
+ opts.banner = subcommand_usage("[name] [options]")
181
+ opts.on('-n', '--name VALUE', String, "Name for this cluster package") do |val|
182
+ params['name'] = val
183
+ end
184
+ opts.on('-d', '--description VALUE', String, "Description") do |val|
185
+ params['description'] = val
186
+ end
187
+ opts.on('-c', '--code VALUE', String, "Code") do |val|
188
+ params['code'] = val
189
+ end
190
+ opts.on('-e', '--enabled [on|off]', String, "Can be used to enable / disable package. Default is on") do |val|
191
+ params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
192
+ end
193
+ opts.on('-r', '--repeatInstall [on|off]', String, "Can be used to retry install package if initial fails") do |val|
194
+ params['repeatInstall'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
195
+ end
196
+ opts.on('-t', '--type VALUE', String, "Type") do |val|
197
+ params['type'] = val
198
+ end
199
+ opts.on('-p', '--packageType VALUE', String, "Package Type") do |val|
200
+ params['packageType'] = val
201
+ end
202
+ opts.on('-v', '--packageVersion VALUE', String, "Package Version") do |val|
203
+ params['packageVersion'] = val
204
+ end
205
+ opts.on('--spec-templates [x,y,z]', Array, "List of Spec Templates to include in this package, comma separated list of IDs.") do |list|
206
+ unless list.nil?
207
+ params['specTemplates'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
208
+ end
209
+ end
210
+ opts.on('--logo FILE', String, "Upload a custom logo icon") do |val|
211
+ filename = val
212
+ logo_file = nil
213
+ if filename == 'null'
214
+ logo_file = 'null' # clear it
215
+ else
216
+ filename = File.expand_path(filename)
217
+ if !File.exist?(filename)
218
+ raise_command_error "File not found: #{filename}"
219
+ end
220
+ logo_file = File.new(filename, 'rb')
221
+ end
222
+ end
223
+ opts.on('--dark-logo FILE', String, "Upload a custom dark logo icon") do |val|
224
+ filename = val
225
+ dark_logo_file = nil
226
+ if filename == 'null'
227
+ dark_logo_file = 'null' # clear it
228
+ else
229
+ filename = File.expand_path(filename)
230
+ if !File.exist?(filename)
231
+ raise_command_error "File not found: #{filename}"
232
+ end
233
+ dark_logo_file = File.new(filename, 'rb')
234
+ end
235
+ end
236
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
237
+ opts.footer = "Create a cluster package."
238
+ end
239
+ optparse.parse!(args)
240
+ connect(options)
241
+ if args.count > 1
242
+ print_error Morpheus::Terminal.angry_prompt
243
+ puts_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
244
+ return 1
245
+ end
246
+ if args[0]
247
+ params['name'] = args[0]
248
+ end
249
+ begin
250
+ if options[:payload]
251
+ payload = options[:payload]
252
+ else
253
+ # support the old -O OPTION switch
254
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
255
+
256
+ # prompt for options
257
+ if params['name'].nil?
258
+ params['name'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Name', 'required' => true}], options[:options], @api_client,{})['name']
259
+ end
260
+
261
+ # code
262
+ if params['code'].nil?
263
+ params['code'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'code', 'type' => 'text', 'fieldLabel' => 'Code', 'required' => true}], options[:options], @api_client,{})['code']
264
+ end
265
+
266
+ # description
267
+ if params['description'].nil?
268
+ params['description'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'type' => 'text', 'fieldLabel' => 'Description', 'required' => false}], options[:options], @api_client,{})['description']
269
+ end
270
+
271
+ # enabled
272
+ if params['enabled'].nil?
273
+ params['enabled'] = Morpheus::Cli::OptionTypes.confirm("Enabled?", {:default => true}) == true
274
+ end
275
+
276
+ # enabled
277
+ if params['repeatInstall'].nil?
278
+ params['repeatInstall'] = Morpheus::Cli::OptionTypes.confirm("Repeat Install?", {:default => true}) == true
279
+ end
280
+
281
+ # type
282
+ if params['type'].nil?
283
+ params['type'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'select', 'fieldLabel' => 'Type', 'required' => true, 'optionSource' => 'clusterPackageTypes'}], options[:options], @api_client,{})['type']
284
+ end
285
+
286
+ # packageType
287
+ if params['packageType'].nil?
288
+ params['packageType'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'packageType', 'packageType' => 'text', 'fieldLabel' => 'Package Type', 'required' => true}], options[:options], @api_client,{})['packageType']
289
+ end
290
+
291
+ # packageVersion
292
+ if params['packageVersion'].nil?
293
+ params['packageVersion'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'packageVersion', 'packageVersion' => 'text', 'fieldLabel' => 'Package Version', 'required' => true}], options[:options], @api_client,{})['packageVersion']
294
+ end
295
+
296
+ # logo
297
+ if params['iconPath'].nil?
298
+ params['iconPath'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'iconPath', 'fieldLabel' => 'Logo', 'type' => 'select', 'optionSource' => 'iconList'}], options[:options], @api_client,{})['iconPath']
299
+ end
300
+
301
+ # specTemplates
302
+ if params['specTemplates'].nil?
303
+ spec_templates = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'specTemplates', 'fieldLabel' => 'Spec Templates', 'type' => 'multiSelect', 'required' => true, 'optionSource' => 'clusterResourceSpecTemplates'}], options[:options], @api_client, {})['specTemplates']
304
+
305
+ params['specTemplates'] = spec_templates
306
+ end
307
+ payload = {'clusterPackage' => params}
308
+ end
309
+
310
+ @library_cluster_packages_interface.setopts(options)
311
+ if options[:dry_run]
312
+ print_dry_run @library_cluster_packages_interface.dry.create(payload)
313
+ return
314
+ end
315
+
316
+ json_response = @library_cluster_packages_interface.create(payload)
317
+ if json_response['success']
318
+ if logo_file || dark_logo_file
319
+ begin
320
+ @library_cluster_packages_interface.update_logo(json_response['id'], logo_file, dark_logo_file)
321
+ rescue RestClient::Exception => e
322
+ print_red_alert "Failed to save logo!"
323
+ print_rest_exception(e, options)
324
+ end
325
+ end
326
+ end
327
+
328
+ if options[:json]
329
+ print JSON.pretty_generate(json_response), "\n"
330
+ return
331
+ end
332
+ print_green_success "Added Cluster Package #{params['name']}"
333
+ get([json_response['id']])
334
+ rescue RestClient::Exception => e
335
+ print_rest_exception(e, options)
336
+ exit 1
337
+ end
338
+ end
339
+
340
+ def update(args)
341
+ options = {}
342
+ params = {}
343
+ logo_file = nil
344
+ dark_logo_file = nil
345
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
346
+ opts.banner = subcommand_usage("[id] [options]")
347
+ opts.on('-n', '--name VALUE', String, "Name for this cluster package") do |val|
348
+ params['name'] = val
349
+ end
350
+ opts.on('-d', '--description VALUE', String, "Description") do |val|
351
+ params['description'] = val
352
+ end
353
+ opts.on('-c', '--code VALUE', String, "Code") do |val|
354
+ params['code'] = val
355
+ end
356
+ opts.on('-e', '--enabled [on|off]', String, "Can be used to enable / disable package. Default is on") do |val|
357
+ params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
358
+ end
359
+ opts.on('-r', '--repeatInstall [on|off]', String, "Can be used to retry install package if initial fails") do |val|
360
+ params['repeatInstall'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
361
+ end
362
+ opts.on('-t', '--type VALUE', String, "Type") do |val|
363
+ params['type'] = val
364
+ end
365
+ opts.on('-p', '--packageType VALUE', String, "Package Type") do |val|
366
+ params['packageType'] = val
367
+ end
368
+ opts.on('-v', '--packageVersion VALUE', String, "Package Version") do |val|
369
+ params['packageVersion'] = val
370
+ end
371
+ opts.on('--spec-templates [x,y,z]', Array, "List of Spec Templates to include in this package, comma separated list of IDs.") do |list|
372
+ unless list.nil?
373
+ params['specTemplates'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
374
+ end
375
+ end
376
+ opts.on('--logo FILE', String, "Upload a custom logo icon") do |val|
377
+ filename = val
378
+ logo_file = nil
379
+ if filename == 'null'
380
+ logo_file = 'null' # clear it
381
+ else
382
+ filename = File.expand_path(filename)
383
+ if !File.exist?(filename)
384
+ raise_command_error "File not found: #{filename}"
385
+ end
386
+ logo_file = File.new(filename, 'rb')
387
+ end
388
+ end
389
+ opts.on('--dark-logo FILE', String, "Upload a custom dark logo icon") do |val|
390
+ filename = val
391
+ dark_logo_file = nil
392
+ if filename == 'null'
393
+ dark_logo_file = 'null' # clear it
394
+ else
395
+ filename = File.expand_path(filename)
396
+ if !File.exist?(filename)
397
+ raise_command_error "File not found: #{filename}"
398
+ end
399
+ dark_logo_file = File.new(filename, 'rb')
400
+ end
401
+ end
402
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
403
+ opts.footer = "Update a cluster package." + "\n" +
404
+ "[id] is required. This is the id of a cluster package."
405
+ end
406
+
407
+ optparse.parse!(args)
408
+ connect(options)
409
+ if args.count != 1
410
+ print_error Morpheus::Terminal.angry_prompt
411
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
412
+ return 1
413
+ end
414
+ begin
415
+ cluster_package = find_package_by_id(args[0])
416
+ exit 1 if cluster_package.nil?
417
+ passed_options = (options[:options] || {}).reject {|k,v| k.is_a?(Symbol) }
418
+ payload = nil
419
+ if options[:payload]
420
+ payload = options[:payload]
421
+ else
422
+ # support the old -O OPTION switch
423
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
424
+ end
425
+
426
+ if params.empty? && !logo_file && !dark_logo_file
427
+ print_green_success "Nothing to update"
428
+ exit 1
429
+ end
430
+ payload = {'clusterPackage' => params}
431
+
432
+ if options[:dry_run]
433
+ print_dry_run @library_cluster_packages_interface.dry.update(cluster_package['id'], payload)
434
+ return 0
435
+ end
436
+
437
+ if (logo_file || dark_logo_file) && params.empty?
438
+ begin
439
+ @library_cluster_packages_interface.update_logo(cluster_package['id'], logo_file, dark_logo_file)
440
+ print_green_success "Updated Cluster Package #{params['name'] || cluster_package['name']}"
441
+ rescue RestClient::Exception => e
442
+ print_red_alert "Failed to save logo!"
443
+ print_rest_exception(e, options)
444
+ end
445
+ else
446
+ json_response = @library_cluster_packages_interface.update(cluster_package['id'], payload)
447
+ if json_response['success']
448
+ if logo_file || dark_logo_file
449
+ begin
450
+ @library_cluster_packages_interface.update_logo(json_response['id'], logo_file, dark_logo_file)
451
+ rescue RestClient::Exception => e
452
+ print_red_alert "Failed to save logo!"
453
+ print_rest_exception(e, options)
454
+ end
455
+ end
456
+ end
457
+ if options[:json]
458
+ print JSON.pretty_generate(json_response), "\n"
459
+ return 0
460
+ end
461
+
462
+ print_green_success "Updated Cluster Package #{params['name'] || cluster_package['name']}"
463
+ end
464
+ return 0
465
+ rescue RestClient::Exception => e
466
+ print_rest_exception(e, options)
467
+ return 1
468
+ end
469
+ end
470
+
471
+ def remove(args)
472
+ options = {}
473
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
474
+ opts.banner = subcommand_usage("[clusterPackage]")
475
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
476
+ opts.footer = "Delete a cluster package." + "\n" +
477
+ "[clusterPackage] is required. This is the id of a cluster package."
478
+ end
479
+ optparse.parse!(args)
480
+
481
+ if args.count != 1
482
+ print_error Morpheus::Terminal.angry_prompt
483
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
484
+ return 1
485
+ end
486
+
487
+ connect(options)
488
+
489
+ begin
490
+ cluster_package = find_package_by_id(args[0])
491
+ if cluster_package.nil?
492
+ return 1
493
+ end
494
+
495
+ unless Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the cluster package #{cluster_package['name']}?", options)
496
+ exit
497
+ end
498
+
499
+ @library_cluster_packages_interface.setopts(options)
500
+ if options[:dry_run]
501
+ print_dry_run @library_cluster_packages_interface.dry.destroy(cluster_package['id'])
502
+ return
503
+ end
504
+ json_response = @library_cluster_packages_interface.destroy(cluster_package['id'])
505
+
506
+ if options[:json]
507
+ print JSON.pretty_generate(json_response)
508
+ print "\n"
509
+ elsif !options[:quiet]
510
+ if json_response['success']
511
+ print_green_success "Removed Cluster Package #{cluster_package['name']}"
512
+ else
513
+ print_red_alert "Error removing cluster package: #{json_response['msg'] || json_response['errors']}"
514
+ end
515
+ end
516
+ return 0
517
+ rescue RestClient::Exception => e
518
+ print_rest_exception(e, options)
519
+ exit 1
520
+ end
521
+ end
522
+
523
+
524
+ private
525
+
526
+ def find_package_by_id(id)
527
+ begin
528
+ json_response = @library_cluster_packages_interface.get(id.to_i)
529
+ return json_response['clusterPackage']
530
+ rescue RestClient::Exception => e
531
+ if e.response && e.response.code == 404
532
+ print_red_alert "Cluster package not found by id #{id}"
533
+ else
534
+ raise e
535
+ end
536
+ end
537
+ end
538
+
539
+ def print_packages_table(packages, opts={})
540
+ columns = [
541
+ {"ID" => lambda {|package| package['id'] } },
542
+ {"NAME" => lambda {|package| package['name'] } },
543
+ {"TECHNOLOGY" => lambda {|package| format_package_technology(package) } },
544
+ {"DESCRIPTION" => lambda {|package| package['description'] } },
545
+ {"OWNER" => lambda {|package| package['account'] ? package['account']['name'] : '' } }
546
+ ]
547
+ if opts[:include_fields]
548
+ columns = opts[:include_fields]
549
+ end
550
+ print as_pretty_table(packages, columns, opts)
551
+ end
552
+ end