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
@@ -3,379 +3,606 @@ require 'io/console'
3
3
  require 'rest_client'
4
4
  require 'optparse'
5
5
  require 'morpheus/cli/cli_command'
6
+ require 'morpheus/cli/mixins/infrastructure_helper'
6
7
  require 'morpheus/cli/option_types'
7
- require 'json'
8
8
 
9
9
  class Morpheus::Cli::Clouds
10
- include Morpheus::Cli::CliCommand
11
-
12
- def initialize()
13
- @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
14
-
15
- end
16
-
17
-
18
- def connect(opts)
19
- if opts[:remote]
20
- @appliance_url = opts[:remote]
21
- @appliance_name = opts[:remote]
22
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials(opts)
23
- else
24
- @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials(opts)
25
- end
26
- @api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
27
- @clouds_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).clouds
28
- @groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
29
- @cloud_types = @clouds_interface.cloud_types['zoneTypes']
30
- if @access_token.empty?
31
- print_red_alert "Invalid Credentials. Unable to acquire access token. Please verify your credentials and try again."
32
- return 1
33
- end
34
- end
35
-
36
- def handle(args)
37
-
38
- if args.empty?
39
- puts "\nUsage: morpheus clouds [list,add,remove,firewall_disable,firewall_enable,security_groups,apply_security_groups] [name]\n\n"
40
- return
41
- end
42
-
43
- case args[0]
44
- when 'list'
45
- list(args[1..-1])
46
- when 'add'
47
- add(args[1..-1])
48
- when 'remove'
49
- remove(args[1..-1])
50
- when 'firewall_disable'
51
- firewall_disable(args[1..-1])
52
- when 'firewall_enable'
53
- firewall_enable(args[1..-1])
54
- when 'security_groups'
55
- security_groups(args[1..-1])
56
- when 'apply_security_groups'
57
- apply_security_groups(args[1..-1])
58
- else
59
- puts "\nUsage: morpheus clouds [list,add,remove,firewall_disable,firewall_enable,security_groups,apply_security_groups] [name]\n\n"
60
- exit 127 #Command now foud exit code
61
- end
62
- end
63
-
64
- def add(args)
65
- options = {}
66
- params = {zone_type: 'standard'}
67
- optparse = OptionParser.new do|opts|
68
- opts.banner = "Usage: morpheus clouds add [name] --group GROUP --type TYPE"
69
- opts.on( '-g', '--group GROUP', "Group Name" ) do |group|
70
- params[:group] = group
71
- end
72
- opts.on( '-t', '--type TYPE', "Cloud Type" ) do |zone_type|
73
- params[:zone_type] = zone_type
74
- end
75
- opts.on( '-d', '--description DESCRIPTION', "Description (optional)" ) do |desc|
76
- params[:description] = desc
77
- end
78
- build_common_options(opts, options, [:options, :json, :remote])
79
- end
80
- optparse.parse!(args)
81
-
82
- if args.count < 1
83
- puts "\n#{optparse.banner}\n\n"
84
- exit 1
85
- end
86
-
87
- connect(options)
88
- zone = {name: args[0], description: params[:description]}
89
- if !params[:group].nil?
90
- group = find_group_by_name(params[:group])
91
- if !group.nil?
92
- zone['groupId'] = group['id']
93
- end
94
- end
95
-
96
- if !params[:zone_type].nil?
97
- cloud_type = cloud_type_for_name(params[:zone_type])
98
- zone['zoneType'] = {code: cloud_type['code']}
99
- end
100
-
101
- begin
102
- zone.merge!(Morpheus::Cli::OptionTypes.prompt(cloud_type['optionTypes'],options[:options],@api_client))
103
- json_response = @clouds_interface.create(zone)
104
- if options[:json]
105
- print JSON.pretty_generate(json_response)
106
- print "\n"
107
- else
108
- list([])
109
- end
110
- rescue RestClient::Exception => e
111
- print_rest_exception(e, options)
112
- exit 1
113
- end
114
- end
115
-
116
- def remove(args)
117
- options = {}
118
- optparse = OptionParser.new do|opts|
119
- opts.banner = "Usage: morpheus clouds remove [name] --group GROUP"
120
- opts.on( '-g', '--group GROUP', "Group Name" ) do |group|
121
- options[:group] = group
122
- end
123
- build_common_options(opts, options, [:auto_confirm, :json, :remote])
124
- end
125
- optparse.parse(args)
126
-
127
- if args.count < 2
128
- puts "\n#{optparse.banner}\n\n"
129
- return
130
- end
131
-
132
- connect(options)
133
- if !options[:group].nil?
134
- group = find_group_by_name(options[:group])
135
- if !group.nil?
136
- options[:groupId] = group['id']
137
- else
138
- puts "\nGroup #{options[:group]} not found!"
139
- exit 1
140
- end
141
- end
142
-
143
-
144
- begin
145
- zone_results = @clouds_interface.get({name: args[0]})
146
- if zone_results['zones'].empty?
147
- puts "Zone not found by name #{args[0]}"
148
- exit 1
149
- end
150
- cloud = zone_results['zones'][0]
151
- unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the cloud #{cloud['name']}?")
152
- exit
153
- end
154
- json_response = @clouds_interface.destroy(cloud['id'])
155
- if options[:json]
156
- print JSON.pretty_generate(json_response)
157
- print "\n"
158
- else
159
- list([])
160
- end
161
- rescue RestClient::Exception => e
162
- print_rest_exception(e, options)
163
- exit 1
164
- end
165
- end
166
-
167
- def list(args)
168
- options={}
169
- optparse = OptionParser.new do|opts|
170
- opts.banner = "Usage: morpheus clouds list"
171
- opts.on( '-g', '--group GROUP', "Group Name" ) do |group|
172
- options[:group] = group
173
- end
174
- build_common_options(opts, options, [:list, :json, :remote])
175
- end
176
- optparse.parse(args)
177
- connect(options)
178
- begin
179
- params = {}
180
- if !options[:group].nil?
181
- group = find_group_by_name(options[:group])
182
- if !group.nil?
183
- params['groupId'] = group['id']
184
- end
185
- end
186
-
187
- json_response = @clouds_interface.get(params)
188
- clouds = json_response['zones']
189
- if options[:json]
190
- print JSON.pretty_generate(json_response)
191
- print "\n"
192
- else
193
- print "\n" ,cyan, bold, "Morpheus Clouds\n","==================", reset, "\n\n"
194
- if clouds.empty?
195
- puts yellow,"No clouds currently configured.",reset
196
- else
197
- clouds_table =clouds.collect do |cloud|
198
- status = nil
199
- if cloud['status'] == 'ok'
200
- status = "#{green}OK#{cyan}"
201
- elsif cloud['status'].nil?
202
- status = "#{white}UNKNOWN#{cyan}"
203
- else
204
- status = "#{red}#{cloud['status'] ? cloud['status'].upcase : 'N/A'}#{cloud['statusMessage'] ? "#{cyan} - #{cloud['statusMessage']}" : ''}#{cyan}"
205
- end
206
- {id: cloud['id'], name: cloud['name'], type: cloud_type_for_id(cloud['zoneTypeId']), location: cloud['location'], status: status}
207
- # print cyan, "= [#{server['id']}] #{server['name']} - #{server['computeServerType'] ? server['computeServerType']['name'] : 'unmanaged'} (#{server['status']}) Power: ", power_state, "\n"
208
- end
209
- print cyan
210
- tp clouds_table, :id, :name, :type, :location, :status
211
- print reset,"\n\n"
212
- end
213
-
214
-
215
-
216
- end
217
- rescue => e
218
- puts "Error Communicating with the Appliance. Please try again later. #{e}"
219
- exit 1
220
- end
221
- end
222
-
223
- def firewall_disable(args)
224
- if args.count < 1
225
- puts "\nUsage: morpheus clouds firewall_disable [name]\n\n"
226
- return
227
- end
228
- begin
229
- zone_results = @clouds_interface.get({name: args[0]})
230
- if zone_results['zones'].empty?
231
- puts "Zone not found by name #{args[0]}"
232
- exit 1
233
- end
234
- @clouds_interface.firewall_disable(zone_results['zones'][0]['id'])
235
- security_groups([args[0]])
236
- rescue RestClient::Exception => e
237
- print_rest_exception(e, options)
238
- exit 1
239
- end
240
- end
241
-
242
- def firewall_enable(args)
243
- if args.count < 1
244
- puts "\nUsage: morpheus clouds firewall_enable [name]\n\n"
245
- return
246
- end
247
- begin
248
- zone_results = @clouds_interface.get({name: args[0]})
249
- if zone_results['zones'].empty?
250
- puts "Zone not found by name #{args[0]}"
251
- exit 1
252
- end
253
- @clouds_interface.firewall_enable(zone_results['zones'][0]['id'])
254
- security_groups([args[0]])
255
- rescue RestClient::Exception => e
256
- print_rest_exception(e, options)
257
- exit 1
258
- end
259
- end
260
-
261
- def security_groups(args)
262
- if args.count < 1
263
- puts "\nUsage: morpheus clouds security_groups [name]\n\n"
264
- return
265
- end
266
- begin
267
- zone_results = @clouds_interface.get({name: args[0]})
268
- if zone_results['zones'].empty?
269
- puts "Zone not found by name #{args[0]}"
270
- exit 1
271
- end
272
-
273
- zone_id = zone_results['zones'][0]['id']
274
- json_response = @clouds_interface.security_groups(zone_id)
275
-
276
- securityGroups = json_response['securityGroups']
277
- print "\n" ,cyan, bold, "Morpheus Security Groups for Zone:#{zone_id}\n","==================", reset, "\n\n"
278
- print cyan, "Firewall Enabled=#{json_response['firewallEnabled']}\n\n"
279
- if securityGroups.empty?
280
- puts yellow,"No security groups currently applied.",reset
281
- else
282
- securityGroups.each do |securityGroup|
283
- print cyan, "= #{securityGroup['id']} (#{securityGroup['name']}) - (#{securityGroup['description']})\n"
284
- end
285
- end
286
- print reset,"\n\n"
287
-
288
- rescue RestClient::Exception => e
289
- print_rest_exception(e, options)
290
- exit 1
291
- end
292
- end
293
-
294
- def apply_security_groups(args)
295
- usage = <<-EOF
296
- Usage: morpheus clouds apply_security_groups [name] [options]
297
- EOF
298
- if args.count < 1
299
- puts usage
300
- exit 1
301
- end
302
-
303
- options = {}
304
- clear_or_secgroups_specified = false
305
- optparse = OptionParser.new do|opts|
306
- opts.banner = usage
307
- opts.on( '-c', '--clear', "Clear all security groups" ) do
308
- options[:securityGroupIds] = []
309
- clear_or_secgroups_specified = true
310
- end
311
- opts.on( '-s', '--secgroups SECGROUPS', "Apply the specified comma separated security group ids" ) do |secgroups|
312
- options[:securityGroupIds] = secgroups.split(",")
313
- clear_or_secgroups_specified = true
314
- end
315
- opts.on( '-h', '--help', "Prints this help" ) do
316
- puts opts
317
- exit
318
- end
319
- end
320
- optparse.parse(args)
321
-
322
- if !clear_or_secgroups_specified
323
- puts usage
324
- exit
325
- end
326
-
327
- begin
328
- zone_results = @clouds_interface.get({name: args[0]})
329
- if zone_results['zones'].empty?
330
- puts "Zone not found by name #{args[0]}"
331
- exit 1
332
- end
333
-
334
- @clouds_interface.apply_security_groups(zone_results['zones'][0]['id'], options)
335
- security_groups([args[0]])
336
- rescue RestClient::Exception => e
337
- print_rest_exception(e, options)
338
- exit 1
339
- end
340
- end
341
-
342
- private
343
-
344
- def cloud_type_for_id(id)
345
- if !@cloud_types.empty?
346
- zone_type = @cloud_types.find { |z| z['id'].to_i == id.to_i}
347
- if !zone_type.nil?
348
- return zone_type['name']
349
- end
350
- end
351
- return nil
352
- end
353
-
354
- def cloud_type_for_name(name)
355
- if !@cloud_types.empty?
356
- zone_type = @cloud_types.find { |z| z['name'].downcase == name.downcase || z['code'].downcase == name.downcase}
357
- if !zone_type.nil?
358
- return zone_type
359
- end
360
- end
361
- return nil
362
- end
363
-
364
- def find_group_by_name(name)
365
- group_results = @groups_interface.get(name)
366
- if group_results['groups'].empty?
367
- puts "Group not found by name #{name}"
368
- return nil
369
- end
370
- return group_results['groups'][0]
371
- end
372
-
373
- def find_group_by_id(id)
374
- group_results = @groups_interface.get(id)
375
- if group_results['groups'].empty?
376
- puts "Group not found by id #{id}"
377
- return nil
378
- end
379
- return group_results['groups'][0]
380
- end
10
+ include Morpheus::Cli::CliCommand
11
+ include Morpheus::Cli::InfrastructureHelper
12
+
13
+ register_subcommands :list, :get, :add, :update, :remove, :firewall_disable, :firewall_enable, :security_groups, :apply_security_groups, :types => :list_cloud_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
+ @clouds_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).clouds
24
+ @groups_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).groups
25
+ @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
26
+ # preload stuff
27
+ get_available_cloud_types()
28
+ end
29
+
30
+ def handle(args)
31
+ handle_subcommand(args)
32
+ end
33
+
34
+ def list(args)
35
+ options={}
36
+ params = {}
37
+ optparse = OptionParser.new do|opts|
38
+ opts.banner = subcommand_usage()
39
+ opts.on( '-g', '--group GROUP', "Group Name" ) do |group|
40
+ options[:group] = group
41
+ end
42
+ opts.on( '-t', '--type TYPE', "Cloud Type" ) do |val|
43
+ options[:zone_type] = val
44
+ end
45
+ build_common_options(opts, options, [:list, :json, :dry_run, :remote])
46
+ end
47
+ optparse.parse!(args)
48
+ connect(options)
49
+ begin
50
+ [:phrase, :offset, :max, :sort, :direction].each do |k|
51
+ params[k] = options[k] unless options[k].nil?
52
+ end
53
+ if options[:zone_type]
54
+ cloud_type = cloud_type_for_name(options[:zone_type])
55
+ params[:type] = cloud_type['code']
56
+ end
57
+ if !options[:group].nil?
58
+ group = find_group_by_name(options[:group])
59
+ if !group.nil?
60
+ params['groupId'] = group['id']
61
+ end
62
+ end
63
+
64
+ if options[:dry_run]
65
+ print_dry_run @clouds_interface.dry.get(params)
66
+ return
67
+ end
68
+
69
+ json_response = @clouds_interface.get(params)
70
+ if options[:json]
71
+ print JSON.pretty_generate(json_response)
72
+ print "\n"
73
+ else
74
+ clouds = json_response['zones']
75
+ print "\n" ,cyan, bold, "Morpheus Clouds\n","==================", reset, "\n\n"
76
+ if clouds.empty?
77
+ puts yellow,"No clouds found.",reset
78
+ else
79
+ print_clouds_table(clouds)
80
+ print_results_pagination(json_response)
81
+ end
82
+ print reset,"\n"
83
+ end
84
+ rescue RestClient::Exception => e
85
+ print_rest_exception(e, options)
86
+ exit 1
87
+ end
88
+ end
89
+
90
+ def get(args)
91
+ options = {}
92
+ optparse = OptionParser.new do|opts|
93
+ opts.banner = subcommand_usage("[name]")
94
+ build_common_options(opts, options, [:json, :dry_run, :remote])
95
+ end
96
+ optparse.parse!(args)
97
+ if args.count < 1
98
+ puts optparse.banner
99
+ exit 1
100
+ end
101
+ connect(options)
102
+ begin
103
+ cloud = find_cloud_by_name_or_id(args[0])
104
+ #json_response = {'zone' => cloud}
105
+ if options[:dry_run]
106
+ print_dry_run @clouds_interface.dry.get(cloud['id'])
107
+ return
108
+ end
109
+ json_response = @clouds_interface.get(cloud['id'])
110
+ cloud = json_response['zone']
111
+ server_counts = json_response['serverCounts']
112
+ if options[:json]
113
+ print JSON.pretty_generate(json_response), "\n"
114
+ return
115
+ end
116
+ cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
117
+ print "\n" ,cyan, bold, "Cloud Details\n","==================", reset, "\n\n"
118
+ print cyan
119
+ puts "ID: #{cloud['id']}"
120
+ puts "Name: #{cloud['name']}"
121
+ puts "Type: #{cloud_type ? cloud_type['name'] : ''}"
122
+ puts "Code: #{cloud['code']}"
123
+ puts "Location: #{cloud['location']}"
124
+ puts "Visibility: #{cloud['visibility'].to_s.capitalize}"
125
+ puts "Groups: #{cloud['groups'].collect {|it| it.instance_of?(Hash) ? it['name'] : it.to_s }.join(', ')}"
126
+ status = nil
127
+ if cloud['status'] == 'ok'
128
+ status = "#{green}OK#{cyan}"
129
+ elsif cloud['status'].nil?
130
+ status = "#{white}UNKNOWN#{cyan}"
131
+ else
132
+ status = "#{red}#{cloud['status'] ? cloud['status'].upcase : 'N/A'}#{cloud['statusMessage'] ? "#{cyan} - #{cloud['statusMessage']}" : ''}#{cyan}"
133
+ end
134
+ puts "Status: #{status}"
135
+
136
+ print "\n" ,cyan, "Cloud Servers (#{cloud['serverCount']})\n","==================", reset, "\n\n"
137
+ print cyan
138
+ if server_counts
139
+ print "Container Hosts: #{server_counts['containerHost']}".center(20)
140
+ print "Hypervisors: #{server_counts['hypervisor']}".center(20)
141
+ print "Bare Metal: #{server_counts['baremetal']}".center(20)
142
+ print "Virtual Machines: #{server_counts['vm']}".center(20)
143
+ print "Unmanaged: #{server_counts['unmanaged']}".center(20)
144
+ print "\n"
145
+ end
146
+
147
+ print reset,"\n"
148
+
149
+ #puts instance
150
+ rescue RestClient::Exception => e
151
+ print_rest_exception(e, options)
152
+ exit 1
153
+ end
154
+ end
155
+
156
+ def add(args)
157
+ options = {}
158
+ params = {}
159
+ optparse = OptionParser.new do|opts|
160
+ opts.banner = subcommand_usage("[name] --group GROUP --type TYPE")
161
+ opts.on( '-g', '--group GROUP', "Group Name" ) do |val|
162
+ params[:group] = val
163
+ end
164
+ opts.on( '-t', '--type TYPE', "Cloud Type" ) do |val|
165
+ params[:zone_type] = val
166
+ end
167
+ opts.on( '-d', '--description DESCRIPTION', "Description (optional)" ) do |desc|
168
+ params[:description] = desc
169
+ end
170
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
171
+ end
172
+ optparse.parse!(args)
173
+ # if args.count < 1
174
+ # puts optparse
175
+ # exit 1
176
+ # end
177
+ connect(options)
178
+
179
+ cloud_payload = {name: args[0], description: params[:description]}
180
+
181
+ begin
182
+
183
+ # use active group by default
184
+ params[:group] ||= @active_group_id
185
+
186
+ # Group
187
+ group_id = nil
188
+ group = params[:group] ? find_group_by_name_or_id(params[:group]) : nil
189
+ if group
190
+ group_id = group["id"]
191
+ else
192
+ # print_red_alert "Group not found or specified!"
193
+ # exit 1
194
+ groups_dropdown = @groups_interface.get({})['groups'].collect {|it| {'name' => it["name"], 'value' => it["id"]} }
195
+ group_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'type' => 'select', 'fieldLabel' => 'Group', 'selectOptions' => groups_dropdown, 'required' => true, 'description' => 'Select Group.'}],options[:options],@api_client,{})
196
+ group_id = group_prompt['group']
197
+ end
198
+ cloud_payload['groupId'] = group_id
199
+ # todo: pass groups as an array instead
200
+
201
+ # Cloud Name
202
+
203
+ if args[0]
204
+ cloud_payload[:name] = args[0]
205
+ options[:options]['name'] = args[0] # to skip prompt
206
+ elsif !options[:no_prompt]
207
+ # name_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true}], options[:options])
208
+ # cloud_payload[:name] = name_prompt['name']
209
+ end
210
+
211
+ # Cloud Type
212
+
213
+ cloud_type = nil
214
+ if params[:zone_type]
215
+ cloud_type = cloud_type_for_name(params[:zone_type])
216
+ elsif !options[:no_prompt]
217
+ # print_red_alert "Cloud Type not found or specified!"
218
+ # exit 1
219
+ cloud_types_dropdown = cloud_types_for_dropdown
220
+ cloud_type_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'select', 'fieldLabel' => 'Cloud Type', 'selectOptions' => cloud_types_dropdown, 'required' => true, 'description' => 'Select Cloud Type.'}],options[:options],@api_client,{})
221
+ cloud_type_code = cloud_type_prompt['type']
222
+ cloud_type = cloud_type_for_name(cloud_type_code) # this does work
223
+ end
224
+ if !cloud_type
225
+ print_red_alert "A cloud type is required."
226
+ exit 1
227
+ end
228
+ cloud_payload[:zoneType] = {code: cloud_type['code']}
229
+
230
+ all_option_types = add_cloud_option_types(cloud_type)
231
+ params = Morpheus::Cli::OptionTypes.prompt(all_option_types, options[:options], @api_client, {zoneTypeId: cloud_type['id']})
232
+ cloud_payload.merge!(params)
233
+ payload = {zone: cloud_payload}
234
+ if options[:dry_run]
235
+ print_dry_run @clouds_interface.dry.create(payload)
236
+ return
237
+ end
238
+ json_response = @clouds_interface.create(payload)
239
+ cloud = json_response['zone']
240
+ if options[:json]
241
+ print JSON.pretty_generate(json_response)
242
+ print "\n"
243
+ else
244
+ #list([])
245
+ get([cloud["id"]])
246
+ end
247
+ rescue RestClient::Exception => e
248
+ print_rest_exception(e, options)
249
+ exit 1
250
+ end
251
+ end
252
+
253
+ def update(args)
254
+ options = {}
255
+ params = {}
256
+ optparse = OptionParser.new do|opts|
257
+ opts.banner = subcommand_usage("[name] [options]")
258
+ # opts.on( '-g', '--group GROUP', "Group Name" ) do |val|
259
+ # params[:group] = val
260
+ # end
261
+ # opts.on( '-t', '--type TYPE', "Cloud Type" ) do |val|
262
+ # params[:zone_type] = val
263
+ # end
264
+ # opts.on( '-d', '--description DESCRIPTION', "Description (optional)" ) do |desc|
265
+ # params[:description] = desc
266
+ # end
267
+ build_common_options(opts, options, [:options, :json, :dry_run, :remote])
268
+ end
269
+ optparse.parse!(args)
270
+ if args.count < 1
271
+ puts optparse
272
+ exit 1
273
+ end
274
+ connect(options)
275
+ begin
276
+ cloud = find_cloud_by_name_or_id(args[0])
277
+ cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
278
+ cloud_payload = {id: cloud['id']}
279
+ all_option_types = update_cloud_option_types(cloud_type)
280
+ #params = Morpheus::Cli::OptionTypes.prompt(all_option_types, options[:options], @api_client, {zoneTypeId: cloud_type['id']})
281
+ params = options[:options] || {}
282
+ if params.empty?
283
+ puts optparse.banner
284
+ print_available_options(all_option_types)
285
+ exit 1
286
+ end
287
+ cloud_payload.merge!(params)
288
+ payload = {zone: cloud_payload}
289
+
290
+ if options[:dry_run]
291
+ print_dry_run @clouds_interface.dry.update(cloud['id'], payload)
292
+ return
293
+ end
294
+ json_response = @clouds_interface.update(cloud['id'], payload)
295
+ if options[:json]
296
+ print JSON.pretty_generate(json_response)
297
+ print "\n"
298
+ else
299
+ #list([])
300
+ get([cloud["id"]])
301
+ end
302
+ rescue RestClient::Exception => e
303
+ print_rest_exception(e, options)
304
+ exit 1
305
+ end
306
+ end
307
+
308
+ def remove(args)
309
+ options = {}
310
+ query_params = {}
311
+ optparse = OptionParser.new do|opts|
312
+ opts.banner = subcommand_usage("[name]")
313
+ opts.on( '-f', '--force', "Force Remove" ) do
314
+ query_params[:force] = 'on'
315
+ end
316
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
317
+ end
318
+ optparse.parse!(args)
319
+ if args.count < 1
320
+ puts optparse
321
+ return
322
+ end
323
+ connect(options)
324
+ begin
325
+ cloud = find_cloud_by_name_or_id(args[0])
326
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the cloud #{cloud['name']}?")
327
+ exit
328
+ end
329
+ json_response = @clouds_interface.destroy(cloud['id'], query_params)
330
+ if options[:json]
331
+ print JSON.pretty_generate(json_response)
332
+ print "\n"
333
+ elsif !options[:quiet]
334
+ print_green_success "Removed cloud #{cloud['name']}"
335
+ #list([])
336
+ end
337
+ rescue RestClient::Exception => e
338
+ print_rest_exception(e, options)
339
+ exit 1
340
+ end
341
+ end
342
+
343
+ def firewall_disable(args)
344
+ options = {}
345
+ clear_or_secgroups_specified = false
346
+ optparse = OptionParser.new do|opts|
347
+ opts.banner = subcommand_usage("[name]")
348
+ build_common_options(opts, options, [:json, :dry_run, :remote])
349
+ end
350
+ optparse.parse!(args)
351
+ if args.count < 1
352
+ puts optparse
353
+ return
354
+ end
355
+ connect(options)
356
+ begin
357
+ cloud = find_cloud_by_name_or_id(args[0])
358
+ if options[:dry_run]
359
+ print_dry_run @clouds_interface.dry.firewall_disable(cloud['id'])
360
+ return
361
+ end
362
+ json_response = @clouds_interface.firewall_disable(cloud['id'])
363
+ if options[:json]
364
+ print JSON.pretty_generate(json_response)
365
+ print "\n"
366
+ return
367
+ end
368
+ security_groups([args[0]])
369
+ rescue RestClient::Exception => e
370
+ print_rest_exception(e, options)
371
+ exit 1
372
+ end
373
+ end
374
+
375
+ def firewall_enable(args)
376
+ options = {}
377
+ clear_or_secgroups_specified = false
378
+ optparse = OptionParser.new do|opts|
379
+ opts.banner = subcommand_usage("[name]")
380
+ build_common_options(opts, options, [:json, :dry_run, :remote])
381
+ end
382
+ optparse.parse!(args)
383
+ if args.count < 1
384
+ puts optparse
385
+ return
386
+ end
387
+ connect(options)
388
+ begin
389
+ cloud = find_cloud_by_name_or_id(args[0])
390
+ if options[:dry_run]
391
+ print_dry_run @clouds_interface.dry.firewall_enable(cloud['id'])
392
+ return
393
+ end
394
+ json_response = @clouds_interface.firewall_enable(cloud['id'])
395
+ if options[:json]
396
+ print JSON.pretty_generate(json_response)
397
+ print "\n"
398
+ return
399
+ end
400
+ security_groups([args[0]])
401
+ rescue RestClient::Exception => e
402
+ print_rest_exception(e, options)
403
+ exit 1
404
+ end
405
+ end
406
+
407
+ def security_groups(args)
408
+ options = {}
409
+ clear_or_secgroups_specified = false
410
+ optparse = OptionParser.new do|opts|
411
+ opts.banner = subcommand_usage("[name]")
412
+ build_common_options(opts, options, [:json, :dry_run, :remote])
413
+ end
414
+ optparse.parse!(args)
415
+ if args.count < 1
416
+ puts optparse
417
+ return
418
+ end
419
+ connect(options)
420
+ begin
421
+ cloud = find_cloud_by_name_or_id(args[0])
422
+ zone_id = cloud['id']
423
+ if options[:dry_run]
424
+ print_dry_run @clouds_interface.dry.security_groups(zone_id)
425
+ return
426
+ end
427
+ json_response = @clouds_interface.security_groups(zone_id)
428
+ if options[:json]
429
+ print JSON.pretty_generate(json_response)
430
+ print "\n"
431
+ return
432
+ end
433
+ securityGroups = json_response['securityGroups']
434
+ print "\n" ,cyan, bold, "Morpheus Security Groups for Cloud: #{cloud['name']}\n","==================", reset, "\n\n"
435
+ print cyan, "Firewall Enabled=#{json_response['firewallEnabled']}\n\n"
436
+ if securityGroups.empty?
437
+ puts yellow,"No security groups currently applied.",reset
438
+ else
439
+ securityGroups.each do |securityGroup|
440
+ print cyan, "= #{securityGroup['id']} (#{securityGroup['name']}) - (#{securityGroup['description']})\n"
441
+ end
442
+ end
443
+ print reset,"\n"
444
+
445
+ rescue RestClient::Exception => e
446
+ print_rest_exception(e, options)
447
+ exit 1
448
+ end
449
+ end
450
+
451
+ def apply_security_groups(args)
452
+ options = {}
453
+ clear_or_secgroups_specified = false
454
+ optparse = OptionParser.new do|opts|
455
+ opts.banner = subcommand_usage("[name] [-s] [--clear]")
456
+ opts.on( '-c', '--clear', "Clear all security groups" ) do
457
+ options[:securityGroupIds] = []
458
+ clear_or_secgroups_specified = true
459
+ end
460
+ opts.on( '-s', '--secgroups SECGROUPS', "Apply the specified comma separated security group ids" ) do |secgroups|
461
+ options[:securityGroupIds] = secgroups.split(",")
462
+ clear_or_secgroups_specified = true
463
+ end
464
+ build_common_options(opts, options, [:json, :dry_run, :remote])
465
+ end
466
+ optparse.parse!(args)
467
+ if !clear_or_secgroups_specified
468
+ puts optparse
469
+ exit
470
+ end
471
+ connect(options)
472
+ begin
473
+ cloud = find_cloud_by_name_or_id(args[0])
474
+ if options[:dry_run]
475
+ print_dry_run @clouds_interface.dry.apply_security_groups(cloud['id'])
476
+ return
477
+ end
478
+ json_response = @clouds_interface.apply_security_groups(cloud['id'], options)
479
+ if options[:json]
480
+ print JSON.pretty_generate(json_response)
481
+ print "\n"
482
+ return
483
+ end
484
+ security_groups([args[0]])
485
+ rescue RestClient::Exception => e
486
+ print_rest_exception(e, options)
487
+ exit 1
488
+ end
489
+ end
490
+
491
+ def list_cloud_types(args)
492
+ options={}
493
+ params = {}
494
+ optparse = OptionParser.new do|opts|
495
+ opts.banner = subcommand_usage()
496
+ opts.on( '-g', '--group GROUP', "Group Name" ) do |group|
497
+ options[:group] = group
498
+ end
499
+ build_common_options(opts, options, [:json, :dry_run, :remote])
500
+ end
501
+ optparse.parse!(args)
502
+ connect(options)
503
+ begin
504
+ if options[:dry_run]
505
+ print_dry_run @clouds_interface.dry.cloud_types({})
506
+ return
507
+ end
508
+ cloud_types = get_available_cloud_types() # @clouds_interface.dry.cloud_types({})['zoneTypes']
509
+ if options[:json]
510
+ print JSON.pretty_generate({zoneTypes: cloud_types})
511
+ print "\n"
512
+ else
513
+ print "\n" ,cyan, bold, "Morpheus Cloud Types\n","==================", reset, "\n\n"
514
+ if cloud_types.empty?
515
+ puts yellow,"No cloud types found.",reset
516
+ else
517
+ print cyan
518
+ cloud_types = cloud_types.select {|it| it['enabled'] }
519
+ rows = cloud_types.collect do |cloud_type|
520
+ {id: cloud_type['id'], name: cloud_type['name'], code: cloud_type['code']}
521
+ end
522
+ tp rows, :id, :name, :code
523
+ end
524
+ print reset,"\n"
525
+ end
526
+ rescue RestClient::Exception => e
527
+ print_rest_exception(e, options)
528
+ exit 1
529
+ end
530
+ end
531
+
532
+ private
533
+
534
+ def print_clouds_table(clouds, opts={})
535
+ table_color = opts[:color] || cyan
536
+ rows = clouds.collect do |cloud|
537
+ status = nil
538
+ if cloud['status'] == 'ok'
539
+ status = "#{green}OK#{table_color}"
540
+ elsif cloud['status'].nil?
541
+ status = "#{white}UNKNOWN#{table_color}"
542
+ else
543
+ status = "#{red}#{cloud['status'] ? cloud['status'].upcase : 'N/A'}#{cloud['statusMessage'] ? "#{table_color} - #{cloud['statusMessage']}" : ''}#{table_color}"
544
+ end
545
+ cloud_type = cloud_type_for_id(cloud['zoneTypeId'])
546
+ {
547
+ id: cloud['id'],
548
+ name: cloud['name'],
549
+ type: cloud_type ? cloud_type['name'] : '',
550
+ location: cloud['location'],
551
+ groups: (cloud['groups'] || []).collect {|it| it.instance_of?(Hash) ? it['name'] : it.to_s }.join(', '),
552
+ servers: cloud['serverCount'],
553
+ status: status
554
+ }
555
+ end
556
+ columns = [
557
+ :id, :name, :type, :location, :groups, :servers, :status
558
+ ]
559
+ print table_color
560
+ tp rows, columns
561
+ print reset
562
+
563
+ end
564
+
565
+ def add_cloud_option_types(cloud_type)
566
+ # note: Type is selected before this
567
+ tmp_option_types = [
568
+ #{'fieldName' => 'zoneType.code', 'fieldLabel' => 'Image Type', 'type' => 'select', 'selectOptions' => cloud_types_for_dropdown, 'required' => true, 'description' => 'Cloud Type.', 'displayOrder' => 0},
569
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
570
+ {'fieldName' => 'code', 'fieldLabel' => 'Code', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
571
+ {'fieldName' => 'location', 'fieldLabel' => 'Location', 'type' => 'text', 'required' => false, 'displayOrder' => 3},
572
+ {'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'},{'name' => 'Public', 'value' => 'public'}], 'required' => false, 'description' => 'Visibility', 'category' => 'permissions', 'defaultValue' => 'private', 'displayOrder' => 4},
573
+ ]
574
+
575
+ # TODO: Account
576
+
577
+ # Details (zoneType.optionTypes)
578
+
579
+ if cloud_type && cloud_type['optionTypes']
580
+ # adjust displayOrder to put these at the end
581
+ #tmp_option_types = tmp_option_types + cloud_type['optionTypes']
582
+ cloud_type['optionTypes'].each do |opt|
583
+ tmp_option_types << opt.merge({'displayOrder' => opt['displayOrder'].to_i + 100})
584
+ end
585
+ end
586
+
587
+ # TODO:
588
+ # Advanced Options
589
+ ## (a whole bunch needed here)
590
+
591
+ # Provisioning Options
592
+
593
+ ## PROXY (dropdown)
594
+ ## BYPASS PROXY FOR APPLIANCE URL (checkbox)
595
+ ## USER DATA LINUX (code)
596
+
597
+ return tmp_option_types
598
+ end
599
+
600
+ def update_cloud_option_types(cloud_type)
601
+ add_cloud_option_types(cloud_type).collect {|it| it['required'] = false; it }
602
+ end
603
+
604
+ def cloud_types_for_dropdown
605
+ get_available_cloud_types().select {|it| it['enabled'] }.collect {|it| {'name' => it['name'], 'value' => it['code']} }
606
+ end
607
+
381
608
  end