ansible-rulebook

repository·main·Indexed 42 days ago

https://github.com/ansible/ansible-rulebook

A command line tool for event-driven automation that enables users to react to real-time event streams by triggering Ansible playbooks or job templates. It supports standalone and worker modes, hot-reloading of rulebooks, variable substitution, and integration with Ansible Automation Platform (AAP) Controller.

Tokens
32.7K
Snippets
100
Records
133
Agent score
51%

What's inside ansible-rulebook

  1. Overview of ansible-rulebook

    main

    ansible-rulebook is a command line tool designed for event-driven automation in Ansible. It allows you to connect to event streams and react to specific events in near real time by executing playbooks or other predefined actions.

    Key capabilities include:

    • Connecting to event streams for real-time event handling.
    • Conditionally launching Ansible playbooks or Ansible Automation Platform (formerly Tower) job templates based on rules that match incoming events.
    • Storing facts derived from event data.
    • Limiting playbook execution to specific hosts based on data contained within the event.
    • Running smaller, faster jobs by using event data to scope host execution.
  2. What is an Ansible Rulebook and how does it work?

    main

    An Ansible Rulebook is a YAML-based file used to codify operational logic for Event-Driven Ansible (EDA). Unlike traditional Ansible Playbooks which follow a linear sequence of tasks, Rulebooks use an "if-this-then-that" approach to respond to events.

    Rulebooks are processed by a rules engine (built using Drools) and are comprised of three main components:

    1. Sources: Define the event listening source. Examples include webhooks, Kafka, Azure service bus, file changes, and alertmanager.
    2. Rules: Define the conditional logic used to match incoming events from a source. If a condition is met, an action is triggered.
    3. Actions: The tasks executed when a rule matches. Common actions include:
      • run_playbook: Executes an Ansible Playbook.
      • run_module: Executes an Ansible module directly.
      • set_fact: Sets facts within the rulebook context.
      • post_event: Posts a new event back to the event handler.
      • debug: Provides debugging information.
  3. Handle Concurrent Messages with Event Feedback

    main

    Because the eda_feedback_queue is shared per source plugin instance, if your plugin processes messages concurrently, you cannot simply wait for the next item in the queue. You must correlate the feedback with the specific event.

    Option 1: Serialized Processing (Simplest) Process one message at a time. Wait for the feedback of the current message before moving to the next one in the loop. This is the safest and easiest pattern to implement.

    Option 2: Tracking In-Flight Events (High Performance) If you must process messages concurrently, maintain a local mapping (e.g., a dictionary) of uuid -> (message, commit_future).

    1. Start a background task to continuously consume from eda_feedback_queue.
    2. When feedback arrives, look up the uuid in your mapping.
    3. Signal the specific message's Future that it is safe to commit.
    4. The main loop awaits the Future for that specific message before calling consumer.commit().
    # Option 2: Tracking in-flight events snippet
    import asyncio
    
    pending_events = {}  # uuid -> (message, commit_future)
    
    async def process_feedback(eda_feedback_queue):
        while True:
            feedback = await eda_feedback_queue.get()
            uuid = feedback.get("meta", {}).get("uuid")
    
            if uuid in pending_events:
                message, future = pending_events.pop(uuid)
                future.set_result(message)  # Signal ready to commit
            else:
                logger.warning("Received feedback for unknown UUID: %s", uuid)
  4. Override the default container command with container_init.cmd

    main
    The container_init.cmd option allows you to override the default command executed when the Decision Environment container is launched. This is useful for pre-configuring the container to run a specific playbook or rulebook without needing to pass full command-line arguments at runtime. Note that this can still be overridden at runtime by passing a command to the container (e.g., via docker run).
  5. Use Jinja filters in rule actions

    main

    When a rule matches, you can use Jinja filters within the action to transform or manipulate data. This allows you to process event payloads before they are used by the action.

    Note: Jinja filters can be used in actions, but they cannot be used within the condition part of a rule.

    Commonly used filters include:

    • regex_replace
    • basename
    • dirname
    • normpath
    • Standard Python string methods
  6. Match multiple events from the same source

    main

    In an Ansible Rulebook, you can define conditions that require multiple events from the same source to match before an action is executed. When multiple events are matched, the playbook receives two additional variables: events and facts. These variables contain the data from the matched events, indexed by their match order or custom assignments.

    By default, if you use a simple all condition, the events are indexed by their match sequence (e.g., m_0, m_1).

    condition:
      all:
        - event.i == 1
        - event.i == 2
  7. Understand the plugin resolution order in ansible-rulebook

    main

    When ansible-rulebook resolves a plugin name, it follows this specific order of operations:

    1. Built-in Legacy Mappings: Checks internal mappings (e.g., ansible.eda.range $\rightarrow$ eda.builtin.range).
    2. Collection Metadata: Loads eda_runtime.yml from the plugin's collection.
    3. Deprecation Check: If a deprecation is defined, it logs a warning.
    4. Tombstone Check: If a tombstone is defined, it raises an error.
    5. Redirect Check: If a redirect is defined, it follows the new plugin name.
    6. Recursion: Repeats steps 2-5 for each redirect in the chain (up to a maximum of 10 hops).
  8. How event filters work in ansible-rulebook

    main

    Event filters allow you to preprocess, modify, enrich, or transform event data before it reaches the rule engine. This is useful for removing unnecessary information or reshaping data so it matches your rule conditions.

    Key Concepts

    • Chaining: Filters can be chained together. The output of one filter becomes the input for the next.
    • Placement: Filters are defined in the rulebook under a sources section, immediately following the source definition.
    • Execution: When the rulebook starts a source plugin, it associates the defined filters and transforms the data before placing it into the event queue.
    • Data Access: You can access nested keys in the event payload using dot (.) notation.
    • Key Constraints: Event payload keys should ideally contain only letters, numbers, and underscores.

    Custom Filters

    When developing your own filters, you can use the -F flag to specify a local directory containing them. Note that if you are using a Decision Environment, filters must be distributed via an Ansible collection.

    sources:
      - name: azure_service_bus
        ansible.eda.azure_service_bus:
          conn_str: "{{connection_str}}"
          queue_name: "{{queue_name}}"
        filters:
          - json_filter:
              include_keys: ['clone_url']
              exclude_keys: ['*_url', '_links', 'base', 'sender', 'owner', 'user']
          - dashes_to_underscores:
  9. Use multiple conditions where any must match (any)

    main

    The any keyword triggers a rule as soon as any one of the provided conditions matches a distinct incoming event.

    Critical Rules for any conditions:

    1. Use events (plural): Even though only one event triggers the rule, you must use the events prefix to access the matching event (e.g., events.m_0).
    2. Custom Naming: Use the << operator to assign the matching event to a custom name like events.match for easier access in actions.
    3. Difference from or: any evaluates different incoming events. To check multiple attributes on a single event, use the or operator.

    Warning: Using any to check different values for the same attribute (e.g., event.os == 'linux' or event.os == 'windows') will wait for two different events to arrive, which is usually not the intended behavior for a single event check.

    # Using custom assignment for readability
    name: Monitor different event types with custom name
    condition:
      any:
        - events.match << event.temperature == 85
        - events.match << event.user == "alice"
        - events.match << event.inventory == 42
    action:
      debug:
        msg: "Found a match: {{ events.match }}"
  10. Understand how rule results are returned

    main

    When a rule's condition(s) are satisfied, the data returned depends on the number of conditions:

    • Multiple conditions: Results are returned as events/facts.
    • Single condition: Results are returned as event/fact.

    This data is made available to your playbook as extra_vars when it is invoked. The facts/fact key is an exact copy of the events/event key, so you can use either in your playbook.

  11. Core Concepts of Ansible Rulebook

    main

    Ansible Rulebook operates on a continuous loop of three main components:

    1. Event Source: A plugin that listens for or generates events (e.g., a webhook, a range of values, or an alert from a monitoring system).
    2. Rules: A set of conditions that are checked against incoming events. A rule consists of a condition that must evaluate to true based on the event data.
    3. Action: The task to be performed when a rule's condition is met (e.g., run_playbook).

    Unlike ansible-playbook, which runs once and exits, ansible-rulebook runs continuously, waiting for events to match its rules. It only exits upon a shutdown action or if the event source itself fails.