morpheus-cli 2.10.0 → 2.10.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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/bin/morpheus +27 -32
  3. data/lib/morpheus/api/accounts_interface.rb +36 -47
  4. data/lib/morpheus/api/api_client.rb +141 -110
  5. data/lib/morpheus/api/app_templates_interface.rb +56 -72
  6. data/lib/morpheus/api/apps_interface.rb +111 -132
  7. data/lib/morpheus/api/auth_interface.rb +30 -0
  8. data/lib/morpheus/api/clouds_interface.rb +71 -76
  9. data/lib/morpheus/api/custom_instance_types_interface.rb +21 -46
  10. data/lib/morpheus/api/dashboard_interface.rb +10 -17
  11. data/lib/morpheus/api/deploy_interface.rb +60 -72
  12. data/lib/morpheus/api/deployments_interface.rb +53 -71
  13. data/lib/morpheus/api/groups_interface.rb +55 -45
  14. data/lib/morpheus/api/instance_types_interface.rb +19 -23
  15. data/lib/morpheus/api/instances_interface.rb +179 -177
  16. data/lib/morpheus/api/key_pairs_interface.rb +11 -17
  17. data/lib/morpheus/api/license_interface.rb +18 -23
  18. data/lib/morpheus/api/load_balancers_interface.rb +54 -69
  19. data/lib/morpheus/api/logs_interface.rb +25 -29
  20. data/lib/morpheus/api/options_interface.rb +13 -17
  21. data/lib/morpheus/api/provision_types_interface.rb +19 -22
  22. data/lib/morpheus/api/roles_interface.rb +75 -94
  23. data/lib/morpheus/api/security_group_rules_interface.rb +28 -37
  24. data/lib/morpheus/api/security_groups_interface.rb +39 -51
  25. data/lib/morpheus/api/servers_interface.rb +113 -115
  26. data/lib/morpheus/api/setup_interface.rb +31 -0
  27. data/lib/morpheus/api/task_sets_interface.rb +36 -38
  28. data/lib/morpheus/api/tasks_interface.rb +56 -69
  29. data/lib/morpheus/api/users_interface.rb +67 -76
  30. data/lib/morpheus/api/virtual_images_interface.rb +61 -61
  31. data/lib/morpheus/api/whoami_interface.rb +12 -15
  32. data/lib/morpheus/cli.rb +71 -60
  33. data/lib/morpheus/cli/accounts.rb +254 -315
  34. data/lib/morpheus/cli/alias_command.rb +219 -0
  35. data/lib/morpheus/cli/app_templates.rb +264 -272
  36. data/lib/morpheus/cli/apps.rb +608 -671
  37. data/lib/morpheus/cli/cli_command.rb +259 -21
  38. data/lib/morpheus/cli/cli_registry.rb +99 -14
  39. data/lib/morpheus/cli/clouds.rb +599 -372
  40. data/lib/morpheus/cli/config_file.rb +126 -0
  41. data/lib/morpheus/cli/credentials.rb +141 -117
  42. data/lib/morpheus/cli/dashboard_command.rb +48 -56
  43. data/lib/morpheus/cli/deployments.rb +254 -268
  44. data/lib/morpheus/cli/deploys.rb +150 -142
  45. data/lib/morpheus/cli/error_handler.rb +38 -0
  46. data/lib/morpheus/cli/groups.rb +551 -179
  47. data/lib/morpheus/cli/hosts.rb +862 -617
  48. data/lib/morpheus/cli/instance_types.rb +103 -95
  49. data/lib/morpheus/cli/instances.rb +1335 -1009
  50. data/lib/morpheus/cli/key_pairs.rb +82 -90
  51. data/lib/morpheus/cli/library.rb +498 -499
  52. data/lib/morpheus/cli/license.rb +83 -101
  53. data/lib/morpheus/cli/load_balancers.rb +314 -300
  54. data/lib/morpheus/cli/login.rb +66 -44
  55. data/lib/morpheus/cli/logout.rb +47 -46
  56. data/lib/morpheus/cli/mixins/accounts_helper.rb +69 -31
  57. data/lib/morpheus/cli/mixins/infrastructure_helper.rb +106 -0
  58. data/lib/morpheus/cli/mixins/print_helper.rb +181 -17
  59. data/lib/morpheus/cli/mixins/provisioning_helper.rb +535 -458
  60. data/lib/morpheus/cli/mixins/whoami_helper.rb +2 -2
  61. data/lib/morpheus/cli/option_parser.rb +35 -0
  62. data/lib/morpheus/cli/option_types.rb +232 -192
  63. data/lib/morpheus/cli/recent_activity_command.rb +61 -65
  64. data/lib/morpheus/cli/remote.rb +446 -199
  65. data/lib/morpheus/cli/roles.rb +884 -906
  66. data/lib/morpheus/cli/security_group_rules.rb +213 -203
  67. data/lib/morpheus/cli/security_groups.rb +237 -192
  68. data/lib/morpheus/cli/shell.rb +338 -231
  69. data/lib/morpheus/cli/tasks.rb +326 -308
  70. data/lib/morpheus/cli/users.rb +457 -462
  71. data/lib/morpheus/cli/version.rb +1 -1
  72. data/lib/morpheus/cli/version_command.rb +16 -18
  73. data/lib/morpheus/cli/virtual_images.rb +526 -345
  74. data/lib/morpheus/cli/whoami.rb +125 -111
  75. data/lib/morpheus/cli/workflows.rb +338 -185
  76. data/lib/morpheus/formatters.rb +8 -1
  77. data/lib/morpheus/logging.rb +1 -1
  78. data/lib/morpheus/rest_client.rb +17 -8
  79. metadata +9 -3
  80. data/lib/morpheus/api/custom_instance_types.rb +0 -55
