Project

General

Profile

Download (18.1 KB) Statistics
| Branch: | Tag: | Revision:
<%#
kind: provision
name: Junos default SLAX
-%>
version 1.0;

/* ------------------------------------------------------------------
* LICENSE
* ------------------------------------------------------------------
*
* Copyright (c) 2013, Juniper Networks
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* (1) Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* (2) Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of Juniper Networks.
*
*
* ------------------------------------------------------------------
* AUTHORS AND CONTRIBUTORS
* ------------------------------------------------------------------
*
* Jeremy Schulman, Juniper Networks
* - initial release (https://github.com/jeremyschulman/jctyztp/)
*
* Frank Wall, noris network AG
* - adapt for integration in The Foreman
* - fix compatibility with older Junos releases
*
*
* ------------------------------------------------------------------
* LIMITATIONS
* ------------------------------------------------------------------
*
* To maintain backwards compatibility with JunOS 11.x (and maybe
* even 10.x) you MUST AVOID all of these:
* - global variable $junos-context (introduced with Junos 11.1)
* - mutable variables (introduced with JunOS 12.2 -> SLAX 1.1)
* - native functions (introduced with JunOS 12.2 -> SLAX 1.1)
*
* ------------------------------------------------------------------
*/

/* ------------------------------------------------------------------ */
/* XML namespaces */
/* ------------------------------------------------------------------ */

/* Juniper */
ns junos = "http://xml.juniper.net/junos/*/junos";
ns xnm = "http://xml.juniper.net/xnm/1.1/xnm";
ns jcs = "http://xml.juniper.net/junos/commit-scripts/1.0";
ns ztp = "http://xml.juniper.net/junos/ztp";

/* EXSLT */
ns exsl extension = "http://exslt.org/common";
ns func extension = "http://exslt.org/functions";
ns str extension = "http://exslt.org/strings";

/* private namespace for this script */
ns jctyztp = "http://xml.juniper.com/jctyztp/1.0";
ns fmztp = "http://xml.juniper.com/fmztp/1.0";

/* depending on Junos version relative path may be broken */
/* import '../import/junos.xsl'; */
import '/usr/libdata/cscript/import/junos.xsl';

/* ------------------------------------------------------------------ */
/* Script parameters */
/* ------------------------------------------------------------------ */

param $server = 'ztpserver';
param $mediapath = '<%= @mediapath %>';

/* ------------------------------------------------------------------ */
/* Constants */
/* ------------------------------------------------------------------ */

var $APPNAME = 'foreman-ztp';
var $SYSLOG = 'user.info';
var $TMPDIR = '/var/tmp';
var $JUNOS_CONF = '/var/tmp/junos.conf';

var $ZTP_GROUP_NAME = "fmztp";
var $ZTP_MACRO_NAME = "conf";
var $ZTP_LOCKFILE = '/tmp/fmztp.lock';

/* ------------------------------------------------------------------ */
/* Global variables */
/* ------------------------------------------------------------------ */

/* Open a connection to the device API */
var $jnx = jcs:open();

/* ------------------------------------------------------------------ */
/* MAIN */
/* ------------------------------------------------------------------ */

