morpheus-cli 2.10.3 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/bin/morpheus +5 -96
  3. data/lib/morpheus/api/api_client.rb +23 -1
  4. data/lib/morpheus/api/checks_interface.rb +106 -0
  5. data/lib/morpheus/api/incidents_interface.rb +102 -0
  6. data/lib/morpheus/api/monitoring_apps_interface.rb +47 -0
  7. data/lib/morpheus/api/monitoring_contacts_interface.rb +47 -0
  8. data/lib/morpheus/api/monitoring_groups_interface.rb +47 -0
  9. data/lib/morpheus/api/monitoring_interface.rb +36 -0
  10. data/lib/morpheus/api/option_type_lists_interface.rb +47 -0
  11. data/lib/morpheus/api/option_types_interface.rb +47 -0
  12. data/lib/morpheus/api/roles_interface.rb +0 -1
  13. data/lib/morpheus/api/setup_interface.rb +19 -9
  14. data/lib/morpheus/cli.rb +20 -1
  15. data/lib/morpheus/cli/accounts.rb +8 -3
  16. data/lib/morpheus/cli/app_templates.rb +19 -11
  17. data/lib/morpheus/cli/apps.rb +52 -37
  18. data/lib/morpheus/cli/cli_command.rb +229 -53
  19. data/lib/morpheus/cli/cli_registry.rb +48 -40
  20. data/lib/morpheus/cli/clouds.rb +55 -26
  21. data/lib/morpheus/cli/command_error.rb +12 -0
  22. data/lib/morpheus/cli/credentials.rb +68 -26
  23. data/lib/morpheus/cli/curl_command.rb +98 -0
  24. data/lib/morpheus/cli/dashboard_command.rb +2 -7
  25. data/lib/morpheus/cli/deployments.rb +4 -4
  26. data/lib/morpheus/cli/deploys.rb +1 -2
  27. data/lib/morpheus/cli/dot_file.rb +5 -8
  28. data/lib/morpheus/cli/error_handler.rb +179 -15
  29. data/lib/morpheus/cli/groups.rb +21 -13
  30. data/lib/morpheus/cli/hosts.rb +220 -110
  31. data/lib/morpheus/cli/instance_types.rb +2 -2
  32. data/lib/morpheus/cli/instances.rb +257 -167
  33. data/lib/morpheus/cli/key_pairs.rb +15 -9
  34. data/lib/morpheus/cli/library.rb +673 -27
  35. data/lib/morpheus/cli/license.rb +2 -2
  36. data/lib/morpheus/cli/load_balancers.rb +4 -4
  37. data/lib/morpheus/cli/log_level_command.rb +6 -4
  38. data/lib/morpheus/cli/login.rb +17 -3
  39. data/lib/morpheus/cli/logout.rb +25 -11
  40. data/lib/morpheus/cli/man_command.rb +388 -0
  41. data/lib/morpheus/cli/mixins/accounts_helper.rb +1 -1
  42. data/lib/morpheus/cli/mixins/monitoring_helper.rb +434 -0
  43. data/lib/morpheus/cli/mixins/print_helper.rb +620 -112
  44. data/lib/morpheus/cli/mixins/provisioning_helper.rb +1 -1
  45. data/lib/morpheus/cli/monitoring_apps_command.rb +29 -0
  46. data/lib/morpheus/cli/monitoring_checks_command.rb +427 -0
  47. data/lib/morpheus/cli/monitoring_contacts_command.rb +373 -0
  48. data/lib/morpheus/cli/monitoring_groups_command.rb +29 -0
  49. data/lib/morpheus/cli/monitoring_incidents_command.rb +711 -0
  50. data/lib/morpheus/cli/option_types.rb +10 -1
  51. data/lib/morpheus/cli/recent_activity_command.rb +2 -5
  52. data/lib/morpheus/cli/remote.rb +874 -134
  53. data/lib/morpheus/cli/roles.rb +54 -27
  54. data/lib/morpheus/cli/security_group_rules.rb +2 -2
  55. data/lib/morpheus/cli/security_groups.rb +23 -19
  56. data/lib/morpheus/cli/set_prompt_command.rb +50 -0
  57. data/lib/morpheus/cli/shell.rb +222 -157
  58. data/lib/morpheus/cli/tasks.rb +19 -15
  59. data/lib/morpheus/cli/users.rb +27 -17
  60. data/lib/morpheus/cli/version.rb +1 -1
  61. data/lib/morpheus/cli/virtual_images.rb +28 -13
  62. data/lib/morpheus/cli/whoami.rb +131 -52
  63. data/lib/morpheus/cli/workflows.rb +24 -9
  64. data/lib/morpheus/formatters.rb +195 -3
  65. data/lib/morpheus/logging.rb +86 -0
  66. data/lib/morpheus/terminal.rb +371 -0
  67. data/scripts/generate_morpheus_commands_help.morpheus +60 -0
  68. metadata +21 -2
