L2VPN version4
변경사항
- 필수 변수
- pw-id
- vlan-id
- endpoint 별 device
- device 별 neighbor ip 및 적용 interface
- 공통변수
- p2p-connection-name = (group-name, pw-class-name, p2p-name)
- vlan-id = (subinterface-id)
- 기존
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
- 현재
Value for 'pw-id' (<unsignedInt>): 1030 Value for 'vlan-id' (<unsignedShort>): 100 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-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 root@ncs(config-p2p-connection-exam)# commit
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; }
leaf control-word { type boolean; default "false"; }
leaf group-name { type string; }
leaf vlan-id {
type int32 {
range "1..4000";
}
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; }
leaf p2p-name { type string; }
leaf mtu { type uint16; }
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 int32 { range "1..4000"; }
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(root, connection, endpoint_a)
template.apply('l2vpn-template', tvars_a)
tvars_b = self.get_tvars(root, connection, endpoint_b)
template.apply('l2vpn-template', tvars_b)
def get_tvars(self, root, conn, local_ep):
tvars = ncs.template.Variables()
tvars.add('PW_ID', conn.pw_id)
tvars.add('PW_CLASS_NAME', conn.connection_name)
tvars.add('CONTROL_WORD_ENABLED', 'true' if conn.control_word else 'false')
tvars.add('XCONNECT_GROUP_NAME', conn.connection_name)
tvars.add('PW_CLASS_REF', conn.connection_name)
tvars.add('L2_SERV_VLAN_ID', conn.vlan_id)
device_name = local_ep.device
iface_type_str = str(local_ep.interface_type)
iface_main_id = local_ep.interface_main_id
full_if_name = f"{iface_type_str}{iface_main_id}"
tvars.add('DEVICE', device_name)
mtu_value = 1522
try:
self.log.info(f"Attempting to read MTU for {device_name}:{full_if_name}")
interface_config = root.devices.device[device_name].config.interface
phy_if = interface_config[iface_type_str][iface_main_id]
if phy_if.mtu is not None:
mtu_value = phy_if.mtu
self.log.info(f"SUCCESS: Read existing MTU {mtu_value} from {device_name}:{full_if_name}")
else:
self.log.info(f"INFO: MTU not configured on {device_name}:{full_if_name}. Using default {mtu_value}.")
except Exception as e:
self.log.info(f"ERROR: Could not read MTU for {device_name}:{full_if_name}. Using default {mtu_value}. Exception: {e}")
full_subif_name = f"{local_ep.interface_type}{local_ep.interface_main_id}.{conn.vlan_id}"
tvars.add('INTERFACE_TYPE', iface_type_str)
tvars.add('INTERFACE_ID', f"{iface_main_id}.{conn.vlan_id}")
tvars.add('FULL_INTERFACE_NAME', full_subif_name)
tvars.add('MTU', mtu_value)
tvars.add('L2_SERV_ENCAP', 'dot1q')
tvars.add('P2P_NAME', conn.connection_name)
tvars.add('NEIGHBOR_IP', local_ep.pe_neighbor_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>