L2VPN Package Prototype
template
<config-template 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> <GigabitEthernet> <id>{$MAIN_INTERFACE_NAME}</id> <mode>l2transport</mode> <mtu>{$MTU}</mtu> <encapsulation> <default ncs:when="{$L2_SERV_ENCAP = 'default'}"/> <dot1q ncs:when="{$L2_SERV_ENCAP = 'dot1q'}"> <vlan-id ncs:when="{$L2_SERV_VLAN_ID != 0}">{$L2_SERV_VLAN_ID}</vlan-id> </dot1q> <untagged ncs:when="{$L2_SERV_ENCAP = 'untagged'}"/> </encapsulation> <rewrite ncs:when="{$L2_SERV_ENCAP = 'dot1q' and $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>symmetric</mode> </tag> </ingress> </rewrite> </GigabitEthernet> </GigabitEthernet-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-template>
python
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(service='{service._path}')") device_name = service.device full_interface_name = service.local_l2transport_interface.interface_name mtu = service.local_l2transport_interface.mtu encapsulation_type = service.local_l2transport_interface.encapsulation_type main_interface_name = full_interface_name if full_interface_name.startswith("GigabitEthernet"): main_interface_name = full_interface_name[len("GigabitEthernet"):] l2_serv_vlan_id = 0 if encapsulation_type == 'dot1q': l2_serv_vlan_id = service.local_l2transport_interface.vlan_id l2_serv_ingress_rewrite_type = 'none' l2_serv_ingress_rewrite_vlan = 0 if encapsulation_type == 'dot1q' and hasattr(service.local_l2transport_interface, 'rewrite_policy'): rewrite_policy = service.local_l2transport_interface.rewrite_policy l2_serv_ingress_rewrite_type = rewrite_policy.rewrite_type if l2_serv_ingress_rewrite_type == 'translate1to1': if hasattr(rewrite_policy, 'rewrite_vlan_id'): l2_serv_ingress_rewrite_vlan = rewrite_policy.rewrite_vlan_id pw_class_name = service.pw_class.pw_class_name control_word_enabled_str = 'true' if service.pw_class.control_word else 'false' xconnect_group_name = service.xconnect.group_name p2p_name = service.xconnect.p2p_name neighbor_ip = service.xconnect.neighbor_ip pw_id = service.xconnect.pw_id pw_class_ref = service.xconnect.pw_class_ref tvars = ncs.template.Variables() tvars.add('DEVICE', device_name) tvars.add('FULL_INTERFACE_NAME', full_interface_name) tvars.add('MAIN_INTERFACE_NAME', main_interface_name) tvars.add('MTU', mtu) tvars.add('L2_SERV_ENCAP', encapsulation_type) tvars.add('L2_SERV_VLAN_ID', l2_serv_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) tvars.add('PW_CLASS_NAME', pw_class_name) tvars.add('CONTROL_WORD_ENABLED', control_word_enabled_str) tvars.add('XCONNECT_GROUP_NAME', xconnect_group_name) tvars.add('P2P_NAME', p2p_name) tvars.add('NEIGHBOR_IP', neighbor_ip) tvars.add('PW_ID', pw_id) tvars.add('PW_CLASS_REF', pw_class_ref) template = ncs.template.Template(service) template.apply('l2vpn-template', tvars) @Service.pre_modification def cb_pre_modification(self, tctx, op, kp, root, proplist): self.log.info(f"Service pre_modification(service='{kp}', op='{op}')") pass @Service.post_modification def cb_post_modification(self, tctx, op, kp, root, proplist): self.log.info(f"Service post_modification(service='{kp}', op='{op}')") pass 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')
yang
module l2vpn { namespace "http://example.com/l2vpn"; prefix l2vpn; import ietf-inet-types { prefix inet; } import tailf-ncs { prefix ncs; } description "L2VPN service model for Cisco IOS XR Router (xrv9k-fullk9-6.4.1). This model configures point-to-point L2VPN services including pseudowire class and xconnect group settings, along with associated l2transport interface parameters."; revision 2025-07-07 { description "Added 'default' encapsulation type based on device config XML."; } revision 2025-07-05 { description "Adjusted YANG model based on NSO 'show full-configuration | display xml' output for IOS XR NED. Modified interface, pw-class, and xconnect structures for accurate mapping."; } revision 2025-07-04 { description "Updated revision to include l2transport interface parameters (interface-name, mtu) and align with corrected Python and XML template logic, and fix compilation errors."; } list l2vpn { key name; description "List of L2VPN service instances."; uses ncs:service-data; leaf name { type string; description "Unique name for this L2VPN service instance."; } leaf device { type leafref { path "/ncs:devices/ncs:device/ncs:name"; } mandatory true; description "The target device on which to configure the L2VPN."; } container local-l2transport-interface { description "Configuration for the local L2 transport interface."; leaf interface-name { type string; // e.g., GigabitEthernet0/0/0/0.1 mandatory true; description "The full name of the local interface including subinterface ID (e.g., GigabitEthernet0/0/0/0.1)."; } leaf mtu { type uint16; default 1522; description "MTU for the L2 transport interface."; } 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)."; } leaf vlan-id { when "../encapsulation-type = 'dot1q'"; type uint16; mandatory true; description "VLAN ID for dot1q encapsulation."; } container rewrite-policy { when "../encapsulation-type = 'dot1q'"; description "Ingress rewrite policy settings."; leaf rewrite-type { type enumeration { enum "none" { description "No rewrite."; } enum "pop1" { description "Pop 1 VLAN tag."; } enum "translate1to1" { description "Translate 1-to-1 VLAN tag."; } } default "none"; description "Type of ingress rewrite operation."; } leaf rewrite-vlan-id { when "../rewrite-type = 'translate1to1'"; type uint16; mandatory true; description "VLAN ID for 1-to-1 translate rewrite."; } } } container pw-class { description "Pseudowire class configuration for the L2VPN."; leaf pw-class-name { type string { pattern "[a-zA-Z0-9_-]+"; } mandatory true; description "Name of the pseudowire class."; } leaf control-word { type boolean; default "false"; description "Enable or disable control-word for the pseudowire."; } } container xconnect { description "Xconnect group and point-to-point connection configuration."; leaf group-name { type string { pattern "[a-zA-Z0-9_-]+"; } mandatory true; description "Name of the xconnect group (e.g., Provisioned)."; } leaf p2p-name { type string { pattern "[a-zA-Z0-9_-]+"; } mandatory true; description "Name of the point-to-point connection (e.g., epl1)."; } leaf neighbor-ip { type inet:ipv4-address; mandatory true; description "Neighbor IPv4 address for the pseudowire (e.g., 10.10.10.10)."; } leaf pw-id { type uint32 { range "1..65535"; } mandatory true; description "Pseudowire ID (e.g., 13)."; } leaf pw-class-ref { type leafref { path "../../pw-class/pw-class-name"; } mandatory true; description "Reference to the pseudowire class to be used for this xconnect."; } } ncs:servicepoint "l2vpn-servicepoint"; } }
package
<ncs-package xmlns="http://tail-f.com/ns/ncs-packages"> <name>l2vpn</name> <package-version>1.0</package-version> <description>Generated Python package</description> <ncs-min-version>6.5</ncs-min-version> <component> <name>l2vpn</name> <application> <python-class-name>l2vpn.l2vpn.L2vpn</python-class-name> </application> </component> </ncs-package>
설정과정
root@ncs(config)# devices sync-from sync-result { device R1 result true } sync-result { device R2 result true } sync-result { device R3 result true } root@ncs(config)# l2vpn my-l2vpn-r1-pe Value for 'device' [R1,R2,R3]: R1 Value for 'local-l2transport-interface interface-name' (<string>): GigabitEthernet0/0/0/1.1 Value for 'local-l2transport-interface encapsulation-type' [default,dot1q,untagged]: default Value for 'pw-class pw-class-name' (<string>): PWClass_13_30-30-30-30 Value for 'xconnect group-name' (<string>): L2VPN_Group Value for 'xconnect p2p-name' (<string>): P2P_R1_R3 Value for 'xconnect neighbor-ip' (<IPv4 address>): 30.30.30.30 Value for 'xconnect pw-id' (<unsignedInt, 1 .. 65535>): 13 Value for 'xconnect pw-class-ref' [PWClass_13_30-30-30-30]: PWClass_13_30-30-30-30 root@ncs(config-l2vpn-my-l2vpn-r1-pe)# commit dry-run cli { local-node { data devices { device R1 { config { l2vpn { + pw-class PWClass_13_30-30-30-30 { + encapsulation { + mpls { + } + } + } xconnect { + group L2VPN_Group { + p2p P2P_R1_R3 { + interface GigabitEthernet0/0/0/1.1; + neighbor 30.30.30.30 13 { + ip-version ipv4; + pw-class PWClass_13_30-30-30-30; + } + } + } } } } } } +l2vpn my-l2vpn-r1-pe { + device R1; + local-l2transport-interface { + interface-name GigabitEthernet0/0/0/1.1; + encapsulation-type default; + } + pw-class { + pw-class-name PWClass_13_30-30-30-30; + } + xconnect { + group-name L2VPN_Group; + p2p-name P2P_R1_R3; + neighbor-ip 30.30.30.30; + pw-id 13; + pw-class-ref PWClass_13_30-30-30-30; + } +} } } root@ncs(config-l2vpn-my-l2vpn-r1-pe)# commit