Rules

Event driven automation uses rules to determine if an action should be taken when an event
is received. A rule comprises of a name, condition(s) and action. The rule decides to run
an action by evaluating the condition(s) that is defined by the rule book author.

Name

Each rule in a rule set should have a unique name. This name is displayed to the end user in the eda-server UI to track which rules were fired.

Conditions

The condition(s) is written using a subset of Jinja syntax. Each of the
condition(s) can use information from
  • Event received

  • Previously saved events within the rule

  • Longer term facts about the system

  • Variables provided by the user at startup saved as facts

When writing conditions
  • use the event prefix when accessing data from the current event

  • use the fact prefix when accessing data from the saved facts or passed in variables

  • use the events prefix when assigning variables and accessing data within the rule

  • use the facts prefix when assigning variables and accessing data within the rule

A condition cannot contain Jinja style substitution when accessing variables passed in
from the command line, we loose the data type information and the rule engine will not
process the condition. Instead use the fact prefix to access the data passed in from the
command line

The following is an example rule:

name:  An automatic remediation rule
condition:  event.outage == true
action:
    run_playbook:
        name: remediate_outage.yml
This rule searches for a recent event with the data outage being true. If an event
with this condition is found then the remediate_outage.yml playbook is run.
After an event is matched it is immediately removed and will not be used in subsequent
rules.
A condition can contain
  • One condition

  • Multiple conditions where all of them have to match

  • Multiple conditions where any one of them has to match

Examples

Single condition

name:  An automatic remediation rule
condition:  event.outage == true
action:
    run_playbook:
        name: remediate_outage.yml

Multiple conditions where all of them have to match

name: All conditions must match
condition:
  all:
    - event.target_os == "linux"
    - event.tracking_id == 345
action:
  debug:
As we receive events from the source plugins we send them to the appropriate
rule set sessions running in the rule engine.
With multiple conditions the rule engine will keep track of the conditions that
have matched and wait for the next event to come in which might match other conditions.
Once all the conditions have been met, it will return you all the events that matched,
which can be used in action.

Multiple conditions where any one of them has to match

name: Any condition can match
condition:
  any:
    - event.target_os == "linux"
    - event.target_os == "windows"
action:
  debug:

Multiple conditions with facts and events and all of one of them have to match

name: Condition using both a fact and an event
condition:
  all:
    - fact.meta.hosts == "localhost"
    - event.target_os == "windows"
action:
  debug:

Condition with fact and event, fact being passed in via –variables on command line

name: Condition using a passed in variable and an event
condition: event.i == fact.custom.expected_index
action:
  debug:

In the above example the custom.expected_index was passed in via –variables from the command line to ansible-rulebook.

When evaluating a single event you can compare multiple
properties/attributes from the event using and or or

Logical and

name: Multiple Attribute match from a single event
condition: event.target_os == "linux" and event.version == "1.1"
action:
  debug:

Logical or

name: Match any one attribute from a single event
condition: event.version == "2.0" or event.version == "1.1"
action:
  debug:
The “and” and “or” keywords are case sensitive. You can’t use
“AND” or “OR” for the logical operators.
When a condition is evaluated if the condition passes the matching event
it is stored in well known attribute(s) called m, m_1, m_2…..
You can optionally alias these attribute(s) using the << operator e.g

Multiple conditions with assignment

name: multiple conditions
condition:
  all:
    - events.first << event.i == 0
    - events.second << event.i == 1
    - events.third << event.i == events.first.i + 2
action:
  debug:
    first: "{{events.first}}"
    second: "{{events.second}}"
    third: "{{events.third}}"
When using the assignment operator the attribute names should have the
events. or facts. prefix. In the above example we are saving the
matching events per condition as events.first, events.second and events.third.
In the third condition we are accessing the saved event in events.first to do
a comparison. events and facts have rule scope and are not available
outside of the rule. They can be used in assignments and accessing the saved
values in a condition or in action.

Multiple condition with default assignments

name: multiple conditions
condition:
   all:
     - event.i == 1
     - event.i == 2
     - event.i == events.m.i + 3
action:
   debug:
     first: "{{events.m}}"
     second: "{{events.m_1}}"
     third: "{{events.m_2}}"

The first match is stored as m, and the subsequent ones are stored as m_1, m_2

Single condition assignment (Not supported)

name: assignment ignored
condition: event.first << event.i == 0
action:
  debug:
    event: "{{event}}"
