morpheus-cli 0.1.1 → 0.9.0

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/morpheus/api/accounts_interface.rb +55 -0
  3. data/lib/morpheus/api/api_client.rb +48 -3
  4. data/lib/morpheus/api/apps_interface.rb +13 -13
  5. data/lib/morpheus/api/{zones_interface.rb → clouds_interface.rb} +10 -10
  6. data/lib/morpheus/api/deploy_interface.rb +4 -4
  7. data/lib/morpheus/api/groups_interface.rb +3 -3
  8. data/lib/morpheus/api/instance_types_interface.rb +2 -2
  9. data/lib/morpheus/api/instances_interface.rb +35 -19
  10. data/lib/morpheus/api/key_pairs_interface.rb +60 -0
  11. data/lib/morpheus/api/license_interface.rb +29 -0
  12. data/lib/morpheus/api/load_balancers_interface.rb +72 -0
  13. data/lib/morpheus/api/logs_interface.rb +37 -0
  14. data/lib/morpheus/api/options_interface.rb +20 -0
  15. data/lib/morpheus/api/provision_types_interface.rb +27 -0
  16. data/lib/morpheus/api/roles_interface.rb +73 -0
  17. data/lib/morpheus/api/security_group_rules_interface.rb +3 -3
  18. data/lib/morpheus/api/security_groups_interface.rb +5 -5
  19. data/lib/morpheus/api/servers_interface.rb +67 -3
  20. data/lib/morpheus/api/task_sets_interface.rb +46 -0
  21. data/lib/morpheus/api/tasks_interface.rb +72 -0
  22. data/lib/morpheus/api/users_interface.rb +72 -0
  23. data/lib/morpheus/cli.rb +27 -4
  24. data/lib/morpheus/cli/accounts.rb +306 -0
  25. data/lib/morpheus/cli/apps.rb +58 -1
  26. data/lib/morpheus/cli/cli_command.rb +87 -0
  27. data/lib/morpheus/cli/cli_registry.rb +6 -1
  28. data/lib/morpheus/cli/{zones.rb → clouds.rb} +99 -70
  29. data/lib/morpheus/cli/credentials.rb +23 -11
  30. data/lib/morpheus/cli/error_handler.rb +31 -11
  31. data/lib/morpheus/cli/groups.rb +1 -0
  32. data/lib/morpheus/cli/hosts.rb +567 -0
  33. data/lib/morpheus/cli/instances.rb +588 -292
  34. data/lib/morpheus/cli/key_pairs.rb +393 -0
  35. data/lib/morpheus/cli/license.rb +118 -0
  36. data/lib/morpheus/cli/load_balancers.rb +366 -0
  37. data/lib/morpheus/cli/mixins/accounts_helper.rb +193 -0
  38. data/lib/morpheus/cli/option_types.rb +260 -0
  39. data/lib/morpheus/cli/roles.rb +164 -0
  40. data/lib/morpheus/cli/security_group_rules.rb +4 -9
  41. data/lib/morpheus/cli/shell.rb +108 -0
  42. data/lib/morpheus/cli/tasks.rb +370 -0
  43. data/lib/morpheus/cli/users.rb +325 -0
  44. data/lib/morpheus/cli/version.rb +1 -1
  45. data/lib/morpheus/cli/workflows.rb +100 -0
  46. data/lib/morpheus/formatters.rb +43 -0
  47. data/morpheus-cli.gemspec +1 -1
  48. metadata +33 -10
  49. data/lib/morpheus/cli/servers.rb +0 -265
