Mantra Networking Mantra Networking

Normir Automation: Result

Normir Automation: Result
Created By: Lauren R. Garcia

Table of Contents

  • Overview
  • Nornir Result Object: Role and Structure
  • Example: Capturing and Using Results
  • Key Points for Engineers
  • Display and Formatting
  • Summary Table: How Nornir Hands Back Results
  • Conclusion

Nornir Automation: Result

Overview

The Nornir Result object is a central concept in the Nornir automation framework. It serves as the structured feedback mechanism every time a task runs on a device within your network automation workflows. The Result object collects and organizes the execution outcome—success, failure, details, and outputs—across all devices and tasks, providing deep visibility and control for network engineers.

Why You Need to Know About It

  • Error Detection & Troubleshooting: Result objects make it easy to identify which tasks or devices failed, pinpoint exceptions, and retrieve context for rapid troubleshooting.
  • Automation Logic: The detailed information carried by each result allows you to write smarter automation, such as automatically retrying failed devices, branching logic, or triggering alerts based on specific outcomes.
  • Auditability & Reporting: For compliance or operational oversight, Result objects offer a full, per-device and per-task trail. They can be parsed for logs, dashboards, or aggregated reports.
  • Fine-Grained Control: Because Nornir produces a separate result object per task and per device, you can make granular decisions. For example, perform a config diff only when changes have occurred, or escalate issues only for critical hosts.

How It Works

  • Execution & Capture: When Nornir runs a task, it creates a Result object that records the primary output (result), success or failure flags, exceptions, host identity, whether a change occurred, configuration diffs, and more.
  • Hierarchical Structure: Results are organized hierarchically. At the top, you have an AggregatedResult encompassing all devices. Underneath, each device has a MultiResult collection for all tasks run on it, and each of those contains a Result object for a specific task.
  • Nesting & Subresults: For tasks that themselves involve multiple steps (for example, nested configuration commands), subresults are maintained as a list within the parent result, ensuring full traceability of multi-step workflows.
  • Programmable Access: Results can be programmatically examined in Python to automate decision making. For instance, you can iterate through all hosts, check for failures, or extract specific fields (outputs, diffs, exceptions) for further analysis or reporting.
  • Formatting & Display: Results are readily displayable in human-readable formats (CLI, logs, dashboards) using helper functions or can be exported as JSON/YAML for integration with other tools.

In summary, the Nornir Result object is not just a return value—it’s a rich, structured source of operational intelligence that empowers network automation to be reliable, auditable, and responsive to real-world challenges. Understanding and utilizing Result objects is fundamental to building resilient, scalable, and maintainable automation systems in modern network engineering.

Nornir Result Object: Role and Structure

The Nornir Result object is a fundamental building block of Nornir, encapsulating the outcome of any task executed across devices. Understanding how this object works is essential for troubleshooting, automation logic, and reporting in network automation workflows.

  • Purpose and Role in Workflows:
    The Result object acts as a structured record for every action Nornir performs on a device. It contains detailed status, output, error messages, and metadata for each executed task, ensuring visibility into what was attempted and what happened.
  • Key Attributes:
    • result: The main output or response from the task (such as CLI command output, API response, parsed data, etc.).
    • failed: A boolean flag indicating if the task failed (True) or succeeded (False).
    • exception: Contains any Python exception object raised during task execution, useful for debugging.
    • host: The name of the host (device) the result is associated with.
    • changed: Indicates if the task made changes on the target device (True) or not.
    • diff: Shows the difference between the previous and updated configuration (present when applicable).
    • severity_level: Sets the impact level (typically for Nornir’s logging system).
  • Nesting and Hierarchy:
    For multi-step tasks or aggregated operations, the Result object can contain a list of subresults, each representing an individual action or step within a larger workflow. This allows fine-grained reporting on complex jobs.

Example:

{
  "result": "Command completed successfully",
  "failed": false,
  "exception": null,
  "host": "router-01",
  "changed": true,
  "diff": "+ interface Loopback10\n+ ip address 10.10.10.1/32",
  "severity_level": 0
}

By leveraging the Result object’s structure, engineers can automate decision logic, create robust error handling, and generate detailed operational reports in their Nornir-driven automation pipelines.

Example: Capturing and Using Results

Capturing and working with results in Nornir allows engineers to conditionally handle errors, extract command outputs, create logic-based workflows, or write automation reports. Here’s a step-by-step example using a basic Nornir script:

  1. Step 1: Define the Task
    You first define a function that represents the task being executed. For example, running a command on network devices:
    from nornir import InitNornir
    from nornir_netmiko.tasks import netmiko_send_command
    
    def get_interfaces(task):
        return task.run(task=netmiko_send_command, command_string="show ip interface brief")
  2. Step 2: Initialize Nornir and Run the Task
    Use your configuration file to initialize Nornir and run the task:
    nr = InitNornir(config_file="config.yaml")
    results = nr.run(task=get_interfaces)
  3. Step 3: Access and Use Results
    Once you have the results, you can iterate through them and access specific fields like output, failure states, and diffs:
    for host, result in results.items():
        print(f"Results for {host}:")
        for r in result:
            if r.failed:
                print("Task failed:", r.exception)
            else:
                print(r.result)
  4. Step 4: Build Your Own Logic
    You can use these structures to automate response triggers, implement retries, or generate reports:
    if any(r.failed for r in result):
        # Retry logic or alert
        alert_engine("Issue detected on device", host)

Sample Output Structure:

{
  "router-1": [
    {
      "result": "Interface  IP-Address  OK? Method Status...",
      "failed": false,
      "exception": null,
      "changed": false,
      "host": "router-1"
    }
  ]
}