@@ -1,6 +1,6 @@
1
1
 
2
2
  module Morpheus
3
3
  module Cli
4
- VERSION = "2.10.0"
4
+ VERSION = "2.10.1"
5
5
  end
6
6
  end
@@ -4,16 +4,15 @@ require 'morpheus/cli/cli_command'
4
4
  class Morpheus::Cli::VersionCommand
5
5
  include Morpheus::Cli::CliCommand
6
6
 
7
- cli_command_name :version
8
-
9
- def initialize
10
- end
7
+ set_command_name :version
8
+ def initialize
9
+ end
11
10
 
12
11
  def usage
13
12
  "morpheus version"
14
13
  end
15
14
 
16
- def handle(args)
15
+ def handle(args)
17
16
  options = {}
18
17
  optparse = OptionParser.new do|opts|
19
18
  opts.banner = usage
@@ -22,24 +21,23 @@ class Morpheus::Cli::VersionCommand
22
21
  end
23
22
  build_common_options(opts, options)
24
23
  end
25
- optparse.parse(args)
24
+ optparse.parse!(args)
26
25
 
27
- client_version = Morpheus::Cli::VERSION
28
- if options[:short]
29
- puts client_version
26
+ client_version = Morpheus::Cli::VERSION
27
+ if options[:short]
28
+ puts client_version
30
29
  else
31
- print cyan
30
+ print cyan
32
31
  banner = "" +
33
- " __ ___ __ \n" +
34
- " / |/ /__ _______ / / ___ __ _____\n" +
35
- " / /|_/ / _ \\/ __/ _ \\/ _ \\/ -_) // (_-<\n" +
36
- "/_/ /_/\\___/_/ / .__/_//_/\\__/\\_,_/___/\n" +
37
- "****************************************"
32
+ " __ ___ __ \n" +
33
+ " / |/ /__ _______ / / ___ __ _____\n" +
34
+ " / /|_/ / _ \\/ __/ _ \\/ _ \\/ -_) // (_-<\n" +
35
+ "/_/ /_/\\___/_/ / .__/_//_/\\__/\\_,_/___/\n" +
36
+ "****************************************"
38
37
  puts(banner)
39
38
  puts(" Client Version: #{client_version}")
40
39
  puts("****************************************")
41
- print reset
40
+ print reset,"\n"
42
41
  end
43
- end
44
-
42
+ end
45
43
  end
@@ -5,351 +5,532 @@ require 'optparse'
5
5
  require 'table_print'
6
6
  require 'morpheus/cli/cli_command'
7
7
 
8
+ # JD: I don't think a lot of this has ever worked, fix it up.
9
+
8
10
  class Morpheus::Cli::VirtualImages
