flapjack 0.5.5 → 0.6.23
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.
- data/.gitignore +10 -0
- data/.rbenv-version +1 -0
- data/.rspec +10 -0
- data/Gemfile +18 -0
- data/Guardfile +14 -0
- data/README.md +152 -173
- data/Rakefile +53 -150
- data/bin/flapjack +72 -0
- data/bin/flapjack-nagios-receiver +111 -0
- data/bin/flapjack-nagios-receiver-control +15 -0
- data/bin/flapjack-netsaint-parser +0 -2
- data/bin/flapjack-populator +133 -16
- data/bin/install-flapjack-systemwide +2 -2
- data/config.ru +11 -0
- data/dist/etc/init.d/flapjack +46 -0
- data/dist/etc/init.d/flapjack-nagios-receiver +36 -0
- data/doc/GLOSSARY.md +19 -0
- data/etc/flapjack_config.yaml.example +90 -0
- data/features/events.feature +132 -0
- data/features/notifications.feature +57 -0
- data/features/packaging-lintian.feature +5 -3
- data/features/steps/events_steps.rb +164 -0
- data/features/steps/flapjack-importer_steps.rb +2 -5
- data/features/steps/flapjack-worker_steps.rb +13 -6
- data/features/steps/notifications_steps.rb +178 -0
- data/features/steps/packaging-lintian_steps.rb +14 -0
- data/features/steps/time_travel_steps.rb +34 -0
- data/features/support/env.rb +63 -36
- data/flapjack.gemspec +35 -186
- data/lib/flapjack.rb +2 -0
- data/lib/flapjack/api.rb +274 -0
- data/lib/flapjack/api/entity_check_presenter.rb +184 -0
- data/lib/flapjack/api/entity_presenter.rb +66 -0
- data/lib/flapjack/cli/worker_manager.rb +1 -2
- data/lib/flapjack/configuration.rb +11 -0
- data/lib/flapjack/coordinator.rb +288 -0
- data/lib/flapjack/daemonizing.rb +186 -0
- data/lib/flapjack/data/contact.rb +45 -0
- data/lib/flapjack/data/entity.rb +89 -0
- data/lib/flapjack/data/entity_check.rb +396 -0
- data/lib/flapjack/data/event.rb +144 -0
- data/lib/flapjack/data/notification.rb +13 -0
- data/lib/flapjack/executive.rb +289 -0
- data/lib/flapjack/filters/acknowledgement.rb +39 -0
- data/lib/flapjack/filters/{any_parents_failed.rb → base.rb} +6 -4
- data/lib/flapjack/filters/delays.rb +53 -0
- data/lib/flapjack/filters/detect_mass_client_failures.rb +44 -0
- data/lib/flapjack/filters/ok.rb +25 -5
- data/lib/flapjack/filters/scheduled_maintenance.rb +17 -0
- data/lib/flapjack/filters/unscheduled_maintenance.rb +17 -0
- data/lib/flapjack/jabber.rb +294 -0
- data/lib/flapjack/notification/common.rb +23 -0
- data/lib/flapjack/notification/email.rb +107 -0
- data/lib/flapjack/notification/email/alert.html.haml +48 -0
- data/lib/flapjack/notification/email/alert.text.erb +14 -0
- data/lib/flapjack/notification/sms.rb +42 -0
- data/lib/flapjack/notification/sms/messagenet.rb +49 -0
- data/lib/flapjack/notifier_engine.rb +4 -4
- data/lib/flapjack/notifiers/mailer/mailer.rb +6 -7
- data/lib/flapjack/notifiers/xmpp/xmpp.rb +12 -12
- data/lib/flapjack/pagerduty.rb +230 -0
- data/lib/flapjack/patches.rb +108 -19
- data/lib/flapjack/persistence/data_mapper/models/check.rb +5 -3
- data/lib/flapjack/persistence/data_mapper/models/check_template.rb +2 -0
- data/lib/flapjack/persistence/data_mapper/models/event.rb +2 -0
- data/lib/flapjack/persistence/data_mapper/models/node.rb +3 -1
- data/lib/flapjack/persistence/data_mapper/models/related_check.rb +3 -1
- data/lib/flapjack/pikelet.rb +56 -0
- data/lib/flapjack/transports/beanstalkd.rb +1 -1
- data/lib/flapjack/transports/result.rb +6 -6
- data/lib/flapjack/utility.rb +46 -0
- data/lib/flapjack/version.rb +5 -0
- data/lib/flapjack/web.rb +198 -0
- data/lib/flapjack/web/views/acknowledge.haml +55 -0
- data/lib/flapjack/web/views/check.haml +162 -0
- data/lib/flapjack/web/views/index.haml +92 -0
- data/lib/flapjack/web/views/self_stats.haml +56 -0
- data/lib/flapjack/{applications/worker.rb → worker/application.rb} +0 -0
- data/lib/flapjack/worker/cli.rb +49 -0
- data/{spec → spec.old}/check_sandbox/echo +0 -0
- data/{spec → spec.old}/check_sandbox/sandboxed_check +0 -0
- data/{spec → spec.old}/configs/flapjack-notifier-couchdb.ini +0 -0
- data/{spec → spec.old}/configs/flapjack-notifier.ini +0 -0
- data/{spec → spec.old}/configs/recipients.ini +0 -0
- data/{spec → spec.old}/helpers.rb +0 -0
- data/{spec → spec.old}/inifile_spec.rb +0 -0
- data/{spec → spec.old}/mock-notifiers/mock/init.rb +0 -0
- data/{spec → spec.old}/mock-notifiers/mock/mock.rb +0 -0
- data/{spec → spec.old}/notifier-directories/spoons/testmailer/init.rb +0 -0
- data/{spec → spec.old}/notifier_application_spec.rb +0 -0
- data/{spec → spec.old}/notifier_filters_spec.rb +0 -0
- data/{spec → spec.old}/notifier_options_multiplexer_spec.rb +0 -0
- data/{spec → spec.old}/notifier_options_spec.rb +0 -0
- data/{spec → spec.old}/notifier_spec.rb +0 -0
- data/{spec → spec.old}/notifiers/mailer_spec.rb +0 -0
- data/{spec → spec.old}/notifiers/xmpp_spec.rb +0 -0
- data/{spec → spec.old}/persistence/datamapper_spec.rb +0 -0
- data/{spec → spec.old}/persistence/mock_persistence_backend.rb +0 -0
- data/{spec → spec.old}/simple.ini +0 -0
- data/{spec → spec.old}/spec.opts +0 -0
- data/{spec → spec.old}/test-filters/blocker.rb +0 -0
- data/{spec → spec.old}/test-filters/mock.rb +0 -0
- data/{spec → spec.old}/transports/beanstalkd_spec.rb +0 -0
- data/{spec → spec.old}/transports/mock_transport.rb +0 -0
- data/{spec → spec.old}/worker_application_spec.rb +0 -0
- data/{spec → spec.old}/worker_options_spec.rb +0 -0
- data/spec/lib/flapjack/api/entity_check_presenter_spec.rb +117 -0
- data/spec/lib/flapjack/api/entity_presenter_spec.rb +92 -0
- data/spec/lib/flapjack/api_spec.rb +170 -0
- data/spec/lib/flapjack/coordinator_spec.rb +16 -0
- data/spec/lib/flapjack/data/entity_check_spec.rb +398 -0
- data/spec/lib/flapjack/data/entity_spec.rb +71 -0
- data/spec/lib/flapjack/data/event_spec.rb +6 -0
- data/spec/lib/flapjack/executive_spec.rb +59 -0
- data/spec/lib/flapjack/filters/acknowledgement_spec.rb +6 -0
- data/spec/lib/flapjack/filters/delays_spec.rb +6 -0
- data/spec/lib/flapjack/filters/detect_mass_client_failures_spec.rb +6 -0
- data/spec/lib/flapjack/filters/ok_spec.rb +6 -0
- data/spec/lib/flapjack/filters/scheduled_maintenance_spec.rb +6 -0
- data/spec/lib/flapjack/filters/unscheduled_maintenance_spec.rb +6 -0
- data/spec/lib/flapjack/jabber_spec.rb +150 -0
- data/spec/lib/flapjack/notification/email_spec.rb +6 -0
- data/spec/lib/flapjack/notification/sms_spec.rb +6 -0
- data/spec/lib/flapjack/pikelet_spec.rb +28 -0
- data/spec/lib/flapjack/web_spec.rb +188 -0
- data/spec/spec_helper.rb +44 -0
- data/spec/support/profile_all_formatter.rb +44 -0
- data/spec/support/uncolored_doc_formatter.rb +9 -0
- data/tasks/events.rake +85 -0
- data/tmp/acknowledge.rb +14 -0
- data/tmp/create_config_yaml.rb +16 -0
- data/tmp/create_events_failure.rb +33 -0
- data/tmp/create_events_ok.rb +33 -0
- data/tmp/create_events_ok_fail_ack_ok.rb +54 -0
- data/tmp/create_events_ok_failure.rb +40 -0
- data/tmp/create_events_ok_failure_ack.rb +54 -0
- data/tmp/dummy_entities.json +1 -0
- data/tmp/generate_nagios_test_hosts.rb +16 -0
- data/tmp/parse_config_yaml.rb +7 -0
- data/tmp/redis_delete_all_keys.rb +11 -0
- data/tmp/test_entities.json +1 -0
- metadata +482 -221
- data/TODO.md +0 -36
- data/VERSION +0 -1
- data/bin/flapjack-benchmark +0 -50
- data/bin/flapjack-notifier +0 -21
- data/bin/flapjack-notifier-manager +0 -43
- data/bin/flapjack-stats +0 -27
- data/bin/flapjack-worker +0 -13
- data/bin/flapjack-worker-manager +0 -35
- data/dist/etc/init.d/flapjack-notifier +0 -47
- data/dist/etc/init.d/flapjack-workers +0 -44
- data/features/flapjack-notifier-manager.feature +0 -19
- data/features/flapjack-worker-manager.feature +0 -27
- data/features/flapjack-worker.feature +0 -27
- data/features/netsaint-config-converter.feature +0 -126
- data/features/persistence/couch.feature +0 -105
- data/features/persistence/sqlite3.feature +0 -105
- data/features/persistence/steps/couch_steps.rb +0 -25
- data/features/persistence/steps/generic_steps.rb +0 -102
- data/features/persistence/steps/sqlite3_steps.rb +0 -13
- data/features/steps/flapjack-notifier-manager_steps.rb +0 -24
- data/features/steps/flapjack-worker-manager_steps.rb +0 -48
- data/lib/flapjack/applications/notifier.rb +0 -222
- data/lib/flapjack/cli/notifier.rb +0 -108
- data/lib/flapjack/cli/notifier_manager.rb +0 -86
- data/lib/flapjack/cli/worker.rb +0 -51
data/lib/flapjack.rb
CHANGED
data/lib/flapjack/api.rb
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# A HTTP-based API server, which provides queries to determine the status of
|
|
4
|
+
# entities and the checks that are reported against them.
|
|
5
|
+
#
|
|
6
|
+
# There's a matching flapjack-diner gem which consumes data from this API
|
|
7
|
+
# (currently at https://github.com/ali-graham/flapjack-diner -- this will change.)
|
|
8
|
+
|
|
9
|
+
require 'time'
|
|
10
|
+
|
|
11
|
+
require 'rack/fiber_pool'
|
|
12
|
+
require 'sinatra/base'
|
|
13
|
+
|
|
14
|
+
require 'flapjack/pikelet'
|
|
15
|
+
|
|
16
|
+
require 'flapjack/api/entity_presenter'
|
|
17
|
+
|
|
18
|
+
require 'flapjack/data/entity'
|
|
19
|
+
require 'flapjack/data/entity_check'
|
|
20
|
+
|
|
21
|
+
module Flapjack
|
|
22
|
+
|
|
23
|
+
class API < Sinatra::Base
|
|
24
|
+
|
|
25
|
+
# doesn't work with Rack::Test for some reason
|
|
26
|
+
unless 'test'.eql?(FLAPJACK_ENV)
|
|
27
|
+
rescue_exception = Proc.new { |env, exception|
|
|
28
|
+
[503, {}, exception.message]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
use Rack::FiberPool, :size => 25, :rescue_exception => rescue_exception
|
|
32
|
+
end
|
|
33
|
+
use Rack::MethodOverride
|
|
34
|
+
extend Flapjack::Pikelet
|
|
35
|
+
|
|
36
|
+
before do
|
|
37
|
+
# will only initialise the first time it's run
|
|
38
|
+
Flapjack::API.bootstrap
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
helpers do
|
|
42
|
+
def json_status(code, reason)
|
|
43
|
+
status code
|
|
44
|
+
{:status => code, :reason => reason}.to_json
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def logger
|
|
48
|
+
Flapjack::API.logger
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
get '/entities' do
|
|
53
|
+
content_type :json
|
|
54
|
+
ret = Flapjack::Data::Entity.all(:redis => @@redis).sort_by(&:name).collect {|e|
|
|
55
|
+
{'id' => e.id, 'name' => e.name,
|
|
56
|
+
'checks' => e.check_list.sort.collect {|c|
|
|
57
|
+
entity_check_status(e, c)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
ret.to_json
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
get '/checks/:entity' do
|
|
65
|
+
content_type :json
|
|
66
|
+
entity = Flapjack::Data::Entity.find_by_name(params[:entity], :redis => @@redis)
|
|
67
|
+
if entity.nil?
|
|
68
|
+
status 404
|
|
69
|
+
return
|
|
70
|
+
end
|
|
71
|
+
entity.check_list.to_json
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
get %r{/status/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(\w+))?} do
|
|
75
|
+
content_type :json
|
|
76
|
+
|
|
77
|
+
entity_name = params[:captures][0]
|
|
78
|
+
check = params[:captures][1]
|
|
79
|
+
|
|
80
|
+
entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => @@redis)
|
|
81
|
+
if entity.nil?
|
|
82
|
+
status 404
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
ret = if check
|
|
87
|
+
entity_check_status(entity, check)
|
|
88
|
+
else
|
|
89
|
+
entity.check_list.sort.collect {|c|
|
|
90
|
+
entity_check_status(entity, c)
|
|
91
|
+
}
|
|
92
|
+
end
|
|
93
|
+
ret.to_json
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# the first capture group in the regex checks for acceptable
|
|
97
|
+
# characters in a domain name -- this will also match strings
|
|
98
|
+
# that aren't acceptable domain names as well, of course.
|
|
99
|
+
get %r{/outages/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(\w+))?} do
|
|
100
|
+
content_type :json
|
|
101
|
+
|
|
102
|
+
entity_name = params[:captures][0]
|
|
103
|
+
check = params[:captures][1]
|
|
104
|
+
|
|
105
|
+
entity = entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => @@redis)
|
|
106
|
+
if entity.nil?
|
|
107
|
+
status 404
|
|
108
|
+
return
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
start_time = validate_and_parsetime(params[:start_time])
|
|
112
|
+
end_time = validate_and_parsetime(params[:end_time])
|
|
113
|
+
|
|
114
|
+
presenter = if check
|
|
115
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
|
116
|
+
check, :redis => @@redis)
|
|
117
|
+
Flapjack::API::EntityCheckPresenter.new(entity_check)
|
|
118
|
+
else
|
|
119
|
+
Flapjack::API::EntityPresenter.new(entity, :redis => @@redis)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
presenter.outages(start_time, end_time).to_json
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
get %r{/unscheduled_maintenances/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(\w+))?} do
|
|
126
|
+
content_type :json
|
|
127
|
+
|
|
128
|
+
entity_name = params[:captures][0]
|
|
129
|
+
check = params[:captures][1]
|
|
130
|
+
|
|
131
|
+
entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => @@redis)
|
|
132
|
+
if entity.nil?
|
|
133
|
+
status 404
|
|
134
|
+
return
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
start_time = validate_and_parsetime(params[:start_time])
|
|
138
|
+
end_time = validate_and_parsetime(params[:end_time])
|
|
139
|
+
|
|
140
|
+
presenter = if check
|
|
141
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
|
142
|
+
check, :redis => @@redis)
|
|
143
|
+
Flapjack::API::EntityCheckPresenter.new(entity_check)
|
|
144
|
+
else
|
|
145
|
+
Flapjack::API::EntityPresenter.new(entity, :redis => @@redis)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
presenter.unscheduled_maintenance(start_time, end_time).to_json
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
get %r{/scheduled_maintenances/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(\w+))?} do
|
|
152
|
+
content_type :json
|
|
153
|
+
|
|
154
|
+
entity_name = params[:captures][0]
|
|
155
|
+
check = params[:captures][1]
|
|
156
|
+
|
|
157
|
+
entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => @@redis)
|
|
158
|
+
if entity.nil?
|
|
159
|
+
status 404
|
|
160
|
+
return
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
start_time = validate_and_parsetime(params[:start_time])
|
|
164
|
+
end_time = validate_and_parsetime(params[:end_time])
|
|
165
|
+
|
|
166
|
+
presenter = if check
|
|
167
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
|
168
|
+
check, :redis => @@redis)
|
|
169
|
+
Flapjack::API::EntityCheckPresenter.new(entity_check)
|
|
170
|
+
else
|
|
171
|
+
Flapjack::API::EntityPresenter.new(entity, :redis => @@redis)
|
|
172
|
+
end
|
|
173
|
+
presenter.scheduled_maintenance(start_time, end_time).to_json
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
get %r{/downtime/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(\w+))?} do
|
|
177
|
+
content_type :json
|
|
178
|
+
|
|
179
|
+
entity_name = params[:captures][0]
|
|
180
|
+
check = params[:captures][1]
|
|
181
|
+
|
|
182
|
+
entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => @@redis)
|
|
183
|
+
if entity.nil?
|
|
184
|
+
status 404
|
|
185
|
+
return
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
start_time = validate_and_parsetime(params[:start_time])
|
|
189
|
+
end_time = validate_and_parsetime(params[:end_time])
|
|
190
|
+
|
|
191
|
+
presenter = if check
|
|
192
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
|
193
|
+
check, :redis => @@redis)
|
|
194
|
+
Flapjack::API::EntityCheckPresenter.new(entity_check)
|
|
195
|
+
else
|
|
196
|
+
Flapjack::API::EntityPresenter.new(entity, :redis => @@redis)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
presenter.downtime(start_time, end_time).to_json
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# create a scheduled maintenance period for a service on an entity
|
|
203
|
+
post '/scheduled_maintenances/:entity/:check' do
|
|
204
|
+
content_type :json
|
|
205
|
+
entity = Flapjack::Data::Entity.find_by_name(params[:entity], :redis => @@redis)
|
|
206
|
+
if entity.nil?
|
|
207
|
+
status 404
|
|
208
|
+
return
|
|
209
|
+
end
|
|
210
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
|
211
|
+
params[:check], :redis => @@redis)
|
|
212
|
+
entity_check.create_scheduled_maintenance(:start_time => params[:start_time],
|
|
213
|
+
:duration => params[:duration], :summary => params[:summary])
|
|
214
|
+
status 201
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# create an acknowledgement for a service on an entity
|
|
218
|
+
# NB currently, this does not acknowledge a specific failure event, just
|
|
219
|
+
# the entity-check as a whole
|
|
220
|
+
post '/acknowledgements/:entity/:check' do
|
|
221
|
+
content_type :json
|
|
222
|
+
entity = Flapjack::Data::Entity.find_by_name(params[:entity], :redis => @@redis)
|
|
223
|
+
if entity.nil?
|
|
224
|
+
status 404
|
|
225
|
+
return
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
dur = params[:duration] ? params[:duration].to_i : nil
|
|
229
|
+
duration = (dur.nil? || (dur <= 0)) ? (4 * 60 * 60) : dur
|
|
230
|
+
|
|
231
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
|
232
|
+
params[:check], :redis => @@redis)
|
|
233
|
+
entity_check.create_acknowledgement('summary' => params[:summary],
|
|
234
|
+
'duration' => duration)
|
|
235
|
+
status 201
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
not_found do
|
|
239
|
+
json_status 404, "Not found"
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
error do
|
|
243
|
+
json_status 500, env['sinatra.error'].message
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
private
|
|
247
|
+
|
|
248
|
+
def entity_check_status(entity, check)
|
|
249
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
|
250
|
+
check, :redis => @@redis)
|
|
251
|
+
return if entity_check.nil?
|
|
252
|
+
{ 'name' => check,
|
|
253
|
+
'state' => entity_check.state,
|
|
254
|
+
'in_unscheduled_maintenance' => entity_check.in_unscheduled_maintenance?,
|
|
255
|
+
'in_scheduled_maintenance' => entity_check.in_scheduled_maintenance?,
|
|
256
|
+
'last_update' => entity_check.last_update,
|
|
257
|
+
'last_problem_notification' => entity_check.last_problem_notification,
|
|
258
|
+
'last_recovery_notification' => entity_check.last_recovery_notification,
|
|
259
|
+
'last_acknowledgement_notification' => entity_check.last_acknowledgement_notification
|
|
260
|
+
}
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# NB: casts to UTC before converting to a timestamp
|
|
264
|
+
def validate_and_parsetime(value)
|
|
265
|
+
return unless value
|
|
266
|
+
Time.iso8601(value).getutc.to_i
|
|
267
|
+
rescue ArgumentError => e
|
|
268
|
+
# FIXME log error
|
|
269
|
+
nil
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
# Formats entity/check data for presentation by the API methods in Flapjack::API.
|
|
4
|
+
|
|
5
|
+
require 'sinatra/base'
|
|
6
|
+
|
|
7
|
+
require 'flapjack/data/entity_check'
|
|
8
|
+
|
|
9
|
+
module Flapjack
|
|
10
|
+
|
|
11
|
+
class API < Sinatra::Base
|
|
12
|
+
|
|
13
|
+
class EntityCheckPresenter
|
|
14
|
+
|
|
15
|
+
def initialize(entity_check)
|
|
16
|
+
@entity_check = entity_check
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# if options[:chop] is true, overlapping outages at start and end
|
|
20
|
+
# times will be sliced to fit.
|
|
21
|
+
def outages(start_time, end_time, options = {})
|
|
22
|
+
# states is an array of hashes, with [state, timestamp, summary] keys
|
|
23
|
+
states = @entity_check.historical_states(start_time, end_time)
|
|
24
|
+
return states if states.empty?
|
|
25
|
+
|
|
26
|
+
# if it started failed, prepend the earlier event
|
|
27
|
+
initial = @entity_check.historical_state_before(states.first[:timestamp])
|
|
28
|
+
if (initial && (initial[:state] == Flapjack::Data::EntityCheck::STATE_CRITICAL))
|
|
29
|
+
states.unshift(initial)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# if it ended failed, append the event when it recovered
|
|
33
|
+
if states.last[:state] == Flapjack::Data::EntityCheck::STATE_CRITICAL
|
|
34
|
+
# TODO ensure this event is not CRITICAL, get first non-CRITICAL if so
|
|
35
|
+
last = @entity_check.historical_state_after(states.last[:timestamp])
|
|
36
|
+
states.push(last) if last
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
last_state = nil
|
|
40
|
+
|
|
41
|
+
# returns an array of hashes, with [:start_time, :end_time, :summary]
|
|
42
|
+
time_periods = states.inject([]) do |ret, obj|
|
|
43
|
+
if (obj[:state] == Flapjack::Data::EntityCheck::STATE_CRITICAL) &&
|
|
44
|
+
(last_state.nil? || (last_state != Flapjack::Data::EntityCheck::STATE_CRITICAL))
|
|
45
|
+
|
|
46
|
+
# flipped to failed, mark next outage
|
|
47
|
+
last_state = obj[:state]
|
|
48
|
+
ret << {:start_time => obj[:timestamp], :end_time => nil, :summary => obj[:summary]}
|
|
49
|
+
elsif (obj[:state] != Flapjack::Data::EntityCheck::STATE_CRITICAL) &&
|
|
50
|
+
(last_state == Flapjack::Data::EntityCheck::STATE_CRITICAL)
|
|
51
|
+
|
|
52
|
+
# flipped to not failed, mark end time for the current outage
|
|
53
|
+
last_state = obj[:state]
|
|
54
|
+
ret.last[:end_time] = obj[:timestamp]
|
|
55
|
+
end
|
|
56
|
+
ret
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if options[:chop]
|
|
60
|
+
if start_time && (time_periods.first[:start_time] < start_time)
|
|
61
|
+
time_periods.first[:start_time] = start_time
|
|
62
|
+
end
|
|
63
|
+
if time_periods.last[:end_time].nil? || (end_time && (time_periods.last[:end_time] > end_time))
|
|
64
|
+
time_periods.last[:end_time] = (end_time || Time.now.to_i)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
time_periods
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def unscheduled_maintenance(start_time, end_time)
|
|
72
|
+
# unsched_maintenance is an array of hashes, with [duration, timestamp, summary] keys
|
|
73
|
+
unsched_maintenance = @entity_check.maintenances(start_time, end_time,
|
|
74
|
+
:scheduled => false)
|
|
75
|
+
|
|
76
|
+
# to see if we start in an unscheduled maintenance period, we must check all unscheduled
|
|
77
|
+
# maintenances before the period and their durations
|
|
78
|
+
start_in_unsched = start_time.nil? ? [] :
|
|
79
|
+
@entity_check.maintenances(nil, start_time, :scheduled => false).select {|pu|
|
|
80
|
+
pu[:end_time] >= start_time
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
start_in_unsched + unsched_maintenance
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def scheduled_maintenance(start_time, end_time)
|
|
87
|
+
# sched_maintenance is an array of hashes, with [duration, timestamp, summary] keys
|
|
88
|
+
sched_maintenance = @entity_check.maintenances(start_time, end_time,
|
|
89
|
+
:scheduled => true)
|
|
90
|
+
|
|
91
|
+
# to see if we start in a scheduled maintenance period, we must check all scheduled
|
|
92
|
+
# maintenances before the period and their durations
|
|
93
|
+
start_in_sched = start_time.nil? ? [] :
|
|
94
|
+
@entity_check.maintenances(nil, start_time, :scheduled => true).select {|ps|
|
|
95
|
+
ps[:end_time] >= start_time
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
start_in_sched + sched_maintenance
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# TODO test whether the below overlapping logic is prone to off-by-one
|
|
102
|
+
# errors; the numbers may line up more neatly if we consider outages to
|
|
103
|
+
# start one second after the maintenance period ends.
|
|
104
|
+
#
|
|
105
|
+
# TODO test performance with larger data sets
|
|
106
|
+
def downtime(start_time, end_time)
|
|
107
|
+
sched_maintenances = scheduled_maintenance(start_time, end_time)
|
|
108
|
+
|
|
109
|
+
outs = outages(start_time, end_time, :chop => true)
|
|
110
|
+
|
|
111
|
+
total_secs = 0
|
|
112
|
+
percentage = 0
|
|
113
|
+
|
|
114
|
+
unless outs.empty?
|
|
115
|
+
|
|
116
|
+
# Initially we need to check for cases where a scheduled
|
|
117
|
+
# maintenance period is fully covered by an outage period.
|
|
118
|
+
# We then create two new outage periods to cover the time around
|
|
119
|
+
# the scheduled maintenance period, and remove the original.
|
|
120
|
+
|
|
121
|
+
sched_maintenances.each do |sm|
|
|
122
|
+
|
|
123
|
+
split_outs = []
|
|
124
|
+
|
|
125
|
+
outs.each { |o|
|
|
126
|
+
next unless o[:start_time] < sm[:start_time] &&
|
|
127
|
+
o[:end_time] > sm[:end_time]
|
|
128
|
+
o[:delete] = true
|
|
129
|
+
split_outs += [{:start_time => o[:start_time],
|
|
130
|
+
:end_time => sm[:start_time],
|
|
131
|
+
:summary => "#{o[:summary]} [split start]"},
|
|
132
|
+
{:start_time => sm[:end_time],
|
|
133
|
+
:end_time => o[:end_time],
|
|
134
|
+
:summary => "#{o[:summary]} [split finish]"}]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
outs.reject! {|o| o[:delete]}
|
|
138
|
+
outs += split_outs
|
|
139
|
+
# not strictly necessary to keep the data sorted, but
|
|
140
|
+
# will make more sense while debgging
|
|
141
|
+
outs.sort! {|a,b| a[:start_time] <=> b[:start_time]}
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
sched_maintenances.each do |sm|
|
|
145
|
+
|
|
146
|
+
outs.each do |o|
|
|
147
|
+
next unless (sm[:start_time] < o[:end_time]) &&
|
|
148
|
+
(sm[:end_time] > o[:start_time])
|
|
149
|
+
|
|
150
|
+
if sm[:start_time] <= o[:start_time] &&
|
|
151
|
+
sm[:end_time] >= o[:end_time]
|
|
152
|
+
|
|
153
|
+
# outage is fully overlapped by the scheduled maintenance
|
|
154
|
+
o[:delete] = true
|
|
155
|
+
|
|
156
|
+
elsif sm[:start_time] <= o[:start_time]
|
|
157
|
+
# partially overlapping on the earlier side
|
|
158
|
+
o[:start_time] = sm[:end_time]
|
|
159
|
+
elsif sm[:end_time] >= o[:end_time]
|
|
160
|
+
# partially overlapping on the later side
|
|
161
|
+
o[:end_time] = sm[:start_time]
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
outs.reject! {|o| o[:delete]}
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# sum outage times
|
|
169
|
+
total_secs = outs.inject(0) {|sum, o|
|
|
170
|
+
sum += (o[:end_time] - o[:start_time])
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
percentage = (start_time.nil? || end_time.nil?) ? nil :
|
|
174
|
+
(total_secs * 100) / (end_time - start_time)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
{:total_seconds => total_secs, :percentage => percentage, :downtime => outs}
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
end
|