How to write reusable scripts with Junos OS


The purpose of this article is not to show you all the things that you can accomplish using Python libraries or how to write scripts in Python or any other language. It is more to show you the flexibility of Junos OS and how one can interact with it without having too much knowledge of Junos CLI or even Junos OS.

The first tool to automate part of the Junos running devices is netconify. Information about prerequisites and installation can be found on the reference section of this article. This library allows the operator to connect to console port of a new device and push a minimal configuration like management IP address, default route, root password, enable ssh/telnet. After this the operator can connect through ssh or telnet and continue the configuration of the device.

When a new device is accessed over console, the following is the prompt returned on the terminal:

Amnesiac (ttyu0)
login:

The root user can connect without any password and start configure the device using Junos CLI. Most of the times, the initial configuration will include a standard configuration that needs to be slightly modified to reflect the particularities of the device(like management IP address). Netconify allows the connection over the serial interface and loading of the configuration. Obviously the server where netconify runs has to have IP connectivity with the terminal server.

Let’s suppose I have a base configuration to which I am adding the IP address used to manage this device. The content is stored in a file, base.conf:

system {
     host-name qfx5100-96s-8q;
     root-authentication {
encrypted-password "$6$rIjJx2br$4pHZkteWpSpiwHD8H1.kPm50jMb7x9NnCvNiYYgJ1159NQY4yGqU4E.
ZAdxj6F3yFgabFIpMQ6tfB3aKPBOoS0"; ## SECRET-DATA
   }
   login {
       user admin {
           uid 2002;
           class super-user;
           authentication {
encrypted-password "$6$a4HVzY/5$l5Okrh0VUOfVbDMnvSqi3KNAi2K0PaM.
EB5nrEdoEf8BiRr5zAmGlDcq7GMTd0WSpR7mQLCcpuyhHQZT37XK11"; ## SECRET-DATA
                }
          }
    }
    services {
          ssh;
                netconf {
                      ssh;
                }
     }
}
interfaces {
    em0 {
        unit 0 {
            family inet {
                address 192.168.158.137/24;
            }
        }
    }
}
routing-options {
    static {
        route 0.0.0.0/0 next-hop 192.168.158.1;
    }
}

This is how netconify can connect to the device on serial port and push the configuration. You will need to specify the terminal server IP address and the port to which device’s console port is connected:

admin@UBUNTU:~/admin$ /usr/local/bin/netconify --telnet 172.16.1.220,7002 -f 
/home/admin/base.conf
TTY:login:connecting to TTY:172.16.1.220:7002 ...
TTY:login:logging in ...
TTY:login:starting NETCONF
conf:loading into device ...
conf:commit ... please be patient
conf:commit completed.
TTY:logout:logging out ...
admin@UBUNTU:~/admin$

After this step, you can connect to the device using ssh using some of the credentials configured:

admin@UBUNTU:~/admin$ ssh admin@192.168.158.137
The authenticity of host '192.168.158.137 (192.168.158.137)' can't be established.
ECDSA key fingerprint is 11:ab:78:f2:23:2c:32:c3:1a:e5:7d:63:60:ba:ce:6e.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.158.137' (ECDSA) to the list of known hosts.
Password:
--- JUNOS 17.3R1.10 built 2017-08-23 06:42:40 UTC
{master:0}
admin@qfx5100-96s-8q>

On top of this, netconify can retrieve information from the device, but only after there is an initial configuration where you set an account or set the root password:

admin@UBUNTU:~/admin$ /usr/local/bin/netconify --telnet 172.16.1.220,7002
 -u admin -P admin123 --facts --savedir /tmp
TTY:login:connecting to TTY:172.16.1.220:7002 ...
TTY:login:logging in ...
TTY:login:starting NETCONF
facts:retrieving device facts...
facts:saving: tmp/qfx5100-96s-8q-facts.json
inventory:saving: /tmp/qfx5100-96s-8q-inventory.xml
TTY:logout:logging out ...
admin@UBUNTU:~/admin$

With this information you can find out the type of the device, the software running and the serial number:

admin@UBUNTU:~/admin$ cat /tmp/qfx5100-96s-8q-facts.json
{"model": "QFX5100-96S-8Q", "version": "17.3R1.10", "serialnumber":
 "VB3714380009", "hostname": "qfx5100-96s-8q"}
admin@UBUNTU:~/admin$

There are few other things that you can accomplish using this library and additional information can be gathered from the reference section links at the end of the article. The library helps the operator to speed up the initial configuration without touching the CLI and minimize the chances of typos that can later trigger unnecessary troubleshooting. This approach can be used for any kind of Junos OS capable device, regardless it is switch, router or firewall.

One other way you can make use of scripts is to use Junos PyEZ. Junos PyEZ is a framework that allows to manage devices running Junos OS using Python. Information about prerequisites and installation can be found on the reference section of this article. Although you can connect using various methods to the device, it is recommended to do it using netconf over ssh. For this to be possible, the following configuration is needed:

> show configuration system services
netconf {
   ssh;
}
>

For testing purposes, we will use a script that will upgrade a device running Junos OS with a specific release based on the type of the device, router or a switch. In real life, it can happen very often that an operator chooses a release that must be used on specific types of device after an extensive validation of device stability with that software, like for instance, all switches should use software X.

