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
@@ -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