Parse and Format Junos XML Protocol Server Responses in Perl Client Applications
In a Junos XML Protocol Perl client application, as the last step in sending a request, the application verifies that there are no errors with the response from the Junos XML protocol server. It can then write the response to a file, to the screen, or both. If the response is for an operational query, the application usually uses XSLT to transform the output into a more readable format, such as HTML or formatted ASCII text. If the response consists of configuration data, the application can store it as XML (the Junos XML tag elements generated by default from the Junos XML protocol server) or transform it into formatted ASCII text.
The following sections discuss parsing and formatting options:
Parsing and Formatting an Operational Response
The Junos XML protocol Perl distribution includes several sample Perl scripts that perform various functions on devices running Junos OS. The following code sample from the diagnose_bgp.pl and get_chassis_inventory.pl sample scripts uses XSLT to transform an operational response from the Junos XML protocol server into a more readable format. A detailed discussion of the functional subsections follows the complete code sample.
# Get the name of the output file my $outputfile = $opt{o} || ""; # Retrieve the XSLT file my $xslfile = $opt{x} || "xsl/text.xsl"; if ($xslfile && ! -f $xslfile) { die "ERROR: XSLT file $xslfile does not exist"; #Get the xmlfile my $xmlfile = "$deviceinfo{hostname}.xml"; $res->printToFile($xmlfile); my $nm = $res->translateXSLtoRelease('xmlns:lc', $xslfile, "$xslfile.tmp"); if ($nm) { print "Transforming $xmlfile with $xslfile...\n" if $outputfile; my $command = "xsltproc $nm $deviceinfo{hostname}.xml"; $command .= "> $outputfile" if $outputfile; system($command); print "Done\n" if $outputfile; print "See $outputfile\n" if $outputfile; } else { print STDERR "ERROR: Invalid XSL file $xslfile\n"; }
The first line of the preceding code sample illustrates
how the scripts read the -o
option from the command line
to obtain the name of the file into which to write the results of
the XSLT transformation:
my $outputfile = $opt{o} || "";
The scripts use an XSLT file to transform the output.
If the -x
command-line option is included when the script
is invoked, the script uses the XSLT file specified in the option.
Otherwise, it uses the default XSLT file specified in the code. The
script exits if the file does not exist. The following example is
from the diagnose_bgp.pl script:
my $xslfile = $opt{x} || "xsl/text.xsl"; if ($xslfile && ! -f $xslfile) { die "ERROR: XSLT file $xslfile does not exist";
For examples of XSLT files, see the following directories in the Junos XML protocol Perl distribution:
The examples/diagnose_bpg/xsl directory contains XSLT files for the diagnose_bpg.pl script: dhtml.xsl generates dynamic HTML, html.xsl generates HTML, and text.xsl generates ASCII text.
The examples/get_chassis_inventory/xsl directory contains XSLT files for the get_chassis_inventory.pl script: chassis_inventory_csv.xsl generates a list of comma-separated values, chassis_inventory_html.xsl generates HTML, and chassis_inventory_xml.xsl generates XML.
The actual parsing operation begins by setting the variable $xmlfile
to a filename of the form device-name.xml and invoking
the printToFile
function to write the Junos
XML protocol server’s response into the file (the printToFile
function is defined in the XML::DOM::Parser
module):
my $xmlfile = "$deviceinfo{hostname}.xml"; $res->printToFile($xmlfile);
The next line invokes the translateXSLtoRelease
function (defined in the Junos::Response
module) to alter one of the namespace definitions in the XSLT file.
This is necessary because the XSLT 1.0 specification requires that
every XSLT file define a specific value for each default namespace
used in the data being transformed. The xmlns
attribute in a Junos XML operational response tag element includes
a code representing the Junos OS version, such as 20.4R1 for
the initial version of Junos OS Release 20.4. Because the same
XSLT file can be applied to operational response tag elements from
devices running different versions of the Junos OS, the XSLT file
cannot predefine an xmlns
namespace value
that matches all versions. The translateXSLtoRelease
function alters the namespace definition in the XSLT file identified
by the $xslfile
variable to match the value
in the Junos XML protocol server’s response. It assigns the
resulting XSLT file to the $nm
variable.
my $nm = $res->translateXSLtoRelease('xmlns:lc', $xslfile, "$xslfile.tmp");
After verifying that the translateXSLtoRelease
function succeeded, the invokes the format_by_xslt
, which builds a command string and assigns it to the $command
variable. The first part of the command string
invokes the xsltproc
command and specifies the names of
the XSLT and configuration data files (stored in $nm
and $deviceinfo{hostname}.xml
):
if ($nm) { print "Transforming $xmlfile with $xslfile...\n" if $outputfile; my $command = "xsltproc $nm $deviceinfo{hostname}.xml";
If the $outputfile
variable
is defined (the file for storing the result of the XSLT transformation
exists), the script appends a string to the $command
variable to write the results of the xsltproc
command
to the file. (If the file does not exist, the script writes the results
to standard out [stdout].) The script then invokes the system
function to execute the command string and prints
status messages to stdout.
$command .= "> $outputfile" if $outputfile; system($command); print "Done\n" if $outputfile; print "See $outputfile\n" if $outputfile; }
If the translateXSLtoRelease
function
fails (the if ($nm)
expression evaluates
to "false"), the script prints an error:
else { print STDERR "ERROR: Invalid XSL file $xslfile\n"; }
Parsing and Outputting Configuration Data
The get_config.pl script in
the examples\RDB directory uses the outconfig
subroutine to write the configuration data obtained from the Junos
XML protocol server to a file either as Junos XML tag elements or
as formatted ASCII text.
The outconfig
subroutine takes four
parameters. Three must have defined values: the directory in which
to store the output file, device hostname, and the XML DOM tree (the
configuration data) returned by the Junos XML protocol server. The
fourth parameter indicates whether to output the configuration as
formatted ASCII text, and has a null value if the requested output
is Junos XML tag elements. In the following code sample, the script
obtains values for the four parameters and passes them to the outconfig
subroutine. A detailed discussion of each
line follows the complete code sample.
my(%opt,$login,$password); getopts('l:p:dm:hit', \%opt) || output_usage(); output_usage() if $opt{h}; my $basepath = shift || output_usage; my $hostname = shift || output_usage; my $config = getconfig( $hostname, $jnx, $opt{t} ); outconfig( $basepath, $hostname, $config, $opt{t} );
In the first lines of the preceding sample code, the get_config.pl script uses the following statements
to obtain values for the four parameters to the outconfig
subroutine:
If the user provides the
-t
option on the command line, thegetopts
subroutine records it in the%opt
hash. The value keyed to$opt{t}
is passed as the fourth parameter to theoutconfig
subroutine. (For more information about reading options from the command line, see Collecting Parameters Interactively in Junos XML Protocol Perl Client Applications.)getopts('l:p:dm:hit', \%opt) || output_usage();
The following line reads the first element of the command line that is not an option preceded by a hyphen. It assigns the value to the
$basepath
variable, defining the name of the directory in which to store the file containing the output from theoutconfig
subroutine. The variable value is passed as the first parameter to theoutconfig
subroutine.my $basepath = shift || output_usage;
The following line reads the next element on the command line. It assigns the value to the
$hostname
variable, defining the routing, switching, or security device hostname. The variable value is passed as the second parameter to theoutconfig
subroutine.my $hostname = shift || output_usage;
The following line invokes the
getconfig
subroutine to obtain configuration data from the Junos XML protocol server on the specified device, assigning the resulting XML DOM tree to the$config
variable. The variable value is passed as the third parameter to theoutconfig
subroutine.my $config = getconfig( $hostname, $jnx, $opt{t} );
The following code sample invokes and defines the outconfig
subroutine. A detailed discussion of each
functional subsection in the subroutine follows the complete code
sample.
outconfig( $basepath, $hostname, $config, $opt{t} ); sub outconfig( $$$$ ) { my $leader = shift; my $hostname = shift; my $config = shift; my $text_mode = shift; my $trailer = "xmlconfig"; my $filename = $leader . "/" . $hostname . "." . $trailer; print "# storing configuration for $hostname as $filename\n"; my $config_node; my $top_tag = "configuration"; $top_tag .= "-text" if $text_mode; if ($config->getTagName() eq $top_tag) { $config_node = $config; } else { print "# unknown response component ", $config->getTagName(), "\n"; } if ( $config_node && $config_node ne "" ) { if ( open OUTPUTFILE, ">$filename" ) { if (!$text_mode) { print OUTPUTFILE "<?xml version=\"1.0\"?>\n"; print OUTPUTFILE $config_node->toString(), "\n"; } else { my $buf = $config_node->getFirstChild()->toString(); $buf =~ s/($char_class)/$escapes{$1}/ge; print OUTPUTFILE "$buf\n"; } close OUTPUTFILE; } else { print "ERROR: could not open output file $filename\n"; } } else { print "ERROR: empty configuration data for $hostname\n"; } }
The first lines of the outconfig
subroutine read in the four parameters passed in when the subroutine
is invoked, assigning each to a local variable:
outconfig( $basepath, $hostname, $config, $opt{t} ); sub outconfig( $$$$ ) { my $leader = shift; my $hostname = shift; my $config = shift; my $text_mode = shift;
The subroutine constructs the name of the file to which
to write the subroutine’s output and assigns the name to the $filename
variable. The filename is constructed from
the first two parameters (the directory name and hostname) and the $trailer
variable, resulting in a name of the form directory-name/hostname.xmlconfig:
my $trailer = "xmlconfig"; my $filename = $leader . "/" . $hostname . "." . $trailer; print "# storing configuration for $hostname as $filename\n";
The subroutine checks that the first tag in the XML DOM tree
correctly indicates the type of configuration data in the file. If
the user included the -t
option on the
command line, the first tag should be <configuration-text>
because the file contains formatted ASCII configuration statements;
otherwise, the first tag should be <configuration>
because the file contains Junos XML tag elements. The subroutine
sets the $top_tag
variable to the appropriate
value depending on the value of the $text_mode
variable (which takes its value from opt{t}
, passed as the fourth parameter to the subroutine). The subroutine
invokes the getTagName
function (defined
in the XML::DOM::Element
module) to retrieve
the name of the first tag in the input file, and compares the name
to the value of the $top_tag
variable.
If the comparison succeeds, the XML DOM tree is assigned to the $config_node
variable. Otherwise, the subroutine prints
an error message because the XML DOM tree is not valid configuration
data.
my $config_node; my $top_tag = "configuration"; $top_tag .= "-text" if $text_mode; if ($config->getTagName( ) eq $top_tag) { $config_node = $config; } else { print "# unknown response component ", $config->getTagName( ), "\n"; }
The subroutine then uses several nested if
statements. The first if
statement verifies that the
XML DOM tree exists and contains data:
if ( $config_node && $config_node ne "" ) { ... actions if XML DOM tree contains data ... } else { print "ERROR: empty configuration data for $hostname\n"; }
If the XML DOM tree contains data, the subroutine verifies that the output file can be opened for writing:
if ( open OUTPUTFILE, ">$filename" ) { ... actions if output file is writable ... } else { print "ERROR: could not open output file $filename\n"; }
If the output file can be opened for writing, the script
writes the configuration data into it. If the user requested Junos
XML tag elements—the user did not include the -t
option
on the command line, so the $text_mode
variable
does not have a value—the script writes the string <?xml version=1.0?>
as the first line in the
output file, and then invokes the toString
function (defined in the XML::DOM
module)
to write each Junos XML tag element in the XML DOM tree on a line
in the output file:
if (!$text_mode) { print OUTPUTFILE "<?xml version=\"1.0\"?>\n"; print OUTPUTFILE $config_node->toString( ), "\n";
If the user requested formatted ASCII text, the script invokes
the getFirstChild
and toString
functions (defined in the XML::DOM
module)
to write the content of each tag on its own line in the output file.
The script substitutes predefined entity references for disallowed
characters (which are defined in the %escapes
hash), writes the output to the output file, and closes the output
file. (For information about defining the %escapes
hash to contain the set of disallowed characters, see Converting Disallowed Characters in Junos XML Protocol
Perl Client Applications.)
} else { my $buf = $config_node->getFirstChild( )->toString( ); $buf =~ s/($char_class)/$escapes{$1}/ge; print OUTPUTFILE "$buf\n"; } close OUTPUTFILE;