Help us improve your experience.

Let us know what you think.

Do you have time for a two-minute survey?

Using Junos Snapshot Administrator in Python (JSNAPy) in Ansible Playbooks

 

Junos® Snapshot Administrator in Python (JSNAPy) enables you to capture and audit runtime environment snapshots of your network devices running Junos OS. You can capture and verify the operational and configuration status of a device and verify changes to a device. The juniper_junos_jsnapy Ansible module in the Juniper.junos role enables you to execute JSNAPy tests against devices running Junos OS as part of an Ansible playbook.

Note

The Ansible control machine must have Junos Snapshot Administrator in Python Release 1.2.1 or later installed in order to use the juniper_junos_jsnapy module. For installation instructions and information about creating JSNAPy configuration and test files, see the Junos Snapshot Administrator in Python Documentation.

Note

Starting in Ansible for Junos OS Release 2.0.0, the juniper_junos_jsnapy module replaces the functionality of the junos_jsnapy module.

The following sections discuss how to use the juniper_junos_jsnapy module in Ansible playbooks:

juniper_junos_jsnapy Module Overview

The juniper_junos_jsnapy Ansible module enables you to execute many of the same JSNAPy functions from an Ansible playbook as you can execute using JSNAPy on the command line, including capturing and saving a runtime environment snapshot, comparing two snapshots, or capturing a snapshot and immediately evaluating it. The juniper_junos_jsnapy module requires the action argument and either the config_file or the test_files argument.

The action argument specifies the JSNAPy action to perform. Table 1 outlines the valid action values and the equivalent JSNAPy commands.

Table 1: juniper_junos_jsnapy action Argument

action Value

Description

Equivalent JSNAPy Command

check

Compare two snapshots based on the given test cases, or if no test cases are supplied, compare the snapshots node by node.

jsnapy --check

snap_post

Take snapshots for the commands or RPCs specified in the test files after making changes on the given devices.

jsnapy --snap

snap_pre

Take snapshots for the commands or RPCs specified in the test files prior to making changes on the given devices.

jsnapy --snap

snapcheck

Take snapshots of the commands or RPCs specified in the test files and immediately evaluate the snapshots against pre-defined criteria in the test cases.

jsnapy --snapcheck

When you execute JSNAPy on the command line, JSNAPy performs the requested action on the hosts specified in the hosts section of the configuration file. In contrast, the juniper_junos_jsnapy module executes the requested action on the hosts in the Ansible inventory group defined in the playbook. As a result, the juniper_junos_jsnapy module can either reference a configuration file, ignoring the hosts section, or it can directly reference one or more test files.

Thus, in addition to the action argument, the juniper_junos_jsnapy module also requires either the config_file or the test_files argument to specify the JSNAPy configuration file or the JSNAPy test files to use for the given action. Table 2 outlines the config_file and test_files arguments.

Table 2: juniper_junos_jsnapy File Arguments

Module argument

Value

Additional Information

config_file

Absolute or relative file path to a JSNAPy configuration file.

If the path is relative, the module checks for the configuration file in the following locations and in the order indicated:

  • Ansible playbook directory

  • Directory specified by the dir argument, if provided

  • /etc/jsnapy/testfiles directory (only if the dir argument is omitted)

If the configuration file references test files by using a relative file path, the module first checks for the test files in the playbook directory and then checks for the test files in the default testfiles directory, which will vary depending on the JSNAPy release.

test_files

Absolute or relative file path to a JSNAPy test file. This can be a single file path or a list of file paths.

For each test file that specifies a relative path, the module checks for the file in the following locations and in the order indicated:

  • Ansible playbook directory

  • Directory specified by the dir argument, if provided

  • /etc/jsnapy/testfiles directory (only if the dir argument is omitted)

The config_file and test_files arguments can take an absolute or relative file path. When using a relative file path, you can optionally include the dir module argument to specify the directory in which the files reside. If a config_file or test_files argument uses a relative file path, the module first checks for the file under the Ansible playbook directory, even if the dir argument is present. If the file does not exist under the playbook directory, the module checks under the dir argument directory, if it is specified, or under the /etc/jsnapy/testfiles directory, if the dir argument is omitted. The playbook generates an error message if the file is not found.

The following sample playbook performs the snap_pre action using the configuration_interface_status.yaml configuration file. If the configuration file does not exist in the playbook directory, the module checks for the file in the user’s home directory under the jsnapy/testfiles subdirectory.

