module vpws {
namespace "http://com/example/vpws";
prefix vpws;
import ietf-inet-types {
prefix inet;
}
import tailf-ncs {
prefix ncs;
}
list vpws {
key name;
uses ncs:service-data;
ncs:servicepoint "vpws";
leaf name {
type string;
}
leaf instance_id {
type uint16;
}
leaf service_id {
type uint16;
}
list link {
key id;
leaf device {
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
leaf id {
type string;
}
leaf vlan_id {
type uint16;
}
leaf interface_type {
type enumeration {
enum GigabitEthernet;
enum TenGigabitEthernet;
}
}
leaf interface_number {
type string;
}
}
}
}
Above, the VPWS service is identified by the leaf “name” (using the key statement). The VPWS service has an instance_id, service_id, and a list of links. Each link is identified by the link ID, and contains a device, vlan_id, interface_type, and interface_number.
Notice that we can restrict the options for an interface_type by using an enumeration. In the CLI, we are only allowed to enter a type that was pre-specified as an enum:
Next, we create a template. I created this by generating the config in NCS and doing a dry-run commit outputted to XML.
To iterate over each item in a list, you use <?foreach {Xpath}?>.
We can also use processing instructions such as if/else statements. This can be used to handle multiple interface types. The issue is that we can’t use an XPath for the XML tag. For example, we cannot do this:
Instead, we can do an if statement. In this template, I do not add TenGig support for brevity. But it can easily be added using another corresponding if statement.
<config>
<interface xmlns="urn:ios">
<? if {interface_type='GigabitEthernet'}?>
<GigabitEthernet>
<name>{interface_number}</name>
In general, XPath statements are always placed in curly braces {}. There is an important thing to realize about XPath statements though. When these evaluate to a node, the XPath context for the template will change to that node. This can break your template in unexpected ways.
Notice that the l2vpn config uses {string(path)} syntax instead of just {path}. This is used because when the XPath statement resolves to a value, the XPath context does not change. If we don’t use this, the XPath context will change from root/link to just root/ when we get the /instance_id value. Then when we go to reference the interface_type later, we cannot find it under the root. We have lost our root/link context.
We make the package again and reload packages in NCS.
I then apply a new VPWS service:
vpws ONE
instance_id 100
service_id 101
link 1
device r1
vlan_id 100
interface_type GigabitEthernet
interface_number 1
However, it doesn’t look like it is working as expected. Where’s my interface as a member of the VPWS?
If I pipe the commit dry-run to debug template, I can see this context change happening, which is breaking my template.
First we start in the root context node, which is /vpws[name=’ONE’]. Then we iterate over the foreach, which puts our contex as the first link:
The context stays as-is, until we process a root node. We process /instance_id, which successfully finds the value. But notice that the next evaluation is now in the root context node. We’ve left our link context.
Then when we go to process the interface_type, it cannot be found at the root context:
As a side note, using this debug we can see how the template works for both XE and XR. NCS only generates config for the namespace that a device supports. IOS-XE does not support the ios-xr namespace, so NCS knows not to generate that config:
To fix the XPath context issue, we have a few options:
Use string() to evaluate a value instead of a node. This prevents a context switch.
Saving and switching context.
Using a string() is probably the easiest method. But let’s demonstrate the use switching contexts. First we save the context under the current link:
In the debug, we can see that this time the context switches back to the link context before we evaluate the link-specific leafs.
Improving the service
I’ve re-written this and made some improvements. First, the YANG module requires that the VPWS has two PEs. This breaks for a VPWS that has two interfaces on the same PE though. However, in that case, we don’t use an EVPN EVI anyways. So this service is only used for a VPWS between two separate PEs.
There is also a restriction on the VLAN ID (0-4094) and interface number (0/0/0/X).
module vpws-new {
namespace "http://com/example/vpwsnew";
prefix vpws-new;
import ietf-inet-types {
prefix inet;
}
import tailf-ncs {
prefix ncs;
}
list vpws-new {
must "count(pe-device) = 2" {
error-message "Must have two PEs for the service";
}
key name;
uses ncs:service-data;
ncs:servicepoint "vpws-new";
leaf name {
type string;
}
list pe-device {
key device-name;
leaf device-name {
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
}
container access-circuit {
leaf interface-type {
type enumeration {
enum GigabitEthernet;
}
}
leaf interface-num {
type string {
pattern "0/0/0/[0-9]*";
}
}
leaf vlan {
type uint16 {
range "0..4094";
}
}
}
}
leaf evi-num {
type uint16;
}
}
}
The XML template sets variables at the beginning when we are still at the root node context, to prevent the need to worry about switching contexts.