Getting Started with Juniper and Ansible

By Kirk Byers
2015-03-24

In this article, I will discuss the steps required to use Ansible on Juniper equipment. We will then use Ansible to gather facts from a Juniper SRX. Finally, we will use Ansible to transfer a small configuration change to the SRX.

For more information on Ansible, see the following:

Ansible Getting Started
Introduction to Ansible Inventory
Introduction to Playbooks


In my lab environment, I have a Juniper SRX that is configured to support NETCONF over SSH:

pyclass@pynet-jnpr-srx1# show system services netconf    
ssh;

[edit] 

Now Ansible will connect to this SRX using the NETCONF API from a remote server (in my case an AWS Linux server).

Before we do this, however, there are a set of things that need to be installed on the AWS server--1)the Juniper-Ansible modules, 2)Juniper's PyEZ library, and 3)Ansible itself (Ansible installation instructions)

# Downloading/extracting Juniper-Ansible modules
[kbyers ~]$ cd TEST
[kbyers TEST]$ wget https://github.com/Juniper/ansible-junos-stdlib/archive/1.1.0.tar.gz
[kbyers TEST]$ gunzip 1.1.0.tar.gz
[kbyers TEST]$ tar -xvpf 1.1.0.tar 

After I have downloaded and extracted the Juniper-Ansible modules, I then make a temporary WORK directory and a library subdirectory. This will enable Ansible to find the Juniper modules (By default, Ansible will search for modules in the ./library subdirectory of the current working directory).