Note

Starting in Junos Snapshot Administrator in Python Release 1.3.0, the JSNAPy directory structure is modified such that the default location for configuration and test files is the ~/jsnapy/testfiles directory.

The juniper_junos_jsnapy module performs the requested action on the hosts specified in the Ansible playbook, even if the module references a configuration file that includes a hosts section. The module reports failed if it encounters an error and fails to execute the JSNAPy tests. It does not report failed if one or more of the JSNAPy tests fail. To check the JSNAPy test results, register the module’s response, and use the assert module to verify the expected result in the response.

Junos Snapshot Administrator in Python logs information regarding its operations to the /var/log/jsnapy/jsnapy.log file by default. The juniper_junos_jsnapy module can optionally include the logfile argument, which specifies the path to a writable file on the Ansible control machine where information for the particular task is logged. The level of information logged in the file is determined by Ansible’s verbosity level and debug options. By default, only messages of severity level WARNING or higher are logged. To log messages equal to or higher than severity level INFO or severity level DEBUG, execute the playbook with the -v or -vv command-line option, respectively.

When you execute JSNAPy tests in an Ansible playbook, you can enable the jsnapy callback plugin to capture and summarize information for failed JSNAPy tests. To enable the callback plugin, add the callback_whitelist = jsnapy statement to the Ansible configuration file. For more information, see Enabling the jsnapy Callback Plugin.

Taking and Comparing Snapshots

JSNAPy enables you to capture runtime environment snapshots of your network devices running Junos OS before and after a change and compare the snapshots to verify the expected changes or identify unexpected issues. The juniper_junos_jsnapy Ansible module enables you to take and compare JSNAPy snapshots as part of an Ansible playbook. The module saves each snapshot for each host in a separate file in the default JSNAPy snapshot directory using a predetermined filename. For more information about juniper_junos_jsnapy output files, see Understanding the juniper_junos_jsnapy Module Output.

Note

Starting in Junos Snapshot Administrator in Python Release 1.3.0, the default directories for the JSNAPy test files and snapshots are ~/jsnapy/testfiles and ~/jsnapy/snapshots, respectively.

To take baseline snapshots of one or more devices prior to making changes, set the juniper_junos_jsnapy module’s action argument to snap_pre, and specify a configuration file or one or more test files.

The following playbook saves PRE snapshots for each device in the Ansible inventory group. The task references the configuration_interface_status.yaml configuration file in the ~/jsnapy/testfiles directory and logs messages to the jsnapy_tests.log file in the playbook directory.

To take a snapshot of one or more devices after performing changes, set the juniper_junos_jsnapy module’s action argument to snap_post, and specify a configuration file or one or more test files.

The following playbook saves POST snapshots for each device in the Ansible inventory group. The task references the configuration_interface_status.yaml configuration file in the ~/jsnapy/testfiles directory and logs messages to the jsnapy_tests.log file in the playbook directory.

When the juniper_junos_jsnapy module performs a snap_pre action or a snap_post action, it saves each snapshot for each host in a separate file using auto-generated filenames that contain a 'PRE' or 'POST' tag, respectively. To compare the PRE and POST snapshots to quickly verify the updates or identify any issues that might have resulted from the changes, set the module’s action argument to check, and specify the same configuration file or test files that were used to take the snapshots.

When the juniper_junos_jsnapy module performs a check action, the preexisting PRE and POST snapshots for each test on each device are compared and evaluated against the criteria defined in the tests: section of the test files. If the test files do not define any test cases, JSNAPy instead compares the snapshots node by node. To check the test results, register the juniper_junos_jsnapy module’s response, and use the assert module to verify the expected result in the response.

The following playbook compares the snapshots taken for previously executed snap_pre and snap_post actions for every device in the Ansible inventory group. The results are evaluated using the criteria in the test files that are referenced in the configuration file. The playbook registers the module’s response as 'test_result' and uses the assert module to verify that all tests passed on the given device.

When you run the playbook, the assertions quickly identify which devices failed the tests.

user@host:~$ ansible-playbook jsnapy-interface-check.yaml

Performing Snapcheck Operations

JSNAPy enables you to take snapshots of the commands or RPCs specified in JSNAPy test files and immediately evaluate the snapshots against pre-defined criteria in the test cases. The juniper_junos_jsnapy Ansible module enables you to perform a JSNAPy snapcheck operation as part of an Ansible playbook.