9
- include Morpheus::Cli::CliCommand
10
-
11
- def initialize()
12
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
13
- end
14
-
15
- def connect(opts)
16
- if opts[:remote]
17
- @appliance_url = opts[:remote]
18
- @appliance_name = opts[:remote]
19
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials(opts)
20
- else
21
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials(opts)
22
- end
23
- @api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
24
- @virtual_images_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).virtual_images
25
-
26
- if @access_token.empty?
27
- print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
28
- exit 1
29
- end
30
- end
31
-
32
-
33
- def handle(args)
34
- if args.empty?
35
- puts "\nUsage: morpheus virtual-images [list,add, update,remove, details, lb-types]\n\n"
36
- return
37
- end
38
-
39
- case args[0]
40
- when 'list'
41
- list(args[1..-1])
42
- when 'add'
43
- add(args[1..-1])
44
- when 'update'
45
- # update(args[1..-1])
46
- when 'details'
47
- details(args[1..-1])
48
- when 'remove'
49
- remove(args[1..-1])
50
- when 'virtual-image-types'
51
- virtual_image_types(args[1..-1])
52
- else
53
- puts "\nUsage: morpheus virtual-images [list,add, update,remove, details, lb-types]\n\n"
54
- exit 127
55
- end
56
- end
57
-
58
- def list(args)
59
- options = {}
60
- optparse = OptionParser.new do|opts|
61
- opts.banner = "Usage: morpheus virtual-images list [-s] [-o] [-m] [-t]"
62
- opts.on( '-t', '--type IMAGE_TYPE', "Image Type" ) do |val|
63
- options[:imageType] = val.downcase
64
- end
65
-
66
- opts.on( '', '--all', "All Images" ) do |val|
67
- options[:filterType] = 'All'
68
- end
69
- opts.on( '', '--user', "User Images" ) do |val|
70
- options[:filterType] = 'User'
71
- end
72
- opts.on( '', '--system', "System Images" ) do |val|
73
- options[:filterType] = 'System'
74
- end
75
- build_common_options(opts, options, [:list, :json, :remote])
76
- end
77
- optparse.parse(args)
78
- connect(options)
79
- begin
80
- params = {}
81
- [:phrase, :offset, :max, :sort, :direction].each do |k|
82
- params[k] = options[k] unless options[k].nil?
83
- end
84
- if options[:imageType]
85
- params[:imageType] = options[:imageType]
86
- end
87
- if options[:filterType]
88
- params[:filterType] = options[:filterType]
89
- end
90
- json_response = @virtual_images_interface.get(params)
91
- if options[:json]
92
- print JSON.pretty_generate(json_response)
93
- else
94
- images = json_response['virtualImages']
95
- print "\n" ,cyan, bold, "Morpheus Virtual Images\n","=======================", reset, "\n\n"
96
- if images.empty?
97
- puts yellow,"No virtual images currently exist.",reset
98
- else
99
- print cyan
100
- image_table_data = images.collect do |image|
101
- {name: image['name'], id: image['id'], type: image['imageType'].upcase, source: image['userUploaded'] ? "#{green}UPLOADED#{cyan}" : (image['systemImage'] ? 'SYSTEM' : "#{white}SYNCED#{cyan}"), storage: !image['storageProvider'].nil? ? image['storageProvider']['name'] : 'Default', size: image['rawSize'].nil? ? 'Unknown' : "#{Filesize.from("#{image['rawSize']} B").pretty}"}
102
- end
103
- tp image_table_data, :id, :name, :type, :storage, :size, :source
104
- end
105
- print reset,"\n\n"
106
- end
107
-
108
-
109
- rescue RestClient::Exception => e
110
- print_rest_exception(e, options)
111
- exit 1
112
- end
113
- end
114
-
115
- def details(args)
116
- image_name = args[0]
117
- options = {}
118
- optparse = OptionParser.new do|opts|
119
- opts.banner = "Usage: morpheus virtual-images details [name]"
120
- build_common_options(opts, options, [:json, :remote])
121
- end
122
- if args.count < 1
123
- puts "\n#{optparse.banner}\n\n"
124
- exit 1
125
- end
126
- optparse.parse(args)
127
- connect(options)
128
- begin
129
- lb = find_lb_by_name(image_name)
130
-
131
- exit 1 if lb.nil?
132
- lb_type = find_lb_type_by_name(lb['type']['name'])
133
- if options[:json]
134
- puts JSON.pretty_generate({task:task})
135
- else
136
- print "\n", cyan, "Lb #{lb['name']} - #{lb['type']['name']}\n\n"
137
- lb_type['optionTypes'].sort { |x,y| x['displayOrder'].to_i <=> y['displayOrder'].to_i }.each do |optionType|
138
- puts " #{optionType['fieldLabel']} : " + (optionType['type'] == 'password' ? "#{task['taskOptions'][optionType['fieldName']] ? '************' : ''}" : "#{task['taskOptions'][optionType['fieldName']] || optionType['defaultValue']}")
139
- end
140
- print reset,"\n\n"
141
- end
142
- rescue RestClient::Exception => e
143
- print_rest_exception(e, options)
144
- exit 1
145
- end
146
- end
147
-
148
- def update(args)
149
- image_name = args[0]
150
- options = {}
151
- account_name = nil
152
- optparse = OptionParser.new do|opts|
153
- opts.banner = "Usage: morpheus tasks update [task] [options]"
154
- build_common_options(opts, options, [:options, :json, :remote])
155
- end
156
- if args.count < 1
157
- puts "\n#{optparse.banner}\n\n"
158
- exit 1
159
- end
160
- optparse.parse(args)
161
-
162
- connect(options)
163
-
164
- begin
165
-
166
-
167
- task = find_task_by_name_or_code_or_id(image_name)
168
- exit 1 if task.nil?
169
- lb_type = find_lb_type_by_name(task['type']['name'])
170
-
171
- #params = Morpheus::Cli::OptionTypes.prompt(add_user_option_types, options[:options], @api_client, options[:params]) # options[:params] is mysterious
172
- params = options[:options] || {}
173
-
174
- if params.empty?
175
- puts "\n#{optparse.banner}\n\n"
176
- option_lines = update_task_option_types(lb_type).collect {|it| "\t-O #{it['fieldContext'] ? (it['fieldContext'] + '.') : ''}#{it['fieldName']}=\"value\"" }.join("\n")
177
- puts "\nAvailable Options:\n#{option_lines}\n\n"
178
- exit 1
179
- end
180
-
181
- #puts "parsed params is : #{params.inspect}"
182
- task_keys = ['name']
183
- changes_payload = (params.select {|k,v| task_keys.include?(k) })
184
- task_payload = task
185
- if changes_payload
186
- task_payload.merge!(changes_payload)
187
- end
188
- puts params
189
- if params['taskOptions']
190
- task_payload['taskOptions'].merge!(params['taskOptions'])
191
- end
192
-
193
- request_payload = {task: task_payload}
194
- response = @virtual_images_interface.update(task['id'], request_payload)
195
- if options[:json]
196
- print JSON.pretty_generate(json_response)
197
- if !response['success']
198
- exit 1
199
- end
200
- else
201
- print "\n", cyan, "Task #{response['task']['name']} updated", reset, "\n\n"
202
- end
203
- rescue RestClient::Exception => e
204
- print_rest_exception(e, options)
205
- exit 1
206
- end
207
- end
208
-
209
-
210
- def virtual_image_types(args)
211
- options = {}
212
- optparse = OptionParser.new do|opts|
213
- opts.banner = "Usage: morpheus virtual-images lb-types"
214
- build_common_options(opts, options, [:json, :remote])
215
- end
216
- optparse.parse(args)
217
- connect(options)
218
- begin
219
- json_response = @virtual_images_interface.load_balancer_types()
220
- if options[:json]
221
- print JSON.pretty_generate(json_response)
222
- else
223
- lb_types = json_response['virtualImageTypes']
224
- print "\n" ,cyan, bold, "Morpheus Virtual Image Types\n","============================", reset, "\n\n"
225
- if lb_types.nil? || lb_types.empty?
226
- puts yellow,"No image types currently exist on this appliance. This could be a seed issue.",reset
227
- else
228
- print cyan
229
- lb_table_data = lb_types.collect do |lb_type|
230
- {name: lb_type['name'], id: lb_type['id'], code: lb_type['code']}
231
- end
232
- tp lb_table_data, :id, :name, :code
233
- end
234
-
235
- print reset,"\n\n"
236
- end
237
-
238
-
239
- rescue RestClient::Exception => e
240
- print_rest_exception(e, options)
241
- exit 1
242
- end
243
- end
244
-
245
- def add(args)
246
- image_name = args[0]
247
- lb_type_name = nil
248
- options = {}
249
- optparse = OptionParser.new do|opts|
250
- opts.banner = "Usage: morpheus virtual-images add [lb] -t LB_TYPE"
251
- opts.on( '-t', '--type LB_TYPE', "Lb Type" ) do |val|
252
- lb_type_name = val
253
- end
254
- build_common_options(opts, options, [:options, :json, :remote])
255
- end
256
- if args.count < 1
257
- puts "\n#{optparse.banner}\n\n"
258
- exit 1
259
- end
260
- optparse.parse(args)
261
- connect(options)
262
-
263
- if lb_type_name.nil?
264
- puts "LB Type must be specified...\n#{optparse.banner}"
265
- exit 1
266
- end
267
-
268
- lb_type = find_lb_type_by_name(lb_type_name)
269
- if lb_type.nil?
270
- puts "LB Type not found!"
271
- exit 1
272
- end
273
- input_options = Morpheus::Cli::OptionTypes.prompt(lb_type['optionTypes'],options[:options],@api_client, options[:params])
274
- payload = {task: {name: image_name, taskOptions: input_options['taskOptions'], type: {code: lb_type['code'], id: lb_type['id']}}}
275
- begin
276
- json_response = @virtual_images_interface.create(payload)
277
- if options[:json]
278
- print JSON.pretty_generate(json_response)
279
- else
280
- print "\n", cyan, "LB #{json_response['virtualImage']['name']} created successfully", reset, "\n\n"
281
- end
282
- rescue RestClient::Exception => e
283
- print_rest_exception(e, options)
284
- exit 1
285
- end
286
- end
287
-
288
- def remove(args)
289
- image_name = args[0]
290
- options = {}
291
- optparse = OptionParser.new do|opts|
292
- opts.banner = "Usage: morpheus virtual-images remove [name]"
293
- build_common_options(opts, options, [:auto_confirm, :json, :remote])
294
- end
295
- if args.count < 1
296
- puts "\n#{optparse.banner}\n\n"
297
- exit 1
298
- end
299
- optparse.parse(args)
300
- connect(options)
301
- begin
302
- image = find_image_by_name(image_name)
303
- exit 1 if image.nil?
304
- unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the virtual image #{image['name']}?")
305
- exit
306
- end
307
- json_response = @virtual_images_interface.destroy(image['id'])
308
- if options[:json]
309
- print JSON.pretty_generate(json_response)
310
- else
311
- print "\n", cyan, "Virtual Image #{image['name']} removed", reset, "\n\n"
312
- end
313
- rescue RestClient::Exception => e
314
- print_rest_exception(e, options)
315
- exit 1
316
- end
317
- end
318
-
319
-
320
- private
321
- def find_image_by_name(val)
322
- raise "find_image_by_name passed a bad name: #{val.inspect}" if val.to_s == ''
323
- results = @virtual_images_interface.get(val)
324
- result = nil
325
- if !results['virtualImages'].nil? && !results['virtualImages'].empty?
326
- result = results['virtualImages'][0]
327
- elsif val.to_i.to_s == val
328
- results = @virtual_images_interface.get(val.to_i)
329
- result = results['virtualImage']
330
- end
331
- if result.nil?
332
- print red,bold, "\nVirtual Image not found by '#{val}'\n\n",reset
333
- return nil
334
- end
335
- return result
336
- end
337
-
338
- def find_image_type_by_name(val)
339
- raise "find_,age_type_by_name passed a bad name: #{val.inspect}" if val.to_s == ''
340
- results = @virtual_images_interface.virtual_image_types(val)
341
- result = nil
342
- if !results['virtualImageTypes'].nil? && !results['virtualImageTypes'].empty?
343
- result = results['virtualImageTypes'][0]
344
- elsif val.to_i.to_s == val
345
- results = @virtual_images_interface.virtual_image_types(val.to_i)
346
- result = results['virtualImageType']
347
- end
348
- if result.nil?
349
- print red,bold, "\nImage Type not found by '#{val}'\n\n",reset
350
- return nil
351
- end
352
- return result
353
- end
11
+ include Morpheus::Cli::CliCommand
12
+
13
+ register_subcommands :list, :get, :add, :add_file, :remove_file, :update, :remove, :types => :virtual_image_types
14
+ alias_subcommand :details, :get
15
+ set_default_subcommand :list
16
+
17
+ # def initialize()
18
+ # # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
19
+ # end
20
+
21
+ def connect(opts)
22
+ @api_client = establish_remote_appliance_connection(opts)
23
+ @virtual_images_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).virtual_images
24
+ end
25
+
26
+ def handle(args)
27
+ handle_subcommand(args)
28
+ end
29
+
30
+ def list(args)
31
+ options = {}
32
+ optparse = OptionParser.new do|opts|
33
+ opts.banner = subcommand_usage()
34
+ opts.on( '-t', '--type IMAGE_TYPE', "Image Type" ) do |val|
35
+ options[:imageType] = val.downcase
36
+ end
37
+
38
+ opts.on( '', '--all', "All Images" ) do |val|
39
+ options[:filterType] = 'All'
40
+ end
41
+ opts.on( '', '--user', "User Images" ) do |val|
42
+ options[:filterType] = 'User'
43
+ end
44
+ opts.on( '', '--system', "System Images" ) do |val|
45
+ options[:filterType] = 'System'
46
+ end
47
+ build_common_options(opts, options, [:list, :json, :dry_run, :remote])
48
+ end
49
+ optparse.parse!(args)
50
+ connect(options)
51
+ begin
52
+ params = {}
53
+ [:phrase, :offset, :max, :sort, :direction].each do |k|
54
+ params[k] = options[k] unless options[k].nil?
55
+ end
56
+ if options[:imageType]
57
+ params[:imageType] = options[:imageType]
58
+ end
59
+ if options[:filterType]
60
+ params[:filterType] = options[:filterType]
61
+ end
62
+ if options[:dry_run]
63
+ print_dry_run @virtual_images_interface.dry.get(params)
64
+ return
65
+ end
66
+ json_response = @virtual_images_interface.get(params)
67
+ if options[:json]
68
+ print JSON.pretty_generate(json_response)
69
+ else
70
+ images = json_response['virtualImages']
71
+ print "\n" ,cyan, bold, "Morpheus Virtual Images\n","=======================", reset, "\n\n"
72
+ if images.empty?
73
+ puts yellow,"No virtual images currently exist.",reset
74
+ else
75
+ print cyan
76
+ image_table_data = images.collect do |image|
77
+ image_type = virtual_image_type_for_name_or_code(image['imageType'])
78
+ image_type_display = image_type ? "#{image_type['name']}" : image['imageType']
79
+ {name: image['name'], id: image['id'], type: image_type_display, source: image['userUploaded'] ? "#{green}UPLOADED#{cyan}" : (image['systemImage'] ? 'SYSTEM' : "#{white}SYNCED#{cyan}"), storage: !image['storageProvider'].nil? ? image['storageProvider']['name'] : 'Default', size: image['rawSize'].nil? ? 'Unknown' : "#{Filesize.from("#{image['rawSize']} B").pretty}"}
80
+ end
81
+ tp image_table_data, :id, :name, :type, :storage, :size, :source
82
+ print_results_pagination(json_response)
83
+ end
84
+ print reset,"\n"
85
+ end
86
+ rescue RestClient::Exception => e
87
+ print_rest_exception(e, options)
88
+ exit 1
89
+ end
90
+ end
91
+
92
+ def get(args)
93
+ options = {}
94
+ optparse = OptionParser.new do|opts|
95
+ opts.banner = subcommand_usage("[name]")
96
+ build_common_options(opts, options, [:json, :dry_run, :remote])
97
+ end
98
+ optparse.parse!(args)
99
+ if args.count < 1
100
+ puts optparse
101
+ exit 1
102
+ end
103
+ image_name = args[0]
104
+ connect(options)
105
+ begin
106
+ if options[:dry_run]
107
+ if args[0].to_s =~ /\A\d{1,}\Z/
108
+ print_dry_run @virtual_images_interface.dry.get(args[0].to_i)
109
+ else
110
+ print_dry_run @virtual_images_interface.dry.get({name:args[0]})
111
+ end
112
+ return
113
+ end
114
+ image = find_virtual_image_by_name_or_id(image_name)
115
+ exit 1 if image.nil?
116
+ # refetch
117
+ json_response = @virtual_images_interface.get(image['id'])
118
+ image = json_response['virtualImage']
119
+ image_files = json_response['cloudFiles'] || json_response['files']
120
+
121
+ if options[:json]
122
+ puts JSON.pretty_generate(json_response)
123
+ else
124
+ image_type = virtual_image_type_for_name_or_code(image['imageType'])
125
+ image_type_display = image_type ? "#{image_type['name']}" : image['imageType']
126
+ print "\n" ,cyan, bold, "Virtual Image Details\n","==================", reset, "\n\n"
127
+ print cyan
128
+ puts "ID: #{image['id']}"
129
+ puts "Name: #{image['name']}"
130
+ puts "Type: #{image_type_display}"
131
+ #puts "Date Created: #{format_local_dt(image['dateCreated'])}"
132
+ #puts "Last Updated: #{format_local_dt(image['lastUpdated'])}"
133
+ if image_files
134
+ puts "Files:"
135
+ image_files.each {|image_file|
136
+ pretty_filesize = Filesize.from("#{image_file['size']} B").pretty
137
+ print cyan," = #{image_file['name']} [#{pretty_filesize}]", "\n"
138
+ }
139
+ end
140
+ print reset,"\n"
141
+ end
142
+ rescue RestClient::Exception => e
143
+ print_rest_exception(e, options)
144
+ exit 1
145
+ end
146
+ end
147
+
148
+ # JD: I don't think this has ever worked
149
+ def update(args)
150
+ image_name = args[0]
151
+ options = {}
152
+ account_name = nil
153
+ optparse = OptionParser.new do|opts|
154
+ opts.banner = subcommand_usage("[name] [options]")
155
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
156
+ end
157
+ optparse.parse!(args)
158
+ if args.count < 1
159
+ puts optparse
160
+ exit 1
161
+ end
162
+
163
+ connect(options)
164
+ begin
165
+
166
+ image = find_virtual_image_by_name_or_id(image_name)
167
+ exit 1 if image.nil?
168
+
169
+ params = options[:options] || {}
170
+
171
+ if params.empty?
172
+ puts optparse
173
+ option_lines = update_virtual_image_option_types().collect {|it| "\t-O #{it['fieldContext'] ? (it['fieldContext'] + '.') : ''}#{it['fieldName']}=\"value\"" }.join("\n")
174
+ puts "\nAvailable Options:\n#{option_lines}\n\n"
175
+ exit 1
176
+ end
177
+
178
+ image_payload = {id: image['id']}
179
+ image_payload.merge(params)
180
+ # JD: what can be updated?
181
+ payload = {virtualImage: image_payload}
182
+ if options[:dry_run]
183
+ print_dry_run @virtual_images_interface.dry.update(image['id'], payload)
184
+ return
185
+ end
186
+ response = @virtual_images_interface.update(image['id'], payload)
187
+ if options[:json]
188
+ print JSON.pretty_generate(json_response)
189
+ if !response['success']
190
+ exit 1
191
+ end
192
+ else
193
+ print "\n", cyan, "Task #{response['task']['name']} updated", reset, "\n\n"
194
+ end
195
+ rescue RestClient::Exception => e
196
+ print_rest_exception(e, options)
197
+ exit 1
198
+ end
199
+ end
200
+
201
+ def virtual_image_types(args)
202
+ options = {}
203
+ optparse = OptionParser.new do|opts|
204
+ opts.banner = subcommand_usage()
205
+ build_common_options(opts, options, [:json, :dry_run, :remote])
206
+ end
207
+ optparse.parse!(args)
208
+ connect(options)
209
+ begin
210
+ params = {}
211
+ if options[:dry_run]
212
+ print_dry_run @virtual_images_interface.dry.virtual_image_types(params)
213
+ return
214
+ end
215
+ json_response = @virtual_images_interface.virtual_image_types(params)
216
+ if options[:json]
217
+ print JSON.pretty_generate(json_response)
218
+ else
219
+ image_types = json_response['virtualImageTypes']
220
+ print "\n" ,cyan, bold, "Morpheus Virtual Image Types\n","============================", reset, "\n\n"
221
+ if image_types.nil? || image_types.empty?
222
+ puts yellow,"No image types currently exist on this appliance. This could be a seed issue.",reset
223
+ else
224
+ print cyan
225
+ lb_table_data = image_types.collect do |lb_type|
226
+ {name: lb_type['name'], code: lb_type['code']}
227
+ end
228
+ tp lb_table_data, :name, :code
229
+ end
230
+
231
+ print reset,"\n\n"
232
+ end
233
+ rescue RestClient::Exception => e
234
+ print_rest_exception(e, options)
235
+ exit 1
236
+ end
237
+ end
238
+
239
+ def add(args)
240
+ image_type_name = nil
241
+ options = {}
242
+ optparse = OptionParser.new do|opts|
243
+ opts.banner = subcommand_usage("[name] -t TYPE")
244
+ opts.on( '-t', '--type TYPE', "Virtual Image Type" ) do |val|
245
+ image_type_name = val
246
+ end
247
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
248
+ end
249
+ optparse.parse!(args)
250
+ # if args.count < 1
251
+ # puts optparse
252
+ # exit 1
253
+ # end
254
+ image_name = args[0]
255
+ connect(options)
256
+
257
+ # if image_type_name.nil?
258
+ # puts "Virtual Image Type must be specified"
259
+ # puts optparse
260
+ # exit 1
261
+ # end
262
+
263
+ #
264
+ if image_name
265
+ options[:options] ||= {}
266
+ options[:options]['name'] ||= image_name
267
+ end
268
+
269
+ if image_type_name
270
+ image_type = virtual_image_type_for_name_or_code(image_type_name)
271
+ exit 1 if image_type.nil?
272
+ # options[:options] ||= {}
273
+ # options[:options]['imageType'] ||= image_type['code']
274
+ else
275
+ image_type_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'imageType', 'fieldLabel' => 'Image Type', 'type' => 'select', 'optionSource' => 'virtualImageTypes', 'required' => true, 'description' => 'Select Virtual Image Type.', 'displayOrder' => 2}],options[:options],@api_client,{})
276
+ image_type = virtual_image_type_for_name_or_code(image_type_prompt['imageType'])
277
+ end
278
+
279
+ begin
280
+ my_option_types = add_virtual_image_option_types(image_type)
281
+ params = Morpheus::Cli::OptionTypes.prompt(add_virtual_image_option_types(image_type), options[:options], @api_client, options[:params])
282
+ virtual_image_payload = {}.merge(params)
283
+ virtual_image_files = virtual_image_payload.delete('virtualImageFiles')
284
+ virtual_image_payload['imageType'] = image_type['code']
285
+ storage_provider_id = virtual_image_payload.delete('storageProviderId')
286
+ if !storage_provider_id.empty?
287
+ virtual_image_payload['storageProvider'] = {id: storage_provider_id}
288
+ end
289
+ payload = {virtualImage: virtual_image_payload}
290
+
291
+ if options[:dry_run]
292
+ print_dry_run @virtual_images_interface.dry.create(payload)
293
+ if !virtual_image_files.empty?
294
+ virtual_image_files.each do |key, filename|
295
+ print_dry_run @virtual_images_interface.dry.upload(":id", "(Contents of file #{filename})")
296
+ end
297
+ end
298
+ return
299
+ end
300
+
301
+ json_response = @virtual_images_interface.create(payload)
302
+ virtual_image = json_response['virtualImage']
303
+
304
+ if options[:json]
305
+ print JSON.pretty_generate(json_response)
306
+ elsif !options[:quiet]
307
+ print "\n", cyan, "Virtual Image #{virtual_image['name']} created successfully", reset, "\n\n"
308
+ end
309
+
310
+ # now upload the file, do this in the background maybe?
311
+ if !virtual_image_files.empty?
312
+ virtual_image_files.each do |key, filename|
313
+ unless options[:quiet]
314
+ print cyan, "Uploading file (#{key}) #{filename} ...", reset, "\n"
315
+ end
316
+ image_file = File.new(filename, 'rb')
317
+ upload_json_response = @virtual_images_interface.upload(virtual_image['id'], image_file)
318
+ if options[:json]
319
+ print JSON.pretty_generate(upload_json_response)
320
+ end
321
+ end
322
+ else
323
+ puts cyan, "No files uploaded.", reset
324
+ end
325
+
326
+ if !options[:json]
327
+ get([virtual_image['id']])
328
+ end
329
+
330
+ rescue RestClient::Exception => e
331
+ print_rest_exception(e, options)
332
+ exit 1
333
+ end
334
+ end
335
+
336
+ def add_file(args)
337
+ image_type_name = nil
338
+ options = {}
339
+ optparse = OptionParser.new do|opts|
340
+ opts.banner = subcommand_usage("[name] [filepath]")
341
+ build_common_options(opts, options, [:json, :dry_run, :remote])
342
+ end
343
+ optparse.parse!(args)
344
+ if args.count < 2
345
+ puts optparse
346
+ exit 1
347
+ end
348
+ image_name = args[0]
349
+ filename = args[1]
350
+
351
+ connect(options)
352
+
353
+ begin
354
+ image = find_virtual_image_by_name_or_id(image_name)
355
+ image_file = File.new(filename, 'rb')
356
+
357
+ if options[:dry_run]
358
+ print_dry_run @virtual_images_interface.dry.upload(image['id'], image_file)
359
+ return
360
+ end
361
+
362
+ unless options[:quiet]
363
+ print cyan, "Uploading file #{filename} ...", reset, "\n"
364
+ end
365
+
366
+ json_response = @virtual_images_interface.upload(image['id'], image_file)
367
+
368
+ if options[:json]
369
+ print JSON.pretty_generate(json_response)
370
+ elsif !options[:quiet]
371
+ print "\n", cyan, "Virtual Image #{image['name']} successfully updated.", reset, "\n\n"
372
+ get([image['id']])
373
+ end
374
+
375
+ rescue RestClient::Exception => e
376
+ print_rest_exception(e, options)
377
+ exit 1
378
+ end
379
+ end
380
+
381
+ def remove_file(args)
382
+ options = {}
383
+ optparse = OptionParser.new do|opts|
384
+ opts.banner = subcommand_usage("[name] [filename]")
385
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
386
+ end
387
+ optparse.parse!(args)
388
+ if args.count < 2
389
+ puts optparse
390
+ exit 1
391
+ end
392
+ image_name = args[0]
393
+ filename = args[1]
394
+ connect(options)
395
+ begin
396
+ image = find_virtual_image_by_name_or_id(image_name)
397
+ exit 1 if image.nil?
398
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the virtual image filename #{filename}?")
399
+ exit
400
+ end
401
+ if options[:dry_run]
402
+ print_dry_run @virtual_images_interface.dry.destroy_file(image['id'], filename)
403
+ return
404
+ end
405
+ json_response = @virtual_images_interface.destroy_file(image['id'], filename)
406
+ if options[:json]
407
+ print JSON.pretty_generate(json_response)
408
+ else
409
+ print "\n", cyan, "Virtual Image #{image['name']} filename #{filename} removed", reset, "\n\n"
410
+ end
411
+ rescue RestClient::Exception => e
412
+ print_rest_exception(e, options)
413
+ exit 1
414
+ end
415
+ end
416
+
417
+ def remove(args)
418
+ options = {}
419
+ optparse = OptionParser.new do|opts|
420
+ opts.banner = subcommand_usage("[name]")
421
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
422
+ end
423
+ optparse.parse!(args)
424
+ if args.count < 1
425
+ puts optparse
426
+ exit 1
427
+ end
428
+ image_name = args[0]
429
+ connect(options)
430
+ begin
431
+ image = find_virtual_image_by_name_or_id(image_name)
432
+ exit 1 if image.nil?
433
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the virtual image #{image['name']}?")
434
+ exit
435
+ end
436
+ if options[:dry_run]
437
+ print_dry_run @virtual_images_interface.dry.destroy(image['id'])
438
+ return
439
+ end
440
+ json_response = @virtual_images_interface.destroy(image['id'])
441
+ if options[:json]
442
+ print JSON.pretty_generate(json_response)
443
+ else
444
+ print "\n", cyan, "Virtual Image #{image['name']} removed", reset, "\n\n"
445
+ end
446
+ rescue RestClient::Exception => e
447
+ print_rest_exception(e, options)
448
+ exit 1
449
+ end
450
+ end
451
+
452
+
453
+ private
454
+ def find_virtual_image_by_name_or_id(val)
455
+ if val.to_s =~ /\A\d{1,}\Z/
456
+ return find_virtual_image_by_id(val)
457
+ else
458
+ return find_virtual_image_by_name(val)
459
+ end
460
+ end
461
+
462
+ def find_virtual_image_by_id(id)
463
+ begin
464
+ json_response = @virtual_images_interface.get(id.to_i)
465
+ return json_response['virtualImage']
466
+ rescue RestClient::Exception => e
467
+ if e.response && e.response.code == 404
468
+ print_red_alert "Virtual Image not found by id #{id}"
469
+ else
470
+ raise e
471
+ end
472
+ end
473
+ end
474
+
475
+ def find_virtual_image_by_name(name)
476
+ json_results = @virtual_images_interface.get({name: name.to_s})
477
+ if json_results['virtualImages'].empty?
478
+ print_red_alert "Virtual Image not found by name #{name}"
479
+ exit 1
480
+ end
481
+ virtual_image = json_results['virtualImages'][0]
482
+ return virtual_image
483
+ end
484
+
485
+
486
+ def get_available_virtual_image_types(refresh=false)
487
+ if !@available_virtual_image_types || refresh
488
+ @available_virtual_image_types = @virtual_images_interface.virtual_image_types['virtualImageTypes'] # || []
489
+ end
490
+ return @available_virtual_image_types
491
+ end
492
+ def virtual_image_type_for_name_or_code(name)
493
+ return get_available_virtual_image_types().find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
494
+ end
495
+
496
+ def add_virtual_image_option_types(image_type)
497
+ image_type_code = image_type['code']
498
+ # todo: make api provide virtualImageType and its optionTypes.
499
+ tmp_option_types = [
500
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
501
+ #{'fieldName' => 'imageType', 'fieldLabel' => 'Image Type', 'type' => 'select', 'optionSource' => 'virtualImageTypes', 'required' => true, 'description' => 'Select Virtual Image Type.', 'displayOrder' => 2},
502
+ {'fieldName' => 'osType', 'fieldLabel' => 'OS Type', 'type' => 'select', 'optionSource' => 'osTypes', 'required' => false, 'description' => 'Select OS Type.', 'displayOrder' => 3},
503
+ {'fieldName' => 'isCloudInit', 'fieldLabel' => 'Cloud Init Enabled?', 'type' => 'checkbox', 'required' => false, 'description' => 'Cloud Init Enabled?', 'displayOrder' => 4},
504
+ {'fieldName' => 'installAgent', 'fieldLabel' => 'Install Agent?', 'type' => 'checkbox', 'required' => false, 'description' => 'Cloud Init Enabled?', 'displayOrder' => 4},
505
+ {'fieldName' => 'sshUsername', 'fieldLabel' => 'SSH Username', 'type' => 'text', 'required' => false, 'description' => 'Enter an SSH Username', 'displayOrder' => 5},
506
+ {'fieldName' => 'sshPassword', 'fieldLabel' => 'SSH Password', 'type' => 'password', 'required' => false, 'description' => 'Enter an SSH Password', 'displayOrder' => 6},
507
+ {'fieldName' => 'storageProviderId', 'type' => 'select', 'fieldLabel' => 'Storage Provider', 'optionSource' => 'storageProviders', 'required' => false, 'description' => 'Select Storage Provider.', 'displayOrder' => 7},
508
+ {'fieldName' => 'userData', 'fieldLabel' => 'Cloud-Init User Data', 'type' => 'textarea', 'required' => false, 'displayOrder' => 10},
509
+ {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}], 'required' => false, 'description' => 'Visibility', 'category' => 'permissions', 'defaultValue' => 'private', 'displayOrder' => 40},
510
+ {'fieldName' => 'isAutoJoinDomain', 'fieldLabel' => 'Auto Join Domain?', 'type' => 'checkbox', 'required' => false, 'description' => 'Cloud Init Enabled?', 'category' => 'advanced', 'displayOrder' => 40},
511
+ {'fieldName' => 'virtioSupported', 'fieldLabel' => 'VirtIO Drivers Loaded?', 'type' => 'checkbox', 'defaultValue' => 'on', 'required' => false, 'description' => 'VirtIO Drivers Loaded?', 'category' => 'advanced', 'displayOrder' => 40}
512
+ ]
513
+
514
+ if image_type_code == 'ami'
515
+ tmp_option_types << {'fieldName' => 'externalId', 'fieldLabel' => 'AMI id', 'type' => 'text', 'required' => false, 'displayOrder' => 10}
516
+ tmp_option_types << {'fieldName' => 'imageFile', 'fieldLabel' => 'Image File', 'type' => 'file', 'required' => true, 'displayOrder' => 10}
517
+ elsif image_type_code == 'vmware'
518
+ tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageFile', 'fieldLabel' => 'OVF File', 'type' => 'file', 'required' => true, 'displayOrder' => 10}
519
+ tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageDescriptorFile', 'fieldLabel' => 'VMDK File', 'type' => 'file', 'required' => false, 'displayOrder' => 10}
520
+ elsif image_type_code == 'pxe'
521
+ tmp_option_types << {'fieldName' => 'config.menu', 'fieldLabel' => 'Menu', 'type' => 'text', 'required' => false, 'displayOrder' => 10}
522
+ tmp_option_types << {'fieldName' => 'imagePath', 'fieldLabel' => 'Image Path', 'type' => 'text', 'required' => true, 'displayOrder' => 10}
523
+ tmp_option_types.reject! {|opt| ['isCloudInit', 'installAgent', 'sshUsername', 'sshPassword'].include?(opt['fieldName'])}
524
+ else
525
+ tmp_option_types << {'fieldContext' => 'virtualImageFiles', 'fieldName' => 'imageFile', 'fieldLabel' => 'Image File', 'type' => 'file', 'required' => true, 'description' => 'Choose an image file to upload', 'displayOrder' => 10}
526
+ end
527
+
528
+ return tmp_option_types
529
+ end
530
+
531
+ # JD: what can be updated?
532
+ def update_virtual_image_option_types
533
+ []
534
+ end
354
535
 
355
536
  end