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
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
# 'Notification' refers to the template object created when an event occurs,
|
|
4
|
-
# from which individual 'Message' objects are created, one for each
|
|
5
|
-
# contact+media recipient.
|
|
6
|
-
|
|
7
|
-
require 'flapjack/data/contact'
|
|
8
|
-
|
|
9
|
-
module Flapjack
|
|
10
|
-
module Data
|
|
11
|
-
class Message
|
|
12
|
-
|
|
13
|
-
attr_reader :medium, :address, :duration, :contact, :rollup
|
|
14
|
-
|
|
15
|
-
def self.for_contact(contact, opts = {})
|
|
16
|
-
self.new(:contact => contact,
|
|
17
|
-
:medium => opts[:medium],
|
|
18
|
-
:address => opts[:address],
|
|
19
|
-
:duration => opts[:duration],
|
|
20
|
-
:rollup => opts[:rollup])
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def id
|
|
24
|
-
return @id if @id
|
|
25
|
-
t = Time.now
|
|
26
|
-
# FIXME: consider using a UUID here
|
|
27
|
-
# this is planned to be used as part of alert history keys
|
|
28
|
-
@id = "#{self.object_id.to_i}-#{t.to_i}.#{t.tv_usec}"
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def contents
|
|
32
|
-
c = {'media' => medium,
|
|
33
|
-
'address' => address,
|
|
34
|
-
'id' => id,
|
|
35
|
-
'rollup' => rollup,
|
|
36
|
-
'contact_id' => contact.id,
|
|
37
|
-
'contact_first_name' => contact.first_name,
|
|
38
|
-
'contact_last_name' => contact.last_name}
|
|
39
|
-
c['duration'] = duration if duration
|
|
40
|
-
c
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
def initialize(opts = {})
|
|
46
|
-
@contact = opts[:contact]
|
|
47
|
-
@medium = opts[:medium]
|
|
48
|
-
@address = opts[:address]
|
|
49
|
-
@duration = opts[:duration]
|
|
50
|
-
@rollup = opts[:rollup]
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'flapjack/data/contact'
|
|
4
|
-
require 'flapjack/data/entity_check'
|
|
5
|
-
|
|
6
|
-
require 'flapjack/data/semaphore'
|
|
7
|
-
|
|
8
|
-
module Flapjack
|
|
9
|
-
module Data
|
|
10
|
-
class Migration
|
|
11
|
-
|
|
12
|
-
ENTITY_DATA_MIGRATION = 'entity_data_migration'
|
|
13
|
-
|
|
14
|
-
# copied from jsonapi/contact_methods.rb, could extract both into separate file
|
|
15
|
-
def self.obtain_semaphore(resource, description, options = {})
|
|
16
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
17
|
-
|
|
18
|
-
logger = options[:logger]
|
|
19
|
-
|
|
20
|
-
semaphore = nil
|
|
21
|
-
strikes = 0
|
|
22
|
-
begin
|
|
23
|
-
semaphore = Flapjack::Data::Semaphore.new(resource, :redis => redis, :expiry => 300)
|
|
24
|
-
rescue Flapjack::Data::Semaphore::ResourceLocked
|
|
25
|
-
strikes += 1
|
|
26
|
-
if strikes < 10
|
|
27
|
-
sleep 2
|
|
28
|
-
retry
|
|
29
|
-
end
|
|
30
|
-
sempahore = nil
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
if semaphore.nil?
|
|
34
|
-
unless logger.nil?
|
|
35
|
-
logger.fatal "Could not obtain lock for data migration (#{reason}). Ensure that " +
|
|
36
|
-
"no other flapjack processes are running that might be executing " +
|
|
37
|
-
"migrations, check logs for any exceptions, manually delete the " +
|
|
38
|
-
"'#{resource}' key from your Flapjack Redis " +
|
|
39
|
-
"database and try running Flapjack again."
|
|
40
|
-
end
|
|
41
|
-
raise "Unable to obtain semaphore #{resource}"
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
semaphore
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def self.create_entity_ids_if_required(options = {})
|
|
48
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
49
|
-
|
|
50
|
-
logger = options[:logger]
|
|
51
|
-
|
|
52
|
-
return if redis.exists('created_ids_for_old_entities_without_ids')
|
|
53
|
-
|
|
54
|
-
semaphore = obtain_semaphore(ENTITY_DATA_MIGRATION, 'entity id creation',
|
|
55
|
-
:redis => redis, :logger => logger)
|
|
56
|
-
|
|
57
|
-
begin
|
|
58
|
-
logger.warn "Ensuring all entities have ids ..." unless logger.nil?
|
|
59
|
-
|
|
60
|
-
Flapjack::Data::EntityCheck.find_current_names_by_entity(:redis => redis, :logger => logger).keys.each {|entity_name|
|
|
61
|
-
entity = Flapjack::Data::Entity.find_by_name(entity_name, :create => true, :redis => redis, :logger => logger)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
all_checks = Flapjack::Data::EntityCheck.all(:redis => redis, :logger => logger, :create_entity => true)
|
|
65
|
-
|
|
66
|
-
redis.set('created_ids_for_old_entities_without_ids', 'true')
|
|
67
|
-
logger.warn "Entity id creation complete."
|
|
68
|
-
ensure
|
|
69
|
-
semaphore.release
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def self.migrate_entity_check_data_if_required(options = {})
|
|
74
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
75
|
-
|
|
76
|
-
logger = options[:logger]
|
|
77
|
-
|
|
78
|
-
return if redis.exists('all_checks')
|
|
79
|
-
|
|
80
|
-
semaphore = obtain_semaphore(ENTITY_DATA_MIGRATION, 'entity check data',
|
|
81
|
-
:redis => redis, :logger => logger)
|
|
82
|
-
|
|
83
|
-
begin
|
|
84
|
-
check_names = redis.keys('check:*').map {|c| c.sub(/^check:/, '') } |
|
|
85
|
-
Flapjack::Data::EntityCheck.find_current_names(:redis => redis)
|
|
86
|
-
|
|
87
|
-
unless check_names.empty?
|
|
88
|
-
logger.warn "Upgrading Flapjack's entity/check Redis indexes..." unless logger.nil?
|
|
89
|
-
|
|
90
|
-
timestamp = Time.now.to_i
|
|
91
|
-
|
|
92
|
-
check_names.each do |ecn|
|
|
93
|
-
redis.zadd("all_checks", timestamp, ecn)
|
|
94
|
-
entity_name, check = ecn.split(':', 2)
|
|
95
|
-
redis.zadd("all_checks:#{entity_name}", timestamp, check)
|
|
96
|
-
# not deleting the check hashes, they store useful data
|
|
97
|
-
end
|
|
98
|
-
logger.warn "Checks indexed." unless logger.nil?
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
entity_name_keys = redis.keys("entity_id:*")
|
|
102
|
-
unless entity_name_keys.empty?
|
|
103
|
-
ids = redis.mget(*entity_name_keys)
|
|
104
|
-
|
|
105
|
-
entity_name_keys.each do |enk|
|
|
106
|
-
enk =~ /^entity_id:(.+)$/; entity_name = $1; entity_id = ids.shift
|
|
107
|
-
|
|
108
|
-
redis.hset('all_entity_names_by_id', entity_id, entity_name)
|
|
109
|
-
redis.hset('all_entity_ids_by_name', entity_name, entity_id)
|
|
110
|
-
|
|
111
|
-
redis.del(enk)
|
|
112
|
-
redis.del("entity:#{entity_id}")
|
|
113
|
-
end
|
|
114
|
-
logger.warn "Entities indexed." unless logger.nil?
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
logger.warn "Indexing complete." unless logger.nil? || (check_names.empty? && entity_name_keys.empty?)
|
|
118
|
-
ensure
|
|
119
|
-
semaphore.release
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def self.clear_orphaned_entity_ids(options = {})
|
|
124
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
125
|
-
|
|
126
|
-
logger = options[:logger]
|
|
127
|
-
|
|
128
|
-
semaphore = obtain_semaphore(ENTITY_DATA_MIGRATION,
|
|
129
|
-
'orphaned entity ids', :redis => redis, :logger => logger)
|
|
130
|
-
|
|
131
|
-
begin
|
|
132
|
-
logger.info "Checking for orphaned entity ids..." unless logger.nil?
|
|
133
|
-
|
|
134
|
-
valid_entity_data = redis.hgetall('all_entity_ids_by_name')
|
|
135
|
-
|
|
136
|
-
missing_ids = redis.hgetall('all_entity_names_by_id').reject {|e_id, e_name|
|
|
137
|
-
valid_entity_data[e_name] == e_id
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
unless missing_ids.empty?
|
|
141
|
-
logger.info "Clearing ids (#{missing_ids.inspect})" unless logger.nil?
|
|
142
|
-
redis.hdel('all_entity_names_by_id', missing_ids.keys)
|
|
143
|
-
end
|
|
144
|
-
ensure
|
|
145
|
-
semaphore.release
|
|
146
|
-
logger.info "Finished checking for orphaned entity ids." unless logger.nil?
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def self.refresh_archive_index(options = {})
|
|
151
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
152
|
-
archive_keys = redis.keys('events_archive:*')
|
|
153
|
-
if archive_keys.empty?
|
|
154
|
-
redis.del('known_events_archive_keys')
|
|
155
|
-
return
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
grouped_keys = archive_keys.group_by do |ak|
|
|
159
|
-
(redis.llen(ak) > 0) ? 'add' : 'remove'
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
{'remove' => :srem, 'add' => :sadd}.each_pair do |k, cmd|
|
|
163
|
-
next unless grouped_keys.has_key?(k) && !grouped_keys[k].empty?
|
|
164
|
-
redis.send(cmd, 'known_events_archive_keys', grouped_keys[k])
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def self.purge_expired_archive_index(options = {})
|
|
169
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
170
|
-
return unless redis.exists('known_events_archive_keys')
|
|
171
|
-
|
|
172
|
-
redis.smembers('known_events_archive_keys').each do |ak|
|
|
173
|
-
redis.srem('known_events_archive_keys', ak) unless redis.exists(ak)
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def self.correct_notification_rule_contact_linkages(options = {})
|
|
178
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
179
|
-
|
|
180
|
-
logger = options[:logger]
|
|
181
|
-
|
|
182
|
-
return if redis.exists('corrected_notification_rule_contact_linkages')
|
|
183
|
-
|
|
184
|
-
invalid_notification_rule_keys = redis.keys("notification_rule:*").select {|k|
|
|
185
|
-
contact_id = redis.hget(k, 'contact_id')
|
|
186
|
-
contact_id.nil? || contact_id.empty?
|
|
187
|
-
}.collect {|nrk| nrk.sub(/^notification_rule:/, '') }
|
|
188
|
-
|
|
189
|
-
unless invalid_notification_rule_keys.empty?
|
|
190
|
-
Flapjack::Data::Contact.all(:redis => redis).each do |contact|
|
|
191
|
-
correctable = contact.notification_rule_ids & invalid_notification_rule_keys
|
|
192
|
-
next if correctable.empty?
|
|
193
|
-
correctable.each {|ck| redis.hset("notification_rule:#{ck}", 'contact_id', contact.id) }
|
|
194
|
-
logger.warn "Set contact #{contact.id} for rules #{correctable.join(', ')}" unless logger.nil?
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
redis.set('corrected_notification_rule_contact_linkages', 'true')
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
def self.validate_scheduled_maintenance_periods(options = {})
|
|
202
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
203
|
-
|
|
204
|
-
logger = options[:logger]
|
|
205
|
-
|
|
206
|
-
return if redis.exists('validated_scheduled_maintenance_periods')
|
|
207
|
-
|
|
208
|
-
Flapjack::Data::EntityCheck.all(:redis => redis).compact.select {|ec|
|
|
209
|
-
ec.in_scheduled_maintenance?
|
|
210
|
-
}.each do |check|
|
|
211
|
-
check.update_current_scheduled_maintenance(:revalidate => true)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
logger.warn "Validated scheduled maintenance period expiry" unless logger.nil?
|
|
215
|
-
redis.set('validated_scheduled_maintenance_periods', 'true')
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
def self.correct_rollup_including_disabled_checks(options = {})
|
|
219
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
220
|
-
logger = options[:logger]
|
|
221
|
-
return if redis.exists('corrected_rollup_including_disabled_checks')
|
|
222
|
-
|
|
223
|
-
Flapjack::Data::Contact.all(:redis => redis).each do |contact|
|
|
224
|
-
contact.media_list.each do |medium|
|
|
225
|
-
contact.clean_alerting_checks_for_media(medium)
|
|
226
|
-
end
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
logger.warn "Corrected rollup to no longer include disabled checks" unless logger.nil?
|
|
230
|
-
redis.set('corrected_rollup_including_disabled_checks', 'true')
|
|
231
|
-
end
|
|
232
|
-
end
|
|
233
|
-
end
|
|
234
|
-
end
|
|
@@ -1,425 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'active_support/time'
|
|
4
|
-
require 'ice_cube'
|
|
5
|
-
require 'flapjack/utility'
|
|
6
|
-
|
|
7
|
-
module Flapjack
|
|
8
|
-
module Data
|
|
9
|
-
class NotificationRule
|
|
10
|
-
|
|
11
|
-
extend Flapjack::Utility
|
|
12
|
-
|
|
13
|
-
attr_accessor :id, :contact_id, :entities, :regex_entities, :tags, :regex_tags,
|
|
14
|
-
:time_restrictions, :unknown_media, :warning_media, :critical_media,
|
|
15
|
-
:unknown_blackhole, :warning_blackhole, :critical_blackhole
|
|
16
|
-
|
|
17
|
-
def self.all(options = {})
|
|
18
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
19
|
-
redis.keys("contact_notification_rules:*").inject([]) do |memo, contact_key|
|
|
20
|
-
redis.smembers(contact_key).each do |rule_id|
|
|
21
|
-
ret = self.find_by_id(rule_id, :redis => redis)
|
|
22
|
-
memo << ret unless ret.nil?
|
|
23
|
-
end
|
|
24
|
-
memo
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def self.exists_with_id?(rule_id, options = {})
|
|
29
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
30
|
-
raise "No id value passed" unless not (rule_id.nil? || rule_id == '')
|
|
31
|
-
redis.exists("notification_rule:#{rule_id}")
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def self.find_by_id(rule_id, options = {})
|
|
35
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
36
|
-
raise "No id value passed" unless not (rule_id.nil? || rule_id == '')
|
|
37
|
-
|
|
38
|
-
# sanity check
|
|
39
|
-
return unless redis.exists("notification_rule:#{rule_id}")
|
|
40
|
-
|
|
41
|
-
self.new({:id => rule_id.to_s}, {:redis => redis})
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def self.find_by_ids(rule_ids, options = {})
|
|
45
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
46
|
-
|
|
47
|
-
rule_ids.map do |id|
|
|
48
|
-
self.find_by_id(id, options)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# replacing save! etc
|
|
53
|
-
def self.add(rule_data, options = {})
|
|
54
|
-
raise "Redis connection not set" unless redis = options[:redis]
|
|
55
|
-
|
|
56
|
-
if rule_data[:id] && self.find_by_id(rule_data[:id], :redis => redis)
|
|
57
|
-
errors = ["a notification rule already exists with id '#{rule_data[:id]}'"]
|
|
58
|
-
return errors
|
|
59
|
-
end
|
|
60
|
-
rule_id = rule_data[:id] || SecureRandom.uuid
|
|
61
|
-
|
|
62
|
-
errors = self.add_or_update(rule_data.merge(:id => rule_id), :redis => redis, :logger => options[:logger])
|
|
63
|
-
return errors unless errors.nil? || errors.empty?
|
|
64
|
-
|
|
65
|
-
self.find_by_id(rule_id, :redis => redis)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def update(update_data, opts = {})
|
|
69
|
-
[:entities, :regex_entities, :tags, :regex_tags,
|
|
70
|
-
:time_restrictions, :unknown_media, :warning_media, :critical_media,
|
|
71
|
-
:unknown_blackhole, :warning_blackhole, :critical_blackhole].each do |update_key|
|
|
72
|
-
|
|
73
|
-
next if update_data.has_key?(update_key)
|
|
74
|
-
update_data[update_key] = self.send(update_key)
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
update_data.update(:id => @id, :contact_id => @contact_id)
|
|
78
|
-
errors = self.class.add_or_update(update_data, :redis => @redis, :logger => opts[:logger])
|
|
79
|
-
return errors unless errors.nil? || errors.empty?
|
|
80
|
-
|
|
81
|
-
refresh
|
|
82
|
-
nil
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# NB: ice_cube doesn't have much rule data validation, and has
|
|
86
|
-
# problems with infinite loops if the data can't logically match; see
|
|
87
|
-
# https://github.com/seejohnrun/ice_cube/issues/127 &
|
|
88
|
-
# https://github.com/seejohnrun/ice_cube/issues/137
|
|
89
|
-
# We may want to consider some sort of timeout-based check around
|
|
90
|
-
# anything that could fall into that.
|
|
91
|
-
#
|
|
92
|
-
# We don't want to replicate IceCube's from_hash behaviour here,
|
|
93
|
-
# but we do need to apply some sanity checking on the passed data.
|
|
94
|
-
def self.time_restriction_to_icecube_schedule(tr, timezone, opts = {})
|
|
95
|
-
return if tr.nil? || !tr.is_a?(Hash) ||
|
|
96
|
-
timezone.nil? || !timezone.is_a?(ActiveSupport::TimeZone)
|
|
97
|
-
prepared_restrictions = prepare_time_restriction(tr, timezone)
|
|
98
|
-
return if prepared_restrictions.nil?
|
|
99
|
-
IceCube::Schedule.from_hash(prepared_restrictions)
|
|
100
|
-
rescue ArgumentError => ae
|
|
101
|
-
if logger = opts[:logger]
|
|
102
|
-
logger.error "Couldn't parse rule data #{e.class}: #{e.message}"
|
|
103
|
-
logger.error prepared_restrictions.inspect
|
|
104
|
-
logger.error e.backtrace.join("\n")
|
|
105
|
-
end
|
|
106
|
-
nil
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def to_jsonapi(opts = {})
|
|
110
|
-
json_data = self.class.hashify(:id, :tags, :regex_tags, :entities, :regex_entities,
|
|
111
|
-
:time_restrictions, :unknown_media, :warning_media, :critical_media,
|
|
112
|
-
:unknown_blackhole, :warning_blackhole, :critical_blackhole) {|k|
|
|
113
|
-
case k
|
|
114
|
-
when :tags, :regex_tags
|
|
115
|
-
[k.to_s, self.send(k).to_a.sort]
|
|
116
|
-
else
|
|
117
|
-
[k.to_s, self.send(k)]
|
|
118
|
-
end
|
|
119
|
-
}.merge('links' => {'contacts' => [self.contact_id]})
|
|
120
|
-
|
|
121
|
-
Flapjack.dump_json(json_data)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# If the rule has any entities, then one of them must match the event's entity
|
|
125
|
-
def match_entity?(event_id)
|
|
126
|
-
return true unless @entities && @entities.length > 0
|
|
127
|
-
event_entity = event_id.split(':').first
|
|
128
|
-
@entities.include?(event_entity)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# If the rule has any regex_entities, then all of them must match the
|
|
132
|
-
# event's entity
|
|
133
|
-
def match_regex_entities?(event_id)
|
|
134
|
-
return true unless @regex_entities && @regex_entities.length > 0
|
|
135
|
-
event_entity = event_id.split(':').first
|
|
136
|
-
matches = 0
|
|
137
|
-
@regex_entities.each do |regex_entity|
|
|
138
|
-
matches += 1 if /#{regex_entity}/ === event_entity
|
|
139
|
-
end
|
|
140
|
-
matches >= @regex_entities.length
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# If the rule has any tags, then they must all be present in the
|
|
144
|
-
# event's tags
|
|
145
|
-
def match_tags?(event_tags)
|
|
146
|
-
return true unless @tags && @tags.length > 0
|
|
147
|
-
@tags.subset?(event_tags)
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# If the rule has any regex_tags, then they must all match at least
|
|
151
|
-
# one of the event's tags
|
|
152
|
-
def match_regex_tags?(event_tags)
|
|
153
|
-
return true unless @regex_tags && @regex_tags.length > 0
|
|
154
|
-
matches = 0
|
|
155
|
-
@regex_tags.each do |regex_tag|
|
|
156
|
-
matches += 1 if event_tags.any? { |event_tag| /#{regex_tag}/ === event_tag }
|
|
157
|
-
end
|
|
158
|
-
matches >= @regex_tags.length
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def blackhole?(severity)
|
|
162
|
-
('unknown'.eql?(severity.downcase) && @unknown_blackhole) ||
|
|
163
|
-
('warning'.eql?(severity.downcase) && @warning_blackhole) ||
|
|
164
|
-
('critical'.eql?(severity.downcase) && @critical_blackhole)
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def media_for_severity(severity)
|
|
168
|
-
case severity
|
|
169
|
-
when 'unknown'
|
|
170
|
-
@unknown_media
|
|
171
|
-
when 'warning'
|
|
172
|
-
@warning_media
|
|
173
|
-
when 'critical'
|
|
174
|
-
@critical_media
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def is_specific?
|
|
179
|
-
(!@entities.nil? && !@entities.empty?) ||
|
|
180
|
-
(!@regex_entities.nil? && !@regex_entities.empty?) ||
|
|
181
|
-
(!@tags.nil? && !@tags.empty?) ||
|
|
182
|
-
(!@regex_tags.nil? && !@regex_tags.empty?)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
private
|
|
186
|
-
|
|
187
|
-
def initialize(rule_data, opts = {})
|
|
188
|
-
@redis ||= opts[:redis]
|
|
189
|
-
raise "a redis connection must be supplied" unless @redis
|
|
190
|
-
@logger = opts[:logger]
|
|
191
|
-
@id = rule_data[:id]
|
|
192
|
-
refresh
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
def self.prevalidate_data(rule_data, options = {})
|
|
196
|
-
errors = self.validate_data(preen(rule_data), options.merge(:id_not_required => true))
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def self.preen(rule_data)
|
|
200
|
-
# make some assumptions about the incoming data
|
|
201
|
-
rule_data[:unknown_blackhole] = rule_data[:unknown_blackhole] || false
|
|
202
|
-
rule_data[:warning_blackhole] = rule_data[:warning_blackhole] || false
|
|
203
|
-
rule_data[:critical_blackhole] = rule_data[:critical_blackhole] || false
|
|
204
|
-
if rule_data[:tags].is_a?(Array)
|
|
205
|
-
rule_data[:tags] = Set.new(rule_data[:tags])
|
|
206
|
-
end
|
|
207
|
-
if rule_data[:regex_tags].is_a?(Array)
|
|
208
|
-
rule_data[:regex_tags] = Set.new(rule_data[:regex_tags])
|
|
209
|
-
end
|
|
210
|
-
rule_data
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
def self.add_or_update(rule_data, options = {})
|
|
214
|
-
redis = options[:redis]
|
|
215
|
-
raise "a redis connection must be supplied" unless redis
|
|
216
|
-
logger = options[:logger]
|
|
217
|
-
|
|
218
|
-
rule_data = preen(rule_data)
|
|
219
|
-
errors = self.validate_data(rule_data, options)
|
|
220
|
-
return errors unless errors.nil? || errors.empty?
|
|
221
|
-
|
|
222
|
-
# whitelisting fields, rather than passing through submitted data directly
|
|
223
|
-
tag_data = rule_data[:tags].is_a?(Set) ? rule_data[:tags].to_a : nil
|
|
224
|
-
regex_tag_data = rule_data[:regex_tags].is_a?(Set) ? rule_data[:regex_tags].to_a : nil
|
|
225
|
-
|
|
226
|
-
json_rule_data = {
|
|
227
|
-
:id => rule_data[:id].to_s,
|
|
228
|
-
:contact_id => rule_data[:contact_id].to_s,
|
|
229
|
-
:entities => Flapjack.dump_json(rule_data[:entities]),
|
|
230
|
-
:regex_entities => Flapjack.dump_json(rule_data[:regex_entities]),
|
|
231
|
-
:tags => Flapjack.dump_json(tag_data),
|
|
232
|
-
:regex_tags => Flapjack.dump_json(regex_tag_data),
|
|
233
|
-
:time_restrictions => Flapjack.dump_json(rule_data[:time_restrictions]),
|
|
234
|
-
:unknown_media => Flapjack.dump_json(rule_data[:unknown_media]),
|
|
235
|
-
:warning_media => Flapjack.dump_json(rule_data[:warning_media]),
|
|
236
|
-
:critical_media => Flapjack.dump_json(rule_data[:critical_media]),
|
|
237
|
-
:unknown_blackhole => rule_data[:unknown_blackhole],
|
|
238
|
-
:warning_blackhole => rule_data[:warning_blackhole],
|
|
239
|
-
:critical_blackhole => rule_data[:critical_blackhole],
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
logger.debug("NotificationRule#add_or_update json_rule_data: #{json_rule_data.inspect}") if logger
|
|
243
|
-
|
|
244
|
-
redis.sadd("contact_notification_rules:#{json_rule_data[:contact_id]}",
|
|
245
|
-
json_rule_data[:id])
|
|
246
|
-
redis.hmset("notification_rule:#{json_rule_data[:id]}",
|
|
247
|
-
*json_rule_data.flatten)
|
|
248
|
-
nil
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
def self.prepare_time_restriction(time_restriction, timezone = nil)
|
|
252
|
-
# this will hand back a 'deep' copy
|
|
253
|
-
tr = symbolize(time_restriction)
|
|
254
|
-
|
|
255
|
-
return unless (tr.has_key?(:start_time) || tr.has_key?(:start_date)) &&
|
|
256
|
-
(tr.has_key?(:end_time) || tr.has_key?(:end_date))
|
|
257
|
-
|
|
258
|
-
# exrules is deprecated in latest ice_cube, but may be stored in data
|
|
259
|
-
# serialised from earlier versions of the gem
|
|
260
|
-
# ( https://github.com/flapjack/flapjack/issues/715 )
|
|
261
|
-
tr.delete(:exrules)
|
|
262
|
-
|
|
263
|
-
parsed_time = proc {|tr, field|
|
|
264
|
-
if t = tr.delete(field)
|
|
265
|
-
t = t.dup
|
|
266
|
-
t = t[:time] if t.is_a?(Hash)
|
|
267
|
-
|
|
268
|
-
if t.is_a?(Time)
|
|
269
|
-
t
|
|
270
|
-
else
|
|
271
|
-
begin; (timezone || Time).parse(t); rescue ArgumentError; nil; end
|
|
272
|
-
end
|
|
273
|
-
else
|
|
274
|
-
nil
|
|
275
|
-
end
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
start_time = parsed_time.call(tr, :start_date) || parsed_time.call(tr, :start_time)
|
|
279
|
-
end_time = parsed_time.call(tr, :end_date) || parsed_time.call(tr, :end_time)
|
|
280
|
-
|
|
281
|
-
return unless start_time && end_time
|
|
282
|
-
|
|
283
|
-
tr[:start_time] = timezone ?
|
|
284
|
-
{:time => start_time, :zone => timezone.name} :
|
|
285
|
-
start_time
|
|
286
|
-
|
|
287
|
-
tr[:end_time] = timezone ?
|
|
288
|
-
{:time => end_time, :zone => timezone.name} :
|
|
289
|
-
end_time
|
|
290
|
-
|
|
291
|
-
tr[:duration] = end_time - start_time
|
|
292
|
-
|
|
293
|
-
# check that rrule types are valid IceCube rule types
|
|
294
|
-
return unless tr[:rrules].is_a?(Array) &&
|
|
295
|
-
tr[:rrules].all? {|rr| rr.is_a?(Hash)} &&
|
|
296
|
-
(tr[:rrules].map {|rr| rr[:rule_type]} -
|
|
297
|
-
['Daily', 'Hourly', 'Minutely', 'Monthly', 'Secondly',
|
|
298
|
-
'Weekly', 'Yearly']).empty?
|
|
299
|
-
|
|
300
|
-
# rewrite Weekly to IceCube::WeeklyRule, etc
|
|
301
|
-
tr[:rrules].each {|rrule|
|
|
302
|
-
rrule[:rule_type] = "IceCube::#{rrule[:rule_type]}Rule"
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
# TODO does this need to check classes for the following values?
|
|
306
|
-
# "validations": {
|
|
307
|
-
# "day": [1,2,3,4,5]
|
|
308
|
-
# },
|
|
309
|
-
# "interval": 1,
|
|
310
|
-
# "week_start": 0
|
|
311
|
-
|
|
312
|
-
tr
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
VALIDATION_PROCS = {
|
|
316
|
-
proc {|d| !d.has_key?(:entities) ||
|
|
317
|
-
( d[:entities].nil? ||
|
|
318
|
-
d[:entities].is_a?(Array) &&
|
|
319
|
-
d[:entities].all? {|e| e.is_a?(String)} ) } =>
|
|
320
|
-
"entities must be a list of strings",
|
|
321
|
-
|
|
322
|
-
proc {|d| !d.has_key?(:regex_entities) ||
|
|
323
|
-
( d[:regex_entities].nil? ||
|
|
324
|
-
d[:regex_entities].is_a?(Array) &&
|
|
325
|
-
d[:regex_entities].all? {|e| e.is_a?(String)} ) } =>
|
|
326
|
-
"regex_entities must be a list of strings",
|
|
327
|
-
|
|
328
|
-
proc {|d| !d.has_key?(:tags) ||
|
|
329
|
-
( d[:tags].nil? ||
|
|
330
|
-
d[:tags].is_a?(Set) &&
|
|
331
|
-
d[:tags].all? {|et| et.is_a?(String)} ) } =>
|
|
332
|
-
"tags must be a tag_set of strings",
|
|
333
|
-
|
|
334
|
-
proc {|d| !d.has_key?(:regex_tags) ||
|
|
335
|
-
( d[:regex_tags].nil? ||
|
|
336
|
-
d[:regex_tags].is_a?(Set) &&
|
|
337
|
-
d[:regex_tags].all? {|et| et.is_a?(String)} ) } =>
|
|
338
|
-
"regex_tags must be a tag_set of strings",
|
|
339
|
-
|
|
340
|
-
# conversion to a schedule needs a time zone, any one will do
|
|
341
|
-
proc {|d| !d.has_key?(:time_restrictions) ||
|
|
342
|
-
( d[:time_restrictions].nil? ||
|
|
343
|
-
d[:time_restrictions].all? {|tr|
|
|
344
|
-
!!self.time_restriction_to_icecube_schedule(symbolize(tr), ActiveSupport::TimeZone['UTC'])
|
|
345
|
-
} )
|
|
346
|
-
} =>
|
|
347
|
-
"time restrictions are invalid",
|
|
348
|
-
|
|
349
|
-
# TODO should the media types be checked against a whitelist?
|
|
350
|
-
proc {|d| !d.has_key?(:unknown_media) ||
|
|
351
|
-
( d[:unknown_media].nil? ||
|
|
352
|
-
d[:unknown_media].is_a?(Array) &&
|
|
353
|
-
d[:unknown_media].all? {|et| et.is_a?(String)} ) } =>
|
|
354
|
-
"unknown_media must be a list of strings",
|
|
355
|
-
|
|
356
|
-
proc {|d| !d.has_key?(:warning_media) ||
|
|
357
|
-
( d[:warning_media].nil? ||
|
|
358
|
-
d[:warning_media].is_a?(Array) &&
|
|
359
|
-
d[:warning_media].all? {|et| et.is_a?(String)} ) } =>
|
|
360
|
-
"warning_media must be a list of strings",
|
|
361
|
-
|
|
362
|
-
proc {|d| !d.has_key?(:critical_media) ||
|
|
363
|
-
( d[:critical_media].nil? ||
|
|
364
|
-
d[:critical_media].is_a?(Array) &&
|
|
365
|
-
d[:critical_media].all? {|et| et.is_a?(String)} ) } =>
|
|
366
|
-
"critical_media must be a list of strings",
|
|
367
|
-
|
|
368
|
-
proc {|d| !d.has_key?(:unknown_blackhole) ||
|
|
369
|
-
[TrueClass, FalseClass].include?(d[:unknown_blackhole].class) } =>
|
|
370
|
-
"unknown_blackhole must be true or false",
|
|
371
|
-
|
|
372
|
-
proc {|d| !d.has_key?(:warning_blackhole) ||
|
|
373
|
-
[TrueClass, FalseClass].include?(d[:warning_blackhole].class) } =>
|
|
374
|
-
"warning_blackhole must be true or false",
|
|
375
|
-
|
|
376
|
-
proc {|d| !d.has_key?(:critical_blackhole) ||
|
|
377
|
-
[TrueClass, FalseClass].include?(d[:critical_blackhole].class) } =>
|
|
378
|
-
"critical_blackhole must be true or false",
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
def self.validate_data(d, options = {})
|
|
382
|
-
id_not_required = !!options[:id_not_required]
|
|
383
|
-
# hash with validation => error_message
|
|
384
|
-
validations = {}
|
|
385
|
-
validations.merge!({ proc { d.has_key?(:id) } => "id not set"}) unless id_not_required
|
|
386
|
-
validations.merge!(VALIDATION_PROCS)
|
|
387
|
-
|
|
388
|
-
errors = validations.keys.inject([]) {|ret,vk|
|
|
389
|
-
ret << "Rule #{validations[vk]}" unless vk.call(d)
|
|
390
|
-
ret
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return if errors.empty?
|
|
394
|
-
|
|
395
|
-
if logger = options[:logger]
|
|
396
|
-
error_str = errors.join(", ")
|
|
397
|
-
logger.info "validation error: #{error_str}"
|
|
398
|
-
logger.debug "rule failing validations: #{d.inspect}"
|
|
399
|
-
end
|
|
400
|
-
errors
|
|
401
|
-
end
|
|
402
|
-
|
|
403
|
-
def refresh
|
|
404
|
-
rule_data = @redis.hgetall("notification_rule:#{@id}")
|
|
405
|
-
|
|
406
|
-
@contact_id = rule_data['contact_id']
|
|
407
|
-
tags = Flapjack.load_json(rule_data['tags'] || '')
|
|
408
|
-
@tags = tags ? Set.new(tags) : nil
|
|
409
|
-
regex_tags = Flapjack.load_json(rule_data['regex_tags'] || '')
|
|
410
|
-
@regex_tags = regex_tags ? Set.new(regex_tags) : nil
|
|
411
|
-
@entities = Flapjack.load_json(rule_data['entities'] || '')
|
|
412
|
-
@regex_entities = Flapjack.load_json(rule_data['regex_entities'] || '')
|
|
413
|
-
@time_restrictions = Flapjack.load_json(rule_data['time_restrictions'] || '')
|
|
414
|
-
@unknown_media = Flapjack.load_json(rule_data['unknown_media'] || '')
|
|
415
|
-
@warning_media = Flapjack.load_json(rule_data['warning_media'] || '')
|
|
416
|
-
@critical_media = Flapjack.load_json(rule_data['critical_media'] || '')
|
|
417
|
-
@unknown_blackhole = ((rule_data['unknown_blackhole'] || 'false').downcase == 'true')
|
|
418
|
-
@warning_blackhole = ((rule_data['warning_blackhole'] || 'false').downcase == 'true')
|
|
419
|
-
@critical_blackhole = ((rule_data['critical_blackhole'] || 'false').downcase == 'true')
|
|
420
|
-
end
|
|
421
|
-
|
|
422
|
-
end
|
|
423
|
-
end
|
|
424
|
-
end
|
|
425
|
-
|