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>