match / {

/* Terminate on connection error */
if(not( $jnx )) {
expr jcs:syslog( $SYSLOG, $APPNAME, ":ERROR: unable to connect to Junos API");
expr fmztp:terminate();
}
var $running = fmztp:only_once();
if( $running ) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": start op script: already running, exiting..." );
<xsl:message terminate="yes">;
}
/* if the $JUNOS_CONF file is not on the device, then */
/* download it from the server */
if(not( fmztp:file-exists( $JUNOS_CONF ))) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": obtaining device config file");
var $cp = fmztp:dl_junos_conf();
}
/* now load $JUNOS_CONF into the candidate configuration so we can */
/* extract the ZTP config */
var $ztp_conf = fmztp:ld_junos_conf();
var $has_version = $ztp_conf/has_version;
var $new_package = $ztp_conf/package;
expr jcs:syslog( $SYSLOG, $APPNAME, ": preparing update from ", $has_version, " to ", $new_package );
/* if we have a version difference, then we will install the new OS */
/* and reboot the device. the $JUNOS_CONF file will be loaded on */
/* after the install process completes */
if( $ztp_conf/install ) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": Junos install required" );
var $os = fmztp:install_os( $ztp_conf );
expr jcs:syslog( $SYSLOG, $APPNAME, ": rebooting in 60 seconds" );
expr jcs:syslog( $SYSLOG, $APPNAME, ": SCRIPT-END");
expr fmztp:reboot( 1 );
expr jcs:close( $jnx );
}
else {
expr jcs:syslog( $SYSLOG, $APPNAME, ": no Junos install required" );
/* puppet agent is only supported on Junos 12.3R2.5 */
if( jcs:regex( "12.3R2.5", $ztp_conf/has_version )) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": puppet agent install required" );
var $puppet = fmztp:install_puppet( $ztp_conf );
}
else {
expr jcs:syslog( $SYSLOG, $APPNAME, ": os version is ", $has_version, ", but puppet agent is only available on 12.3R2.5" );
}
var $fini = fmztp:finalize();
expr jcs:syslog( $SYSLOG, $APPNAME, ": SCRIPT-END");
expr jcs:close( $jnx );
}
}

/* ------------------------------------------------------------------ */
/* HTTP Junos configuration file onto the device */
/* ------------------------------------------------------------------ */

<func:function name="fmztp:dl_junos_conf">
{
expr jcs:syslog( $SYSLOG, $APPNAME, ": downloading new Junos conf...");
expr jcs:syslog( $SYSLOG, $APPNAME, ": URL: ", '<%= foreman_url('finish') %>');

var $get = <file-copy> {
<source> '<%= foreman_url('finish') %>';
<destination> $JUNOS_CONF;
<staging-directory> $TMPDIR;
};

var $got = jcs:execute( $jnx, $get );

if(not( fmztp:file-exists( $JUNOS_CONF ))) {
expr jcs:syslog( $SYSLOG, $APPNAME, ":ERROR: unable to find new Junos conf at ", $JUNOS_CONF);
expr fmztp:terminate();
}

<func:result select="true()">;
}

/* ------------------------------------------------------------------ */
/* Load the $JUNOS_CONF file into the candidate config and extract */
/* the ZTP config from the [edit groups] area. Do *NOT* commit */
/* this configuration yet, since we may need to install the OS first */
/* ------------------------------------------------------------------ */