@@ -0,0 +1,108 @@
1
+ # require 'yaml'
2
+ require 'io/console'
3
+ require 'rest_client'
4
+ require 'term/ansicolor'
5
+ require 'optparse'
6
+ require 'table_print'
7
+ require 'morpheus/cli/cli_command'
8
+ require "shellwords"
9
+
10
+
11
+ class Morpheus::Cli::Shell
12
+ include Morpheus::Cli::CliCommand
13
+ include Term::ANSIColor
14
+ def initialize()
15
+ @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
16
+ end
17
+
18
+ def handle(args)
19
+ usage = "Usage: morpheus shell"
20
+ @command_options = {}
21
+ optparse = OptionParser.new do|opts|
22
+ opts.banner = usage
23
+ opts.on('-a','--account ACCOUNT', "Account Name") do |val|
24
+ @command_options[:account_name] = val
25
+ end
26
+ opts.on('-A','--account-id ID', "Account ID") do |val|
27
+ @command_options[:account_id] = val
28
+ end
29
+ opts.on('-C','--nocolor', "ANSI") do
30
+ @command_options[:nocolor] = true
31
+ Term::ANSIColor::coloring = false
32
+ end
33
+ opts.on( '-h', '--help', "Prints this help" ) do
34
+ puts opts
35
+ exit
36
+ end
37
+ end
38
+ optparse.parse(args)
39
+
40
+ history = []
41
+ exit = false
42
+ while !exit do
43
+ print cyan,"morpheus > ",reset
44
+ input = $stdin.gets.chomp!
45
+ if !input.empty?
46
+
47
+ if input == 'exit'
48
+ break
49
+ elsif input == 'history'
50
+ commands = history.last(100)
51
+ puts "Last #{commands.size} commands"
52
+ commands.reverse.each do |cmd|
53
+ puts "#{cmd}"
54
+ end
55
+ next
56
+ elsif input == 'clear'
57
+ print "\e[H\e[2J"
58
+ next
59
+ elsif input == '!'
60
+ input = history[-1]
61
+ end
62
+
63
+ begin
64
+ history << input
65
+ argv = Shellwords.shellsplit(input)
66
+ if @command_options[:account_name]
67
+ argv.push "--account", @command_options[:account_name]
68
+ end
69
+ if @command_options[:account_id]
70
+ argv.push "--account-id", @command_options[:account_id]
71
+ end
72
+ if @command_options[:nocolor]
73
+ argv.push "--nocolor"
74
+ end
75
+ #puts "cmd: #{argv.join(' ')}"
76
+ if Morpheus::Cli::CliRegistry.has_command?(argv[0])
77
+ Morpheus::Cli::CliRegistry.exec(argv[0], argv[1..-1])
78
+ else
79
+ puts "Unrecognized Command."
80
+ end
81
+ rescue ArgumentError
82
+ puts "Argument Syntax Error..."
83
+ rescue SystemExit, Interrupt
84
+ # nothing to do
85
+ rescue => e
86
+ print red, "\n", e.message, "\n", reset
87
+ print e.backtrace.join("\n"), "\n"
88
+ end
89
+
90
+ end
91
+ end
92
+ end
93
+
94
+
95
+ def get_prompt
96
+ input = ''
97
+ while char=STDIN.getch do
98
+ if char == '\n'
99
+ print "\r\n"
100
+ puts "executing..."
101
+ break
102
+ end
103
+ print char
104
+ input << char
105
+ end
106
+ return input
107
+ end
108
+ end
@@ -0,0 +1,370 @@
1
+ # require 'yaml'
2
+ require 'io/console'
3
+ require 'rest_client'
4
+ require 'term/ansicolor'
5
+ require 'optparse'
6
+ require 'table_print'
7
+ require 'morpheus/cli/cli_command'
8
+
9
+ class Morpheus::Cli::Tasks
10
+ include Morpheus::Cli::CliCommand
11
+ include Term::ANSIColor
12
+ def initialize()
13
+ @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
14
+ end
15
+
16
+ def connect(opts)
17
+ if opts[:remote]
18
+ @appliance_url = opts[:remote]
19
+ @appliance_name = opts[:remote]
20
+ @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials(opts)
21
+ else
22
+ @access_token = Morpheus::Cli::Credentials.new(@appliance_name,@appliance_url).request_credentials(opts)
23
+ end
24
+ @api_client = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url)
25
+ @tasks_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).tasks
26
+ @task_sets_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).task_sets
27
+ if @access_token.empty?
28
+ print red,bold, "\nInvalid Credentials. Unable to acquire access token. Please verify your credentials and try again.\n\n",reset
29
+ exit 1
30
+ end
31
+ end
32
+
33
+
34
+ def handle(args)
35
+ if args.empty?
36
+ puts "\nUsage: morpheus tasks [list,add, update,remove, details, task-types]\n\n"
37
+ return
38
+ end
39
+
40
+ case args[0]
41
+ when 'list'
42
+ list(args[1..-1])
43
+ when 'add'
44
+ add(args[1..-1])
45
+ when 'update'
46
+ update(args[1..-1])
47
+ when 'details'
48
+ details(args[1..-1])
49
+ when 'remove'
50
+ remove(args[1..-1])
51
+ when 'task-types'
52
+ task_types(args[1..-1])
53
+ else
54
+ puts "\nUsage: morpheus tasks [list,add, update,remove, details, task-types]\n\n"
55
+ exit 127
56
+ end
57
+ end
58
+
59
+ def list(args)
60
+ options = {}
61
+ optparse = OptionParser.new do|opts|
62
+ opts.banner = "Usage: morpheus tasks list [-s] [-o] [-m]"
63
+ Morpheus::Cli::CliCommand.genericOptions(opts,options)
64
+ end
65
+ optparse.parse(args)
66
+ connect(options)
67
+ begin
68
+ params = {}
69
+ if options[:offset]
70
+ params[:offset] = options[:offset]
71
+ end
72
+ if options[:max]
73
+ params[:max] = options[:max]
74
+ end
75
+ if options[:phrase]
76
+ params[:phrase] = options[:phrase]
77
+ end
78
+ json_response = @tasks_interface.get(params)
79
+ if options[:json]
80
+ print JSON.pretty_generate(json_response)
81
+ else
82
+ tasks = json_response['tasks']
83
+ print "\n" ,cyan, bold, "Morpheus Tasks\n","==================", reset, "\n\n"
84
+ if tasks.empty?
85
+ puts yellow,"No tasks currently configured.",reset
86
+ else
87
+ print cyan
88
+ tasks_table_data = tasks.collect do |task|
89
+ {name: task['name'], id: task['id'], type: task['taskType']['name']}
90
+ end
91
+ tp tasks_table_data, :id, :name, :type
92
+ end
93
+ print reset,"\n\n"
94
+ end
95
+
96
+
97
+ rescue => e
98
+ if e.response.code == 400
99
+ error = JSON.parse(e.response.to_s)
100
+ ::Morpheus::Cli::ErrorHandler.new.print_errors(error,options)
101
+ else
102
+ puts "Error Communicating with the Appliance. Please try again later. #{e}"
103
+ end
104
+ exit 1
105
+ end
106
+ end
107
+
108
+ def details(args)
109
+ task_name = args[0]
110
+ options = {}
111
+ optparse = OptionParser.new do|opts|
112
+ opts.banner = "Usage: morpheus tasks details [task]"
113
+ Morpheus::Cli::CliCommand.genericOptions(opts,options)
114
+ end
115
+ if args.count < 1
116
+ puts "\n#{optparse.banner}\n\n"
117
+ exit 1
118
+ end
119
+ optparse.parse(args)
120
+ connect(options)
121
+ begin
122
+ task = find_task_by_name_or_code_or_id(task_name)
123
+
124
+ exit 1 if task.nil?
125
+ task_type = find_task_type_by_name(task['taskType']['name'])
126
+ if options[:json]
127
+ puts JSON.pretty_generate({task:task})
128
+ else
129
+ print "\n", cyan, "Task #{task['name']} - #{task['taskType']['name']}\n\n"
130
+ task_type['optionTypes'].sort { |x,y| x['displayOrder'].to_i <=> y['displayOrder'].to_i }.each do |optionType|
131
+ puts " #{optionType['fieldLabel']} : " + (optionType['type'] == 'password' ? "#{task['taskOptions'][optionType['fieldName']] ? '************' : ''}" : "#{task['taskOptions'][optionType['fieldName']] || optionType['defaultValue']}")
132
+ end
133
+ print reset,"\n\n"
134
+ end
135
+ rescue RestClient::Exception => e
136
+ if e.response.code == 400
137
+ error = JSON.parse(e.response.to_s)
138
+ ::Morpheus::Cli::ErrorHandler.new.print_errors(error,options)
139
+ else
140
+ puts "Error Communicating with the Appliance. Please try again later. #{e}"
141
+ end
142
+ exit 1
143
+ end
144
+ end
145
+
146
+ def update(args)
147
+ task_name = args[0]
148
+ options = {}
149
+ account_name = nil
150
+ optparse = OptionParser.new do|opts|
151
+ opts.banner = "Usage: morpheus tasks update [task] [options]"
152
+ Morpheus::Cli::CliCommand.genericOptions(opts,options)
153
+ end
154
+ if args.count < 1
155
+ puts "\n#{optparse.banner}\n\n"
156
+ exit 1
157
+ end
158
+ optparse.parse(args)
159
+
160
+ connect(options)
161
+
162
+ begin
163
+
164
+
165
+ task = find_task_by_name_or_code_or_id(task_name)
166
+ exit 1 if task.nil?
167
+ task_type = find_task_type_by_name(task['taskType']['name'])
168
+
169
+ #params = Morpheus::Cli::OptionTypes.prompt(add_user_option_types, options[:options], @api_client, options[:params]) # options[:params] is mysterious
170
+ params = options[:options] || {}
171
+
172
+ if params.empty?
173
+ puts "\n#{optparse.banner}\n\n"
174
+ option_lines = update_task_option_types(task_type).collect {|it| "\t-O #{it['fieldContext'] ? (it['fieldContext'] + '.') : ''}#{it['fieldName']}=\"value\"" }.join("\n")
175
+ puts "\nAvailable Options:\n#{option_lines}\n\n"
176
+ exit 1
177
+ end
178
+
179
+ #puts "parsed params is : #{params.inspect}"
180
+ task_keys = ['name']
181
+ changes_payload = (params.select {|k,v| task_keys.include?(k) })
182
+ task_payload = task
183
+ if changes_payload
184
+ task_payload.merge!(changes_payload)
185
+ end
186
+ puts params
187
+ if params['taskOptions']
188
+ task_payload['taskOptions'].merge!(params['taskOptions'])
189
+ end
190
+
191
+ request_payload = {task: task_payload}
192
+ response = @tasks_interface.update(task['id'], request_payload)
193
+ if options[:json]
194
+ print JSON.pretty_generate(json_response)
195
+ if !response['success']
196
+ exit 1
197
+ end
198
+ else
199
+ print "\n", cyan, "Task #{response['task']['name']} updated", reset, "\n\n"
200
+ end
201
+ rescue RestClient::Exception => e
202
+ if e.response.code == 400
203
+ error = JSON.parse(e.response.to_s)
204
+ ::Morpheus::Cli::ErrorHandler.new.print_errors(error)
205
+ else
206
+ puts "Error Communicating with the Appliance. Please try again later. #{e}"
207
+ end
208
+ exit 1
209
+ end
210
+ end
211
+
212
+
213
+ def task_types(args)
214
+ options = {}
215
+ optparse = OptionParser.new do|opts|
216
+ opts.banner = "Usage: morpheus tasks task-types"
217
+ Morpheus::Cli::CliCommand.genericOptions(opts,options)
218
+ end
219
+ optparse.parse(args)
220
+ connect(options)
221
+ begin
222
+ json_response = @tasks_interface.task_types()
223
+ if options[:json]
224
+ print JSON.pretty_generate(json_response)
225
+ else
226
+ task_types = json_response['taskTypes']
227
+ print "\n" ,cyan, bold, "Morpheus Task Types\n","==================", reset, "\n\n"
228
+ if task_types.nil? || task_types.empty?
229
+ puts yellow,"No task types currently exist on this appliance. This could be a seed issue.",reset
230
+ else
231
+ print cyan
232
+ tasks_table_data = task_types.collect do |task_type|
233
+ {name: task_type['name'], id: task_type['id'], code: task_type['code'], description: task_type['description']}
234
+ end
235
+ tp tasks_table_data, :id, :name, :code, :description
236
+ end
237
+
238
+ print reset,"\n\n"
239
+ end
240
+
241
+
242
+ rescue => e
243
+ if e.response.code == 400
244
+ error = JSON.parse(e.response.to_s)
245
+ ::Morpheus::Cli::ErrorHandler.new.print_errors(error,options)
246
+ else
247
+ puts "Error Communicating with the Appliance. Please try again later. #{e}"
248
+ end
249
+ exit 1
250
+ end
251
+ end
252
+
253
+ def add(args)
254
+ task_name = args[0]
255
+ task_type_name = nil
256
+ options = {}
257
+ optparse = OptionParser.new do|opts|
258
+ opts.banner = "Usage: morpheus tasks add [task] -t TASK_TYPE"
259
+ opts.on( '-t', '--type TASK_TYPE', "Task Type" ) do |val|
260
+ task_type_name = val
261
+ end
262
+ Morpheus::Cli::CliCommand.genericOptions(opts,options)
263
+ end
264
+ if args.count < 1
265
+ puts "\n#{optparse.banner}\n\n"
266
+ exit 1
267
+ end
268
+ optparse.parse(args)
269
+ connect(options)
270
+
271
+ if task_type_name.nil?
272
+ puts "Task Type must be specified...\n#{optparse.banner}"
273
+ exit 1
274
+ end
275
+
276
+ task_type = find_task_type_by_name(task_type_name)
277
+ if task_type.nil?
278
+ puts "Task Type not found!"
279
+ exit 1
280
+ end
281
+ input_options = Morpheus::Cli::OptionTypes.prompt(task_type['optionTypes'],options[:options],@api_client, options[:params])
282
+ payload = {task: {name: task_name, taskOptions: input_options['taskOptions'], taskType: {code: task_type['code'], id: task_type['id']}}}
283
+ json_response = @tasks_interface.create(payload)
284
+ if options[:json]
285
+ print JSON.pretty_generate(json_response)
286
+ else
287
+ if json_response['success']
288
+ print "\n", cyan, "Task #{json_response['task']['name']} created successfully", reset, "\n\n"
289
+ else
290
+ print "\n", red, "Error Creating Task #{json_response['errors']}", reset, "\n\n"
291
+ end
292
+ end
293
+ end
294
+
295
+ def remove(args)
296
+ task_name = args[0]
297
+ options = {}
298
+ optparse = OptionParser.new do|opts|
299
+ opts.banner = "Usage: morpheus tasks remove [task]"
300
+ Morpheus::Cli::CliCommand.genericOptions(opts,options)
301
+ end
302
+ if args.count < 1
303
+ puts "\n#{optparse.banner}\n\n"
304
+ exit 1
305
+ end
306
+ optparse.parse(args)
307
+ connect(options)
308
+ begin
309
+ task = find_task_by_name_or_code_or_id(task_name)
310
+ exit 1 if task.nil?
311
+ exit unless Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the task #{task['name']}?")
312
+ json_response = @tasks_interface.destroy(task['id'])
313
+ if options[:json]
314
+ print JSON.pretty_generate(json_response)
315
+ else
316
+ print "\n", cyan, "Task #{task['name']} removed", reset, "\n\n"
317
+ end
318
+ rescue RestClient::Exception => e
319
+ if e.response.code == 400
320
+ error = JSON.parse(e.response.to_s)
321
+ ::Morpheus::Cli::ErrorHandler.new.print_errors(error,options)
322
+ else
323
+ puts "Error Communicating with the Appliance. Please try again later. #{e}"
324
+ end
325
+ exit 1
326
+ end
327
+ end
328
+
329
+
330
+ private
331
+ def find_task_by_name_or_code_or_id(val)
332
+ raise "find_task_by_name_or_code_or_id passed a bad name: #{val.inspect}" if val.to_s == ''
333
+ results = @tasks_interface.get(val)
334
+ result = nil
335
+ if !results['tasks'].nil? && !results['tasks'].empty?
336
+ result = results['tasks'][0]
337
+ elsif val.to_i.to_s == val
338
+ results = @tasks_interface.get(val.to_i)
339
+ result = results['task']
340
+ end
341
+ if result.nil?
342
+ print red,bold, "\nTask not found by '#{val}'\n\n",reset
343
+ return nil
344
+ end
345
+ return result
346
+ end
347
+
348
+ def find_task_type_by_name(val)
349
+ raise "find_task_type_by_name passed a bad name: #{val.inspect}" if val.to_s == ''
350
+ results = @tasks_interface.task_types(val)
351
+ result = nil
352
+ if !results['taskTypes'].nil? && !results['taskTypes'].empty?
353
+ result = results['taskTypes'][0]
354
+ elsif val.to_i.to_s == val
355
+ results = @tasks_interface.task_types(val.to_i)
356
+ result = results['taskType']
357
+ end
358
+ if result.nil?
359
+ print red,bold, "\nTask Type not found by '#{val}'\n\n",reset
360
+ return nil
361
+ end
362
+ return result
363
+ end
364
+
365
+ def update_task_option_types(task_type)
366
+ [
367
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 0}
368
+ ] + task_type['optionTypes']
369
+ end
370
+ end