ON THIS PAGE
Using the Junos PyEZ Config Utility to Configure Devices Running Junos OS
Junos PyEZ enables you to make structured and unstructured configuration
changes on devices running Junos OS. This topic discuss how to use
the jnpr.junos.utils.config.Config
utility to make
unstructured configuration changes, which consist of static or templatized
configuration data that is formatted as ASCII text, Junos XML elements,
Junos OS set commands, or JavaScript Object Notation
(JSON). The Config
utility also enables
you to roll back to a previously committed configuration or revert
to a rescue configuration.
Configuration Process Overview
After successfully connecting to a device running Junos
OS, to configure the device using the Config
utility, first create a Config
object
and associate it with the Device
instance.
For example:
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
dev = Device(host='dc1a.example.com').open()
cu = Config(dev)
By default, Junos PyEZ updates the candidate global configuration
(also known as the shared configuration database). The basic process for making configuration changes is to lock
the configuration database, load the configuration changes, commit
the configuration to make it active, and then unlock the configuration
database. When using the Junos PyEZ Config
utility to make unstructured configuration changes in the shared
configuration database, you can perform these actions by calling the
appropriate instance methods outlined here:
Lock the configuration using
lock()
Modify the configuration by performing one of the following actions:
Call
load()
when loading a new complete configuration or modifying specific portions of the configurationCall
rollback()
to revert to a previously committed configuration, as described in Rolling Back the ConfigurationCall
rescue()
to load the rescue configuration, as described in Loading the Rescue Configuration
Commit the configuration using
commit()
, as described in Committing the Configuration and Using Junos PyEZ to Commit the ConfigurationUnlock the configuration using
unlock()
You can also use a context manager (with
... as
syntax) to create a Config
object instance, and certain configuration modes
require that you use one. For these modes, Junos PyEZ automatically
locks and unlocks the configuration. For more information, see Specifying the Configuration Mode.
When you use the load()
method to
modify the configuration, in addition to specifying the configuration
mode, you can also specify the type of load operation, the format
of the configuration changes, and the source of the configuration
data. The Config
utility supports many
of the same load operations and formats that are available in the
Junos OS command-line interface (CLI). For more information, see:
You can specify the source of the configuration data as a file on the local server, a file on the client device, or a file at a URL that is reachable from the client device, or as a string, an XML object, or a Jinja2 template. For information about specifying the configuration data source, see the following sections:
Specifying the Configuration Mode
By default, when you create a Config
object and do not explicitly specify a configuration mode, Junos
PyEZ updates the candidate global configuration. Starting in Junos
PyEZ Release 2.0, you can specify the configuration mode to use when
modifying the configuration database. To specify a mode other than
the default, you must create the Config
object using a context manager and set the mode
argument to the desired mode. Supported modes include private
, exclusive
, dynamic
, batch
, and ephemeral
.
Starting in Junos PyEZ Release 2.1.3, you can update the ephemeral configuration database on devices that support this database. The ephemeral database is an alternate configuration database that provides a fast programmatic interface for performing configuration updates on devices running Junos OS. It is an advanced feature which if used incorrectly can have a serious negative impact on the operation of the device. For more information, see Understanding the Ephemeral Configuration Database.
When you specify a mode other than the default, the context
manager handles opening and locking and closing and unlocking the
database. This ensures that you do not unintentionally leave the database
in a locked state. In these cases, you only need to call the load()
and commit()
methods
to configure the device.
For example, the following code makes configuration changes using the configure private mode, which opens a private copy of the candidate configuration:
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
dev = Device(host='dc1a.example.com').open()
with Config(dev, mode='private') as cu:
cu.load('set system services netconf traceoptions file test.log', format='set')
cu.pdiff()
cu.commit()
dev.close()
For more information about the different configuration modes, see the CLI User Guide and Using Junos PyEZ to Configure Devices Running Junos OS.
Specifying the Load Operation
Junos PyEZ supports loading configuration changes using a load merge, load replace, load override, or load update operation. You specify the desired load
operation by including or omitting the appropriate parameters in the Config
load()
method.
Table 1 outlines
the supported load operations and the corresponding load()
method argument. By default, Junos PyEZ performs a load replace operation. To perform a load merge, load override, or load update operation, set the merge
, overwrite
, or update
parameter, respectively, to True
in the load()
method.
Table 1: Parameters for Specifying the Load Operation Type in the load() and set() Methods
Load Operation | Argument | First Supported Junos PyEZ Release | Description |
---|---|---|---|
load merge |
| 1.0 | Merge the loaded configuration with the existing configuration. |
load override |
| 1.0 | Replace the entire configuration with the loaded configuration. |
load replace (Default) | – | 1.0 | Merge the loaded configuration with the existing configuration,
but replace statements in the existing configuration with those that
specify the |
load update |
| 2.1.0 | Compare the complete loaded configuration against the existing configuration. Each configuration element that is different in the loaded configuration replaces its corresponding element in the existing configuration. During the commit operation, only system processes that are affected by changed configuration elements parse the new configuration. |
The following example performs a load override operation, which replaces the entire candidate configuration with the loaded configuration, and then commits the candidate configuration to make it active.
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
config_mx = 'configs/junos-config-mx.conf'
dev = Device(host='router1.example.com').open()
with Config(dev, mode='exclusive') as cu:
cu.load(path=config_mx, overwrite=True)
cu.commit()
dev.close()
Specifying the Format of the Configuration Data to Load
The Junos PyEZ Config
utility enables you to configure devices
running Junos OS using one of the standard, supported formats. You
can provide configuration data as strings, files, XML objects, or
Jinja2 Template objects. Files can contain either configuration data
snippets or Jinja2 templates. When providing configuration data within
a string, file, or Jinja2 template, supported formats for the data
include ASCII text, Junos XML elements, Junos OS set commands, and JSON. You can specify the format of the configuration
data either by explicitly including the format
parameter in the Config
utility load()
method or by adding the appropriate extension
to the configuration data file. If you do not specify a format, the
default is XML.
Starting in Junos PyEZ Release 1.2, Junos PyEZ automatically detects the format when you supply the configuration data as a string.
Table 2 summarizes
the supported formats for the configuration data and the corresponding
value for the file extension and format
parameter. When using Junos XML formatting for the configuration
data, you must enclose the data in the top-level <configuration>
tag.
You do not need to enclose configuration data that is
formatted as ASCII text, Junos OS set commands, or
JSON in <configuration-text>
, <configuration-set>
, or <configuration-json>
tags as required when configuring the device directly within a NETCONF
session.
Table 2: Specifying the Format for Configuration Data
Configuration Data Format | File Extension | format Parameter |
---|---|---|
ASCII text |
| text |
JavaScript Object Notation (JSON) |
| json |
Junos OS set commands |
| set |
Junos XML elements |
| xml |
When the overwrite
or update
parameter is set to True
, you cannot use the Junos OS set command format.
Starting in Junos PyEZ Release 2.0, Junos PyEZ supports loading configuration data in JSON format on devices running Junos OS Release 16.1R1 and later releases.
Specifying the Location of the Configuration Data
Junos PyEZ enables you to load configuration data as strings, files, XML objects, or Jinja2 Template objects. Files can contain either configuration data snippets or Jinja2 templates.
Table 3 summarizes
the load()
method parameters that you use to pass
in or reference the location of the configuration data. You must always
specify the format of the data by including the format
parameter in the method call except when using strings, XML objects,
or files that have the format indicated by the file extension. When
using Jinja2 templates, include the template_vars
parameter to pass in the dictionary of required template variables.
Table 3: Referencing Configuration Data in the load() Method
Parameter | Configuration Data Source | Description |
|
---|---|---|---|
| Local file | Path to a file on the local configuration management server containing configuration data formatted as ASCII text, Junos XML elements, Junos OS set commands, or JSON. | You must include the |
| Jinja2 Template object | Pre-loaded Jinja2 Template object. Include the | You must include the |
| Local Jinja2 template file | Path to a file on the local configuration management server containing a Jinja2 template formatted as ASCII text, Junos XML elements, Junos OS set commands, or JSON. Include the | You must include the |
| Remote file | Path to a file located on the client device running Junos OS or at a remote URL that is reachable from the client device using an FTP or Hypertext Transfer Protocol (HTTP) URL. | You must include the |
| XML object String | XML object or a string that contains configuration data formatted as ASCII text, Junos XML elements, Junos OS set commands, or JSON. | Junos PyEZ automatically detects the format of the configuration
data in this case, and the |
Loading Configuration Data from a Local or Remote File
Junos PyEZ enables you to load configuration data formatted
as ASCII text, Junos XML elements, Junos OS set commands,
or JSON from a file. To load configuration data from a local file
on the configuration management server, set the load()
method’s path
parameter to the absolute or relative path of the file. For example:
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
dev = Device(host='dc1a.example.com').open()
conf_file = 'configs/junos-config-interfaces.conf'
with Config(dev, mode='exclusive') as cu:
cu.load(path=conf_file, merge=True)
cu.commit()
dev.close()
Starting in Junos PyEZ Release 2.1.5, you can load configuration
data from a file located on the client device running Junos OS or
at a URL that is reachable from the client device. To load configuration
data from a file on the device running Junos OS or at a remote URL,
set the url
parameter to the absolute or
relative path of the file on the client device or the FTP location
or Hypertext Transfer Protocol (HTTP) URL of a remote file, and include
any other parameters required for the load operation. For example:
cu.load(url="/var/home/user/golden.conf")
cu.load(url="ftp://username@ftp.hostname.net/path/filename")
cu.load(url="http://username:password@example.com/path/filename")
For detailed information about specifying the URL, see the url
attribute for the Junos XML protocol <load-configuration> operation.
If the file does not indicate the format of the configuration
data by using one of the accepted file extensions as listed in Specifying the Format of the Configuration Data to Load, then you must
specify the format by including the format
parameter in the load()
method parameter
list. For example:
conf_file = "configs/junos-config-interfaces"
cu.load(path=conf_file, format="text", merge=True)
For information about loading configuration data from Jinja2 templates or template files, see Loading Configuration Data Using Jinja2 Templates.
Loading Configuration Data from a String
To load configuration data that is formatted as ASCII
text, Junos XML elements, Junos OS set commands, or JSON
from a string, include the string as the first argument in the load()
method argument list. Starting in Junos
PyEZ Release 1.2, Junos PyEZ automatically detects the format of the
configuration data in strings, so the format
parameter is optional in this case.
The following code snippets present sample multiline strings
containing configuration data in the different formats and the corresponding
calls to the load()
method. The optional format
parameter is explicitly included in each example
for clarity. In the examples, cu
is an
instance of the Config
utility, which operates
on the target device running Junos OS.
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
dev = Device(host='dc1a.example.com').open()
cu = Config(dev)
For configuration data formatted as ASCII text:
config_text = """ system { scripts { op { file test.slax; } } } """
Load the configuration data by supplying the string as the first argument in the list, and optionally specify
format="text"
.cu.load(config_text, format="text", merge=True)
For configuration data formatted as Junos XML:
config_xml = """ <configuration> <system> <scripts> <op> <file> <name>test.slax</name> </file> </op> </scripts> </system> </configuration> """
Load the configuration data by supplying the string as the first argument in the list, and optionally specify
format="xml"
.cu.load(config_xml, format="xml", merge=True)
For configuration data formatted as Junos OS set commands:
config_set = """ set system scripts op file test.slax """
Load the configuration data by supplying the string as the first argument in the list, and optionally specify
format="set"
.cu.load(config_set, format="set", merge=True)
For configuration data formatted using JSON:
config_json = """{ "configuration" : { "system" : { "scripts" : { "op" : { "file" : [ { "name" : "test.slax" } ] } } } } }"""
Load the configuration data by supplying the string as the first argument in the list, and optionally specify
format="json"
.cu.load(config_json, format="json", merge=True)
Loading Configuration Data Formatted as an XML Object
To load configuration data formatted as an XML object, include
the object as the first argument in the load()
method argument list, and supply any other
required parameters. Because the default format for configuration
data is XML, you do not need to explicitly include the format
parameter in the method call.
The following code presents an XML object and the corresponding
call to the load()
method:
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
from lxml.builder import E
config_xml_obj = (
E.configuration( # create an Element called "configuration"
E.system(
E.scripts(
E.op (
E.file (
E.name("test.slax"),
)
)
)
)
)
)
dev = Device(host='dc1a.example.com').open()
with Config(dev, mode='exclusive') as cu:
cu.load(config_xml_obj, merge=True)
cu.commit()
dev.close()
Loading Configuration Data Using Jinja2 Templates
Junos PyEZ supports using Jinja2 templates to render Junos OS configuration data. Jinja is a template engine for Python that enables you to generate documents from predefined templates. The templates, which are text files in the desired language, provide flexibility through the use of expressions and variables. You can create Junos OS configuration data using Jinja2 templates in one of the supported configuration formats, which includes ASCII text, Junos XML elements, Junos OS set commands, and JSON. Junos PyEZ uses the Jinja2 template and a supplied dictionary of variables to render the configuration data.
Jinja2 templates provide a powerful method to generate configuration
data, particularly for similar configuration stanzas. For example,
rather than manually adding the same configuration statements for
each interface on a device, you can create a template that iterates
over a list of interfaces and creates the required configuration statements
for each one. In Jinja, blocks are delimited by '{%
' and '%}
' and variables are
enclosed within '{{
' and '}}
'.
The following sample Jinja2 template generates configuration data that enables MPLS on logical unit 0 for each interface in a given list and also configures the interface under the MPLS and RSVP protocols.
interfaces { {% for item in interfaces %} {{ item }} { description "{{ description }}"; unit 0 { family {{ family }}; } } {% endfor %} } protocols { mpls { {% for item in interfaces %} interface {{ item }}; {% endfor %} } rsvp { {% for item in interfaces %} interface {{ item }}; {% endfor %} } }
In the Junos PyEZ code, the corresponding dictionary of Jinja2 template variables is:
config_vars = {
'interfaces': ['ge-1/0/1', 'ge-1/0/2', 'ge-1/0/3'],
'description': 'MPLS interface',
'family': 'mpls'
}
To load the Jinja2 template in the Junos PyEZ code, set the template_path
parameter to the path of the template
file, and set the template_vars
parameter
to the dictionary of template variables. If you do not use one of
the accepted file extensions to indicate the format of the configuration
data, then you must include the format
parameter
in the load()
method parameter list.
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
conf_file = "configs/junos-config-interfaces-mpls.conf"
config_vars = {
'interfaces': ['ge-1/0/1', 'ge-1/0/2', 'ge-1/0/3'],
'description': 'MPLS interface',
'family': 'mpls'
}
dev = Device(host='router1.example.com').open()
with Config(dev, mode='exclusive') as cu:
cu.load(template_path=conf_file, template_vars=config_vars, merge=True)
cu.commit()
dev.close()
If you are supplying a pre-loaded Jinja2 Template object,
you must use the template
parameter instead
of the template_path
parameter in the load()
method argument list.
Junos PyEZ uses the Jinja2 template and dictionary of variables to render the following configuration data, which is then loaded into the candidate configuration and committed on the device:
interfaces { ge-1/0/1 { description "MPLS interface"; unit 0 { family mpls; } } ge-1/0/2 { description "MPLS interface"; unit 0 { family mpls; } } ge-1/0/3 { description "MPLS interface"; unit 0 { family mpls; } } } protocols { mpls { interface ge-1/0/1; interface ge-1/0/2; interface ge-1/0/3; } rsvp { interface ge-1/0/1; interface ge-1/0/2; interface ge-1/0/3; } }
The following video presents a short Python session that demonstrates how to use a Jinja2 template to configure a device running Junos OS.
For additional information about Jinja2, see the Jinja2 documentation at http://jinja.pocoo.org/docs/.
Rolling Back the Configuration
Devices running Junos OS store a copy of the most recently committed configuration and up to 49 previous configurations, depending on the platform. You can roll back to any of the stored configurations. This is useful when configuration changes cause undesirable results, and you want to revert back to a known working configuration. Rolling back the configuration is similar to the process for making configuration changes on the device, but instead of loading configuration data, you perform a rollback, which replaces the entire candidate configuration with a previously committed configuration.
The Junos PyEZ jnpr.junos.utils.config.Config
class rollback()
method enables you to roll back the
configuration on a device running Junos OS. To roll back the configuration,
call the rollback()
method and and set
the rb_id
argument to the ID of the rollback
configuration. Valid ID values are 0 (zero, for the most recently
committed configuration) through one less than the number of stored
previous configurations (maximum is 49). If you omit this parameter
in the method call, it defaults to 0.
The following example prompts for the rollback ID of the configuration to restore, rolls back the configuration, prints the configuration differences, and then commits the configuration to make it the active configuration on the device.
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
rollback_id = int(input("Rollback ID of the configuration to restore: "))
dev = Device(host='dc1a.example.com').open()
with Config(dev, mode='exclusive') as cu:
cu.rollback(rb_id=rollback_id)
cu.pdiff()
cu.commit()
dev.close()
user@server:~$ python junos-pyez-rollback.py
Rollack ID of the configuration to restore: 1 [edit interfaces] - ge-0/0/1 { - unit 0 { - family inet { - address 198.51.100.1/24; - } - } - }
For a more extensive example that includes error handling, see Example: Using Junos PyEZ to Roll Back the Configuration.
Loading the Rescue Configuration
A rescue configuration allows you to define a known working configuration or a configuration with a known state that you can restore at any time. You use the rescue configuration when you need to revert to a known configuration or as a last resort if your router or switch configuration and the backup configuration files become damaged beyond repair. When you create a rescue configuration, the device saves the most recently committed configuration as the rescue configuration.
The Junos PyEZ jnpr.junos.utils.config.Config
utility enables
you to manage the rescue configuration on devices running Junos OS.
After creating an instance of the Config
class, you use the rescue()
method to mange the rescue configuration.
You specify the action to perform on the rescue configuration by setting
the rescue()
method action
parameter to the desired operation.
To load the existing rescue configuration into the candidate
configuration, specify action="reload"
.
If no rescue configuration exists, the load operation returns False
. After loading the rescue configuration, you
must commit the configuration to make it the active configuration
on the device.
The following example loads and commits the rescue configuration, if one exists:
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
dev = Device(host='dc1a.example.com').open()
with Config(dev, mode='exclusive') as cu:
rescue = cu.rescue(action="reload")
if rescue is False:
print ("No existing rescue configuration.")
else:
cu.pdiff()
cu.commit()
dev.close()
For information about creating, retrieving, or deleting the rescue configuration and for additional examples, see Using Junos PyEZ to Manage the Rescue Configuration on Devices Running Junos OS.
Committing the Configuration
After modifying the configuration, you must commit the configuration
to make it the active configuration on the device. When you use the Config
utility to make unstructured configuration changes
on a device, you commit the candidate configuration by calling the commit()
method.
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
dev = Device(host='dc1a.example.com').open()
conf_file = "configs/junos-config-interfaces.conf"
with Config(dev, mode='exclusive') as cu:
cu.load(path=conf_file, merge=True)
cu.commit()
dev.close()
For more information about the commit operation and supported commit options in Junos PyEZ scripts, see Using Junos PyEZ to Commit the Configuration.