Module: Sensu::Server::Filter

Included in:
Process
Defined in:
lib/sensu/server/filter.rb

Constant Summary collapse

EVAL_PREFIX =
"eval:".freeze

Instance Method Summary collapse

Instance Method Details

#eval_attribute_value(event, raw_eval_string, raw_value) ⇒ TrueClass, FalseClass

Ruby ‘eval()` a string containing an expression, within the scope/context of a sandbox. This method is for filter attribute values starting with “eval:”, with the Ruby expression following the colon. A single variable is provided to the expression, `value`, equal to the corresponding event attribute value. Dot notation tokens in the expression, e.g. `:::mysql.user:::`, are substituted with the corresponding event data values prior to evaluation. The expression is expected to return a boolean value.

Parameters:

  • event (Hash)
  • raw_eval_string (String)

    containing the Ruby expression to be evaluated.

  • raw_value (Object)

    of the corresponding event attribute value.

Returns:

  • (TrueClass, FalseClass)


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/sensu/server/filter.rb', line 112

def eval_attribute_value(event, raw_eval_string, raw_value)
  eval_string = process_eval_string(event, raw_eval_string)
  unless eval_string.nil?
    begin
      value = Marshal.load(Marshal.dump(raw_value))
      !!Sandbox.eval(eval_string, value)
    rescue StandardError, SyntaxError => error
      @logger.error("filter attribute eval error", {
        :event => event,
        :raw_eval_string => raw_eval_string,
        :raw_value => raw_value,
        :error => error.to_s
      })
      false
    end
  else
    false
  end
end

#event_filter(filter_name, event) {|filtered| ... } ⇒ Object

Determine if an event is filtered by an event filter, native or extension. This method first checks for the existence of a native filter, then checks for an extension if a native filter is not defined. The provided callback is called with a single parameter, indicating if the event was filtered by a filter. If a filter does not exist for the provided name, the event is not filtered.

Parameters:

  • filter_name (String)
  • event (Hash)

Yields:

  • (filtered)

    callback/block called with a single parameter to indicate if the event was filtered.

Yield Parameters:

  • filtered (TrueClass, FalseClass)

    indicating if the event was filtered.

  • filter_name (String)

    name of the filter being evaluated



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/sensu/server/filter.rb', line 229

def event_filter(filter_name, event)
  case
  when @settings.filter_exists?(filter_name)
    native_filter(filter_name, event) do |filtered|
      yield(filtered, filter_name)
    end
  when @extensions.filter_exists?(filter_name)
    extension_filter(filter_name, event) do |filtered|
      yield(filtered, filter_name)
    end
  else
    @logger.error("unknown filter", :filter_name => filter_name)
    yield(false, filter_name)
  end
end

#event_filtered?(handler, event) {|filtered| ... } ⇒ Boolean

Determine if an event is filtered for a handler. If a handler specifies one or more filters, via ‘:filters` or `:filter`, the `event_filter()` method is called for each of them. If any of the filters return `true`, the event is filtered for the handler. If no filters are defined in the handler definition, the event is not filtered.

Parameters:

  • handler (Hash)

    definition.

  • event (Hash)

Yields:

  • (filtered)

    callback/block called with a single parameter to indicate if the event was filtered.

Yield Parameters:

  • filtered (TrueClass, FalseClass)

    indicating if the event was filtered.

Returns:

  • (Boolean)


258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/sensu/server/filter.rb', line 258

def event_filtered?(handler, event)
  if handler.has_key?(:filters) || handler.has_key?(:filter)
    filter_list = Array(handler[:filters] || handler[:filter]).dup
    filter = Proc.new do |filter_list|
      filter_name = filter_list.shift
      if filter_name.nil?
        yield(false)
      else
        event_filter(filter_name, event) do |filtered|
          filtered ? yield(true) : EM.next_tick { filter.call(filter_list) }
        end
      end
    end
    filter.call(filter_list)
  else
    yield(false)
  end
end

