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>