ON THIS PAGE
Use Junos PyEZ Configuration Tables to Configure Structured Resources on Junos Devices
Junos PyEZ configuration Tables that specify the set
property enable you to
define structured resources that can be used to programmatically configure Junos
devices. After loading or importing the Table definition for your structured resource
into your Junos PyEZ application, the application can configure the resource on your
devices. This topic discusses the general process and some specific tasks for using
Junos PyEZ configuration Tables and Views to configure structured resources on a
device.
General Configuration Process
The configuration Table set
property identifies the configuration hierarchy level at which a
resource is configured and sets the XPath context for fields in the
View. For example, the following Table defines a user
resource at the [edit system login]
hierarchy
level:
UserConfigTable: set: system/login/user key-field: username view: UserConfigView UserConfigView: groups: auth: authentication fields: username: name userclass: { class : { 'default' : 'unauthorized' }} uid: { uid: { 'type': 'int', 'minValue':100, 'maxValue':64000 }} fullname: full-name fields_auth: password: encrypted-password
The fields that are included in the View define which leaf statements the user can configure for that resource. A field can define a default value as well as type and constraint checks.
To configure a structured resource on a device, you must load or import the Table into your
application. You then create a Table object and associate it with the
Device
object of the target device. For example:
from jnpr.junos import Device from myTables.ConfigTables import UserConfigTable from lxml import etree with Device(host='router.example.com') as dev: uc = UserConfigTable(dev)
To define values for a resource’s configuration statements, set the corresponding field names (as defined in the View) equal to the desired values.
table_object.fieldname = value
The default value for a field is None
unless the View explicitly defines a default for that field. If
the View defines a type or constraint check for a field, the application
must supply the correct data type and value for that field and ideally
handle any errors that might be raised in the event that the check
fails. You must always define values for any key fields that are declared
in the Table’s key-field
property,
which in this example is username
.
The following code imports UserConfigTable
and configures values for the
username
, userclass
, and
password
fields. The View's password
field
references the encrypted-password
statement in the
configuration; thus, the data must supply a pre-encrypted password.
# Python 3 from jnpr.junos import Device from myTables.ConfigTables import UserConfigTable from lxml import etree with Device(host='router.example.com') as dev: uc = UserConfigTable(dev) uc.username = 'user1' uc.userclass = 'operator' uc.password = '$ABC123' ...
For detailed information about more specific configuration tasks, such as configuring statements with fixed-form keywords or multiple values, configuring multiple instances of a statement or resource, deleting a leaf or container statement, or configuring an object property that corresponds to a Junos XML attribute, see the following sections:
After you configure an object, you must call the append()
method to build the
corresponding Junos XML configuration and add it to the lxml
object that stores the master set of configuration changes for that Table
object. The configuration changes include only those fields that have either a
default value defined in the View or a user-configured value. Fields that retain
their initial value of None
are ignored.
uc.append()
After building the XML, the append()
method also resets all fields to the their default values or to None
if the View does not define a default for that
field. This enables you to configure multiple objects in the same
application and ensures that you do not unintentionally use a value
defined for one resource when you configure subsequent resources.
Each time you configure a new resource, you must call append()
to add the configuration changes to the master
set of changes. For more information about the append()
method, see Use append() to Generate the Junos XML Configuration Data.
If necessary, you can also manually reset all fields
for a Table object by calling the reset()
method.
uc.reset()
The reset()
method restores all fields
back to their default values or to None
if the View does not define a default. The reset()
method only resets the current values of the fields. It does not
affect the XML containing the configuration changes that has been
constructed up to that point by calls to the append()
method.
You can retrieve the XML configuration representing your
changes at any point in the application by calling the get_table_xml()
method, which is discussed in detail
in View Your Configuration Changes.
configXML = uc.get_table_xml() if (configXML is not None): print (etree.tostring(configXML, encoding='unicode', pretty_print=True))
After configuring all necessary objects and calling append()
, you can load your configuration changes into
the shared configuration database on the device by using one of two
methods:
Call the
set()
method, which automatically calls thelock()
,load()
,commit()
, andunlock()
methodsCall the
lock()
,load()
,commit()
, andunlock()
methods individually
uc.set()
When you create the Table instance with a context manager
(with ... as
syntax) that includes
the mode
parameter to use a specific configuration
mode, the context manager handles opening and locking and closing
and unlocking the database. In this case, you only need to call the load()
and commit()
methods
to configure the device. Calling the lock()
or set()
method results in a LockError
exception.
Using the single set()
method provides simplicity, but calling the individual
methods provides additional flexibility such as when you need to call other
methods after loading the configuration data, but before committing it. For
example, you might want to call the diff()
or
pdiff()
methods to review the configuration differences
after you load the data but before you commit it. Or you might need to call the
rollback()
method to reset the candidate configuration back
to the active configuration instead of committing it. For more information about
the using the different methods to load and commit the configuration data, see
Use Junos PyEZ to Configure Junos Devices and Use Junos PyEZ to Commit the Configuration.
In the case of large load and commit operations that might time
out, you can adjust the RPC timeout interval by including the timeout
parameter in the set()
or commit()
method argument list. For
more information, see How to Control the RPC Timeout Interval.
A configuration table that specifies the set
parameter is a superset and has all
the features of a configuration table that specifies the get
parameter. You can retrieve configuration data in the same way in your Junos
PyEZ application whether the Table specifies set
or
get
. For information about using configuration Tables to
retrieve configuration data, see Use Junos PyEZ Configuration Tables to Retrieve Configuration Data.
Configure Statements Consisting of a Fixed-Form Keyword
A leaf statement is a CLI configuration statement that does not contain any other statements. Most leaf statements define a value for one characteristic of a configuration object and have the following form:
keyword value;
Some leaf statements consist of a fixed-form keyword
only, without an associated variable-form value. For example, the ftp
statement at the [edit system services]
hierarchy
level is an example of a fixed-form keyword.
system { services { ftp; } }
The Junos XML API represents such statements with an empty tag.
<configuration> <system> <services> <ftp> </ftp> ... </services> </system> </configuration>
To configure a fixed-form keyword in your Junos PyEZ application,
such as the ftp
statement under [edit system services]
, set the value of the corresponding field name as defined in the
View equal to the Boolean value True
.
Consider the following View, which defines the ftp
field with a type constraint to ensure that the
value for the field is a Boolean:
ServicesView: fields: ftp: { 'ftp' : { 'type': 'bool' } } ...
To configure the ftp
field
in your Junos PyEZ application, set the field equal to True
.
from jnpr.junos import Device from myTables.ConfigTables import ServicesConfigTable with Device(host='router.example.com') as dev: sc = ServicesConfigTable(dev) sc.ftp = True sc.append() sc.set()
Configure Multiple Values for the Same Statement
Some Junos OS leaf statements accept multiple values, which might be either user defined or drawn from a set of predefined values. CLI notation uses square brackets to enclose all values in a single statement, as in the following:
keyword [ value1 value2 value3 ...];
For example, you might need to configure a VLAN ID list for a trunk interface, as in the following configuration:
interfaces { ge-0/0/1 { native-vlan-id 510; unit 0 { family bridge { interface-mode trunk; vlan-id-list [ 510 520 530 ]; } } } }
To configure a leaf statement with multiple values in your Junos PyEZ application, set the value
of the corresponding field (as defined in the View) equal to a Python list
containing the desired values. In the following example, the
vlan_list
field maps to the vlan-id-list
statement in the CLI. To configure the statement with multiple VLAN IDs, set the
field name equal to the list of IDs.
from jnpr.junos import Device from myTables.ConfigTables import InterfacesConfigTable with Device(host='router.example.com') as dev: intf = InterfacesConfigTable(dev) intf.name = 'ge-0/0/1' intf.mode = 'trunk' intf.native_vlan = 510 intf.vlan_list = [510, 520, 530] intf.append() intf.set()
Configure Multiple Instances of the Same Statement
In certain situations, the Junos OS configuration enables you
to configure multiple instances of the same statement. For example,
you might configure multiple addresses under the same protocol family
for a logical interface. In the following configuration snippet, the
loopback interface has multiple addresses configured at the [edit
interfaces lo0 unit 0 family inet]
hierarchy level:
interfaces { lo0 { unit 0 { family inet { address 192.168.100.1/32; address 192.168.100.2/32; } } } }
The Junos XML representation of the configuration is as follows:
<configuration> <interfaces> <interface> <name>lo0</name> <unit> <name>0</name> <family> <inet> <address> <name>192.168.100.1/32</name> </address> <address> <name>192.168.100.2/32</name> </address> </inet> </family> </unit> </interface> </interfaces> </configuration>
When you use Junos PyEZ configuration Tables to manage structured resources, you define values for configuration statements by setting the corresponding field names equal to the desired values. However, you cannot define the same field twice in your Junos PyEZ application, because the second value will overwrite the first value. Instead, you must set the field equal to a list of values, and Junos PyEZ handles the conversion to XML.
Consider the following Table and View:
InterfaceTable: set: interfaces/interface key-field: - name - unit_name view: InterfaceView InterfaceView: fields: name: name desc: description unit_name: unit/name ip_address: unit/family/inet/address
The following sample code illustrates how to configure
multiple addresses for the loopback interface in a Junos PyEZ application.
In this case, you set the ip_address
field
equal to a list of addresses.
lo0_addresses = ['192.168.100.1/32', '192.168.100.2/32'] ... intf = InterfaceTable(dev) intf.name='lo0' intf.unit_name = 0 intf.ip_address = lo0_addresses intf.append() intf.set() ...
The resulting configuration is:
[edit interfaces] + lo0 { + unit 0 { + family inet { + address 192.168.100.1/32; + address 192.168.100.2/32; + } + } + }
Configure Multiple Instances of the Same Resource
When you use Junos PyEZ configuration Tables to configure structured resources, you might need to
configure multiple objects, or records, for the same resource. For example, you
might configure multiple interfaces or users at the same time. To configure
multiple objects for the same structured resource in a Junos PyEZ application,
you must define the values for one object’s fields, call the
append()
method, and then repeat this process for each
subsequent object.
For example, to configure multiple users, define the field values
for the first user, and call the append()
method. Then define the field values for the second user and call
the append()
method. The append()
method builds the Junos XML data for the configuration change and
adds it to the lxml
object storing the
master set of configuration changes. The method also automatically
resets all of the fields back to their default values, as defined
in the View, or to None
if a field does
not have a defined default.
The following example configures two user objects and commits the changes:
from jnpr.junos import Device from myTables.ConfigTables import UserConfigTable from lxml import etree with Device(host='router.example.com') as dev: uc = UserConfigTable(dev) uc.username = 'user1' uc.userclass = 'operator' uc.uid = 1005 uc.password = '$ABC123' uc.append() uc.username = 'user2' uc.userclass = 'operator' uc.uid = 1006 uc.password = '$ABC123' uc.append() uc.set()
If you do not call the append()
method after configuring one of multiple objects for the same resource,
the field values for the second object will overwrite the field values
for the first object.
The following sample code configures the same two users using a more compact syntax:
from jnpr.junos import Device from myTables.ConfigTables import UserConfigTable from lxml import etree users = ['user1', 'user2'] uids = ['1005', '1006'] passwds = ['$ABC123', '$ABC123'] with Device(host='router.example.com') as dev: uc = UserConfigTable(dev) for user, uid, passwd in zip(users, uids, passwds): uc.username = user uc.userclass = 'operator' uc.uid = uid uc.password = passwd uc.append() uc.set()
Delete Containers or Leaf Statements
In some cases, you might need to delete containers or leaf statements in the configuration. When
you use Junos PyEZ configuration Tables to manage structured resources, you can
perform this operation in your application by setting the appropriate field
value to {'operation' : 'delete'}
. You must always define
values for all key fields when deleting a container or leaf statement to
indicate to which object the deletion applies.
Consider the following Junos PyEZ configuration Table and View:
--- UserConfigTable2: set: system/login key-field: - username view: UserConfigView2 UserConfigView2: groups: auth: user/authentication fields: user: user username: user/name classname: { user/class : { 'type' : { 'enum' : ['operator', 'read-only', 'super-user'] } } } uid: { user/uid : { 'type' : 'int', 'minValue' : 100, 'maxValue' : 64000 } } fields_auth: password: encrypted-password
To delete a leaf statement for the resource defined in
the Table and View, set the value of the field corresponding to that
statement to {'operation' : 'delete'}
. The following example deletes the uid
statement for
user jsmith
:
from jnpr.junos import Device from myTables.ConfigTables import UserConfigTable2 with Device(host='router.example.com') as dev: uc = UserConfigTable2(dev) uc.username = 'jsmith' uc.uid = { 'operation' : 'delete' } uc.append() uc.set()
To delete a container from the configuration, the View
must define a field for that container. In the example Table and View,
the configuration scope defined by the set
property is system/login
. The View defines
the field 'user
', which maps to the system/login/user
container. This definition enables you to
delete user objects, if necessary. If you do not define a field for
the container, you can only delete statements within the container,
but you cannot delete the container itself.
To delete a container in the Junos PyEZ application, set the
value of the field corresponding to the container to {'operation' : 'delete'}
, and define the
key field to indicate the object to delete. The following example
deletes the user jsmith
from the configuration:
from jnpr.junos import Device from myTables.ConfigTables import UserConfigTable2 from lxml import etree with Device(host='router.example.com') as dev: uc = UserConfigTable2(dev) uc.user = { 'operation' : 'delete' } uc.username = 'jsmith' uc.append() print (etree.tostring(uc.get_table_xml(), encoding='unicode', pretty_print=True)) uc.set()
The application prints the Junos XML configuration data
returned by the get_table_xml()
method.
The user element with identifier 'jsmith' includes the operation="delete"
attribute to instruct Junos OS to
remove that object from the configuration.
<configuration> <system> <login> <user operation="delete"> <name>jsmith</name> </user> </login> </system> </configuration>
Configure Properties Corresponding to Junos XML Attributes
Some configuration mode commands, for example deactivate
or protect
, apply or remove a specific property, such
as the inactive or protect property, to a configuration statement.
In the CLI, this property is indicated by a tag preceding the configuration
statement. The Junos XML configuration indicates this property using
an XML attribute for the object.
For example, the following command deactivates the given interface.
[edit] user@host# deactivate interfaces ge-1/0/2
When you view the configuration in the CLI, the inactive
tag precedes the interface name.
[edit] user@host# show interfaces inactive: ge-1/0/2 { description "to CustomerA"; unit 0 { family inet { address 198.51.100.1/24; } } }
Similarly, in the Junos XML output, the <interface>
element for the same interface includes
the inactive="inactive"
attribute.
user@host# show interfaces | display xml <rpc-reply xmlns:junos="http://xml.juniper.net/junos/18.3R1/junos"> <configuration junos:changed-seconds="1544581124" junos:changed-localtime="2018-12-11 18:18:44 PST"> <interfaces> <interface inactive="inactive"> <name>ge-1/0/2</name> <description>to CustomerA</description> <unit> <name>0</name> <family> <inet> <address> <name>198.51.100.1/24</name> </address> </inet> </family> </unit> </interface> </interfaces> </configuration> </rpc-reply>
Junos PyEZ configuration Tables enable you to define supported XML attributes for an object when configuring structured resources. Consider the following Junos PyEZ configuration Table and View:
InterfaceTable: set: interfaces key-field: - name view: InterfaceView InterfaceView: fields: interface: interface name: interface/name desc: interface/description unit_name: interface/unit/name ip_address: interface/unit/family/inet/address
To define the XML attribute for a given configuration object, set its field (as defined by the
View) to a dictionary containing the attribute and its value. For example, to
define an interface but immediately deactivate it, set the field corresponding
to the <interface>
element to
{'inactive':'inactive'}
. The following example configures
and deactivates the given interface:
from jnpr.junos import Device from myTables.ifConfigTable import InterfaceTable from lxml import etree with Device(host='router.example.com') as dev: intf = InterfaceTable(dev) intf.name = 'ge-1/0/2' intf.unit_name = 0 intf.ip_address = '198.51.100.1/24' intf.desc = 'to CustomerA' intf.interface = {'inactive':'inactive'} intf.append() configXML = intf.get_table_xml() if (configXML is not None): print (etree.tostring(configXML, encoding='unicode', pretty_print=True)) else: print (configXML) intf.set()
The application prints the Junos XML configuration data
returned by the get_table_xml()
method.
The interface element with identifier 'ge-1/0/2' includes the inactive="inactive"
attribute.
<configuration> <interfaces> <interface inactive="inactive"> <name>ge-1/0/2</name> <unit> <name>0</name> <family> <inet> <address>198.51.100.1/24</address> </inet> </family> </unit> <description>to CustomerA</description> </interface> </interfaces> </configuration>
To activate an inactive object, set the View field corresponding
to the inactive object to {'active':'active'}
.
from jnpr.junos import Device from myTables.ifConfigTable import InterfaceTable from lxml import etree with Device(host='router.example.com') as dev: intf = InterfaceTable(dev) intf.name = 'ge-1/0/2' intf.interface = {'active':'active'} intf.append() intf.set()
Similarly, to protect the configuration element or remove the protect
attribute from a protected element, set the
appropriate field value to {'protect':'protect'}
or {'unprotect':'unprotect'}
. For more
information about XML attributes in the Junos OS configuration, see
the Junos XML Management Protocol Developer Guide .
Use append() to Generate the Junos XML Configuration Data
When you use Junos PyEZ configuration Tables to configure structured resources, you define the
values for a resource’s fields and then call the append()
method. Each call to the append()
method generates the Junos
XML configuration data for the current set of changes and adds it to the
lxml
object that stores the master set of configuration
changes.
from jnpr.junos import Device from myTables.ConfigTables import UserConfigTable with Device(host='router.example.com') as dev: uc = UserConfigTable(dev) uc.username = 'user1' uc.userclass = 'operator' uc.password = '$ABC123' uc.append() uc.set()
Calling the append()
method generates
the Junos XML configuration data for your resource. The configuration
changes only include those fields that have either a default value
defined in the View or a user-configured value. Fields that retain
their initial value of None
are ignored.
After building the XML, the append()
method also resets all fields back to their default values, as defined
in the View, or to None
if a field does
not have a defined default. Resetting the fields ensures that when
you configure multiple objects in the same application, you do not
set a field value for one object and then unintentionally use that
value in subsequent calls to append()
for
a different object. Thus, you must define new values for all key-field
fields for each call to append()
.
Once you append nodes to the master set of configuration changes, you cannot undo the operation.
The append()
method only adds the
new changes to the lxml
object containing
the master set of configuration changes. You must explicitly call
the set()
method or the load()
and commit()
methods to load and commit
the changes on the device.
View Your Configuration Changes
When you use Junos PyEZ configuration Tables to configure structured resources, you define the
values for a resource’s fields and then call the append()
method. Each call to the append()
method generates the Junos
XML configuration data for the current set of changes and adds it to the
lxml
object that stores the master set of configuration
changes. At times, you might need to review the configuration data that has been
constructed up to a certain point in the application, or you might want to view
the differences between the candidate and active configurations after you load
your configuration changes onto the device.
To retrieve the Junos XML configuration data containing your
changes, call the Table object’s get_table_xml()
method. The get_table_xml()
method returns
the XML configuration that has been constructed up to that point in
the application. When you call the set()
method or the load()
and commit()
methods, the application loads and commits
this Junos XML configuration data on the device.
The following example calls the get_table_xml()
method to retrieve the configuration changes and then stores them
in the configXML
variable. Prior to calling
the append()
method, the get_table_xml()
method returns None
. Thus, the application
only serializes and prints the XML configuration data if the returned
value is not None
.
from jnpr.junos import Device from myTables.ConfigTables import UserConfigTable from lxml import etree with Device(host='router.example.com') as dev: uc = UserConfigTable(dev) uc.username = 'user1' uc.userclass = 'operator' uc.password = '$ABC123' uc.append() configXML = uc.get_table_xml() if (configXML is not None): print (etree.tostring(configXML, encoding='unicode', pretty_print=True)) else: print (configXML) uc.set()
The get_table_xml()
method only returns
the Junos XML data for your configuration changes. You might also
want to compare the candidate and active configurations after loading
the configuration changes onto the device to review the differences
before you commit the changes.
To retrieve the differences, you can call the lock()
, load()
, commit()
, and unlock()
methods
separately and view your configuration differences by calling the pdiff()
method after you load the data but before you
commit it. The pdiff()
method with an empty
argument list compares the candidate configuration to the active configuration
and prints the difference in patch format directly to standard output.
... uc.append() uc.lock() uc.load() uc.pdiff() ... uc.commit() uc.unlock()
How to Control the RPC Timeout Interval
When you use Junos PyEZ configuration Tables to configure structured resources, you can load and
commit your configuration changes by calling the set()
method
or the load()
and commit()
methods. The
set()
and commit()
methods use the RPC
timeout value as defined in the device
module. If you do not
configure a new value for the Device
timeout
property, Junos PyEZ uses the default value of 30
seconds.
Large configuration changes might exceed the default or configured
timeout value, causing the operation to time out before the configuration
can be uploaded and committed on the device. To accommodate certain
configuration changes that might require load and commit times that
are longer than the default or configured timeout interval, set the timeout=seconds
argument to an
appropriate value when you call the set()
or commit()
method in your application.
For example:
uc = UserConfigTable(dev) uc.username = 'user1' uc.userclass = 'operator' uc.uid = 1005 uc.password = '$ABC123' uc.append() uc.set(timeout=300)