Create Custom Configuration Syntax with Commit Script Macros
Commit script macros enable you to create custom configuration syntax and expand it into standard Junos OS configuration statements. Your custom syntax serves as input to a commit script. The output of the commit script is standard Junos OS configuration syntax, as shown in Figure 1. The standard Junos OS statements are added to the configuration to cause your intended operational changes.
Macros use either persistent or transient change elements to expand your custom syntax into standard Junos OS configuration statements. If you use persistent changes, both the custom syntax and the standard Junos OS syntax appear in the candidate configuration. If you use transient changes, the custom syntax appears in the candidate configuration, and the standard Junos OS syntax is copied to the checkout configuration only.
This section discusses the following topics:
Creating a Custom Syntax
Macros work by locating apply-macro
statements in
the candidate configuration and using the values specified in the apply-macro
statement as parameters to a set of instructions
defined in a commit script. In effect, your custom configuration syntax
serves a dual purpose. The syntax allows you to simplify your configuration
tasks, and it provides to the script the data necessary to generate
a complex configuration.
To enter custom syntax, you include the apply-macro
statement at any hierarchy level and specify any data that you want
inside the apply-macro
statement, for example:
apply-macro macro-name { parameter-name parameter-value; }
You can include the apply-macro
statement at any
level of the configuration hierarchy. In this sense, the apply-macro
statement is similar to the apply-groups
statement. Each apply-macro
statement must be uniquely named, relative to other apply-macro
statements at the same hierarchy level.
An apply-macro
statement can contain a set of parameters
with optional values. The corresponding commit script can refer to
the macro name, its parameters, or the parameters’ values. When
the script inspects the configuration and finds the data, the script
performs the actions specified by the corresponding persistent or
transient change.
For example, given the following configuration stanza, you can write script instructions to generate a standard configuration based on the name of the parameter:
protocols { mpls { apply-macro blue-type-lsp { color blue; } } }
The following <xsl:for-each>
programming
instruction finds apply-macro
statements at the [edit
protocols mpls]
hierarchy level that contain a parameter named color
:
<xsl:for-each select="protocols/mpls/apply-macro[data/name = 'color']">
The following instruction creates a variable named color
and assigns to the variable the value of the color
parameter, which in this case is blue
:
<xsl:variable name="color" select="data[name = 'color']/value"/>
The following instruction adds the admin-groups
statement
to the configuration and assigns the value of the color
variable to the group name:
<transient-change> <protocols> <mpls> <admin-groups> <name> <xsl:value-of select="$color"/> </name> </admin-groups> </mpls> </protocols> </transient-change>
The resulting configuration statements are as follows:
protocols { mpls { admin-groups { blue; } } }
<data> Element
In the XML rendering of the custom syntax within an apply-macro
statement, parameters and their values are contained in <name>
and <value>
elements, respectively. The <name>
and <value>
elements are sibling children
of the <data>
element. For example,
the apply-macro blue-type-lsp
statement contains six
parameters, as follows:
[edit protocols mpls] apply-macro blue-type-lsp { 10.1.1.1; 10.2.2.2; 10.3.3.3; 10.4.4.4; color blue; group-value 0; }
The parameters and values are rendered in Junos XML tag elements as follows:
[edit protocols mpls] user@host# show | display xml <rpc-reply xmlns:junos="http://xml.juniper.net/junos/10.0R1/junos"> <configuration> <protocols> <mpls> <apply-macro> <name>blue-type-lsp</name> <data> <name>10.1.1.1</name> </data> <data> <name>10.2.2.2</name> </data> <data> <name>10.3.3.3</name> </data> <data> <name>10.4.4.4</name> </data> <data> <name>color</name> <value>blue</value> </data> <data> <name>group-value</name> <value>0</value> </data> </apply-macro> </mpls> </protocols> </configuration> </rpc-reply>
When you write commit script macros, you can extract and manipulate
the parameters contained in apply-macro
statements by referring
to the <data>
, <name>
, and <value>
elements.
In the following example, the select
attribute’s XPath expression
extracts the text contained in the <value>
element that is a child of a <data>
element that also contains a <name>
child element with the text color
. The
variable declaration assigns the text of the <value>
element to a variable named color
.
<xsl:variable name="color" select="data[name = 'color']/value"/>
The SLAX equivalent is:
var $color = ./data[name='color']/value;
The Python equivalent, which assumes that element
has selected an apply-macro
element,
is:
color = element.find("data[name='color']/value").text
Expanding the Custom Syntax
In the corresponding commit script, you include one or more
programming instructions that inspect the configuration for the apply-macro
statement at a specified hierarchy level. Optionally,
you can use the data/name
expression to
select a parameter in the apply-macro
statement.
<xsl:for-each select="xpath-expression/apply-macro[data/name = 'parameter-name']">
For example, the following XSLT programming instruction selects
every apply-macro
statement that contains the color
parameter and that appears at the [edit
protocols mpls]
hierarchy level:
<xsl:for-each select="protocols/mpls/apply-macro[data/name = 'color']">
The SLAX equivalent is:
for-each (protocols/mpls/apply-macro[data/name = 'color']) {
The Python equivalent, which spans multiple lines for readability, is:
for element in Junos_Configuration.xpath \ ("./protocols/mpls/apply-macro[data/name='color']"):
When expanding macros, a particularly useful programming instruction
in XSLT scripts is the <xsl:value-of>
instruction. This instruction selects a parameter value and uses
it to build option values for Junos OS statements. For example, the
following instruction concatenates the value of the color
variable, the text -lsp-
, and the current context node (represented by “ .
” ) to build a name for an LSP.
<label-switched-path> <name> <xsl:value-of select="concat($color, '-lsp-', .)"/> </name> </label-switched-path>
SLAX uses the underscore (_
) to concatenate
values.
<label-switched-path> { <name> $color _ '-lsp-' _ .; }
When the script includes instructions to find the necessary data, you can provide content for a persistent or transient change that uses the data to construct a standard Junos OS configuration.
The following transient change creates an administration group
and adds the label-switched-path
statement to the configuration.
The label-switched path is assigned
a name that concatenates the value of the color
variable, the text -lsp-
, and the currently
selected IP address represented by the period (“.
”). The transient change also adds the to
statement and assigns the currently selected IP address.
Finally, the transient change adds the admin-group include-any
statement and assigns the value of the color
variable.
<transient-change> <protocols> <mpls> <admin-groups> <name><xsl:value-of select="$color"/></name> <group-value><xsl:value-of select="$group-value"/></group-value> </admin-groups> <xsl:for-each select="data[not(value)]/name"> <label-switched-path> <name><xsl:value-of select="concat($color, '-lsp-', .)"/></name> <to><xsl:value-of select="."/></to> <admin-group> <include-any><xsl:value-of select="$color"/></include-any> </admin-group> </label-switched-path> </xsl:for-each> </mpls> </protocols> </transient-change>
The SLAX equivalent is:
<transient-change> { <protocols> { <mpls> { <admin-groups> { <name> $color; <group-value> $group-value; } for-each (data[not(value)]/name) { <label-switched-path> { <name> $color _ '-lsp-' _ .; <to> .; <admin-group> { <include-any> $color; } } } } } }
Similarly in Python:
lsp_config ="" for element2 in element.xpath("data[not(value)]/name"): lsp_config = lsp_config + """ <label-switched-path> <name>{0}-lsp-{1}</name> <to>{1}</to> <admin-group> <include-any>{0}</include-any> </admin-group> </label-switched-path> """.format(color, element2.text) change_xml = """ <protocols> <mpls> <admin-groups> <name>{0}</name> <group-value>{1}</group-value> </admin-groups> {2} </mpls> </protocols> """.format(color, group_value, lsp_config).strip() jcs.emit_change(change_xml, "transient-change", "xml")
The example shown here is partial. For a full example, see Example: Creating Custom Configuration Syntax with Commit Script Macros.
After committing the configuration, the script runs, and the resulting full configuration looks like this:
[edit] protocols { mpls { admin-groups { blue 0; } label-switched-path blue-lsp-10.1.1.1 { to 10.1.1.1; admin-group include-any blue; } label-switched-path blue-lsp-10.2.2.2 { to 10.2.2.2; admin-group include-any blue; } label-switched-path blue-lsp-10.3.3.3 { to 10.3.3.3; admin-group include-any blue; } label-switched-path blue-lsp-10.4.4.4 { to 10.4.4.4; admin-group include-any blue; } } }
The previous example demonstrates how you can use a simplified custom syntax to configure label-switched paths (LSPs). If your network design requires a large number of LSPs to be configured, using a commit script macro can save time, ensure consistency, and prevent configuration errors.
Other Ways to Use Macros
The example discussed in Creating a Custom Syntax shows a macro that uses transient changes to create the intended operational impact. Alternatively, you can create a commit script that uses persistent changes to add the standard Junos OS statements to the candidate configuration and delete your custom syntax entirely. This way, a network operator who might be unfamiliar with your custom syntax can view the configuration file and see the full configuration rendered as standard Junos OS statements. Still, because the commit script macro remains in effect, you can quickly and easily create a complex configuration using your custom syntax.
In addition to the type of application discussed in Creating a Custom Syntax, you can also use macros to prevent a
commit script from performing a task. For example, a basic commit
script that automatically adds MPLS configuration to interfaces can
make an exception for interfaces you explicitly tag as not requiring
MPLS, by testing for the presence of an apply-macro
statement
named no-mpls
. For an example of this use
of macros, see Example: Control LDP Configuration.
You can use the apply-macro
statement as a place
to store external data. The commit script does not inspect the apply-macro
statement, so the apply-macro
statement
has no operational impact on the device, but the data can be carried
in the configuration file to be used by external applications.