By capturing and parsing results, you unlock dynamic control within your Nornir automation pipeline — allowing error handling, real-time decision making, and clean output formatting for logs or dashboards.

Key Points for Engineers

When working with Nornir's Result objects in network automation, understanding these essential practices helps maximize efficiency and reliability:

  1. Always Inspect Results
    Don’t just assume every task succeeded. Examine the failed and exception attributes of each result object to catch any errors early. This prevents partial configuration and silent failures.
  2. Leverage Parallel Execution
    Nornir excels at executing tasks in parallel across many devices. This dramatically improves speed when automating large-scale changes or verifications.
  3. Exploit Granularity in Results
    Each device and each task yields its own result object, so you can see, log, and act on outcomes at a per-device and per-task level.
  4. Take Advantage of Built-in Logging
    Use Nornir’s logging and output formatting functions (like print_result()) for visibility and to maintain audit trails of everything executed.
  5. Write Logic Based on Outcomes
    Use the structure of result objects to create intelligent automation workflows—automatically retry failed operations, alert on anomalies, or conditionally perform further actions.
  6. Use Plugins Strategically
    Expand Nornir's core functionalities with plugins designed for specific vendor devices or workflows to further customize result handling and task execution.
  7. Validate and Diff Changes
    For configuration tasks, inspect the diff field (where present) to verify changes and support change-control or compliance reporting requirements.

By keeping these points in mind, network engineers can create robust, scalable, and reliable Nornir-driven automation pipelines that are resilient to errors and easy to maintain.

Display and Formatting

Presenting Nornir results in a clean and readable format is crucial when debugging, auditing, or simply sharing results with other engineers. Nornir provides built-in methods and practices to help display results effectively in scripts, CLI outputs, or external logs. Here’s how:

  1. Use the print_result() Helper
    The print_result() function from Nornir’s utilities module is the quickest way to print results in a tree-like structure.
    from nornir_utils.plugins.functions import print_result
    
    print_result(result)
    This displays a nested, readable breakdown of each task, host, and result — including failure flags and output.
  2. Customize Output with rich
    For more advanced formatting, you can integrate the rich library to colorize or style the output for CLI tools or TUI dashboards.
    from rich import print
    print(f"[green]{result['router1'][0].result}[/green]")
  3. Exporting to JSON
    When results need to be stored, integrated with external systems, or transmitted via APIs, convert them into JSON format:
    import json
    
    json_string = json.dumps(result["router1"][0].result, indent=2)
    This is useful for logging systems, dashboards, or storing daily automation snapshots.
  4. Handling Large Outputs
    If your commands return extensive data (like entire configuration files or routes), consider trimming, paginating, or filtering the result output for clarity:
    full_output = result["core-switch"][0].result
    first_10_lines = "\n".join(full_output.splitlines()[:10])
  5. Tabular Formats and Jinja Templates
    Format structured data (like interfaces, routes, BGP peers) into fixed-width tables using Jinja2 templates or tabulate:
    from tabulate import tabulate
    
    parsed_output = [
        ["Gig0/0", "up", "10.0.0.1"],
        ["Gig0/1", "down", "-"]
    ]
    print(tabulate(parsed_output, headers=["Interface", "Status", "IP"]))

Good formatting practices make results more actionable, easier to monitor, and ready for presentation in automation reports or CI/CD pipelines.

Summary Table: How Nornir Hands Back Results

Nornir structures its results in a hierarchical way that makes it easy to retrieve exactly what you need, whether it’s a global summary, per-device breakdown, or individual task result. Here’s a summary table of the key result objects and what each one organizes:

Object Type Description What It Holds Access Example
AggregatedResult Top-level, dict-like object grouping results per host/device Each key is a hostname; values are MultiResult objects result['router1']
MultiResult List-like container for all results of tasks (and subtasks) run on a host Each item is a Result object for one specific task/subtask result['router1'][0]
Result Individual object representing outcome of a single task/subtask Output, failed flag, exception, changed, diff, severity, etc. result['router1'][0].result
result['router1'][0].failed

Here’s a quick visual to show the hierarchy:


AggregatedResult
 ├── 'router1': MultiResult
 │    ├── Result (task 1)
 │    ├── Result (task 2)
 ├── 'router2': MultiResult
 │    ├── Result (task 1)
 │    ├── Result (task 2)

This structure lets you easily drill down from the big picture (all devices and tasks) to granular details for precise reporting, debugging, or automation logic.

Conclusion

Throughout this blog post, we explored the core structure, functionality, and power of the Nornir Result object within network automation workflows. Whether you're troubleshooting failed tasks, extracting interface information, or building CI/CD pipeline logic — understanding how Nornir hands back results is key to working smarter, not harder.

Let’s quickly recap the key takeaways:

  • 🎯 Result objects are your feedback loop in every automation workflow. They contain output, success/failure status, exceptions, diffs, and more.
  • 🔍 Each host and task set receives its own structured result, empowering fine-grained control over monitoring and logic.
  • 📊 Formatting tools like print_result()json.dumps(), or tabulate() help you display results clearly and readably — great for logs or reports.
  • âś… Engineers can use Nornir results to trigger conditionals, retries, notifications, and even decision-making within task chains.
  • 📚 Result hierarchies — AggregatedResult > MultiResult > Result — keep your task data well organized, giving you full visibility at every level.

By mastering how results are captured, formatted, and interpreted, you position yourself to build clean, scalable, and intelligent Nornir automations that go beyond simple task execution.

Thanks for following along — and happy automating!
🚀💻🔧