[kbyers TEST]$ mkdir WORK
[kbyers TEST]$ cd WORK/
[kbyers WORK]$ mkdir library
[kbyers WORK]$ cd library/ 
[kbyers library]$ cp ../../ansible-junos-stdlib-1.1.0/library/* .
[kbyers library]$ ls -al
total 56
drwxrwxr-x. 2 kbyers kbyers  4096 Mar  9 15:09 .
drwxrwxr-x. 3 kbyers kbyers    98 Mar  9 15:26 ..
-rw-rw-r--. 1 kbyers kbyers     0 Mar  9 15:09 __init__.py
-rw-rw-r--. 1 kbyers kbyers  7348 Mar  9 15:09 junos_get_facts
-rw-rw-r--. 1 kbyers kbyers 14080 Mar  9 15:09 junos_install_config
-rw-rw-r--. 1 kbyers kbyers  9378 Mar  9 15:09 junos_install_os
-rw-rw-r--. 1 kbyers kbyers  4838 Mar  9 15:09 junos_shutdown
-rw-rw-r--. 1 kbyers kbyers  7554 Mar  9 15:09 junos_zeroize 

After the Juniper-Ansible modules are installed, I then install Juniper's PyEZ library (this library is required by the Juniper-Ansible modules):

# Installing Juniper's PyEZ library 
[kbyers TEST]$ wget https://github.com/Juniper/py-junos-eznc/archive/1.1.2.tar.gz
[kbyers TEST]$ gunzip 1.1.2.tar.gz  
[kbyers TEST]$ tar -xvpf 1.1.2.tar 
[kbyers TEST]$ cd py-junos-eznc-1.1.2/
[kbyers py-junos-eznc-1.1.2]$ python setup.py install     # 'sudo' might be required 

Finally, I need to install Ansible itself (if not already done):

[kbyers WORK]$ pip install ansible       # 'sudo' might be required in your environment 

Now, I am ready to start working on Ansible. I start by defining the SRX in my Ansible inventory file (note, the following is all one line):

[juniper]
pynet-sf-srx ansible_connection=local ansible_ssh_host=10.10.10.10  ansible_python_interpreter=~/applied_python/bin/python juniper_user=username  juniper_passwd=password 

The above definition states the following:

'ansible_connection=local' -- Ansible is going to execute the Juniper module on the local computer (i.e. on the Ansible server). By default, Ansible expects to transfer the module using SSH to the remote device. But in this case, the module runs locally and then uses the Juniper API to communicate to the device.

'ansible_ssh_host=10.10.10.10' -- IP address of the Juniper SRX, Juniper will make its API connection to this device (specified in playbook).

'ansible_python_interpreter = ~/applied_python/bin/python' -- Juniper-Ansible modules by default try to use /usr/bin/python. I am running in a virtualenv so I needed to override this. You probably don't need this in your environment.

'juniper_user=username' -- Username that Juniper will use in its NETCONF connection (specified in playbook).

'juniper_passwd=password' -- Password that Juniper will use in its NETCONF connection (specified in playbook).


Now, I am ready to start testing Ansible and Juniper. Here is a simple Ansible playbook that checks that the NETCONF port on my SRX is reachable.

[kbyers WORK]$ cat simple.yml 
---
- name: Verifying NETCONF
  hosts: pynet-sf-srx
  tasks: 
    - name: Verifying NETCONF
      wait_for: host={{ ansible_ssh_host }}  port=830      # variable defined in inventory file


[kbyers WORK]$ ansible-playbook simple.yml 

PLAY [Verifying NETCONF] ****************************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sf-srx]

TASK: [Verifying NETCONF] ***************************************************** 
ok: [pynet-sf-srx]

PLAY RECAP ******************************************************************** 
pynet-sf-srx               : ok=2    changed=0    unreachable=0    failed=0   

I can then expand on this and create a playbook that will gather facts from my SRX.

[kbyers WORK]$ cat facts.yml 
---
- name: Testing Juniper and Ansible
  hosts: pynet-sf-srx
  tasks: 
    - name: Verifying NETCONF
      wait_for: host={{ ansible_ssh_host }} port=830

    - name: Retrieve Juniper Facts
      junos_get_facts: host={{ ansible_ssh_host }} user={{ juniper_user }} passwd={{ juniper_passwd }}
      register: srx_info 

    - name: Print SRX information
      debug: msg="{{ srx_info.facts }}"


[kbyers WORK]$ ansible-playbook facts.yml 

PLAY [Testing Juniper and Ansible] ******************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sf-srx]

TASK: [Verifying NETCONF] ***************************************************** 
ok: [pynet-sf-srx]

TASK: [Retrieve Juniper Facts] ************************************************ 
ok: [pynet-sf-srx]

TASK: [Print SRX information] ************************************************* 
ok: [pynet-sf-srx] => {
    "msg": "{u'domain': None, u'serialnumber': u'BZ4614AF0938', u'ifd_style': u'CLASSIC', u'version_info': {u'major': [12, 1], u'type': u'X', u'build': 5, u'minor': [44, u'D', 35]}, u'RE0': {u'status': u'OK', u'last_reboot_reason': u'0x1:power cycle/failure', u'model': u'RE-SRX100H2', u'up_time': u'81 days, 4 hours, 6 minutes, 57 seconds'}, u'hostname': u'pynet-jnpr-srx1', u'fqdn': u'pynet-jnpr-srx1', u'has_2RE': False, u'switch_style': u'VLAN', u'version': u'12.1X44-D35.5', u'srx_cluster': False, u'HOME': u'/cf/var/home/pyclass', u'model': u'SRX100H2', u'personality': u'SRX_BRANCH'}"
} 

PLAY RECAP ******************************************************************** 
pynet-sf-srx               : ok=4    changed=0    unreachable=0    failed=0   

Now that I have verified my ability to community with the SRX, let's use Ansible to make a configuration change.

First, I define a configuration file that will add a static route:

[kbyers WORK]$ cat test_config.conf 

routing-options {
    static {
        route 1.1.1.0/24 next-hop 10.220.88.1;
    }
} 

Then I verify the current state of the SRX:

pyclass@pynet-jnpr-srx1# show routing-options 
static {
    route 0.0.0.0/0 next-hop 10.220.88.1;
}

[edit] 

Finally, I create an Ansible playbook. This playbook will load the test_config.conf file onto the SRX. Note, the playbook will automatically perform a 'commit' as part of this process.

[kbyers WORK]$ cat load_config.yml 
---
- name: Testing Juniper and Ansible
  hosts: pynet-sf-srx
  tasks: 
    - name: Verifying NETCONF
      wait_for: host={{ ansible_ssh_host }} port=830

    - name: Add a static route
      junos_install_config:
        host={{ ansible_ssh_host }}    # variable is from inventory file
        file=test_config.conf
        overwrite=false
        user={{ juniper_user }}        # variable is from inventory file
        passwd={{ juniper_passwd }}    # variable is from inventory file


[kbyers WORK]$ ansible-playbook load_config.yml 

PLAY [Testing Juniper and Ansible] ******************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sf-srx] 

TASK: [Verifying NETCONF] ***************************************************** 
ok: [pynet-sf-srx] 

TASK: [Add a static route] **************************************************** 
changed: [pynet-sf-srx] 

PLAY RECAP ******************************************************************** 
pynet-sf-srx                : ok=3    changed=1    unreachable=0    failed=0   

[kbyers WORK]$  

I then verify the change on the SRX:

pyclass@pynet-jnpr-srx1# show routing-options    
static {
    route 0.0.0.0/0 next-hop 10.220.88.1;
    route 1.1.1.0/24 next-hop 10.220.88.1;
}

[edit]
pyclass@pynet-jnpr-srx1#  

For additional reference see:

Using Ansible to Install Software on Devices Running Junos OS

junos_get_facts

junos_install_config


If you want to learn more about network automation, Python, and Ansible, join my email-list or follow me on Twitter: @kirkbyers. I also periodically run a free Python for Network Engineers email course which you can sign-up for here.

Kirk Byers
CCIE #6243 emeritus
Twitter: @kirkbyers

Getting Started with Juniper and Ansible

By Kirk Byers
2015-03-24

In this article, I will discuss the steps required to use Ansible on Juniper equipment. We will then use Ansible to gather facts from a Juniper SRX. Finally, we will use Ansible to transfer a small configuration change to the SRX.

For more information on Ansible, see the following:

Ansible Getting Started
Introduction to Ansible Inventory
Introduction to Playbooks


In my lab environment, I have a Juniper SRX that is configured to support NETCONF over SSH:

pyclass@pynet-jnpr-srx1# show system services netconf    
ssh;

[edit] 

Now Ansible will connect to this SRX using the NETCONF API from a remote server (in my case an AWS Linux server).

Before we do this, however, there are a set of things that need to be installed on the AWS server--1)the Juniper-Ansible modules, 2)Juniper's PyEZ library, and 3)Ansible itself (Ansible installation instructions)

# Downloading/extracting Juniper-Ansible modules
[kbyers ~]$ cd TEST
[kbyers TEST]$ wget https://github.com/Juniper/ansible-junos-stdlib/archive/1.1.0.tar.gz
[kbyers TEST]$ gunzip 1.1.0.tar.gz
[kbyers TEST]$ tar -xvpf 1.1.0.tar 

After I have downloaded and extracted the Juniper-Ansible modules, I then make a temporary WORK directory and a library subdirectory. This will enable Ansible to find the Juniper modules (By default, Ansible will search for modules in the ./library subdirectory of the current working directory).

[kbyers TEST]$ mkdir WORK
[kbyers TEST]$ cd WORK/
[kbyers WORK]$ mkdir library
[kbyers WORK]$ cd library/ 
[kbyers library]$ cp ../../ansible-junos-stdlib-1.1.0/library/* .
[kbyers library]$ ls -al
total 56
drwxrwxr-x. 2 kbyers kbyers  4096 Mar  9 15:09 .
drwxrwxr-x. 3 kbyers kbyers    98 Mar  9 15:26 ..
-rw-rw-r--. 1 kbyers kbyers     0 Mar  9 15:09 __init__.py
-rw-rw-r--. 1 kbyers kbyers  7348 Mar  9 15:09 junos_get_facts
-rw-rw-r--. 1 kbyers kbyers 14080 Mar  9 15:09 junos_install_config
-rw-rw-r--. 1 kbyers kbyers  9378 Mar  9 15:09 junos_install_os
-rw-rw-r--. 1 kbyers kbyers  4838 Mar  9 15:09 junos_shutdown
-rw-rw-r--. 1 kbyers kbyers  7554 Mar  9 15:09 junos_zeroize 

After the Juniper-Ansible modules are installed, I then install Juniper's PyEZ library (this library is required by the Juniper-Ansible modules):

# Installing Juniper's PyEZ library 
[kbyers TEST]$ wget https://github.com/Juniper/py-junos-eznc/archive/1.1.2.tar.gz
[kbyers TEST]$ gunzip 1.1.2.tar.gz  
[kbyers TEST]$ tar -xvpf 1.1.2.tar 
[kbyers TEST]$ cd py-junos-eznc-1.1.2/
[kbyers py-junos-eznc-1.1.2]$ python setup.py install     # 'sudo' might be required 

Finally, I need to install Ansible itself (if not already done):

[kbyers WORK]$ pip install ansible       # 'sudo' might be required in your environment 

Now, I am ready to start working on Ansible. I start by defining the SRX in my Ansible inventory file (note, the following is all one line):

[juniper]
pynet-sf-srx ansible_connection=local ansible_ssh_host=10.10.10.10  ansible_python_interpreter=~/applied_python/bin/python juniper_user=username  juniper_passwd=password 

The above definition states the following:

'ansible_connection=local' -- Ansible is going to execute the Juniper module on the local computer (i.e. on the Ansible server). By default, Ansible expects to transfer the module using SSH to the remote device. But in this case, the module runs locally and then uses the Juniper API to communicate to the device.

'ansible_ssh_host=10.10.10.10' -- IP address of the Juniper SRX, Juniper will make its API connection to this device (specified in playbook).

'ansible_python_interpreter = ~/applied_python/bin/python' -- Juniper-Ansible modules by default try to use /usr/bin/python. I am running in a virtualenv so I needed to override this. You probably don't need this in your environment.

'juniper_user=username' -- Username that Juniper will use in its NETCONF connection (specified in playbook).

'juniper_passwd=password' -- Password that Juniper will use in its NETCONF connection (specified in playbook).


Now, I am ready to start testing Ansible and Juniper. Here is a simple Ansible playbook that checks that the NETCONF port on my SRX is reachable.

[kbyers WORK]$ cat simple.yml 
---
- name: Verifying NETCONF
  hosts: pynet-sf-srx
  tasks: 
    - name: Verifying NETCONF
      wait_for: host={{ ansible_ssh_host }}  port=830      # variable defined in inventory file


[kbyers WORK]$ ansible-playbook simple.yml 

PLAY [Verifying NETCONF] ****************************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sf-srx]

TASK: [Verifying NETCONF] ***************************************************** 
ok: [pynet-sf-srx]

PLAY RECAP ******************************************************************** 
pynet-sf-srx               : ok=2    changed=0    unreachable=0    failed=0   

I can then expand on this and create a playbook that will gather facts from my SRX.

[kbyers WORK]$ cat facts.yml 
---
- name: Testing Juniper and Ansible
  hosts: pynet-sf-srx
  tasks: 
    - name: Verifying NETCONF
      wait_for: host={{ ansible_ssh_host }} port=830

    - name: Retrieve Juniper Facts
      junos_get_facts: host={{ ansible_ssh_host }} user={{ juniper_user }} passwd={{ juniper_passwd }}
      register: srx_info 

    - name: Print SRX information
      debug: msg="{{ srx_info.facts }}"


[kbyers WORK]$ ansible-playbook facts.yml 

PLAY [Testing Juniper and Ansible] ******************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sf-srx]

TASK: [Verifying NETCONF] ***************************************************** 
ok: [pynet-sf-srx]

TASK: [Retrieve Juniper Facts] ************************************************ 
ok: [pynet-sf-srx]

TASK: [Print SRX information] ************************************************* 
ok: [pynet-sf-srx] => {
    "msg": "{u'domain': None, u'serialnumber': u'BZ4614AF0938', u'ifd_style': u'CLASSIC', u'version_info': {u'major': [12, 1], u'type': u'X', u'build': 5, u'minor': [44, u'D', 35]}, u'RE0': {u'status': u'OK', u'last_reboot_reason': u'0x1:power cycle/failure', u'model': u'RE-SRX100H2', u'up_time': u'81 days, 4 hours, 6 minutes, 57 seconds'}, u'hostname': u'pynet-jnpr-srx1', u'fqdn': u'pynet-jnpr-srx1', u'has_2RE': False, u'switch_style': u'VLAN', u'version': u'12.1X44-D35.5', u'srx_cluster': False, u'HOME': u'/cf/var/home/pyclass', u'model': u'SRX100H2', u'personality': u'SRX_BRANCH'}"
} 

PLAY RECAP ******************************************************************** 
pynet-sf-srx               : ok=4    changed=0    unreachable=0    failed=0   

Now that I have verified my ability to community with the SRX, let's use Ansible to make a configuration change.

First, I define a configuration file that will add a static route:

[kbyers WORK]$ cat test_config.conf 

routing-options {
    static {
        route 1.1.1.0/24 next-hop 10.220.88.1;
    }
} 

Then I verify the current state of the SRX:

pyclass@pynet-jnpr-srx1# show routing-options 
static {
    route 0.0.0.0/0 next-hop 10.220.88.1;
}

[edit] 

Finally, I create an Ansible playbook. This playbook will load the test_config.conf file onto the SRX. Note, the playbook will automatically perform a 'commit' as part of this process.

[kbyers WORK]$ cat load_config.yml 
---
- name: Testing Juniper and Ansible
  hosts: pynet-sf-srx
  tasks: 
    - name: Verifying NETCONF
      wait_for: host={{ ansible_ssh_host }} port=830

    - name: Add a static route
      junos_install_config:
        host={{ ansible_ssh_host }}    # variable is from inventory file
        file=test_config.conf
        overwrite=false
        user={{ juniper_user }}        # variable is from inventory file
        passwd={{ juniper_passwd }}    # variable is from inventory file


[kbyers WORK]$ ansible-playbook load_config.yml 

PLAY [Testing Juniper and Ansible] ******************************************** 

GATHERING FACTS *************************************************************** 
ok: [pynet-sf-srx] 

TASK: [Verifying NETCONF] ***************************************************** 
ok: [pynet-sf-srx] 

TASK: [Add a static route] **************************************************** 
changed: [pynet-sf-srx] 

PLAY RECAP ******************************************************************** 
pynet-sf-srx                : ok=3    changed=1    unreachable=0    failed=0   

[kbyers WORK]$  

I then verify the change on the SRX:

pyclass@pynet-jnpr-srx1# show routing-options    
static {
    route 0.0.0.0/0 next-hop 10.220.88.1;
    route 1.1.1.0/24 next-hop 10.220.88.1;
}

[edit]
pyclass@pynet-jnpr-srx1#  

For additional reference see:

Using Ansible to Install Software on Devices Running Junos OS

junos_get_facts

junos_install_config


If you want to learn more about network automation, Python, and Ansible, join my email-list or follow me on Twitter: @kirkbyers. I also periodically run a free Python for Network Engineers email course which you can sign-up for here.

Kirk Byers
CCIE #6243 emeritus
Twitter: @kirkbyers