Assignment cannot be used for rules that have a single condition, the
matching event will always be called event. In the above example event.first
is ignored and the matching event is stored as event. Compare this to multiple
condition rules where the matching events are stored as events

Actions

When a rule matches the condition(s), it fires the corresponding action for the rule. The following actions are supported

Actions

Name

Description

run_playbook

Run an Ansible playbook from a collection

run_module

Run an Ansible module from a collection or from the Ansible built in modules

set_fact

Set a fact for the rule set, will fire all matching rules different from post_event

post_event

Assert an event to the rule set, will fire the first matching rule. An event is retracted after it matches.

retract_fact

Retract a fact from the rule set, will fire all matching rules that checks for the missing fact.

print_event

Print the matching event to stdout

shutdown

Generate a shutdown event

debug

Log the matching event

none

No operation

run_playbook

Run a playbook

Name

Description

Required

name

The name of the playbook, using the FQCN (fully qualified collection name)

Yes

set_facts

The artifacts from the playbook execution are inserted back into the rule set as facts

No

post_events

The artifacts from the playbook execution are inserted back into the rule set as events

No

ruleset

The name of the ruleset to post the event or assert the fact to, default is current rule set.

No

retry

If the playbook fails execution, retry it once, boolean value true|false

No

retries

If the playbook fails execution, the number of times to retry it. An integer value

No

delay

The retry interval, an integer value specified in seconds

No

verbosity

Verbosity level when running the playbook, a value between 1-4

No

var_root

If the event is a deeply nested dictionary, the var_root can specify the key name whose value should replace the matching event value. The var_root can take a dictionary to account for data when we have multiple matching events.

No

* (any other args)

These will be passed to the playbook

No

run_module

Run an Ansible module

Name

Description

Required

name

The name of the module, using the FQCN (fully qualified collection name)

Yes

module_args

The arguments to pass into the Ansible Module

No

retry

If the module fails execution, retry it once, boolean value true|false. Default false

No

retries

If the module fails execution, the number of times to retry it. Integer value, default 0

No

delay

The retry interval, an integer value

No

verbosity

Verbosity level when running the module, a value between 1-4

No

post_event

Post an event to a running rule set in the rules engine

Name

Description

Required

event

The event dictionary to post

Yes

ruleset

The name of the rule set to post the event, default is the current rule set name

No

Example:

action:
  post_event:
    ruleset: Test rules4
    event:
      j: 4

Example, using data saved with assignment

name: multiple conditions
condition:
  all:
    - events.first << event.i == 0
    - events.second << event.i == 1
    - events.third << event.i == events.first.i + 2
action:
  post_event:
    ruleset: Test rules4
    event:
      data: "{{events.third}}"
The events and facts prefixes have rule scope and cannot be accessed outside of
rules. Please note the use of Jinja substitution when accessing the event results.

set_fact

Post a fact to the running rule set in the rules engine

Name

Description

Required

fact

The fact dictionary to post

Yes

ruleset

The name of the rule set to post the fact, default is the current rule set name

No

Example

action:
    set_fact:
      ruleset: Test rules4
      fact:
        j: 1

Example, using data saved with assignment in multiple condition

name: multiple conditions
condition:
  all:
    - events.first << event.i == 0
    - events.second << event.i == 1
    - events.third << event.i == events.first.i + 2
action:
  set_fact:
    ruleset: Test rules4
    fact:
      data: "{{events.first}}"

Example, using data saved with single condition

name: single condition
condition: event.i == 23
action:
  set_fact:
    fact:
      myfact: "{{event.i}}"
A rulebook can have multiple rule sets, the set_fact/retract_fact/post_event allow you
to target different rule sets within the rulebook. You currently cannot assert an event to
multiple rule sets, it can be asserted to a single rule set. The default being the current
rule set. Please note the use of Jinja substitution in the above examples when accessing
the event results in an action.

retract_fact

Remove a fact from the running rule set in the rules engine

Name

Description

Required

fact

The fact dictionary to remove

Yes

ruleset

The name of the rule set to retract the fact, default is the current rule set name

No

Example:

action:
  retract_fact:
    ruleset: Test rules4
    fact:
      j: 3

shutdown

Generate a shutdown event which will terminate the rulebook engine. If there are multiple
If there are multiple rule-sets running in your rule book, issuing a shutdown will cause
all other rule-sets to end, care needs to be taken to account for running playbooks which
can be impacted when one of the rule set decides to shutdown.

