flapjack 0.7.35 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -1
- data/Gemfile +3 -4
- data/Guardfile +1 -1
- data/README.md +38 -19
- data/Rakefile +1 -3
- data/etc/flapjack_config.yaml.example +11 -1
- data/features/steps/cli_steps.rb +3 -3
- data/features/steps/events_steps.rb +7 -6
- data/features/steps/flapjack-netsaint-parser_steps.rb +8 -8
- data/features/steps/notifications_steps.rb +10 -10
- data/features/steps/packaging-lintian_steps.rb +5 -9
- data/features/steps/time_travel_steps.rb +1 -1
- data/flapjack.gemspec +4 -3
- data/lib/flapjack/data/contact.rb +78 -6
- data/lib/flapjack/data/entity.rb +11 -2
- data/lib/flapjack/data/notification_rule.rb +67 -59
- data/lib/flapjack/data/semaphore.rb +44 -0
- data/lib/flapjack/gateways/api.rb +24 -28
- data/lib/flapjack/gateways/api/contact_methods.rb +1 -2
- data/lib/flapjack/gateways/api/entity_methods.rb +3 -3
- data/lib/flapjack/gateways/jsonapi.rb +249 -0
- data/lib/flapjack/gateways/jsonapi/contact_methods.rb +544 -0
- data/lib/flapjack/gateways/jsonapi/entity_check_presenter.rb +217 -0
- data/lib/flapjack/gateways/jsonapi/entity_methods.rb +350 -0
- data/lib/flapjack/gateways/jsonapi/entity_presenter.rb +75 -0
- data/lib/flapjack/gateways/jsonapi/rack/json_params_parser.rb +32 -0
- data/lib/flapjack/gateways/web.rb +78 -12
- data/lib/flapjack/gateways/web/public/css/bootstrap-theme.css +397 -0
- data/lib/flapjack/gateways/web/public/css/bootstrap-theme.min.css +7 -0
- data/lib/flapjack/gateways/web/public/css/bootstrap.css +7118 -0
- data/lib/flapjack/gateways/web/public/css/bootstrap.min.css +6 -8
- data/lib/flapjack/gateways/web/public/css/font-awesome.css +1338 -0
- data/lib/flapjack/gateways/web/public/css/font-awesome.min.css +4 -0
- data/lib/flapjack/gateways/web/public/css/screen.css +80 -0
- data/lib/flapjack/gateways/web/public/css/select2-bootstrap.css +87 -0
- data/lib/flapjack/gateways/web/public/css/select2.css +615 -0
- data/lib/flapjack/gateways/web/public/fonts/FontAwesome.otf +0 -0
- data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.eot +0 -0
- data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.svg +414 -0
- data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.ttf +0 -0
- data/lib/flapjack/gateways/web/public/fonts/fontawesome-webfont.woff +0 -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 +229 -0
- 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/img/flapjack-2013-notext-transparent-300-300.png +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-min.js +2 -0
- data/lib/flapjack/gateways/web/public/js/backbone.js +1581 -0
- data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +75 -0
- data/lib/flapjack/gateways/web/public/js/bootstrap.js +2276 -0
- data/lib/flapjack/gateways/web/public/js/contacts.js +225 -0
- data/lib/flapjack/gateways/web/public/js/jquery-1.10.2.js +9789 -0
- data/lib/flapjack/gateways/web/public/js/jquery-1.10.2.min.js +6 -0
- data/lib/flapjack/gateways/web/public/js/select2.js +3255 -0
- data/lib/flapjack/gateways/web/public/js/select2.min.js +22 -0
- data/lib/flapjack/gateways/web/public/js/underscore-min.js +6 -0
- data/lib/flapjack/gateways/web/public/js/underscore.js +1276 -0
- data/lib/flapjack/gateways/web/views/check.html.erb +423 -193
- data/lib/flapjack/gateways/web/views/checks.html.erb +51 -71
- data/lib/flapjack/gateways/web/views/contact.html.erb +142 -164
- data/lib/flapjack/gateways/web/views/contacts.html.erb +20 -40
- data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +83 -0
- data/lib/flapjack/gateways/web/views/entities.html.erb +18 -37
- data/lib/flapjack/gateways/web/views/entity.html.erb +46 -65
- data/lib/flapjack/gateways/web/views/index.html.erb +6 -27
- data/lib/flapjack/gateways/web/views/layout.erb +95 -0
- data/lib/flapjack/gateways/web/views/self_stats.html.erb +100 -114
- data/lib/flapjack/pikelet.rb +4 -2
- data/lib/flapjack/version.rb +1 -1
- data/spec/lib/flapjack/coordinator_spec.rb +120 -120
- data/spec/lib/flapjack/data/contact_spec.rb +66 -58
- data/spec/lib/flapjack/data/entity_check_spec.rb +179 -179
- data/spec/lib/flapjack/data/entity_spec.rb +71 -71
- data/spec/lib/flapjack/data/event_spec.rb +34 -30
- data/spec/lib/flapjack/data/message_spec.rb +6 -6
- data/spec/lib/flapjack/data/notification_rule_spec.rb +24 -24
- data/spec/lib/flapjack/data/notification_spec.rb +19 -19
- data/spec/lib/flapjack/data/semaphore_spec.rb +24 -0
- data/spec/lib/flapjack/data/tag_spec.rb +11 -10
- data/spec/lib/flapjack/gateways/api/contact_methods_spec.rb +201 -201
- data/spec/lib/flapjack/gateways/api/entity_check_presenter_spec.rb +55 -55
- data/spec/lib/flapjack/gateways/api/entity_methods_spec.rb +257 -257
- data/spec/lib/flapjack/gateways/api/entity_presenter_spec.rb +26 -26
- data/spec/lib/flapjack/gateways/api_spec.rb +1 -1
- data/spec/lib/flapjack/gateways/email_spec.rb +4 -4
- data/spec/lib/flapjack/gateways/jabber_spec.rb +77 -77
- data/spec/lib/flapjack/gateways/jsonapi/contact_methods_spec.rb +830 -0
- data/spec/lib/flapjack/gateways/jsonapi/entity_check_presenter_spec.rb +211 -0
- data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +863 -0
- data/spec/lib/flapjack/gateways/jsonapi/entity_presenter_spec.rb +108 -0
- data/spec/lib/flapjack/gateways/jsonapi_spec.rb +8 -0
- data/spec/lib/flapjack/gateways/oobetet_spec.rb +35 -35
- data/spec/lib/flapjack/gateways/pagerduty_spec.rb +40 -40
- data/spec/lib/flapjack/gateways/sms_messagenet_spec.rb +3 -3
- data/spec/lib/flapjack/gateways/web/views/check.html.erb_spec.rb +1 -1
- data/spec/lib/flapjack/gateways/web/views/contact.html.erb_spec.rb +5 -5
- data/spec/lib/flapjack/gateways/web/views/index.html.erb_spec.rb +1 -1
- data/spec/lib/flapjack/gateways/web_spec.rb +73 -74
- data/spec/lib/flapjack/logger_spec.rb +13 -13
- data/spec/lib/flapjack/pikelet_spec.rb +33 -33
- data/spec/lib/flapjack/processor_spec.rb +22 -22
- data/spec/lib/flapjack/redis_pool_spec.rb +1 -1
- data/spec/lib/flapjack/utility_spec.rb +12 -12
- data/spec/spec_helper.rb +9 -9
- data/spec/support/erb_view_helper.rb +4 -0
- metadata +107 -96
- data/lib/flapjack/gateways/web/public/css/flapjack.css +0 -49
- data/lib/flapjack/gateways/web/views/_css.html.erb +0 -42
- data/lib/flapjack/gateways/web/views/_foot.html.erb +0 -3
- data/lib/flapjack/gateways/web/views/_head.html.erb +0 -5
- data/lib/flapjack/gateways/web/views/_nav.html.erb +0 -10
@@ -0,0 +1,217 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Formats entity/check data for presentation by the API methods in Flapjack::Gateways::API.
|
4
|
+
|
5
|
+
require 'sinatra/base'
|
6
|
+
|
7
|
+
require 'flapjack/data/entity_check'
|
8
|
+
|
9
|
+
module Flapjack
|
10
|
+
|
11
|
+
module Gateways
|
12
|
+
|
13
|
+
class JSONAPI < Sinatra::Base
|
14
|
+
|
15
|
+
class EntityCheckPresenter
|
16
|
+
|
17
|
+
def initialize(entity_check)
|
18
|
+
@entity_check = entity_check
|
19
|
+
end
|
20
|
+
|
21
|
+
def status
|
22
|
+
{'name' => @entity_check.check,
|
23
|
+
'state' => @entity_check.state,
|
24
|
+
'enabled' => @entity_check.enabled?,
|
25
|
+
'summary' => @entity_check.summary,
|
26
|
+
'details' => @entity_check.details,
|
27
|
+
'in_unscheduled_maintenance' => @entity_check.in_unscheduled_maintenance?,
|
28
|
+
'in_scheduled_maintenance' => @entity_check.in_scheduled_maintenance?,
|
29
|
+
'last_update' => @entity_check.last_update,
|
30
|
+
'last_problem_notification' => @entity_check.last_notification_for_state(:problem)[:timestamp],
|
31
|
+
'last_recovery_notification' => @entity_check.last_notification_for_state(:recovery)[:timestamp],
|
32
|
+
'last_acknowledgement_notification' => @entity_check.last_notification_for_state(:acknowledgement)[:timestamp]}
|
33
|
+
end
|
34
|
+
|
35
|
+
def outages(start_time, end_time, options = {})
|
36
|
+
# hist_states is an array of hashes, with [state, timestamp, summary] keys
|
37
|
+
hist_states = @entity_check.historical_states(start_time, end_time)
|
38
|
+
return hist_states if hist_states.empty?
|
39
|
+
|
40
|
+
initial = @entity_check.historical_state_before(hist_states.first[:timestamp])
|
41
|
+
hist_states.unshift(initial) if initial
|
42
|
+
|
43
|
+
# TODO the following works, but isn't the neatest
|
44
|
+
num_states = hist_states.size
|
45
|
+
|
46
|
+
index = 0
|
47
|
+
result = []
|
48
|
+
obj = nil
|
49
|
+
|
50
|
+
while index < num_states do
|
51
|
+
last_obj = obj
|
52
|
+
obj = hist_states[index]
|
53
|
+
index += 1
|
54
|
+
|
55
|
+
next if obj[:state] == 'ok'
|
56
|
+
|
57
|
+
if last_obj && (last_obj[:state] == obj[:state])
|
58
|
+
# TODO maybe build up arrays of these instead, and leave calling
|
59
|
+
# classes to join them together if needed?
|
60
|
+
result.last[:summary] << " / #{obj[:summary]}"
|
61
|
+
result.last[:details] << " / #{obj[:details]}"
|
62
|
+
next
|
63
|
+
end
|
64
|
+
|
65
|
+
ts = obj[:timestamp]
|
66
|
+
|
67
|
+
obj_st = (last_obj || !start_time) ? ts : [ts, start_time].max
|
68
|
+
|
69
|
+
next_ts_obj = hist_states[index..-1].detect {|hs| hs[:state] != obj[:state] }
|
70
|
+
obj_et = next_ts_obj ? next_ts_obj[:timestamp] : end_time
|
71
|
+
|
72
|
+
obj_dur = obj_et ? obj_et - obj_st : nil
|
73
|
+
|
74
|
+
result << {:state => obj[:state],
|
75
|
+
:start_time => obj_st,
|
76
|
+
:end_time => obj_et,
|
77
|
+
:duration => obj_dur,
|
78
|
+
:summary => obj[:summary] || '',
|
79
|
+
:details => obj[:details] || ''
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
result
|
84
|
+
end
|
85
|
+
|
86
|
+
def unscheduled_maintenances(start_time, end_time)
|
87
|
+
# unsched_maintenance is an array of hashes, with [duration, timestamp, summary] keys
|
88
|
+
unsched_maintenance = @entity_check.maintenances(start_time, end_time,
|
89
|
+
:scheduled => false)
|
90
|
+
|
91
|
+
# to see if we start in an unscheduled maintenance period, we must check all unscheduled
|
92
|
+
# maintenances before the period and their durations
|
93
|
+
start_in_unsched = start_time.nil? ? [] :
|
94
|
+
@entity_check.maintenances(nil, start_time, :scheduled => false).select {|pu|
|
95
|
+
pu[:end_time] >= start_time
|
96
|
+
}
|
97
|
+
|
98
|
+
start_in_unsched + unsched_maintenance
|
99
|
+
end
|
100
|
+
|
101
|
+
def scheduled_maintenances(start_time, end_time)
|
102
|
+
# sched_maintenance is an array of hashes, with [duration, timestamp, summary] keys
|
103
|
+
sched_maintenance = @entity_check.maintenances(start_time, end_time,
|
104
|
+
:scheduled => true)
|
105
|
+
|
106
|
+
# to see if we start in a scheduled maintenance period, we must check all scheduled
|
107
|
+
# maintenances before the period and their durations
|
108
|
+
start_in_sched = start_time.nil? ? [] :
|
109
|
+
@entity_check.maintenances(nil, start_time, :scheduled => true).select {|ps|
|
110
|
+
ps[:end_time] >= start_time
|
111
|
+
}
|
112
|
+
|
113
|
+
start_in_sched + sched_maintenance
|
114
|
+
end
|
115
|
+
|
116
|
+
# TODO test whether the below overlapping logic is prone to off-by-one
|
117
|
+
# errors; the numbers may line up more neatly if we consider outages to
|
118
|
+
# start one second after the maintenance period ends.
|
119
|
+
#
|
120
|
+
# TODO test performance with larger data sets
|
121
|
+
def downtime(start_time, end_time)
|
122
|
+
sched_maintenances = scheduled_maintenances(start_time, end_time)
|
123
|
+
|
124
|
+
outs = outages(start_time, end_time)
|
125
|
+
|
126
|
+
total_secs = {}
|
127
|
+
percentages = {}
|
128
|
+
|
129
|
+
outs.collect {|obj| obj[:state]}.uniq.each do |st|
|
130
|
+
total_secs[st] = 0
|
131
|
+
percentages[st] = (start_time.nil? || end_time.nil?) ? nil : 0
|
132
|
+
end
|
133
|
+
|
134
|
+
unless outs.empty?
|
135
|
+
|
136
|
+
# Initially we need to check for cases where a scheduled
|
137
|
+
# maintenance period is fully covered by an outage period.
|
138
|
+
# We then create two new outage periods to cover the time around
|
139
|
+
# the scheduled maintenance period, and remove the original.
|
140
|
+
|
141
|
+
sched_maintenances.each do |sm|
|
142
|
+
|
143
|
+
split_outs = []
|
144
|
+
|
145
|
+
outs.each { |o|
|
146
|
+
next unless o[:end_time] && (o[:start_time] < sm[:start_time]) &&
|
147
|
+
(o[:end_time] > sm[:end_time])
|
148
|
+
o[:delete] = true
|
149
|
+
split_outs += [{:state => o[:state],
|
150
|
+
:start_time => o[:start_time],
|
151
|
+
:end_time => sm[:start_time],
|
152
|
+
:duration => sm[:start_time] - o[:start_time],
|
153
|
+
:summary => "#{o[:summary]} [split start]"},
|
154
|
+
{:state => o[:state],
|
155
|
+
:start_time => sm[:end_time],
|
156
|
+
:end_time => o[:end_time],
|
157
|
+
:duration => o[:end_time] - sm[:end_time],
|
158
|
+
:summary => "#{o[:summary]} [split finish]"}]
|
159
|
+
}
|
160
|
+
|
161
|
+
outs.reject! {|o| o[:delete]}
|
162
|
+
outs += split_outs
|
163
|
+
# not strictly necessary to keep the data sorted, but
|
164
|
+
# will make more sense while debgging
|
165
|
+
outs.sort! {|a,b| a[:start_time] <=> b[:start_time]}
|
166
|
+
end
|
167
|
+
|
168
|
+
sched_maintenances.each do |sm|
|
169
|
+
|
170
|
+
outs.each do |o|
|
171
|
+
next unless o[:end_time] && (sm[:start_time] < o[:end_time]) &&
|
172
|
+
(sm[:end_time] > o[:start_time])
|
173
|
+
|
174
|
+
if sm[:start_time] <= o[:start_time] &&
|
175
|
+
sm[:end_time] >= o[:end_time]
|
176
|
+
|
177
|
+
# outage is fully overlapped by the scheduled maintenance
|
178
|
+
o[:delete] = true
|
179
|
+
|
180
|
+
elsif sm[:start_time] <= o[:start_time]
|
181
|
+
# partially overlapping on the earlier side
|
182
|
+
o[:start_time] = sm[:end_time]
|
183
|
+
o[:duration] = o[:end_time] - o[:start_time]
|
184
|
+
elsif sm[:end_time] >= o[:end_time]
|
185
|
+
# partially overlapping on the later side
|
186
|
+
o[:end_time] = sm[:start_time]
|
187
|
+
o[:duration] = o[:end_time] - o[:start_time]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
outs.reject! {|o| o[:delete]}
|
192
|
+
end
|
193
|
+
|
194
|
+
total_secs = outs.inject(total_secs) {|ret, o|
|
195
|
+
ret[o[:state]] += o[:duration] if o[:duration]
|
196
|
+
ret
|
197
|
+
}
|
198
|
+
|
199
|
+
unless (start_time.nil? || end_time.nil?)
|
200
|
+
total_secs.each_pair do |st, ts|
|
201
|
+
percentages[st] = (total_secs[st] * 100.0) / (end_time.to_f - start_time.to_f)
|
202
|
+
end
|
203
|
+
total_secs['ok'] = (end_time - start_time) - total_secs.values.reduce(:+)
|
204
|
+
percentages['ok'] = 100 - percentages.values.reduce(:+)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
{:total_seconds => total_secs, :percentages => percentages, :downtime => outs}
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
@@ -0,0 +1,350 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sinatra/base'
|
4
|
+
|
5
|
+
require 'flapjack/data/entity'
|
6
|
+
require 'flapjack/data/entity_check'
|
7
|
+
|
8
|
+
require 'flapjack/gateways/jsonapi/entity_presenter'
|
9
|
+
require 'flapjack/gateways/jsonapi/entity_check_presenter'
|
10
|
+
|
11
|
+
module Flapjack
|
12
|
+
|
13
|
+
module Gateways
|
14
|
+
|
15
|
+
class JSONAPI < Sinatra::Base
|
16
|
+
|
17
|
+
module EntityMethods
|
18
|
+
|
19
|
+
module Helpers
|
20
|
+
|
21
|
+
def find_entity(entity_name)
|
22
|
+
entity = Flapjack::Data::Entity.find_by_name(entity_name, :redis => redis)
|
23
|
+
raise Flapjack::Gateways::JSONAPI::EntityNotFound.new(entity_name) if entity.nil?
|
24
|
+
entity
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_entity_check(entity, check)
|
28
|
+
entity_check = Flapjack::Data::EntityCheck.for_entity(entity,
|
29
|
+
check, :redis => redis)
|
30
|
+
raise Flapjack::Gateways::JSONAPI::EntityCheckNotFound.new(entity, check) if entity_check.nil?
|
31
|
+
entity_check
|
32
|
+
end
|
33
|
+
|
34
|
+
def find_tags(tags)
|
35
|
+
halt err(403, "no tags") if tags.nil? || tags.empty?
|
36
|
+
tags
|
37
|
+
end
|
38
|
+
|
39
|
+
def entities_and_checks(entity_name, check)
|
40
|
+
if entity_name
|
41
|
+
# backwards-compatible, single entity or entity&check from route
|
42
|
+
entities = check ? nil : [entity_name]
|
43
|
+
checks = check ? {entity_name => check} : nil
|
44
|
+
else
|
45
|
+
# new and improved bulk API queries
|
46
|
+
entities = params[:entity]
|
47
|
+
checks = params[:check]
|
48
|
+
entities = [entities] unless entities.nil? || entities.is_a?(Array)
|
49
|
+
# TODO err if checks isn't a Hash (similar rules as in flapjack-diner)
|
50
|
+
end
|
51
|
+
[entities, checks]
|
52
|
+
end
|
53
|
+
|
54
|
+
def bulk_api_check_action(entities, entity_checks, action, params = {})
|
55
|
+
unless entities.nil? || entities.empty?
|
56
|
+
entities.each do |entity_name|
|
57
|
+
entity = find_entity(entity_name)
|
58
|
+
checks = entity.check_list.sort
|
59
|
+
checks.each do |check|
|
60
|
+
action.call( find_entity_check(entity, check) )
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
unless entity_checks.nil? || entity_checks.empty?
|
66
|
+
entity_checks.each_pair do |entity_name, checks|
|
67
|
+
entity = find_entity(entity_name)
|
68
|
+
checks = [checks] unless checks.is_a?(Array)
|
69
|
+
checks.each do |check|
|
70
|
+
action.call( find_entity_check(entity, check) )
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def present_api_results(entities, entity_checks, result_type, &block)
|
77
|
+
result = []
|
78
|
+
|
79
|
+
unless entities.nil? || entities.empty?
|
80
|
+
result += entities.collect {|entity_name|
|
81
|
+
entity = find_entity(entity_name)
|
82
|
+
yield(Flapjack::Gateways::JSONAPI::EntityPresenter.new(entity, :redis => redis))
|
83
|
+
}.flatten(1)
|
84
|
+
end
|
85
|
+
|
86
|
+
unless entity_checks.nil? || entity_checks.empty?
|
87
|
+
result += entity_checks.inject([]) {|memo, (entity_name, checks)|
|
88
|
+
checks = [checks] unless checks.is_a?(Array)
|
89
|
+
entity = find_entity(entity_name)
|
90
|
+
memo += checks.collect {|check|
|
91
|
+
entity_check = find_entity_check(entity, check)
|
92
|
+
{:entity => entity_name,
|
93
|
+
:check => check,
|
94
|
+
result_type.to_sym => yield(Flapjack::Gateways::JSONAPI::EntityCheckPresenter.new(entity_check))
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}.flatten(1)
|
98
|
+
end
|
99
|
+
|
100
|
+
result
|
101
|
+
end
|
102
|
+
|
103
|
+
# NB: casts to UTC before converting to a timestamp
|
104
|
+
def validate_and_parsetime(value)
|
105
|
+
return unless value
|
106
|
+
Time.iso8601(value).getutc.to_i
|
107
|
+
rescue ArgumentError => e
|
108
|
+
logger.error "Couldn't parse time from '#{value}'"
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
# used for backwards-compatible route matching below
|
115
|
+
ENTITY_CHECK_FRAGMENT = '(?:/([a-zA-Z0-9][a-zA-Z0-9\.\-]*[a-zA-Z0-9])(?:/(.+))?)?'
|
116
|
+
|
117
|
+
def self.registered(app)
|
118
|
+
|
119
|
+
app.helpers Flapjack::Gateways::JSONAPI::EntityMethods::Helpers
|
120
|
+
|
121
|
+
app.get '/entities' do
|
122
|
+
content_type :json
|
123
|
+
cors_headers
|
124
|
+
|
125
|
+
entities_json = Flapjack::Data::Entity.all(:redis => redis).collect {|e|
|
126
|
+
presenter = Flapjack::Gateways::JSONAPI::EntityPresenter.new(e, :redis => redis)
|
127
|
+
id = (e.id.respond_to?(:length) && e.id.length > 0) ? e.id : e.name
|
128
|
+
{'id' => id, 'name' => e.name, 'checks' => presenter.status }.to_json
|
129
|
+
}.join(',')
|
130
|
+
|
131
|
+
'{"entities":[' + entities_json + ']}'
|
132
|
+
end
|
133
|
+
|
134
|
+
app.get '/checks/:entity' do
|
135
|
+
content_type :json
|
136
|
+
entity = find_entity(params[:entity])
|
137
|
+
entity.check_list.to_json
|
138
|
+
end
|
139
|
+
|
140
|
+
app.get %r{/status#{ENTITY_CHECK_FRAGMENT}} do
|
141
|
+
content_type :json
|
142
|
+
|
143
|
+
captures = params[:captures] || []
|
144
|
+
entity_name = captures[0]
|
145
|
+
check = captures[1]
|
146
|
+
|
147
|
+
entities, checks = entities_and_checks(entity_name, check)
|
148
|
+
|
149
|
+
results = present_api_results(entities, checks, 'status') {|presenter|
|
150
|
+
presenter.status
|
151
|
+
}
|
152
|
+
|
153
|
+
if entity_name
|
154
|
+
# compatible with previous data format
|
155
|
+
results = results.collect {|status_h| status_h[:status]}
|
156
|
+
check ? results.first.to_json : "[" + results.map {|r| r.to_json }.join(',') + "]"
|
157
|
+
else
|
158
|
+
# new and improved data format which reflects the request param structure
|
159
|
+
"[" + results.map {|r| r.to_json }.join(',') + "]"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
app.get %r{/((?:outages|(?:un)?scheduled_maintenances|downtime))#{ENTITY_CHECK_FRAGMENT}} do
|
164
|
+
action = params[:captures][0].to_sym
|
165
|
+
entity_name = params[:captures][1]
|
166
|
+
check = params[:captures][2]
|
167
|
+
|
168
|
+
entities, checks = entities_and_checks(entity_name, check)
|
169
|
+
|
170
|
+
start_time = validate_and_parsetime(params[:start_time])
|
171
|
+
end_time = validate_and_parsetime(params[:end_time])
|
172
|
+
|
173
|
+
results = present_api_results(entities, checks, action) {|presenter|
|
174
|
+
presenter.send(action, start_time, end_time)
|
175
|
+
}
|
176
|
+
|
177
|
+
if check
|
178
|
+
# compatible with previous data format
|
179
|
+
results.first[action].to_json
|
180
|
+
elsif entity_name
|
181
|
+
# compatible with previous data format
|
182
|
+
rename = {:unscheduled_maintenances => :unscheduled_maintenance,
|
183
|
+
:scheduled_maintenances => :scheduled_maintenance}
|
184
|
+
drop = [:entity]
|
185
|
+
results.collect{|r|
|
186
|
+
r.inject({}) {|memo, (k, v)|
|
187
|
+
if new_k = rename[k]
|
188
|
+
memo[new_k] = v
|
189
|
+
elsif !drop.include?(k)
|
190
|
+
memo[k] = v
|
191
|
+
end
|
192
|
+
memo
|
193
|
+
}
|
194
|
+
}.to_json
|
195
|
+
else
|
196
|
+
# new and improved data format which reflects the request param structure
|
197
|
+
results.to_json
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# create a scheduled maintenance period for a check on an entity
|
202
|
+
app.post %r{/scheduled_maintenances#{ENTITY_CHECK_FRAGMENT}} do
|
203
|
+
|
204
|
+
captures = params[:captures] || []
|
205
|
+
entity_name = captures[0]
|
206
|
+
check = captures[1]
|
207
|
+
|
208
|
+
entities, checks = entities_and_checks(entity_name, check)
|
209
|
+
|
210
|
+
start_time = validate_and_parsetime(params[:start_time])
|
211
|
+
halt( err(403, "start time must be provided") ) unless start_time
|
212
|
+
|
213
|
+
act_proc = proc {|entity_check|
|
214
|
+
entity_check.create_scheduled_maintenance(start_time,
|
215
|
+
params[:duration].to_i, :summary => params[:summary])
|
216
|
+
}
|
217
|
+
|
218
|
+
bulk_api_check_action(entities, checks, act_proc)
|
219
|
+
status 204
|
220
|
+
end
|
221
|
+
|
222
|
+
# create an acknowledgement for a service on an entity
|
223
|
+
# NB currently, this does not acknowledge a specific failure event, just
|
224
|
+
# the entity-check as a whole
|
225
|
+
app.post %r{/acknowledgements#{ENTITY_CHECK_FRAGMENT}} do
|
226
|
+
captures = params[:captures] || []
|
227
|
+
entity_name = captures[0]
|
228
|
+
check = captures[1]
|
229
|
+
|
230
|
+
entities, checks = entities_and_checks(entity_name, check)
|
231
|
+
|
232
|
+
dur = params[:duration] ? params[:duration].to_i : nil
|
233
|
+
duration = (dur.nil? || (dur <= 0)) ? (4 * 60 * 60) : dur
|
234
|
+
summary = params[:summary]
|
235
|
+
|
236
|
+
opts = {'duration' => duration}
|
237
|
+
opts['summary'] = summary if summary
|
238
|
+
|
239
|
+
act_proc = proc {|entity_check|
|
240
|
+
Flapjack::Data::Event.create_acknowledgement(
|
241
|
+
entity_check.entity_name, entity_check.check,
|
242
|
+
:summary => params[:summary],
|
243
|
+
:duration => duration,
|
244
|
+
:redis => redis)
|
245
|
+
}
|
246
|
+
|
247
|
+
bulk_api_check_action(entities, checks, act_proc)
|
248
|
+
status 204
|
249
|
+
end
|
250
|
+
|
251
|
+
app.delete %r{/((?:un)?scheduled_maintenances)} do
|
252
|
+
action = params[:captures][0]
|
253
|
+
|
254
|
+
# no backwards-compatible mode here, it's a new method
|
255
|
+
entities, checks = entities_and_checks(nil, nil)
|
256
|
+
|
257
|
+
act_proc = case action
|
258
|
+
when 'scheduled_maintenances'
|
259
|
+
start_time = validate_and_parsetime(params[:start_time])
|
260
|
+
halt( err(403, "start time must be provided") ) unless start_time
|
261
|
+
opts = {}
|
262
|
+
proc {|entity_check| entity_check.end_scheduled_maintenance(start_time.to_i) }
|
263
|
+
when 'unscheduled_maintenances'
|
264
|
+
end_time = validate_and_parsetime(params[:end_time]) || Time.now
|
265
|
+
proc {|entity_check| entity_check.end_unscheduled_maintenance(end_time.to_i) }
|
266
|
+
end
|
267
|
+
|
268
|
+
bulk_api_check_action(entities, checks, act_proc)
|
269
|
+
status 204
|
270
|
+
end
|
271
|
+
|
272
|
+
app.post %r{/test_notifications#{ENTITY_CHECK_FRAGMENT}} do
|
273
|
+
captures = params[:captures] || []
|
274
|
+
entity_name = captures[0]
|
275
|
+
check = captures[1]
|
276
|
+
|
277
|
+
entities, checks = entities_and_checks(entity_name, check)
|
278
|
+
|
279
|
+
act_proc = proc {|entity_check|
|
280
|
+
summary = params[:summary] ||
|
281
|
+
"Testing notifications to all contacts interested in entity #{entity_check.entity.name}"
|
282
|
+
Flapjack::Data::Event.test_notifications(
|
283
|
+
entity_check.entity_name, entity_check.check,
|
284
|
+
:summary => summary,
|
285
|
+
:redis => redis)
|
286
|
+
}
|
287
|
+
|
288
|
+
bulk_api_check_action(entities, checks, act_proc)
|
289
|
+
status 204
|
290
|
+
end
|
291
|
+
|
292
|
+
app.post '/entities' do
|
293
|
+
pass unless Flapjack::Gateways::JSONAPI::JSON_REQUEST_MIME_TYPES.include?(request.content_type)
|
294
|
+
|
295
|
+
errors = []
|
296
|
+
ret = nil
|
297
|
+
|
298
|
+
# FIXME should scan for invalid records before making any changes, fail early
|
299
|
+
|
300
|
+
entities = params[:entities]
|
301
|
+
unless entities
|
302
|
+
logger.debug("no entities object found in the following supplied JSON:")
|
303
|
+
logger.debug(request.body)
|
304
|
+
return err(403, "No entities object received")
|
305
|
+
end
|
306
|
+
return err(403, "The received entities object is not an Enumerable") unless entities.is_a?(Enumerable)
|
307
|
+
return err(403, "Entity with a nil id detected") unless entities.any? {|e| !e['id'].nil?}
|
308
|
+
|
309
|
+
entities.each do |entity|
|
310
|
+
unless entity['id']
|
311
|
+
errors << "Entity not imported as it has no id: #{entity.inspect}"
|
312
|
+
next
|
313
|
+
end
|
314
|
+
Flapjack::Data::Entity.add(entity, :redis => redis)
|
315
|
+
end
|
316
|
+
errors.empty? ? 204 : err(403, *errors)
|
317
|
+
end
|
318
|
+
|
319
|
+
app.post '/entities/:entity/tags' do
|
320
|
+
content_type :json
|
321
|
+
|
322
|
+
tags = find_tags(params[:tag])
|
323
|
+
entity = find_entity(params[:entity])
|
324
|
+
entity.add_tags(*tags)
|
325
|
+
entity.tags.to_json
|
326
|
+
end
|
327
|
+
|
328
|
+
app.delete '/entities/:entity/tags' do
|
329
|
+
tags = find_tags(params[:tag])
|
330
|
+
entity = find_entity(params[:entity])
|
331
|
+
entity.delete_tags(*tags)
|
332
|
+
status 204
|
333
|
+
end
|
334
|
+
|
335
|
+
app.get '/entities/:entity/tags' do
|
336
|
+
content_type :json
|
337
|
+
|
338
|
+
entity = find_entity(params[:entity])
|
339
|
+
entity.tags.to_json
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|
343
|
+
|
344
|
+
end
|
345
|
+
|
346
|
+
end
|
347
|
+
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|