To take a snapshot and immediately evaluate it based on the pre-defined set of criteria in the tests: section of the test files, set the juniper_junos_jsnapy module’s action argument to snapcheck, and specify a configuration file or one or more test files. To check the test results, register the module’s response, and use the assert module to verify the expected result in the response.

For example, for each device in the Ansible inventory group, the following playbook saves a separate snapshot for each command or RPC in the test files, registers the juniper_junos_jsnapy module’s response, and uses the assert module to verify that all tests defined in the test files passed on that device.

Understanding the juniper_junos_jsnapy Module Output

When the juniper_junos_jsnapy module performs a snap_pre, snap_post, or snapcheck action, it automatically saves the snapshots in the default JSNAPy snapshots directory. The juniper_junos_jsnapy module creates a separate file for each command or RPC executed on each device in the Ansible inventory group. Table 3 outlines the filenames of the snapshot files for each value of the action argument.

Note

Starting in Junos Snapshot Administrator in Python Release 1.3.0, the default directories for the JSNAPy test files and snapshots are ~/jsnapy/testfiles and ~/jsnapy/snapshots. In earlier release, the default directories are /etc/jsnapy/testfiles and /etc/jsnapy/snapshots.

Table 3: juniper_junos_jsnapy Output Filenames

action value

Output Files

snap_pre

hostname_PRE_hash_command.format

snap_post

hostname_POST_hash_command.format

snapcheck

hostname_snap_temp_hash_command.format

where:

  • hostname—Hostname of the device on which the command or RPC is executed.

  • hash—Hash generated from kwargs for test files that include the rpc and kwargs keys. If test files use the same RPC but include different arguments, and the RPCs are executed on the same host, the hash ensures unique output filenames in those cases. If a test file defines the command key, or a test file defines the rpc key but does not include the kwargs key, the hash value is omitted.

  • command—Command or RPC executed on the managed device. The module replaces whitespace and special characters in the command or RPC name with underscores ( _ ).

  • format—Format of the output, for example, xml.

Note

The juniper_junos_jsnapy module only differentiates the snapshot filenames for a given action based on hostname and command or RPC. As a result, if the module takes snapshots on the same device for the same action using test files that define the same command or RPC, the module will generate snapshots with the same filename, and the new file will overwrite the old file.

For example, if the juniper_junos_jsnapy module includes action: "snap_pre" and references test files that execute the show chassis fpc and show interfaces terse commands on devices dc1a.example.net and dc1b.example.net, the resulting files would be:

If the juniper_junos_jsnapy module includes action: "snap_post" and references a test file that executes the get-interface-information RPC with kwargs item interface_name: lo0 on device dc1a.example.net, the resulting file would be:

In addition to generating the snapshot files, the juniper_junos_jsnapy module also returns the following keys in its response:

  • action—JSNAPy action performed by the module.

  • changed—Indicates if the device’s state changed. Since JSNAPy only reports on state, the value is always false.

  • failed—Indicates if the playbook task failed.

  • msg—JSNAPy test results.

Enabling the jsnapy Callback Plugin

When you execute JSNAPy tests against devices running Junos OS and one or more tests fail, it can be difficult to identify and extract the failed tests if the output is extensive. The jsnapy callback plugin enables you to easily extract and summarize the information for failed JSNAPy tests. When you enable the jsnapy callback plugin and execute a playbook that includes JSNAPy tests, the plugin summarizes the information for the failed JSNAPy tests after the playbook PLAY RECAP.

Callback plugins are not enabled by default. To enable the jsnapy callback plugin, add the callback_whitelist = jsnapy statement to the Ansible configuration file.

When you enable the jsnapy callback plugin and run a playbook, the plugin summarizes the failed JSNAPy tests in a human-readable format. For example:

Example: Using the juniper_junos_jsnapy Module to Perform a JSNAPy Snapcheck Operation

The juniper_junos_jsnapy module enables you to execute JSNAPy tests against devices running Junos OS as part of an Ansible playbook. This examples uses the juniper_junos_jsnapy module perform a snapcheck action to verify the operational state of devices running Junos OS after applying specific configuration changes.

Requirements

This example uses the following hardware and software components:

  • Ansible control machine running:

    • Ansible 2.1 or later

    • Juniper.junos Ansible role version 2.0.0 or later

    • Junos PyEZ Release 2.1.7 or later

    • Junos Snapshot Administrator in Python Release 1.3.1 or later

