flapjack 1.6.0 → 2.0.0b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (301) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -6
  3. data/.gitmodules +1 -1
  4. data/.rspec +1 -1
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +12 -13
  7. data/CHANGELOG.md +2 -9
  8. data/CONTRIBUTING.md +7 -2
  9. data/Gemfile +4 -13
  10. data/LICENCE +1 -0
  11. data/README.md +8 -2
  12. data/Rakefile +2 -2
  13. data/bin/flapjack +3 -12
  14. data/build.sh +4 -2
  15. data/etc/flapjack_config.toml.example +273 -0
  16. data/features/ack_after_sched_maint.feature +18 -21
  17. data/features/cli.feature +11 -71
  18. data/features/cli_flapjack-feed-events.feature +14 -15
  19. data/features/cli_flapjack-nagios-receiver.feature +12 -41
  20. data/features/cli_flapper.feature +12 -41
  21. data/features/cli_purge.feature +5 -6
  22. data/features/cli_receive-events.feature +6 -7
  23. data/features/cli_simulate-failed-check.feature +5 -6
  24. data/features/events.feature +206 -181
  25. data/features/events_check_names.feature +4 -7
  26. data/features/notification_rules.feature +144 -223
  27. data/features/notifications.feature +65 -57
  28. data/features/rollup.feature +45 -47
  29. data/features/steps/cli_steps.rb +4 -5
  30. data/features/steps/events_steps.rb +163 -373
  31. data/features/steps/notifications_steps.rb +408 -264
  32. data/features/steps/packaging-lintian_steps.rb +0 -4
  33. data/features/steps/time_travel_steps.rb +0 -26
  34. data/features/support/daemons.rb +6 -31
  35. data/features/support/env.rb +65 -74
  36. data/flapjack.gemspec +22 -24
  37. data/lib/flapjack.rb +14 -7
  38. data/lib/flapjack/cli/flapper.rb +74 -173
  39. data/lib/flapjack/cli/maintenance.rb +278 -109
  40. data/lib/flapjack/cli/migrate.rb +950 -0
  41. data/lib/flapjack/cli/purge.rb +19 -22
  42. data/lib/flapjack/cli/receiver.rb +150 -326
  43. data/lib/flapjack/cli/server.rb +8 -235
  44. data/lib/flapjack/cli/simulate.rb +42 -57
  45. data/lib/flapjack/configuration.rb +51 -37
  46. data/lib/flapjack/coordinator.rb +138 -129
  47. data/lib/flapjack/data/acknowledgement.rb +177 -0
  48. data/lib/flapjack/data/alert.rb +97 -158
  49. data/lib/flapjack/data/check.rb +611 -0
  50. data/lib/flapjack/data/condition.rb +70 -0
  51. data/lib/flapjack/data/contact.rb +226 -456
  52. data/lib/flapjack/data/event.rb +96 -184
  53. data/lib/flapjack/data/extensions/associations.rb +59 -0
  54. data/lib/flapjack/data/extensions/short_name.rb +25 -0
  55. data/lib/flapjack/data/medium.rb +428 -0
  56. data/lib/flapjack/data/metrics.rb +194 -0
  57. data/lib/flapjack/data/notification.rb +22 -281
  58. data/lib/flapjack/data/rule.rb +473 -0
  59. data/lib/flapjack/data/scheduled_maintenance.rb +244 -0
  60. data/lib/flapjack/data/state.rb +221 -0
  61. data/lib/flapjack/data/statistic.rb +112 -0
  62. data/lib/flapjack/data/tag.rb +277 -0
  63. data/lib/flapjack/data/test_notification.rb +182 -0
  64. data/lib/flapjack/data/unscheduled_maintenance.rb +159 -0
  65. data/lib/flapjack/data/validators/id_validator.rb +20 -0
  66. data/lib/flapjack/exceptions.rb +6 -0
  67. data/lib/flapjack/filters/acknowledgement.rb +23 -16
  68. data/lib/flapjack/filters/base.rb +0 -5
  69. data/lib/flapjack/filters/delays.rb +53 -43
  70. data/lib/flapjack/filters/ok.rb +23 -14
  71. data/lib/flapjack/filters/scheduled_maintenance.rb +3 -3
  72. data/lib/flapjack/filters/unscheduled_maintenance.rb +12 -3
  73. data/lib/flapjack/gateways/aws_sns.rb +65 -49
  74. data/lib/flapjack/gateways/aws_sns/alert.text.erb +2 -2
  75. data/lib/flapjack/gateways/aws_sns/alert_subject.text.erb +2 -2
  76. data/lib/flapjack/gateways/aws_sns/rollup_subject.text.erb +1 -1
  77. data/lib/flapjack/gateways/email.rb +107 -90
  78. data/lib/flapjack/gateways/email/alert.html.erb +19 -18
  79. data/lib/flapjack/gateways/email/alert.text.erb +20 -14
  80. data/lib/flapjack/gateways/email/alert_subject.text.erb +2 -1
  81. data/lib/flapjack/gateways/email/rollup.html.erb +14 -13
  82. data/lib/flapjack/gateways/email/rollup.text.erb +13 -10
  83. data/lib/flapjack/gateways/jabber.rb +679 -671
  84. data/lib/flapjack/gateways/jabber/alert.text.erb +9 -6
  85. data/lib/flapjack/gateways/jsonapi.rb +164 -350
  86. data/lib/flapjack/gateways/jsonapi/data/join_descriptor.rb +44 -0
  87. data/lib/flapjack/gateways/jsonapi/data/method_descriptor.rb +21 -0
  88. data/lib/flapjack/gateways/jsonapi/helpers/headers.rb +63 -0
  89. data/lib/flapjack/gateways/jsonapi/helpers/miscellaneous.rb +136 -0
  90. data/lib/flapjack/gateways/jsonapi/helpers/resources.rb +227 -0
  91. data/lib/flapjack/gateways/jsonapi/helpers/serialiser.rb +313 -0
  92. data/lib/flapjack/gateways/jsonapi/helpers/swagger_docs.rb +322 -0
  93. data/lib/flapjack/gateways/jsonapi/methods/association_delete.rb +115 -0
  94. data/lib/flapjack/gateways/jsonapi/methods/association_get.rb +288 -0
  95. data/lib/flapjack/gateways/jsonapi/methods/association_patch.rb +178 -0
  96. data/lib/flapjack/gateways/jsonapi/methods/association_post.rb +116 -0
  97. data/lib/flapjack/gateways/jsonapi/methods/metrics.rb +71 -0
  98. data/lib/flapjack/gateways/jsonapi/methods/resource_delete.rb +119 -0
  99. data/lib/flapjack/gateways/jsonapi/methods/resource_get.rb +186 -0
  100. data/lib/flapjack/gateways/jsonapi/methods/resource_patch.rb +239 -0
  101. data/lib/flapjack/gateways/jsonapi/methods/resource_post.rb +197 -0
  102. data/lib/flapjack/gateways/jsonapi/middleware/array_param_fixer.rb +27 -0
  103. data/lib/flapjack/gateways/jsonapi/{rack → middleware}/json_params_parser.rb +7 -6
  104. data/lib/flapjack/gateways/jsonapi/middleware/request_timestamp.rb +18 -0
  105. data/lib/flapjack/gateways/oobetet.rb +222 -170
  106. data/lib/flapjack/gateways/pager_duty.rb +388 -0
  107. data/lib/flapjack/gateways/pager_duty/alert.text.erb +13 -0
  108. data/lib/flapjack/gateways/slack.rb +56 -48
  109. data/lib/flapjack/gateways/slack/alert.text.erb +1 -1
  110. data/lib/flapjack/gateways/slack/rollup.text.erb +1 -1
  111. data/lib/flapjack/gateways/sms_aspsms.rb +155 -0
  112. data/lib/flapjack/gateways/sms_aspsms/alert.text.erb +7 -0
  113. data/lib/flapjack/gateways/sms_aspsms/rollup.text.erb +2 -0
  114. data/lib/flapjack/gateways/sms_messagenet.rb +77 -57
  115. data/lib/flapjack/gateways/sms_messagenet/alert.text.erb +3 -2
  116. data/lib/flapjack/gateways/sms_nexmo.rb +53 -51
  117. data/lib/flapjack/gateways/sms_nexmo/alert.text.erb +2 -2
  118. data/lib/flapjack/gateways/sms_nexmo/rollup.text.erb +1 -1
  119. data/lib/flapjack/gateways/sms_twilio.rb +79 -62
  120. data/lib/flapjack/gateways/sms_twilio/alert.text.erb +3 -2
  121. data/lib/flapjack/gateways/web.rb +437 -345
  122. data/lib/flapjack/gateways/web/middleware/request_timestamp.rb +18 -0
  123. data/lib/flapjack/gateways/web/public/css/bootstrap.css +3793 -4340
  124. data/lib/flapjack/gateways/web/public/css/bootstrap.css.map +1 -0
  125. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.eot +0 -0
  126. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.svg +273 -214
  127. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  128. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff +0 -0
  129. data/lib/flapjack/gateways/web/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  130. data/lib/flapjack/gateways/web/public/js/bootstrap.js +1637 -1607
  131. data/lib/flapjack/gateways/web/public/js/self_stats.js +1 -2
  132. data/lib/flapjack/gateways/web/views/_pagination.html.erb +19 -0
  133. data/lib/flapjack/gateways/web/views/check.html.erb +159 -121
  134. data/lib/flapjack/gateways/web/views/checks.html.erb +82 -41
  135. data/lib/flapjack/gateways/web/views/contact.html.erb +59 -71
  136. data/lib/flapjack/gateways/web/views/contacts.html.erb +32 -8
  137. data/lib/flapjack/gateways/web/views/index.html.erb +2 -2
  138. data/lib/flapjack/gateways/web/views/{layout.erb → layout.html.erb} +7 -23
  139. data/lib/flapjack/gateways/web/views/self_stats.html.erb +32 -33
  140. data/lib/flapjack/gateways/web/views/tag.html.erb +32 -0
  141. data/lib/flapjack/gateways/web/views/tags.html.erb +51 -0
  142. data/lib/flapjack/logger.rb +34 -3
  143. data/lib/flapjack/notifier.rb +180 -112
  144. data/lib/flapjack/patches.rb +8 -63
  145. data/lib/flapjack/pikelet.rb +185 -143
  146. data/lib/flapjack/processor.rb +323 -191
  147. data/lib/flapjack/record_queue.rb +33 -0
  148. data/lib/flapjack/redis_proxy.rb +66 -0
  149. data/lib/flapjack/utility.rb +21 -15
  150. data/lib/flapjack/version.rb +2 -1
  151. data/libexec/httpbroker.go +218 -14
  152. data/libexec/oneoff.go +13 -10
  153. data/spec/lib/flapjack/configuration_spec.rb +286 -0
  154. data/spec/lib/flapjack/coordinator_spec.rb +103 -157
  155. data/spec/lib/flapjack/data/check_spec.rb +175 -0
  156. data/spec/lib/flapjack/data/contact_spec.rb +26 -349
  157. data/spec/lib/flapjack/data/event_spec.rb +76 -291
  158. data/spec/lib/flapjack/data/medium_spec.rb +19 -0
  159. data/spec/lib/flapjack/data/rule_spec.rb +43 -0
  160. data/spec/lib/flapjack/data/scheduled_maintenance_spec.rb +976 -0
  161. data/spec/lib/flapjack/data/unscheduled_maintenance_spec.rb +34 -0
  162. data/spec/lib/flapjack/gateways/aws_sns_spec.rb +111 -60
  163. data/spec/lib/flapjack/gateways/email_spec.rb +194 -161
  164. data/spec/lib/flapjack/gateways/jabber_spec.rb +961 -162
  165. data/spec/lib/flapjack/gateways/jsonapi/methods/check_links_spec.rb +155 -0
  166. data/spec/lib/flapjack/gateways/jsonapi/methods/checks_spec.rb +426 -0
  167. data/spec/lib/flapjack/gateways/jsonapi/methods/contact_links_spec.rb +217 -0
  168. data/spec/lib/flapjack/gateways/jsonapi/methods/contacts_spec.rb +425 -0
  169. data/spec/lib/flapjack/gateways/jsonapi/methods/events_spec.rb +271 -0
  170. data/spec/lib/flapjack/gateways/jsonapi/methods/media_spec.rb +257 -0
  171. data/spec/lib/flapjack/gateways/jsonapi/methods/medium_links_spec.rb +163 -0
  172. data/spec/lib/flapjack/gateways/jsonapi/methods/metrics_spec.rb +8 -0
  173. data/spec/lib/flapjack/gateways/jsonapi/methods/rule_links_spec.rb +212 -0
  174. data/spec/lib/flapjack/gateways/jsonapi/methods/rules_spec.rb +289 -0
  175. data/spec/lib/flapjack/gateways/jsonapi/methods/scheduled_maintenance_links_spec.rb +49 -0
  176. data/spec/lib/flapjack/gateways/jsonapi/methods/scheduled_maintenances_spec.rb +242 -0
  177. data/spec/lib/flapjack/gateways/jsonapi/methods/tag_links_spec.rb +274 -0
  178. data/spec/lib/flapjack/gateways/jsonapi/methods/tags_spec.rb +302 -0
  179. data/spec/lib/flapjack/gateways/jsonapi/methods/unscheduled_maintenance_links_spec.rb +49 -0
  180. data/spec/lib/flapjack/gateways/jsonapi/methods/unscheduled_maintenances_spec.rb +339 -0
  181. data/spec/lib/flapjack/gateways/jsonapi_spec.rb +1 -1
  182. data/spec/lib/flapjack/gateways/oobetet_spec.rb +151 -79
  183. data/spec/lib/flapjack/gateways/pager_duty_spec.rb +353 -0
  184. data/spec/lib/flapjack/gateways/slack_spec.rb +53 -53
  185. data/spec/lib/flapjack/gateways/sms_aspsms_spec.rb +106 -0
  186. data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +111 -54
  187. data/spec/lib/flapjack/gateways/sms_nexmo_spec.rb +50 -51
  188. data/spec/lib/flapjack/gateways/sms_twilio_spec.rb +108 -48
  189. data/spec/lib/flapjack/gateways/web_spec.rb +144 -216
  190. data/spec/lib/flapjack/notifier_spec.rb +132 -1
  191. data/spec/lib/flapjack/pikelet_spec.rb +111 -50
  192. data/spec/lib/flapjack/processor_spec.rb +210 -40
  193. data/spec/lib/flapjack/redis_proxy_spec.rb +45 -0
  194. data/spec/lib/flapjack/utility_spec.rb +11 -15
  195. data/spec/service_consumers/fixture_data.rb +547 -0
  196. data/spec/service_consumers/pact_helper.rb +21 -32
  197. data/spec/service_consumers/pacts/flapjack-diner_v2.0.json +4652 -0
  198. data/spec/service_consumers/provider_states_for_flapjack-diner.rb +279 -322
  199. data/spec/service_consumers/provider_support.rb +8 -0
  200. data/spec/spec_helper.rb +34 -44
  201. data/spec/support/erb_view_helper.rb +1 -1
  202. data/spec/support/factories.rb +58 -0
  203. data/spec/support/jsonapi_helper.rb +15 -26
  204. data/spec/support/mock_logger.rb +43 -0
  205. data/spec/support/xmpp_comparable.rb +24 -0
  206. data/src/flapjack/transport_test.go +30 -1
  207. data/tasks/dump_keys.rake +82 -0
  208. data/tasks/events.rake +7 -7
  209. data/tasks/support/flapjack_config_benchmark.toml +28 -0
  210. data/tasks/support/flapjack_config_benchmark.yaml +0 -2
  211. metadata +175 -222
  212. data/Guardfile +0 -14
  213. data/etc/flapjack_config.yaml.example +0 -477
  214. data/features/cli_flapjack-populator.feature +0 -90
  215. data/features/support/silent_system.rb +0 -4
  216. data/lib/flapjack/cli/import.rb +0 -108
  217. data/lib/flapjack/data/entity.rb +0 -652
  218. data/lib/flapjack/data/entity_check.rb +0 -1044
  219. data/lib/flapjack/data/message.rb +0 -56
  220. data/lib/flapjack/data/migration.rb +0 -234
  221. data/lib/flapjack/data/notification_rule.rb +0 -425
  222. data/lib/flapjack/data/semaphore.rb +0 -44
  223. data/lib/flapjack/data/tagged.rb +0 -48
  224. data/lib/flapjack/gateways/jsonapi/check_methods.rb +0 -206
  225. data/lib/flapjack/gateways/jsonapi/check_presenter.rb +0 -221
  226. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +0 -186
  227. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +0 -223
  228. data/lib/flapjack/gateways/jsonapi/medium_methods.rb +0 -185
  229. data/lib/flapjack/gateways/jsonapi/metrics_methods.rb +0 -132
  230. data/lib/flapjack/gateways/jsonapi/notification_rule_methods.rb +0 -141
  231. data/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods.rb +0 -139
  232. data/lib/flapjack/gateways/jsonapi/report_methods.rb +0 -146
  233. data/lib/flapjack/gateways/pagerduty.rb +0 -318
  234. data/lib/flapjack/gateways/pagerduty/alert.text.erb +0 -10
  235. data/lib/flapjack/gateways/web/public/css/select2-bootstrap.css +0 -87
  236. data/lib/flapjack/gateways/web/public/css/select2.css +0 -615
  237. data/lib/flapjack/gateways/web/public/css/tablesort.css +0 -67
  238. data/lib/flapjack/gateways/web/public/img/select2-spinner.gif +0 -0
  239. data/lib/flapjack/gateways/web/public/img/select2.png +0 -0
  240. data/lib/flapjack/gateways/web/public/img/select2x2.png +0 -0
  241. data/lib/flapjack/gateways/web/public/js/backbone.js +0 -1581
  242. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +0 -322
  243. data/lib/flapjack/gateways/web/public/js/flapjack.js +0 -82
  244. data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.js +0 -1640
  245. data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.widgets.js +0 -1390
  246. data/lib/flapjack/gateways/web/public/js/modules/contact.js +0 -520
  247. data/lib/flapjack/gateways/web/public/js/modules/entity.js +0 -28
  248. data/lib/flapjack/gateways/web/public/js/modules/medium.js +0 -40
  249. data/lib/flapjack/gateways/web/public/js/select2.js +0 -3397
  250. data/lib/flapjack/gateways/web/public/js/tablesort.js +0 -44
  251. data/lib/flapjack/gateways/web/public/js/underscore.js +0 -1276
  252. data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +0 -173
  253. data/lib/flapjack/gateways/web/views/entities.html.erb +0 -30
  254. data/lib/flapjack/gateways/web/views/entity.html.erb +0 -51
  255. data/lib/flapjack/rack_logger.rb +0 -47
  256. data/lib/flapjack/redis_pool.rb +0 -42
  257. data/spec/lib/flapjack/data/entity_check_spec.rb +0 -1418
  258. data/spec/lib/flapjack/data/entity_spec.rb +0 -872
  259. data/spec/lib/flapjack/data/message_spec.rb +0 -30
  260. data/spec/lib/flapjack/data/migration_spec.rb +0 -104
  261. data/spec/lib/flapjack/data/notification_rule_spec.rb +0 -232
  262. data/spec/lib/flapjack/data/notification_spec.rb +0 -53
  263. data/spec/lib/flapjack/data/semaphore_spec.rb +0 -24
  264. data/spec/lib/flapjack/filters/acknowledgement_spec.rb +0 -6
  265. data/spec/lib/flapjack/filters/delays_spec.rb +0 -6
  266. data/spec/lib/flapjack/filters/ok_spec.rb +0 -6
  267. data/spec/lib/flapjack/filters/scheduled_maintenance_spec.rb +0 -6
  268. data/spec/lib/flapjack/filters/unscheduled_maintenance_spec.rb +0 -6
  269. data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +0 -315
  270. data/spec/lib/flapjack/gateways/jsonapi/check_presenter_spec.rb +0 -223
  271. data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +0 -131
  272. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +0 -389
  273. data/spec/lib/flapjack/gateways/jsonapi/medium_methods_spec.rb +0 -231
  274. data/spec/lib/flapjack/gateways/jsonapi/notification_rule_methods_spec.rb +0 -169
  275. data/spec/lib/flapjack/gateways/jsonapi/pagerduty_credential_methods_spec.rb +0 -114
  276. data/spec/lib/flapjack/gateways/jsonapi/report_methods_spec.rb +0 -590
  277. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +0 -249
  278. data/spec/lib/flapjack/gateways/web/views/check.html.erb_spec.rb +0 -21
  279. data/spec/lib/flapjack/gateways/web/views/contact.html.erb_spec.rb +0 -24
  280. data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +0 -16
  281. data/spec/lib/flapjack/redis_pool_spec.rb +0 -29
  282. data/spec/service_consumers/pacts/flapjack-diner_v1.0.json +0 -4702
  283. data/tasks/entities.rake +0 -151
  284. data/tasks/profile.rake +0 -282
  285. data/tmp/acknowledge.rb +0 -13
  286. data/tmp/create_config_yaml.rb +0 -16
  287. data/tmp/create_event_ok.rb +0 -30
  288. data/tmp/create_event_unknown.rb +0 -30
  289. data/tmp/create_events_failure.rb +0 -34
  290. data/tmp/create_events_ok.rb +0 -32
  291. data/tmp/create_events_ok_fail_ack_ok.rb +0 -53
  292. data/tmp/create_events_ok_failure.rb +0 -41
  293. data/tmp/create_events_ok_failure_ack.rb +0 -53
  294. data/tmp/dummy_contacts.json +0 -43
  295. data/tmp/dummy_entities.json +0 -37
  296. data/tmp/generate_nagios_test_hosts.rb +0 -16
  297. data/tmp/notification_rules.rb +0 -73
  298. data/tmp/parse_config_yaml.rb +0 -7
  299. data/tmp/redis_find_spurious_unknown_states.rb +0 -52
  300. data/tmp/test_json_post.rb +0 -19
  301. data/tmp/test_notification_rules_api.rb +0 -171