<func:function name="fmztp:ld_junos_conf">
{
expr jcs:syslog( $SYSLOG, $APPNAME, ": loading new Junos conf...");

/* get the current version from the configuration file */
var $get_cur_ver = <get-configuration database='committed'> { <configuration> { <version>; }};
var $got_cur_ver = jcs:execute( $jnx, $get_cur_ver );

/* now load the configuration file we got from the ztp server */
var $do_load = <load-configuration action="override" url=$JUNOS_CONF format="text">;
var $did_load = jcs:execute( $jnx, $do_load );
if(not( $did_load/load-success )) {
expr jcs:syslog( $SYSLOG, $APPNAME, ":ERROR: unable to load config ", $JUNOS_CONF );
expr fmztp:terminate();
}
expr jcs:syslog( $SYSLOG, $APPNAME, ": extracting Junos config parameters...");
var $get = <get-configuration> { <configuration> {
<version>;
<groups> { <name> $ZTP_GROUP_NAME;
<apply-macro> { <name> $ZTP_MACRO_NAME;
}
}
}};
var $got = jcs:execute( $jnx, $get );

expr jcs:syslog( $SYSLOG, $APPNAME, ": package: ", $got//data[name = 'package']/value);
expr jcs:syslog( $SYSLOG, $APPNAME, ": URL: ", $mediapath);

/* create a node-set to store the following elements */
/* has_version = current Junos version string */
/* package = filename of Junos package (*.tgz) */
/* mediapath = URL where package is obtained from */
/* install = present if a install is requeired */
var $ver = $got_cur_ver/version;
var $package = $got//data[name = 'package']/value;
var $puppet = $got//data[name = 'puppet']/value;
var $conf := {
<has_version> $ver;
<package> $package;
<puppet> $puppet;
<url> $mediapath;
if(not( jcs:regex( $ver, $package ))) {
<install>;
}
}

/* @@@ should put some trap here on ensuring that the config */
/* @@@ file actually had the correct group/macro defined */
<func:result select="$conf">;
}

/* ------------------------------------------------------------------ */
/* Junos Software Installation - download the software from the HTTP */
/* server and perform the 'request system software add' operation */
/* ------------------------------------------------------------------ */

<func:function name="fmztp:install_os">
{
param $ztp_conf;

var $local_image = $TMPDIR _ "/" _ $ztp_conf/package;
if( fmztp:file-exists( $local_image )) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": junos image exists, no download needed" );
}
else {
/* request system storage cleanup */
expr jcs:syslog( $SYSLOG, $APPNAME, ": cleaning filesystem" );
var $clean = jcs:execute( $jnx, 'request-system-storage-cleanup' );

/* file copy .... */
expr jcs:syslog( $SYSLOG, $APPNAME, ": downloading junos image..." );
expr jcs:syslog( $SYSLOG, $APPNAME, ": URL: ", $ztp_conf/url, $ztp_conf/package );
var $do_copy = <file-copy> {
<source> $ztp_conf/url _ $ztp_conf/package;
<destination> $TMPDIR;
<staging-directory> $TMPDIR;
};
var $did_copy = jcs:execute( $jnx, $do_copy );
/* trap error here */
if( not(fmztp:file-exists( $local_image )) ) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": ERROR: unable to download junos image" );
expr fmztp:terminate();
}
}
/* request system software add ... */
expr jcs:syslog( $SYSLOG, $APPNAME, ": installing junos image" );
var $do_install = <request-package-add> {
<no-validate>;
<package-name> $local_image;
};
var $did_install = jcs:execute( $jnx, $do_install );
/* @@@ need to trap error here on $did_install */
expr jcs:syslog( $SYSLOG, $APPNAME, ": completed installing junos image" );
<func:result select="true()">;
}

/* ------------------------------------------------------------------ */
/* Reboot the device given a delay, in minutes */
/* ------------------------------------------------------------------ */

<func:function name="fmztp:reboot">
{
param $in_min;

var $do_reboot = <request-reboot> { <in> $in_min; };
var $did_reboot = jcs:execute( $jnx, $do_reboot );
<func:result select="true()">;
}

/* ------------------------------------------------------------------ */
/* Puppet Agent Installation - download the software from the HTTP */
/* server and perform the 'request system software add' operation */
/* ------------------------------------------------------------------ */

<func:function name="fmztp:install_puppet">
{
param $ztp_conf;
expr jcs:syslog( $SYSLOG, $APPNAME, ": starting puppet installation..." );

if (fmztp:is-installed("puppet")) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": package puppet is already installed" );
<func:result select="true()">;
}

var $local_image = $TMPDIR _ "/" _ $ztp_conf/puppet;
if( fmztp:file-exists( $local_image )) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": package puppet exists, no download needed" );
}
else {
/* request system storage cleanup */
expr jcs:syslog( $SYSLOG, $APPNAME, ": cleaning filesystem" );
var $clean = jcs:execute( $jnx, 'request-system-storage-cleanup' );

/* file copy .... */
expr jcs:syslog( $SYSLOG, $APPNAME, ": downloading puppet image..." );
expr jcs:syslog( $SYSLOG, $APPNAME, ": URL: ", $ztp_conf/url, $ztp_conf/puppet );
var $do_copy = <file-copy> {
<source> $ztp_conf/url _ $ztp_conf/puppet;
<destination> $TMPDIR;
<staging-directory> $TMPDIR;
};
var $did_copy = jcs:execute( $jnx, $do_copy );
/* trap error on $did_copy */
if( not(fmztp:file-exists( $local_image )) ) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": ERROR: failed to download puppet image" );
expr fmztp:terminate();
}
}

/* request system software add ... */
expr jcs:syslog( $SYSLOG, $APPNAME, ": installing puppet image" );
var $do_install = <request-package-add> {
<no-validate>;
<package-name> $local_image;
};
var $did_install = jcs:execute( $jnx, $do_install );
/* NOTE: To complete the puppet installation you need to take manual steps, see:
* http://www.juniper.net/techpubs/en_US/release-independent/junos-puppet/information-products/pathway-pages/index.html
*/

