L2VPN Package Prototype
ncs-make-package --service-skeleton python-and-template --component-class l2vpn.L2vpn l2vpn
토폴로지
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