#extension_filter(filter_name, event) {|filtered| ... } ⇒ Object

Determine if an event is filtered by a filter extension.

Parameters:

  • filter_name (String)
  • event (Hash)

Yields:

  • (filtered)

    callback/block called with a single parameter to indicate if the event was filtered.

Yield Parameters:

  • filtered (TrueClass, FalseClass)

    indicating if the event was filtered.

  • filter_name (String)

    name of the filter being evaluated



203
204
205
206
207
208
209
210
211
212
# File 'lib/sensu/server/filter.rb', line 203

def extension_filter(filter_name, event)
  extension = @extensions[:filters][filter_name]
  if in_filter_time_windows?(extension.definition)
    extension.safe_run(event) do |output, status|
      yield(status == 0, filter_name)
    end
  else
    yield(false, filter_name)
  end
end

#filter_attributes_match?(event, filter_attributes, event_attributes = nil) ⇒ TrueClass, FalseClass

Determine if all filter attribute values match those of the corresponding event attributes. Attributes match if the value objects are equivalent, are both hashes with matching key/value pairs (recursive), have equal string values, or evaluate to true (Ruby eval).

Parameters:

  • event (Hash)
  • filter_attributes (Object)
  • event_attributes (Object) (defaults to: nil)

Returns:

  • (TrueClass, FalseClass)


142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/sensu/server/filter.rb', line 142

def filter_attributes_match?(event, filter_attributes, event_attributes=nil)
  event_attributes ||= event
  filter_attributes.all? do |key, value_one|
    value_two = event_attributes[key]
    case
    when value_one == value_two
      true
    when value_one.is_a?(Hash) && value_two.is_a?(Hash)
      filter_attributes_match?(event, value_one, value_two)
    when value_one.to_s == value_two.to_s
      true
    when value_one.is_a?(String) && value_one.start_with?(EVAL_PREFIX)
      eval_attribute_value(event, value_one, value_two)
    else
      false
    end
  end
end

#filter_event(handler, event) {|event| ... } ⇒ Object

Attempt to filter an event for a handler. This method will check to see if handling is disabled, if the event action is handled, if the event check severity is handled, if the handler is subdued, and if the event is filtered by any of the filters specified in the handler definition.

Parameters:

  • handler (Hash)

    definition.

  • event (Hash)

Yields:

  • (event)

    callback/block called if the event has not been filtered.

Yield Parameters:

  • event (Hash)


288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/sensu/server/filter.rb', line 288

def filter_event(handler, event)
  details = {:handler => handler, :event => event}
  filter_message = case
  when handling_disabled?(event)
    "event handling disabled for event"
  when !handle_action?(handler, event)
    "handler does not handle action"
  when !handle_severity?(handler, event)
    "handler does not handle event severity"
  when handler_silenced?(handler, event)
    "handler is silenced"
  end
  if filter_message
    @logger.info(filter_message, details)
    @in_progress[:events] -= 1 if @in_progress
  else
    event_filtered?(handler, event) do |filtered, filter_name|
      unless filtered
        yield(event)
      else
        details[:filter] = filter_name
        @logger.info("event was filtered", details)
        @in_progress[:events] -= 1 if @in_progress
      end
    end
  end
end

#handle_action?(handler, event) ⇒ TrueClass, FalseClass

Determine if an event with an action should be handled. An event action of ‘:flapping` indicates that the event state is flapping, and the event should not be handled unless its handler has `:handle_flapping` set to `true`.

Parameters:

  • handler (Hash)

    definition.

  • event (Hash)

Returns:

  • (TrueClass, FalseClass)


35
36
37
38
# File 'lib/sensu/server/filter.rb', line 35

def handle_action?(handler, event)
  event[:action] != :flapping ||
    (event[:action] == :flapping && !!handler[:handle_flapping])
end

#handle_severity?(handler, event) ⇒ TrueClass, FalseClass

Determine if an event with a check severity will be handled. Event handlers can specify the check severities they will handle, using the definition attribute ‘:severities`. The possible severities are “ok”, “warning”, “critical”, and “unknown”. Handler severity filtering is bypassed when the event `:action` is `:resolve`, if the check history contains one of the specified severities since the last OK result.