/* validate installation */
if (not(fmztp:is-installed("puppet"))) {
expr jcs:syslog( $SYSLOG, $APPNAME, ": ERROR: failed to install package puppet" );
expr fmztp:terminate();
}

expr jcs:syslog( $SYSLOG, $APPNAME, ": completed installing puppet image" );
<func:result select="true()">;
}

/* ------------------------------------------------------------------ */
/* Finalize the ZTP process; i.e. after the OS is correct. Remove */
/* the $JUNOS_CONF file and committing the configuration to make */
/* it active. */
/* ------------------------------------------------------------------ */

<func:function name="fmztp:finalize">
{
expr jcs:syslog( $SYSLOG, $APPNAME, ": deleting temp config file...");
var $rm1 = fmztp:file-delete( $JUNOS_CONF );

expr jcs:syslog( $SYSLOG, $APPNAME, ": sending finish signal to foreman...");
var $do_foreman = <file-copy> {
<source> '<%= foreman_url %>';
<destination> "/tmp";
<staging-directory> "/tmp";
};
var $did_foreman = jcs:execute( $jnx, $do_foreman );

expr jcs:syslog( $SYSLOG, $APPNAME, ": deleting ztp lock file...");
var $rm2 = fmztp:file-delete( $ZTP_LOCKFILE );

/* commit the configuration that was previously loaded */
expr jcs:syslog( $SYSLOG, $APPNAME, ": committing configuration...");
var $commit = jcs:execute( $jnx, 'commit-configuration' );
if( $commit//self::xnm:error ) {
expr jcs:syslog( $SYSLOG, $APPNAME, ":ERROR: unable to commit configuration: ", $commit//self::xnm:error/message );
var $die = fmztp:terminate();
}

<func:result select="true()">;
}

/* ------------------------------------------------------------------ */
/* Helper routine: check to see if a file exists on the device, */
/* returns [ true | false ] */
/* ------------------------------------------------------------------ */

<func:function name="fmztp:file-exists">
{
param $filename;
var $ls_file = <file-list> { <path> $filename; };
var $ls_got = jcs:execute( $jnx, $ls_file );
var $retval = boolean( $ls_got//file-information );

<func:result select="$retval">;
}

<func:function name="fmztp:file-delete">
{
param $filename;
var $do_rm = <file-delete> { <path> $filename; };
var $did_rm = jcs:execute( $jnx, $do_rm );
/* @@@ trap error */
<func:result select="true()">;
}

/* ------------------------------------------------------------------ */
/* Helper routine: create a lockfile to make sure the script only */
/* runs once, and terminate if it is already running. */
/* returns [ true | false ] */
/* ------------------------------------------------------------------ */

<func:function name="fmztp:only_once">
{
if( fmztp:file-exists( $ZTP_LOCKFILE )) {
<func:result select="true()">;
}
else {
var $do_lock = <file-put> {
<filename> $ZTP_LOCKFILE;
<encoding> 'ascii';
<file-contents> 'locked';
};
var $did_lock = jcs:execute( $jnx, $do_lock );
<func:result select="false()">;
}
}

<func:function name="fmztp:terminate">
{
expr jcs:syslog( $SYSLOG, $APPNAME, ": SCRIPT-FAILED" );
var $rm_lock = fmztp:file-delete( $ZTP_LOCKFILE );
<xsl:message terminate="yes">;
}

/* ------------------------------------------------- */
/* check if software package is installed */
/* ------------------------------------------------- */
<func:function name="fmztp:is-installed">
{
param $string;

var $do_query = <get-software-information>;
var $did_query = jcs:execute( $jnx, $do_query );

if( jcs:regex( $string, $did_query )) {
expr jcs:output("package found: ", $string);
expr jcs:syslog( $SYSLOG, $APPNAME, ": package found: ", $string);
<func:result select="true()">;
}
else {
expr jcs:output("package NOT found: ", $string);
expr jcs:syslog( $SYSLOG, $APPNAME, ": package NOT found: ", $string);
<func:result select="false()">;
}
}
(4-4/4)