morpheus-cli 2.10.0 → 2.10.1

Sign up to get free protection for your applications and to get access to all the features.
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