@@ -0,0 +1,373 @@
1
+ # require 'yaml'
2
+ require 'time'
3
+ require 'io/console'
4
+ require 'rest_client'
5
+ require 'optparse'
6
+ require 'filesize'
7
+ require 'table_print'
8
+ require 'morpheus/cli/cli_command'
9
+ require 'morpheus/cli/mixins/provisioning_helper'
10
+
11
+ class Morpheus::Cli::MonitorContactsCommand
12
+ include Morpheus::Cli::CliCommand
13
+ include Morpheus::Cli::MonitoringHelper
14
+
15
+ set_command_name :'monitoring-contacts'
16
+
17
+ register_subcommands :list, :get, :add, :update, :remove
18
+
19
+ def initialize()
20
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
21
+ end
22
+
23
+ def connect(opts)
24
+ @api_client = establish_remote_appliance_connection(opts)
25
+ @monitoring_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).monitoring
26
+ end
27
+
28
+ def handle(args)
29
+ handle_subcommand(args)
30
+ end
31
+
32
+ def list(args)
33
+ options = {}
34
+ params = {}
35
+ optparse = OptionParser.new do|opts|
36
+ opts.banner = subcommand_usage()
37
+ build_common_options(opts, options, [:list, :json, :csv, :fields, :json, :dry_run])
38
+ end
39
+ optparse.parse!(args)
40
+ connect(options)
41
+ begin
42
+ [:phrase, :offset, :max, :sort, :direction].each do |k|
43
+ params[k] = options[k] unless options[k].nil?
44
+ end
45
+ # JD: lastUpdated 500ing, contacts don't have that property ? =o Fix it!
46
+
47
+ if options[:dry_run]
48
+ print_dry_run @monitoring_interface.contacts.dry.list(params)
49
+ return
50
+ end
51
+
52
+ json_response = @monitoring_interface.contacts.list(params)
53
+ if options[:json]
54
+ if options[:include_fields]
55
+ json_response = {"contacts" => filter_data(json_response["contacts"], options[:include_fields]) }
56
+ end
57
+ puts as_json(json_response, options)
58
+ return 0
59
+ end
60
+ if options[:csv]
61
+ puts records_as_csv(json_response['contacts'], options)
62
+ return 0
63
+ end
64
+ contacts = json_response['contacts']
65
+ title = "Morpheus Monitoring Contacts"
66
+ subtitles = []
67
+ if params[:phrase]
68
+ subtitles << "Search: #{params[:phrase]}".strip
69
+ end
70
+ print_h1 title, subtitles
71
+ if contacts.empty?
72
+ print cyan,"No contacts found.",reset,"\n"
73
+ else
74
+ print_contacts_table(contacts, options)
75
+ print_results_pagination(json_response, {:label => "contact", :n_label => "contacts"})
76
+ end
77
+ print reset,"\n"
78
+ rescue RestClient::Exception => e
79
+ print_rest_exception(e, options)
80
+ exit 1
81
+ end
82
+ end
83
+
84
+ def get(args)
85
+ options = {}
86
+ optparse = OptionParser.new do|opts|
87
+ opts.banner = subcommand_usage("[id list]")
88
+ opts.on(nil,'--history', "Display History") do |val|
89
+ options[:show_history] = true
90
+ end
91
+ opts.on(nil,'--notifications', "Display Notifications") do |val|
92
+ options[:show_notifications] = true
93
+ end
94
+ build_common_options(opts, options, [:json, :csv, :fields, :dry_run, :remote])
95
+ end
96
+ optparse.parse!(args)
97
+ if args.count < 1
98
+ puts optparse
99
+ exit 1
100
+ end
101
+ connect(options)
102
+ id_list = parse_id_list(args)
103
+ return run_command_for_each_arg(id_list) do |arg|
104
+ _get(arg, options)
105
+ end
106
+ end
107
+
108
+ def _get(id, options)
109
+
110
+ begin
111
+ contact = find_contact_by_name_or_id(id)
112
+ if options[:dry_run]
113
+ print_dry_run @monitoring_interface.contacts.dry.get(contact['id'])
114
+ return
115
+ end
116
+ json_response = @monitoring_interface.contacts.get(contact['id'])
117
+ contact = json_response['contact']
118
+
119
+ if options[:json]
120
+ if options[:include_fields]
121
+ json_response = {"contact" => filter_data(json_response["contact"], options[:include_fields]) }
122
+ end
123
+ puts as_json(json_response, options)
124
+ return 0
125
+ elsif options[:csv]
126
+ puts records_as_csv([json_response['contact']], options)
127
+ return 0
128
+ end
129
+
130
+ print_h1 "Contact Details"
131
+ print cyan
132
+ description_cols = {
133
+ "ID" => 'id',
134
+ "Name" => 'name',
135
+ "Email" => 'emailAddress',
136
+ "Mobile" => 'smsAddress',
137
+ # "Slack Hook" => 'slackHook'
138
+ }
139
+ description_cols["Slack Hook"] = 'slackHook' if !contact['slackHook'].empty?
140
+ puts as_description_list(contact, description_cols)
141
+
142
+ ## Notifications
143
+ # show notify events here...
144
+
145
+ print reset,"\n"
146
+
147
+ rescue RestClient::Exception => e
148
+ print_rest_exception(e, options)
149
+ exit 1
150
+ end
151
+ end
152
+
153
+ def add(args)
154
+ options = {}
155
+ params = {}
156
+ optparse = OptionParser.new do|opts|
157
+ opts.banner = subcommand_usage("[id]")
158
+ opts.on("--name STRING", String, "Contact name") do |val|
159
+ params['name'] = val
160
+ end
161
+ opts.on("--email STRING", String, "Contact email address") do |val|
162
+ params['emailAddress'] = val == 'null' ? nil : val
163
+ end
164
+ opts.on("--mobile STRING", String, "Contact sms addresss") do |val|
165
+ params['smsAddress'] = val == 'null' ? nil : val
166
+ end
167
+ opts.on("--slackHook STRING", String, "Contact slack hook") do |val|
168
+ params['slackHook'] = val == 'null' ? nil : val
169
+ end
170
+ build_common_options(opts, options, [:json, :dry_run, :quiet])
171
+ end
172
+ optparse.parse!(args)
173
+ connect(options)
174
+
175
+ begin
176
+
177
+ if !params["name"]
178
+ print_red_alert "Name is required"
179
+ puts optparse
180
+ exit 1
181
+ end
182
+
183
+ payload = {
184
+ 'contact' => {}
185
+ }
186
+ payload['contact'].merge!(params)
187
+
188
+ if options[:dry_run]
189
+ print_dry_run @monitoring_interface.contacts.dry.create(payload)
190
+ return
191
+ end
192
+
193
+ json_response = @monitoring_interface.contacts.create(payload)
194
+ contact = json_response['contact']
195
+ if options[:json]
196
+ puts as_json(json_response, options)
197
+ elsif !options[:quiet]
198
+ print_green_success "Created contact (#{contact['id']}) #{contact['name']}"
199
+ #_get(contact['id'], {})
200
+ end
201
+
202
+ rescue RestClient::Exception => e
203
+ print_rest_exception(e, options)
204
+ exit 1
205
+ end
206
+ end
207
+
208
+ def update(args)
209
+ options = {}
210
+ params = {}
211
+ optparse = OptionParser.new do|opts|
212
+ opts.banner = subcommand_usage("[id]")
213
+ opts.on("--name STRING", String, "Contact name") do |val|
214
+ params['name'] = val
215
+ end
216
+ opts.on("--email STRING", String, "Contact email address") do |val|
217
+ params['emailAddress'] = val == 'null' ? nil : val
218
+ end
219
+ opts.on("--mobile STRING", String, "Contact sms addresss") do |val|
220
+ params['smsAddress'] = val == 'null' ? nil : val
221
+ end
222
+ opts.on("--slackHook STRING", String, "Contact slack hook") do |val|
223
+ params['slackHook'] = val == 'null' ? nil : val
224
+ end
225
+ build_common_options(opts, options, [:json, :dry_run, :quiet])
226
+ end
227
+ optparse.parse!(args)
228
+ if args.count < 1
229
+ puts optparse
230
+ exit 1
231
+ end
232
+ connect(options)
233
+
234
+ begin
235
+ contact = find_contact_by_name_or_id(args[0])
236
+
237
+ if params.empty?
238
+ print_red_alert "Specify atleast one option to update"
239
+ puts optparse
240
+ exit 1
241
+ end
242
+
243
+ payload = {
244
+ 'contact' => {id: contact["id"]}
245
+ }
246
+ payload['contact'].merge!(params)
247
+
248
+ if options[:dry_run]
249
+ print_dry_run @monitoring_interface.contacts.dry.update(contact["id"], payload)
250
+ return
251
+ end
252
+
253
+ json_response = @monitoring_interface.contacts.update(contact["id"], payload)
254
+ if options[:json]
255
+ puts as_json(json_response, options)
256
+ elsif !options[:quiet]
257
+ print_green_success "Updated contact #{contact['id']}"
258
+ _get(contact['id'], {})
259
+ end
260
+
261
+ rescue RestClient::Exception => e
262
+ print_rest_exception(e, options)
263
+ exit 1
264
+ end
265
+ end
266
+
267
+
268
+ def remove(args)
269
+ options = {}
270
+ optparse = OptionParser.new do|opts|
271
+ opts.banner = subcommand_usage("[id list]")
272
+ build_common_options(opts, options, [:auto_confirm, :quiet, :json, :dry_run, :remote])
273
+ end
274
+ optparse.parse!(args)
275
+ if args.count < 1
276
+ puts optparse
277
+ exit 1
278
+ end
279
+ connect(options)
280
+ id_list = parse_id_list(args)
281
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete contact #{id_list.size == 1 ? 'contact' : 'contacts'} #{anded_list(id_list)}?", options)
282
+ exit 1
283
+ end
284
+ return run_command_for_each_arg(id_list) do |arg|
285
+ _remove(arg, options)
286
+ end
287
+ end
288
+
289
+ def _remove(id, options)
290
+
291
+ begin
292
+ contact = find_contact_by_name_or_id(id)
293
+ if options[:dry_run]
294
+ print_dry_run @monitoring_interface.contacts.dry.destroy(contact['id'])
295
+ return
296
+ end
297
+ json_response = @monitoring_interface.contacts.destroy(contact['id'])
298
+ if options[:json]
299
+ puts as_json(json_response, options)
300
+ elsif !options[:quiet]
301
+ print_green_success json_response["msg"] || "Contact (#{contact['id']}) #{contact['name']} deleted"
302
+ end
303
+ rescue RestClient::Exception => e
304
+ print_rest_exception(e, options)
305
+ exit 1
306
+ end
307
+ end
308
+
309
+ def reopen(args)
310
+ options = {}
311
+ optparse = OptionParser.new do|opts|
312
+ opts.banner = subcommand_usage("[id list]")
313
+ build_common_options(opts, options, [:auto_confirm, :quiet, :json, :dry_run, :remote])
314
+ end
315
+ optparse.parse!(args)
316
+ if args.count < 1
317
+ puts optparse
318
+ exit 1
319
+ end
320
+ connect(options)
321
+ id_list = parse_id_list(args)
322
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to reopen #{id_list.size == 1 ? 'contact' : 'contacts'} #{anded_list(id_list)}?", options)
323
+ exit 1
324
+ end
325
+ return run_command_for_each_arg(id_list) do |arg|
326
+ _reopen(arg, options)
327
+ end
328
+ end
329
+
330
+ def _reopen(id, options)
331
+
332
+ begin
333
+ contact = find_contact_by_name_or_id(id)
334
+ already_open = contact['status'] == 'open'
335
+ if already_open
336
+ print bold,yellow,"contact #{contact['id']} is already open",reset,"\n"
337
+ return false
338
+ end
339
+ if options[:dry_run]
340
+ print_dry_run @monitoring_interface.contacts.dry.reopen(contact['id'])
341
+ return
342
+ end
343
+ json_response = @monitoring_interface.contacts.reopen(contact['id'])
344
+ if options[:json]
345
+ print JSON.pretty_generate(json_response)
346
+ print "\n"
347
+ elsif !options[:quiet]
348
+ print_green_success json_response["msg"] || "contact #{contact['id']} is now open"
349
+ # _get(contact['id'] {})
350
+ end
351
+ rescue RestClient::Exception => e
352
+ print_rest_exception(e, options)
353
+ exit 1
354
+ end
355
+ end
356
+
357
+ private
358
+
359
+ def print_contacts_table(contacts, opts={})
360
+ columns = [
361
+ {"ID" => "id" },
362
+ {"NAME" => "name" },
363
+ {"E-MAIL" => "emailAddress" },
364
+ {"MOBILE" => "smsAddress" },
365
+ ]
366
+ if opts[:include_fields]
367
+ columns = opts[:include_fields]
368
+ end
369
+ print as_pretty_table(contacts, columns, opts)
370
+ end
371
+
372
+
373
+ end
@@ -0,0 +1,29 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'morpheus/cli/mixins/monitoring_helper'
3
+
4
+ class Morpheus::Cli::MonitoringGroupsCommand
5
+ include Morpheus::Cli::CliCommand
6
+ include Morpheus::Cli::MonitoringHelper
7
+
8
+ set_command_name :'check-groups'
9
+ register_subcommands :list, :get, :add, :update, :remove, :quarantine, :history, :statistics
10
+ set_default_subcommand :list
11
+
12
+ set_command_hidden # remove me when implemented
13
+
14
+ def initialize()
15
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
16
+ end
17
+
18
+ def connect(opts)
19
+ @api_client = establish_remote_appliance_connection(opts)
20
+ @monitoring_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).monitoring
21
+ end
22
+
23
+ def handle(args)
24
+ handle_subcommand(args)
25
+ end
26
+
27
+ # todo: API updates and subcommands
28
+
29
+ end
@@ -0,0 +1,711 @@
1
+ require 'morpheus/cli/cli_command'
2
+ require 'morpheus/cli/mixins/monitoring_helper'
3
+
4
+ class Morpheus::Cli::MonitoringIncidentsCommand
5
+ include Morpheus::Cli::CliCommand
6
+ include Morpheus::Cli::MonitoringHelper
7
+
8
+ set_command_name :incidents # :'monitoring-incidents'
9
+ register_subcommands :list, :stats, :get, :history, :notifications, :update, :close, :reopen, :quarantine
10
+ set_default_subcommand :list
11
+
12
+ def initialize()
13
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
14
+ end
15
+
16
+ def connect(opts)
17
+ @api_client = establish_remote_appliance_connection(opts)
18
+ @monitoring_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).monitoring
19
+ end
20
+
21
+ def handle(args)
22
+ handle_subcommand(args)
23
+ end
24
+
25
+ def list(args)
26
+ options = {}
27
+ params = {}
28
+ optparse = OptionParser.new do|opts|
29
+ opts.banner = subcommand_usage()
30
+ opts.on('--status LIST', Array, "Filter by status. open, closed") do |list|
31
+ params['status'] = list
32
+ end
33
+ opts.on('--severity LIST', Array, "Filter by severity. critical, warning, info") do |list|
34
+ params['severity'] = list
35
+ end
36
+ build_common_options(opts, options, [:list, :last_updated, :json, :csv, :fields, :json, :dry_run])
37
+ end
38
+ optparse.parse!(args)
39
+ connect(options)
40
+ begin
41
+ [:phrase, :offset, :max, :sort, :direction, :lastUpdated].each do |k|
42
+ params[k] = options[k] unless options[k].nil?
43
+ end
44
+ # JD: lastUpdated 500ing, incidents don't have that property ? =o Fix it!
45
+
46
+ if options[:dry_run]
47
+ print_dry_run @monitoring_interface.incidents.dry.list(params)
48
+ return
49
+ end
50
+
51
+ json_response = @monitoring_interface.incidents.list(params)
52
+ if options[:json]
53
+ if options[:include_fields]
54
+ json_response = {"incidents" => filter_data(json_response["incidents"], options[:include_fields]) }
55
+ end
56
+ puts as_json(json_response, options)
57
+ return 0
58
+ end
59
+ if options[:csv]
60
+ puts records_as_csv(json_response['incidents'], options)
61
+ return 0
62
+ end
63
+ incidents = json_response['incidents']
64
+ title = "Morpheus Incidents"
65
+ subtitles = []
66
+ # if group
67
+ # subtitles << "Group: #{group['name']}".strip
68
+ # end
69
+ # if cloud
70
+ # subtitles << "Cloud: #{cloud['name']}".strip
71
+ # end
72
+ if params[:phrase]
73
+ subtitles << "Search: #{params[:phrase]}".strip
74
+ end
75
+ print_h1 title, subtitles
76
+ if incidents.empty?
77
+ print cyan,"No incidents found.",reset,"\n"
78
+ else
79
+ print_incidents_table(incidents, options)
80
+ print_results_pagination(json_response, {:label => "incident", :n_label => "incidents"})
81
+ end
82
+ print reset,"\n"
83
+ rescue RestClient::Exception => e
84
+ print_rest_exception(e, options)
85
+ exit 1
86
+ end
87
+ end
88
+
89
+ # this show date range counts and current open incidents
90
+ # it should be perhaps called 'summary' or 'dashboard'
91
+ # it is not stats about a particular incident
92
+ def stats(args)
93
+ options = {}
94
+ optparse = OptionParser.new do|opts|
95
+ opts.banner = subcommand_usage()
96
+ #opts.on('-j','--json', "JSON Output") do
97
+ opts.on( '-m', '--max MAX', "Max open incidents to display. Default is 25" ) do |max|
98
+ if max.to_s == 'all'
99
+ options[:max] = 10000 # 'all'
100
+ else
101
+ options[:max] = max.to_i
102
+ end
103
+ end
104
+ opts.on( '-o', '--offset OFFSET', "Offset open incidents results for pagination." ) do |offset|
105
+ options[:offset] = offset.to_i.abs
106
+ end
107
+ build_common_options(opts, options, [:json, :fields, :json, :dry_run])
108
+ end
109
+ optparse.parse!(args)
110
+ connect(options)
111
+ begin
112
+ params = {}
113
+ [:phrase, :offset, :max, :sort, :direction, :lastUpdated].each do |k|
114
+ params[k] = options[k] unless options[k].nil?
115
+ end
116
+ if options[:dry_run]
117
+ print_dry_run @monitoring_interface.incidents.dry.stats(params)
118
+ return
119
+ end
120
+ json_response = @monitoring_interface.incidents.stats(params)
121
+ if options[:json]
122
+ if options[:include_fields]
123
+ json_response.merge!({"openIncidents" => filter_data(json_response["openIncidents"], options[:include_fields])})
124
+ end
125
+ puts as_json(json_response, options)
126
+ return 0
127
+ end
128
+ # if options[:csv]
129
+ # puts records_as_csv(json_response['openIncidents'], options)
130
+ # return 0
131
+ # end
132
+ open_incidents = json_response['openIncidents']
133
+ open_incidents_count = json_response['openIncidentCount']
134
+ stats = json_response['incidentStats']
135
+
136
+ print_h1 "Morpheus Incidents: Stats"
137
+ print cyan
138
+
139
+ # print_h2 "Counts"
140
+ # print_description_list({
141
+ # "Today" => 'today',
142
+ # "Week" => 'week',
143
+ # "Month" => 'month',
144
+ # }, stats)
145
+
146
+ if stats
147
+ print justify_string("Today: #{stats['today']}", 20)
148
+ print justify_string("Week: #{stats['week']}", 20)
149
+ print justify_string("Month: #{stats['month']}", 20)
150
+ print "\n"
151
+ else
152
+ puts "No stats"
153
+ end
154
+
155
+ if !open_incidents || open_incidents.size() == 0
156
+ print bold,green,"0 open incidents",reset,"\n"
157
+ else
158
+ if open_incidents.size() == 1
159
+ #print bold,yellow,"#{open_incidents.size()} open incident",reset,"\n"
160
+ print_h2 "#{open_incidents.size()} open incident"
161
+ else
162
+ #print bold,yellow,"#{open_incidents.size()} open incidents",reset,"\n"
163
+ print_h2 "#{open_incidents.size()} open incidents"
164
+ end
165
+ options[:max] ||= 20
166
+
167
+ print_incidents_table(open_incidents)
168
+ if open_incidents.size > 0
169
+ print_results_pagination(size: open_incidents.size, total: open_incidents_count, offset: options[:offset])
170
+ end
171
+ end
172
+ print reset,"\n"
173
+ rescue RestClient::Exception => e
174
+ print_rest_exception(e, options)
175
+ exit 1
176
+ end
177
+ end
178
+
179
+ def get(args)
180
+ options = {}
181
+ optparse = OptionParser.new do|opts|
182
+ opts.banner = subcommand_usage("[id list]")
183
+ opts.on(nil,'--history', "Display Incident History") do |val|
184
+ options[:show_history] = true
185
+ end
186
+ opts.on(nil,'--notifications', "Display Incident Notifications") do |val|
187
+ options[:show_notifications] = true
188
+ end
189
+ build_common_options(opts, options, [:json, :csv, :fields, :dry_run, :remote])
190
+ end
191
+ optparse.parse!(args)
192
+ if args.count < 1
193
+ puts optparse
194
+ exit 1
195
+ end
196
+ connect(options)
197
+ id_list = parse_id_list(args)
198
+ return run_command_for_each_arg(id_list) do |arg|
199
+ _get(arg, options)
200
+ end
201
+ end
202
+
203
+ def _get(id, options)
204
+
205
+ begin
206
+ incident = find_incident_by_id(id)
207
+ if options[:dry_run]
208
+ print_dry_run @monitoring_interface.incidents.dry.get(incident['id'])
209
+ return
210
+ end
211
+ json_response = @monitoring_interface.incidents.get(incident['id'])
212
+ incident = json_response['incident']
213
+
214
+ if options[:json]
215
+ if options[:include_fields]
216
+ json_response = {"incident" => filter_data(json_response["incident"], options[:include_fields]) }
217
+ end
218
+ puts as_json(json_response, options)
219
+ return 0
220
+ elsif options[:csv]
221
+ puts records_as_csv([json_response['incident']], options)
222
+ return 0
223
+ end
224
+
225
+ print_h1 "Incident Details"
226
+ print cyan
227
+ description_cols = {
228
+ "ID" => 'id',
229
+ "Severity" => lambda {|it| format_severity(it['severity']) },
230
+ "Name" => lambda {|it| it['displayName'] || it['name'] || 'No Subject' },
231
+ "Start" => lambda {|it| format_local_dt(it['startDate']) },
232
+ "End" => lambda {|it| format_local_dt(it['endDate']) },
233
+ "Duration" => lambda {|it| format_duration(it['startDate'], it['endDate']) },
234
+ "Status" => lambda {|it| format_monitoring_issue_status(it) },
235
+ "Visibility" => 'visibility',
236
+ "Last Check" => lambda {|it| format_local_dt(it['lastCheckTime']) },
237
+ "Last Error" => lambda {|it| it['lastError'] },
238
+ "Comment" => 'comment',
239
+ "Resolution" => 'resolution'
240
+ }
241
+ # description_cols.delete("End") if incident['endDate'].nil?
242
+ description_cols.delete("Comment") if incident['comment'].empty?
243
+ description_cols.delete("Resolution") if incident['resolution'].empty?
244
+ # description_cols.delete("Last Check") if incident['lastCheckTime'].empty?
245
+ # description_cols.delete("Last Error") if incident['lastError'].empty?
246
+ print_description_list(description_cols, incident)
247
+ # puts as_vertical_table(incident, description_cols)
248
+ ## Issues
249
+
250
+ issues = json_response["issues"]
251
+ if issues && !issues.empty?
252
+ print_h2 "Issues"
253
+ print_incident_history_table(issues, options)
254
+ else
255
+ print "\n"
256
+ puts "No checks involved in this incident"
257
+ end
258
+
259
+ ## History (MonitorIncidentEvent)
260
+ if options[:show_history]
261
+ # history_items = json_response["history"]
262
+ # gotta go get it
263
+ history_json_response = @monitoring_interface.incidents.history(incident["id"], {})
264
+ history_items = history_json_response["history"] || history_json_response["events"] || history_json_response["issues"]
265
+ issues = history_items
266
+ if history_items && !history_items.empty?
267
+ print_h2 "History"
268
+ print_incident_history_table(history_items, options)
269
+ print_results_pagination(history_json_response, {:label => "event", :n_label => "events"})
270
+ else
271
+ print "\n"
272
+ puts "No history found for this incident"
273
+ end
274
+ end
275
+
276
+ ## Members (MonitorIncidentNotifyEvent)
277
+ if options[:show_notifications]
278
+ # history_items = json_response["history"]
279
+ # gotta go get it
280
+ notifications_json_response = @monitoring_interface.incidents.notifications(incident["id"], {max: 10})
281
+ notification_items = notifications_json_response["notifications"]
282
+ if notification_items && notification_items.empty?
283
+ print_h2 "Notifications"
284
+ print_incident_notifications_table(notification_items, options)
285
+ print_results_pagination(notifications_json_response, {:label => "notification", :n_label => "notifications"})
286
+ else
287
+ print "\n"
288
+ puts "Nobody has been notified about this incident."
289
+ end
290
+ end
291
+
292
+ print reset,"\n"
293
+
294
+ rescue RestClient::Exception => e
295
+ print_rest_exception(e, options)
296
+ exit 1
297
+ end
298
+ end
299
+
300
+ def history(args)
301
+ options = {}
302
+ params = {}
303
+ optparse = OptionParser.new do|opts|
304
+ opts.banner = subcommand_usage("[id] [options]")
305
+ # opts.on('--status LIST', Array, "Filter by status. open, closed") do |list|
306
+ # params['status'] = list
307
+ # end
308
+ opts.on('--severity LIST', Array, "Filter by severity. critical, warning, info") do |list|
309
+ params['severity'] = list
310
+ end
311
+ build_common_options(opts, options, [:list, :last_updated, :json, :csv, :fields, :json, :dry_run])
312
+ end
313
+ optparse.parse!(args)
314
+ if args.count < 1
315
+ puts optparse
316
+ exit 1
317
+ end
318
+ connect(options)
319
+ begin
320
+ incident = find_incident_by_id(args[0])
321
+ # return false if incident.nil?
322
+
323
+ [:phrase, :offset, :max, :sort, :direction, :lastUpdated].each do |k|
324
+ params[k] = options[k] unless options[k].nil?
325
+ end
326
+ # JD: lastUpdated 500ing, incidents don't have that property ? =o Fix it!
327
+
328
+ if options[:dry_run]
329
+ print_dry_run @monitoring_interface.incidents.dry.history(incident['id'], params)
330
+ return
331
+ end
332
+
333
+ json_response = @monitoring_interface.incidents.history(incident['id'], params)
334
+ if options[:json]
335
+ if options[:include_fields]
336
+ json_response = {"history" => filter_data(json_response["history"], options[:include_fields]) }
337
+ end
338
+ puts as_json(json_response, options)
339
+ return 0
340
+ end
341
+ if options[:csv]
342
+ puts records_as_csv(json_response['history'], options)
343
+ return 0
344
+ end
345
+ history_items = json_response['history']
346
+ title = "Incident History: #{incident['id']}: #{incident['displayName'] || incident['name']}"
347
+ subtitles = []
348
+ if params[:phrase]
349
+ subtitles << "Search: #{params[:phrase]}".strip
350
+ end
351
+ print_h1 title, subtitles
352
+ if history_items.empty?
353
+ print cyan,"No history found.",reset,"\n"
354
+ else
355
+ print_incident_history_table(history_items, options)
356
+ print_results_pagination(json_response, {:label => "event", :n_label => "events"})
357
+ end
358
+ print reset,"\n"
359
+ rescue RestClient::Exception => e
360
+ print_rest_exception(e, options)
361
+ exit 1
362
+ end
363
+ end
364
+
365
+ def notifications(args)
366
+ options = {}
367
+ optparse = OptionParser.new do|opts|
368
+ opts.banner = subcommand_usage("[id] [options]")
369
+ build_common_options(opts, options, [:list, :json, :csv, :fields, :json, :dry_run])
370
+ end
371
+ optparse.parse!(args)
372
+ if args.count < 1
373
+ puts optparse
374
+ exit 1
375
+ end
376
+ connect(options)
377
+ begin
378
+ incident = find_incident_by_id(args[0])
379
+ # return false if incident.nil?
380
+ params = {}
381
+ [:phrase, :offset, :max, :sort, :direction].each do |k|
382
+ params[k] = options[k] unless options[k].nil?
383
+ end
384
+
385
+ if options[:dry_run]
386
+ print_dry_run @monitoring_interface.incidents.dry.notifications(incident['id'], params)
387
+ return
388
+ end
389
+
390
+ json_response = @monitoring_interface.incidents.notifications(incident['id'], params)
391
+ if options[:json]
392
+ if options[:include_fields]
393
+ json_response = {"notifications" => filter_data(json_response["notifications"], options[:include_fields]) }
394
+ end
395
+ puts as_json(json_response, options)
396
+ return 0
397
+ end
398
+ if options[:csv]
399
+ puts records_as_csv(json_response['notifications'], options)
400
+ return 0
401
+ end
402
+ notification_items = json_response['notifications']
403
+ title = "Incident Notifications: #{incident['id']}: #{incident['displayName'] || incident['name']}"
404
+ subtitles = []
405
+ if params[:phrase]
406
+ subtitles << "Search: #{params[:phrase]}".strip
407
+ end
408
+ print_h1 title, subtitles
409
+ if notification_items.empty?
410
+ print cyan,"No notifications found.",reset,"\n"
411
+ else
412
+ print_incident_history_table(notification_items, options)
413
+ print_results_pagination(json_response, {:label => "notification", :n_label => "notifications"})
414
+ end
415
+ print reset,"\n"
416
+ rescue RestClient::Exception => e
417
+ print_rest_exception(e, options)
418
+ exit 1
419
+ end
420
+ end
421
+
422
+ def update(args)
423
+ options = {}
424
+ params = {}
425
+ optparse = OptionParser.new do|opts|
426
+ opts.banner = subcommand_usage("[id]")
427
+ opts.on("-c", "--comment STRING", String, "Comment on this incident") do |val|
428
+ params['comment'] = val == 'null' ? nil : val
429
+ end
430
+ opts.on("--resolution STRING", String, "Description of the resolution to this incident") do |val|
431
+ params['resolution'] = val == 'null' ? nil : val
432
+ end
433
+ opts.on("--status STATUS", String, "Set status (open or closed)") do |val|
434
+ params['status'] = val
435
+ end
436
+ opts.on("--severity STATUS", String, "Set severity (critical, warning or info)") do |val|
437
+ params['severity'] = val
438
+ end
439
+ opts.on("--name STRING", String, "Set display name (subject)") do |val|
440
+ params['name'] = val == 'null' ? nil : val
441
+ end
442
+ opts.on("--startDate TIME", String, "Set start time") do |val|
443
+ begin
444
+ params['startDate'] = parse_time(val).utc.iso8601
445
+ rescue => e
446
+ raise OptionParser::InvalidArgument.new "Failed to parse --startDate '#{val}'. Error: #{e}"
447
+ end
448
+ end
449
+ opts.on("--endDate TIME", String, "Set end time") do |val|
450
+ begin
451
+ params['endDate'] = parse_time(val).utc.iso8601
452
+ rescue => e
453
+ raise OptionParser::InvalidArgument.new "Failed to parse --endDate '#{val}'. Error: #{e}"
454
+ end
455
+ end
456
+ opts.on("--inUptime BOOL", String, "Set 'In Availability'") do |val|
457
+ params['inUptime'] = ['true','on'].include?(val.to_s.strip)
458
+ end
459
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
460
+ end
461
+ optparse.parse!(args)
462
+ if args.count < 1
463
+ puts optparse
464
+ exit 1
465
+ end
466
+ connect(options)
467
+
468
+ begin
469
+ incident = find_incident_by_id(args[0])
470
+
471
+ if params['status'] == 'closed'
472
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to close the incident '#{incident['id']}'?", options)
473
+ return false
474
+ end
475
+ end
476
+
477
+ if params.empty?
478
+ print_red_alert "Specify atleast one option to update"
479
+ puts optparse
480
+ exit 1
481
+ end
482
+
483
+ payload = {
484
+ 'incident' => {id: incident["id"]}
485
+ }
486
+ payload['incident'].merge!(params)
487
+
488
+ if options[:dry_run]
489
+ print_dry_run @monitoring_interface.incidents.dry.update(incident["id"], payload)
490
+ return
491
+ end
492
+
493
+ json_response = @monitoring_interface.incidents.update(incident["id"], payload)
494
+ if options[:json]
495
+ puts as_json(json_response, options)
496
+ elsif !options[:quiet]
497
+ print_green_success "Updated incident #{incident['id']}"
498
+ _get(incident['id'], {})
499
+ end
500
+
501
+ rescue RestClient::Exception => e
502
+ print_rest_exception(e, options)
503
+ exit 1
504
+ end
505
+ end
506
+
507
+
508
+ def quarantine(args)
509
+ options = {}
510
+ params = {'enabled' => true}
511
+ optparse = OptionParser.new do|opts|
512
+ opts.banner = subcommand_usage("[id list]")
513
+ # this one is a bit weird.. it's a way to toggle incident.inUptime
514
+ # opts.on("--enabled BOOL", String, "Quarantine can be removed with --enabled false") do |val|
515
+ # params['enabled'] = ['on','true'].include?(val.to_s.downcase)
516
+ # end
517
+ opts.on("-d", "--disabled", "Disable Quarantine instead") do
518
+ params['enabled'] = false
519
+ end
520
+ build_common_options(opts, options, [:json, :dry_run, :quiet])
521
+ end
522
+ optparse.parse!(args)
523
+ if args.count < 1
524
+ puts optparse
525
+ exit 1
526
+ end
527
+ connect(options)
528
+
529
+ begin
530
+ incident = find_incident_by_id(args[0])
531
+
532
+ if params.empty?
533
+ print_red_alert "Specify atleast one option to update"
534
+ puts optparse
535
+ exit 1
536
+ end
537
+
538
+ # payload = {
539
+ # 'incident' => {id: incident["id"]}
540
+ # }
541
+ # payload['incident'].merge!(params)
542
+ payload = params
543
+
544
+ if options[:dry_run]
545
+ print_dry_run @monitoring_interface.incidents.dry.update(incident["id"], payload)
546
+ return
547
+ end
548
+
549
+ json_response = @monitoring_interface.incidents.update(incident["id"], payload)
550
+ if options[:json]
551
+ puts as_json(json_response, options)
552
+ elsif !options[:quiet]
553
+ print_green_success "Quarantined incident #{incident['id']}"
554
+ _get(incident['id'], {})
555
+ end
556
+
557
+ rescue RestClient::Exception => e
558
+ print_rest_exception(e, options)
559
+ exit 1
560
+ end
561
+ end
562
+
563
+ def close(args)
564
+ options = {}
565
+ optparse = OptionParser.new do|opts|
566
+ opts.banner = subcommand_usage("[id list]")
567
+ build_common_options(opts, options, [:auto_confirm, :quiet, :json, :dry_run, :remote])
568
+ end
569
+ optparse.parse!(args)
570
+ if args.count < 1
571
+ puts optparse
572
+ exit 1
573
+ end
574
+ connect(options)
575
+ id_list = parse_id_list(args)
576
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to close #{id_list.size == 1 ? 'incident' : 'incidents'} #{anded_list(id_list)}?", options)
577
+ exit 1
578
+ end
579
+ return run_command_for_each_arg(id_list) do |arg|
580
+ _close(arg, options)
581
+ end
582
+ end
583
+
584
+ def _close(id, options)
585
+
586
+ begin
587
+ incident = find_incident_by_id(id)
588
+ already_closed = incident['status'] == 'closed'
589
+ if options[:dry_run]
590
+ print_dry_run @monitoring_interface.incidents.dry.close(incident['id'])
591
+ return
592
+ end
593
+ json_response = @monitoring_interface.incidents.close(incident['id'])
594
+ if options[:json]
595
+ print JSON.pretty_generate(json_response)
596
+ print "\n"
597
+ elsif !options[:quiet]
598
+ print_green_success json_response["msg"] || "Incident #{incident['id']} is now closed"
599
+ # _get(incident['id'] {})
600
+ end
601
+ rescue RestClient::Exception => e
602
+ print_rest_exception(e, options)
603
+ exit 1
604
+ end
605
+ end
606
+
607
+ def reopen(args)
608
+ options = {}
609
+ optparse = OptionParser.new do|opts|
610
+ opts.banner = subcommand_usage("[id list]")
611
+ build_common_options(opts, options, [:auto_confirm, :quiet, :json, :dry_run, :remote])
612
+ end
613
+ optparse.parse!(args)
614
+ if args.count < 1
615
+ puts optparse
616
+ exit 1
617
+ end
618
+ connect(options)
619
+ id_list = parse_id_list(args)
620
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to reopen #{id_list.size == 1 ? 'incident' : 'incidents'} #{anded_list(id_list)}?", options)
621
+ exit 1
622
+ end
623
+ return run_command_for_each_arg(id_list) do |arg|
624
+ _reopen(arg, options)
625
+ end
626
+ end
627
+
628
+ def _reopen(id, options)
629
+
630
+ begin
631
+ incident = find_incident_by_id(id)
632
+ already_open = incident['status'] == 'open'
633
+ if already_open
634
+ print bold,yellow,"Incident #{incident['id']} is already open",reset,"\n"
635
+ return false
636
+ end
637
+ if options[:dry_run]
638
+ print_dry_run @monitoring_interface.incidents.dry.reopen(incident['id'])
639
+ return
640
+ end
641
+ json_response = @monitoring_interface.incidents.reopen(incident['id'])
642
+ if options[:json]
643
+ print JSON.pretty_generate(json_response)
644
+ print "\n"
645
+ elsif !options[:quiet]
646
+ print_green_success json_response["msg"] || "Incident #{incident['id']} is now open"
647
+ # _get(incident['id'] {})
648
+ end
649
+ rescue RestClient::Exception => e
650
+ print_rest_exception(e, options)
651
+ exit 1
652
+ end
653
+ end
654
+
655
+ private
656
+
657
+ def print_incidents_table(incidents, opts={})
658
+ columns = [
659
+ {"ID" => lambda {|incident| incident['id'] } },
660
+ {"SEVERITY" => lambda {|incident| format_severity(incident['severity']) } },
661
+ {"NAME" => lambda {|incident| incident['displayName'] || incident['name'] || 'No Subject' } },
662
+ {"TIME" => lambda {|incident| format_local_dt(incident['startDate']) } },
663
+ {"STATUS" => lambda {|incident| format_monitoring_incident_status(incident) } },
664
+ {"DURATION" => lambda {|incident| format_duration(incident['startDate'], incident['endDate']) } }
665
+ ]
666
+ if opts[:include_fields]
667
+ columns = opts[:include_fields]
668
+ end
669
+ print as_pretty_table(incidents, columns, opts)
670
+ end
671
+
672
+ def print_incident_history_table(history_items, opts={})
673
+ columns = [
674
+ # {"ID" => lambda {|issue| issue['id'] } },
675
+ {"SEVERITY" => lambda {|issue| format_severity(issue['severity']) } },
676
+ {"AVAILABLE" => lambda {|issue| format_boolean issue['available'] } },
677
+ {"TYPE" => lambda {|issue| issue["attachmentType"] } },
678
+ {"NAME" => lambda {|issue| issue['name'] } },
679
+ {"DATE CREATED" => lambda {|issue| format_local_dt(issue['startDate']) } }
680
+ ]
681
+ if opts[:include_fields]
682
+ columns = opts[:include_fields]
683
+ end
684
+ print as_pretty_table(history_items, columns, opts)
685
+ end
686
+
687
+ def print_incident_notifications_table(notifications, opts={})
688
+ columns = [
689
+ {"NAME" => lambda {|notification| notification['recipient'] ? notification['recipient']['name'] : '' } },
690
+ {"DELIVERY TYPE" => lambda {|notification| notification['addressTypes'].to_s } },
691
+ {"NOTIFIED ON" => lambda {|notification| format_local_dt(notification['dateCreated']) } },
692
+ # {"AVAILABLE" => lambda {|notification| format_boolean notification['available'] } },
693
+ # {"TYPE" => lambda {|notification| notification["attachmentType"] } },
694
+ # {"NAME" => lambda {|notification| notification['name'] } },
695
+ {"DATE CREATED" => lambda {|notification|
696
+ date_str = format_local_dt(notification['startDate']).to_s
697
+ if notification['pendingUtil']
698
+ "(pending) #{date_str}"
699
+ else
700
+ date_str
701
+ end
702
+ } }
703
+ ]
704
+ #event['pendingUntil']
705
+ if opts[:include_fields]
706
+ columns = opts[:include_fields]
707
+ end
708
+ print as_pretty_table(notifications, columns, opts)
709
+ end
710
+
711
+ end