@@ -1,147 +1,110 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'digest'
4
-
5
3
  require 'active_support/inflector'
4
+
5
+ require 'zermelo/records/redis'
6
+
6
7
  require 'flapjack/utility'
7
8
 
9
+ require 'flapjack/data/condition'
10
+ require 'flapjack/data/state'
11
+
8
12
  # Alert is the object ready to send to someone, complete with an address and all
9
13
  # the data with which to render the text of the alert in the appropriate gateway
10
- #
11
- # It should possibly be renamed AlertPresenter
12
14
 
13
15
  module Flapjack
14
16
  module Data
15
17
  class Alert
16
18
 
17
- # from Flapjack::Data::Notification
18
- attr_reader :event_id,
19
- :state,
20
- :summary,
21
- :acknowledgement_duration,
22
- :last_state,
23
- :last_summary,
24
- :state_duration,
25
- :details,
26
- :time,
27
- :notification_type,
28
- :event_count,
29
- :tags
30
-
31
- # from Flapjack::Data::Message
32
- # :id,
33
- attr_reader :media,
34
- :address,
35
- :rollup,
36
- :contact_id,
37
- :contact_first_name,
38
- :contact_last_name
39
-
40
- # from Flapjack::Notifier
41
- attr_reader :rollup_threshold,
42
- :rollup_alerts,
43
- :in_scheduled_maintenance,
44
- :in_unscheduled_maintenance
45
-
46
- # from self
47
- attr_reader :entity,
48
- :check,
49
- :notification_id,
50
- :event_hash
51
-
52
19
  include Flapjack::Utility