This basic script will have a hardcoded images information and based on the device will choose to install the switch compatible software or the router compatible software. Without any intention of stating what should be the recommended release, the two releases were chose for QFX and MX devices randomly (as in only these two were available for testing). The script will use the IP address provided as parameter and along with the hardcoded username and password, will retrieve the device facts and figure out if the device is a router or a switch.

Going further, the script will copy the appropriate release on the device (the default location is /var/tmp if nothing else is specified) and it will start the installation. Once the installation is successful, the script will initiate the reboot of the device to start using the new release. To make it clear, this is a very simple example how PyEZ can be used on device using Junos OS.

This script does not do the following things:

  • It does not return an error if it cannot connect to the device(maybe the username is not correct, or the password or netconf is not enabled)
  • It does not return an error if the installation is not successful
  • It does not upgrade both Routing Engines of a device, but only the active one

All the above things can be implemented pretty easy by just following the steps from the official documentation. This is the script:

import os, sys
from jnpr.junos import Device
from jnpr.junos.utils.sw import SW

package_sw_qfx = '/home/admin/pyez/jinstall-host-qfx-5-17.3R1.10-signed.tgz'
package_sw_mx = '/home/admin/pyez/jinstall-14.2R8.4-domestic-signed.tgz'
dut = Device(host=(sys.argv[1]), user='admin', password='admin123')
dut.open()
from pprint import pprint
type = (dut.facts['personality'])
software = SW(dut)
if type == 'SWITCH':
    print("This is a switch. Commencing the switch upgrade")
    installation = software.install(package=package_sw_qfx)
elif type == 'MX':
    print("This is a router. Commencing the router upgrade")
    installation = software.install(all_re=False,package=package_sw_mx)
if installation == True:
    rsp = software.reboot()
dut.close()

So, let’s see what happens when the script is used against an MX480.
This is the first device on which the script will be used:

[edit]
admin@mx480-re0# run show version | match "Junos:|Model:"
Model: mx480
Junos: 13.3R6.5

[edit]
admin@mx480-re0# show interfaces fxp0 | display inheritance no-comments
unit 0 {
    family inet {
        address 192.168.151.83/24;
    }
}

[edit]
admin@mx480-re0#

The script is taking as parameter the IP address used to manage the device:

admin@UBUNTU:~/pyez$ python upgrade.py 192.168.151.83
This is a router. Commencing the router upgrade

Due to how the script was built, it first detects what kind of the device is it so that it will know what Junos will try to install on the device. In the logs of the device, it can be easily seen that the release was successfully copied on the device and the upgrade started:

Mar 15 15:46:12  mx480-re0 file[79813]: /usr/libexec/ui/package -X update
 -no-validate /var/tmp/jinstall-14.2R8.4-domestic-signed.tgz

After the upgrade is completed, the device is performing a reboot to complete the upgrade and to boot the new release:

*** FINAL System shutdown message from admin@mx480-re0 ***

System going down IMMEDIATELY

After the reboot, the device shows the new release:

[edit]
admin@mx480-re0# run show version | match "Junos:|Model:"
Model: mx480
Junos: 14.2R8.4

[edit]
admin@mx480-re0#

Next, let’s run the script on a QFX5100:

{master:0}[edit]
admin@qfx5100-96s-8q# run show version | match "Junos:|Model:"
Model: qfx5100-96s-8q
Junos: 15.1R6.7
{master:0}[edit]
admin@qfx5100-96s-8q# show interfaces em0
unit 0 {
    family inet {
        address 192.168.158.137/24;
    }
}

{master:0}[edit]
admin@qfx5100-96s-8q#

Once the script is executed, it detects that the new device is a switch:

admin@UBUNTU:~/pyez$ python upgrade.py 192.168.158.137
This is a switch. Commencing the switch upgrade

Therefore, a different type of image should be copied on the device and installed:

Mar 15 16:41:00  qfx5100-96s-8q mgd[2253]: /usr/libexec/ui/package -X
 update -no-validate /var/tmp/jinstall-host-qfx-5-17.3R1.10-signed.tgz

Again, if the installation is successful, the device will reboot to finalize the upgrade and boot with the new software:

*** FINAL System shutdown message from admin@qfx5100-96s-8q ***

System going down IMMEDIATELY

After reboot:

{master:0}[edit]
admin@qfx5100-96s-8q# run show version | match "Junos:|Model:"
Model: qfx5100-96s-8q
Junos: 17.3R1.10

{master:0}[edit]
admin@qfx5100-96s-8q#

As can be seen, the upgrade was successful in both situations and there was no need that the operator to choose manually what release should be installed on the device. The operator did not even need to know what kind of device that was.

Less human touch means decreased chances of configuration errors caused by typos. In this article, we saw how various Python libraries can be used to automate operations on Junos OS. The interactions can be operational or configuration tasks.

Reference articles:

  1. Netconify
  2. Understanding Junos PyEZ
  3. Welcome to Junos PyEZ’s documentation!
  4. py-junos-eznc

Thank you to community member Paris Arau for contributing this content. If you are ready to start developing your own python or ind. scripts head over to the Indeni Community. Would you like to have these tasks automated? Download Indeni and see how you can automate task validation for Juniper SRX devices today.

About the author
Paris Arau

Start the discussion at community.indeni.com