Parameters:

  • handler (Hash)

    definition.

  • event (Hash)

Returns:

  • (TrueClass, FalseClass)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/sensu/server/filter.rb', line 51

def handle_severity?(handler, event)
  if handler.has_key?(:severities)
    case event[:action]
    when :resolve
      event[:check][:history].reverse[1..-1].any? do |status|
        if status.to_i == 0
          break false
        end
        severity = SEVERITIES[status.to_i] || "unknown"
        handler[:severities].include?(severity)
      end
    else
      severity = SEVERITIES[event[:check][:status]] || "unknown"
      handler[:severities].include?(severity)
    end
  else
    true
  end
end

#handler_silenced?(handler, event) ⇒ TrueClass, FalseClass

Determine if an event handler is silenced.

Parameters:

  • handler (Hash)

    definition.

  • event (Hash)

Returns:

  • (TrueClass, FalseClass)


13
14
15
# File 'lib/sensu/server/filter.rb', line 13

def handler_silenced?(handler, event)
  event[:silenced] && !handler[:handle_silenced]
end

#handling_disabled?(event) ⇒ TrueClass, FalseClass

Determine if handling is disabled for an event. Check definitions can disable event handling with an attribute, ‘:handle`, by setting it to `false`.

Parameters:

  • event (Hash)

Returns:

  • (TrueClass, FalseClass)


23
24
25
# File 'lib/sensu/server/filter.rb', line 23

def handling_disabled?(event)
  event[:check][:handle] == false
end

#in_filter_time_windows?(filter) ⇒ TrueClass, FalseClass

Determine if a filter is to be evoked for the current time. A filter can be configured with a time window defining when it is to be evoked, e.g. Monday through Friday, 9-5.

Parameters:

  • filter (Hash)

    definition.

Returns:

  • (TrueClass, FalseClass)


167
168
169
170
171
172
173
# File 'lib/sensu/server/filter.rb', line 167

def in_filter_time_windows?(filter)
  if filter[:when]
    in_time_windows?(filter[:when])
  else
    true
  end
end

#native_filter(filter_name, event) {|filtered| ... } ⇒ Object

Determine if an event is filtered by a native filter.

Parameters:

  • filter_name (String)
  • event (Hash)

Yields:

  • (filtered)

    callback/block called with a single parameter to indicate if the event was filtered.

Yield Parameters:

  • filtered (TrueClass, FalseClass)

    indicating if the event was filtered.

  • filter_name (String)

    name of the filter being evaluated



184
185
186
187
188
189
190
191
192
# File 'lib/sensu/server/filter.rb', line 184

def native_filter(filter_name, event)
  filter = @settings[:filters][filter_name]
  if in_filter_time_windows?(filter)
    matched = filter_attributes_match?(event, filter[:attributes])
    yield(filter[:negate] ? matched : !matched, filter_name)
  else
    yield(false, filter_name)
  end
end

#process_eval_string(event, raw_eval_string) ⇒ String

Process a filter eval attribute, a Ruby ‘eval()` string containing an expression to be evaluated within the scope/context of a sandbox. This methods strips away the expression prefix, `eval:`, and substitues any dot notation tokens with the corresponding event data values. If there are unmatched tokens, this method will return `nil`.

Returns:

  • (String)

    processed eval string.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/sensu/server/filter.rb', line 81

def process_eval_string(event, raw_eval_string)
  eval_string = raw_eval_string.slice(5..-1)
  eval_string, unmatched_tokens = substitute_tokens(eval_string, event)
  if unmatched_tokens.empty?
    eval_string
  else
    @logger.error("filter eval unmatched tokens", {
      :raw_eval_string => raw_eval_string,
      :unmatched_tokens => unmatched_tokens,
      :event => event
    })
    nil
  end
end