Before executing the Ansible playbook, be sure you have:

  • Devices running Junos OS with NETCONF over SSH enabled and a user account configured with appropriate permissions

  • SSH public/private key pair configured for the appropriate user on the Ansible control machine and device running Junos OS

  • Existing Ansible inventory file with required hosts defined

Overview

In this example, the Ansible playbook configures BGP peering sessions on three devices running Junos OS and uses the juniper_junos_jsnapy module to verify that the BGP session is established for each neighbor address. If the playbook verifies that the sessions are established on a device, it confirms the commit for the new configuration. If the playbook does not confirm the commit, the device running Junos OS automatically rolls back to the previously committed configuration. The Ansible project defines the group and host variables for the playbook under the group_vars and host_vars directories, respectively.

The playbook has two plays. The first play, Load and commit BGP configuration, generates and assembles the configuration, loads the configuration on the device, and commits it using a commit confirmed operation. If the configuration is updated, one handler is notified. The play executes the following tasks:

Remove build directoryDeletes the existing build directory for the given device, if present.
Create build directoryCreates a new, empty build directory for the given device.
Build BGP configurationUses the template module with the Jinja2 template and host variables to render the BGP configuration for the given device and save it to a file in the device’s build directory.
Assemble configuration partsUses the assemble module to assemble the device configuration file from the files in that device’s build directory.

In this example, only the BGP configuration file will be present, and thus the resulting configuration file is identical to the BGP configuration file rendered in the previous task. If you later add new tasks to generate additional configuration files from other templates, the assemble module will combine all files into a single configuration.

Load and commit config, require confirmationLoads the configuration onto the device running Junos OS and commits the configuration using a commit confirmed operation, which requires explicit confirmation for the commit to become permanent. If this task makes a change to the configuration, it also notifies the handler that pauses playbook execution for a specified amount of time to allow the BGP peers to establish connections before the second play is executed.

If the requested configuration is already present on the device, the juniper_junos_config module does not load and commit the configuration. In this case, the module returns changed: false, and thus does not notify the handler.

The second play, Verify BGP, performs a JSNAPy snapcheck operation on each device using the tests in the JSNAPy test files and confirms the commit, provided all tests pass. The play executes the following tasks:

Execute snapcheckPerforms a JSNAPy snapcheck operation, which in this case, validates that the BGP session is established for each of the device’s neighbors and that there are no down peers.

In this example, the playbook directly references JSNAPy test files by setting the test_files argument equal to the list of JSNAPy test files. The dir argument specifies the directory where the test files are stored.

Confirm commitExecutes a commit check operation, which confirms the previous commit operation, provided that the first playbook play updated the configuration and that all of the JSNAPy tests passed. If the playbook updates the configuration but does not confirm the commit, the device running Junos OS automatically rolls the configuration back to the previously committed configuration.
Note

You can confirm the previous commit operation with either a commit check or commit operation on the device, which corresponds to the check: true or commit: true argument, respectively, in the juniper_junos_config module.

Verify BGP configuration(Optional) Explicitly indicates whether the JSNAPy tests passed or failed on the given device. This task is not specifically required, but it more easily identifies when the JSNAPy tests fail and on which devices.

Configuration

Defining the Group Variables

Step-by-Step Procedure

To define the group variables:

  • In the group_vars/all file, define variables for the build directory and for the filenames of the configuration and log files.

Defining the Host Variables and Jinja2 Template

Step-by-Step Procedure

To define the host variables that are used with the Jinja2 template to generate the BGP configuration:

  1. In the project’s host_vars directory, create a separate file named hostname.yaml for each host.
  2. Define the variables for host r1 in the r1.yaml file.

  3. Define the variables for host r2 in the r2.yaml file.

  4. Define the variables for host r3 in the r3.yaml file.

Step-by-Step Procedure

To create the Jinja2 template that is used to generate the BGP configuration:

  1. Create a file named bgp-template.j2 in the project’s playbook directory.
  2. Add the BGP configuration template to the file.

Creating the JSNAPy Test Files

Step-by-Step Procedure

The juniper_junos_jsnapy module references JSNAPy test files in the ~/jsnapy/testfiles directory. To create the JSNAPy test files:

  1. Create the jsnapy_test_file_bgp_states.yaml file, which executes the show bgp neighbor command and tests that the BGP peer state is established.
  2. Create the jsnapy_test_file_bgp_summary.yaml file, which executes the show bgp summary command and asserts that the BGP down peers count must be 0.

