Documente Academic
Documente Profesional
Documente Cultură
Lab Guide
Text Part Number: NSO30020
Americas Headquarters Asia Pacific Headquarters Europe Headquarters
Cisco Systems, Inc. Cisco Systems (USA) Pte. Ltd. Cisco Systems International BV
San Jose, CA Singapore Amsterdam,
The Netherlands
Cisco has more than 200 offices worldwide. Addresses, phone numbers, and fax numbers are listed on the Cisco Website
at www.cisco.com/go/offices.
© 2017 Cisco and/or its affiliates. All rights reserved. Cisco and the Cisco logo are trademarks or registered trademarks of
Cisco and/or its affiliates in the U.S. and other countries. Cisco and the Cisco logo are trademarks or registered trademarks
of Cisco and/or its affiliates in the U.S. and other countries. To view a list of Cisco trademarks, go to this URL:
www.cisco.com/go/trademarks. Third party company names, trademarks, and logos referenced in these materials are the
property of their respective owners and their use does not constitute or imply an endorsement, sponsorship, affiliation,
association or approval by the third parties of these materials or with Cisco. The use of the word partner does not imply a
partnership relationship between Cisco and any other company.
DISCLAIMER WARRANTY: THIS CONTENT IS BEING PROVIDED "AS IS" AND AS SUCH MAY INCLUDE
TYPOGRAPHICAL, GRAPHICS, OR FORMATTING ERRORS. CISCO MAKES AND YOU RECEIVE NO WARRANTIES IN
CONNECTION WITH THE CONTENT PROVIDED HEREUNDER, EXPRESS, IMPLIED, STATUTORY OR IN ANY OTHER
PROVISION OF THIS CONTENT OR COMMUNICATION BETWEEN CISCO AND YOU. CISCO SPECIFICALLY
DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT
AND FITNESS FOR A PARTICULAR PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE
PRACTICE. This learning product may contain early release content, and while Cisco believes it to be accurate, it falls
subject to the disclaimer above.
Contents
Overview for All Labs......................................................................................1–1
Command Syntax Reference .................................................................................... 1–3
Answer Key..........................................................................................................1
Lab 1 .............................................................................................................................. 1
Overview
This guide presents the instructions and other information concerning the lab activities
for this course. The activities follow the topics presented in lessons for which you were
provided with the necessary information in order to be able to complete these lab
exercises. You can find the solutions to these lab exercises in the lab activity Answer Key.
Outline
This guide includes these activities and the corresponding lessons:
Lab 1-1: Layer 3 MPLS VPN Service
Module 1, Lesson 1: Service Applications
Lab 2-1: Layer 2 VPN Service Enhancements
Module 1, Lesson 1: Service Applications
Lab 3-1: Creating a Simple Cloud Service Bundle
Module 2, Lesson 3: Service Chaining Design
Required Resources
The following resources and equipment are required for completing the activities in this
lab guide:
Cisco CCO account to access dCloud
PC or laptop with a web browser (Internet Explorer or Mozilla Firefox),
Cisco AnyConnect software, and terminal application (Telnet, PuTTY, and
so on) installed
Access to the Internet
Job Aids
The following job aids are available to help you complete the lab activities:
NSO300_4-3 Network Services Orchestrator (NSO) Advanced Design
Student Guide
The workstation in the lab contains three documents on the desktop:
Cisco NSO Getting Started Guide
Cisco NSO Installation Guide
Cisco NSO User Guide
brackets ([ ]) Indicates optional element. You can choose one of the options.
Example:
(config-if)# frame-relay lmi-type
{ansi|cisco|q933a}
italics font Arguments for which you supply values are in italics.
Example: Open file ip tcp window-size bytes
angle brackets (<>) In contexts that do not allow italics, arguments for which you
supply values are enclosed in angle brackets [<>]. Do not type the
brackets when entering the command.
Example: If the command syntax is ping <ip_address>, you
enter ping 192.32.10.12
vertical line (|) Indicates that you enter one of the choices. The vertical line
separates choices. Do not type the vertical line when entering the
command.
Example: If the command syntax is show ip route|arp, you
enter either show ip route or show ip arp, but not both.
Description
Complete this lab exercise to practice what you learned in the the “Service Applications”
lesson of the “Service Provider VPN Service Design with Cisco NSO” module of this
course.
Activity Objective
You have inherited an implementation of a Layer 3 MPLS VPN service from the service
architect. Unfortunately, the service architect did not implement any validations within
the YANG model and did not include Cisco IOS XR support. Because you are responsible
for the implementation of the service in production, you must make sure that the
appropriate validation is added and that additional device types are supported.
In this activity, you will learn how to use YANG and Python to improve and optimize an
existing Layer 3 MPLS VPN service model. After completing this activity, you will be
able to meet these objectives:
Use advanced YANG modeling techniques
Use Python with MAAGIC API to implement advanced service models
Visual Objective
Activity Procedure
Complete these steps:
Step 1 Connect to the lab, using one of the available mechanisms.
Step 2 Use a web browser to connect to https://dcloud.cisco.com, and
log in using your CCO account. When asked, choose the Americas
region.
Step 3 Click the My Dashboard link at the top-right corner of the page to
access the lab that has been shared with you.
Step 4 Click the Schedule demo link, and choose the time range for the lab.
Make sure that the lab is scheduled until at least 18:00 (6 P.M.).
Step 5 After the lab starts, click the View Demo link to connect to the lab.
Step 6 Click the Connect Laptop via AnyConnect link and follow the steps
to complete the establishment of a secure session to the lab. Navigate to
Session Details to get the AnyConnect link and credentials.
___________________ Note ________________________________
You must have the Cisco AnyConnect client installed on your PC.
______________________________________________________________
Step 7 After the session is established, you can start connecting to lab devices:
Workstation: Click the Workstation icon in the Topology view.
Then click the Remote Desktop link to open a new browser
window with an RDP session to the Windows desktop. Log in with
the username Administrator and the password C1sco12345.
Alternatively, you can use an arbitrary RDP client, such as
mstsc.exe, to connect to the workstation by using the IP address
198.18.133.253.
Step 9 Log in to the shell of the NSO system with the username cisco and
the password cisco. Start Cisco NSO if it has not been started
already.
cisco@ncs:~$ source nso-4.2/ncsrc
cisco@ncs:~$ cd ncs-run
cisco@ncs:~/ncs-run$ ncs
Step 10 Start the netsim lab devices if the simulated environment has not
been started already.
cisco@ncs:~$ cd /opt/lab
cisco@ncs:/opt/lab$ ncs-netsim start
DEVICE PE11 OK STARTED
DEVICE PE12 OK STARTED
DEVICE PE31 OK STARTED
DEVICE CE11 OK STARTED
DEVICE CE12 OK STARTED
DEVICE CE21 OK STARTED
DEVICE CE31 OK STARTED
DEVICE PE21 OK STARTED
cisco@ncs:~$
Step 11 Log in to the Cisco NSO CLI, using a new SSH session from the
workstation by connecting to TCP port 2024. Alternatively, you can use
the ncs_cli command to log in to the CLI from the Linux shell. Use
the username admin and the password admin to log in to the Cisco
NSO CLI.
Activity Verification
You have completed this task when you attain these results:
You have successfully logged in to the Cisco NSO CLI.
Activity Procedure
Complete these steps:
Step 1 Edit the file package-meta-data.xml in the l3mplsvpn package
folder in your favorite text editor. Uncomment the second component
with the name l3mplsvpn-python.
<ncs-package xmlns="http://tail-f.com/ns/ncs-packages">
<name>l3mplsvpn</name>
<package-version>1.0</package-version>
<description>Generated Python package</description>
<ncs-min-version>4.2</ncs-min-version>
<component>
<name>l3mplsvpn-python</name>
<application>
<python-class-
name>com.example.l3mplsvpn.l3mplsvpnRFS</python-class-name>
</application>
</component>
</ncs-package>
Step 3 In the Cisco NSO CLI, examine the operational data for the l3mplsvpn
package with the show packages package l3mplsvpn command.
Verify that the Python component for the l3mplsvpn package is
successfully loaded.
admin@ncs# show packages package l3mplsvpn
packages package l3mplsvpn
package-version 1.0
description "Generated Python package"
ncs-min-version [ 4.2 ]
directory ./state/packages-in-use/1/l3mplsvpn
templates [ l3mplsvpn-iosxr-template l3mplsvpn-ios-
template ]
component l3mplsvpn-python
application python-class-name l3mplsvpn.L3MplsVpn
application start-phase phase2
oper-status up
Activity Verification
You have completed this task when you attain these results:
The Python component for the l3mplsvpn package is successfully loaded.
Activity Procedure
Complete these steps:
Step 1 Log in to the shell of the Cisco NSO system, using the username cisco
and the password cisco. View the directories and files in the ncs-
run/packages directory.
cisco@ncs:~$ cd ncs-run/packages
cisco@ncs:~/ncs-run/packages$ ls -l
Step 2 Review the current service model for the l3mplsvpn service package
located in the ~/ncs-run/packages/l3mplsvpn/src/yang
directory.
module l3mplsvpn {
namespace "http://com/example/l3mplsvpn";
prefix l3mplsvpn;
augment /ncs:services {
leaf l3mplsvpn-id-cnt {
description "Provides a unique 32-bit number used
as VPN instance identifier";
type uint32;
default 1;
}
}
augment /ncs:services {
list l3mplsvpn {
tailf:info "Layer-3 MPLS VPN Service";
key vpn-name;
uses ncs:service-data;
© 2017 Cisco Systems, Inc. NSO30020 Lab Guide 1–12
This document is for training purposes only. All content is subject to change without notice.
Cisco Learning Services (www.cisco.com/go/cls) Layer 3 MPLS VPN Service
ncs:servicepoint "l3mplsvpn-servicepoint";
leaf vpn-name {
tailf:info "Service Instance Name";
type string;
}
leaf vpn-id {
tailf:info "Service Instance ID (1 to 65535)";
type uint32;
}
leaf link-id-cnt {
description "Provides a unique 32-bit number
used as site identifier";
default 1;
type uint64;
}
leaf customer {
tailf:info "VPN Customer";
type leafref {
path "/ncs:customers/ncs:customer/ncs:id";
}
}
leaf link-name {
tailf:info "Link Name";
type string;
}
leaf link-id {
tailf:info "Link ID (1 to 65535)";
type uint32;
}
leaf device {
tailf:info "PE Router";
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
leaf pe-ip {
leaf ce-ip {
tailf:info "CE Neighbor IP Address";
type string;
}
leaf interface {
tailf:info "Customer Facing Interface";
type string;
}
leaf routing-protocol {
tailf:info "Routing option for the PE-CE
link";
type enumeration {
enum bgp;
enum rip;
}
}
}
}
}
}
The following table describes the meaning of some of the leafs in the model:
Leaf Description
vpn-name A descriptive name of the VPN instance with no other
functionality.
Leaf Description
link-id An identifier of each link. Currently not used for any
functional purpose.
Step 4 Use the following table to note the problems that you have identified
with the current design and implementation:
Issue Description
Step 5 To help identify some of the issues with the design and
implementation, try to add a third link, using the following CLI
commands:
!
exit
services l3mplsvpn vpn2
vpn-id 1
link site1
link-id 1
device CE11
interface 0/5
exit
!
exit
The model allows you to enter a new link with the same link ID that
another link uses. The same applies to the VPN identifier. The model
allows you to do the following:
Assign the same interface to multiple service links
Enter the same IP addresses to multiple interfaces
Not specify the routing option which may result in no connectivity
between customer site
The IP addresses can be omitted, which will not result in a successful
device configuration. IP addresses can be any strings that will not result in
a successful device configuration. You can choose CE devices when
configuring PE functionality.
______________________ Note ________________________________
The lack of a proper data model and the lack of controls in the Python
code can result in Python exceptions or possible misconfiguration of
devices.
_________________________________________________________________
Activity Verification
You have completed this task when you attain these results:
You have successfully identified possible limitations in the existing Layer 3
MPLS VPN service model.
You have successfully identified functional deficiencies of the existing Layer 3
MPLS VPN service implementation.
Activity Procedure
Complete these steps:
Step 1 Redesign the YANG service model, based on your own list of identified
issues and based on the requirements listed in the following table.
Requirement Suggestions
leaf vpn-id {
type uint32 {
range "1..65535" {
error-message "VPN ID is out of range. Should be between 1 and
65535.";
}
}
}
...
list link {
tailf:info "PE-CE Attachment Point";
key link-name;
unique "link-id";
...
leaf link-id {
type uint32 {
range "1..65535" {
error-message "Link ID is out of range. Should be between 1
and 65535.";
}
}
}
Requirement Suggestions
Example:
The following printout illustrates how to inspect the current data
structure in XPath format, including the prefix.
admin@ncs# show running-config services l3mplsvpn | display xpath |
display prefix
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-name='vpn1']/l3mplsvpn:vpn-id 33
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-name='vpn1']/l3mplsvpn:customer ACME
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site1']/l3mplsvpn:link-id 1
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site1']/l3mplsvpn:device PE11
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site1']/l3mplsvpn:interface 0/8
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site1']/l3mplsvpn:pe-ip 172.31.1.1
Requirement Suggestions
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site1']/l3mplsvpn:ce-ip 172.31.1.2
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site1']/l3mplsvpn:routing-protocol
bgp
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site2']/l3mplsvpn:link-id 1
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site2']/l3mplsvpn:device PE21
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site2']/l3mplsvpn:interface 0/0/0/8
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site2']/l3mplsvpn:pe-ip 172.31.2.1
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site2']/l3mplsvpn:ce-ip 172.31.2.2
/ncs:services/l3mplsvpn:l3mplsvpn[l3mplsvpn:vpn-
name='vpn1']/l3mplsvpn:link[l3mplsvpn:link-name='site2']/l3mplsvpn:routing-protocol
bgp
leaf interface {
tailf:info "Customer Facing Interface";
mandatory true;
type string;
must "count(../../../l3mplsvpn[vpn-name != current()/../../vpn-
name]" +
"/link[device = current()/../device][interface = current()]) =
0" {
error-message "Interface is already used for another link.";
}
}
...
Requirement Suggestions
leaf pe-ip {
tailf:info "PE Interface IP Address";
mandatory true;
type inet:ipv4-address {
pattern "172\.([1][6-9]|[2][0-9]|3[0-1])\..*" {
error-message "Invalid IP address. IP address should be in the
172.16.0.0/12 range.";
}
}
}
Requirement Suggestions
leaf device {
tailf:info "PE Router";
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
must "starts-with(current(),'PE')" {
error-message "Only PE devices can be selected.";
}
}
Requirement Suggestions
list link {
tailf:info "PE-CE Attachment Point";
key link-name;
unique "link-id";
unique "device interface";
min-elements 1;
...
leaf ce-ip {
tailf:info "CE Interface IP Address";
when "../routing-protocol='bgp'";
mandatory true;
...
}
leaf rip-net {
tailf:info "IP network for RIP";
when "../routing-protocol='rip'";
mandatory true;
...
Requirement Suggestions
leaf routing-protocol {
tailf:info "Routing option for the PE-CE link";
type enumeration {
enum bgp;
enum rip;
}
default bgp;
}
leaf link-id-cnt {
description "Provides a unique 32-bit number used as site
identifier";
tailf:hidden "Counter";
default 1;
type uint64 { range "1..65535"; }
}
BUILD SUCCESSFUL
Total time: 0 seconds
cisco@NCS:~/ncs-run/packages/l3mplsvpn/src$
admin@ncs# conf
Entering configuration mode terminal
admin@ncs(config)# services l3mplsvpn vpn121
admin@ncs(config-l3mplsvpn-vpn121)# customer ACME
admin@ncs(config-l3mplsvpn-vpn121)# link site1
admin@ncs(config-link-site1)# device PE11
admin@ncs(config-link-site1)# interface 0/8
admin@ncs(config-link-site1)# routing-protocol bgp
admin@ncs(config-link-site1)# top
admin@ncs(config)# validate
Aborted: 'services l3mplsvpn vpn121 link site1 interface'
(value "0/8"): Interface is already used for another link.
admin@ncs(config)#
admin@ncs# conf
Entering configuration mode terminal
admin@ncs(config)# services l3mplsvpn test
admin@ncs(config-l3mplsvpn-test)# ?
Possible completions:
check-sync Check if device config is according to
the service
commit-queue
customer VPN Customer
deep-check-sync Check if device config is according to
the service
get-modifications Get the configuration data this
service created
link PE-CE Attachment Point
re-deploy Run/Dryrun the service logic again
un-deploy Undo the effects of this service to
the network
---
…
commandadmin@ncs(config-l3mplsvpn-test)# link site1
admin@ncs(config-link-site1)# ?
Possible completions:
device PE Router
interface Customer Facing Interface
pe-ip PE Interface IP Address
routing-protocol Routing option for the PE-CE link
---
…
admin@ncs(config-link-site1)# device ?
Possible completions:
PE11 PE12 PE21 PE31
admin@ncs(config-link-site1)# device
Activity Verification
You have completed this task when you attain these results:
You have successfully analyzed an existing deployment of a Layer 3 MPLS
VPN service and have designed a more reliable solution.
You have successfully implemented improvements based on the identified
limitations of the original service model.
Activity Procedure
Complete these steps:
Step 1 Use the existing service framework to add the required automation
techniques. Use your preferred editor to edit the Python code:
vim or nano on a Linux server (NSO)
PyCharm from the workstation
Notepad++ from the workstation (has YANG and Python syntax
highlighting). Note that there is a share available on the
workstation to access the files on a Linux server (NSO).
Notepad from the workstation
Open Python file ~/ncs- run/packages/
l3mplsvpn/python/l3mplsvpn_callbacks.py.
Step 2 Note that the Python service module contains a class with the name
ServiceCallbacks. The implemented service callbacks for this
service are as follows:
Pre-modification: Used to handle automatic assignment of VPN
ID and Link ID
Create: Used to configure devices based on service model and
configuration data
# -*- mode: python; python-indent: 4 -*-
import _ncs
from ncs.application import Service
import ncs.template
TYPE_CISCO_IOS = '{tailf-ned-cisco-ios}'
TYPE_CISCO_IOSXR = '{tailf-ned-cisco-ios-xr}'
# ------------------------
# SERVICE CALLBACK EXAMPLE
# ------------------------
class ServiceCallbacks(Service):
@Service.pre_modification
def cb_pre_modification(self, tctx, op, kp, root,
proplist):
self.log.info('Service premod(service=', kp, ')')
tvars = ncs.template.Variables()
template = ncs.template.Template(service)
tvars.add('VPNID', vpn_id)
leaf pe-ip {
mandatory false;
}
leaf ce-ip {
mandatory false;
}
leaf rip-net {
mandatory false;
}
BUILD SUCCESSFUL
Total time: 0 seconds
pylint --disable=R,C --reports=n ../python/__init__.py ||
(test $? -ge 4)
No config file found, using default configuration
************* Module python
W: 1, 0: Relative import 'l3mplsvpn', should be
'python.l3mplsvpn' (relative-import)
pylint --disable=R,C --reports=n
../python/l3mplsvpn_callbacks.py || (test $? -ge 4)
No config file found, using default configuration
************* Module python.l3mplsvpn_callbacks
W: 18,49: Access to a protected member _path of a client
class (protected-access)
pylint --disable=R,C --reports=n ../python/l3mplsvpn.py ||
(test $? -ge 4)
No config file found, using default configuration
Activity Verification
You have completed this task when you attain these results:
You have successfully analyzed an existing deployment of a Layer 3 MPLS
VPN service and designed an optimized solution.
Activity Procedure
Complete these steps:
Step 1 Augment the existing functionality to add another device type. Create a
test service instance named vpn2 by using PE21 as one of the devices.
services l3mplsvpn vpn2
customer ACME
link site1
device PE12
interface 0/7
routing-protocol bgp
exit
link site2
device PE21
interface 0/0/0/8
routing-protocol bgp
top
<vrf xmlns="http://tail-f.com/ned/cisco-ios-xr">
<vrf-list>
<name>vpn10001</name>
<address-family>
<ipv4>
<unicast>
<import>
<route-target>
<address-list>
<name>1:10001</name>
</address-list>
</route-target>
</import>
<export>
<route-target>
<address-list>
<name>1:10001</name>
</address-list>
</route-target>
</export>
</unicast>
</ipv4>
</address-family>
</vrf-list>
</vrf>
<router xmlns="http://tail-f.com/ned/cisco-ios-xr">
<bgp>
<bgp-no-instance>
<id>1</id>
<vrf>
<name>vpn10001</name>
<neighbor>
<id>172.31.1.6</id>
<remote-as>65001</remote-as>
<address-family>
<ipv4>
<unicast>
<route-policy>
<direction>in</direction>
<name>pass</name>
</route-policy>
<route-policy>
<direction>out</direction>
<name>pass</name>
</route-policy>
<as-override/>
<default-originate/>
</unicast>
</ipv4>
</address-family>
</neighbor>
<address-family>
<ipv4>
<unicast>
<redistribute>
<connected/>
<static/>
</redistribute>
</unicast>
</ipv4>
</address-family>
<rd>1:10001</rd>
</vrf>
</bgp-no-instance>
</bgp>
<static>
<address-family>
<ipv4>
<unicast>
<routes>
<net>192.168.21.0/24</net>
<interface>GigabitEthernet0/0/0/1</interface>
<address>172.31.1.6</address>
</routes>
</unicast>
</ipv4>
</address-family>
</static>
</router>
<interface xmlns="http://tail-f.com/ned/cisco-ios-xr">
<GigabitEthernet>
<id>0/0/0/1</id>
<ipv4>
<address>
<mask>255.255.255.252</mask>
<ip>172.31.1.5</ip>
</address>
</ipv4>
<vrf>vpn10001</vrf>
</GigabitEthernet>
</interface>
<route-policy xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<name>pass</name>
<cmd>
<value>pass</value>
</cmd>
</route-policy>
<config-template xmlns="http://tail-f.com/ns/config/1.0"
servicepoint="l3mplsvpn">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{$DEVICE}</name>
<config>
<vrf xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<vrf-list>
<name>vpn{$VPNID}</name>
<address-family>
<ipv4>
<unicast>
<import>
<route-target>
<address-list>
<name>1:{$VPNID}</name>
</address-list>
</route-target>
</import>
<export>
<route-target>
<address-list>
<name>1:{$VPNID}</name>
</address-list>
</route-target>
</export>
</unicast>
</ipv4>
</address-family>
</vrf-list>
</vrf>
<router xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<bgp>
<bgp-no-instance>
<id>1</id>
<vrf>
<name>vpn{$VPNID}</name>
<neighbor>
<id>{$CEIP}</id>
<remote-as>65001</remote-as>
<address-family>
<ipv4>
<unicast>
<route-policy>
<direction>in</direction>
<name>pass</name>
</route-policy>
<route-policy>
<direction>out</direction>
<name>pass</name>
</route-policy>
<as-override/>
<default-originate/>
</unicast>
</ipv4>
</address-family>
</neighbor>
<address-family>
<ipv4>
<unicast>
<redistribute>
<connected/>
<static/>
</redistribute>
</unicast>
</ipv4>
</address-family>
<rd>1:{$VPNID}</rd>
</vrf>
</bgp-no-instance>
</bgp>
</router>
<interface xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<GigabitEthernet>
<id>{$INTERFACE}</id>
<ipv4>
<address>
<mask>255.255.255.252</mask>
<ip>{$PEIP}</ip>
</address>
</ipv4>
<vrf>vpn{$VPNID}</vrf>
</GigabitEthernet>
</interface>
<route-policy xmlns="http://tail-f.com/ned/cisco-ios-
xr" tags="merge">
<name>pass</name>
<cmd>
<value>pass</value>
</cmd>
</route-policy>
</config>
</device>
</devices>
</config-template>
BUILD SUCCESSFUL
Total time: 0 seconds
Step 10 Redeploy the existing service vpn2 instance that makes use of router
PE21. You should now see that Cisco NSO has detected that the service
requires configuration also on device PE21, which was initially not
configured because this device type was not supported.
+ ip 172.31.2.1;
+ mask 255.255.255.252;
}
}
+ vrf vpn2;
}
}
cisco-ios-xr:router {
bgp {
bgp-no-instance 1 {
+ vrf vpn2 {
+ rd 1:2;
+ address-family {
+ ipv4 {
+ unicast {
+ redistribute
{
+ connected
{
+ }
+ static {
+ }
+ }
+ }
+ }
+ }
+ neighbor 172.31.0.10 {
+ remote-as 65001;
+ address-family {
+ ipv4 {
+ unicast {
+ route-
policy in {
+ pass;
+ }
+ route-
policy out {
+ pass;
+ }
+ as-
override {
+ }
+ default-
originate {
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}
cisco-ios-xr:vrf {
+ vrf-list vpn2 {
+ address-family {
+ ipv4 {
+ unicast {
+ import {
+ route-target {
+ address-list
1:2;
+ }
+ }
+ export {
+ route-target {
+ address-list
1:2;
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}
}
admin@ncs#
admin@ncs# services l3mplsvpn vpn2 check-sync
in-sync true
admin@ncs#
Activity Verification
You have completed this task when you attain these results:
You have successfully analyzed the existing deployment of a Layer 3 MPLS
VPN service and have added Layer 3 MPLS VPN support for Cisco IOS XR
devices.
Description
Complete this lab exercise to practice what you learned in the “Service Applications”
lesson of the “Service Provider VPN Service Design with Cisco NSO” module of this
course.
Activity Objective
In this activity, you will learn how to use Python to implement the mapping logic, which
maps service attributes to device parameters. After completing this activity, you will be
able to meet these objectives:
Use Maagic API
Use Python to implement mappings
Visual Objective
Activity Procedure
Complete these steps:
Step 1 Log in to the shell of the Cisco NSO system with the username cisco
and the password cisco, and start Cisco NSO if it is not running
already.
Step 2 Start the netsim lab devices if the simulated devices have not been
started.
cisco@ncs:~$ cd /opt/lab
cisco@ncs:/opt/lab$ ncs-netsim start
DEVICE PE11 OK STARTED
DEVICE PE12 OK STARTED
DEVICE PE31 OK STARTED
DEVICE CE11 OK STARTED
DEVICE CE12 OK STARTED
DEVICE CE21 OK STARTED
DEVICE CE31 OK STARTED
DEVICE PE21 OK STARTED
cisco@ncs:~$
Activity Verification
You have completed this task when you attain these results:
Cisco NSO and netsim are running in the lab environment.
Activity Procedure
Complete these steps:
Step 1 Use the SSH connection to the Cisco NSO server from the previous
task.
Step 2 Navigate to the Cisco NSO running directory.
Step 4 For this service, reuse the l2vpn YANG model, which can be found in
the /opt/resources/lab2/ folder. Copy the YANG model to the
$HOME/ncs-run/packages/l2vpn/src/yang folder and replace the
one that was created as a part of the service skeleton. Alternatively,
you can also open both files in your favorite text editor and replace the
contents of the service skeleton YANG model with the one provided in
the /opt/resources folder.
Step 5 Make sure that you add the following highlighted lines to the Makefile
located in the src folder of the l2vpn package. Because your package
has dependencies on other YANG models, you need to specify where
those can be found. Otherwise, your package will fail at the compile
stage.
…
SRC = $(wildcard yang/*.yang)
FXS = $(SRC:yang/%.yang=../load-dir/%.fxs)
fxs: $(FXS)
…
Step 6 Optionally, add some instruction to the Makefile that will allow you to
check the syntax and style of your Python code. You can do this by
running the pylint tool on all Python source files. The tool has already
been installed in the lab environment but is not bundled by default
with Cisco NSO installation.
all: fxs pylint
PYLINT = pylint
PYLINTFLAGS = --disable=R,C --reports=n
PYDIR = ../python
PYTHONFILES = $(wildcard $(PYDIR)/*.py)
%.pylint:
$(PYLINT) $(PYLINTFLAGS) $*.py || (test $$? -ge 4)
fxs: $(FXS)
Step 7 Compile the l2vpn package. This will ensure that the YANG model
and any included code are valid.
cisco@NCS:~/ncs-run/packages/l2vpn/src$ make
/home/cisco/nso-4.2/bin/ncsc `ls l2vpn-ann.yang >
/dev/null 2>&1 && echo "-a l2vpn-ann.yang"` \
cisco@NCS:~/ncs-run/packages/l2vpn/src$
Step 8 Verify that you have a directory structure and Python files for the
L2VPN service.
cisco@nso:$~/ncs-run/# cd packages/l2vpn/
cisco@nso:$~/ncs-run/packages/l2vpn# dir
load-dir package-meta-data.xml python src
cisco@nso:$~/ncs-run/packages/l2vpn# cd src/
root@ubuntu:/home/ncs/packages/l2vpn/src# dir
Makefile yang
Step 9 Verify that your YANG model structure mirrors the one below.
Activity Verification
You have completed this task when you attain these results:
You have successfully created a Python-based service skeleton for an
L2VPN service.
Activity Procedure
Complete these steps:
Step 1 To help with service development, you will find PyCharm IDE on the
workstation desktop. You can access files on the NCS server from
within the IDE, which can help you with writing your code and can be
used for syntax highlighting and debugging. Open the PyCharm IDE or
your favorite Python editor. The current Cisco NSO project (in ~/ncs-
run) is already imported into PyCharm.
Step 3 In the code, find the cb_create() method where you must put your
mapping code. Your coding entry point is marked below.
# -*- mode: python; python-indent: 4 -*-
import ncs
from ncs.application import Service
# import ncs.template
# ------------------------
# SERVICE CALLBACK EXAMPLE
# ------------------------
class ServiceCallbacks(Service):
# DO STUFF HERE.....
# Example:
# vars = ncs.template.Variables()
# vars.add('MAGIC', "42")
# template = ncs.template.Template(service)
# template.apply('foo-template1', vars)
# ---------------------------------------------
# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
# ---------------------------------------------
# omitted
Parameter Description
_________________________________________________________________
Step 5 Use the Cisco NSO CLI to obtain the configuration in the XML format,
to be used in the config template. Use the following configuration for
L2VPN on Cisco IOS and IOS XR devices.
! Cisco IOS
interface GigabitEthernet0/9
description Link to CE11
xconnect 10.0.0.21 1001121 encapsulation mpls
! Cisco IOS XR
l2vpn
xconnect group ACME
p2p CE11-to-CE21
interface GigabitEthernet0/0/0/9 neighbor 10.0.0.11 pw-id
1001121
!
interface GigabitEthernet0/0/0/9
description Link to CE21
l2transport
Step 6 Use the commit dry-run outformat xml CLI command to obtain
the configuration in XML format, and place the XML for both device
types into a single l2vpn-template.xml file, in the templates
directory (the directory must be created manually) of your l2vpn
service package.
Step 7 Parameterize the XML template. Identify the four dummy values used
in the sample configuration, and replace them with XPath variables.
Variable Description
$PW-ID Pseudowire ID
<config-template xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{$DEVICE}</name>
<config>
<interface xmlns="urn:ios">
<GigabitEthernet>
<name>{$INTERFACE-ID}</name>
<xconnect tags="merge">
<encapsulation>mpls</encapsulation>
<vcid>{$PW-ID}</vcid>
<address>{$REMOTE-IP}</address>
</xconnect>
</GigabitEthernet>
</interface>
<l2vpn xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<xconnect>
<group>
<name>{$CUSTOMER}</name>
<p2p>
<name>{$SERVICE}</name>
<neighbor>
<address>{$REMOTE-IP}</address>
<pw-id>{$PW-ID}</pw-id>
</neighbor>
<interface>
<name>GigabitEthernet{$INTERFACE-ID}</name>
</interface>
</p2p>
</group>
</xconnect>
</l2vpn>
<interface xmlns="http://tail-f.com/ned/cisco-ios-xr">
<GigabitEthernet>
<id>{$INTERFACE-ID}</id>
<l2transport tags="merge">
</l2transport>
</GigabitEthernet>
</interface>
</config>
</device>
</devices>
</config-template>
vpn_name = service.name
customer = service.customer
pw_id = service.pw_id
Step 10 Import the helper module into the l2vpn.py module by adding an
import statement at the beginning of the file. Uncomment the
ncs.template import.
# -*- mode: python; python-indent: 4 -*-
import ncs
from ncs.application import Service
import ncs.template
import device_helper
Step 11 Loop through all entries in the link list. For each entry, determine the
type of the device, and read the IP address of the loopback interface
directly from device configuration, using the device-specific device
model. Store this data in an auxiliary list named links_data, to be
used later in providing the service instance configuration. An example
of the final data structure is displayed below. The list contains
dictionaries with the fields device, intf-number, loopback-
address.
In [5]: pprint.pprint(links_data)
[{'device': 'PE11', 'intf-number': '0/1', 'loopback-
address': '10.0.0.1'},
{'device': 'PE21', 'intf-number': '0/0/0/1', 'loopback-
address': '10.0.0.2'}]
Step 12 Prepare an empty list, and add an entry for each attachment circuit
containing the device name (snippet PREPARE-LINKS-DATA).
vpn_name = service.name
customer = service.customer
pw_id = service.pw_id
links_data = []
for link in service.link:
link_data = {'device': link.device}
links_data.append(link_data)
Step 13 Add data to the dictionary. Use helper methods from the
device_helper module to determine the device type, and read the
value of the IP address from the device configuration (snippet READ-
LINK-INTERFACE-DATA). Read the value of the customer-facing
intf-number interface from the device-specific container (ios or ios-xr)
and store it in the intf-number entry.
links_data = []
for link in service.link:
link_data = {'device': link.device}
device_type =
device_helper.get_device_type(root, link.device)
self.log.info('Normalizing data for device {} of
type {}'
.format(link.device, device_type))
device_type_container = getattr(link,
device_helper.DEVICE_LOOKUP[device_type])
link_data['intf-number'] =
device_type_container.intf_number
link_data['loopback-address'] = \
device_helper.get_loopback_address(root,
link.device,
device_type,
device_type_container.loopback_interface)
links_data.append(link_data)
Step 14 With the data prepared, you can now configure the devices. In the
cb_create method, loop through the auxiliary list links_data and
apply the configuration template that you prepared at the beginning of
the task. Recall the variables names that you used, and provide the
values with a new variables object (snippet APPLY-TEMPLATE).
links_data.append(link_data)
template = ncs.template.Template(service)
template.apply('l2vpn-template', tvars)
Step 17 Make sure that your netsim devices are still running (see the previous
Lab – Task 1).
Step 18 Make sure that Cisco NSO is running and that the devices are synced
with Cisco NSO. If they are not synced, sync them with Cisco NSO.
admin@ncs# devices sync-from
sync-result {
device CE11
result true
}
sync-result {
device CE12
result true
}
sync-result {
device CE21
result true
}
sync-result {
device CE31
result true
}
sync-result {
device PE11
result true
}
sync-result {
device PE12
result true
}
sync-result {
device PE21
result true
© 2017 Cisco Systems, Inc. NSO30020 Lab Guide 2–15
This document is for training purposes only. All content is subject to change without notice.
Cisco Learning Services (www.cisco.com/go/cls) Layer 2 VPN Service Enhancements
}
sync-result {
device PE31
result true
}
- encapsulation mpls;
}
}
}
}
}
device PE21 {
config {
cisco-ios-xr:interface {
GigabitEthernet 0/0/0/6 {
- l2transport {
- }
}
}
cisco-ios-xr:l2vpn {
xconnect {
- group ACME {
- p2p PE11-PE21 {
- interface
GigabitEthernet0/0/0/6;
- neighbor 10.0.0.11 123;
- }
- }
}
}
}
}
}
services {
- l2vpn PE11-PE21 {
- customer ACME;
- pw-id 123;
- link PE11 {
- ios {
- intf-number 0/5;
- }
- }
- link PE21 {
- iosxr {
- intf-number 0/0/0/6;
- }
- }
- }
admin@ncs(config)# commit
Activity Verification
You have completed this task when you attain these results:
You have a directory structure and Python files for the L2VPN service.
You have successfully compiled the L2VPN service package.
You have successfully created and deleted an L2VPN service instance in
your lab network.
Visual Objective
Activity Procedure
Complete these steps:
Step 1 Examine the current model for the l2vpn service. Note that the pw-id
leaf is a mandatory numeric input parameter.
Step 2 Flag the leaf pw-id as read-only using the config statement.
Step 3 Mark this leaf to be stored in the CDB. Make sure that the data stored
in CDB will be persistent. Use the tailf:cdb-oper and
tailf:persistent YANG extension statements.
leaf pw-id {
tailf:info "Pseudowire ID";
type uint32 {
range "1..4294967295";
}
config false;
tailf:cdb-oper {
tailf:persistent true;
}
}
Activity Verification
You have completed this task when you attain these results:
You have successfully updated the pw-id leaf using the config statement.
Activity Procedure
Complete these steps:
__________________________________ Note ________________________________
Refer to the snippets in the /opt/resources/lab2/lab1-task5-snippets-
python.txt file.
______________________________________________________________________________
Step 1 Change the service mapping code in l2vpn.py to request PW ID
allocation, and read the response. The response is stored in an
operational leaf pw-id, residing in the service model. In general, this
data would be stored outside the service model. In that scenario, the
data would be affected by FASTMAP (removed before the mapping logic
runs). Therefore, it is necessary to open a separate transaction toward
the operational data store to read the data unaffected by FASTMAP.
Use the high-level MAAPI API in Python to accomplish this. If the pw-
id is not available, your code must exit (snippet READ-PWID).
vpn_name = service.name
customer = service.customer
pw_id = None
with ncs.maapi.single_read_trans(tctx.username,
tctx.context, db=ncs.OPERATIONAL) as t:
try:
oper_service = ncs.maagic.get_node(t,
service._path)
pw_id = oper_service.pw_id
except KeyError:
# service instance does not yet exist
pass
if pw_id is None:
self.log.info('pw-id does not exit, waiting for
redeploy')
return proplist
Step 4 Open the subscriber application Python module in your favorite text
editor, and first, take a few moments to go through the existing code.
Alternatively, you can also edit the file, using the PyCharm editor. The
skeleton subscriber does not directly use the CDB Subscription API
(which is very low-level), but instead uses a high-level Python wrapper,
located in the ncs.experimental.Subscriber class. You will extend this
class, and provide implementations for the following methods:
init() –Initializer for the subscriber.
import ncs
import ncs.maapi
import ncs.experimental
import _ncs
import fake_external_allocator
# ------------------------------------------------
# SUBSCRIBER ITERATOR OBJECT
# ------------------------------------------------
class AllocatorSubscriber(ncs.experimental.Subscriber):
"""This subscriber subscribes to changes in the..."""
return ncs.ITER_RECURSE
def cleanup(self):
pass
Step 5 Implement the init() method. This method is called by the super
class (ncs.experimental.Subscriber) constructor and is
responsible for setting up the subscription. Use the register()
method defined in the Subscriber wrapper to subscribe to the list of
service instances for the l2vpn service (snippet CDB-SUBSCRIBE).
Replace the pass statement.
# ------------------------------------------------
# SUBSCRIBER ITERATOR OBJECT
# ------------------------------------------------
class AllocatorSubscriber(ncs.experimental.Subscriber):
"""This subscriber subscribes to changes in the..."""
return ncs.ITER_RECURSE
Step 7 After iterating through the changes, the Subscriber wrapper will
execute the post_iterate() method in a background worker thread.
The method has access to the same state object populated by the
with ncs.maapi.single_write_trans('admin',
'system', db=ncs.OPERATIONAL) as t:
t.set_elem(_ncs.Value(allocated_id,
_ncs.C_UINT32), path)
t.apply()
fake_external_allocator.deallocate_id(request['value'])
self.log.info('Deallocated pwid ',
request['value'])
def setup(self):
self.log.info('L2vpn RUNNING')
self.register_service('l2vpn-servicepoint',
ServiceCallbacks)
self.sub =
l2vpn_subscriber.AllocatorSubscriber(app=self)
self.sub.start()
def teardown(self):
...
Step 10 Now we are ready to compile our package. Navigate to the package
source folder (packages/l2vpn/src) and compile it by running the
make command.
root@ubuntu:/home/marko/ncs-rfs-run/packages/l2vpn/src# make
pylint --disable=R,C --reports=n
../python/fake_external_allocator.py || (test $? -ge 4)
No config file found, using default configuration
pylint --disable=R,C --reports=n ../python/l2vpn.py || (test
$? -ge 4)
No config file found, using default configuration
************* Module l2vpn
W: 18,49: Access to a protected member _path of a client
class (protected-access)
W: 25,54: Access to a protected member _path of a client
class (protected-access)
pylint --disable=R,C --reports=n
../python/l2vpn_subscriber.py || (test $? -ge 4)
No config file found, using default configuration
************* Module l2vpn_subscriber
W: 41,19: Catching too general exception Exception (broad-
except)
pylint --disable=R,C --reports=n ../python/device_helper.py
|| (test $? -ge 4)
No config file found, using default configuration
Step 11 After the package is compiled successfully, reload the packages in NSO,
using the reload packages command.
admin@ncs# packages reload
Step 12 Repeat the verification procedure from the previous task. Notice how
you no longer need to provide the pw-id parameter, because it is
assigned automatically.
Activity Verification
You have completed this task when you attain these results:
You have a directory structure and Java files for L2VPN service.
You are able to successfully compile the L2VPN service package.
You are able to successfully create and delete an L2VPN service instance
in your lab network.
When you repeated the verification procedure, the pw-id parameter was
assigned automatically.
Activity Procedure
Complete these steps:
Step 1 Copy the packages id-allocator and resource-manager from the
next lab solution folder (located in /home/cisco/advanced-lab3)
into your lab project folder. The packages are located in the
/opt/resources/lab3/packages/ folder.
cisco@nso:~/ncs-run$ cp -pr ~/advanced-
lab3/packages/resource-manager packages/
cisco@nso:~/ncs-run$ cp -pr ~/advanced-lab3/packages/id-
allocator packages/
pw_id = None
service_path =
"/services/l2vpn[name='{}']".format(service.name)
self.log.info('Path: ', service_path)
id_allocator.id_request(service, service_path, 'pw-
id', tctx.username, vpn_name)
try:
pw_id = id_allocator.id_read(tctx, root, 'pw-
id', vpn_name)
except id_allocator.ResourceWait:
pass
if pw_id is None:
self.log.info('pw-id does not exit, waiting for
redeploy')
return proplist
Step 5 Remove the pw-id leaf and the unique pw-id statement from the
l2vpn.yang service model.
def setup(self):
self.log.info('L2vpn RUNNING')
self.register_service('l2vpn-servicepoint',
ServiceCallbacks)
self.sub =
l2vpn_subscriber.AllocatorSubscriber(app=self)
self.sub.start()
def teardown(self):
...
Step 8 Delete any existing test l2vpn services that you have created. Compile
the service, and reload the packages in Cisco NSO.
______________________ Note ________________________________
The change that we just made—migration to Resource Allocator—is a
significant upgrade of the service model and the mapping logic. In a
production environment, you would want to preserve existing service
instances during the upgrade. This action would require writing a
migration program that would populate the Resource Allocator
database with the already allocated PW IDs, and associate them with
the services.
_________________________________________________________________
Step 9 Before the Resource Allocator can be used, the ID pool must be
configured in the CDB. Use Cisco NSO CLI to create an ID pool named
pw-id, and assign it the range 100 to 200.
admin@ncs(config)# resource-pools id-pool pw-id range start
100 end 200
admin@ncs(config-id-pool-pw-id)# commit
Commit complete.
Step 10 Create a test l2vpn service instance, as in previous steps. Note that
the pw-id leaf is no longer available as a service input parameter.
Step 11 Verify the service configuration, using the show services l2vpn
<instance> command. Note that the allocation request appears as
configuration data (device modification). You can examine the current
allocations in the pool with the show resource-pools id-pool pw-
id command from the operational mode.
admin@ncs(config)# show configuration
services l2vpn PE11-PE21
customer ACME
link PE11
ios intf-number 0/6
!
link PE21
iosxr intf-number 0/0/0/3
!
!
admin@ncs(config)# commit
admin@ncs(config)# exit
admin@ncs# show services l2vpn PE11-PE21
services l2vpn PE11-PE21
device-modifications devices {
device PE11 {
config {
ios:interface {
GigabitEthernet 0/6 {
xconnect {
+ address
10.1.1.3;
+ vcid 100;
+ encapsulation
mpls;
}
}
}
}
}
device PE21 {
config {
cisco-ios-xr:interface {
GigabitEthernet
0/0/0/3 {
+ l2transport {
+ }
}
}
cisco-ios-xr:l2vpn {
xconnect {
+ group ACME {
+ p2p PE11-PE21
{
+ interface
GigabitEthernet0/0/0/3;
+ neighbor
10.1.1.1 100;
+ }
+ }
}
}
}
}
}
resource-pools {
id-pool pw-id {
+ allocation PE11-PE21 {
+ allocating-service
/services/l2vpn:l2vpn[name='PE11-PE21'];
+ }
}
}
Activity Verification
You have completed this task when you attain these results:
You have successfully loaded the new packages.
You have successfully created a new test l2vpn service instance.
Activity Procedure
Complete these steps:
Step 1 Copy the juniper-junos NED to your packages directory in
$HOME/ncs-run/packages from
$NCS_DIR/packages/neds/juniper-junos.
cisco@nso-stack:~/ncs-run/packages$ cp -pr ~/nso-
4.2/packages/neds/juniper-junos .
Step 4 Use the simulated device PE41 to prepare the XML template from the
Cisco NSO CLI. The device configuration for L2VPN on Junos devices
is as follows:
admin@ncs(config)# show configuration
devices device PE41
config
junos:configuration interfaces interface ge-0/1/1
encapsulation ethernet-ccc
!
! first
junos:configuration protocols l2circuit neighbor 10.0.0.1
! first
interface ge-0/1/1
virtual-circuit-id 1234
!
!
!
!
<encapsulation>ethernet-ccc</encapsulation>
</interface>
</interfaces>
<protocols tags="merge">
<l2circuit>
<neighbor>
<name>{$REMOTE-IP}</name>
<interface>
<name>{$INTERFACE-ID}</name>
<virtual-circuit-id>{$PW-ID}</virtual-circuit-id>
</interface>
</neighbor>
</l2circuit>
</protocols>
</configuration>
tailf:cli-drop-node-name;
leaf intf-number {
tailf:info "GigabitEthernet Interface ID";
mandatory true;
type leafref {
path
"deref(../../device)/../ncs:config/junos:configuration/junos
:interfaces/junos:interface/junos:name";
}
}
leaf loopback-interface {
tailf:info "Loopback Interface ID";
type string;
default "0";
}
}
Step 7 Add an import statement to the l2vpn service model at the top,
because it now references the junos NED.
module l2vpn {
...
import tailf-ned-cisco-ios-xr { prefix cisco-ios-xr; }
import junos { prefix junos; }
Step 8 Add the junos NED to the yangpath in the l2vpn service package
Makefile.
YANGPATH += ../../juniper-junos/src/ncsc-out/modules/yang
DEVICE_LOOKUP = {
TYPE_CISCO_IOS: 'ios',
TYPE_CISCO_IOSXR: 'iosxr'
TYPE_JUNIPER_JUNOS: 'junos'
Activity Verification
You have completed this task when you attain these results:
You have successfully added a new simulated device, PE41.
You have successfully enhanced your Python mapping code to support
Juniper devices.
Description
Complete this lab exercise to practice what you learned in the “Cloud VPN Service
Design” module of this course.
Activity Objective
In this activity, you will learn how to create a cloud bundle by implementing service
stacking. After completing this activity, you will be able to meet these objectives:
Use service stacking to instantiate services
Use Cisco Elastic Services Controller (Cisco ESC) to provision a virtual
network function (VNF) on OpenStack
Visual Objective
The graphic below illustrates the network topology used in the activity:
customer-server
Lab Network
198.18.134.4/26 198.18.134.12/26 198.18.134.11/26
Neutron 20.0.0.20/24
customer-network (Neutron)
Cisco mgmt-router
NSO
10.1.0.1/24
mgmt-network (Neutron)
10.1.0.10/24
internet-network (Neutron)
30.0.0.30/24
ESC
internet-server
1 / HTDDEV100_2-1 © 2017 Cisco and/or its affiliates. All rights reserved.
customer-server
Lab Network
198.18.134.4/26 198.18.134.12/26 198.18.134.11/26
Neutron 20.0.0.20/24
customer-network (Neutron)
Cisco mgmt-router
NSO 20.0.0.25/24
IOS
10.1.0.1/24
10.1.0.16/24
mgmt-network (Neutron)
Activate
Cloud 10.1.0.10/24 CRS VNF
30.0.0.35/24
internet-network (Neutron)
30.0.0.30/24
Task Data
The following data will be required for this lab.
MGMT IP
Device Type Addresses SSH Port
The preceding shows the graphical representation of the model used for the vRouter
service.
Activity Procedure
Complete these steps:
Step 1 Use the ncs-make-package tool to create a new service skeleton
called vrouter. Use the template-based service skeleton to create both
an XML template for the device configuration and an empty service
model.
cisco@nso-stack:~/ncs-run/packages$ ncs-make-package --
service-skeleton template-based vrouter
Step 2 Edit the vrouter.yang file, and create the service model. Create a
grouping vrouter, containing a list of routes. Each route will use the
leafs network, mask, and gateway for storing the route information.
module vrouter {
namespace "http://com/example/vrouter";
prefix vrouter;
grouping vrouter {
list routes {
key "network mask";
min-elements 1;
leaf network {
type inet:ipv4-address;
}
leaf mask {
type inet:ipv4-address;
}
leaf gateway {
mandatory true;
type inet:ipv4-address;
}
}
}
augment /ncs:services {
list vrouter {
description "This is a vRouter service";
key name;
leaf name {
type string;
}
uses ncs:service-data;
ncs:servicepoint vrouter-servicepoint;
leaf device {
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
uses vrouter;
}
}
Step 3 Create the vrouter-template.xml XML template for the static route
configuration. Use the Cisco NSO CLI and the test device CLOUD-TEST
to obtain the configuration in XML format.
admin@ncs# config
Entering configuration mode terminal
admin@ncs(config)# devices device CLOUD-TEST config
admin@ncs(config-config)# ios:ip route 192.168.1.0
255.255.255.0 10.0.0.1
admin@ncs(config-config)# commit dry-run outformat xml
device CLOUD-TEST
<ip xmlns="urn:ios">
<route>
<ip-route-forwarding-list>
<prefix>192.168.1.0</prefix>
<mask>255.255.255.0</mask>
<forwarding-address>10.0.0.1</forwarding-address>
</ip-route-forwarding-list>
</route>
</ip>
Step 4 Use the XML configuration to build the template file, and parameterize
it. Replace the placeholder values with XPath queries that will use the
service instance input data:
192.168.1.0: {network}
255.255.255.0: {mask}
10.0.0.1: {gateway}
In addition, add a foreach tag to the ip-route-forwarding-list
element to repeat the configuration for every entry in the routes list.
<config-template xmlns="http://tail-f.com/ns/config/1.0"
servicepoint="vrouter-servicepoint">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{/device}</name>
<config>
<ip xmlns="urn:ios">
<route tags="merge">
<ip-route-forwarding-list foreach="{routes}">
<prefix>{network}</prefix>
<mask>{mask}</mask>
<forwarding-address>{gateway}</forwarding-
address>
</ip-route-forwarding-list>
</route>
</ip>
</config>
</device>
</devices>
</config-template>
Activity Verification
You have completed this task when you attain these results:
You have successfully created the vrouter service for Cisco NSO.
The graphic below illustrates the model used for the vFirewall service.
Activity Procedure
Complete these steps:
Step 1 Use the ncs-make-package tool to create a new service skeleton
called vfirewall. Use the Python service-example service skeleton
to create an XML template for the device configuration, an empty
service model, and some skeleton Python code.
cisco@nso-stack:~/ncs-run/packages$ ncs-make-package --
python-skeleton --service-example --component-name Vfirewall
vfirewall
Step 2 Edit the vfirewall.yang file, and create the service model. Create a
grouping vfirewall, containing a list of access-list-rules. Each
access list rule will use leafs listed in the following table for storing the
rule information.
protocol enum: ip, icmp, udp, Layer 3 protocol for the ACL rule
tcp
grouping vfirewall {
list access-list-rules {
ordered-by user;
key name;
leaf name {
type string;
}
leaf action {
mandatory true;
type enumeration {
enum permit;
enum deny;
}
}
leaf protocol {
default ip;
type enumeration {
enum ip;
enum icmp;
enum tcp;
enum udp;
}
}
leaf src-ip {
mandatory true;
type inet:ipv4-address;
}
leaf src-mask {
mandatory true;
type inet:ipv4-address;
}
leaf src-port {
when "../protocol = 'tcp' or ../protocol = 'udp'";
default any;
type union {
type uint16;
type enumeration {
enum any;
}
}
}
leaf dest-ip {
mandatory true;
type inet:ipv4-address;
}
leaf dest-mask {
mandatory true;
type inet:ipv4-address;
}
leaf dest-port {
when "../protocol = 'tcp' or ../protocol = 'udp'";
default any;
type union {
type uint16;
type enumeration {
enum any;
}
}
}
}
}
augment /ncs:services {
list vfirewall {
uses ncs:service-data;
ncs:servicepoint vfirewall-servicepoint;
key name;
leaf name {
type string;
}
leaf device {
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
uses vfirewall;
}
}
}
<ext-named-acl>
<name>FIREWALL</name>
<ext-access-list-rule>
<rule>permit tcp 1.2.3.0 0.0.0.255 eq 12445
5.6.7.0 0.0.0.255 eq 80</rule>
</ext-access-list-rule>
</ext-named-acl>
</extended>
</access-list>
</ip>
admin@ncs(config-std-nacl)# commit dry-run outformat native
device CLOUD-TEST
ip access-list extended FIREWALL
permit tcp 1.2.3.0 0.0.0.255 eq 12445 5.6.7.0 0.0.0.255
eq 80
!
interface GigabitEthernet3
ip access-group FIREWALL in
exit
Step 5 Parameterize the static XML template with XPath variables. You will
need the following variables in place of the static values.
Name Description
Step 6 The final parameterized template should look like the following listing.
<config xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{$DEVICE}</name>
<config>
<interface xmlns="urn:ios">
<GigabitEthernet>
<name>{$INTERFACE-ID}</name>
<ip tags="merge">
<access-group>
<direction>{$ACCESS-LIST-
DIRECTION}</direction>
<access-list>{$ACCESS-LIST-NAME}</access-
list>
</access-group>
</ip>
</GigabitEthernet>
</interface>
<ip xmlns="urn:ios">
<access-list tags="merge">
<extended>
<ext-named-acl>
<name>{$ACCESS-LIST-NAME}</name>
<ext-access-list-rule>
<rule>{$ACCESS-LIST-RULE}</rule>
</ext-access-list-rule>
</ext-named-acl>
</extended>
</access-list>
</ip>
</config>
</device>
</devices>
</config>
Step 7 Open the vfirewall.py template and implement the mapping logic
that will build the correct ACL rule from the input parameters. Then
apply the template. Recall the configuration command that you used to
create the static XML template. The first helper method that you will
need will be used to invert the subnet mask to create a wildcard
network. Create the helper method convert_to_wildcard in the
ServiceCallbacks class (snippet CONVERT-TO-WILDCARD).
class ServiceCallbacks(Service):
...
def _convert_to_wildcard(self, mask):
# Could use python3 ipaddress module in future
mask_bytes = struct.unpack('BBBB',
socket.inet_aton(mask))
inverted_bytes = struct.pack('BBBB', *[~x & 0xFF for
x in mask_bytes])
return socket.inet_ntoa(inverted_bytes)
Step 8 The helper method uses two standard Python modules: socket and
struct. Add import statements at the top of your vfirewall.py
module (snippet IMPORT-BATTERIES).
import socket
import struct
Step 9 The rules for building an ACL rule state that the TCP or UDP port
number must be prepended with the ‘eq ‘ string. Create another helper
method, ¬_stringify_port, in the SeviceCallbacks class (snippet
STRINGIFY-PORT).
def _stringify_port(self, port):
if isinstance(port, int):
return 'eq ' + str(port)
else:
return ''
Step 10 The third helper method, _build_acl_rule, will be used to create the
ACL rule string from the input parameters (snippet BUILD-ACL-
RULE).
def _build_acl_rule(self, rule):
src_port = self._stringify_port(rule.src_port)
dest_port = self._stringify_port(rule.dest_port)
src_mask=self._convert_to_wildcard(rule.src_mask),
src_port=src_port,
dest_ip=rule.dest_ip,
dest_mask=self._convert_to_wildcard(rule.dest_mask),
dest_port=dest_port)
device = service.device
tvars.add('DEVICE', device)
tvars.add('ACCESS-LIST-NAME', acl_name)
tvars.add('INTERFACE-ID', acl_interface)
tvars.add('ACCESS-LIST-DIRECTION',
acl_direction)
tvars.add('ACCESS-LIST-RULE',
self._build_acl_rule(rule))
template.apply('vfirewall-template', tvars)
Step 13 Optionally, add some instruction to the Makefile that will allow you to
check the syntax and style of your Python code. Do this step by running
the pylint tool on all Python source files. The tool has already been
installed in the lab environment but is not bundled by default with
Cisco NSO installation.
all: fxs pylint
PYLINT = pylint
PYLINTFLAGS = --disable=R,C --reports=n
PYDIR = ../python
PYTHONFILES = $(wildcard $(PYDIR)/*.py)
%.pylint:
$(PYLINT) $(PYLINTFLAGS) $*.py || (test $$? -ge 4)
fxs: $(FXS)
src-ip 0.0.0.0
src-mask 0.0.0.0
dest-ip 1.2.3.4
dest-mask 255.255.255.255
dest-port 80
!
!
admin@ncs(config)# commit dry-run outformat native
device CLOUD-TEST
ip access-list extended FIREWALL
permit tcp 0.0.0.0 255.255.255.255 1.2.3.4 0.0.0.0 eq 80
!
interface GigabitEthernet3
ip access-group FIREWALL in
exit
Activity Verification
You have completed this task when you attain these results:
You have successfully implemented the vfirewall service.
Visual Objective
The graphic below shows the graphical representation of the cloud service model.
Activity Procedure
Subtask 1: Create the Cloud Service YANG Model
Complete these steps:
Step 1 Use the ncs-make-package tool to create a new service skeleton
called “vcloud.” Use the Python service-example service skeleton to
create an XML template for the device configuration, an empty service
model and some skeleton Python code.
cisco@nso-stack:~/ncs-run/packages$ ncs-make-package --
python-skeleton --service-example --component-name Cloud
cloud
Step 2 Edit the cloud.yang file, containing the service model. Add a leaf,
device, for specifying an existing device. Add two containers for the
vrouter and vfirewall configuration. To avoid repetition when
modeling the vRouter and vFirewall parameters, use a uses
statement, for referencing the vrouter and vfirewall service model
(snippets CLOUD-MODEL-IMPORT and CLOUD-MODEL-SERVICE).
module cloud {
namespace "http://com/example/cloud";
prefix cloud;
augment /ncs:services {
list cloud {
description "This is the Cloud service";
key name;
leaf name {
type string;
}
uses ncs:service-data;
ncs:servicepoint cloud-servicepoint;
leaf device {
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
container vrouter {
presence "vrouter";
uses vrouter:vrouter;
}
container vfirewall {
presence "vfirewall";
uses vfirewall:vfirewall;
}
}
}
Step 4 Optionally, add some instruction to the Makefile that will allow you to
check the syntax and style of your Python code. This is accomplished by
running the pylint tool on all Python source files. The tool has already
been installed in the lab environment, but is not bundled by default
with Cisco NSO installation.
all: fxs pylint
...
PYLINT = pylint
PYLINTFLAGS = --disable=R,C --reports=n
PYDIR = ../python
PYTHONFILES = $(wildcard $(PYDIR)/*.py)
%.pylint:
$(PYLINT) $(PYLINTFLAGS) $*.py || (test $$? -ge 4)
fxs: $(FXS)
Step 2 Prepare the variables for storing the “global service input parameters.”
Store the value of the device leaf in a device variable (snippet
DEVICE-NAME).
def cb_create(self, tctx, root, service, proplist):
self.log.info('Service create(service=',
service._path, ')')
device = service.device
Step 3 Recall the input parameters for the vrouter service. You will need to
construct an XML template in order to create a service instance. Use
the Cisco NSO CLI to create an instance (or simply use an existing
configured instance from the previous task) and obtain the
configuration in XML.
___________________ Note ________________________________
Use the show running-configuration services vrouter
<instance> | display xml command to get the service instance
configuration in XML.
______________________________________________________________
Step 4 Parameterize the template. Use $NAME and $DEVICE variables for the
name and device input parameters for the vrouter service. Use the
foreach tag to copy the input parameters containing the list of static
routes directly from the cloud service instance data. Save the XML
template in the vrouter-service-template.xml template in the
cloud service package (under the templates directory, create it
manually). The final solution is as follows (snippet VROUTER-
SERVICE-TEMPLATE).
<config xmlns="http://tail-f.com/ns/config/1.0">
<services xmlns="http://tail-f.com/ns/ncs">
<vrouter xmlns="http://com/example/vrouter">
<name>{$NAME}</name>
<device>{$DEVICE}</device>
<routes foreach="{/routes}">
<network>{network}</network>
<mask>{mask}</mask>
<gateway>{gateway}</gateway>
</routes>
</vrouter>
</services>
</config>
Step 6 Perform Steps 2 through 4 for the vfirewall service. Create an XML
template for the service input parameters, and conditionally apply the
template from the cloud service based on the existence of the
vfirewall container. The final template and code are as follows
(snippets VFIREWALL-SERVICE-TEMPLATE and CREATE-
VFIREWALL-SERVICE).
<config xmlns="http://tail-f.com/ns/config/1.0">
<services xmlns="http://tail-f.com/ns/ncs">
<vfirewall xmlns="http://com/example/vfirewall">
<name>{$NAME}</name>
<device>{$DEVICE}</device>
<access-list-rules foreach="{/access-list-rules}">
<name>{name}</name>
<action>{action}</action>
<protocol>{protocol}</protocol>
<src-ip>{src-ip}</src-ip>
<src-mask>{src-mask}</src-mask>
<src-port>{src-port}</src-port>
<dest-ip>{dest-ip}</dest-ip>
<dest-mask>{dest-mask}</dest-mask>
<dest-port>{dest-port}</dest-port>
</access-list-rules>
</vfirewall>
</services>
</config>
if service.vfirewall.exists():
self.log.info('vFirewall config exists, will
create vFirewall instance')
template =
ncs.template.Template(service.vfirewall)
tvars = ncs.template.Variables()
tvars.add('NAME', service.name)
tvars.add('DEVICE', device)
template.apply('vfirewall-service-template',
tvars)
admin@ncs# config
Entering configuration mode terminal
admin@ncs(config)# services cloud first-test
admin@ncs(config-cloud-first-test)# vfirewall access-list-
rules test-rule
Value for 'action' [deny,permit]: permit
Value for 'src-ip' (<IPv4 address>): 0.0.0.0
Value for 'src-mask' (<IPv4 address>): 0.0.0.0
Value for 'dest-ip' (<IPv4 address>): 1.2.3.4
Value for 'dest-mask' (<IPv4 address>): 255.255.255.255
admin@ncs(config-access-list-rules-test-rule)# protocol tcp
admin@ncs(config-access-list-rules-test-rule)# commit dry-
run outformat native
native {
device {
name CLOUD-TEST
data ip access-list extended FIREWALL
permit tcp 0.0.0.0 255.255.255.255 1.2.3.4
0.0.0.0
exit
}
}
admin@ncs(config-access-list-rules-test-rule)# commit
Commit complete.
Step 9 The commit operation must be completed. Examine the output of the
show services cloud first-test again, and notice the changed
device-modifications, containing the static route and a firewall rule.
admin@ncs# show services cloud first-test
services cloud first-test
device-modifications devices {
device CLOUD-TEST {
config {
ios:ip {
route {
+ ip-route-
forwarding-list 1.2.3.0 255.255.255.0 5.34.23.4 {
+ }
}
access-list {
extended {
+ ext-named-acl
FIREWALL {
+ ext-
access-list-rule "permit tcp 0.0.0.0 255.255.255.255
1.2.3.4 0.0.0.0 ";
+ }
}
}
}
}
}
}
services {
+ vfirewall first-test {
+ device CLOUD-TEST;
+ access-list-rules test-rule {
+ action permit;
+ protocol tcp;
+ src-ip 0.0.0.0;
+ src-mask 0.0.0.0;
+ dest-ip 1.2.3.4;
+ dest-mask
255.255.255.255;
+ dest-port any;
+ }
+ }
+ vrouter first-test {
+ device CLOUD-TEST;
+ routes 1.2.3.0 255.255.255.0
{
+ gateway 5.34.23.4;
+ }
+ }
}
Activity Verification
You have completed this task when you attain these results:
You have successfully implemented the cloud service.
You have successfully created a test instance of the cloud service, and
configured the vRouter and vFirewall functionality on the test CLOUD-
TEST device.
Activity Procedure
Subtask 1: Copy the Required Packages
Complete these steps:
Step 1 The Resource Management packages required by the cloud service are
located on the NSO server, in the /home/cisco/advanced-
lab3/packages/ folder. Copy all packages to your packages folder in
$HOME/ncs-run/packages.
cisco@ncs:~/ncs-run/packages$ cp -pr ~/advanced-
lab3/packages/vm-manager .
cisco@ncs:~/ncs-run/packages$ cp -pr ~/advanced-
lab3/packages/vm-manager-esc .
cisco@ncs:~/ncs-run/packages$ cp -pr ~/advanced-
lab3/packages/esc .
cisco@ncs:~/ncs-run/packages$ cp -pr ~/advanced-
lab3/packages/resource-manager .
cisco@ncs:~/ncs-run/packages$ cp -pr ~/advanced-
lab3/packages/ipaddress-allocator .
Step 2 Copy the updated Cisco IOS NED from the Lab3 solution folder. In
Task 5, you will use the new functionality of the NED.
cisco@ncs:~/ncs-run/packages$ cp -pr ~/advanced-
lab3/packages/cisco-ios .
Step 3 Reload packages and make sure that all packages are loaded correctly
before proceeding.
___________________________ WARNING ____________________________
After upgrading the Cisco IOS NED, you will receive an error message during
the package reload, notifying you that the l3mplsvpn-iosxr-template.xml
is invalid. The template that is part of the l3mplsvpn package still refers to
the old device model. If you do not plan to use the l3mplsvpn package from
Lab 1 any longer, it is safe to ignore the error. Otherwise, you must update the
template to use the upgraded Cisco IOS device model.
________________________________________________________________________
list cloud {
...
uses ncs:service-data;
ncs:servicepoint cloud-servicepoint;
leaf device {
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
leaf tenant {
mandatory true;
type string;
}
leaf customer-network {
mandatory true;
type string;
}
leaf internet-network {
mandatory true;
type string;
}
container vrouter {
presence "vrouter";
uses vrouter:vrouter;
}
...
Step 3 Add a helper method for allocating the management IP address to the
ServiceCallbacks class. The method will use the
resource_manager Python module to interface with the resource-
manager packages to request a new IP address from the IP address
pool named mgmt. When the request is processed, the method will
return the allocated IP address (snippet ALLOCATE-MGMT-
ADDRESS).
ServiceCallbacks(Service):
...
Step 4 Execute the new helper method from the main cb_create() method,
and check the returned result. If the IP address is not returned, it
means that the service mapping logic must wait for a redeploy from the
external subscriber. Exit from the main method at this point (snippet
EXECUTE-ALLOCATE-MGMT-ADDRESS).
def cb_create(self, tctx, root, service, proplist):
self.log.info('Service create(service=',
service._path, ')')
service_path =
"/services/cloud[name='{}']".format(service.name)
mgmt_ip = self._allocate_mgmt_address(tctx,
service,
service_path,
root,
service.name)
if not mgmt_ip:
self.log.info('Waiting for redeploy')
return proplist
Step 6 Add a new helper method for creating a new VNF request to the
ServiceCallbacks class. The method will set the
resource_manager Python module to interface with the vm-manager
packages to request a new VNF. In the first part, apply the prepared
template vm-manager-template to create the start request (snippet
CREATE-VNF).
def _create_vnf(self, tctx, service, service_path,
tenant, deployment_name, mgmt_ip):
tvars = ncs.template.Variables()
deployment_name=deployment_name,
vm_group='CSR',
esc='esc0')
if vm_manager.device_ready(tctx, device_name):
return device_name
Step 8 Execute the new helper method from the main cb_create() method
(overwrite device = service.device line), and check the returned
result. If the device name is not returned, it means that the service
mapping logic must wait for a redeploy from the external subscriber.
Exit from the main method at this point (snippet EXECUTE-CREATE-
VNF).
deployment_name = 'cloud-' + service.name VNF).
device = self._create_vnf(tctx,
service,
service_path,
service.tenant,
deployment_name,
mgmt_ip)
if not device:
self.log.info('Waiting for CSR')
return proplist
Step 9 After the device is automatically added into NSO Device Manager by
the vm-manager-esc package, you can store the device name in the
operational device leaf that is part of the service model. Create
another helper method, _set_operational_device_leaf, in the
ServiceCallbacks class to write the operational data (snippet SET-
OPERATIONAL-DEVICE-LEAF).
def _set_operational_device_leaf(self, tctx, service,
device_name):
with ncs.maapi.single_write_trans(tctx.username,
tctx.context, db=ncs.OPERATIONAL) as oper_th:
oper_service = ncs.maagic.get_node(oper_th,
service._path)
oper_service.device = device_name
oper_th.apply()
Step 10 Execute the helper method from the main cb_create() method after
the device is available for configuration (snippet EXECUTE-SET-
OPERATIONAL-DEVICE-LEAF).
if not device:
self.log.info('Waiting for CSR')
return proplist
self._set_operational_device_leaf(tctx, service,
device)
Step 11 Use the Maagic API to set the hostname on the Cisco IOS device. You
can put this code into another helper method, and execute it from the
main cb_create() method (snippet SET-HOSTNAME).
class ServiceCallbacks(Service):
def cb_create(self, tctx, root, service, proplist):
...
self._set_operational_device_leaf(tctx, service,
device)
self._set_hostname(root, device)
...
root.devices.device[device_name].config.ios__hostname =
device_name[:16]
Plan Plan
Component Component
Name Type Plan Component States Description
ncs:init Represents the complete service
self ncs:self instance configuration
ncs:ready
ncs:init Represents the provisioning and
configuration process of the VNF
cloud:mgmt-address-
CSR cloud:vnf allocated
cloud:vnf-created
ncs:ready
Step 1 Add the new components and states to the cloud service model. Use
the preceding table as a reference for the plan component types and
states (snippet SERVICE-PLAN-COMPONENTS).
module cloud {
namespace "http://com/example/cloud";
prefix cloud;
identity vnf {
base ncs:plan-component-type;
}
identity mgmt-address-allocated {
base ncs:plan-state;
}
identity vnf-created {
base ncs:plan-state;
}
list cloud {
description "This is the Cloud service";
key name;
leaf name {
type string;
}
uses ncs:service-data;
uses ncs:plan-data;
ncs:servicepoint cloud-servicepoint;`
Step 3 In the Python code, you will now define the plan components and states
and set the status as part of the mapping logic. At the beginning of the
cb_create() method, define a plan_data dictionary that will store
all possible and reached states for the additional VNF component. In
the initialization, the reached_states list will be empty, but you will
add new entries for each of the Reactive FASTMAP stages (snippet
PLAN-DATA).
class ServiceCallbacks(Service):
@Service.create
def cb_create(self, tctx, root, service, proplist):
self.log.info('Service create(service=',
service._path, ')')
service_path =
"/services/cloud[name='{}']".format(service.name)
plan_data = {'CSR':{'states':['mgmt-address-
allocated', 'vnf-created'],
'reached_states': []}}
finished = True
for component, states in plan_data.iteritems():
vnf_plan = PlanComponent(service, component,
'cloud:vnf')
vnf_plan.append_state('ncs:init')
vnf_plan.set_reached('ncs:init')
[vnf_plan.append_state(x) for x in
states['states']]
[vnf_plan.set_reached(x) for x in
states['reached_states']]
vnf_plan.append_state('ncs:ready')
if len(states['states']) ==
len(states['reached_states']):
vnf_plan.set_reached('ncs:ready')
else:
finished = False
if finished:
self_plan.set_reached('ncs:ready')
plan_data['CSR']['reached_states'].append('mgmt-
address- allocated')
self._set_operational_device_leaf(tctx, service,
device)
plan_data['CSR']['reached_states'].append('vnf-
created')
Step 8 At the end of the mapping logic, just before the end of the
cb_create() method, add the final call to the _write_plan_data
method to ensure that plan components are created in the final stage of
the service provisioning process as well (snippet FINAL-WRITE-PLAN-
DATA).
if service.vrouter.exists():
...
self._write_plan_data(service, plan_data)
name: mgmt
subnet: 10.1.0.0/24
exclude: 10.1.0.0/28
admin@ncs(config)# resource-pools ip-address-pool mgmt
admin@ncs(config-ip-address-pool-mgmt)# subnet 10.1.0.0 24
admin@ncs(config-ip-address-pool-mgmt)# exclude 10.1.0.0 28
admin@ncs(config-ip-address-pool-mgmt)# commit
Step 2 Add Cisco ESC into Cisco NSO. The NED is already loaded, and Cisco
ESC is running. Use the following parameters for the new device:
name: esc0
address: 198.18.134.12
port 830
authgroup: esc
device-type: netconf
admin@ncs(config)# devices device esc0
admin@ncs(config-device-esc0)# address 198.18.134.12
admin@ncs(config-device-esc0)# port 830
admin@ncs(config-device-esc0)# device-type netconf
admin@ncs(config-device-esc0)# authgroup esc
admin@ncs(config-device-esc0)# state admin-state unlocked
admin@ncs(config-device-esc0)# commit
admin@ncs# devices device esc0 ssh fetch-host-keys
result updated
fingerprint {
algorithm ssh-dss
value 71:66:f6:a2:af:a8:1f:73:11:00:82:33:d0:c5:3d:3f
}
Step 6 View the progress of the request through RFM Service Plan:
admin@ncs# show services cloud second-test plan
NAME TYPE STATE STATUS WHEN
------------------------------------------------------------
----------
self self init reached 2016-08-
23T15:07:11
ready not-reached -
CSR vnf init reached 2016-08-
23T15:07:11
mgmt-address-allocated reached 2016-08-
23T15:07:11
vnf-created not-reached -
ready not-reached -
escmanager.log on ESC
(10.1.0.10:/var/log/esc/escmanager.log)
| 657016fa-c22e-465a-94b2-a17213f8ca54 | moo
| ACTIVE | - | Running | public=172.16.122.208
|
+--------------------------------------+--------------------
----------------------------------------+--------+----------
--+-------------+-------------------------------------------
---------------------------+
Step 9 Check the service status in Cisco NSO with the show services
cloud first-test command.
admin@ncs# show services cloud second-test
services cloud first-test
device-modifications devices {
device admin_cloud-second-
test_CSR_esc0 {
config {
- ios:hostname csr;
+ ios:hostname admin_cloud-
second-test_CSR_esc0;
}
}
}
vm-manager {
+ start admin_cloud-second-test_CSR
{
+ deployment-name cloud-second-
test;
+ vm-device esc0;
+ tenant admin;
+ vm-type csr;
+ vm-group CSR;
+ day0-url
http://controller:8080/csr_config.txt;
+ image-id csr;
+ flavor-id csr;
+ interface 0 {
+ name mgmt;
+ ip 10.1.0.16;
+ }
+ interface 1 {
+ name customer;
+ }
+ interface 2 {
+ name internet;
+ }
+ scaling-min 1;
+ scaling-max 1;
+ allocators
/services/cloud:cloud[name='second-test'];
+ }
}
resource-pools {
ip-address-pool mgmt {
+ allocation admin_cloud-
second-test_CSR_esc0 {
+ allocating-service
/services/cloud:cloud[name='second-test'];
+ request {
+ subnet-size 32;
+ }
+ }
}
}
Step 11 The commit operation must be completed. Because the VNF for the
cloud service already exists, this change will only reconfigure the
existing virtual device. Examine the output of show services cloud
second-test again, and notice the changed device modifications
containing the static route.
admin@ncs# show services cloud second-test
services cloud first-test
device-modifications devices {
device admin_cloud-second-
test_CSR_esc0 {
config {
- ios:hostname csr;
+ ios:hostname
admin_second-first-test_CSR_esc0;
ios:ip {
route {
+ ip-route-
forwarding-list 1.2.3.0 255.255.255.0 5.6.7.8 {
+ }
}
}
}
}
}
Activity Verification
You have completed this task when you attain these results:
You have successfully implemented the cloud service.
You have successfully created a test instance of the cloud service, and
configured the vRouter and vFirewall functionality on a virtual device.
Although this new action will only return the string result of the ping command from
the device, it shows how to use Cisco NSO features to provide some additional
operational data that can be used for service assurance.
Activity Procedure
Complete these steps:
Step 1 Examine the ping action definition in the Cisco IOS NED. The file
tailf-ned-cisco-ios-stats.yang is located in the
packages/cisco-ios/src/yang folder. Use the model to determine
the input and output parameters for the ping action and the path to
execute the action.
Step 2 Verify that the ping action works on the virtual device when invoked
through the Cisco NSO CLI, by pinging one of the virtual servers in the
customer or Internet networks (either 20.0.0.20 or 30.0.0.30).
admin@ncs# devices device admin_cloud-second-test_CSR_esc0
live-status exec ping 20.0.0.20
result
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 20.0.0.20, timeout is 2
seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max =
4/12/30 ms
admin_cloud-second-test_CSR_esc0#
admin@ncs#
Step 3 Add your ping action to the cloud.yang service model. Use the
tailf:action statement. The action must have one mandatory input
parameter of the type ipv4-address and one output parameter of the
type string (snippet PING-ACTION-MODEL).
tailf:action ping {
tailf:actionpoint cloud-ping;
input {
leaf ip {
mandatory true;
type inet:ipv4-address;
}
}
output {
leaf result {
type string;
}
}
}
import ncs.maapi
import ncs.maagic
from ncs.dp import Action
class PingAction(Action):
@Action.action
def cb_action(self, uinfo, name, kp, action_input,
action_output):
self.log.info('action name: ', name)
self.log.info('action input.ip: ', action_input.ip)
Step 5 In the annotated action callback cb_action, you will start a new
Maapi session and attach to the current transaction of the user
executing the action. Use the Python high-level Maapi API to start a
new session (snippet ATTACH-MAAPI).
with ncs.maapi.Maapi() as m:
with ncs.maapi.Session(m, uinfo.username,
uinfo.context):
tid = uinfo.actx_thandle
self.log.info(
'Action {} attaching to user {} tid
{}'.format(name, uinfo.username, tid))
t = m.attach(tid, usid=uinfo.usid)
Step 6 After executing the attach method, you will have a new Transaction
object to use as a Maagic back end. Create two Maagic Nodes – one
pointing to the current service instance list entry (use the get_node
Maagic method and kp) and one pointing to the root of the CDB (use
the get_root Maagic method) (snippet MAAGIC-NODES).
service = ncs.maagic.get_node(t, kp)
root = ncs.maagic.get_root(t)
Step 7 Use the root Maagic Node to get a reference to the ping action on
the device that the current service instance is using (snippet MAAGIC-
PING-ACTION).
ping =
root.devices.device[service.device].live_status.ios_stats__e
xec.ping
Step 8 Execute the Maagic action, passing the input parameters from your
cb_action method (snippet EXECUTE-PING).
ping_input = ping.get_input()
ping_input.args = [action_input.ip]
ping_output = ping(ping_input)
action_output.result = ping_output.result
Step 9 Detach the current Maapi session from the ongoing user transaction
(snippet DETACH-MAAPI).
m.detach(tid)
Step 10 You must tell Cisco NSO about the new action callback. Open the
cloud.py file and add an import of the file that you just created at the
top (snippet IMPORT-CLOUD-ACTIONS).
from cloud_actions import PingAction
Step 11 Find the component thread code, located in the Cloud class at the
bottom of the file. Add a new action registration in the existing
setup() method (snippet REGISTER-ACTION).
class Cloud(ncs.application.Application):
def setup(self):
self.log.info('Cloud RUNNING')
self.register_service('cloud-servicepoint',
ServiceCallbacks)
self.register_action('cloud-ping', PingAction)
Step 12 Compile the cloud package with the make command and reload the
packages in Cisco NSO.
Step 13 Invoke the ping action through the Cisco NSO CLI on an existing
service instance. The output should be similar to the one at the
beginning of this task, when you were testing the ping command
directly on the virtual device.
admin@ncs# services cloud second-test ping ip 20.0.0.20
result
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 20.0.0.20, timeout is 2
seconds:
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max =
11/18/33 ms
admin_cloud-second-test_CSR_esc0#
admin@ncs#
Activity Verification
You have completed this task when you attain these results:
You have successfully added a new ping action to the existing cloud
service.
You have successfully tested the new action by executing the action on an
existing instance of the cloud service.
Lab 1
l3mplsvpn.yang
module l3mplsvpn {
namespace "http://com/example/l3mplsvpn";
prefix l3mplsvpn;
augment /ncs:services {
leaf l3mplsvpn-id-cnt {
description "Provides a unique 32-bit number used as VPN
instance identifier";
tailf:hidden "Counter";
type uint32;
default 1;
}
}
augment /ncs:services {
list l3mplsvpn {
tailf:info "Layer-3 MPLS VPN Service";
key vpn-name;
unique vpn-id;
uses ncs:service-data;
ncs:servicepoint "l3mplsvpn-servicepoint";
leaf vpn-name {
type string;
tailf:info "Service Instance Name";
}
leaf vpn-id {
tailf:info "Layer-3 MPLS VPN Service Instance
identifier";
tailf:hidden "ID";
type uint32 {
range "1..65535" {
error-message "VPN ID is out of range. Should be
between 1 and 65535.";
}
}
}
leaf link-id-cnt {
description "Provides a unique 32-bit number used as site
identifier";
tailf:hidden "Counter";
default 1;
type uint64 {
range "1..65535";
}
}
leaf customer {
tailf:info "VPN Customer";
type leafref {
path "/ncs:customers/ncs:customer/ncs:id";
}
}
list link {
tailf:info "PE-CE Attachment Point";
key link-name;
unique "link-id";
unique "device interface";
min-elements 1;
leaf link-name {
type string;
tailf:info "Link Name";
}
leaf link-id {
tailf:hidden "ID";
type uint32 {
range "1..65535" {
error-message "Link ID is out of range. Should be
between 1 and 65535.";
}
}
}
leaf device {
tailf:info "PE Router";
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
must "starts-with(current(),'PE')" {
error-message "Only PE devices can be selected.";
}
}
leaf interface {
tailf:info "Customer Facing Interface";
type string;
mandatory true;
must "count(../../../l3mplsvpn[vpn-name !=
current()/../../vpn-name]/link[device
= current()/../device][interface=current()])
= 0" {
error-message "Interface is already used for another
link.";
}
}
leaf pe-ip {
tailf:info "PE Interface IP Address";
mandatory false;
type inet:ipv4-address {
pattern "172\.([1][6-9]|[2][0-9]|3[0-1])\..*" {
error-message "Invalid IP address. IP address
should be in the 172.16.0.0/12
range.";
}
}
}
leaf ce-ip {
tailf:info "CE Interface IP Address";
when "../routing-protocol='bgp'";
mandatory false;
type inet:ipv4-address {
pattern "172\.([1][6-9]|[2][0-9]|3[0-1])\..*" {
error-message "Invalid IP address. IP address
should be in the 172.16.0.0/12
range.";
}
}
}
leaf rip-net {
tailf:info "IP network for RIP";
when "../routing-protocol='rip'";
mandatory false;
type inet:ipv4-address {
pattern "172\.([1][6-9]|[2][0-9]|3[0-1])\.[0-9]+\.0"
{
error-message "Invalid IP address. IP address
should be in the 172.16.0.0/12
range.";
}
}
}
leaf routing-protocol {
tailf:info "Routing option for the PE-CE link";
type enumeration {
enum bgp;
enum rip;
}
default bgp;
}
}
}
}
}
l3mplsvpn.py
# -*- mode: python; python-indent: 4 -*-
import ncs
import l3mplsvpn_callbacks
# ---------------------------------------------
# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
# ---------------------------------------------
class L3MplsVpn(ncs.application.Application):
def setup(self):
# The application class sets up logging for us. Is is
accessible
# through 'self.log' and is a ncs.log.Log instance.
self.log.info('l3mplsvpn RUNNING')
def teardown(self):
# When the application is finished (which would happen if
NCS went
# down, packages were reloaded or some error occurred)
this teardown
# method will be called.
self.log.info('l3mplsvpn FINISHED')
13mplsvpn_callbacks.py
# -*- mode: python; python-indent: 4 -*-
import _ncs
from ncs.application import Service
import ncs.template
TYPE_CISCO_IOS = '{tailf-ned-cisco-ios}'
TYPE_CISCO_IOSXR = '{tailf-ned-cisco-ios-xr}'
# ------------------------
# SERVICE CALLBACK EXAMPLE
# ------------------------
class ServiceCallbacks(Service):
tvars = ncs.template.Variables()
template = ncs.template.Template(service)
tvars.add('VPNID', vpn_id)
link_ip = link.pe_ip
peer_ip = link.ce_ip
rip_net = link.rip_net
tvars.add('DEVICE', link.device)
tvars.add('PEIP', link_ip)
tvars.add('CEIP', peer_ip)
tvars.add('INTERFACE', link.interface)
tvars.add('ROUTING-PROTOCOL', link.routing_protocol)
if link.routing_protocol == 'rip':
tvars.add('RIP-NET', rip_net)
else:
tvars.add('RIP-NET', '')
if device_type == TYPE_CISCO_IOS:
self.log.info('Applying IOS template for device
', link.device)
template.apply('l3mplsvpn-ios-template', tvars)
elif device_type == TYPE_CISCO_IOSXR:
self.log.info('Applying IOS-XR template for
device ', link.device)
template.apply('l3mplsvpn-iosxr-template', tvars)
else:
raise Exception('Unknown device type ' +
device_type)
return proplist
@Service.pre_modification
def cb_pre_modification(self, tctx, op, kp, root, proplist):
self.log.info('Service premod(service=', kp, ')')
if op == _ncs.dp.NCS_SERVICE_DELETE:
self.log.info('Delete, ignore')
return proplist
if op == _ncs.dp.NCS_SERVICE_CREATE:
vpn_id =
froot.ncs__services.l3mplsvpn__l3mplsvpn_id_cnt
proplist.append(('vpn-id', str(vpn_id)))
froot.ncs__services.l3mplsvpn__l3mplsvpn_id_cnt =
vpn_id + 1
self.log.info('Setting vpn_id to ',
froot.ncs__services.l3mplsvpn__l3mplsvpn_id_cnt)
m.detach(tctx)
return proplist
l3mplsvpn-ios-template.xml
<config-template xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{$DEVICE}</name>
<config>
<vrf xmlns="urn:ios" tags="merge">
<definition>
<name>vpn{$VPNID}</name>
<address-family>
<ipv4 />
</address-family>
<route-target>
<import>
<asn-
ip>1:{$VPNID}</asn-ip>
</import>
<export>
<asn-
ip>1:{$VPNID}</asn-ip>
</export>
</route-target>
<rd>1:{$VPNID}</rd>
</definition>
</vrf>
<router xmlns="urn:ios" tags="merge">
<rip when="{$ROUTING-PROTOCOL='rip'}">
<address-family>
<ipv4>
<vrf>
<name>vpn{$VPNID}</name>
<default-
information>
<originate />
</default-
information>
<network>
<ip>{$RIP-NET}</ip>
</network>
</vrf>
</ipv4>
</address-family>
</rip>
<bgp when="{$ROUTING-PROTOCOL='bgp'}">
<as-no>1</as-no>
<address-family>
<with-vrf>
<ipv4>
<unicast-
multicast>unicast</unicast-multicast>
<vrf>
<name>vpn{$VPNID}</name>
<neighbor>
<id>{$CEIP}</id>
<remote-as>65001</remote-as>
</neighbor>
<redistribute>
<connected />
<static />
</redistribute>
</vrf>
</ipv4>
</with-vrf>
</address-family>
</bgp>
</router>
<interface xmlns="urn:ios" tags="nocreate">
<GigabitEthernet>
<name>{$INTERFACE}</name>
<vrf tags="create">
<forwarding>vpn{$VPNID}</forwarding>
</vrf>
<ip tags="merge">
<address>
<primary>
<mask>255.255.255.252</mask>
<address>{$PEIP}</address>
</primary>
</address>
</ip>
</GigabitEthernet>
</interface>
</config>
</device>
</devices>
</config-template>
l3mplsvpn-iosxr-template.xml
<config-template xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{$DEVICE}</name>
<config>
<vrf xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<vrf-list>
<name>vpn{$VPNID}</name>
<address-family>
<ipv4>
<unicast>
<import>
<route-target>
<address-list>
<name>1:{$VPNID}</name>
</address-list>
</route-target>
</import>
<export>
<route-target>
<address-list>
<name>1:{$VPNID}</name>
</address-list>
</route-target>
</export>
</unicast>
</ipv4>
</address-family>
</vrf-list>
</vrf>
<router xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<bgp>
<bgp-no-instance>
<id>1</id>
<vrf>
<name>vpn{$VPNID}</name>
<neighbor>
<id>{$CEIP}</id>
<remote-as>65001</remote-as>
<address-family>
<ipv4>
<unicast>
<route-policy>
<direction>in</direction>
<name>pass</name>
</route-policy>
<route-policy>
<direction>out</direction>
<name>pass</name>
</route-policy>
<as-override/>
<default-originate/>
</unicast>
</ipv4>
</address-family>
</neighbor>
<address-family>
<ipv4>
<unicast>
<redistribute>
<connected/>
<static/>
</redistribute>
</unicast>
</ipv4>
</address-family>
<rd>1:{$VPNID}</rd>
</vrf>
</bgp-no-instance>
</bgp>
</router>
<interface xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<GigabitEthernet>
<id>{$INTERFACE}</id>
<ipv4>
<address>
<mask>255.255.255.252</mask>
<ip>{$PEIP}</ip>
</address>
</ipv4>
<vrf>vpn{$VPNID}</vrf>
</GigabitEthernet>
</interface>
<route-policy xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<name>pass</name>
<cmd>
<value>pass</value>
</cmd>
</route-policy>
</config>
</device>
</devices>
</config-template>
Lab 2
l2vpn.yang
module l2vpn {
namespace "http://com/example/l2vpn";
prefix l2vpn;
container id-database {
leaf start {
type uint32;
default 100;
}
leaf stop {
must "current() > ../start";
type uint32;
default 200;
}
leaf-list used-ids {
type uint32;
config false;
tailf:cdb-oper {
tailf:persistent true;
}
}
}
augment /ncs:services {
list l2vpn {
key name;
unique pw-id;
uses ncs:service-data;
ncs:servicepoint l2vpn-servicepoint;
leaf name {
tailf:info "Service Instance Name";
mandatory true;
type string;
}
leaf customer {
tailf:info "Customer Name";
mandatory true;
type leafref {
path "/ncs:customers/ncs:customer/ncs:id";
}
}
leaf pw-id {
tailf:info "Unique Pseudowire ID";
type uint32 {
range "1..4294967295";
}
list link {
tailf:info "Attachment Circuits";
min-elements 2;
max-elements 2;
key device;
leaf device {
tailf:info "PE Router";
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
container ios {
when
"deref(../device)/../ncs:module[1]/ncs:name='tailf-ned-cisco-
ios'" {
tailf:dependency "../device";
}
tailf:cli-drop-node-name;
leaf intf-number {
tailf:info "GigabitEthernet Interface ID";
mandatory true;
type leafref {
path
"deref(../../device)/../ncs:config/ios:interface/ios:GigabitEther
net/ios:name";
}
}
leaf loopback-interface {
tailf:info "Loopback Interface ID";
type string;
default "0";
}
}
container iosxr {
when
"deref(../device)/../ncs:module[1]/ncs:name='tailf-ned-cisco-ios-
xr'" {
tailf:dependency "../device";
}
tailf:cli-drop-node-name;
leaf intf-number {
tailf:info "GigabitEthernet Interface ID";
mandatory true;
type leafref {
path "deref(../../device)/../ncs:config/cisco-ios-
xr:interface/cisco-ios-xr:GigabitEthernet/cisco-ios-xr:id";
}
}
leaf loopback-interface {
tailf:info "Loopback Interface ID";
type string;
default "0";
}
}
leaf intf-number {
tailf:info "GigabitEthernet Interface ID";
mandatory true;
type leafref {
path
"deref(../../device)/../ncs:config/junos:configuration/junos:inte
rfaces/junos:interface/junos:name";
}
}
leaf loopback-interface {
tailf:info "Loopback Interface ID";
type string;
default "0";
}
}
*/
}
}
}
}
l2vpn.py
# -*- mode: python; python-indent: 4 -*-
import ncs
from ncs.application import Service
import ncs.template
import ncs.experimental
#import l2vpn_subscriber
import device_helper
import id_allocator
# ------------------------
# SERVICE CALLBACK EXAMPLE
# ------------------------
class ServiceCallbacks(Service):
vpn_name = service.name
customer = service.customer
if pw_id is None:
self.log.info('pw-id does not exit, waiting for
redeploy')
return proplist
"""
pw_id = None
service_path =
"/services/l2vpn[name='{}']".format(service.name)
self.log.info('Path: ', service_path)
id_allocator.id_request(service, service_path, 'pw-id',
tctx.username, vpn_name)
try:
if pw_id is None:
self.log.info('pw-id does not exit, waiting for
redeploy')
return proplist
links_data = []
for link in service.link:
link_data = {'device': link.device}
device_type = device_helper.get_device_type(root,
link.device)
self.log.info('Normalizing data for device {} of type
{}'
.format(link.device, device_type))
device_type_container = getattr(link,
device_helper.DEVICE_LOOKUP[device_type])
link_data['intf-number'] =
device_type_container.intf_number
link_data['loopback-address'] = \
device_helper.get_loopback_address(root,
link.device,
device_type,
device_type_container.loopback_interface)
links_data.append(link_data)
template = ncs.template.Template(service)
template.apply('l2vpn-template', tvars)
# ---------------------------------------------
# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
# ---------------------------------------------
class L2vpn(ncs.application.Application):
def __init__(self, *args, **kwargs):
self.sub = None
def setup(self):
# The application class sets up logging for us. Is is
accessible
# through 'self.log' and is a ncs.log.Log instance.
self.log.info('L2vpn RUNNING')
#self.sub =
l2vpn_subscriber.AllocatorSubscriber(app=self)
#self.sub.start()
def teardown(self):
# When the application is finished (which would happen if
NCS went
# down, packages were reloaded or some error occurred)
this teardown
# method will be called.
#self.sub.cleanup()
#self.sub.stop()
self.log.info('L2vpn FINISHED')
l2vpn-template.xml
<config-template xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{$DEVICE}</name>
<config>
<interface xmlns="urn:ios">
<GigabitEthernet>
<name>{$INTERFACE-ID}</name>
<xconnect tags="merge">
<encapsulation>mpls</encapsulation>
<vcid>{$PW-ID}</vcid>
<address>{$REMOTE-IP}</address>
</xconnect>
</GigabitEthernet>
</interface>
<l2vpn xmlns="http://tail-f.com/ned/cisco-ios-xr"
tags="merge">
<xconnect>
<group>
<name>{$CUSTOMER}</name>
<p2p>
<name>{$SERVICE}</name>
<neighbor>
<address>{$REMOTE-IP}</address>
<pw-id>{$PW-ID}</pw-id>
</neighbor>
<interface>
<name>GigabitEthernet{$INTERFACE-ID}</name>
</interface>
</p2p>
</group>
</xconnect>
</l2vpn>
<interface xmlns="http://tail-f.com/ned/cisco-ios-xr">
<GigabitEthernet>
<id>{$INTERFACE-ID}</id>
<l2transport tags="merge">
</l2transport>
</GigabitEthernet>
</interface>
</config>
</device>
</devices>
</config-template>
12vp_subscriber.py
# -*- mode: python; python-indent: 4 -*-
import ncs
import ncs.maapi
import ncs.experimental
import _ncs
import fake_external_allocator
# ------------------------------------------------
# SUBSCRIBER ITERATOR OBJECT
# ------------------------------------------------
class AllocatorSubscriber(ncs.experimental.Subscriber):
"""This subscriber subscribes to changes in the..."""
def init(self):
self.service_path = '/ncs:services/l2vpn:l2vpn'
self.register(self.service_path, priority=100)
return ncs.ITER_RECURSE
with ncs.maapi.single_write_trans('admin',
'system', db=ncs.OPERATIONAL) as t:
t.set_elem(_ncs.Value(allocated_id,
_ncs.C_UINT32), path)
t.apply()
fake_external_allocator.deallocate_id(request['value'])
self.log.info('Deallocated pwid ',
request['value'])
def cleanup(self):
pass
device_helper.py
TYPE_CISCO_IOS = '{tailf-ned-cisco-ios}'
TYPE_CISCO_IOSXR = '{tailf-ned-cisco-ios-xr}'
TYPE_JUNIPER_JUNOS = '{juniper-junos}'
DEVICE_LOOKUP = {
TYPE_CISCO_IOS: 'ios',
TYPE_CISCO_IOSXR: 'iosxr',
TYPE_JUNIPER_JUNOS: 'junos'
}
Arguments:
root -- Maagic CDB root
device -- name of the device
"""
modules = root.devices.device[device].module.keys()
return str(modules[0])
Arguments:
root -- Maagic CDB root
device -- name of the device
device_type -- device type (YANG module name)
loopback_interface -- Loopback interface ID
"""
device_config =
root.ncs__devices.ncs__device[device].ncs__config
address = None
if device_type == TYPE_CISCO_IOS:
loopback =
device_config.ios__interface.ios__Loopback[loopback_interface]
address = loopback.ip.address.primary.address
elif device_type == TYPE_CISCO_IOSXR:
loopback =
device_config.cisco_ios_xr__interface.Loopback[loopback_interface
]
address = loopback.ipv4.address.ip
elif device_type == TYPE_JUNIPER_JUNOS:
loopback =
device_config.junos__configuration.interfaces.interface[loopback_
interface]
address = loopback.unit['0'].family.inet.address[0].name
else:
raise Exception('Unknown device type ' + device_type)
return address
id_allocator.py
import ncs.template
import ncs.maagic
class ResourceException(Exception):
def __init__(self, value):
super(ResourceException, self).__init__()
self.value = value
def __str__(self):
return repr(self.value)
class ResourceError(ResourceException):
pass
class ResourceWait(ResourceException):
pass
Arguments:
root -- maagic node referencing the CDB root
service_path -- XPath to service instance (not keypath!)
pool_name -- name of pool to request from
username -- username to use when redeploying the requesting
service
allocation_id -- unique allocation id
"""
template = ncs.template.Template(root)
tvars = ncs.template.Variables()
tvars.add('POOL', pool_name)
tvars.add('ALLOCATIONID', allocation_id)
tvars.add('USERNAME', username)
tvars.add('SERVICE', service_path)
template.apply('id-allocation', tvars)
Arguments:
tctx -- transaction context
root -- maagic root
pool_name -- name of pool the request was created in
id -- unique allocation id
"""
if pool is None:
raise ResourceError('Pool {} does not
exist'.format(pool_name))
if allocation is None:
raise ResourceWait('Not ready')
if allocation.response.id:
return allocation.response.id
elif allocation.response.error:
raise ResourceError(allocation.response.error)
fake_external_allocator.py
#!/usr/bin/env python
import ncs.maapi
import ncs.maagic
def allocate_id():
with ncs.maapi.single_write_trans('admin', 'system') as t:
root = ncs.maagic.get_root(t)
db = root.l2vpn__id_database
try:
l = db.used_ids
if l is None: l = list()
allocated_id = [n for n in range(db.start, db.stop)
if n not in l][0]
l.append(allocated_id)
db.used_ids = l
t.apply()
return allocated_id
except IndexError:
raise Exception('Exhausted all IDs!')
def deallocate_id(i):
with ncs.maapi.single_write_trans('admin', 'system') as t:
root = ncs.maagic.get_root(t)
db = root.l2vpn__id_database
l = db.used_ids
if l is None: return
try:
l.remove(i)
db.used_ids = l
t.apply()
except ValueError:
pass
def main():
print 'Testing Fake External Allocator'
allocated_id = allocate_id()
print 'Allocated ID:', allocated_id
deallocate_id(allocated_id)
print 'Successfully deallocated ID!'
if __name__ == '__main__':
main()
Lab 3
cloud.yang
module cloud {
namespace "http://com/example/cloud";
prefix cloud;
augment /ncs:services {
list cloud {
description "This is the Cloud service";
key name;
leaf name {
type string;
}
uses ncs:service-data;
ncs:servicepoint cloud-servicepoint;
leaf device {
config false;
tailf:cdb-oper {
tailf:persistent true;
}
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
leaf tenant {
mandatory true;
type string;
}
leaf customer-network {
mandatory true;
type string;
}
leaf internet-network {
mandatory true;
type string;
}
container vrouter {
presence "vrouter";
uses vrouter:vrouter;
}
container vfirewall {
presence "vfirewall";
uses vfirewall:vfirewall;
}
tailf:action ping {
tailf:actionpoint cloud-ping;
input {
leaf ip {
mandatory true;
type inet:ipv4-address;
}
}
output {
leaf result {
type string;
}
}
}
}
}
}
vrouter.yang
module vrouter {
namespace "http://com/example/vrouter";
prefix vrouter;
grouping vrouter {
list routes {
key "network mask";
min-elements 1;
leaf network {
type inet:ipv4-address;
}
leaf mask {
type inet:ipv4-address;
}
leaf gateway {
mandatory true;
type inet:ipv4-address;
}
}
}
augment /ncs:services {
list vrouter {
description "This is a vRouter service";
key name;
leaf name {
type string;
}
uses ncs:service-data;
ncs:servicepoint vrouter-servicepoint;
leaf device {
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
uses vrouter;
}
}
}
vfirewall.yang
module vfirewall {
namespace "http://com/example/vfirewall";
prefix vfirewall;
grouping vfirewall {
list access-list-rules {
ordered-by user;
key name;
leaf name {
type string;
}
leaf action {
mandatory true;
type enumeration {
enum permit;
enum deny;
}
leaf protocol {
default ip;
type enumeration {
enum ip;
enum icmp;
enum tcp;
enum udp;
}
}
leaf src-ip {
mandatory true;
type inet:ipv4-address;
}
leaf src-mask {
mandatory true;
type inet:ipv4-address;
}
leaf src-port {
when "../protocol = 'tcp' or ../protocol = 'udp'";
default any;
type union {
type uint16;
type enumeration {
enum any;
}
}
}
leaf dest-ip {
mandatory true;
type inet:ipv4-address;
}
leaf dest-mask {
mandatory true;
type inet:ipv4-address;
}
leaf dest-port {
when "../protocol = 'tcp' or ../protocol = 'udp'";
default any;
type union {
type uint16;
type enumeration {
enum any;
}
}
}
}
}
augment /ncs:services {
list vfirewall {
uses ncs:service-data;
ncs:servicepoint vfirewall-servicepoint;
key name;
leaf name {
type string;
}
leaf device {
mandatory true;
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
uses vfirewall;
}
}
}
cloud.py
# -*- mode: python; python-indent: 4 -*-
import re
import ncs
from ncs.application import Service, PlanComponent
import ncs.template
from resource_management import ipaddress_allocator, vm_manager
from cloud_actions import PingAction
# ------------------------
# SERVICE CALLBACK EXAMPLE
# ------------------------
class ServiceCallbacks(Service):
plan_data = {'CSR':{'states':['mgmt-address-allocated',
'vnf-created'],
'reached_states': []}}
mgmt_ip = self._allocate_mgmt_address(tctx,
service,
service_path,
root,
service.name)
if not mgmt_ip:
self.log.info('Waiting for redeploy')
self._write_plan_data(service, plan_data)
return proplist
plan_data['CSR']['reached_states'].append('mgmt-address-
allocated')
self._set_hostname(root, device)
if service.vfirewall.exists():
self.log.info('vFirewall config exists, will create
vFirewall instance')
template = ncs.template.Template(service.vfirewall)
tvars = ncs.template.Variables()
tvars.add('NAME', service.name)
tvars.add('DEVICE', device)
template.apply('vfirewall-service-template', tvars)
if service.vrouter.exists():
self.log.info('vRouter config exists, will create
vRouter instance')
template = ncs.template.Template(service.vrouter)
tvars = ncs.template.Variables()
tvars.add('NAME', service.name)
tvars.add('DEVICE', device)
template.apply('vrouter-service-template', tvars)
self._write_plan_data(service, plan_data)
finished = True
for component, states in plan_data.iteritems():
if finished:
self_plan.set_reached('ncs:ready')
vm_manager.register_start_request(service, vm_name,
service_path)
device_name =
'{tenant}_{deployment_name}_{vm_group}_{esc}'\
.format(tenant=tenant,
deployment_name=deployment_name,
vm_group='CSR',
esc='esc0')
if vm_manager.device_ready(tctx, device_name):
return device_name
# ---------------------------------------------
# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
# ---------------------------------------------
class Cloud(ncs.application.Application):
def setup(self):
self.log.info('Cloud RUNNING')
self.register_service('cloud-servicepoint',
ServiceCallbacks)
self.register_action('cloud-ping', PingAction)
def teardown(self):
self.log.info('Cloud FINISHED')
cloud_actions.py
import ncs.maapi
import ncs.maagic
from ncs.dp import Action
class PingAction(Action):
@Action.action
def cb_action(self, uinfo, name, kp, action_input,
action_output):
self.log.info('action name: ', name)
self.log.info('action input.ip: ', action_input.ip)
with ncs.maapi.Maapi() as m:
with ncs.maapi.Session(m, uinfo.username,
uinfo.context):
tid = uinfo.actx_thandle
self.log.info(
'Action {} attaching to user {} tid
{}'.format(name,
uinfo.username,
tid))
t = m.attach(tid, usid=uinfo.usid)
ping_input = ping.get_input()
ping_input.args = [action_input.ip]
ping_output = ping(ping_input)
action_output.result = ping_output.result
m.detach(tid)
vm-manager-template.xml
<config xmlns="http://tail-f.com/ns/config/1.0">
<vm-manager xmlns="http://cisco.com/yang/nso/vm-manager"
tags="merge">
<start>
<name>{$NAME}</name>
<deployment-name>{$DEPNAME}</deployment-name>
<vm-device>{$ESC}</vm-device>
<tenant>{$TENANT}</tenant>
<vm-group>{$VM_GROUP}</vm-group>
<vm-type>{$VM_TYPE}</vm-type>
<flavor-id>{$FLAVOR_ID}</flavor-id>
<image-id>{$IMAGE_ID}</image-id>
<day0-url>http://198.18.134.4:8081/csr_config.txt</day0-
url>
<interface>
<id>0</id>
<name>mgmt</name>
<ip>{$MGMT-IP}</ip>
</interface>
<interface>
<id>1</id>
<name>{$CUSTOMER-NETWORK}</name>
</interface>
<interface>
<id>2</id>
<name>{$INTERNET-NETWORK}</name>
</interface>
<scaling-min>1</scaling-min>
<scaling-max>1</scaling-max>
</start>
</vm-manager>
</config>
vrouter-service-template.xml
<config xmlns="http://tail-f.com/ns/config/1.0">
<services xmlns="http://tail-f.com/ns/ncs">
<vrouter xmlns="http://com/example/vrouter">
<name>{$NAME}</name>
<device>{$DEVICE}</device>
<routes foreach="{/routes}">
<network>{network}</network>
<mask>{mask}</mask>
<gateway>{gateway}</gateway>
</routes>
</vrouter>
</services>
</config>
vfirewall-service-template.xml
<config xmlns="http://tail-f.com/ns/config/1.0">
<services xmlns="http://tail-f.com/ns/ncs">
<vfirewall xmlns="http://com/example/vfirewall">
<name>{$NAME}</name>
<device>{$DEVICE}</device>
<access-list-rules foreach="{/access-list-rules}">
<name>{name}</name>
<action>{action}</action>
<protocol>{protocol}</protocol>
<src-ip>{src-ip}</src-ip>
<src-mask>{src-mask}</src-mask>
<src-port>{src-port}</src-port>
<dest-ip>{dest-ip}</dest-ip>
<dest-mask>{dest-mask}</dest-mask>
<dest-port>{dest-port}</dest-port>
</access-list-rules>
</vfirewall>
</services>
</config>
vrouter-template.xml
<config-template xmlns="http://tail-f.com/ns/config/1.0"
servicepoint="vrouter-servicepoint">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{/device}</name>
<config>
<ip xmlns="urn:ios">
<route tags="merge">
<ip-route-forwarding-list
foreach="{routes}">
<prefix>{network}</prefix>
<mask>{mask}</mask>
<forwarding-
address>{gateway}</forwarding-address>
</ip-route-forwarding-list>
</route>
</ip>
</config>
</device>
</devices>
</config-template>
vfirewall.py
# -*- mode: python; python-indent: 4 -*-
import socket
import struct
import ncs
from ncs.application import Service
import ncs.template
class ServiceCallbacks(Service):
device = service.device
tvars.add('DEVICE', device)
tvars.add('ACCESS-LIST-NAME', acl_name)
tvars.add('INTERFACE-ID', acl_interface)
tvars.add('ACCESS-LIST-DIRECTION', acl_direction)
tvars.add('ACCESS-LIST-RULE',
self._build_acl_rule(rule))
template.apply('vfirewall-template', tvars)
protocol=rule.protocol,
src_ip=rule.src_ip,
src_mask=self._convert_to_wildcard(rule.src_mask),
src_port=src_port,
dest_ip=rule.dest_ip,
dest_mask=self._convert_to_wildcard(rule.dest_mask),
dest_port=dest_port)
# ---------------------------------------------
# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
# ---------------------------------------------
class Vfirewall(ncs.application.Application):
def setup(self):
# The application class sets up logging for us. Is is
accessible
# through 'self.log' and is a ncs.log.Log instance.
self.log.info('Vfirewall RUNNING')
def teardown(self):
self.log.info('Vfirewall FINISHED')
vfirewall-template.xml
<config xmlns="http://tail-f.com/ns/config/1.0">
<devices xmlns="http://tail-f.com/ns/ncs">
<device tags="nocreate">
<name>{$DEVICE}</name>
<config>
<interface xmlns="urn:ios">
<GigabitEthernet>
<name>{$INTERFACE-ID}</name>
<ip tags="merge">
<access-group>
<direction>{$ACCESS-
LIST-DIRECTION}</direction>
<access-
list>{$ACCESS-LIST-NAME}</access-list>
</access-group>
</ip>
</GigabitEthernet>
</interface>
<ip xmlns="urn:ios">
<access-list tags="merge">
<extended>
<ext-named-acl>
<name>{$ACCESS-LIST-
NAME}</name>
<ext-access-list-
rule>
<rule>{$ACCESS-
LIST-RULE}</rule>
</ext-access-list-
rule>
</ext-named-acl>
</extended>
</access-list>
</ip>
</config>
</device>
</devices>
</config>