53
-
54
- def initialize(contents, opts = {})
55
- raise "no logger supplied" unless @logger = opts[:logger]
56
-
57
- @event_id = contents['event_id']
58
- @event_hash = contents['event_hash'] ||
59
- Digest.hexencode(Digest::SHA1.new.digest(@event_id))[0..7].downcase
60
- @state = contents['state']
61
- @summary = contents['summary']
62
- @acknowledgement_duration = contents['duration'] # SMELLY
63
- @last_state = contents['last_state']
64
- @last_summary = contents['last_summary']
65
- @state_duration = contents['state_duration']
66
- @details = contents['details']
67
- @time = contents['time']
68
- @notification_type = contents['notification_type']
69
- @event_count = contents['event_count']
70
- @tags = contents['tags']
71
-
72
- @media = contents['media']
73
- @address = contents['address']
74
- @rollup = contents['rollup']
75
- @contact_id = contents['contact_id']
76
- @contact_first_name = contents['contact_first_name']
77
- @contact_last_name = contents['contact_last_name']
78
-
79
- @rollup_threshold = contents['rollup_threshold']
80
- @rollup_alerts = contents['rollup_alerts']
81
- @in_scheduled_maintenance = contents['in_scheduled_maintenance']
82
- @in_unscheduled_maintenance = contents['in_unscheduled_maintenance']
83
-
84
- @entity, @check = @event_id.split(':', 2)
85
- @notification_id = contents['id'] || SecureRandom.uuid
86
-
87
- allowed_states = ['ok', 'critical', 'warning', 'unknown', 'test_notifications', 'acknowledgement']
88
- allowed_rollup_states = ['critical', 'warning', 'unknown']
89
- raise "state #{@state.inspect} is invalid" unless
90
- allowed_states.include?(@state)
91
-
92
- if @state_duration
93
- raise "state_duration (#{@state_duration.inspect}) is invalid" unless
94
- @state_duration.is_a?(Integer) && @state_duration >= 0
95
- end
96
-
97
- if @rollup_alerts
98
- raise "rollup_alerts should be nil or a hash" unless @rollup_alerts.is_a?(Hash)
99
- @rollup_alerts.each_pair do |check, details|
100
- raise "duration of rollup_alerts['#{check}'] must be an integer" unless
101
- details['duration'] && details['duration'].is_a?(Integer)
102
- raise "state of rollup_alerts['#{check}'] is invalid" unless
103
- details['state'] && allowed_rollup_states.include?(details['state'])
20
+ include Zermelo::Records::RedisSet
21
+
22
+ define_attributes :condition => :string,
23
+ :action => :string,
24
+ :summary => :string,
25
+ :details => :string,
26
+ :last_condition => :string,
27
+ :last_action => :string,
28
+ :last_summary => :string,
29
+ :event_count => :integer,
30
+ :time => :timestamp,
31
+ :acknowledgement_duration => :integer, # passed in as duration in other code
32
+ :condition_duration => :float,
33
+ :rollup => :string,
34
+ :rollup_states_json => :string,
35
+ :event_hash => :string
36
+
37
+ belongs_to :medium, :class_name => 'Flapjack::Data::Medium', :inverse_of => :alerts
38
+
39
+ belongs_to :check, :class_name => 'Flapjack::Data::Check', :inverse_of => :alerts
40
+
41
+ validates :condition, :unless => proc {|s| !s.action.nil? },
42
+ :inclusion => { :in => Flapjack::Data::Condition.healthy.keys +
43
+ Flapjack::Data::Condition.unhealthy.keys }
44
+
45
+ validates :action, :allow_nil => true, :inclusion => {:in => Flapjack::Data::State::ACTIONS}
46
+
47
+ validates :condition_duration, :presence => true, :allow_nil => true,
48
+ :numericality => {:minimum => 0}, :unless => proc {|n| n.type == 'test'}
49
+
50
+ validates_each :rollup_states_json do |record, att, value|
51
+ unless value.nil?
52
+ states = Flapjack.load_json(value)
53
+ case states
54
+ when Hash
55
+ record.errors.add(att, 'must contain a serialized Hash (String => Array[String])') unless states.all? {|k,v|
56
+ k.is_a?(String) && v.is_a?(Array) && v.all?{|vs| vs.is_a?(String)}
57
+ }
58
+ else
59
+ record.errors.add(att, 'must contain a serialized Hash (String => Array[String])')
104
60
  end
