morpheus-cli 2.10.3 → 2.11.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 (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