L2VPN version5
변경사항
- 필수 변수
- pw-id
- vlan-id
- endpoint device
- device neighbor ip 및 적용 interface
- group-name
- 공통변수
- p2p-connection-name = (pw-class-name, p2p-name)
- vlan-id = (subinterface-id)
- 서비스 구성 계층도
l2vpn <서비스 이름> ├─ group-name <그룹 이름> ├─ endpoint-A device <장비 이름> ├─ endpoint-A pe-neighbor-ip <IP 주소> ├─ endpoint-B device <장비 이름> └─ endpoint-B pe-neighbor-ip <IP 주소> │ └─ p2p-connection ├─ p2p-connection <연결 이름> │ ├─ pw-id <ID> │ ├─ vlan-id <ID> │ ├─ encapsulation-type <캡슐화 타입> │ ├─ endpoint-A interface <인터페이스 타입> <ID> │ └─ endpoint-B interface <인터페이스 타입> <ID> │ └─ p2p-connection <다른 연결 이름> ├─ pw-id <ID> ├─ vlan-id <ID> ├─ encapsulation-type <캡슐화 타입> ├─ endpoint-A interface <인터페이스 타입> <ID> └─ endpoint-B interface <인터페이스 타입> <ID> - 변경사항
- 기존에는 하나의 그룹에 여러 개가 불가능했지만 현재는 같은 그룹에 여러 P2P 추가 가능
- 기존
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
- 현재
root@ncs(config)# l2vpn test Value for 'group-name' (<string>): tt Value for 'endpoint-A device' [CE01,CE1,CE02,CE2,...]: R1 Value for 'endpoint-A pe-neighbor-ip' (<IPv4 address>): 30.30.30.30 Value for 'endpoint-B device' [CE01,CE1,CE02,CE2,...]: R3 Value for 'endpoint-B pe-neighbor-ip' (<IPv4 address>): 10.10.10.10 root@ncs(config-l2vpn-test)# p2p-connection test1 Value for 'pw-id' (<unsignedInt>): 1030 Value for 'vlan-id' (<int, 1 .. 4000>): 100 Value for 'encapsulation-type' [default,dot1q,untagged]: dot1q 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 interface-type' [GigabitEthernet,HundredGigE,TenGigE]: GigabitEthernet Value for 'endpoint-B interface-main-id' (<string>): 0/0/0/2 root@ncs(config-p2p-connection-test1)# commit Commit complete. root@ncs(config-p2p-connection-test1)# exit root@ncs(config-l2vpn-test)# p2p-connection test2 Value for 'pw-id' (<unsignedInt>): 1031 Value for 'vlan-id' (<int, 1 .. 4000>): 200 Value for 'encapsulation-type' [default,dot1q,untagged]: untagged 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 interface-type' [GigabitEthernet,HundredGigE,TenGigE]: GigabitEthernet Value for 'endpoint-B interface-main-id' (<string>): 0/0/0/2 root@ncs(config-p2p-connection-test2)# exit root@ncs(config-l2vpn-test)# p2p-connection test3 Value for 'pw-id' (<unsignedInt>): 1032 Value for 'vlan-id' (<int, 1 .. 4000>): 11 Value for 'encapsulation-type' [default,dot1q,untagged]: dot1q 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 interface-type' [GigabitEthernet,HundredGigE,TenGigE]: GigabitEthernet Value for 'endpoint-B interface-main-id' (<string>): 0/0/0/2
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-12 {
description "Refactored to define common endpoint data at the top level.";
}
grouping L2VPN-ENDPOINT-COMMON {
leaf device {
type leafref {
path "/ncs:devices/ncs:device/ncs:name";
}
mandatory true;
description "The PE device for this endpoint.";
}
leaf pe-neighbor-ip {
type inet:ipv4-address;
mandatory true;
description "The IP address of this PE router for peering (e.g., Loopback IP).";
}
}
grouping L2VPN-ENDPOINT-CONNECTION-DETAILS {
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;
}
}
}
list l2vpn {
key name;
uses ncs:service-data;
ncs:servicepoint "l2vpn-servicepoint";
leaf name {
type string;
description "Unique name for this L2VPN service container.";
}
leaf group-name {
type string;
mandatory true;
}
container endpoint-A {
uses L2VPN-ENDPOINT-COMMON;
}
container endpoint-B {
uses L2VPN-ENDPOINT-COMMON;
}
list p2p-connection {
key "connection-name";
description "A list of individual P2P connections between the two endpoints defined above.";
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 vlan-id {
type int32 {
range "1..4000";
}
mandatory true;
}
leaf encapsulation-type {
type enumeration {
enum "dot1q";
enum "untagged";
enum "default";
}
mandatory true;
description "Encapsulation type for the L2 transport interface (dot1q, untagged, or default).";
}
container endpoint-A {
uses L2VPN-ENDPOINT-CONNECTION-DETAILS;
}
container endpoint-B {
uses L2VPN-ENDPOINT-CONNECTION-DETAILS;
}
}
}
}
l2vpn.py
import ncs
from ncs.application import Service
class ServiceCallbacks(Service):
@Service.create
def cb_create(self, tctx, root, service, proplist):
self.log.info(f"Service create called for l2vpn: {service.name}")
template = ncs.template.Template(service)
for connection in service.p2p_connection:
self.log.info(f"Processing connection: {connection.connection_name}")
tvars_a = self.get_tvars(root, service, connection, 'A')
template.apply('l2vpn-template', tvars_a)
tvars_b = self.get_tvars(root, service, connection, 'B')
template.apply('l2vpn-template', tvars_b)
def get_tvars(self, root, service, conn, endpoint_id):
tvars = ncs.template.Variables()
if endpoint_id == 'A':
common_ep = service.endpoint_A
conn_ep = conn.endpoint_A
else:
common_ep = service.endpoint_B
conn_ep = conn.endpoint_B
tvars.add('XCONNECT_GROUP_NAME', service.group_name)
tvars.add('PW_ID', conn.pw_id)
tvars.add('CONTROL_WORD_ENABLED', 'true' if conn.control_word else 'false')
tvars.add('L2_SERV_VLAN_ID', conn.vlan_id)
tvars.add('L2_SERV_ENCAP', conn.encapsulation_type)
tvars.add('PW_CLASS_NAME', conn.pw_class_name if conn.pw_class_name else conn.connection_name)
tvars.add('P2P_NAME', conn_ep.p2p_name if conn_ep.p2p_name else conn.connection_name)
tvars.add('PW_CLASS_REF', conn_ep.p2p_name if conn_ep.p2p_name else conn.connection_name)
device_name = common_ep.device
tvars.add('DEVICE', device_name)
tvars.add('NEIGHBOR_IP', common_ep.pe_neighbor_ip)
iface_type_str = str(conn_ep.interface_type)
iface_main_id = conn_ep.interface_main_id
sub_id = conn_ep.subinterface_id if conn_ep.subinterface_id else conn.vlan_id
full_subif_name = f"{iface_type_str}{iface_main_id}.{sub_id}"
tvars.add('INTERFACE_TYPE', iface_type_str)
tvars.add('INTERFACE_ID', f"{iface_main_id}.{sub_id}")
tvars.add('FULL_INTERFACE_NAME', full_subif_name)
mtu_value = conn_ep.mtu if conn_ep.mtu else 1522
try:
full_if_name = f"{iface_type_str}{iface_main_id}"
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 value from service: {mtu_value}.")
except Exception as e:
self.log.info(f"WARN: Could not read device MTU for {device_name}:{full_if_name}. Using value from service: {mtu_value}. Exception: {e}")
tvars.add('MTU', mtu_value)
l2_serv_ingress_rewrite_type = 'none'
l2_serv_ingress_rewrite_vlan = 0
if hasattr(conn_ep, 'rewrite_policy'):
rewrite_policy = conn_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)
self.log.info(f"Generated TVARS for {device_name} ({endpoint_id}): {tvars}")
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>