Download as pdf or txt
Download as pdf or txt
You are on page 1of 11

How to automate your network using

Ansible and Napalm Part 4


In the previous posts, we outlined the use of Ansible in Network Automation and
how to use NAPALM in multi-vendor networks for both Configuration deployment
and Validation. Next, we outlined how to use both Ansible and NAPALM to
perform Device provisioning and Validation for deploying new configuration related
to infrastructure network ( not service related). In this final post, we will outline how
to perform service provisioning and deployment using Ansible and NAPALM.
 
This post combines many of the techniques we outlined in the previous three posts
regarding the configuration deployment, and state validation in order to deploy and
validate a simple L3VPN service activation. The below diagram outline the high-level
approach that we are going to use in order to provision and validate the
deployment of any new L3VPN.

In this scenario, we are going to use only juniper vMX nodes in order to simulate the
whole topology and the below diagram outline the network topology.
In the next section, we are going to outline all the above-outlined steps in order to
achieve the service provisioning for the L3VPN service.
 

Service Data Model


The L3VPN service data model can be built using different approaches in order to
provision a L3VPN in a service provider environment. In this setup, we opted to use a
more standard approach using a service data model very similar to the
standard data model for L3VPN described in  RFC8299 (Using YANG Data Model for
L3VPN Service Delivery).
 
The RFC modeled the L3VPN service using the YANG Data model which can be
transformed into different representation language (YAML, XML or JSON). We will
use the YAML data format to represent this generic YANG model and render it in the
YAML format. This process is done manually, however, it can also be done
programmatically however for simplicity we will show the YAML rendered format.
Below is the Snipped of  the YAML rendered L3VPN service model
We can see from the above model that the L3VPN is described using two main
constructs
Generic VPN parameters
This section contains all the generic parameters that describe the VPN like the
vpn_name, Route-target and Route-distinguisher and the VPN type.
Sites for this VPN
This section has a list of all the sites under this VPN that need to be provisioned and
each item in this list has all the required parameters like
PE node which the site is attached to.
Interface on this PE that this site is connected to
VLAN used
IP prefix
Routing protocol used (BGP in this scenario)
From this Service model, we have all the needed parameters in order to provision
the L3VPN and we understand that we need to provision this service on two PE
routers (vMX2 and vMX3), however as we explanined in a previous post that it is very
hard to transform this generic service model into a per device configuration, thus we
need to transform this generic serivce model into a node service model that we can
easily combine with the JINJA2 template for the L3VPN (JunOS conifguration in this
case) in order to deliver the exact per node configuration for each device.
 
The below snippet outline the per-node data model generated from the above
service data model.

The below snippet outline the JINJA2 template for the L3VPN provisioning for JunOS.
Using the same techniques we outlined in the previous posts we can render the final
device configuration for both nodes to get the final per node configuration that will
be pushed by Ansible into the devices. The below snippet outlines the final rendered
configuration for vMX2 router for this L3VPN service.
Resource Validation
After we have generated the required configuration and before deploying this
configuration into the devices we should perform some sanity checks in order to
validate that this configuration when it will be pushed to the device will take effect
as well as it will not impact any other service (other L3VPN customers) running on
these nodes. Thus we need to perform some validation tests in order to verify that
the network is ready and that there is no impact if this configuration went active.
Below are some tests that we will perform in order to validate that the resources are
ready on the network as well as that there will be no impact on existing services.
More complicated checks can be performed like RT/RD overlapping or IP subnet
overlapping however these kind of tests require a backend DB to cross check
against, so the goal is just to illustrate the idea, however for a real world scenario
more advanced tests should be carried out.
The input to this pre-validation is the node data model that we generate in the
previous check, we connect to the devices that will be provisioned for this service
and retrieve some outputs from it (like show interface) and compare it to the node
service model to make sure that all the above check pass. We use an Ansible
playbook to trigger this pre-validation step before service deployment and it is very
similar to the validation script that we use in the previous post. The below snippet
outline the Playbook that we used to do the pre-validation using network state
gathered by NAPALM.
- name: Gather Network Resources
gather_facts: no
connection: local
hosts: all
tags: [ gather ]
vars_files:
- "./l3vpn-node.yml"
tasks:
- name: GET BGP and LLDP output
napalm_get_facts:
hostname: "{{ ansible_host }}"
username: "{{ ansible_user }}"
dev_os: "{{ dev_os }}"
password: "{{ ansible_ssh_pass }}"
optional_args:
port: "{{ ansible_port }}"
filter:
- interfaces_ip
- interfaces
when: inventory_hostname in nodes
- set_fact: node={{nodes[inventory_hostname]}}
when: inventory_hostname in nodes
- set_fact: node_interfaces={{node|map(attribute='links')|list}}
when: inventory_hostname in nodes
- set_fact: node_vlans=''
when: inventory_hostname in nodes
- set_fact: node_vlans="{{ node_vlans|list + item.keys()}}"
with_list: "{{node_interfaces}}"
when: inventory_hostname in nodes
- name: Validate Interfaces are available on the router
assert:
that: item.split('.')[0] in hostvars[inventory_hostname].napalm_interfaces.keys()
msg: |
Interface {{item.split('.')[0]}} Is not available on {{inventory_hostname}}
with_list: "{{node_vlans}}"
when: inventory_hostname in nodes
ignore_errors: true
- name: Validate all VPN Interface is operational
assert:
that: hostvars[inventory_hostname].napalm_interfaces[item.split('.')[0]].is_up == true
msg: |
Interface {{item.split('.')[0]}} Is DOWN on {{inventory_hostname}}
with_list: "{{node_vlans}}"
when: inventory_hostname in nodes
ignore_errors: true
- name: Validate VLANs are not already used on the interfaces
assert:
that: item not in hostvars[inventory_hostname].napalm_interfaces_ip.keys()
msg: |
Interface {{item}} Is already used in {{inventory_hostname}}
with_list: "{{node_vlans}}"
when: inventory_hostname in nodes
ignore_errors: true