105
61
  end
106
62
  end
107
63
 
108
- def self.add(queue, alert_data, opts = {})
109
- raise "Redis connection not set" unless redis = opts[:redis]
110
- redis.rpush(queue, Flapjack.dump_json(alert_data))
64
+ # TODO handle JSON exception
65
+ def rollup_states
66
+ if self.rollup_states_json.nil?
67
+ @rollup_states = nil
68
+ return
69
+ end
70
+ @rollup_states = Flapjack.load_json(self.rollup_states_json)
111
71
  end
112
72
 
113
- def self.next(queue, opts = {})
114
- raise "Redis connection not set" unless redis = opts[:redis]
73
+ def rollup_states=(rollup_states)
74
+ @rollup_states = rollup_states
75
+ self.rollup_states_json = rollup_states.nil? ? nil : Flapjack.dump_json(rollup_states)
76
+ end
115
77
 
116
- defaults = { :block => true }
117
- options = defaults.merge(opts)
78
+ def notification_type
79
+ self.class.notification_type(action, condition)
80
+ end
118
81
 
119
- if options[:block]
120
- raw = redis.blpop(queue, 0)[1]
121
- else
122
- raw = redis.lpop(queue)
123
- return unless raw
124
- end
125
- begin
126
- parsed = ::Flapjack.load_json( raw )
127
- rescue Oj::Error => e
128
- if options[:logger]
129
- options[:logger].warn("Error deserialising alert json: #{e}, raw json: #{raw.inspect}")
82
+ def self.notification_type(act, cond)
83
+ case act
84
+ when 'acknowledgement'
85
+ 'acknowledgement'
86
+ when /\Atest_notifications(?:\s+#{Flapjack::Data::Condition.unhealthy.keys.join('|')})?\z/
87
+ 'test'
88
+ when nil
89
+ case cond
90
+ when 'ok'
91
+ 'recovery'
92
+ when 'warning', 'critical', 'unknown'
93
+ 'problem'
94
+ else
95
+ 'unknown'
130
96
  end
131
- return nil
132
97
  end
133
- return if 'shutdown'.eql?(parsed['notification_type'])
134
- self.new( parsed, :logger => opts[:logger] )
135
98
  end
136
99
 
137
100
  def type
138
- case @rollup
101
+ case self.rollup
139
102
  when "problem"
140
103
  "rollup_problem"
141
104
  when "recovery"
142
105
  "rollup_recovery"
143
106
  else
144
- @notification_type
107
+ notification_type
145
108
  end
146
109
  end
147
110
 
@@ -156,61 +119,51 @@ module Flapjack
156
119
  end
157
120
  end
158
121
 
159
- def state_title_case
160
- ['ok'].include?(@state) ? @state.upcase : @state.titleize
122
+ def state
123
+ @state ||= (self.action || self.condition)
161
124
  end
162
125
 
163
- def last_state_title_case
164
- ['ok'].include?(@last_state) ? @last_state.upcase : @last_state.titleize
126
+ def last_state
127
+ @last_state ||= (self.last_action || self.last_condition)
165
128
  end
166
129
 
167
- def rollup_alerts_by_state
168
- ['critical', 'warning', 'unknown'].inject({}) do |memo, state|
169
- alerts = rollup_alerts.find_all {|alert| alert[1]['state'] == state}
170
- memo[state] = alerts
171
- memo
172
- end
130
+ def state_title_case
131
+ ['ok'].include?(state) ? state.upcase : state.titleize
173
132
  end
174
133
 
175
- def rollup_state_counts
176
- rollup_alerts.inject({}) do |memo, alert|
177
- memo[alert[1]['state']] = (memo[alert[1]['state']] || 0) + 1
178
- memo
179
- end
134
+ def last_state_title_case
135
+ ['ok'].include?(last_state) ? last_state.upcase : last_state.titleize
180
136
  end
181
137
 
182
138
  def rollup_states_summary
183
- state_counts = rollup_state_counts
184
- ['critical', 'warning', 'unknown'].inject([]) do |memo, state|
185
- next memo unless rollup_state_counts[state]
186
- memo << "#{state.titleize}: #{state_counts[state]}"
187
- memo
139
+ return '' if rollup_states.nil?
140
+ rollup_states.each_with_object([]) do |(alert_state, alerts), memo|
141
+ memo << "#{alert_state.titleize}: #{alerts.size}"
188
142
  end.join(', ')
189
143
  end
190
144
 
191
145
  # produces a textual list of checks that are failing broken down by state, eg:
192
146
  # Critical: 'PING' on 'foo-app-01.example.com', 'SSH' on 'foo-app-01.example.com';
193
147
  # Warning: 'Disk / Utilisation' on 'foo-app-02.example.com'
194
- def rollup_states_detail_text(opts)
148
+ def rollup_states_detail_text(opts = {})
149
+ return '' if rollup_states.nil?
195
150
  max_checks = opts[:max_checks_per_state]
196
- rollup_alerts_by_state.inject([]) do |memo, state|
197
- state_titleized = state[0].titleize
198
- alerts = max_checks && max_checks > 0 ? state[1][0..(max_checks - 1)] : state[1]
199
- next memo if alerts.empty?
200
- checks = alerts.map {|alert| alert[0]}
201
- checks << '...' if checks.length < rollup_state_counts[state[0]]
202
- memo << "#{state[0].titleize}: #{checks.join(', ')}"
203
- memo
151
+ rollup_states.each_with_object([]) do |(alert_state, alerts), memo|
152
+ alerts = alerts[0..(max_checks - 1)] unless max_checks.nil? || (max_checks <= 0)
153
+ next if alerts.empty?
154
+ alerts << '...' if alerts.size < rollup_states[alert_state].size
155
+ memo << "#{alert_state.titleize}: #{alerts.join(', ')}"
204
156
  end.join('; ')
205
157
  end
206
158
 
207
159
  def to_s
208
- msg = "Alert via #{media}:#{address} to contact #{contact_id} (#{contact_first_name} #{contact_last_name}): "
160
+ contact = medium.contact
161
+ msg = "Alert via #{medium.transport}:#{medium.address} to contact #{contact.id} (#{contact.name}): "
209
162
  msg += type_sentence_case
210
163
  if rollup
211
164
  msg += " - #{rollup_states_summary} (#{rollup_states_detail_text(:max_checks_per_state => 3)})"
212
165
  else
213
- msg += " - '#{check}' on #{entity}"
166
+ msg += " - '#{self.check.name}'"
214
167
  unless ['acknowledgement', 'test'].include?(type)
215
168
  msg += " is #{state_title_case}"
216
169
  end
@@ -223,20 +176,6 @@ module Flapjack
223
176
  end
224
177
  end
225
178
  end
226
-
227
- def record_send_success!
228
- @logger.info "Sent alert successfully: #{to_s}"
229
- end
230
-
231
- # TODO: perhaps move message send failure porting to this method
232
- # to avoid duplication in the gateways, and to more easily allow
233
- # better error reporting on message generation / send failure
234
- #def record_send_failure!(opts)
235
- # exception = opts[:exception]
236
- # message = opts[:message]
237
- # @logger.error "Error sending an alert! #{alert}"
238
- #end
239
-
240
179
  end
241
180
  end
242
181
  end
@@ -0,0 +1,611 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'digest'
4
+
5
+ require 'swagger/blocks'
6
+
7
+ require 'zermelo/records/redis'
8
+
9
+ require 'flapjack/data/extensions/short_name'
10
+ require 'flapjack/data/validators/id_validator'
11
+
12
+ require 'flapjack/data/condition'
13
+ require 'flapjack/data/state'
14
+ require 'flapjack/data/unscheduled_maintenance'
15
+
16
+ require 'flapjack/data/extensions/associations'
17
+ require 'flapjack/data/extensions/short_name'
18
+
19
+ require 'flapjack/gateways/jsonapi/data/join_descriptor'
20
+ require 'flapjack/gateways/jsonapi/data/method_descriptor'
21
+
22
+ module Flapjack
23
+ module Data
24
+ class Check
25
+
26
+ include Zermelo::Records::RedisSet
27
+ include ActiveModel::Serializers::JSON
28
+ self.include_root_in_json = false
29
+ include Swagger::Blocks
30
+
31
+ include Flapjack::Data::Extensions::Associations
32
+ include Flapjack::Data::Extensions::ShortName
33
+
34
+ define_attributes :name => :string,
35
+ :enabled => :boolean,
36
+ :ack_hash => :string,
37
+ :initial_failure_delay => :integer,
38
+ :repeat_failure_delay => :integer,
39
+ :notification_count => :integer,
40
+ :condition => :string,
41
+ :failing => :boolean,
42
+ :alertable => :boolean
43
+
44
+ index_by :enabled, :failing, :alertable
45
+ unique_index_by :name, :ack_hash
46
+
47
+ # TODO validate uniqueness of :name, :ack_hash
48
+
49
+ has_and_belongs_to_many :tags, :class_name => 'Flapjack::Data::Tag',
50
+ :inverse_of => :checks
51
+
52
+ has_sorted_set :scheduled_maintenances,
53
+ :class_name => 'Flapjack::Data::ScheduledMaintenance',
54
+ :key => :start_time, :order => :desc, :inverse_of => :check
55
+
56
+ has_sorted_set :unscheduled_maintenances,
57
+ :class_name => 'Flapjack::Data::UnscheduledMaintenance',
58
+ :key => :start_time, :order => :desc, :inverse_of => :check
59
+
60
+ has_sorted_set :states, :class_name => 'Flapjack::Data::State',
61
+ :key => :created_at, :order => :desc, :inverse_of => :check
62
+
63
+ # shortcut to expose the latest of the above to the API
64
+ has_one :current_state, :class_name => 'Flapjack::Data::State',
65
+ :inverse_of => :current_check
66
+
67
+ has_sorted_set :latest_notifications, :class_name => 'Flapjack::Data::State',
68
+ :key => :created_at, :order => :desc, :inverse_of => :latest_notifications_check,
69
+ :after_remove => :destroy_states
70
+
71
+ def self.destroy_states(check_id, *st_ids)
72
+ # states won't be deleted if still referenced elsewhere -- see the State
73
+ # before_destroy callback
74
+ Flapjack::Data::State.intersect(:id => st_ids).destroy_all
75
+ end
76
+
77
+ # the following associations are used internally, for the notification
78
+ # and alert queue inter-pikelet workflow
79
+ has_one :most_severe, :class_name => 'Flapjack::Data::State',
80
+ :inverse_of => :most_severe_check, :after_clear => :destroy_states
81
+
82
+ has_many :notifications, :class_name => 'Flapjack::Data::Notification',
83
+ :inverse_of => :check
84
+
85
+ has_many :alerts, :class_name => 'Flapjack::Data::Alert',
86
+ :inverse_of => :check
87
+
88
+ # this can be called from the API (with no args) or from notifier.rb
89
+ # (which will pass a severity to use, and an effective time)
90
+ def alerting_media(opts = {})
91
+ time = opts[:time] || Time.now
92
+ severity = opts[:severity] || self.condition
93
+
94
+ # return empty set if disabled, or in a maintenance period (for API only,
95
+ # these will have been checked already in processor if called by notifier)
96
+ if opts.empty?
97
+ unless self.enabled
98
+ return Flapjack::Data::Medium.empty
99
+ end
100
+
101
+ unless self.current_unscheduled_maintenance.nil?
102
+ return Flapjack::Data::Medium.empty
103
+ end
104
+
105
+ start_range = Zermelo::Filters::IndexRange.new(nil, time, :by_score => true)
106
+ end_range = Zermelo::Filters::IndexRange.new(time, nil, :by_score => true)
107
+
108
+ unless self.scheduled_maintenances.
109
+ intersect(:start_time => start_range, :end_time => end_range).empty?
110
+
111
+ return Flapjack::Data::Medium.empty
112
+ end
113
+ end
114
+
115
+ # determine matching acceptors
116
+ tag_ids = self.tags.ids
117
+
118
+ acceptor_ids = matching_rule_ids(tag_ids, :blackhole => false, :severity => severity)
119
+ acceptor_media_ids = Flapjack::Data::Rule.matching_media_ids(acceptor_ids,
120
+ :time => time)
121
+
122
+ return Flapjack::Data::Medium.empty if acceptor_media_ids.empty?
123
+
124
+ # and matching rejectors
125
+ rejector_ids = matching_rule_ids(tag_ids, :blackhole => true, :severity => severity)
126
+ rejector_media_ids = Flapjack::Data::Rule.matching_media_ids(rejector_ids,
127
+ :time => time)
128
+
129
+ unless rejector_media_ids.empty?
130
+ acceptor_media_ids -= rejector_media_ids
131
+ return Flapjack::Data::Medium.empty if acceptor_media_ids.empty?
132
+ end
133
+
134
+ Flapjack::Data::Medium.intersect(:id => acceptor_media_ids)
135
+ end
136
+
137
+ def contacts
138
+ # return empty set if disabled
139
+ return Flapjack::Data::Contact.empty unless self.enabled
140
+
141
+ # determine matching acceptors
142
+ tag_ids = self.tags.ids
143
+ time = Time.now
144
+
145
+ acceptor_ids = matching_rule_ids(tag_ids, :blackhole => false,)
146
+ acceptor_contact_ids = Flapjack::Data::Rule.matching_contact_ids(acceptor_ids,
147
+ :time => time)
148
+ return Flapjack::Data::Contact.empty if acceptor_contact_ids.empty?
149
+
150
+
151
+ # and matching rejectors
152
+ rejector_ids = matching_rule_ids(tag_ids, :blackhole => true)
153
+ rejector_contact_ids = Flapjack::Data::Rule.matching_contact_ids(rejector_ids,
154
+ :time => time)
155
+ unless rejector_contact_ids.empty?
156
+ acceptor_contact_ids -= rejector_contact_ids
157
+ return Flapjack::Data::Contact.empty if acceptor_contact_ids.empty?
158
+ end
159
+
160
+ Flapjack::Data::Contact.intersect(:id => acceptor_contact_ids)
161
+ end
162
+
163
+ def matching_rule_ids(tag_ids, opts = {})
164
+ severity = opts[:severity]
165
+ blackhole = opts[:blackhole]
166
+
167
+ matcher_by_strategy = {
168
+ 'global' => nil,
169
+ 'all_tags' => proc {|rule_tag_ids| (rule_tag_ids - tag_ids).empty? },
170
+ 'any_tag' => proc {|rule_tag_ids| !((rule_tag_ids & tag_ids).empty?) },
171
+ 'no_tag' => proc {|rule_tag_ids| (rule_tag_ids & tag_ids).empty? }
172
+ }
173
+
174
+ matcher_by_strategy.each_with_object(Set.new) do |(strategy, matcher), memo|
175
+ rules = Flapjack::Data::Rule.intersect(:enabled => true,
176
+ :blackhole => blackhole, :strategy => strategy)
177
+ unless severity.nil?
178
+ rules = rules.intersect(:conditions_list => [nil, /(?:^|,)#{severity}(?:,|$)/])
179
+ end
180
+
181
+ if matcher.nil?
182
+ memo.merge(rules.ids)
183
+ next
184
+ end
185
+
186
+ rules.associated_ids_for(:tags).each_pair do |rule_id, rule_tag_ids|
187
+ memo << rule_id if matcher.call(rule_tag_ids)
188
+ end
189
+ end
190
+ end
191
+
192
+ # end internal associations
193
+
194
+ validates :name, :presence => true
195
+ validates :enabled, :inclusion => {:in => [true, false]}
196
+
197
+ validates :condition, :presence => true, :unless => proc {|c| c.failing.nil? }
198
+ validates :failing, :inclusion => {:in => [true, false]},
199
+ :unless => proc {|c| c.condition.nil? }
200
+
201
+ validates :initial_failure_delay, :allow_nil => true,
202
+ :numericality => {:greater_than_or_equal_to => 0, :only_integer => true}
203
+
204
+ validates :repeat_failure_delay, :allow_nil => true,
205
+ :numericality => {:greater_than_or_equal_to => 0, :only_integer => true}
206
+
207
+ before_validation :create_ack_hash
208
+ validates :ack_hash, :presence => true
209
+
210
+ validates_with Flapjack::Data::Validators::IdValidator
211
+
212
+ attr_accessor :count
213
+
214
+ swagger_schema :Check do
215
+ key :required, [:id, :type, :name, :enabled, :initial_failure_delay,
216
+ :repeat_failure_delay, :failing]
217
+ property :id do
218
+ key :type, :string
219
+ key :format, :uuid
220
+ end
221
+ property :type do
222
+ key :type, :string
223
+ key :enum, [Flapjack::Data::Check.short_model_name.singular]
224
+ end
225
+ property :name do
226
+ key :type, :string
227
+ end
228
+ property :enabled do
229
+ key :type, :boolean
230
+ key :enum, [true, false]
231
+ end
232
+ property :initial_failure_delay do
233
+ key :type, :integer
234
+ end
235
+ property :repeat_failure_delay do
236
+ key :type, :integer
237
+ end
238
+ property :failing do
239
+ key :type, :boolean
240
+ key :enum, [true, false]
241
+ end
242
+ property :condition do
243
+ key :type, :string
244
+ key :enum, Flapjack::Data::Condition.healthy.keys +
245
+ Flapjack::Data::Condition.unhealthy.keys
246
+ end
247
+ property :ack_hash do
248
+ key :type, :string
249
+ end
250
+ property :relationships do
251
+ key :"$ref", :CheckLinks
252
+ end
253
+ end
254
+
255
+ swagger_schema :CheckLinks do
256
+ property :alerting_media do
257
+ key :"$ref", :MediaLinkage
258
+ end
259
+ property :contacts do
260
+ key :"$ref", :ContactsLinkage
261
+ end
262
+ property :current_scheduled_maintenances do
263
+ key :"$ref", :ScheduledMaintenancesLinkage
264
+ end
265
+ property :current_state do
266
+ key :"$ref", :StateLinkage
267
+ end
268
+ property :current_unscheduled_maintenance do
269
+ key :"$ref", :UnscheduledMaintenanceLinkage
270
+ end
271
+ property :latest_notifications do
272
+ key :"$ref", :StatesLinkage
273
+ end
274
+ property :scheduled_maintenances do
275
+ key :"$ref", :ScheduledMaintenancesLinkage
276
+ end
277
+ property :states do
278
+ key :"$ref", :StatesLinkage
279
+ end
280
+ property :tags do
281
+ key :"$ref", :TagsLinkage
282
+ end
283
+ property :unscheduled_maintenances do
284
+ key :"$ref", :UnscheduledMaintenancesLinkage
285
+ end
286
+ end
287
+
288
+ swagger_schema :CheckCreate do
289
+ key :required, [:type, :name, :enabled, :initial_failure_delay,
290
+ :repeat_failure_delay]
291
+ property :id do
292
+ key :type, :string
293
+ key :format, :uuid
294
+ end
295
+ property :type do
296
+ key :type, :string
297
+ key :enum, [Flapjack::Data::Check.short_model_name.singular]
298
+ end
299
+ property :name do
300
+ key :type, :string
301
+ end
302
+ property :enabled do
303
+ key :type, :boolean
304
+ key :enum, [true, false]
305
+ end
306
+ property :initial_failure_delay do
307
+ key :type, :integer
308
+ end
309
+ property :repeat_failure_delay do
310
+ key :type, :integer
311
+ end
312
+ property :relationships do
313
+ key :"$ref", :CheckCreateLinks
314
+ end
315
+ end
316
+
317
+ swagger_schema :CheckCreateLinks do
318
+ property :tags do
319
+ key :"$ref", :data_TagsReference
320
+ end
321
+ end
322
+
323
+ swagger_schema :CheckUpdate do
324
+ key :required, [:id, :type]
325
+ property :id do
326
+ key :type, :string
327
+ key :format, :uuid
328
+ end
329
+ property :type do
330
+ key :type, :string
331
+ key :enum, [Flapjack::Data::Check.short_model_name.singular]
332
+ end
333
+ property :name do
334
+ key :type, :string
335
+ end
336
+ property :enabled do
337
+ key :type, :boolean
338
+ key :enum, [true, false]
339
+ end
340
+ property :initial_failure_delay do
341
+ key :type, :integer
342
+ end
343
+ property :repeat_failure_delay do
344
+ key :type, :integer
345
+ end
346
+ property :relationships do
347
+ key :"$ref", :CheckUpdateLinks
348
+ end
349
+ end
350
+
351
+ swagger_schema :CheckUpdateLinks do
352
+ property :tags do
353
+ key :"$ref", :data_TagsReference
354
+ end
355
+ end
356
+
357
+ def self.swagger_included_classes
358
+ # hack -- hardcoding for now
359
+ [
360
+ Flapjack::Data::Check,
361
+ Flapjack::Data::Contact,
362
+ Flapjack::Data::Medium,
363
+ Flapjack::Data::Rule,
364
+ Flapjack::Data::ScheduledMaintenance,
365
+ Flapjack::Data::State,
366
+ Flapjack::Data::Tag,
367
+ Flapjack::Data::UnscheduledMaintenance
368
+ ]
369
+ end
370
+
371
+ def self.jsonapi_methods
372
+ @jsonapi_methods ||= {
373
+ :post => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
374
+ :attributes => [:name, :enabled, :initial_failure_delay,
375
+ :repeat_failure_delay],
376
+ :descriptions => {
377
+ :singular => "Create a check.",
378
+ :multiple => "Create checks."
379
+ }
380
+ ),
381
+ :get => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
382
+ :attributes => [:name, :enabled, :initial_failure_delay,
383
+ :repeat_failure_delay, :ack_hash, :failing, :condition],
384
+ :descriptions => {
385
+ :singular => "Returns data for a check.",
386
+ :multiple => "Returns data for multiple check records."
387
+ }
388
+ ),
389
+ :patch => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
390
+ :attributes => [:name, :enabled, :initial_failure_delay,
391
+ :repeat_failure_delay],
392
+ :descriptions => {
393
+ :singular => "Update a check.",
394
+ :multiple => "Update checks."
395
+ }
396
+ ),
397
+ :delete => Flapjack::Gateways::JSONAPI::Data::MethodDescriptor.new(
398
+ :descriptions => {
399
+ :singular => "Delete a check.",
400
+ :multiple => "Delete checks."
401
+ }
402
+ )
403
+ }
404
+ end
405
+
406
+ def self.jsonapi_associations
407
+ unless instance_variable_defined?('@jsonapi_associations')
408
+ @jsonapi_associations = {
409
+ :alerting_media => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
410
+ :get => true,
411
+ :number => :multiple, :link => true, :includable => true,
412
+ :type => 'medium',
413
+ :klass => Flapjack::Data::Medium,
414
+ :callback_classes => [
415
+ Flapjack::Data::Contact,
416
+ Flapjack::Data::Rule,
417
+ Flapjack::Data::Tag,
418
+ Flapjack::Data::ScheduledMaintenance
419
+ ],
420
+ :descriptions => {
421
+ :get => "While this check is failing, returns media records " \
422
+ "which are receiving notifications during this failure."
423
+ }
424
+ ),
425
+ :contacts => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
426
+ :get => true,
427
+ :number => :multiple, :link => true, :includable => true,
428
+ :type => 'contact',
429
+ :klass => Flapjack::Data::Contact,
430
+ :callback_classes => [
431
+ Flapjack::Data::Rule,
432
+ Flapjack::Data::Tag
433
+ ],
434
+ :descriptions => {
435
+ :get => "Returns contacts whose notification rules will " \
436
+ "allow them to receive notifications for events on " \
437
+ "this check."
438
+ }
439
+ ),
440
+ :current_scheduled_maintenances => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
441
+ :get => true,
442
+ :number => :multiple, :link => true, :includable => true,
443
+ :type => 'scheduled_maintenance',
444
+ :klass => Flapjack::Data::ScheduledMaintenance,
445
+ :descriptions => {
446
+ :get => "Returns scheduled maintenance periods currently in " \
447
+ " effect for this check."
448
+ }
449
+ ),
450
+ :current_state => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
451
+ :get => true,
452
+ :number => :singular, :link => true, :includable => true,
453
+ :descriptions => {
454
+ :get => "Returns the current State record for this check."
455
+ }
456
+ ),
457
+ :current_unscheduled_maintenance => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
458
+ :get => true,
459
+ :number => :singular, :link => true, :includable => true,
460
+ :type => 'unscheduled_maintenance',
461
+ :klass => Flapjack::Data::UnscheduledMaintenance,
462
+ :descriptions => {
463
+ :get => "If the check is currently acknowledged, returns the " \
464
+ "unscheduled maintenance period created for that."
465
+ }
466
+ ),
467
+ :latest_notifications => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
468
+ :get => true,
469
+ :number => :multiple, :link => true, :includable => true,
470
+ :descriptions => {
471
+ :get => "Returns the most recent State records for each " \
472
+ "problem condition that produced notifications."
473
+ }
474
+ ),
475
+ :scheduled_maintenances => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
476
+ :get => true,
477
+ :number => :multiple, :link => true, :includable => false,
478
+ :descriptions => {
479
+ :get => "Returns all scheduled maintenance periods for the " \
480
+ "check; default sort order is newest first."
481
+ }
482
+ ),
483
+ :states => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
484
+ :get => true,
485
+ :number => :multiple, :link => true, :includable => false,
486
+ :descriptions => {
487
+ :get => "Returns all state records for the check; default " \
488
+ "sort order is newest first."
489
+ }
490
+ ),
491
+ :tags => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
492
+ :post => true, :get => true, :patch => true, :delete => true,
493
+ :number => :multiple, :link => true, :includable => true,
494
+ :descriptions => {
495
+ :post => "Associate tags with this check.",
496
+ :get => "Returns all tags linked to this check.",
497
+ :patch => "Update the tags associated with this check.",
498
+ :delete => "Delete associations between tags and this check."
499
+ }
500
+ ),
501
+ :unscheduled_maintenances => Flapjack::Gateways::JSONAPI::Data::JoinDescriptor.new(
502
+ :get => true,
503
+ :number => :multiple, :link => true, :includable => false,
504
+ :descriptions => {
505
+ :get => "Returns all unscheduled maintenance periods for the " \
506
+ "check; default sort order is newest first."
507
+ }
508
+ )
509
+ }
510
+ populate_association_data(@jsonapi_associations)
511
+ end
512
+ @jsonapi_associations
513
+ end
514
+
515
+ def in_scheduled_maintenance?(t = Time.now)
516
+ !scheduled_maintenances_at(t).empty?
517
+ end
518
+
519
+ def current_scheduled_maintenances
520
+ scheduled_maintenances_at(Time.now)
521
+ end
522
+
523
+ def in_unscheduled_maintenance?(t = Time.now)
524
+ !unscheduled_maintenances_at(t).empty?
525
+ end
526
+
527
+ def current_unscheduled_maintenance
528
+ unscheduled_maintenances_at(Time.now).all.first
529
+ end
530
+
531
+ # TODO allow summary to be changed as part of the termination
532
+ def end_scheduled_maintenance(sched_maint, at_time)
533
+ at_time = Time.at(at_time) unless at_time.is_a?(Time)
534
+
535
+ if sched_maint.start_time >= at_time
536
+ # the scheduled maintenance period is in the future
537
+ self.scheduled_maintenances.remove(sched_maint)
538
+ sched_maint.destroy
539
+ return true
540
+ elsif sched_maint.end_time >= at_time
541
+ # it spans the current time, so we'll stop it at that point
542
+ sched_maint.end_time = at_time
543
+ sched_maint.save
544
+ return true
545
+ end
546
+
547
+ false
548
+ end
549
+
550
+ def set_unscheduled_maintenance(unsched_maint, options = {})
551
+ current_time = Time.now
552
+
553
+ self.class.lock(Flapjack::Data::UnscheduledMaintenance,
554
+ Flapjack::Data::State) do
555
+
556
+ self.alertable = false
557
+ self.save!
558
+
559
+ # time_remaining
560
+ if (unsched_maint.end_time - current_time) > 0
561
+ self.clear_unscheduled_maintenance(unsched_maint.start_time)
562
+ end
563
+
564
+ self.unscheduled_maintenances << unsched_maint
565
+ end
566
+ end
567
+
568
+ def clear_unscheduled_maintenance(end_time)
569
+ Flapjack::Data::UnscheduledMaintenance.lock do
570
+ t = Time.now
571
+ start_range = Zermelo::Filters::IndexRange.new(nil, t, :by_score => true)
572
+ end_range = Zermelo::Filters::IndexRange.new(t, nil, :by_score => true)
573
+ unsched_maints = self.unscheduled_maintenances.intersect(:start_time => start_range,
574
+ :end_time => end_range)
575
+ unsched_maints_count = unsched_maints.empty?
576
+ unless unsched_maints_count == 0
577
+ # FIXME log warning if count > 1
578
+ unsched_maints.each do |usm|
579
+ usm.end_time = end_time
580
+ usm.save
581
+ end
582
+ end
583
+ end
584
+ end
585
+
586
+ private
587
+
588
+ # would need to be "#{entity.name}:#{name}" to be compatible with v1, but
589
+ # to support name changes it must be something invariant
590
+ def create_ack_hash
591
+ return unless self.ack_hash.nil? # :on => :create isn't working
592
+ self.id = self.class.generate_id if self.id.nil?
593
+ self.ack_hash = Digest.hexencode(Digest::SHA1.new.digest(self.id))[0..7].downcase
594
+ end
595
+
596
+ def scheduled_maintenances_at(t)
597
+ start_range = Zermelo::Filters::IndexRange.new(nil, t, :by_score => true)
598
+ end_range = Zermelo::Filters::IndexRange.new(t, nil, :by_score => true)
599
+ self.scheduled_maintenances.intersect(:start_time => start_range,
600
+ :end_time => end_range)
601
+ end
602
+
603
+ def unscheduled_maintenances_at(t)
604
+ start_range = Zermelo::Filters::IndexRange.new(nil, t, :by_score => true)
605
+ end_range = Zermelo::Filters::IndexRange.new(t, nil, :by_score => true)
606
+ self.unscheduled_maintenances.intersect(:start_time => start_range,
607
+ :end_time => end_range)
608
+ end
609
+ end
610
+ end
611
+ end