flapjack 1.6.0 → 2.0.0b1
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -6
- data/.gitmodules +1 -1
- data/.rspec +1 -1
- data/.ruby-version +1 -1
- data/.travis.yml +12 -13
- data/CHANGELOG.md +2 -9
- data/CONTRIBUTING.md +7 -2
- data/Gemfile +4 -13
- data/LICENCE +1 -0
- data/README.md +8 -2
- data/Rakefile +2 -2
- data/bin/flapjack +3 -12
- data/build.sh +4 -2
- data/etc/flapjack_config.toml.example +273 -0
- data/features/ack_after_sched_maint.feature +18 -21
- data/features/cli.feature +11 -71
- data/features/cli_flapjack-feed-events.feature +14 -15
- data/features/cli_flapjack-nagios-receiver.feature +12 -41
- data/features/cli_flapper.feature +12 -41
- data/features/cli_purge.feature +5 -6
- data/features/cli_receive-events.feature +6 -7
- data/features/cli_simulate-failed-check.feature +5 -6
- data/features/events.feature +206 -181
- data/features/events_check_names.feature +4 -7
- data/features/notification_rules.feature +144 -223
- data/features/notifications.feature +65 -57
- data/features/rollup.feature +45 -47
- data/features/steps/cli_steps.rb +4 -5
- data/features/steps/events_steps.rb +163 -373
- data/features/steps/notifications_steps.rb +408 -264
- data/features/steps/packaging-lintian_steps.rb +0 -4
- data/features/steps/time_travel_steps.rb +0 -26
- data/features/support/daemons.rb +6 -31
- data/features/support/env.rb +65 -74
- data/flapjack.gemspec +22 -24
- data/lib/flapjack.rb +14 -7
- data/lib/flapjack/cli/flapper.rb +74 -173
- data/lib/flapjack/cli/maintenance.rb +278 -109
- data/lib/flapjack/cli/migrate.rb +950 -0
- data/lib/flapjack/cli/purge.rb +19 -22
- data/lib/flapjack/cli/receiver.rb +150 -326
- data/lib/flapjack/cli/server.rb +8 -235
- data/lib/flapjack/cli/simulate.rb +42 -57
- data/lib/flapjack/configuration.rb +51 -37
- data/lib/flapjack/coordinator.rb +138 -129
- data/lib/flapjack/data/acknowledgement.rb +177 -0
- data/lib/flapjack/data/alert.rb +97 -158
- data/lib/flapjack/data/check.rb +611 -0
- data/lib/flapjack/data/condition.rb +70 -0
- data/lib/flapjack/data/contact.rb +226 -456
- data/lib/flapjack/data/event.rb +96 -184
- data/lib/flapjack/data/extensions/associations.rb +59 -0
- data/lib/flapjack/data/extensions/short_name.rb +25 -0
- data/lib/flapjack/data/medium.rb +428 -0
- data/lib/flapjack/data/metrics.rb +194 -0
- data/lib/flapjack/data/notification.rb +22 -281
- data/lib/flapjack/data/rule.rb +473 -0
- data/lib/flapjack/data/scheduled_maintenance.rb +244 -0
- data/lib/flapjack/data/state.rb +221 -0
- data/lib/flapjack/data/statistic.rb +112 -0
- data/lib/flapjack/data/tag.rb +277 -0
- data/lib/flapjack/data/test_notification.rb +182 -0
- data/lib/flapjack/data/unscheduled_maintenance.rb +159 -0
- data/lib/flapjack/data/validators/id_validator.rb +20 -0
- data/lib/flapjack/exceptions.rb +6 -0
- data/lib/flapjack/filters/acknowledgement.rb +23 -16
- data/lib/flapjack/filters/base.rb +0 -5
- data/lib/flapjack/filters/delays.rb +53 -43
- data/lib/flapjack/filters/ok.rb +23 -14
- data/lib/flapjack/filters/scheduled_maintenance.rb +3 -3
- data/lib/flapjack/filters/unscheduled_maintenance.rb +12 -3
- data/lib/flapjack/gateways/aws_sns.rb +65 -49
- data/lib/flapjack/gateways/aws_sns/alert.text.erb +2 -2
- data/lib/flapjack/gateways/aws_sns/alert_subject.text.erb +2 -2
- data/lib/flapjack/gateways/aws_sns/rollup_subject.text.erb +1 -1
- data/lib/flapjack/gateways/email.rb +107 -90
- data/lib/flapjack/gateways/email/alert.html.erb +19 -18
- data/lib/flapjack/gateways/email/alert.text.erb +20 -14
- data/lib/flapjack/gateways/email/alert_subject.text.erb +2 -1
- data/lib/flapjack/gateways/email/rollup.html.erb +14 -13
- data/lib/flapjack/gateways/email/rollup.text.erb +13 -10
- data/lib/flapjack/gateways/jabber.rb +679 -671
- data/lib/flapjack/gateways/jabber/alert.text.erb +9 -6
- data/lib/flapjack/gateways/jsonapi.rb +164 -350
- data/lib/flapjack/gateways/jsonapi/data/join_descriptor.rb +44 -0
- data/lib/flapjack/gateways/jsonapi/data/method_descriptor.rb +21 -0
- data/lib/flapjack/gateways/jsonapi/helpers/headers.rb +63 -0
- data/lib/flapjack/gateways/jsonapi/helpers/miscellaneous.rb +136 -0
- data/lib/flapjack/gateways/jsonapi/helpers/resources.rb +227 -0
- data/lib/flapjack/gateways/jsonapi/helpers/serialiser.rb +313 -0
- data/lib/flapjack/gateways/jsonapi/helpers/swagger_docs.rb +322 -0
- data/lib/flapjack/gateways/jsonapi/methods/association_delete.rb +115 -0
- data/lib/flapjack/gateways/jsonapi/methods/association_get.rb +288 -0
- data/lib/flapjack/gateways/jsonapi/methods/association_patch.rb +178 -0
- data/lib/flapjack/gateways/jsonapi/methods/association_post.rb +116 -0
- data/lib/flapjack/gateways/jsonapi/methods/metrics.rb +71 -0
- data/lib/flapjack/gateways/jsonapi/methods/resource_delete.rb +119 -0
- data/lib/flapjack/gateways/jsonapi/methods/resource_get.rb +186 -0
- data/lib/flapjack/gateways/jsonapi/methods/resource_patch.rb +239 -0
- data/lib/flapjack/gateways/jsonapi/methods/resource_post.rb +197 -0
- data/lib/flapjack/gateways/jsonapi/middleware/array_param_fixer.rb +27 -0
- data/lib/flapjack/gateways/jsonapi/{rack → middleware}/json_params_parser.rb +7 -6
- data/lib/flapjack/gateways/jsonapi/middleware/request_timestamp.rb +18 -0
- data/lib/flapjack/gateways/oobetet.rb +222 -170
- data/lib/flapjack/gateways/pager_duty.rb +388 -0
- data/lib/flapjack/gateways/pager_duty/alert.text.erb +13 -0
- data/lib/flapjack/gateways/slack.rb +56 -48
- data/lib/flapjack/gateways/slack/alert.text.erb +1 -1
- data/lib/flapjack/gateways/slack/rollup.text.erb +1 -1
- data/lib/flapjack/gateways/sms_aspsms.rb +155 -0
- data/lib/flapjack/gateways/sms_aspsms/alert.text.erb +7 -0
- data/lib/flapjack/gateways/sms_aspsms/rollup.text.erb +2 -0
- data/lib/flapjack/gateways/sms_messagenet.rb +77 -57
- data/lib/flapjack/gateways/sms_messagenet/alert.text.erb +3 -2
- data/lib/flapjack/gateways/sms_nexmo.rb +53 -51
- data/lib/flapjack/gateways/sms_nexmo/alert.text.erb +2 -2
- data/lib/flapjack/gateways/sms_nexmo/rollup.text.erb +1 -1
- data/lib/flapjack/gateways/sms_twilio.rb +79 -62
- data/lib/flapjack/gateways/sms_twilio/alert.text.erb +3 -2
- data/lib/flapjack/gateways/web.rb +437 -345
- data/lib/flapjack/gateways/web/middleware/request_timestamp.rb +18 -0
- data/lib/flapjack/gateways/web/public/css/bootstrap.css +3793 -4340
- data/lib/flapjack/gateways/web/public/css/bootstrap.css.map +1 -0
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.svg +273 -214
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/lib/flapjack/gateways/web/public/js/bootstrap.js +1637 -1607
- data/lib/flapjack/gateways/web/public/js/self_stats.js +1 -2
- data/lib/flapjack/gateways/web/views/_pagination.html.erb +19 -0
- data/lib/flapjack/gateways/web/views/check.html.erb +159 -121
- data/lib/flapjack/gateways/web/views/checks.html.erb +82 -41
- data/lib/flapjack/gateways/web/views/contact.html.erb +59 -71
- data/lib/flapjack/gateways/web/views/contacts.html.erb +32 -8
- data/lib/flapjack/gateways/web/views/index.html.erb +2 -2
- data/lib/flapjack/gateways/web/views/{layout.erb → layout.html.erb} +7 -23
- data/lib/flapjack/gateways/web/views/self_stats.html.erb +32 -33
- data/lib/flapjack/gateways/web/views/tag.html.erb +32 -0
- data/lib/flapjack/gateways/web/views/tags.html.erb +51 -0
- data/lib/flapjack/logger.rb +34 -3
- data/lib/flapjack/notifier.rb +180 -112
- data/lib/flapjack/patches.rb +8 -63
- data/lib/flapjack/pikelet.rb +185 -143
- data/lib/flapjack/processor.rb +323 -191
- data/lib/flapjack/record_queue.rb +33 -0
- data/lib/flapjack/redis_proxy.rb +66 -0
- data/lib/flapjack/utility.rb +21 -15
- data/lib/flapjack/version.rb +2 -1
- data/libexec/httpbroker.go +218 -14
- data/libexec/oneoff.go +13 -10
- data/spec/lib/flapjack/configuration_spec.rb +286 -0
- data/spec/lib/flapjack/coordinator_spec.rb +103 -157
- data/spec/lib/flapjack/data/check_spec.rb +175 -0
- data/spec/lib/flapjack/data/contact_spec.rb +26 -349
- data/spec/lib/flapjack/data/event_spec.rb +76 -291
- data/spec/lib/flapjack/data/medium_spec.rb +19 -0
- data/spec/lib/flapjack/data/rule_spec.rb +43 -0
- data/spec/lib/flapjack/data/scheduled_maintenance_spec.rb +976 -0
- data/spec/lib/flapjack/data/unscheduled_maintenance_spec.rb +34 -0
- data/spec/lib/flapjack/gateways/aws_sns_spec.rb +111 -60
- data/spec/lib/flapjack/gateways/email_spec.rb +194 -161
- data/spec/lib/flapjack/gateways/jabber_spec.rb +961 -162
- data/spec/lib/flapjack/gateways/jsonapi/methods/check_links_spec.rb +155 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/checks_spec.rb +426 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/contact_links_spec.rb +217 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/contacts_spec.rb +425 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/events_spec.rb +271 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/media_spec.rb +257 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/medium_links_spec.rb +163 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/metrics_spec.rb +8 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/rule_links_spec.rb +212 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/rules_spec.rb +289 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/scheduled_maintenance_links_spec.rb +49 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/scheduled_maintenances_spec.rb +242 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/tag_links_spec.rb +274 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/tags_spec.rb +302 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/unscheduled_maintenance_links_spec.rb +49 -0
- data/spec/lib/flapjack/gateways/jsonapi/methods/unscheduled_maintenances_spec.rb +339 -0
- data/spec/lib/flapjack/gateways/jsonapi_spec.rb +1 -1
- data/spec/lib/flapjack/gateways/oobetet_spec.rb +151 -79
- data/spec/lib/flapjack/gateways/pager_duty_spec.rb +353 -0
- data/spec/lib/flapjack/gateways/slack_spec.rb +53 -53
- data/spec/lib/flapjack/gateways/sms_aspsms_spec.rb +106 -0
- data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +111 -54
- data/spec/lib/flapjack/gateways/sms_nexmo_spec.rb +50 -51
- data/spec/lib/flapjack/gateways/sms_twilio_spec.rb +108 -48
- data/spec/lib/flapjack/gateways/web_spec.rb +144 -216
- data/spec/lib/flapjack/notifier_spec.rb +132 -1
- data/spec/lib/flapjack/pikelet_spec.rb +111 -50
- data/spec/lib/flapjack/processor_spec.rb +210 -40
- data/spec/lib/flapjack/redis_proxy_spec.rb +45 -0
- data/spec/lib/flapjack/utility_spec.rb +11 -15
- data/spec/service_consumers/fixture_data.rb +547 -0
- data/spec/service_consumers/pact_helper.rb +21 -32
- data/spec/service_consumers/pacts/flapjack-diner_v2.0.json +4652 -0
- data/spec/service_consumers/provider_states_for_flapjack-diner.rb +279 -322
- data/spec/service_consumers/provider_support.rb +8 -0
- data/spec/spec_helper.rb +34 -44
- data/spec/support/erb_view_helper.rb +1 -1
- data/spec/support/factories.rb +58 -0
- data/spec/support/jsonapi_helper.rb +15 -26
- data/spec/support/mock_logger.rb +43 -0
- data/spec/support/xmpp_comparable.rb +24 -0
- data/src/flapjack/transport_test.go +30 -1
- data/tasks/dump_keys.rake +82 -0
- data/tasks/events.rake +7 -7
- data/tasks/support/flapjack_config_benchmark.toml +28 -0
- data/tasks/support/flapjack_config_benchmark.yaml +0 -2
- metadata +175 -222
- data/Guardfile +0 -14
- data/etc/flapjack_config.yaml.example +0 -477
- data/features/cli_flapjack-populator.feature +0 -90
- data/features/support/silent_system.rb +0 -4
- data/lib/flapjack/cli/import.rb +0 -108
- data/lib/flapjack/data/entity.rb +0 -652
- data/lib/flapjack/data/entity_check.rb +0 -1044
- data/lib/flapjack/data/message.rb +0 -56
- data/lib/flapjack/data/migration.rb +0 -234
- data/lib/flapjack/data/notification_rule.rb +0 -425
- data/lib/flapjack/data/semaphore.rb +0 -44
- data/lib/flapjack/data/tagged.rb +0 -48
- data/lib/flapjack/gateways/jsonapi/check_methods.rb +0 -206
- data/lib/flapjack/gateways/jsonapi/check_presenter.rb +0 -221
- data/lib/flapjack/gateways/jsonapi/contact_methods.rb +0 -186
- data/lib/flapjack/gateways/jsonapi/entity_methods.rb +0 -223
- data/lib/flapjack/gateways/jsonapi/medium_methods.rb +0 -185
- data/lib/flapjack/gateways/jsonapi/metrics_methods.rb +0 -132
- data/lib/flapjack/gateways/jsonapi/notification_rule_methods.rb +0 -141
- data/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods.rb +0 -139
- data/lib/flapjack/gateways/jsonapi/report_methods.rb +0 -146
- data/lib/flapjack/gateways/pagerduty.rb +0 -318
- data/lib/flapjack/gateways/pagerduty/alert.text.erb +0 -10
- data/lib/flapjack/gateways/web/public/css/select2-bootstrap.css +0 -87
- data/lib/flapjack/gateways/web/public/css/select2.css +0 -615
- data/lib/flapjack/gateways/web/public/css/tablesort.css +0 -67
- data/lib/flapjack/gateways/web/public/img/select2-spinner.gif +0 -0
- data/lib/flapjack/gateways/web/public/img/select2.png +0 -0
- data/lib/flapjack/gateways/web/public/img/select2x2.png +0 -0
- data/lib/flapjack/gateways/web/public/js/backbone.js +0 -1581
- data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +0 -322
- data/lib/flapjack/gateways/web/public/js/flapjack.js +0 -82
- data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.js +0 -1640
- data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.widgets.js +0 -1390
- data/lib/flapjack/gateways/web/public/js/modules/contact.js +0 -520
- data/lib/flapjack/gateways/web/public/js/modules/entity.js +0 -28
- data/lib/flapjack/gateways/web/public/js/modules/medium.js +0 -40
- data/lib/flapjack/gateways/web/public/js/select2.js +0 -3397
- data/lib/flapjack/gateways/web/public/js/tablesort.js +0 -44
- data/lib/flapjack/gateways/web/public/js/underscore.js +0 -1276
- data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +0 -173
- data/lib/flapjack/gateways/web/views/entities.html.erb +0 -30
- data/lib/flapjack/gateways/web/views/entity.html.erb +0 -51
- data/lib/flapjack/rack_logger.rb +0 -47
- data/lib/flapjack/redis_pool.rb +0 -42
- data/spec/lib/flapjack/data/entity_check_spec.rb +0 -1418
- data/spec/lib/flapjack/data/entity_spec.rb +0 -872
- data/spec/lib/flapjack/data/message_spec.rb +0 -30
- data/spec/lib/flapjack/data/migration_spec.rb +0 -104
- data/spec/lib/flapjack/data/notification_rule_spec.rb +0 -232
- data/spec/lib/flapjack/data/notification_spec.rb +0 -53
- data/spec/lib/flapjack/data/semaphore_spec.rb +0 -24
- data/spec/lib/flapjack/filters/acknowledgement_spec.rb +0 -6
- data/spec/lib/flapjack/filters/delays_spec.rb +0 -6
- data/spec/lib/flapjack/filters/ok_spec.rb +0 -6
- data/spec/lib/flapjack/filters/scheduled_maintenance_spec.rb +0 -6
- data/spec/lib/flapjack/filters/unscheduled_maintenance_spec.rb +0 -6
- data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +0 -315
- data/spec/lib/flapjack/gateways/jsonapi/check_presenter_spec.rb +0 -223
- data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +0 -131
- data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +0 -389
- data/spec/lib/flapjack/gateways/jsonapi/medium_methods_spec.rb +0 -231
- data/spec/lib/flapjack/gateways/jsonapi/notification_rule_methods_spec.rb +0 -169
- data/spec/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods_spec.rb +0 -114
- data/spec/lib/flapjack/gateways/jsonapi/report_methods_spec.rb +0 -590
- data/spec/lib/flapjack/gateways/pagerduty_spec.rb +0 -249
- data/spec/lib/flapjack/gateways/web/views/check.html.erb_spec.rb +0 -21
- data/spec/lib/flapjack/gateways/web/views/contact.html.erb_spec.rb +0 -24
- data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +0 -16
- data/spec/lib/flapjack/redis_pool_spec.rb +0 -29
- data/spec/service_consumers/pacts/flapjack-diner_v1.0.json +0 -4702
- data/tasks/entities.rake +0 -151
- data/tasks/profile.rake +0 -282
- data/tmp/acknowledge.rb +0 -13
- data/tmp/create_config_yaml.rb +0 -16
- data/tmp/create_event_ok.rb +0 -30
- data/tmp/create_event_unknown.rb +0 -30
- data/tmp/create_events_failure.rb +0 -34
- data/tmp/create_events_ok.rb +0 -32
- data/tmp/create_events_ok_fail_ack_ok.rb +0 -53
- data/tmp/create_events_ok_failure.rb +0 -41
- data/tmp/create_events_ok_failure_ack.rb +0 -53
- data/tmp/dummy_contacts.json +0 -43
- data/tmp/dummy_entities.json +0 -37
- data/tmp/generate_nagios_test_hosts.rb +0 -16
- data/tmp/notification_rules.rb +0 -73
- data/tmp/parse_config_yaml.rb +0 -7
- data/tmp/redis_find_spurious_unknown_states.rb +0 -52
- data/tmp/test_json_post.rb +0 -19
- data/tmp/test_notification_rules_api.rb +0 -171
|
@@ -11,20 +11,16 @@
|
|
|
11
11
|
}
|
|
12
12
|
</style>
|
|
13
13
|
|
|
14
|
-
<p>Hi <%= @alert.
|
|
14
|
+
<p>Hi <%= @alert.medium.contact.name %></p>
|
|
15
15
|
|
|
16
16
|
<p>Monitoring has detected the following:</p>
|
|
17
17
|
|
|
18
|
+
<% check = @alert.check -%>
|
|
18
19
|
<table>
|
|
19
20
|
<tbody>
|
|
20
|
-
<tr>
|
|
21
|
-
<td><strong>Entity</strong></td>
|
|
22
|
-
<td><%= @alert.entity %></td>
|
|
23
|
-
</tr>
|
|
24
|
-
|
|
25
21
|
<tr>
|
|
26
22
|
<td><strong>Check</strong></td>
|
|
27
|
-
<td><%=
|
|
23
|
+
<td><%= check.name %></td>
|
|
28
24
|
</tr>
|
|
29
25
|
|
|
30
26
|
<tr>
|
|
@@ -32,45 +28,50 @@
|
|
|
32
28
|
<td><%= @alert.state_title_case %></td>
|
|
33
29
|
</tr>
|
|
34
30
|
|
|
35
|
-
<%
|
|
31
|
+
<% summary = @alert.summary -%>
|
|
32
|
+
<% unless summary.nil? || summary.empty? -%>
|
|
36
33
|
<tr>
|
|
37
34
|
<td><strong>Summary</strong></td>
|
|
38
|
-
<td><%=
|
|
35
|
+
<td><%= summary %></td>
|
|
39
36
|
</tr>
|
|
40
37
|
<% end %>
|
|
41
38
|
|
|
42
|
-
<%
|
|
39
|
+
<% details = @alert.details -%>
|
|
40
|
+
<% unless details.nil? || details.empty? -%>
|
|
43
41
|
<tr>
|
|
44
42
|
<td><strong>Details</strong></td>
|
|
45
|
-
<td><%=
|
|
43
|
+
<td><%= details %></td>
|
|
46
44
|
</tr>
|
|
47
45
|
<% end %>
|
|
48
46
|
|
|
49
|
-
<%
|
|
47
|
+
<% time = @alert.time -%>
|
|
48
|
+
<% unless time.nil? -%>
|
|
50
49
|
<tr>
|
|
51
50
|
<td><strong>Time</strong></td>
|
|
52
|
-
<td><%= Time.at(
|
|
51
|
+
<td><%= Time.at(time.to_i).to_s %></td>
|
|
53
52
|
</tr>
|
|
54
53
|
<% end %>
|
|
55
54
|
|
|
56
|
-
<%
|
|
55
|
+
<% condition_duration = @alert.condition_duration -%>
|
|
56
|
+
<% unless condition_duration.nil? || (condition_duration <= 40.0) -%>
|
|
57
57
|
<tr>
|
|
58
58
|
<td><strong>Duration</strong></td>
|
|
59
|
-
<td><%= ChronicDuration.output(
|
|
59
|
+
<td><%= ChronicDuration.output(condition_duration, :keep_zero => true) || '0 secs' %></td>
|
|
60
60
|
</tr>
|
|
61
61
|
<% end %>
|
|
62
62
|
|
|
63
|
-
<%
|
|
63
|
+
<% unless @alert.last_state.nil? -%>
|
|
64
64
|
<tr>
|
|
65
65
|
<td><strong>Previous State</strong></td>
|
|
66
66
|
<td><%= @alert.last_state_title_case %></td>
|
|
67
67
|
</tr>
|
|
68
68
|
<% end %>
|
|
69
69
|
|
|
70
|
-
<%
|
|
70
|
+
<% last_summary = @alert.last_summary -%>
|
|
71
|
+
<% unless last_summary.nil? || last_summary.empty? -%>
|
|
71
72
|
<tr>
|
|
72
73
|
<td><strong>Previous Summary</strong></td>
|
|
73
|
-
<td><%=
|
|
74
|
+
<td><%= last_summary %></td>
|
|
74
75
|
</tr>
|
|
75
76
|
<% end %>
|
|
76
77
|
|
|
@@ -1,27 +1,33 @@
|
|
|
1
|
-
Hi <%= @alert.
|
|
1
|
+
Hi <%= @alert.medium.contact.name %>
|
|
2
2
|
|
|
3
3
|
Monitoring has detected the following:
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
<% check = @alert.check -%>
|
|
6
|
+
|
|
7
|
+
Check: <%= check.name %>
|
|
7
8
|
State: <%= @alert.state_title_case %>
|
|
8
|
-
<%
|
|
9
|
-
|
|
9
|
+
<% summary = @alert.summary -%>
|
|
10
|
+
<% unless summary.nil? || summary.empty? -%>
|
|
11
|
+
Summary: <%= summary %>
|
|
10
12
|
<% end -%>
|
|
11
|
-
<%
|
|
12
|
-
|
|
13
|
+
<% details = @alert.details -%>
|
|
14
|
+
<% unless details.nil? || details.empty? -%>
|
|
15
|
+
Details: <%= details %>
|
|
13
16
|
<% end -%>
|
|
14
|
-
<%
|
|
15
|
-
|
|
17
|
+
<% time = @alert.time -%>
|
|
18
|
+
<% unless time.nil? -%>
|
|
19
|
+
Time: <%= Time.at(time.to_i).to_s %>
|
|
16
20
|
<% end -%>
|
|
17
|
-
<%
|
|
18
|
-
|
|
21
|
+
<% condition_duration = @alert.condition_duration -%>
|
|
22
|
+
<% unless condition_duration.nil? || (condition_duration <= 40) -%>
|
|
23
|
+
Duration: <%= ChronicDuration.output(condition_duration, :keep_zero => true) || '0 secs' %>
|
|
19
24
|
<% end -%>
|
|
20
|
-
<%
|
|
25
|
+
<% unless @alert.last_state.nil? -%>
|
|
21
26
|
Previous State: <%= @alert.last_state_title_case %>
|
|
22
27
|
<% end -%>
|
|
23
|
-
<%
|
|
24
|
-
|
|
28
|
+
<% last_summary = @alert.last_summary -%>
|
|
29
|
+
<% unless last_summary.nil? || last_summary.empty? -%>
|
|
30
|
+
Previous Summary: <%= last_summary %>
|
|
25
31
|
<% end -%>
|
|
26
32
|
|
|
27
33
|
Cheers,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
<% check = @alert.check -%>
|
|
2
|
+
<%= @alert.type_sentence_case %>: '<%= check.name %>'
|
|
2
3
|
<% unless ['acknowledgement', 'test'].include?(@alert.notification_type) -%>
|
|
3
4
|
is <%= @alert.state_title_case -%>
|
|
4
5
|
<% end -%>
|
|
@@ -11,27 +11,27 @@
|
|
|
11
11
|
}
|
|
12
12
|
</style>
|
|
13
13
|
|
|
14
|
-
<p>Hi <%= @alert.
|
|
14
|
+
<p>Hi <%= @alert.contact_name %></p>
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
<% rollup_alerts_count = @alert.rollup_alerts.length -%>
|
|
17
|
+
<p>You have <%= rollup_alerts_count %> alerting check<%= (rollup_alerts_count == 1) ? '' : 's' %> as follows:</p>
|
|
17
18
|
|
|
18
19
|
<table>
|
|
19
20
|
<tbody>
|
|
20
21
|
<tr>
|
|
21
22
|
<th>Check</th>
|
|
22
|
-
<th>Entity</th>
|
|
23
23
|
<th>State</th>
|
|
24
24
|
<th>Duration</th>
|
|
25
25
|
<th>Summary</th>
|
|
26
26
|
</tr>
|
|
27
|
-
|
|
28
|
-
<%
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
|
|
28
|
+
<% @alert.rollup_alerts.all.sort_by(&:duration).each do |rollup_alert|
|
|
29
|
+
check = rollup_alert.check
|
|
30
|
+
state = rollup_alert.state
|
|
31
|
+
duration = (ChronicDuration.output(rollup_alert.duration, :keep_zero => true) || '0 secs') -%>
|
|
32
|
+
summary = rollup_alert.summary
|
|
32
33
|
<tr>
|
|
33
|
-
<td><%=
|
|
34
|
-
<td><%= r_entity %></td>
|
|
34
|
+
<td><%= check.name %></td>
|
|
35
35
|
<td><%= ['ok'].include?(state) ? state.upcase : state.titleize %></td>
|
|
36
36
|
<td><%= duration %></td>
|
|
37
37
|
<td><%= summary %></td>
|
|
@@ -40,10 +40,11 @@
|
|
|
40
40
|
</tbody>
|
|
41
41
|
</table>
|
|
42
42
|
|
|
43
|
-
<%
|
|
44
|
-
|
|
43
|
+
<% rollup_threshold = @alert.medium.rollup_threshold -%>
|
|
44
|
+
<% if 'recovery'.eql?(@alert.rollup) %>
|
|
45
|
+
<p>As your email summary threshold is <%= rollup_threshold %>, we're taking your email alerts out of summary mode now. You'll now be emailed individually for each alerting check.</p>
|
|
45
46
|
<% else %>
|
|
46
|
-
<p>Your email alerts are being summarised as your email summary threshold is set to <%=
|
|
47
|
+
<p>Your email alerts are being summarised as your email summary threshold is set to <%= rollup_threshold %>. You'll receive summary emails like this one until your number of alerting checks falls below <%= rollup_threshold %>.</p>
|
|
47
48
|
<% end %>
|
|
48
49
|
|
|
49
50
|
<p>Cheers,<br/>
|
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
Hi <%= @alert.
|
|
1
|
+
Hi <%= @alert.contact_name %>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<% rollup_alerts_count = @alert.rollup_alerts.length %>
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
<%
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
You have <%= rollup_alerts_count %> alerting check<%= rollup_alerts_count == 1 ? '' : 's' %> as follows:
|
|
6
|
+
|
|
7
|
+
<% @alert.rollup_alerts.all.sort_by(&:duration).each do |rollup_alert|
|
|
8
|
+
check = rollup_alert.check
|
|
9
|
+
state = rollup_alert.state
|
|
10
|
+
duration = (ChronicDuration.output(rollup_alert.duration, :keep_zero => true) || '0 secs') -%>
|
|
11
|
+
* <%= check.name %> is <%= ['ok'].include?(state) ? state.upcase : state.titleize %> (<%= duration %>)
|
|
10
12
|
<% end -%>
|
|
11
13
|
|
|
12
|
-
<%
|
|
13
|
-
|
|
14
|
+
<% rollup_threshold = @alert.medium.rollup_threshold -%>
|
|
15
|
+
<% if 'recovery'.eql?(@alert.rollup) -%>
|
|
16
|
+
As your email summary threshold is <%= rollup_threshold %>, we're taking your email alerts out of summary mode now. You'll now be emailed individually for each alerting check.
|
|
14
17
|
<% else -%>
|
|
15
|
-
Your email alerts are being summarised as your email summary threshold is set to <%=
|
|
18
|
+
Your email alerts are being summarised as your email summary threshold is set to <%= rollup_threshold %>. You'll receive summary emails like this one until your number of alerting checks falls below <%= rollup_threshold %>.
|
|
16
19
|
<% end -%>
|
|
17
20
|
|
|
18
21
|
Cheers,
|
|
@@ -1,823 +1,831 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
-
require '
|
|
4
|
-
|
|
3
|
+
require 'monitor'
|
|
5
4
|
require 'socket'
|
|
6
5
|
|
|
7
|
-
require 'blather/client/client'
|
|
8
6
|
require 'chronic_duration'
|
|
7
|
+
require 'rexml/document'
|
|
8
|
+
require 'xmpp4r/query'
|
|
9
|
+
require 'xmpp4r/muc'
|
|
9
10
|
|
|
10
|
-
require 'flapjack/
|
|
11
|
-
require 'flapjack/
|
|
11
|
+
require 'flapjack/redis_proxy'
|
|
12
|
+
require 'flapjack/record_queue'
|
|
12
13
|
require 'flapjack/utility'
|
|
14
|
+
require 'flapjack/exceptions'
|
|
13
15
|
require 'flapjack/version'
|
|
16
|
+
|
|
14
17
|
require 'flapjack/data/alert'
|
|
18
|
+
require 'flapjack/data/check'
|
|
19
|
+
require 'flapjack/data/event'
|
|
20
|
+
require 'flapjack/data/scheduled_maintenance'
|
|
21
|
+
require 'flapjack/data/unscheduled_maintenance'
|
|
22
|
+
require 'flapjack/data/tag'
|
|
23
|
+
|
|
15
24
|
|
|
16
25
|
module Flapjack
|
|
17
26
|
|
|
18
27
|
module Gateways
|
|
19
28
|
|
|
20
|
-
|
|
21
|
-
include Flapjack::Utility
|
|
29
|
+
module Jabber
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
log.level = ::Logger::INFO
|
|
25
|
-
Blather.logger = log
|
|
31
|
+
class Notifier
|
|
26
32
|
|
|
27
|
-
|
|
28
|
-
def initialize
|
|
29
|
-
@chunks = []
|
|
30
|
-
end
|
|
33
|
+
include Flapjack::Utility
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
attr_accessor :siblings
|
|
33
36
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
def initialize(options = {})
|
|
38
|
+
@lock = options[:lock]
|
|
39
|
+
@config = options[:config]
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
@
|
|
41
|
+
# TODO support for config reloading
|
|
42
|
+
@queue = Flapjack::RecordQueue.new(@config['queue'] || 'jabber_notifications',
|
|
43
|
+
Flapjack::Data::Alert)
|
|
40
44
|
end
|
|
41
|
-
end
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
@boot_time = opts[:boot_time]
|
|
46
|
+
def start
|
|
47
|
+
begin
|
|
48
|
+
Zermelo.redis = Flapjack.redis
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
loop do
|
|
51
|
+
@lock.synchronize do
|
|
52
|
+
@queue.foreach {|alert| handle_alert(alert) }
|
|
53
|
+
end
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
@queue.wait
|
|
56
|
+
end
|
|
57
|
+
ensure
|
|
58
|
+
Flapjack.redis.quit
|
|
59
|
+
end
|
|
60
|
+
end
|
|
51
61
|
|
|
52
|
-
|
|
62
|
+
def stop_type
|
|
63
|
+
:exception
|
|
64
|
+
end
|
|
53
65
|
|
|
54
|
-
|
|
55
|
-
@hostname = Socket.gethostname
|
|
66
|
+
private
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@alias = @config['alias'] || 'flapjack'
|
|
60
|
-
@identifiers = ((@config['identifiers'] || []) + [@alias]).uniq
|
|
61
|
-
@logger.debug("I will respond to the following identifiers: #{@identifiers.join(', ')}")
|
|
68
|
+
def handle_alert(alert)
|
|
69
|
+
@bot ||= @siblings && @siblings.detect {|sib| sib.respond_to?(:announce) }
|
|
62
70
|
|
|
63
|
-
|
|
64
|
-
|
|
71
|
+
if @bot.nil?
|
|
72
|
+
Flapjack.logger.warn("jabber bot not running, won't announce")
|
|
73
|
+
return
|
|
74
|
+
end
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
redis_uri = @redis_config[:path] ||
|
|
69
|
-
"redis://#{@redis_config[:host] || '127.0.0.1'}:#{@redis_config[:port] || '6379'}/#{@redis_config[:db] || '0'}"
|
|
70
|
-
shutdown_redis = EM::Hiredis.connect(redis_uri)
|
|
71
|
-
shutdown_redis.rpush(@config['queue'], Flapjack.dump_json('notification_type' => 'shutdown'))
|
|
72
|
-
end
|
|
76
|
+
check = alert.check
|
|
77
|
+
check_name = check.name
|
|
73
78
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
jid += '/' + @hostname unless jid.include?('/')
|
|
77
|
-
@flapjack_jid = Blather::JID.new(jid)
|
|
79
|
+
address = alert.address
|
|
80
|
+
state = alert.state
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
Flapjack.logger.debug("processing jabber notification address: #{address}, " +
|
|
83
|
+
"check: '#{check_name}', state: #{state}, summary: #{alert.summary}")
|
|
80
84
|
|
|
81
|
-
|
|
82
|
-
@flapjack_jid.to_s + ", port: " + @config['port'].to_s +
|
|
83
|
-
", server: " + @config['server'].to_s + ", password: " +
|
|
84
|
-
@config['password'].to_s)
|
|
85
|
+
# event_count = alert.event_count
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
@ack_str = if state.eql?('ok') || ['test', 'acknowledgement'].include?(alert.type)
|
|
88
|
+
nil
|
|
89
|
+
else
|
|
90
|
+
"#{@bot.alias}: ACKID #{alert.event_hash}"
|
|
89
91
|
end
|
|
90
|
-
end
|
|
91
92
|
|
|
92
|
-
|
|
93
|
-
@logger.debug("identifier: #{identifier}, memo: #{memo}")
|
|
94
|
-
memo << {:body => /^#{identifier}[:\s]/}
|
|
95
|
-
memo
|
|
96
|
-
end
|
|
97
|
-
@logger.debug("body_matchers: #{body_matchers}")
|
|
98
|
-
register_handler :message, :groupchat?, body_matchers do |stanza|
|
|
99
|
-
EventMachine::Synchrony.next_tick do
|
|
100
|
-
on_groupchat(stanza)
|
|
101
|
-
end
|
|
102
|
-
end
|
|
93
|
+
message_type = alert.rollup ? 'rollup' : 'alert'
|
|
103
94
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
end
|
|
108
|
-
end
|
|
95
|
+
message_template_erb, message_template =
|
|
96
|
+
load_template(@config['templates'], message_type,
|
|
97
|
+
'text', File.join(File.dirname(__FILE__), 'jabber'))
|
|
109
98
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
EventMachine::Synchrony.next_tick do
|
|
113
|
-
ret = on_disconnect(stanza)
|
|
114
|
-
end
|
|
115
|
-
ret
|
|
116
|
-
end
|
|
117
|
-
end
|
|
99
|
+
@alert = alert
|
|
100
|
+
bnd = binding
|
|
118
101
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@logger.info("Joining room #{room}")
|
|
127
|
-
presence = Blather::Stanza::Presence.new
|
|
128
|
-
presence.from = @flapjack_jid
|
|
129
|
-
presence.to = Blather::JID.new("#{room}/#{@alias}")
|
|
130
|
-
presence << "<x xmlns='http://jabber.org/protocol/muc'><history maxstanzas='0'></x>"
|
|
131
|
-
EventMachine::Synchrony.next_tick do
|
|
132
|
-
write presence
|
|
133
|
-
say(room, "flapjack's jabber gateway started at #{Time.now}, hello! Try typing 'help'.", :groupchat)
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
return if @buffer.empty?
|
|
138
|
-
while stanza = @buffer.shift
|
|
139
|
-
@logger.debug("Sending a buffered jabber message to: #{stanza.to}, using: #{stanza.type}, message: #{stanza.body}")
|
|
140
|
-
EventMachine::Synchrony.next_tick do
|
|
141
|
-
write(stanza)
|
|
102
|
+
message = nil
|
|
103
|
+
begin
|
|
104
|
+
message = message_template_erb.result(bnd).chomp
|
|
105
|
+
rescue
|
|
106
|
+
Flapjack.logger.error "Error while executing the ERB for a jabber message, " +
|
|
107
|
+
"ERB being executed: #{message_template}"
|
|
108
|
+
raise
|
|
142
109
|
end
|
|
110
|
+
|
|
111
|
+
# FIXME: should also check if presence has been established in any group chat rooms that are
|
|
112
|
+
# configured before starting to process events, otherwise the first few may get lost (send
|
|
113
|
+
# before joining the group chat rooms)
|
|
114
|
+
@bot.announce(address, message)
|
|
143
115
|
end
|
|
116
|
+
|
|
144
117
|
end
|
|
145
118
|
|
|
146
|
-
|
|
147
|
-
sched = entity_check.current_maintenance(:scheduled => true)
|
|
148
|
-
unsched = entity_check.current_maintenance(:unscheduled => true)
|
|
149
|
-
out = ''
|
|
119
|
+
class Interpreter
|
|
150
120
|
|
|
151
|
-
|
|
152
|
-
out += "Not in scheduled or unscheduled maintenance.\n"
|
|
153
|
-
else
|
|
154
|
-
if sched.nil?
|
|
155
|
-
out += "Not in scheduled maintenance.\n"
|
|
156
|
-
else
|
|
157
|
-
start = Time.at(sched[:start_time])
|
|
158
|
-
finish = Time.at(sched[:start_time] + sched[:duration])
|
|
159
|
-
remain = time_period_in_words( (finish - current_time).ceil )
|
|
160
|
-
# TODO a simpler time format?
|
|
161
|
-
out += "In scheduled maintenance: #{start} -> #{finish} (#{remain} remaining)\n"
|
|
162
|
-
end
|
|
121
|
+
CHECK_MATCH_FRAGMENT = "(?:checks\s+(?:matching\s+\/(.+?)\/|with\s+tag\s+(.+?))|(.+?))"
|
|
163
122
|
|
|
164
|
-
|
|
165
|
-
out += "Not in unscheduled maintenance.\n"
|
|
166
|
-
else
|
|
167
|
-
start = Time.at(unsched[:start_time])
|
|
168
|
-
finish = Time.at(unsched[:start_time] + unsched[:duration])
|
|
169
|
-
remain = time_period_in_words( (finish - current_time).ceil )
|
|
170
|
-
# TODO a simpler time format?
|
|
171
|
-
out += "In unscheduled maintenance: #{start} -> #{finish} (#{remain} remaining)\n"
|
|
172
|
-
end
|
|
173
|
-
end
|
|
123
|
+
attr_accessor :siblings
|
|
174
124
|
|
|
175
|
-
|
|
176
|
-
end
|
|
125
|
+
include Flapjack::Utility
|
|
177
126
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
th = TextHandler.new
|
|
184
|
-
parser = Nokogiri::HTML::SAX::Parser.new(th)
|
|
185
|
-
parser.parse(command_raw)
|
|
186
|
-
command = th.chunks.join(' ')
|
|
187
|
-
|
|
188
|
-
case command
|
|
189
|
-
when /^ACKID\s+([0-9A-F]+)(?:\s*(.*?)(?:\s*duration:.*?(\w+.*))?)$/im
|
|
190
|
-
ackid = $1
|
|
191
|
-
comment = $2
|
|
192
|
-
duration_str = $3
|
|
193
|
-
|
|
194
|
-
error = nil
|
|
195
|
-
dur = nil
|
|
196
|
-
|
|
197
|
-
if comment.nil? || (comment.length == 0)
|
|
198
|
-
error = "please provide a comment, eg \"#{@config['alias']}: ACKID #{$1} AL looking\""
|
|
199
|
-
elsif duration_str
|
|
200
|
-
# a fairly liberal match above, we'll let chronic_duration do the heavy lifting
|
|
201
|
-
dur = ChronicDuration.parse(duration_str)
|
|
202
|
-
end
|
|
127
|
+
def initialize(opts = {})
|
|
128
|
+
@lock = opts[:lock]
|
|
129
|
+
@stop_cond = opts[:stop_condition]
|
|
130
|
+
@config = opts[:config]
|
|
203
131
|
|
|
204
|
-
|
|
205
|
-
duration = (dur.nil? || (dur <= 0)) ? four_hours : dur
|
|
132
|
+
@boot_time = opts[:boot_time]
|
|
206
133
|
|
|
207
|
-
|
|
134
|
+
@should_quit = false
|
|
208
135
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
else
|
|
212
|
-
entity_check = Flapjack::Data::EntityCheck.for_event_id(event_id, :redis => @redis)
|
|
213
|
-
error = "unknown entity" if entity_check.nil?
|
|
214
|
-
end
|
|
136
|
+
@messages = []
|
|
137
|
+
end
|
|
215
138
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
else
|
|
219
|
-
entity_name, check = event_id.split(':', 2)
|
|
139
|
+
def start
|
|
140
|
+
Zermelo.redis = Flapjack.redis
|
|
220
141
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
msg =
|
|
142
|
+
@lock.synchronize do
|
|
143
|
+
|
|
144
|
+
@bot = self.siblings ? self.siblings.detect {|sib| sib.respond_to?(:announce)} : nil
|
|
145
|
+
|
|
146
|
+
until @messages.empty? && @should_quit
|
|
147
|
+
while msg = @messages.pop
|
|
148
|
+
Flapjack.logger.info "interpreter received #{msg.inspect}"
|
|
149
|
+
interpret(msg[:room], msg[:nick], msg[:time], msg[:message])
|
|
150
|
+
end
|
|
151
|
+
@stop_cond.wait_while { @messages.empty? && !@should_quit }
|
|
227
152
|
end
|
|
228
|
-
action = Proc.new {
|
|
229
|
-
Flapjack::Data::Event.create_acknowledgement(
|
|
230
|
-
entity_name, check,
|
|
231
|
-
:summary => (comment || ''),
|
|
232
|
-
:acknowledgement_id => ackid,
|
|
233
|
-
:duration => duration,
|
|
234
|
-
:redis => @redis
|
|
235
|
-
)
|
|
236
|
-
}
|
|
237
153
|
end
|
|
154
|
+
Flapjack.redis.quit
|
|
155
|
+
end
|
|
238
156
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
" find entities matching /pattern/\n" +
|
|
248
|
-
" find checks[ matching /pattern/] on (<entity>|entities matching /pattern/)\n" +
|
|
249
|
-
" test notifications for <entity>[:<check>]\n" +
|
|
250
|
-
" tell me about <entity>[:<check>]\n" +
|
|
251
|
-
" identify\n" +
|
|
252
|
-
" help\n"
|
|
253
|
-
|
|
254
|
-
when /^identify$/i
|
|
255
|
-
t = Process.times
|
|
256
|
-
fqdn = `/bin/hostname -f`.chomp
|
|
257
|
-
pid = Process.pid
|
|
258
|
-
msg = "Flapjack #{Flapjack::VERSION} process #{pid} on #{fqdn}\n" +
|
|
259
|
-
"Identifiers: #{@identifiers.join(', ')}\n" +
|
|
260
|
-
"Boot time: #{@boot_time}\n" +
|
|
261
|
-
"User CPU Time: #{t.utime}\n" +
|
|
262
|
-
"System CPU Time: #{t.stime}\n" +
|
|
263
|
-
`uname -a`.chomp + "\n"
|
|
264
|
-
|
|
265
|
-
when /^test notifications for\s+([a-z0-9\-\.]+)(?::(.+))?$/im
|
|
266
|
-
entity_name = $1
|
|
267
|
-
check_name = $2 || 'test'
|
|
268
|
-
|
|
269
|
-
if entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => @redis)
|
|
270
|
-
msg = "so you want me to test notifications for entity: #{entity_name}, check: #{check_name} eh? ... well OK!"
|
|
271
|
-
|
|
272
|
-
summary = "Testing notifications to all contacts interested in entity: #{entity_name}, check: #{check_name}"
|
|
273
|
-
Flapjack::Data::Event.test_notifications(entity_name, check_name, :summary => summary, :redis => @redis)
|
|
274
|
-
else
|
|
275
|
-
msg = "yeah, no I can't see #{entity_name} in my systems"
|
|
157
|
+
def stop_type
|
|
158
|
+
:signal
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def receive_message(room, nick, time, msg)
|
|
162
|
+
@lock.synchronize do
|
|
163
|
+
@messages += [{:room => room, :nick => nick, :time => time, :message => msg}]
|
|
164
|
+
@stop_cond.signal
|
|
276
165
|
end
|
|
166
|
+
end
|
|
277
167
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
168
|
+
def get_check_details(check, at_time)
|
|
169
|
+
start_range = Zermelo::Filters::IndexRange.new(nil, at_time, :by_score => true)
|
|
170
|
+
end_range = Zermelo::Filters::IndexRange.new(at_time, nil, :by_score => true)
|
|
281
171
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
msg = "so you'd like details on entity: #{entity_name}#{check_str} hmm? ... OK!\n"
|
|
172
|
+
sched = check.scheduled_maintenances.intersect(:start_time => start_range,
|
|
173
|
+
:end_time => end_range).all.max_by(&:end_time)
|
|
285
174
|
|
|
286
|
-
|
|
175
|
+
unsched = check.unscheduled_maintenances.intersect(:start_time => start_range,
|
|
176
|
+
:end_time => end_range).all.max_by(&:end_time)
|
|
287
177
|
|
|
288
|
-
|
|
178
|
+
out = ''
|
|
289
179
|
|
|
290
|
-
|
|
291
|
-
|
|
180
|
+
if sched.nil? && unsched.nil?
|
|
181
|
+
out += "Not in scheduled or unscheduled maintenance.\n"
|
|
182
|
+
else
|
|
183
|
+
if sched.nil?
|
|
184
|
+
out += "Not in scheduled maintenance.\n"
|
|
292
185
|
else
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
186
|
+
remain = time_period_in_words( (sched.end_time - at_time).ceil )
|
|
187
|
+
# TODO a simpler time format?
|
|
188
|
+
out += "In scheduled maintenance: #{sched.start_time} -> #{sched.end_time} (#{remain} remaining)\n"
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
if unsched.nil?
|
|
192
|
+
out += "Not in unscheduled maintenance.\n"
|
|
193
|
+
else
|
|
194
|
+
remain = time_period_in_words( (unsched.end_time - at_time).ceil )
|
|
195
|
+
# TODO a simpler time format?
|
|
196
|
+
out += "In unscheduled maintenance: #{unsched.start_time} -> #{unsched.end_time} (#{remain} remaining)\n"
|
|
299
197
|
end
|
|
300
|
-
else
|
|
301
|
-
msg = "hmmm, I can't see #{entity_name} in my systems"
|
|
302
198
|
end
|
|
303
199
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
entity_pattern = $2 ? $2.strip : nil
|
|
307
|
-
entity_name = $3
|
|
200
|
+
out
|
|
201
|
+
end
|
|
308
202
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
elsif entity_pattern
|
|
312
|
-
Flapjack::Data::Entity.find_all_name_matching(entity_pattern, :redis => @redis)
|
|
313
|
-
else
|
|
314
|
-
[]
|
|
315
|
-
end
|
|
203
|
+
def derive_check_ids_for(pattern, tag_name, check_name, options = {})
|
|
204
|
+
lock_klasses = options[:lock_klasses] || []
|
|
316
205
|
|
|
317
|
-
|
|
206
|
+
deriver = if !pattern.nil? && !pattern.strip.empty?
|
|
207
|
+
proc {
|
|
208
|
+
checks = begin
|
|
209
|
+
Flapjack::Data::Check.intersect(:name => Regexp.new(pattern.strip))
|
|
210
|
+
rescue RegexpError
|
|
211
|
+
nil
|
|
212
|
+
end
|
|
318
213
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
214
|
+
if checks.nil?
|
|
215
|
+
"Error parsing /#{pattern.strip}/"
|
|
216
|
+
else
|
|
217
|
+
num_checks = checks.count
|
|
218
|
+
if num_checks == 0
|
|
219
|
+
"No checks match /#{pattern.strip}/"
|
|
220
|
+
else
|
|
221
|
+
checks = checks.sort(:name, :limit => options[:limit]) if options[:limit]
|
|
222
|
+
yield(checks.ids, "matching /#{pattern.strip}/", num_checks) if block_given?
|
|
223
|
+
end
|
|
224
|
+
end
|
|
325
225
|
}
|
|
326
|
-
|
|
327
|
-
|
|
226
|
+
elsif !tag_name.nil? && !tag_name.strip.empty?
|
|
227
|
+
|
|
228
|
+
lock_klasses.unshift(Flapjack::Data::Tag)
|
|
328
229
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
230
|
+
proc {
|
|
231
|
+
tag = Flapjack::Data::Tag.intersect(:name => tag_name.strip).all.first
|
|
232
|
+
|
|
233
|
+
if tag.nil?
|
|
234
|
+
"No tag '#{tag_name.strip}'"
|
|
333
235
|
else
|
|
334
|
-
|
|
236
|
+
checks = tag.checks
|
|
237
|
+
num_checks = checks.count
|
|
238
|
+
if num_checks == 0
|
|
239
|
+
"No checks with tag '#{tag_name.strip}'"
|
|
240
|
+
else
|
|
241
|
+
checks = checks.sort(:name, :limit => options[:limit]) if options[:limit]
|
|
242
|
+
yield(checks.ids, "with tag '#{tag_name}'", num_checks) if block_given?
|
|
243
|
+
end
|
|
335
244
|
end
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
245
|
+
}
|
|
246
|
+
elsif !check_name.nil? && !check_name.strip.empty?
|
|
247
|
+
proc {
|
|
248
|
+
checks = Flapjack::Data::Check.intersect(:name => check_name.strip)
|
|
340
249
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
when entity_name
|
|
350
|
-
if entities.empty?
|
|
351
|
-
msg = "found no entity for '#{entity_name}'"
|
|
352
|
-
else
|
|
353
|
-
msg = report_entities.call(entities)
|
|
354
|
-
end
|
|
250
|
+
if checks.empty?
|
|
251
|
+
"No check exists with name '#{check_name.strip}'"
|
|
252
|
+
else
|
|
253
|
+
num_checks = checks.count
|
|
254
|
+
checks = checks.sort(:name, :limit => options[:limit]) if options[:limit]
|
|
255
|
+
yield(checks.ids, "with name '#{check_name.strip}'", num_checks) if block_given?
|
|
256
|
+
end
|
|
257
|
+
}
|
|
355
258
|
end
|
|
356
259
|
|
|
357
|
-
|
|
358
|
-
pattern = $1.strip
|
|
359
|
-
entity_list = Flapjack::Data::Entity.find_all_name_matching(pattern, :redis => @redis)
|
|
360
|
-
|
|
361
|
-
if entity_list
|
|
362
|
-
max_showable = 30
|
|
363
|
-
number_found = entity_list.length
|
|
364
|
-
entity_list = entity_list[0..(max_showable - 1)] if number_found > max_showable
|
|
365
|
-
|
|
366
|
-
case
|
|
367
|
-
when number_found == 0
|
|
368
|
-
msg = "found no entities matching /#{pattern}/"
|
|
369
|
-
when number_found == 1
|
|
370
|
-
msg = "found 1 entity matching /#{pattern}/ ... \n"
|
|
371
|
-
when number_found > max_showable
|
|
372
|
-
msg = "showing first #{max_showable} of #{number_found} entities found matching /#{pattern}/\n"
|
|
373
|
-
else
|
|
374
|
-
msg = "found #{number_found} entities matching /#{pattern}/ ... \n"
|
|
375
|
-
end
|
|
376
|
-
msg += entity_list.join(', ') unless entity_list.empty?
|
|
260
|
+
return if deriver.nil?
|
|
377
261
|
|
|
262
|
+
if lock_klasses.empty?
|
|
263
|
+
Flapjack::Data::Check.lock(&deriver)
|
|
378
264
|
else
|
|
379
|
-
|
|
265
|
+
Flapjack::Data::Check.lock(*lock_klasses, &deriver)
|
|
380
266
|
end
|
|
267
|
+
end
|
|
381
268
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
duration = ChronicDuration.parse(duration_str)
|
|
387
|
-
entity_list = Flapjack::Data::Entity.find_all_name_matching(entity_pattern, :redis => @redis)
|
|
269
|
+
def interpret(room, nick, time, command)
|
|
270
|
+
msg = nil
|
|
271
|
+
action = nil
|
|
272
|
+
check = nil
|
|
388
273
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
274
|
+
begin
|
|
275
|
+
case command
|
|
276
|
+
when /^help\s*$/
|
|
277
|
+
msg = "commands: \n" +
|
|
278
|
+
" find (number) checks matching /pattern/\n" +
|
|
279
|
+
" find (number) checks with tag <tag>\n" +
|
|
280
|
+
" state of <check>\n" +
|
|
281
|
+
" state of checks matching /pattern/\n" +
|
|
282
|
+
" state of checks with tag <tag>\n" +
|
|
283
|
+
" tell me about <check>\n" +
|
|
284
|
+
" tell me about (number) checks matching /pattern/\n" +
|
|
285
|
+
" tell me about (number) checks with tag <tag>\n" +
|
|
286
|
+
" ACKID <id>[ duration: <time spec>][ comment: <comment>]\n" +
|
|
287
|
+
" ack <check>[ duration: <time spec>][ comment: <comment>]\n" +
|
|
288
|
+
" ack checks matching /pattern/[ duration: <time spec>][ comment: <comment>]\n" +
|
|
289
|
+
" ack checks with tag <tag>[ duration: <time spec>][ comment: <comment>]\n" +
|
|
290
|
+
" maint <check>[ (start-at|start-in): <time spec>][ duration: <time spec>][ comment: <comment>]\n" +
|
|
291
|
+
" maint checks matching /pattern/[ (start-at|start-in): <time spec>][ duration: <time spec>][ comment: <comment>]\n" +
|
|
292
|
+
" maint checks with tag <tag>[ (start-at|start-in): <time spec>][ duration: <time spec>][ comment: <comment>]\n" +
|
|
293
|
+
" test notifications for <check>\n" +
|
|
294
|
+
" test notifications for checks matching /pattern/\n" +
|
|
295
|
+
" test notifications for checks with tag <tag>\n" +
|
|
296
|
+
" identify\n" +
|
|
297
|
+
" help\n"
|
|
298
|
+
|
|
299
|
+
when /^identify\s*$/
|
|
300
|
+
t = Process.times
|
|
301
|
+
fqdn = `/bin/hostname -f`.chomp
|
|
302
|
+
pid = Process.pid
|
|
303
|
+
msg = "Flapjack #{Flapjack::VERSION} process #{pid} on #{fqdn} \n" +
|
|
304
|
+
"Identifiers: #{@bot.identifiers.join(', ')}\n" +
|
|
305
|
+
"Boot time: #{@boot_time}\n" +
|
|
306
|
+
"User CPU Time: #{t.utime}\n" +
|
|
307
|
+
"System CPU Time: #{t.stime}\n" +
|
|
308
|
+
`uname -a`.chomp + "\n"
|
|
309
|
+
|
|
310
|
+
when /^find\s+(\d+\s+)?checks\s+(?:matching\s+\/(.+)\/|with\s+tag\s+(.+))\s*$/im
|
|
311
|
+
limit_checks = Regexp.last_match(1).nil? ? 30 : Regexp.last_match(1).strip.to_i
|
|
312
|
+
pattern = Regexp.last_match(2)
|
|
313
|
+
tag = Regexp.last_match(3)
|
|
314
|
+
|
|
315
|
+
msg = derive_check_ids_for(pattern, tag, nil, :limit => limit_checks) do |check_ids, descriptor, num_checks|
|
|
316
|
+
resp = "Checks #{descriptor}:\n"
|
|
317
|
+
checks = Flapjack::Data::Check.find_by_ids(*check_ids)
|
|
318
|
+
resp += "Showing first #{limit_checks} results of #{num_checks}:\n" if num_checks > limit_checks
|
|
319
|
+
resp += checks.map {|chk|
|
|
320
|
+
cond = chk.condition
|
|
321
|
+
"#{chk.name} is #{cond.nil? ? '[none]' : cond.upcase}"
|
|
322
|
+
}.join(', ')
|
|
323
|
+
resp
|
|
324
|
+
end
|
|
394
325
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
entity, check,
|
|
408
|
-
:summary => comment,
|
|
409
|
-
:duration => duration,
|
|
410
|
-
:redis => @redis
|
|
411
|
-
)
|
|
412
|
-
end
|
|
413
|
-
end
|
|
414
|
-
msg = entities.inject("Ack list:\n") {|memo,kv|
|
|
415
|
-
kv[1].each {|e| memo << "#{kv[0]}:#{e}\n" }
|
|
416
|
-
memo
|
|
417
|
-
}
|
|
418
|
-
else
|
|
419
|
-
msg = "found no matching entities with failing checks"
|
|
420
|
-
end
|
|
421
|
-
else
|
|
422
|
-
msg = "that doesn't seem to be a valid pattern - /#{pattern}/"
|
|
423
|
-
end
|
|
424
|
-
end
|
|
326
|
+
when /^state\s+of\s+#{CHECK_MATCH_FRAGMENT}\s*$/im
|
|
327
|
+
pattern = Regexp.last_match(1)
|
|
328
|
+
tag = Regexp.last_match(2)
|
|
329
|
+
check_name = Regexp.last_match(3)
|
|
330
|
+
|
|
331
|
+
msg = derive_check_ids_for(pattern, tag, check_name) do |check_ids, descriptor|
|
|
332
|
+
"State of checks #{descriptor}:\n" +
|
|
333
|
+
Flapjack::Data::Check.intersect(:id => check_ids).collect {|chk|
|
|
334
|
+
cond = chk.condition
|
|
335
|
+
"#{chk.name} - #{cond.nil? ? '[none]' : cond.upcase}"
|
|
336
|
+
}.join("\n")
|
|
337
|
+
end
|
|
425
338
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
339
|
+
when /^tell\s+me\s+about\s(\d+\s+)?+#{CHECK_MATCH_FRAGMENT}\s*$/im
|
|
340
|
+
limit_checks = Regexp.last_match(1).nil? ? 30 : Regexp.last_match(1).strip.to_i
|
|
341
|
+
pattern = Regexp.last_match(2)
|
|
342
|
+
tag = Regexp.last_match(3)
|
|
343
|
+
check_name = Regexp.last_match(4)
|
|
344
|
+
|
|
345
|
+
msg = derive_check_ids_for(pattern, tag, check_name, :limit => limit_checks,
|
|
346
|
+
:lock_klasses => [Flapjack::Data::ScheduledMaintenance,
|
|
347
|
+
Flapjack::Data::UnscheduledMaintenance]) do |check_ids, descriptor, num_checks|
|
|
348
|
+
current_time = Time.now
|
|
349
|
+
resp = "Details of checks #{descriptor}\n"
|
|
350
|
+
resp += "Showing first #{limit_checks} results of #{num_checks}:\n" if num_checks > limit_checks
|
|
351
|
+
resp += Flapjack::Data::Check.intersect(:id => check_ids).collect {|chk|
|
|
352
|
+
get_check_details(chk, current_time)
|
|
353
|
+
}.join("")
|
|
354
|
+
end
|
|
440
355
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
Chronic.parse(start_at).to_i
|
|
446
|
-
else
|
|
447
|
-
Time.now.to_i
|
|
448
|
-
end
|
|
356
|
+
when /^ACKID\s+([0-9A-F]+)(?:\s*(.*?)(?:\s*duration:.*?(\w+.*))?)$/im
|
|
357
|
+
ackid = Regexp.last_match(1)
|
|
358
|
+
comment = Regexp.last_match(2)
|
|
359
|
+
duration_str = Regexp.last_match(3)
|
|
449
360
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
361
|
+
error = nil
|
|
362
|
+
dur = nil
|
|
363
|
+
|
|
364
|
+
if comment.nil? || (comment.length == 0)
|
|
365
|
+
error = "please provide a comment, eg \"#{@bot.alias}: ACKID #{Regexp.last_match(1)} AL looking\""
|
|
366
|
+
elsif duration_str
|
|
367
|
+
# a fairly liberal match above, we'll let chronic_duration do the heavy lifting
|
|
368
|
+
dur = ChronicDuration.parse(duration_str)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
four_hours = 4 * 60 * 60
|
|
372
|
+
duration = (dur.nil? || (dur <= 0)) ? four_hours : dur
|
|
373
|
+
|
|
374
|
+
check = Flapjack::Data::Check.intersect(:ack_hash => ackid).all.first
|
|
375
|
+
|
|
376
|
+
if check.nil?
|
|
377
|
+
msg = "ERROR - couldn't ACK #{ackid} - not found"
|
|
378
|
+
else
|
|
379
|
+
check_name = check.name
|
|
380
|
+
|
|
381
|
+
details = "#{check_name} (#{ackid})"
|
|
382
|
+
if check.in_unscheduled_maintenance?
|
|
383
|
+
msg = "Changing ACK for #{details}"
|
|
384
|
+
else
|
|
385
|
+
msg = "ACKing #{details}"
|
|
473
386
|
end
|
|
474
|
-
msg = entities.inject("Maint list:\n") {|memo,kv|
|
|
475
|
-
kv[1].each {|e| memo << "#{kv[0].name}:#{e}\n" }
|
|
476
|
-
memo
|
|
477
|
-
}
|
|
478
|
-
else
|
|
479
|
-
msg = "found no matching entities with active checks"
|
|
480
|
-
end
|
|
481
|
-
else
|
|
482
|
-
msg = "that doesn't seem to be a valid pattern - /#{pattern}/"
|
|
483
|
-
end
|
|
484
|
-
end
|
|
485
387
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
msg = "found no entities matching /#{entity_pattern}/"
|
|
495
|
-
when number_found >= 1
|
|
496
|
-
entities = entity_names.map {|name|
|
|
497
|
-
Flapjack::Data::Entity.find_by_name(name, :redis => @redis)
|
|
498
|
-
}.compact.inject({}) {|memo, entity|
|
|
499
|
-
memo[entity.name] = entity.check_list.map {|check_name|
|
|
500
|
-
ec = Flapjack::Data::EntityCheck.for_entity(entity, check_name, :redis => @redis)
|
|
501
|
-
"#{check_name}: #{ec.state}"
|
|
388
|
+
action = Proc.new {
|
|
389
|
+
Flapjack::Data::Event.create_acknowledgements(
|
|
390
|
+
@config['processor_queue'] || 'events',
|
|
391
|
+
[check],
|
|
392
|
+
:summary => (comment || ''),
|
|
393
|
+
:acknowledgement_id => ackid,
|
|
394
|
+
:duration => duration,
|
|
395
|
+
)
|
|
502
396
|
}
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
msg = entities.inject("Status list:\n") {|memo,kv|
|
|
506
|
-
kv[1].each {|e| memo << "#{kv[0]}:#{e}\n"}
|
|
507
|
-
memo
|
|
508
|
-
}
|
|
509
|
-
else
|
|
510
|
-
msg = "found no matching entities with failing checks"
|
|
511
|
-
end
|
|
512
|
-
else
|
|
513
|
-
msg = "that doesn't seem to be a valid pattern - /#{pattern}/"
|
|
514
|
-
end
|
|
397
|
+
end
|
|
515
398
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
399
|
+
when /^ack\s+#{CHECK_MATCH_FRAGMENT}(?:\s+(.*?)(?:\s*duration:.*?(\w+.*))?)?\s*$/im
|
|
400
|
+
pattern = Regexp.last_match(1)
|
|
401
|
+
tag = Regexp.last_match(2)
|
|
402
|
+
check_name = Regexp.last_match(3)
|
|
403
|
+
comment = Regexp.last_match(4) ? Regexp.last_match(4).strip : nil
|
|
404
|
+
duration_str = Regexp.last_match(5) ? Regexp.last_match(5).strip : '1 hour'
|
|
405
|
+
duration = ChronicDuration.parse(duration_str)
|
|
406
|
+
|
|
407
|
+
msg = derive_check_ids_for(pattern, tag, check_name,
|
|
408
|
+
:lock_klasses => [Flapjack::Data::UnscheduledMaintenance]) do |check_ids, descriptor|
|
|
409
|
+
|
|
410
|
+
failing_checks = Flapjack::Data::Check.
|
|
411
|
+
intersect(:id => check_ids, :failing => true)
|
|
412
|
+
|
|
413
|
+
if failing_checks.empty?
|
|
414
|
+
"No failing checks #{descriptor}"
|
|
415
|
+
else
|
|
416
|
+
summary = "#{nick}: #{comment.blank? ? 'Set via chatbot' : comment}"
|
|
417
|
+
|
|
418
|
+
action = Proc.new {
|
|
419
|
+
Flapjack::Data::Event.create_acknowledgements(
|
|
420
|
+
@config['processor_queue'] || 'events',
|
|
421
|
+
failing_checks,
|
|
422
|
+
:summary => summary,
|
|
423
|
+
:duration => duration
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
"Ack list:\n" + failing_checks.map(&:name).join("\n")
|
|
428
|
+
end
|
|
429
|
+
end
|
|
523
430
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
431
|
+
when /^maint\s+#{CHECK_MATCH_FRAGMENT}\s+(?:start-in:.*?(\w+.*?)|start-at:.*?(\w+.*?))?(?:\s+duration:.*?(\w+.*?))?(?:\s+comment:.*?(\w+.*?))?\s*$/im
|
|
432
|
+
pattern = Regexp.last_match(1)
|
|
433
|
+
tag = Regexp.last_match(2)
|
|
434
|
+
check_name = Regexp.last_match(3)
|
|
435
|
+
start_in = Regexp.last_match(4) ? Regexp.last_match(4).strip : nil
|
|
436
|
+
start_at = Regexp.last_match(5) ? Regexp.last_match(5).strip : nil
|
|
437
|
+
duration_str = Regexp.last_match(6) ? Regexp.last_match(6).strip : '1 hour'
|
|
438
|
+
duration = ChronicDuration.parse(duration_str)
|
|
439
|
+
comment = Regexp.last_match(7) ? Regexp.last_match(7).strip : 'Test maintenance'
|
|
440
|
+
|
|
441
|
+
started = case
|
|
442
|
+
when start_in
|
|
443
|
+
Time.now.to_i + ChronicDuration.parse(start_in)
|
|
444
|
+
when start_at
|
|
445
|
+
Chronic.parse(start_at).to_i
|
|
446
|
+
else
|
|
447
|
+
Time.now.to_i
|
|
448
|
+
end
|
|
529
449
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
when number_found == 0
|
|
534
|
-
msg = "found no entities matching /#{entity_pattern}/"
|
|
535
|
-
when number_found >= 1
|
|
450
|
+
msg = derive_check_ids_for(pattern, tag, check_name,
|
|
451
|
+
:lock_klasses => [Flapjack::Data::ScheduledMaintenance]) do |check_ids, descriptor|
|
|
452
|
+
checks = Flapjack::Data::Check.find_by_ids(*check_ids)
|
|
536
453
|
|
|
537
|
-
|
|
454
|
+
checks.each do |chk|
|
|
455
|
+
sched_maint = Flapjack::Data::ScheduledMaintenance.new(
|
|
456
|
+
:start_time => started,
|
|
457
|
+
:end_time => started + duration,
|
|
458
|
+
:summary => comment
|
|
459
|
+
)
|
|
460
|
+
sched_maint.save
|
|
538
461
|
|
|
539
|
-
|
|
540
|
-
if entity_list.include?(k)
|
|
541
|
-
[k, v.keep_if {|e| e =~ /#{check_pattern}/}].compact
|
|
462
|
+
chk.scheduled_maintenances << sched_maint
|
|
542
463
|
end
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
464
|
+
|
|
465
|
+
"Scheduled maintenance for #{duration/60} minutes starting at #{Time.at(started)} on:\n" + checks.collect {|c| "#{c.name}" }.join("\n")
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
when /^test\s+notifications\s+for\s+#{CHECK_MATCH_FRAGMENT}\s*$/im
|
|
469
|
+
pattern = Regexp.last_match(1)
|
|
470
|
+
tag = Regexp.last_match(2)
|
|
471
|
+
check_name = Regexp.last_match(3)
|
|
472
|
+
|
|
473
|
+
msg = derive_check_ids_for(pattern, tag, check_name) do |check_ids, descriptor|
|
|
474
|
+
summary = "Testing notifications to all contacts interested in checks #{descriptor}"
|
|
475
|
+
|
|
476
|
+
checks = Flapjack::Data::Check.find_by_ids(*check_ids)
|
|
477
|
+
|
|
478
|
+
action = Proc.new {
|
|
479
|
+
Flapjack::Data::Event.test_notifications(@config['processor_queue'] || 'events',
|
|
480
|
+
checks, :summary => summary)
|
|
558
481
|
}
|
|
559
|
-
|
|
560
|
-
|
|
482
|
+
"Testing notifications for check#{(checks.size > 1) ? 's' : ''} #{descriptor}"
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
when /^(.*)/
|
|
486
|
+
words = Regexp.last_match(1)
|
|
487
|
+
msg = "what do you mean, '#{words}'? Type 'help' for a list of acceptable commands."
|
|
488
|
+
|
|
561
489
|
end
|
|
562
|
-
|
|
563
|
-
|
|
490
|
+
|
|
491
|
+
rescue => e
|
|
492
|
+
Flapjack.logger.error { "Exception when interpreting command '#{command}' - #{e.class}, #{e.message}" }
|
|
493
|
+
Flapjack.logger.debug { e.backtrace.join("\n") }
|
|
494
|
+
msg = "Oops, something went wrong processing that command (#{e.class}, #{e.message})"
|
|
564
495
|
end
|
|
565
|
-
end
|
|
566
496
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
number_found = entity_names.length
|
|
574
|
-
case
|
|
575
|
-
when number_found == 0
|
|
576
|
-
msg = "found no entities matching /#{entity_pattern}/"
|
|
577
|
-
when number_found >= 1
|
|
578
|
-
entities = entity_names.map {|name|
|
|
579
|
-
Flapjack::Data::Entity.find_by_name(name, :redis => @redis)
|
|
580
|
-
}.compact.inject({}) {|memo, entity|
|
|
581
|
-
memo[entity.name] = entity.check_list.map {|check_name|
|
|
582
|
-
if check_name =~ /#{check_pattern}/
|
|
583
|
-
ec = Flapjack::Data::EntityCheck.for_entity(entity, check_name, :redis => @redis)
|
|
584
|
-
"#{check_name}: #{ec.state}"
|
|
585
|
-
end
|
|
586
|
-
}.compact
|
|
587
|
-
memo
|
|
588
|
-
}
|
|
589
|
-
if entities.delete_if {|k,v| v.empty? }.length >= 1
|
|
590
|
-
msg = entities.inject("Status list:\n") {|memo,kv|
|
|
591
|
-
kv[1].each {|e| memo << "#{kv[0]}:#{e}\n" ; memo }
|
|
592
|
-
memo
|
|
593
|
-
}
|
|
594
|
-
else
|
|
595
|
-
msg = "found no matching checks"
|
|
596
|
-
end
|
|
497
|
+
@bot ||= @siblings && @siblings.detect {|sib| sib.respond_to?(:announce) }
|
|
498
|
+
|
|
499
|
+
if @bot && (room || nick)
|
|
500
|
+
if room
|
|
501
|
+
Flapjack.logger.info "sending to room #{room}: #{msg}"
|
|
502
|
+
@bot.announce(room, msg)
|
|
597
503
|
else
|
|
598
|
-
|
|
504
|
+
Flapjack.logger.info "sending to user #{nick}: #{msg}"
|
|
505
|
+
@bot.say(nick, msg)
|
|
599
506
|
end
|
|
600
507
|
else
|
|
601
|
-
|
|
508
|
+
Flapjack.logger.warn "jabber bot not running, won't send #{msg} to #{room || nick}"
|
|
602
509
|
end
|
|
603
510
|
|
|
604
|
-
|
|
605
|
-
words = $1
|
|
606
|
-
msg = "what do you mean, '#{words}'? Type 'help' for a list of acceptable commands."
|
|
607
|
-
|
|
511
|
+
action.call if action
|
|
608
512
|
end
|
|
609
513
|
|
|
610
|
-
{:msg => msg, :action => action}
|
|
611
514
|
end
|
|
612
515
|
|
|
613
|
-
|
|
614
|
-
return if @should_quit
|
|
615
|
-
@logger.debug("groupchat message received: #{stanza.inspect}")
|
|
516
|
+
class Bot
|
|
616
517
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
end
|
|
518
|
+
attr_accessor :siblings
|
|
519
|
+
|
|
520
|
+
def initialize(opts = {})
|
|
521
|
+
@lock = opts[:lock]
|
|
522
|
+
@stop_cond = opts[:stop_condition]
|
|
523
|
+
@config = opts[:config]
|
|
524
|
+
@boot_time = opts[:boot_time]
|
|
625
525
|
|
|
626
|
-
|
|
526
|
+
@say_buffer = []
|
|
527
|
+
@announce_buffer = []
|
|
528
|
+
@hostname = Socket.gethostname
|
|
627
529
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
@logger.debug("Exception when interpreting command '#{the_command}' - #{e.class}, #{e.message}")
|
|
634
|
-
msg = "Oops, something went wrong processing that command (#{e.class}, #{e.message})"
|
|
530
|
+
@alias = @config['alias'] || 'flapjack'
|
|
531
|
+
@identifiers = ((@config['identifiers'] || []) + [@alias]).uniq
|
|
532
|
+
|
|
533
|
+
@state_buffer = []
|
|
534
|
+
@should_quit = false
|
|
635
535
|
end
|
|
636
536
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
action.call if action
|
|
537
|
+
def alias
|
|
538
|
+
ret = nil
|
|
539
|
+
@lock.synchronize do
|
|
540
|
+
ret = @alias
|
|
642
541
|
end
|
|
542
|
+
ret
|
|
643
543
|
end
|
|
644
|
-
end
|
|
645
|
-
|
|
646
|
-
def on_chat(stanza)
|
|
647
|
-
return if @should_quit
|
|
648
|
-
@logger.debug("chat message received: #{stanza.inspect}")
|
|
649
544
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
545
|
+
def identifiers
|
|
546
|
+
ret = nil
|
|
547
|
+
@lock.synchronize do
|
|
548
|
+
ret = @identifiers
|
|
549
|
+
end
|
|
550
|
+
ret
|
|
654
551
|
end
|
|
655
552
|
|
|
656
|
-
|
|
553
|
+
def start
|
|
554
|
+
Flapjack.logger.debug("I will respond to the following identifiers: #{@identifiers.join(', ')}")
|
|
657
555
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
msg = results[:msg]
|
|
661
|
-
action = results[:action]
|
|
662
|
-
rescue => e
|
|
663
|
-
@logger.error("Exception when interpreting command '#{command}' - #{e.class}, #{e.message}")
|
|
664
|
-
msg = "Oops, something went wrong processing that command (#{e.class}, #{e.message})"
|
|
665
|
-
end
|
|
556
|
+
@lock.synchronize do
|
|
557
|
+
interpreter = self.siblings ? self.siblings.detect {|sib| sib.respond_to?(:interpret)} : nil
|
|
666
558
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
@logger.info("Sending to #{stanza.from.stripped}: #{msg}")
|
|
670
|
-
say(stanza.from.stripped, msg, :chat)
|
|
671
|
-
action.call if action
|
|
672
|
-
end
|
|
673
|
-
end
|
|
674
|
-
end
|
|
559
|
+
Flapjack.logger.info("starting")
|
|
560
|
+
Flapjack.logger.debug("new jabber pikelet with the following options: #{@config.inspect}")
|
|
675
561
|
|
|
676
|
-
|
|
677
|
-
attempt = 0
|
|
678
|
-
delay = 2
|
|
679
|
-
begin
|
|
680
|
-
attempt += 1
|
|
681
|
-
delay = 10 if attempt > 10
|
|
682
|
-
delay = 60 if attempt > 60
|
|
683
|
-
EventMachine::Synchrony.sleep(delay || 3) if attempt > 1
|
|
684
|
-
@logger.debug("attempting connection to the jabber server")
|
|
685
|
-
connect # Blather::Client.connect
|
|
686
|
-
rescue StandardError => detail
|
|
687
|
-
@logger.error("unable to connect to the jabber server (attempt #{attempt}), retrying in #{delay} seconds ...")
|
|
688
|
-
@logger.error("detail: #{detail.message}")
|
|
689
|
-
@logger.debug(detail.backtrace.join("\n"))
|
|
690
|
-
retry unless @should_quit
|
|
691
|
-
end
|
|
692
|
-
end
|
|
562
|
+
# ::Jabber::debug = true
|
|
693
563
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
@logger.warn("jabbers disconnected! reconnecting after a short deley ...")
|
|
699
|
-
EventMachine::Synchrony.sleep(5)
|
|
700
|
-
connect_with_retry
|
|
701
|
-
true
|
|
702
|
-
end
|
|
564
|
+
jabber_id = @config['jabberid'] || 'flapjack'
|
|
565
|
+
jabber_id += '/' + @hostname unless jabber_id.include?('/')
|
|
566
|
+
flapjack_jid = ::Jabber::JID.new(jabber_id)
|
|
567
|
+
client = ::Jabber::Client.new(flapjack_jid)
|
|
703
568
|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
569
|
+
client.on_exception do |exc, stream, loc|
|
|
570
|
+
leave_and_rejoin = nil
|
|
571
|
+
|
|
572
|
+
@lock.synchronize do
|
|
573
|
+
|
|
574
|
+
# called with a nil exception on disconnect for some reason
|
|
575
|
+
if exc
|
|
576
|
+
Flapjack.logger.error exc.class.name
|
|
577
|
+
Flapjack.logger.error ":#{loc.to_s}"
|
|
578
|
+
Flapjack.logger.error exc.message
|
|
579
|
+
Flapjack.logger.error exc.backtrace.join("\n")
|
|
580
|
+
end
|
|
714
581
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
# EM.sleep(1) in a loop, I suppose
|
|
722
|
-
ki = 0
|
|
723
|
-
keepalive_timer = EventMachine::Synchrony.add_periodic_timer(1) do
|
|
724
|
-
ki += 1
|
|
725
|
-
if ki == 60
|
|
726
|
-
ki = 0
|
|
727
|
-
@logger.debug("calling keepalive on the jabber connection")
|
|
728
|
-
if connected?
|
|
729
|
-
EventMachine::Synchrony.next_tick do
|
|
730
|
-
write(' ')
|
|
582
|
+
leave_and_rejoin = @joined && !@should_quit
|
|
583
|
+
|
|
584
|
+
if leave_and_rejoin
|
|
585
|
+
@state_buffer << 'leave'
|
|
586
|
+
@stop_cond.signal
|
|
587
|
+
end
|
|
731
588
|
end
|
|
732
|
-
end
|
|
733
|
-
end
|
|
734
|
-
end
|
|
735
589
|
|
|
736
|
-
|
|
737
|
-
|
|
590
|
+
if leave_and_rejoin
|
|
591
|
+
sleep 3
|
|
592
|
+
@lock.synchronize do
|
|
593
|
+
unless @should_quit
|
|
594
|
+
@state_buffer << 'rejoin'
|
|
595
|
+
@stop_cond.signal
|
|
596
|
+
end
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
end
|
|
738
600
|
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
601
|
+
check_xml = Proc.new do |data|
|
|
602
|
+
if data.nil?
|
|
603
|
+
nil
|
|
604
|
+
else
|
|
605
|
+
Flapjack.logger.debug "xml_data: #{data}"
|
|
606
|
+
text = ''
|
|
607
|
+
begin
|
|
608
|
+
enc_name = Encoding.default_external.name
|
|
609
|
+
REXML::Document.new("<?xml version=\"1.0\" encoding=\"#{enc_name}\"?>" + data).
|
|
610
|
+
each_element_with_text do |elem|
|
|
611
|
+
|
|
612
|
+
text += elem.texts.join(" ")
|
|
613
|
+
end
|
|
614
|
+
text = data if text.empty? && !data.empty?
|
|
615
|
+
rescue REXML::ParseException
|
|
616
|
+
# invalid XML, so we'll just clear everything inside angled brackets
|
|
617
|
+
text = data.gsub(/<[^>]+>/, '').strip
|
|
618
|
+
end
|
|
619
|
+
text
|
|
620
|
+
end
|
|
621
|
+
end
|
|
742
622
|
|
|
743
|
-
|
|
623
|
+
client.add_message_callback do |m|
|
|
624
|
+
unless (m.type != :chat) || m.body.nil? || m.body.strip.empty?
|
|
625
|
+
Flapjack.logger.debug "received message #{m.inspect}"
|
|
626
|
+
nick = m.from
|
|
627
|
+
time = nil
|
|
628
|
+
m.each_element('x') { |x|
|
|
629
|
+
if x.kind_of?(::Jabber::Delay::XDelay)
|
|
630
|
+
time = x.stamp
|
|
631
|
+
end
|
|
632
|
+
}
|
|
744
633
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
EM::Synchrony.sleep(1)
|
|
751
|
-
next
|
|
752
|
-
end
|
|
634
|
+
unless interpreter.nil?
|
|
635
|
+
interpreter.receive_message(nil, nick, time, check_xml.call(m.body))
|
|
636
|
+
end
|
|
637
|
+
end
|
|
638
|
+
end
|
|
753
639
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
640
|
+
muc_clients = @config['rooms'].inject({}) do |memo, room|
|
|
641
|
+
muc_client = ::Jabber::MUC::SimpleMUCClient.new(client)
|
|
642
|
+
muc_client.on_message do |time, nick, text|
|
|
643
|
+
Flapjack.logger.debug("message #{text} -- #{time} -- #{nick}")
|
|
644
|
+
next if nick == jabber_id
|
|
645
|
+
identifier = identifiers.detect {|id| /^(?:@#{id}|#{id}:)\s*(.*)/m === check_xml.call(text) }
|
|
646
|
+
|
|
647
|
+
unless identifier.nil?
|
|
648
|
+
the_command = Regexp.last_match(1)
|
|
649
|
+
Flapjack.logger.debug("matched identifier: #{identifier}, command: #{the_command.inspect}")
|
|
650
|
+
if interpreter
|
|
651
|
+
interpreter.receive_message(room, nick, time, the_command)
|
|
652
|
+
end
|
|
653
|
+
end
|
|
654
|
+
end
|
|
759
655
|
|
|
760
|
-
|
|
656
|
+
memo[room] = muc_client
|
|
657
|
+
memo
|
|
658
|
+
end
|
|
761
659
|
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
660
|
+
attempts_allowed = 3
|
|
661
|
+
attempts_remaining = attempts_allowed
|
|
662
|
+
@joined = false
|
|
663
|
+
|
|
664
|
+
loop do
|
|
665
|
+
|
|
666
|
+
if @joined
|
|
667
|
+
# block this thread until signalled to quit / leave / rejoin
|
|
668
|
+
@stop_cond.wait_until { @should_quit || !@state_buffer.empty? }
|
|
669
|
+
elsif attempts_remaining > 0
|
|
670
|
+
unless @should_quit || (attempts_remaining == attempts_allowed)
|
|
671
|
+
# The only thing that should be interrupting this wait is
|
|
672
|
+
# a pikelet.stop, which would set @should_quit to true;
|
|
673
|
+
# thus we shouldn't see multiple connection attempts happening
|
|
674
|
+
# too quickly.
|
|
675
|
+
@stop_cond.wait(3)
|
|
768
676
|
end
|
|
677
|
+
unless @should_quit # may have changed during previous wait
|
|
678
|
+
begin
|
|
679
|
+
attempts_remaining -= 1
|
|
680
|
+
_join(client, muc_clients)
|
|
681
|
+
@joined = true
|
|
682
|
+
rescue Errno::ECONNREFUSED, ::Jabber::JabberError => je
|
|
683
|
+
report_error("Couldn't join Jabber server #{@hostname}", je)
|
|
684
|
+
end
|
|
685
|
+
end
|
|
686
|
+
else
|
|
687
|
+
# TODO should we quit Flapjack entirely?
|
|
688
|
+
Flapjack.logger.error "stopping jabber bot, couldn't connect in #{attempts_allowed} attempts"
|
|
689
|
+
@should_quit = true
|
|
769
690
|
end
|
|
770
|
-
|
|
691
|
+
|
|
692
|
+
break if @should_quit
|
|
693
|
+
handle_state_change(client, muc_clients) unless @state_buffer.empty?
|
|
771
694
|
end
|
|
772
695
|
|
|
773
|
-
|
|
696
|
+
# main loop has finished, stop() must have been called -- disconnect
|
|
697
|
+
_leave(client, muc_clients) if client.is_connected?
|
|
698
|
+
end
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
def announce(room, msg)
|
|
702
|
+
@lock.synchronize do
|
|
703
|
+
@announce_buffer += [{:room => room, :msg => msg}]
|
|
704
|
+
@state_buffer << 'announce'
|
|
705
|
+
@stop_cond.signal
|
|
706
|
+
end
|
|
707
|
+
end
|
|
774
708
|
|
|
775
|
-
|
|
776
|
-
|
|
709
|
+
def say(nick, msg)
|
|
710
|
+
@lock.synchronize do
|
|
711
|
+
@say_buffer += [{:nick => nick, :msg => msg}]
|
|
712
|
+
@state_buffer << 'say'
|
|
713
|
+
@stop_cond.signal
|
|
714
|
+
end
|
|
715
|
+
end
|
|
777
716
|
|
|
778
|
-
|
|
779
|
-
|
|
717
|
+
def handle_state_change(client, muc_clients)
|
|
718
|
+
connected = client.is_connected?
|
|
719
|
+
Flapjack.logger.debug "connected? #{connected}"
|
|
720
|
+
|
|
721
|
+
while state = @state_buffer.pop
|
|
722
|
+
Flapjack.logger.debug "state change #{state}"
|
|
723
|
+
case state
|
|
724
|
+
when 'announce'
|
|
725
|
+
_announce(muc_clients) if connected
|
|
726
|
+
when 'say'
|
|
727
|
+
_say(client) if connected
|
|
728
|
+
when 'leave'
|
|
729
|
+
connected ? _leave(client, muc_clients) : _deactivate(muc_clients)
|
|
730
|
+
when 'rejoin'
|
|
731
|
+
_join(client, muc_clients, :rejoin => true) unless connected
|
|
780
732
|
else
|
|
781
|
-
"
|
|
733
|
+
Flapjack.logger.warn "unknown state change #{state}"
|
|
782
734
|
end
|
|
735
|
+
end
|
|
736
|
+
end
|
|
783
737
|
|
|
784
|
-
|
|
738
|
+
def stop_type
|
|
739
|
+
:signal
|
|
740
|
+
end
|
|
785
741
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
742
|
+
def report_error(error_msg, je)
|
|
743
|
+
Flapjack.logger.error error_msg
|
|
744
|
+
message = je.respond_to?(:message) ? je.message : '-'
|
|
745
|
+
Flapjack.logger.error "#{je.class.name} #{message}"
|
|
746
|
+
# if je.respond_to?(:backtrace) && trace = je.backtrace
|
|
747
|
+
# Flapjack.logger.error trace.join("\n")
|
|
748
|
+
# end
|
|
749
|
+
end
|
|
789
750
|
|
|
790
|
-
|
|
791
|
-
|
|
751
|
+
def _join(client, muc_clients, opts = {})
|
|
752
|
+
client.connect
|
|
753
|
+
client.auth(@config['password'])
|
|
754
|
+
client.send(::Jabber::Presence.new)
|
|
755
|
+
muc_clients.each_pair do |room, muc_client|
|
|
756
|
+
attempts_allowed = 3
|
|
757
|
+
attempts_remaining = attempts_allowed
|
|
758
|
+
joined = nil
|
|
759
|
+
while !joined && (attempts_remaining > 0)
|
|
760
|
+
@lock.synchronize do
|
|
761
|
+
unless @should_quit || (attempts_remaining == attempts_allowed)
|
|
762
|
+
# The only thing that should be interrupting this wait is
|
|
763
|
+
# a pikelet.stop, which would set @should_quit to true;
|
|
764
|
+
# thus we shouldn't see multiple connection attempts happening
|
|
765
|
+
# too quickly.
|
|
766
|
+
@stop_cond.wait(3)
|
|
767
|
+
end
|
|
768
|
+
end
|
|
792
769
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
770
|
+
# may have changed during previous wait
|
|
771
|
+
sq = nil
|
|
772
|
+
@lock.synchronize do
|
|
773
|
+
sq = @should_quit
|
|
774
|
+
end
|
|
775
|
+
|
|
776
|
+
unless sq
|
|
777
|
+
attempts_remaining -= 1
|
|
778
|
+
begin
|
|
779
|
+
muc_client.join(room + '/' + @alias, nil, :history => false)
|
|
780
|
+
t = Time.now
|
|
781
|
+
msg = opts[:rejoin] ? "flapjack jabber gateway rejoining at #{t}, hello again!" :
|
|
782
|
+
"flapjack jabber gateway started at #{t}, hello! Try typing 'help'."
|
|
783
|
+
muc_client.say(msg) if @config['chatbot_announce']
|
|
784
|
+
joined = true
|
|
785
|
+
rescue Errno::ECONNREFUSED, ::Jabber::JabberError => muc_je
|
|
786
|
+
report_error("Couldn't join MUC room #{room}, #{attempts_remaining} attempts remaining", muc_je)
|
|
787
|
+
raise if attempts_remaining <= 0
|
|
788
|
+
joined = false
|
|
789
|
+
end
|
|
790
|
+
end
|
|
799
791
|
end
|
|
792
|
+
end
|
|
793
|
+
end
|
|
794
|
+
|
|
795
|
+
def _leave(client, muc_clients)
|
|
796
|
+
if @joined
|
|
797
|
+
muc_clients.values.each {|muc_client| muc_client.exit if muc_client.active? }
|
|
798
|
+
client.close
|
|
799
|
+
end
|
|
800
|
+
@joined = false
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
def _deactivate(muc_clients)
|
|
804
|
+
# send method has been overridden in MUCClient class
|
|
805
|
+
# without this MUC clients will still think they are active
|
|
806
|
+
muc_clients.values.each {|muc_client| muc_client.__send__(:deactivate) }
|
|
807
|
+
end
|
|
800
808
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
say(
|
|
805
|
-
|
|
809
|
+
def _announce(muc_clients)
|
|
810
|
+
@announce_buffer.each do |announce|
|
|
811
|
+
if (muc_client = muc_clients[announce[:room]]) && muc_client.active?
|
|
812
|
+
muc_client.say(announce[:msg])
|
|
813
|
+
announce[:sent] = true
|
|
806
814
|
end
|
|
815
|
+
end
|
|
816
|
+
@announce_buffer.delete_if {|announce| announce[:sent] }
|
|
817
|
+
end
|
|
807
818
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
@logger.debug "Message that could not be processed: \n" + event_json
|
|
819
|
+
def _say(client)
|
|
820
|
+
while speak = @say_buffer.pop
|
|
821
|
+
msg = ::Jabber::Message::new(speak[:nick], speak[:msg])
|
|
822
|
+
msg.type = :chat
|
|
823
|
+
client.send( msg )
|
|
814
824
|
end
|
|
815
825
|
end
|
|
816
826
|
|
|
817
|
-
keepalive_timer.cancel
|
|
818
827
|
end
|
|
819
828
|
|
|
820
829
|
end
|
|
821
|
-
|
|
822
830
|
end
|
|
823
831
|
end
|