Class: Flapjack::Data::Event

Inherits:
Object
  • Object
show all
Defined in:
lib/flapjack/data/event.rb

Constant Summary collapse

REQUIRED_KEYS =

type was a required key in v1, but is superfluous tags are now ignored, tags on the checks are used for rule matching

%w(state check)
OPTIONAL_KEYS =
%w(entity time initial_failure_delay
repeat_failure_delay initial_recovery_delay summary details
acknowledgement_id duration tags perfdata type)
VALIDATIONS =
{
  proc {|e| e['state'].is_a?(String) &&
      ['ok', 'warning', 'critical', 'unknown', 'acknowledgement',
       'test_notifications'].include?(e['state'].downcase) ||
       !(e['state'] =~ /\Atest_notifications(?:\s+#{Flapjack::Data::Condition.unhealthy.keys.join('|')})?\z/).nil? } =>
    "state must be one of 'ok', 'warning', 'critical', 'unknown', " +
    "'acknowledgement', 'test_notifications', or 'test_notifications [problem]' ",

  proc {|e| e['entity'].nil? || e['entity'].is_a?(String) } =>
    "entity must be a string",

  proc {|e| e['check'].is_a?(String) } =>
    "check must be a string",

  proc {|e| e['time'].nil? ||
            e['time'].is_a?(Integer) ||
           (e['time'].is_a?(String) && !!(e['time'] =~ /^\d+$/)) } =>
    "time must be a positive integer, or a string castable to one",

  proc {|e| e['initial_failure_delay'].nil? ||
            e['initial_failure_delay'].is_a?(Integer) ||
           (e['initial_failure_delay'].is_a?(String) && !!(e['initial_failure_delay'] =~ /^\d+$/)) } =>
    "initial_failure_delay must be a positive integer, or a string castable to one",

  proc {|e| e['repeat_failure_delay'].nil? ||
            e['repeat_failure_delay'].is_a?(Integer) ||
           (e['repeat_failure_delay'].is_a?(String) && !!(e['repeat_failure_delay'] =~ /^\d+$/)) } =>
    "repeat_failure_delay must be a positive integer, or a string castable to one",

  proc {|e| e['initial_recovery_delay'].nil? ||
            e['initial_recovery_delay'].is_a?(Integer) ||
           (e['initial_recovery_delay'].is_a?(String) && !!(e['initial_recovery_delay'] =~ /^\d+$/)) } =>
    "initial_recovery_delay must be a positive integer, or a string castable to one",

  proc {|e| e['summary'].nil? || e['summary'].is_a?(String) } =>
    "summary must be a string",

  proc {|e| e['details'].nil? || e['details'].is_a?(String) } =>
    "details must be a string",

  proc { |e| e['perfdata'].nil? || e['perfdata'].is_a?(String) } =>
    "perfdata must be a string",

  proc {|e| e['acknowledgement_id'].nil? ||
            e['acknowledgement_id'].is_a?(String) ||
            e['acknowledgement_id'].is_a?(Integer) } =>
    "acknowledgement_id must be a string or an integer",

  proc {|e| e['duration'].nil? ||
            e['duration'].is_a?(Integer) ||
           (e['duration'].is_a?(String) && !!(e['duration'] =~ /^\d+$/)) } =>
    "duration must be a positive integer, or a string castable to one",

  proc {|e| e['tags'].nil? ||
           (e['tags'].is_a?(Array) &&
            e['tags'].all? {|tag| tag.is_a?(String)}) } =>
    "tags must be an array of strings",
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attrs = {}) ⇒ Event



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/flapjack/data/event.rb', line 178

def initialize(attrs = {})
  @id = if attrs['entity'].nil?
    attrs['check']
  else
    "#{attrs['entity']}:#{attrs['check']}"
  end
  [:state, :time, :initial_failure_delay, :repeat_failure_delay,
   :initial_recovery_delay, :summary, :details, :perfdata,
   :acknowledgement_id, :duration].each do |key|

    instance_variable_set("@#{key.to_s}", attrs[key.to_s])
  end
  # summary, details and perfdata are optional. set to nil if they only contain whitespace
  ['@summary', '@details', '@perfdata'].each do |inst|
    value = instance_variable_get(inst)
    v = if value.is_a?(String)
      vs = value.strip
      vs.empty? ? nil : vs
    else
      nil
    end
    instance_variable_set(inst, v)
  end
end

Instance Attribute Details

#acknowledgement_idObject (readonly)

Returns the value of attribute acknowledgement_id.



11
12
13
# File 'lib/flapjack/data/event.rb', line 11

def acknowledgement_id
  @acknowledgement_id
end

#counterObject

Returns the value of attribute counter.



9
10
11
# File 'lib/flapjack/data/event.rb', line 9

def counter
  @counter
end

#detailsObject (readonly)

Returns the value of attribute details.



11
12
13
# File 'lib/flapjack/data/event.rb', line 11

def details
  @details
end

#idObject (readonly)

Returns the value of attribute id.



11
12
13
# File 'lib/flapjack/data/event.rb', line 11

def id
  @id
end

#id_hashObject

Returns the value of attribute id_hash.



9
10
11
# File 'lib/flapjack/data/event.rb', line 9

def id_hash
  @id_hash
end

#perfdataObject (readonly)

Returns the value of attribute perfdata.



11
12
13
# File 'lib/flapjack/data/event.rb', line 11

def perfdata
  @perfdata
end

#summaryObject (readonly)

Returns the value of attribute summary.



11
12
13
# File 'lib/flapjack/data/event.rb', line 11

def summary
  @summary
end

Class Method Details

.create_acknowledgements(queue, checks, opts = {}) ⇒ Object



153
154
155
156
157
158
159
160
161
162
# File 'lib/flapjack/data/event.rb', line 153

def self.create_acknowledgements(queue, checks, opts = {})
  raise "Check(s) must be provided" if checks.nil?
  checks.each do |check|
    self.push(queue, 'state'              => 'acknowledgement',
                     'check'              => check.name,
                     'summary'            => opts[:summary],
                     'duration'           => opts[:duration],
                     'acknowledgement_id' => opts[:acknowledgement_id])
  end
end

.parse_and_validate(raw, opts = {}) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/flapjack/data/event.rb', line 79

def self.parse_and_validate(raw, opts = {})
  errors = []
  if parsed = Flapjack.load_json(raw)
    if parsed.is_a?(Hash)
      errors = validation_errors_for_hash(parsed, opts)
    else
      errors << "Event must be a JSON hash, see http://flapjack.io/docs/1.0/development/DATA_STRUCTURES#event-queue"
    end
    return [parsed, errors]
  end
  [nil, errors]
rescue JSON::JSONError => e
  errors << "Error deserialising event json: #{e}, raw json: #{raw.inspect}"
  [nil, errors]
end

.pending_count(queue) ⇒ Object

Provide a count of the number of events on the queue to be processed.



149
150
151
# File 'lib/flapjack/data/event.rb', line 149

def self.pending_count(queue)
  Flapjack.redis.llen(queue)
end

.push(queue, event) ⇒ Object

creates, or modifies, an event object and adds it to the events list in redis

'entity'                => entity,
'check'                 => check,
'time'                  => timestamp,
'initial_failure_delay' => initial_failure_delay,
'repeat_failure_delay'  => repeat_failure_delay,
'initial_recovery_delay' => initial_recovery_delay,
'type'                  => 'service',
'state'                 => state,
'summary'               => check_output,
'details'               => check_long_output,
'perfdata'              => perf_data


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/flapjack/data/event.rb', line 130

def self.push(queue, event)
  event['time'] = Time.now.to_i if event['time'].nil?

  begin
    event_json = Flapjack.dump_json(event)
  rescue JSON::JSONError => e
    Flapjack.logger.warn("Error serialising event json: #{e}, event: #{event.inspect}")
    event_json = nil
  end

  if event_json
    Flapjack.redis.multi do
      Flapjack.redis.lpush(queue, event_json)
      Flapjack.redis.lpush("#{queue}_actions", "+")
    end
  end
end

.test_notifications(queue, checks, opts = {}) ⇒ Object



164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/flapjack/data/event.rb', line 164

def self.test_notifications(queue, checks, opts = {})
  raise "Check(s) must be provided" if checks.nil?
  condition = opts[:condition] || 'critical'
  unless Flapjack::Data::Condition.unhealthy.keys.include?(condition)
    raise "Condition must be a problem"
  end
  checks.each do |check|
    self.push(queue, 'state'              => "test_notifications #{condition}",
                     'check'              => check.name,
                     'summary'            => opts[:summary],
                     'details'            => opts[:details])
  end
end

.validation_errors_for_hash(hash, opts = {}) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/flapjack/data/event.rb', line 95

def self.validation_errors_for_hash(hash, opts = {})
  errors = []
  missing_keys = REQUIRED_KEYS.select {|k|
    !hash.has_key?(k) || hash[k].nil? || hash[k].empty?
  }
  unless missing_keys.empty?
    errors << "Event hash has missing keys '#{missing_keys.join('\', \'')}'"
  end

  unknown_keys =  hash.keys - (REQUIRED_KEYS + OPTIONAL_KEYS)
  unless unknown_keys.empty?
    errors << "Event hash has unknown key(s) '#{unknown_keys.join('\', \'')}'"
  end

  if errors.empty?
    errors += VALIDATIONS.keys.inject([]) {|ret,vk|
      ret << "Event #{VALIDATIONS[vk]}" unless vk.call(hash)
      ret
    }
  end
  errors
end

Instance Method Details

#dumpObject



217
218
219
220
221
222
# File 'lib/flapjack/data/event.rb', line 217

def dump
  return @dump unless @dump.nil?
  @dump = "#{id}, #{state}"
  @dump << ", #{summary}" unless summary.nil?
  @dump << ", #{Time.at(time).to_s}" unless time.nil?
end

#stateObject



203
204
205
206
# File 'lib/flapjack/data/event.rb', line 203

def state
  return unless @state
  @state.downcase
end