Creating the Ansible Playbook

Step-by-Step Procedure

To create the first play, which renders the configuration, loads it on the device, and commits the configuration as a commit confirmed operation:

  1. Include the boilerplate for the playbook and the first play, which must contain connection: local and the Juniper.junos role.

  2. Create the tasks that replace the existing build directory with an empty directory, which will hold the new configuration files.

  3. Create the task that renders the BGP configuration from the Jinja2 template file and host variables and stores it in the bgp.conf file in the build directory for that host.

  4. Create a task to assemble the configuration files in the build directory into the final junos.conf configuration file.

  5. Create the task that loads the configuration on the device, performs a commit operation that requires confirmation, and notifies the given handler, provided the configuration was changed.

  6. Create the handler that pauses playbook execution if the device configuration is updated, and set the pause time to an appropriate value for your environment.

Step-by-Step Procedure

To create the second play, which performs a JSNAPy snapcheck operation and confirms the committed configuration, provided that the configuration changed and the JSNAPy tests passed:

  1. Include the boilerplate for the second play, which must contain connection: local and the Juniper.junos role.

  2. Create a task to perform a JSNAPy snapcheck operation based on the tests in the given JSNAPy test files, and register the module’s response.

  3. Create the task to confirm the commit provided that the given conditions are met.

  4. (Optional) Create a task that uses the assert module to assert that the JSNAPy tests passed.

Results

On the Ansible control machine, review the completed playbook. If the playbook does not display the intended code, repeat the instructions in this section to correct the playbook.

Executing the Playbook

Step-by-Step Procedure

To execute the playbook:

  • Issue the ansible-playbook command on the control machine, and provide the playbook path and any desired options.
    user@ansible-cm:~/ansible$ ansible-playbook ansible-pb-bgp-configuration.yaml

Verification

Verifying BGP Neighbors

Purpose

Verify that the BGP session is established for each neighbor address.

The JSNAPy test files test that the BGP session is established for each neighbor address and that the there are no down peers. The Verify BGP configuration task output enables you to quickly verify that the given device passed all JSNAPy tests. If the JSNAPy passPercentage is equal to 100 percent, the task includes "msg": "All assertions passed" in the task output.

Action

Review the Verify BGP configuration task output, and verify that each device returns the All assertions passed message.

Troubleshooting Ansible Playbook Errors

Troubleshooting Configuration Load Errors

Problem

The Ansible playbook generates a ConfigLoadError error indicating that it failed to load the configuration on the device because of a syntax error.

Solution

The playbook renders the Junos OS configuration by using the Jinja2 template and the host variables defined for that device in the host_vars directory. The playbook generates a syntax error when the Jinja2 template produces an invalid configuration. To correct this error, update the Jinja2 template to correct the element identified by the bad_element key in the error message.

Troubleshooting Failed JSNAPy Tests

Problem

The Verify BGP configuration task output indicates that the assertion failed, because the JSNAPy passPercentage was not equal to 100 percent.

The assertion fails when the device has not established the BGP session with its neighbor or the session goes down. If the assertion fails, and the configuration for that device was updated in the first play, the playbook does not confirm the commit for the new configuration on the device, and the device rolls the configuration back to the previously committed configuration.

Solution

The JSNAPy tests might fail if the snapcheck operation is taken before the peers can establish the session or because the BGP neighbors are not configured correctly. If the playbook output indicates that the configuration was successfully loaded and committed on the device, try increasing the handler’s pause interval to a suitable value for your environment and rerun the playbook.

If the tests still fail, verify that the Jinja2 template and the host variables for each device contain the correct data and that the resulting configuration for each device is correct.

Troubleshooting Failed Commit Confirmations

Problem

The configuration was not confirmed on one or more devices.

Solution

The playbook only confirms the configuration if it changed and the JSNAPy tests pass. If the Load and commit config, require confirmation task output indicates that the configuration did not change, the playbook does not execute the task to confirm the commit. If the configuration changed but was not confirmed, then the JSNAPy tests failed. The JSNAPy tests might fail if the BGP neighbors are not configured correctly, or if the playbook does not provide enough time between the plays for the devices to establish the BGP session. For more information, see Troubleshooting Failed JSNAPy Tests.

Release History Table
Release
Description
Starting in Ansible for Junos OS Release 2.0.0, the juniper_junos_jsnapy module replaces the functionality of the junos_jsnapy module.