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