Example:

name: shutdown after 5 events
condition: event.i >= 5
action:
   shutdown:

debug

Write the event to stdout No arguments needed

none

No action, useful when writing tests No arguments needed

Results

When a rule’s condition are satisfied we get the results back as
  • events/facts for multiple conditions

  • event/fact if a single condition

This data is made available to your playbook as extra_vars when its invoked.
In all the examples below you would see that facts/fact is an exact copy of events/event respectively
and you can use either one of them in your playbook.

Single condition rule

name: "Single event"
condition: event.i == 1
action:
     debug:


The extra_vars passed into the playbook will contain this data

{'event': {'i': 1}, 'fact': {'i': 1}}

Multiple condition rule with no assignment

name: "Multiple event"
condition:
   all:
     - event.i == 1
     - event.i == 3
action:
   debug:

The extra vars passed into the playbook will contain this data

{'events': {'m': {'i': 1}, 'm_1': {'i': 3}},
 'facts':  {'m': {'i': 1}, 'm_1': {'i': 3}}}

Multiple condition rule with assignment

  name: "Multiple event with assignment"
  condition:
     all:
       - events.first << event.i == 1
       - events.second << event.i == 3
  action:
     debug:

The extra vars passed into the playbook will contain this data

 {'events': {'first': {'i': 1}, 'second': {'i': 3}},
  'facts':  {'first': {'i': 1}, 'second': {'i': 3}}}

Multiple condition rule with both a fact and an event without assignment

   name: r2
   condition:
     all:
       - event.i == 8
       - fact.os == "windows"
   action:
     debug:

The extra vars passed into the playbook will contain this data

  {'events': {'m_0': {'i': 8}, 'm_1': {'os': 'windows'}},
   'facts':  {'m_0': {'i': 8}, 'm_1': {'os': 'windows'}}}

Multiple condition rule with both a fact and an event with assignment

 name: r2
 condition:
     all:
       - events.attr1 << event.i == 8
       - events.attr2 << fact.os == "windows"
 action:
     debug:

The extra vars passed into the playbook will contain this data

 {'events': {'attr1': {'i': 8}, 'attr2': {'os': 'windows'}},
  'facts':  {'attr1': {'i': 8}, 'attr2': {'os': 'windows'}}}

Supported Operators

The conditions use a subset of Jinja syntax, the following operators are currently supported

Operators

Name

Description

==

The equality operator for strings and numbers

!=

The non equality operator for strings and numbers

>

The greater than operator for numbers

<

The less than operator for numbers

>=

The greater than equal to operator for numbers

<=

The less than equal to operator for numbers

+

The addition operator for numbers

-

The subtraction operator for numbers

*

The multiplication operator for numbers

and

The conjunctive add, for making compound expressions

or

The disjunctive or

is defined

To check if a variable is defined

is not defined

To check if a variable is not defined

<<

Assignment operator, to save the matching events or facts with events or facts prefix

FAQ

Q: In a multiple condition scenario when 1 event matches and the rest of the events don’t match
how long does the Rule engine keep the previous event around?
Ans: Currently there is no time limit on how long the rule engine keeps the matched event.
Once they match they are retracted.
Q: When does the Ansible rulebook stop processing?
Ans: When a Shutdown event is generated from the source plugin, or shutdown action is invoked.
Q: Will a condition be evaluated if a variable is missing?
Ans: If a condition refers to an object.attribute which doesn’t exist then that condition
is skipped and not processed.

Example:

name: send to debug
condition: event.payload.eventType != 'GET'
action:
     debug:


In the above case if any of the event.payload.eventType is undefined the condition is
ignored and doesn't match anything.
Q: When a rule book has multiple rule sets and one of them shuts down are all rule sets terminated?
Ans: Yes, so care should be taken if there are any playbooks running in the other rule sets
Q: How do I check if an attribute in an object referred in a condition exists?
Ans: Use the is defined

Example:

name: rule1
condition: event.msg is defined
action:
  retract_fact:
    fact:
      msg: "{{event.msg}}"
Q: How do I check if an attribute in an object referred in a condition does not exist?
Ans: Use the is not defined

Example:

name: rule2
condition: fact.msg is not defined
action:
  set_fact:
    fact:
      msg: Hello World