l2vpn version3
l2vpn.yang
module l2vpn {
namespace "http://example.com/l2vpn";
prefix l2vpn;
import ietf-inet-types { prefix inet; }
import tailf-ncs { prefix ncs; }
description "A service for managing multiple L2VPN point-to-point connections.";
revision 2025-08-06 {
description "Refactored to manage a list of P2P connections using xconnect.";
}
list l2vpn {
key name;
uses ncs:service-data;
ncs:servicepoint "l2vpn-servicepoint";
leaf name {
type string;
description "Unique name for this L2VPN service container.";
}
list p2p-connection {
key "connection-name";
description "A list of individual P2P connections.";
leaf connection-name { type string; }
leaf pw-id { type uint32; mandatory true; }
leaf pw-class-name { type string; mandatory true; }
leaf control-word { type boolean; default "false"; }
leaf group-name { type string; mandatory true; }
leaf vlan-id { type uint16; mandatory true; }
container endpoint-A {
uses L2VPN-ENDPOINT;
}
container endpoint-B {
uses L2VPN-ENDPOINT;
}
}
}
grouping L2VPN-ENDPOINT {
leaf device {
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
mandatory true;
}
leaf pe-neighbor-ip {
type inet:ipv4-address;
mandatory true;
description "The IP address of this PE router for peering (e.g., Loopback IP).";
}
leaf interface-type {
type enumeration {
enum "GigabitEthernet";
enum "TenGigE";
enum "HundredGigE";
}
mandatory true;
}
leaf interface-main-id { type string; mandatory true; }
leaf subinterface-id { type uint32; mandatory true; }
leaf p2p-name { type string; mandatory true; }
leaf mtu { type uint16; default 1522; }
container rewrite-policy {
leaf rewrite-type {
type enumeration {
enum "none";
enum "pop1";
enum "translate1to1";
}
default "none";
}
leaf rewrite-vlan-id {
when "../rewrite-type = 'translate1to1'";
type uint16 { range "1..4094"; }
mandatory true;
}
}
}
}
l2vpn.py
import ncs
from ncs.application import Service, Application
class ServiceCallbacks(Service):
@Service.create
def cb_create(self, tctx, root, service, proplist):
template = ncs.template.Template(service)
for connection in service.p2p_connection:
endpoint_a = connection.endpoint_A
endpoint_b = connection.endpoint_B
tvars_a = self.get_tvars(connection, endpoint_a, endpoint_b.pe_neighbor_ip)
template.apply('l2vpn-template', tvars_a)
tvars_b = self.get_tvars(connection, endpoint_b, endpoint_a.pe_neighbor_ip)
template.apply('l2vpn-template', tvars_b)
def get_tvars(self, conn, local_ep, remote_ip):
tvars = ncs.template.Variables()
tvars.add('PW_ID', conn.pw_id)
tvars.add('PW_CLASS_NAME', conn.pw_class_name)
tvars.add('CONTROL_WORD_ENABLED', 'true' if conn.control_word else 'false')
tvars.add('XCONNECT_GROUP_NAME', conn.group_name)
tvars.add('PW_CLASS_REF', conn.pw_class_name)
tvars.add('L2_SERV_VLAN_ID', conn.vlan_id)
tvars.add('DEVICE', local_ep.device)
full_if_name = f"{local_ep.interface_type}{local_ep.interface_main_id}.{local_ep.subinterface_id}"
tvars.add('INTERFACE_TYPE', local_ep.interface_type)
tvars.add('INTERFACE_ID', f"{local_ep.interface_main_id}.{local_ep.subinterface_id}")
tvars.add('FULL_INTERFACE_NAME', full_if_name)
tvars.add('MTU', local_ep.mtu)
tvars.add('L2_SERV_ENCAP', 'dot1q')
tvars.add('P2P_NAME', local_ep.p2p_name)
tvars.add('NEIGHBOR_IP', remote_ip)
l2_serv_ingress_rewrite_type = 'none'
l2_serv_ingress_rewrite_vlan = 0
if hasattr(local_ep, 'rewrite_policy'):
rewrite_policy = local_ep.rewrite_policy
l2_serv_ingress_rewrite_type = rewrite_policy.rewrite_type
if l2_serv_ingress_rewrite_type == 'translate1to1' and hasattr(rewrite_policy, 'rewrite_vlan_id'):
l2_serv_ingress_rewrite_vlan = rewrite_policy.rewrite_vlan_id
tvars.add('L2_SERV_INGRESS_REWRITE_TYPE', l2_serv_ingress_rewrite_type)
tvars.add('L2_SERV_INGRESS_REWRITE_VLAN', l2_serv_ingress_rewrite_vlan)
return tvars
class L2vpn(ncs.application.Application):
def setup(self):
self.log.info('L2vpn RUNNING')
self.register_service('l2vpn-servicepoint', ServiceCallbacks)
def teardown(self):
self.log.info('L2vpn FINISHED')
l2vpn-template.xml
<config xmlns="http://tail-f.com/ns/config/1.0"
xmlns:ncs="http://tail-f.com/ns/ncs">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<name>{$DEVICE}</name>
<config>
<interface xmlns="http://tail-f.com/ned/cisco-ios-xr">
<GigabitEthernet-subinterface ncs:when="{$INTERFACE_TYPE = 'GigabitEthernet'}">
<GigabitEthernet>
<id>{$INTERFACE_ID}</id>
<mode>l2transport</mode>
<mtu>{$MTU}</mtu>
<encapsulation>
<dot1q>
<vlan-id>{$L2_SERV_VLAN_ID}</vlan-id>
</dot1q>
</encapsulation>
<rewrite ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE != 'none'}">
<ingress>
<tag>
<pop ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'pop1'}">1</pop>
<translate ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">1-to-1</translate>
<dot1q ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1' and $L2_SERV_INGRESS_REWRITE_VLAN != 0}">{$L2_SERV_INGRESS_REWRITE_VLAN}</dot1q>
<mode ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">symmetric</mode>
</tag>
</ingress>
</rewrite>
</GigabitEthernet>
</GigabitEthernet-subinterface>
<TenGigE-subinterface ncs:when="{$INTERFACE_TYPE = 'TenGigE'}">
<TenGigE>
<id>{$INTERFACE_ID}</id>
<mode>l2transport</mode>
<mtu>{$MTU}</mtu>
<encapsulation>
<dot1q>
<vlan-id>{$L2_SERV_VLAN_ID}</vlan-id>
</dot1q>
</encapsulation>
<rewrite ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE != 'none'}">
<ingress>
<tag>
<pop ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'pop1'}">1</pop>
<translate ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">1-to-1</translate>
<dot1q ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1' and $L2_SERV_INGRESS_REWRITE_VLAN != 0}">{$L2_SERV_INGRESS_REWRITE_VLAN}</dot1q>
<mode ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">symmetric</mode>
</tag>
</ingress>
</rewrite>
</TenGigE>
</TenGigE-subinterface>
<HundredGigE-subinterface ncs:when="{$INTERFACE_TYPE = 'HundredGigE'}">
<HundredGigE>
<id>{$INTERFACE_ID}</id>
<mode>l2transport</mode>
<mtu>{$MTU}</mtu>
<encapsulation>
<dot1q>
<vlan-id>{$L2_SERV_VLAN_ID}</vlan-id>
</dot1q>
</encapsulation>
<rewrite ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE != 'none'}">
<ingress>
<tag>
<pop ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'pop1'}">1</pop>
<translate ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">1-to-1</translate>
<dot1q ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1' and $L2_SERV_INGRESS_REWRITE_VLAN != 0}">{$L2_SERV_INGRESS_REWRITE_VLAN}</dot1q>
<mode ncs:when="{$L2_SERV_INGRESS_REWRITE_TYPE = 'translate1to1'}">symmetric</mode>
</tag>
</ingress>
</rewrite>
</HundredGigE>
</HundredGigE-subinterface>
</interface>
<l2vpn xmlns="http://tail-f.com/ned/cisco-ios-xr">
<pw-class>
<name>{$PW_CLASS_NAME}</name>
<encapsulation>
<mpls>
<control-word ncs:when="{$CONTROL_WORD_ENABLED = 'true'}"/>
</mpls>
</encapsulation>
</pw-class>
<xconnect>
<group>
<name>{$XCONNECT_GROUP_NAME}</name>
<p2p>
<name>{$P2P_NAME}</name>
<interface>
<name>{$FULL_INTERFACE_NAME}</name>
</interface>
<neighbor>
<address>{$NEIGHBOR_IP}</address>
<pw-id>{$PW_ID}</pw-id>
<ip-version>ipv4</ip-version>
<pw-class>{$PW_CLASS_REF}</pw-class>
</neighbor>
</p2p>
</group>
</xconnect>
</l2vpn>
</config>
</device>
</devices>
</config>
사용 방법
roo@ncs(config)# l2vpn exam root@ncs(config-l2vpn-exam)# p2p-connection VLAN10 Value for 'pw-id' (<unsignedInt>): 1010 Value for 'pw-class-name' (<string>): L2VPN-CLASS Value for 'group-name' (<string>): L2VPN-GROUP Value for 'vlan-id' (<unsignedShort>): 10 Value for 'endpoint-A device' [CE01,CE1,CE02,CE2,...]: R1 Value for 'endpoint-A pe-neighbor-ip' (<IPv4 address>): 10.10.10.10 Value for 'endpoint-A interface-type' [GigabitEthernet,HundredGigE,TenGigE]: GigabitEthernet Value for 'endpoint-A interface-main-id' (<string>): 0/0/0/2 Value for 'endpoint-A subinterface-id' (<unsignedInt>): 10 Value for 'endpoint-A p2p-name' (<string>): R1_to_R3_VLAN10 Value for 'endpoint-B device' [CE01,CE1,CE02,CE2,...]: R3 Value for 'endpoint-B pe-neighbor-ip' (<IPv4 address>): 30.30.30.30 Value for 'endpoint-B interface-type' [GigabitEthernet,HundredGigE,TenGigE]: GigabitEthernet Value for 'endpoint-B interface-main-id' (<string>): 0/0/0/2 Value for 'endpoint-B subinterface-id' (<unsignedInt>): 10 Value for 'endpoint-B p2p-name' (<string>): R3_to_R1_VLAN10 root@ncs(config-l2vpn-exam)# p2p-connection vlan20 Value for 'pw-id' (<unsignedInt>): 2020 Value for 'pw-class-name' (<string>): L2VPN-CLASS Value for 'group-name' (<string>): L2VPN-GROUP Value for 'vlan-id' (<unsignedShort>): 20 Value for 'endpoint-A device' [CE01,CE1,CE02,CE2,...]: R1 Value for 'endpoint-A pe-neighbor-ip' (<IPv4 address>): 10.10.10.10 Value for 'endpoint-A interface-type' [GigabitEthernet,HundredGigE,TenGigE]: GigabitEthernet Value for 'endpoint-A interface-main-id' (<string>): 0/0/0/2 Value for 'endpoint-A subinterface-id' (<unsignedInt>): 20 Value for 'endpoint-A p2p-name' (<string>): R1_to_R3_VLAN20 Value for 'endpoint-B device' [CE01,CE1,CE02,CE2,...]: R3 Value for 'endpoint-B pe-neighbor-ip' (<IPv4 address>): 30.30.30.30 Value for 'endpoint-B interface-type' [GigabitEthernet,HundredGigE,TenGigE]: GigabitEthernet Value for 'endpoint-B interface-main-id' (<string>): 0/0/0/2 Value for 'endpoint-B subinterface-id' (<unsignedInt>): 20 Value for 'endpoint-B p2p-name' (<string>): R3_to_R1_VLAN20
결과
xconnect group L2VPN-GROUP
p2p R1_to_R3_VLAN10
interface GigabitEthernet0/0/0/2.10
neighbor ipv4 30.30.30.30 pw-id 1010
pw-class L2VPN-CLASS
!
!
p2p R1_to_R3_VLAN20
interface GigabitEthernet0/0/0/2.20
neighbor ipv4 30.30.30.30 pw-id 2020
pw-class L2VPN-CLASS
!
!
!