Service Deployment
Once we made sure that this new service model is valid and all the resources are
available to provision the service we move forward to deploy the configuration on
the devices. We use another Ansible playbook very similar to the one we used in the
2nd post in order to push the devices using Ansible and NAPALM to each device in
the service model. The below snippet outline the playbook that we used to push the
configuration for this customer L3VPN provisioning task.
---
- hosts: localhost
name: Create node data model
gather_facts: no
tags: [ model ]
vars_files:
- "{{model|default('l3vpn-model.yml')}}"
tasks:
- name: Create per-node data model from fabric data model
template: src=l3vpn-service.j2 dest=./l3vpn-node.yml
- name: Generate Configuration for all routers
gather_facts: no
connection: local
hosts: all
tags: [ template ]
tasks:
- include_vars: "./l3vpn-node.yml"
- file: path=l3vpn-{{common.vrf_name}}-config state=directory
run_once: true
- name: Generate Configuration
template: src=conf-template/jnpr-l3vpn.j2 dest=l3vpn-{{common.vrf_name}}-
config/{{inventory_hostname}}-l3vpn.txt
when: "inventory_hostname in nodes.keys()"
## Third Play
# commit variable is used to control the play
# if commit=0 then we will not commit the changes we will only generate diff
# if commit=1 then we will commit the changes and generate diff
- name: push the configuration to the devices
gather_facts: no
connection: local
hosts: all
tags: [ deploy ]
tasks:
- include_vars: "./l3vpn-node.yml"
- file: path=l3vpn-conf-diff state=directory
run_once: true
- name: load the configuration to the devices
napalm_install_config:
hostname: "{{ ansible_host }}"
username: "{{ ansible_user }}"
dev_os: "{{dev_os}}"
password: "{{ ansible_ssh_pass }}"
optional_args:
port: "{{ ansible_port }}"
config_file: l3vpn-{{common.vrf_name}}-config/{{inventory_hostname}}-l3vpn.txt
commit_changes: "{{commit}}"
diff_file: l3vpn-conf-diff/{{inventory_hostname}}-diff.txt
when: "inventory_hostname in nodes.keys()"

Service Validation
Once the configuration is pushed into the devices we perform another validation
step in order to verify that the configuration was pushed correctly and that the VPN
service is working as expected, again any custom checks can be applied however we
will use the following tests just for illustration.
 

We again take the node data model and compare it against the state of the network
after the configuration is pushed (retrieve show commands from the provisioned
devices) and validate that all the above test cases have passed. The playbook is very
similar to the one used in the pre validation step and it was illustrated in the 3rd
post.
 

Conclusion
In this post, we outlined how to use Ansible as an orchestration system for service
deployment and how it can be used to build the data model for the service and how
it can be used to deploy and validate the configuration for a L3VPN. This simple
model can be extended to provide orchestration for other services and the
validation can be as well extended to include other tests for service pre and post
validation.Further, the overall process can be further automated and extended by
providing a front-end to Ansible where the service parameters are entered there
(Like vpn_name, RD/RT,etc..) and then these values are pushed into the service
model, also a backend DB can be used to store this information so as it can be used
for validation. All these enhancements can be used to deliver a carrier-grade service
provisioning solution with Ansible in its core as outlined in this post.
 

You might also like