morpheus-cli 6.2.3 → 6.3.1

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,485 @@
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 name or 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_name_or_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
+ "Spec Templates" => lambda {|it|
147
+ "(#{it['specTemplates'].count}) #{it['specTemplates'].collect {|it| it['name'] }.join(', ')}"
148
+ }
149
+ }
150
+
151
+ print_description_list(description_cols, package)
152
+
153
+ if (package['optionTypes'] || []).count > 0
154
+ rows = package['optionTypes'].collect do |opt|
155
+ {
156
+ label: opt['fieldLabel'],
157
+ type: opt['type']
158
+ }
159
+ end
160
+ print_h2 "Option Types"
161
+ puts as_pretty_table(rows, [:label, :type])
162
+ end
163
+
164
+ print reset,"\n"
165
+ rescue RestClient::Exception => e
166
+ print_rest_exception(e, options)
167
+ return 1
168
+ end
169
+ end
170
+
171
+ def add(args)
172
+ options = {}
173
+ params = {}
174
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
175
+ opts.banner = subcommand_usage("[name] [options]")
176
+ opts.on('-n', '--name VALUE', String, "Name for this cluster package") do |val|
177
+ params['name'] = val
178
+ end
179
+ opts.on('-d', '--description VALUE', String, "Description") do |val|
180
+ params['description'] = val
181
+ end
182
+ opts.on('-c', '--code VALUE', String, "Code") do |val|
183
+ params['code'] = val
184
+ end
185
+ opts.on('-e', '--enabled [on|off]', String, "Can be used to enable / disable package. Default is on") do |val|
186
+ params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
187
+ end
188
+ opts.on('-r', '--repeatInstall [on|off]', String, "Can be used to retry install package if initial fails") do |val|
189
+ params['repeatInstall'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
190
+ end
191
+ opts.on('-t', '--type VALUE', String, "Type") do |val|
192
+ params['type'] = val
193
+ end
194
+ opts.on('-p', '--packageType VALUE', String, "Package Type") do |val|
195
+ params['packageType'] = val
196
+ end
197
+ opts.on('-v', '--packageVersion VALUE', String, "Package Version") do |val|
198
+ params['packageVersion'] = val
199
+ end
200
+ opts.on('--spec-templates [x,y,z]', Array, "List of Spec Templates to include in this package, comma separated list of IDs.") do |list|
201
+ unless list.nil?
202
+ params['specTemplates'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
203
+ end
204
+ end
205
+
206
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
207
+ opts.footer = "Create a cluster package."
208
+ end
209
+ optparse.parse!(args)
210
+ connect(options)
211
+ if args.count > 1
212
+ print_error Morpheus::Terminal.angry_prompt
213
+ puts_error "wrong number of arguments, expected 0-1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
214
+ return 1
215
+ end
216
+ if args[0]
217
+ params['name'] = args[0]
218
+ end
219
+ begin
220
+ if options[:payload]
221
+ payload = options[:payload]
222
+ else
223
+ # support the old -O OPTION switch
224
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
225
+
226
+ # prompt for options
227
+ if params['name'].nil?
228
+ params['name'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'type' => 'text', 'fieldLabel' => 'Name', 'required' => true}], options[:options], @api_client,{})['name']
229
+ end
230
+
231
+ # code
232
+ if params['code'].nil?
233
+ params['code'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'code', 'type' => 'text', 'fieldLabel' => 'Code', 'required' => true}], options[:options], @api_client,{})['code']
234
+ end
235
+
236
+ # description
237
+ if params['description'].nil?
238
+ params['description'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'type' => 'text', 'fieldLabel' => 'Description', 'required' => false}], options[:options], @api_client,{})['description']
239
+ end
240
+
241
+ # enabled
242
+ if params['enabled'].nil?
243
+ params['enabled'] = Morpheus::Cli::OptionTypes.confirm("Enabled?", {:default => true}) == true
244
+ end
245
+
246
+ # enabled
247
+ if params['repeatInstall'].nil?
248
+ params['repeatInstall'] = Morpheus::Cli::OptionTypes.confirm("Repeat Install?", {:default => true}) == true
249
+ end
250
+
251
+ # type
252
+ if params['type'].nil?
253
+ params['type'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'select', 'fieldLabel' => 'Type', 'required' => true, 'optionSource' => 'clusterPackageTypes'}], options[:options], @api_client,{})['type']
254
+ end
255
+
256
+ # packageType
257
+ if params['packageType'].nil?
258
+ params['packageType'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'packageType', 'packageType' => 'text', 'fieldLabel' => 'Package Type', 'required' => true}], options[:options], @api_client,{})['packageType']
259
+ end
260
+
261
+ # packageVersion
262
+ if params['packageVersion'].nil?
263
+ params['packageVersion'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'packageVersion', 'packageVersion' => 'text', 'fieldLabel' => 'Package Version', 'required' => true}], options[:options], @api_client,{})['packageVersion']
264
+ end
265
+
266
+ # specTemplates
267
+ if params['specTemplates'].nil?
268
+ spec_templates = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'specTemplates', 'fieldLabel' => 'Spec Templates', 'type' => 'multiSelect', 'required' => true, 'optionSource' => 'clusterResourceSpecTemplates'}], options[:options], @api_client, {})['specTemplates']
269
+
270
+ params['specTemplates'] = spec_templates
271
+ end
272
+ payload = {'clusterPackage' => params}
273
+ end
274
+
275
+ @library_cluster_packages_interface.setopts(options)
276
+ if options[:dry_run]
277
+ print_dry_run @library_cluster_packages_interface.dry.create(payload)
278
+ return
279
+ end
280
+
281
+ json_response = @library_cluster_packages_interface.create(payload)
282
+
283
+ if options[:json]
284
+ print JSON.pretty_generate(json_response), "\n"
285
+ return
286
+ end
287
+ print_green_success "Added Cluster Package #{params['name']}"
288
+ get([json_response['id']])
289
+ rescue RestClient::Exception => e
290
+ print_rest_exception(e, options)
291
+ exit 1
292
+ end
293
+ end
294
+
295
+ def update(args)
296
+ options = {}
297
+ params = {}
298
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
299
+ opts.banner = subcommand_usage("[name] [options]")
300
+ opts.on('-n', '--name VALUE', String, "Name for this cluster package") do |val|
301
+ params['name'] = val
302
+ end
303
+ opts.on('-d', '--description VALUE', String, "Description") do |val|
304
+ params['description'] = val
305
+ end
306
+ opts.on('-c', '--code VALUE', String, "Code") do |val|
307
+ params['code'] = val
308
+ end
309
+ opts.on('-e', '--enabled [on|off]', String, "Can be used to enable / disable package. Default is on") do |val|
310
+ params['enabled'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
311
+ end
312
+ opts.on('-r', '--repeatInstall [on|off]', String, "Can be used to retry install package if initial fails") do |val|
313
+ params['repeatInstall'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == '1' || val.to_s == ''
314
+ end
315
+ opts.on('-t', '--type VALUE', String, "Type") do |val|
316
+ params['type'] = val
317
+ end
318
+ opts.on('-p', '--packageType VALUE', String, "Package Type") do |val|
319
+ params['packageType'] = val
320
+ end
321
+ opts.on('-v', '--packageVersion VALUE', String, "Package Version") do |val|
322
+ params['packageVersion'] = val
323
+ end
324
+ opts.on('--spec-templates [x,y,z]', Array, "List of Spec Templates to include in this package, comma separated list of IDs.") do |list|
325
+ unless list.nil?
326
+ params['specTemplates'] = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
327
+ end
328
+ end
329
+
330
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
331
+ opts.footer = "Update a cluster package." + "\n" +
332
+ "[name] is required. This is the name or id of a cluster package."
333
+ end
334
+
335
+ optparse.parse!(args)
336
+ connect(options)
337
+ if args.count != 1
338
+ print_error Morpheus::Terminal.angry_prompt
339
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
340
+ return 1
341
+ end
342
+ begin
343
+ cluster_package = find_package_by_name_or_id(args[0])
344
+ exit 1 if cluster_package.nil?
345
+ passed_options = (options[:options] || {}).reject {|k,v| k.is_a?(Symbol) }
346
+ payload = nil
347
+ if options[:payload]
348
+ payload = options[:payload]
349
+ else
350
+ # support the old -O OPTION switch
351
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
352
+ end
353
+
354
+ if params.empty?
355
+ print_green_success "Nothing to update"
356
+ exit 1
357
+ end
358
+ payload = {'clusterPackage' => params}
359
+
360
+ if options[:dry_run]
361
+ print_dry_run @library_cluster_packages_interface.dry.update(cluster_package['id'], payload)
362
+ return 0
363
+ end
364
+
365
+ json_response = @library_cluster_packages_interface.update(cluster_package['id'], payload)
366
+
367
+ if options[:json]
368
+ print JSON.pretty_generate(json_response), "\n"
369
+ return 0
370
+ end
371
+
372
+ print_green_success "Updated Cluster Package #{params['name'] || cluster_package['name']}"
373
+ return 0
374
+ rescue RestClient::Exception => e
375
+ print_rest_exception(e, options)
376
+ return 1
377
+ end
378
+ end
379
+
380
+ def remove(args)
381
+ options = {}
382
+ optparse = Morpheus::Cli::OptionParser.new do|opts|
383
+ opts.banner = subcommand_usage("[clusterPackage]")
384
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
385
+ opts.footer = "Delete a cluster package." + "\n" +
386
+ "[clusterPackage] is required. This is the name or id of a cluster package."
387
+ end
388
+ optparse.parse!(args)
389
+
390
+ if args.count != 1
391
+ print_error Morpheus::Terminal.angry_prompt
392
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
393
+ return 1
394
+ end
395
+
396
+ connect(options)
397
+
398
+ begin
399
+ cluster_package = find_package_by_name_or_id(args[0])
400
+ if cluster_package.nil?
401
+ return 1
402
+ end
403
+
404
+ unless Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the cluster package #{cluster_package['name']}?", options)
405
+ exit
406
+ end
407
+
408
+ @library_cluster_packages_interface.setopts(options)
409
+ if options[:dry_run]
410
+ print_dry_run @library_cluster_packages_interface.dry.destroy(cluster_package['id'])
411
+ return
412
+ end
413
+ json_response = @library_cluster_packages_interface.destroy(cluster_package['id'])
414
+
415
+ if options[:json]
416
+ print JSON.pretty_generate(json_response)
417
+ print "\n"
418
+ elsif !options[:quiet]
419
+ if json_response['success']
420
+ print_green_success "Removed Cluster Package #{cluster_package['name']}"
421
+ else
422
+ print_red_alert "Error removing cluster package: #{json_response['msg'] || json_response['errors']}"
423
+ end
424
+ end
425
+ return 0
426
+ rescue RestClient::Exception => e
427
+ print_rest_exception(e, options)
428
+ exit 1
429
+ end
430
+ end
431
+
432
+
433
+ private
434
+
435
+ def find_package_by_name_or_id(val)
436
+ if val.to_s =~ /\A\d{1,}\Z/
437
+ return find_package_by_id(val)
438
+ else
439
+ return find_package_by_name(val)
440
+ end
441
+ end
442
+
443
+ def find_package_by_id(id)
444
+ begin
445
+ json_response = @library_cluster_packages_interface.get(id.to_i)
446
+ return json_response['clusterPackage']
447
+ rescue RestClient::Exception => e
448
+ if e.response && e.response.code == 404
449
+ print_red_alert "Cluster package not found by id #{id}"
450
+ else
451
+ raise e
452
+ end
453
+ end
454
+ end
455
+
456
+ def find_package_by_name(name)
457
+ packages = @library_cluster_packages_interface.list({name: name.to_s})['clusterPackages']
458
+ if packages.empty?
459
+ print_red_alert "Cluster package not found by name #{name}"
460
+ return nil
461
+ elsif packages.size > 1
462
+ print_red_alert "#{packages.size} cluster packages found by name #{name}"
463
+ print_packages_table(packages, {color: red})
464
+ print_red_alert "Try using ID instead"
465
+ print reset,"\n"
466
+ return nil
467
+ else
468
+ return packages[0]
469
+ end
470
+ end
471
+
472
+ def print_packages_table(packages, opts={})
473
+ columns = [
474
+ {"ID" => lambda {|package| package['id'] } },
475
+ {"NAME" => lambda {|package| package['name'] } },
476
+ {"TECHNOLOGY" => lambda {|package| format_package_technology(package) } },
477
+ {"DESCRIPTION" => lambda {|package| package['description'] } },
478
+ {"OWNER" => lambda {|package| package['account'] ? package['account']['name'] : '' } }
479
+ ]
480
+ if opts[:include_fields]
481
+ columns = opts[:include_fields]
482
+ end
483
+ print as_pretty_table(packages, columns, opts)
484
+ end
485
+ end