diff --git a/l3vpn_qinq_cfs/README b/backup/l3vpn_qinq_cfs/README
similarity index 100%
rename from l3vpn_qinq_cfs/README
rename to backup/l3vpn_qinq_cfs/README
diff --git a/backup/l3vpn_qinq_cfs/load-dir/l3vpn_qinq_cfs.fxs b/backup/l3vpn_qinq_cfs/load-dir/l3vpn_qinq_cfs.fxs
new file mode 100644
index 0000000..e7b49e3
Binary files /dev/null and b/backup/l3vpn_qinq_cfs/load-dir/l3vpn_qinq_cfs.fxs differ
diff --git a/l3vpn_qinq_cfs/package-meta-data.xml b/backup/l3vpn_qinq_cfs/package-meta-data.xml
similarity index 100%
rename from l3vpn_qinq_cfs/package-meta-data.xml
rename to backup/l3vpn_qinq_cfs/package-meta-data.xml
diff --git a/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/__init__.py b/backup/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/__init__.py
similarity index 100%
rename from l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/__init__.py
rename to backup/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/__init__.py
diff --git a/backup/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/main.py b/backup/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/main.py
new file mode 100644
index 0000000..51dc111
--- /dev/null
+++ b/backup/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/main.py
@@ -0,0 +1,83 @@
+# -*- mode: python; python-indent: 4 -*-
+import ncs
+from ncs.application import Service
+import requests
+import socket
+import struct
+
+class ServiceCallbacks(Service):
+
+ @Service.create
+ def cb_create(self, tctx, root, service, proplist):
+ self.log.info('Service create(service=', service._path, ')')
+
+ def cidr_to_netmask(cidr):
+ network, net_bits = cidr.split('/')
+ host_bits = 32 - int(net_bits)
+ netmask = socket.inet_ntoa(struct.pack('!I', (1 << 32) - (1 << host_bits)))
+ return network, netmask, net_bits
+
+ headers = {"Authorization":"Token e657404c0e1f57481ad0a06c293e8fe794e720ac", "Accept": "application/json", "Content-Type": "application/json"}
+ body = {}
+
+ vars = ncs.template.Variables()
+ template = ncs.template.Template(service)
+ for endpoint in service.endpoint:
+ # PE part
+ # Get an IP address from Netbox for the PE VPN interface
+ api_url="https://10.101.180.45/api/ipam/prefixes/1/available-ips/"
+ response=requests.post(api_url, headers=headers, json=body, verify=False)
+ self.log.info('Netbox response, IP address reservation: ', response.json())
+ ipv4_cidr = response.json()["address"]
+ self.log.info('CIDR from Netbox: ', ipv4_cidr)
+ ipv4_address, ipv4_mask, ipv4_cidr_mask = cidr_to_netmask(ipv4_cidr)
+ self.log.info('Address: ', ipv4_address)
+ self.log.info('Net Mask: ', ipv4_mask)
+ self.log.info('CIDR Mask: ', ipv4_cidr_mask)
+ vars.add('pe_ipv4_address', ipv4_address)
+ vars.add('pe_ipv4_mask', ipv4_mask)
+
+ # Get an Outer VLAN from Netbox. The VLAN group for outer VLAN's is #2.
+ api_url="https://10.101.180.45/api/ipam/vlan-groups/2/available-vlans/"
+ ovlan_name = "L3VPN-Outer-VLAN-"+str(endpoint.pe_device)+"-"+str(endpoint.id)
+ body = {"name": ovlan_name}
+ response=requests.post(api_url, headers=headers, json=body, verify=False)
+ self.log.info('Netbox response, Outer VLAN reservation: ', response.json())
+ ovlan_id = response.json()["vid"]
+ self.log.info('VLAN from Netbox: ', ovlan_id)
+ vars.add('ovlan', ovlan_id)
+
+ # Get an Inner VLAN from Netbox. The VLAN group for inner VLAN's is #3.
+ api_url="https://10.101.180.45/api/ipam/vlan-groups/3/available-vlans/"
+ ivlan_name = "L3VPN-Inner-VLAN-"+str(endpoint.pe_device)+"-"+str(endpoint.id)
+ body = {"name": ivlan_name}
+ response=requests.post(api_url, headers=headers, json=body, verify=False)
+ self.log.info('Netbox response, Inner VLAN reservation: ', response.json())
+ ivlan_id = response.json()["vid"]
+ self.log.info('VLAN from Netbox: ', ivlan_id)
+ vars.add('ivlan', ivlan_id)
+
+ name = "L3VPN-"+str(endpoint.pe_device)+"-"+str(endpoint.id)
+ vars.add('name', name)
+ self.log.info('Name: ', name)
+ vars.add('pe_device', endpoint.pe_device)
+ vars.add('cpe_interface_address', endpoint.cpe_interface.ipv4_address)
+ self.log.info('cpe_interface_address: ', endpoint.cpe_interface.ipv4_address)
+ vars.add('pe_interface_name', endpoint.pe_interface.interface_name)
+ vars.add('pe_interface_number', endpoint.pe_interface.interface_number)
+ pe_interface_description = "L3VPN interface to " + str(endpoint.cpe_device) + ", interface " + str(endpoint.cpe_interface.interface_name) + str(endpoint.cpe_interface.interface_number)
+ vars.add('pe_interface_description', pe_interface_description)
+ self.log.info('pe_interface_description: ', pe_interface_description)
+ # vars.add('pe_ipv4_address', endpoint.pe_interface.ipv4_address)
+ # vars.add('pe_ipv4_mask', endpoint.pe_interface.ipv4_mask)
+ self.log.info('ipv4_mask: ', ipv4_mask)
+ template.apply('l3vpn_qinq_pe-template', vars)
+
+
+class Main(ncs.application.Application):
+ def setup(self):
+ self.log.info('Main RUNNING')
+ self.register_service('l3vpn_qinq_cfs-servicepoint', ServiceCallbacks)
+
+ def teardown(self):
+ self.log.info('Main FINISHED')
diff --git a/l3vpn_qinq_cfs/src/Makefile b/backup/l3vpn_qinq_cfs/src/Makefile
similarity index 100%
rename from l3vpn_qinq_cfs/src/Makefile
rename to backup/l3vpn_qinq_cfs/src/Makefile
diff --git a/l3vpn_qinq_cfs/src/yang/l3vpn_qinq_cfs.yang b/backup/l3vpn_qinq_cfs/src/yang/l3vpn_qinq_cfs.yang
similarity index 98%
rename from l3vpn_qinq_cfs/src/yang/l3vpn_qinq_cfs.yang
rename to backup/l3vpn_qinq_cfs/src/yang/l3vpn_qinq_cfs.yang
index 2c88ae2..38831fe 100644
--- a/l3vpn_qinq_cfs/src/yang/l3vpn_qinq_cfs.yang
+++ b/backup/l3vpn_qinq_cfs/src/yang/l3vpn_qinq_cfs.yang
@@ -107,7 +107,7 @@ module l3vpn_qinq_cfs {
type inet:ipv4-address;
}
- leaf ovlan {
+ /* leaf ovlan {
tailf:info "Outer VLAN eg 10";
type uint16;
}
@@ -115,7 +115,7 @@ module l3vpn_qinq_cfs {
leaf ivlan {
tailf:info "Inner VLAN eg 20";
type uint16;
- }
+ } */
container pe_interface {
uses interface_grouping;
diff --git a/l3vpn_qinq_cfs/templates/l3vpn_qinq_cfs-template.xml b/backup/l3vpn_qinq_cfs/templates/l3vpn_qinq_cfs-template.xml
similarity index 100%
rename from l3vpn_qinq_cfs/templates/l3vpn_qinq_cfs-template.xml
rename to backup/l3vpn_qinq_cfs/templates/l3vpn_qinq_cfs-template.xml
diff --git a/l3vpn_qinq_cfs/templates/l3vpn_qinq_pe-template.xml b/backup/l3vpn_qinq_cfs/templates/l3vpn_qinq_pe-template.xml
similarity index 92%
rename from l3vpn_qinq_cfs/templates/l3vpn_qinq_pe-template.xml
rename to backup/l3vpn_qinq_cfs/templates/l3vpn_qinq_pe-template.xml
index 76cb226..77b7130 100644
--- a/l3vpn_qinq_cfs/templates/l3vpn_qinq_pe-template.xml
+++ b/backup/l3vpn_qinq_cfs/templates/l3vpn_qinq_pe-template.xml
@@ -6,8 +6,8 @@
{/as_number}
{/customer_as_number}
{$cpe_interface_address}
- {/ovlan}
- {/ivlan}
+ {$ovlan}
+ {$ivlan}
{$pe_interface_name}
{$pe_interface_number}
diff --git a/l3vpn_qinq_cfs/test/Makefile b/backup/l3vpn_qinq_cfs/test/Makefile
similarity index 100%
rename from l3vpn_qinq_cfs/test/Makefile
rename to backup/l3vpn_qinq_cfs/test/Makefile
diff --git a/l3vpn_qinq_cfs/test/internal/Makefile b/backup/l3vpn_qinq_cfs/test/internal/Makefile
similarity index 100%
rename from l3vpn_qinq_cfs/test/internal/Makefile
rename to backup/l3vpn_qinq_cfs/test/internal/Makefile
diff --git a/l3vpn_qinq_cfs/test/internal/lux/Makefile b/backup/l3vpn_qinq_cfs/test/internal/lux/Makefile
similarity index 100%
rename from l3vpn_qinq_cfs/test/internal/lux/Makefile
rename to backup/l3vpn_qinq_cfs/test/internal/lux/Makefile
diff --git a/l3vpn_qinq_cfs/test/internal/lux/service/Makefile b/backup/l3vpn_qinq_cfs/test/internal/lux/service/Makefile
similarity index 100%
rename from l3vpn_qinq_cfs/test/internal/lux/service/Makefile
rename to backup/l3vpn_qinq_cfs/test/internal/lux/service/Makefile
diff --git a/l3vpn_qinq_cfs/test/internal/lux/service/dummy-device.xml b/backup/l3vpn_qinq_cfs/test/internal/lux/service/dummy-device.xml
similarity index 100%
rename from l3vpn_qinq_cfs/test/internal/lux/service/dummy-device.xml
rename to backup/l3vpn_qinq_cfs/test/internal/lux/service/dummy-device.xml
diff --git a/l3vpn_qinq_cfs/test/internal/lux/service/dummy-service.xml b/backup/l3vpn_qinq_cfs/test/internal/lux/service/dummy-service.xml
similarity index 100%
rename from l3vpn_qinq_cfs/test/internal/lux/service/dummy-service.xml
rename to backup/l3vpn_qinq_cfs/test/internal/lux/service/dummy-service.xml
diff --git a/l3vpn_qinq_cfs/test/internal/lux/service/pyvm.xml b/backup/l3vpn_qinq_cfs/test/internal/lux/service/pyvm.xml
similarity index 100%
rename from l3vpn_qinq_cfs/test/internal/lux/service/pyvm.xml
rename to backup/l3vpn_qinq_cfs/test/internal/lux/service/pyvm.xml
diff --git a/l3vpn_qinq_cfs/test/internal/lux/service/run.lux b/backup/l3vpn_qinq_cfs/test/internal/lux/service/run.lux
similarity index 100%
rename from l3vpn_qinq_cfs/test/internal/lux/service/run.lux
rename to backup/l3vpn_qinq_cfs/test/internal/lux/service/run.lux
diff --git a/l3vpn_qinq_cpe_rfs/README b/backup/l3vpn_qinq_cpe_rfs/README
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/README
rename to backup/l3vpn_qinq_cpe_rfs/README
diff --git a/l3vpn_qinq_cpe_rfs/package-meta-data.xml b/backup/l3vpn_qinq_cpe_rfs/package-meta-data.xml
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/package-meta-data.xml
rename to backup/l3vpn_qinq_cpe_rfs/package-meta-data.xml
diff --git a/l3vpn_qinq_cpe_rfs/python/l3vpn_qinq_cpe_rfs/__init__.py b/backup/l3vpn_qinq_cpe_rfs/python/l3vpn_qinq_cpe_rfs/__init__.py
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/python/l3vpn_qinq_cpe_rfs/__init__.py
rename to backup/l3vpn_qinq_cpe_rfs/python/l3vpn_qinq_cpe_rfs/__init__.py
diff --git a/l3vpn_qinq_cpe_rfs/python/l3vpn_qinq_cpe_rfs/main.py b/backup/l3vpn_qinq_cpe_rfs/python/l3vpn_qinq_cpe_rfs/main.py
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/python/l3vpn_qinq_cpe_rfs/main.py
rename to backup/l3vpn_qinq_cpe_rfs/python/l3vpn_qinq_cpe_rfs/main.py
diff --git a/l3vpn_qinq_cpe_rfs/src/Makefile b/backup/l3vpn_qinq_cpe_rfs/src/Makefile
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/src/Makefile
rename to backup/l3vpn_qinq_cpe_rfs/src/Makefile
diff --git a/l3vpn_qinq_cpe_rfs/src/yang/l3vpn_qinq_cpe_rfs.yang b/backup/l3vpn_qinq_cpe_rfs/src/yang/l3vpn_qinq_cpe_rfs.yang
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/src/yang/l3vpn_qinq_cpe_rfs.yang
rename to backup/l3vpn_qinq_cpe_rfs/src/yang/l3vpn_qinq_cpe_rfs.yang
diff --git a/l3vpn_qinq_cpe_rfs/templates/l3vpn_qinq_cpe_rfs-template.xml b/backup/l3vpn_qinq_cpe_rfs/templates/l3vpn_qinq_cpe_rfs-template.xml
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/templates/l3vpn_qinq_cpe_rfs-template.xml
rename to backup/l3vpn_qinq_cpe_rfs/templates/l3vpn_qinq_cpe_rfs-template.xml
diff --git a/l3vpn_qinq_cpe_rfs/test/Makefile b/backup/l3vpn_qinq_cpe_rfs/test/Makefile
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/test/Makefile
rename to backup/l3vpn_qinq_cpe_rfs/test/Makefile
diff --git a/l3vpn_qinq_cpe_rfs/test/internal/Makefile b/backup/l3vpn_qinq_cpe_rfs/test/internal/Makefile
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/test/internal/Makefile
rename to backup/l3vpn_qinq_cpe_rfs/test/internal/Makefile
diff --git a/l3vpn_qinq_cpe_rfs/test/internal/lux/Makefile b/backup/l3vpn_qinq_cpe_rfs/test/internal/lux/Makefile
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/test/internal/lux/Makefile
rename to backup/l3vpn_qinq_cpe_rfs/test/internal/lux/Makefile
diff --git a/l3vpn_qinq_cpe_rfs/test/internal/lux/service/Makefile b/backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/Makefile
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/test/internal/lux/service/Makefile
rename to backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/Makefile
diff --git a/l3vpn_qinq_cpe_rfs/test/internal/lux/service/dummy-device.xml b/backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/dummy-device.xml
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/test/internal/lux/service/dummy-device.xml
rename to backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/dummy-device.xml
diff --git a/l3vpn_qinq_cpe_rfs/test/internal/lux/service/dummy-service.xml b/backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/dummy-service.xml
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/test/internal/lux/service/dummy-service.xml
rename to backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/dummy-service.xml
diff --git a/l3vpn_qinq_cpe_rfs/test/internal/lux/service/pyvm.xml b/backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/pyvm.xml
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/test/internal/lux/service/pyvm.xml
rename to backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/pyvm.xml
diff --git a/l3vpn_qinq_cpe_rfs/test/internal/lux/service/run.lux b/backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/run.lux
similarity index 100%
rename from l3vpn_qinq_cpe_rfs/test/internal/lux/service/run.lux
rename to backup/l3vpn_qinq_cpe_rfs/test/internal/lux/service/run.lux
diff --git a/l3vpn_qinq_pe_rfs/README b/backup/l3vpn_qinq_pe_rfs/README
similarity index 100%
rename from l3vpn_qinq_pe_rfs/README
rename to backup/l3vpn_qinq_pe_rfs/README
diff --git a/l3vpn_qinq_pe_rfs/load-dir/l3vpn_qinq_pe_rfs.fxs b/backup/l3vpn_qinq_pe_rfs/load-dir/l3vpn_qinq_pe_rfs.fxs
similarity index 100%
rename from l3vpn_qinq_pe_rfs/load-dir/l3vpn_qinq_pe_rfs.fxs
rename to backup/l3vpn_qinq_pe_rfs/load-dir/l3vpn_qinq_pe_rfs.fxs
diff --git a/l3vpn_qinq_pe_rfs/package-meta-data.xml b/backup/l3vpn_qinq_pe_rfs/package-meta-data.xml
similarity index 100%
rename from l3vpn_qinq_pe_rfs/package-meta-data.xml
rename to backup/l3vpn_qinq_pe_rfs/package-meta-data.xml
diff --git a/l3vpn_qinq_pe_rfs/python/l3vpn_qinq_pe_rfs/__init__.py b/backup/l3vpn_qinq_pe_rfs/python/l3vpn_qinq_pe_rfs/__init__.py
similarity index 100%
rename from l3vpn_qinq_pe_rfs/python/l3vpn_qinq_pe_rfs/__init__.py
rename to backup/l3vpn_qinq_pe_rfs/python/l3vpn_qinq_pe_rfs/__init__.py
diff --git a/l3vpn_qinq_pe_rfs/python/l3vpn_qinq_pe_rfs/main.py b/backup/l3vpn_qinq_pe_rfs/python/l3vpn_qinq_pe_rfs/main.py
similarity index 100%
rename from l3vpn_qinq_pe_rfs/python/l3vpn_qinq_pe_rfs/main.py
rename to backup/l3vpn_qinq_pe_rfs/python/l3vpn_qinq_pe_rfs/main.py
diff --git a/l3vpn_qinq_pe_rfs/src/Makefile b/backup/l3vpn_qinq_pe_rfs/src/Makefile
similarity index 100%
rename from l3vpn_qinq_pe_rfs/src/Makefile
rename to backup/l3vpn_qinq_pe_rfs/src/Makefile
diff --git a/l3vpn_qinq_pe_rfs/src/yang/l3vpn_qinq_pe_rfs.yang b/backup/l3vpn_qinq_pe_rfs/src/yang/l3vpn_qinq_pe_rfs.yang
similarity index 100%
rename from l3vpn_qinq_pe_rfs/src/yang/l3vpn_qinq_pe_rfs.yang
rename to backup/l3vpn_qinq_pe_rfs/src/yang/l3vpn_qinq_pe_rfs.yang
diff --git a/l3vpn_qinq_pe_rfs/templates/l3vpn_qinq_pe_rfs-template.xml b/backup/l3vpn_qinq_pe_rfs/templates/l3vpn_qinq_pe_rfs-template.xml
similarity index 100%
rename from l3vpn_qinq_pe_rfs/templates/l3vpn_qinq_pe_rfs-template.xml
rename to backup/l3vpn_qinq_pe_rfs/templates/l3vpn_qinq_pe_rfs-template.xml
diff --git a/l3vpn_qinq_pe_rfs/test/Makefile b/backup/l3vpn_qinq_pe_rfs/test/Makefile
similarity index 100%
rename from l3vpn_qinq_pe_rfs/test/Makefile
rename to backup/l3vpn_qinq_pe_rfs/test/Makefile
diff --git a/l3vpn_qinq_pe_rfs/test/internal/Makefile b/backup/l3vpn_qinq_pe_rfs/test/internal/Makefile
similarity index 100%
rename from l3vpn_qinq_pe_rfs/test/internal/Makefile
rename to backup/l3vpn_qinq_pe_rfs/test/internal/Makefile
diff --git a/l3vpn_qinq_pe_rfs/test/internal/lux/Makefile b/backup/l3vpn_qinq_pe_rfs/test/internal/lux/Makefile
similarity index 100%
rename from l3vpn_qinq_pe_rfs/test/internal/lux/Makefile
rename to backup/l3vpn_qinq_pe_rfs/test/internal/lux/Makefile
diff --git a/l3vpn_qinq_pe_rfs/test/internal/lux/service/Makefile b/backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/Makefile
similarity index 100%
rename from l3vpn_qinq_pe_rfs/test/internal/lux/service/Makefile
rename to backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/Makefile
diff --git a/l3vpn_qinq_pe_rfs/test/internal/lux/service/dummy-device.xml b/backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/dummy-device.xml
similarity index 100%
rename from l3vpn_qinq_pe_rfs/test/internal/lux/service/dummy-device.xml
rename to backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/dummy-device.xml
diff --git a/l3vpn_qinq_pe_rfs/test/internal/lux/service/dummy-service.xml b/backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/dummy-service.xml
similarity index 100%
rename from l3vpn_qinq_pe_rfs/test/internal/lux/service/dummy-service.xml
rename to backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/dummy-service.xml
diff --git a/l3vpn_qinq_pe_rfs/test/internal/lux/service/pyvm.xml b/backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/pyvm.xml
similarity index 100%
rename from l3vpn_qinq_pe_rfs/test/internal/lux/service/pyvm.xml
rename to backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/pyvm.xml
diff --git a/l3vpn_qinq_pe_rfs/test/internal/lux/service/run.lux b/backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/run.lux
similarity index 100%
rename from l3vpn_qinq_pe_rfs/test/internal/lux/service/run.lux
rename to backup/l3vpn_qinq_pe_rfs/test/internal/lux/service/run.lux
diff --git a/l3vpn_qinq_sw_rfs/README b/backup/l3vpn_qinq_sw_rfs/README
similarity index 100%
rename from l3vpn_qinq_sw_rfs/README
rename to backup/l3vpn_qinq_sw_rfs/README
diff --git a/l3vpn_qinq_sw_rfs/package-meta-data.xml b/backup/l3vpn_qinq_sw_rfs/package-meta-data.xml
similarity index 100%
rename from l3vpn_qinq_sw_rfs/package-meta-data.xml
rename to backup/l3vpn_qinq_sw_rfs/package-meta-data.xml
diff --git a/l3vpn_qinq_sw_rfs/python/l3vpn_qinq_sw_rfs/__init__.py b/backup/l3vpn_qinq_sw_rfs/python/l3vpn_qinq_sw_rfs/__init__.py
similarity index 100%
rename from l3vpn_qinq_sw_rfs/python/l3vpn_qinq_sw_rfs/__init__.py
rename to backup/l3vpn_qinq_sw_rfs/python/l3vpn_qinq_sw_rfs/__init__.py
diff --git a/l3vpn_qinq_sw_rfs/python/l3vpn_qinq_sw_rfs/main.py b/backup/l3vpn_qinq_sw_rfs/python/l3vpn_qinq_sw_rfs/main.py
similarity index 100%
rename from l3vpn_qinq_sw_rfs/python/l3vpn_qinq_sw_rfs/main.py
rename to backup/l3vpn_qinq_sw_rfs/python/l3vpn_qinq_sw_rfs/main.py
diff --git a/l3vpn_qinq_sw_rfs/src/Makefile b/backup/l3vpn_qinq_sw_rfs/src/Makefile
similarity index 100%
rename from l3vpn_qinq_sw_rfs/src/Makefile
rename to backup/l3vpn_qinq_sw_rfs/src/Makefile
diff --git a/l3vpn_qinq_sw_rfs/src/yang/l3vpn_qinq_sw_rfs.yang b/backup/l3vpn_qinq_sw_rfs/src/yang/l3vpn_qinq_sw_rfs.yang
similarity index 100%
rename from l3vpn_qinq_sw_rfs/src/yang/l3vpn_qinq_sw_rfs.yang
rename to backup/l3vpn_qinq_sw_rfs/src/yang/l3vpn_qinq_sw_rfs.yang
diff --git a/l3vpn_qinq_sw_rfs/templates/l3vpn_qinq_sw_rfs-template.xml b/backup/l3vpn_qinq_sw_rfs/templates/l3vpn_qinq_sw_rfs-template.xml
similarity index 100%
rename from l3vpn_qinq_sw_rfs/templates/l3vpn_qinq_sw_rfs-template.xml
rename to backup/l3vpn_qinq_sw_rfs/templates/l3vpn_qinq_sw_rfs-template.xml
diff --git a/l3vpn_qinq_sw_rfs/test/Makefile b/backup/l3vpn_qinq_sw_rfs/test/Makefile
similarity index 100%
rename from l3vpn_qinq_sw_rfs/test/Makefile
rename to backup/l3vpn_qinq_sw_rfs/test/Makefile
diff --git a/l3vpn_qinq_sw_rfs/test/internal/Makefile b/backup/l3vpn_qinq_sw_rfs/test/internal/Makefile
similarity index 100%
rename from l3vpn_qinq_sw_rfs/test/internal/Makefile
rename to backup/l3vpn_qinq_sw_rfs/test/internal/Makefile
diff --git a/l3vpn_qinq_sw_rfs/test/internal/lux/Makefile b/backup/l3vpn_qinq_sw_rfs/test/internal/lux/Makefile
similarity index 100%
rename from l3vpn_qinq_sw_rfs/test/internal/lux/Makefile
rename to backup/l3vpn_qinq_sw_rfs/test/internal/lux/Makefile
diff --git a/l3vpn_qinq_sw_rfs/test/internal/lux/service/Makefile b/backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/Makefile
similarity index 100%
rename from l3vpn_qinq_sw_rfs/test/internal/lux/service/Makefile
rename to backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/Makefile
diff --git a/l3vpn_qinq_sw_rfs/test/internal/lux/service/dummy-device.xml b/backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/dummy-device.xml
similarity index 100%
rename from l3vpn_qinq_sw_rfs/test/internal/lux/service/dummy-device.xml
rename to backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/dummy-device.xml
diff --git a/l3vpn_qinq_sw_rfs/test/internal/lux/service/dummy-service.xml b/backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/dummy-service.xml
similarity index 100%
rename from l3vpn_qinq_sw_rfs/test/internal/lux/service/dummy-service.xml
rename to backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/dummy-service.xml
diff --git a/l3vpn_qinq_sw_rfs/test/internal/lux/service/pyvm.xml b/backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/pyvm.xml
similarity index 100%
rename from l3vpn_qinq_sw_rfs/test/internal/lux/service/pyvm.xml
rename to backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/pyvm.xml
diff --git a/l3vpn_qinq_sw_rfs/test/internal/lux/service/run.lux b/backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/run.lux
similarity index 100%
rename from l3vpn_qinq_sw_rfs/test/internal/lux/service/run.lux
rename to backup/l3vpn_qinq_sw_rfs/test/internal/lux/service/run.lux
diff --git a/l2vpn_cfs/README b/l2vpn_cfs/README
new file mode 100644
index 0000000..e9b9de3
--- /dev/null
+++ b/l2vpn_cfs/README
@@ -0,0 +1,23 @@
+This is a generated Python package, made by:
+
+ ncs-make-package --service-skeleton python-and-template \
+ --component-class main.Main l2vpn_cfs
+
+It contains a dummy YANG model which implements a minimal Service
+and an Action that doesn't really do anything useful. They are
+there just to get you going.
+
+You will also find two test cases in:
+
+ test/internal/lux/service/
+ test/internal/lux/action/
+
+that you can run if you have the 'lux' testing tool.
+Your top Makefile also need to implement some Make targets
+as described in the Makefiles of the test cases.
+You can also just read the corresponding run.lux tests and
+do them manually if you wish.
+
+The 'lux' test tool can be obtained from:
+
+ https://github.com/hawk/lux.git
diff --git a/l2vpn_cfs/load-dir/l2vpn_cfs.fxs b/l2vpn_cfs/load-dir/l2vpn_cfs.fxs
new file mode 100644
index 0000000..e10b2bf
Binary files /dev/null and b/l2vpn_cfs/load-dir/l2vpn_cfs.fxs differ
diff --git a/l2vpn_cfs/package-meta-data.xml b/l2vpn_cfs/package-meta-data.xml
new file mode 100644
index 0000000..e487c21
--- /dev/null
+++ b/l2vpn_cfs/package-meta-data.xml
@@ -0,0 +1,13 @@
+
+ l2vpn_cfs
+ 1.0
+ Generated Python package
+ 6.2
+
+
+ main
+
+ l2vpn_cfs.main.Main
+
+
+
diff --git a/l2vpn_cfs/python/l2vpn_cfs/__init__.py b/l2vpn_cfs/python/l2vpn_cfs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/l2vpn_cfs/python/l2vpn_cfs/main.py b/l2vpn_cfs/python/l2vpn_cfs/main.py
new file mode 100644
index 0000000..91f8336
--- /dev/null
+++ b/l2vpn_cfs/python/l2vpn_cfs/main.py
@@ -0,0 +1,109 @@
+# -*- mode: python; python-indent: 4 -*-
+import ncs
+from ncs.application import Service
+import requests
+
+class ServiceCallbacks(Service):
+
+ @Service.create
+ def cb_create(self, tctx, root, service, proplist):
+ self.log.info('Service create(service=', service._path, ')')
+
+ headers = {"Authorization":"Token e657404c0e1f57481ad0a06c293e8fe794e720ac", "Accept": "application/json", "Content-Type": "application/json"}
+ body = {}
+
+ vars = ncs.template.Variables()
+ template = ncs.template.Template(service)
+
+ for endpoint in service.endpoint:
+
+ # Get an Outer VLAN from Netbox. The VLAN group for outer VLAN's is #2.
+ api_url="https://10.101.180.45/api/ipam/vlan-groups/2/available-vlans/"
+ ovlan_name = "L2VPN-Outer-VLAN-"+str(endpoint.pe_device)+"-"+str(endpoint.id)
+ body = {"name": ovlan_name}
+ try:
+ response=requests.post(api_url, headers=headers, json=body, verify=False)
+ self.log.info('Netbox response, Outer VLAN reservation: ', response.json())
+ ovlan_id = response.json()["vid"]
+ self.log.info('VLAN from Netbox: ', ovlan_id)
+ except:
+ if endpoint.ovlan:
+ ovlan_id = endpoint.ovlan
+ else:
+ ovlan_id = 254
+ self.log.info('Connection to Netbox failed. Statically allocated Outer VLAN: ', ovlan_id)
+ vars.add('ovlan', ovlan_id)
+
+ # Get an Inner VLAN from Netbox. The VLAN group for inner VLAN's is #3.
+ api_url="https://10.101.180.45/api/ipam/vlan-groups/3/available-vlans/"
+ ivlan_name = "L2VPN-Inner-VLAN-"+str(endpoint.pe_device)+"-"+str(endpoint.id)
+ body = {"name": ivlan_name}
+ try:
+ response=requests.post(api_url, headers=headers, json=body, verify=False)
+ self.log.info('Netbox response, Inner VLAN reservation: ', response.json())
+ ivlan_id = response.json()["vid"]
+ self.log.info('VLAN from Netbox: ', ivlan_id)
+ except:
+ if endpoint.ivlan:
+ ivlan_id = endpoint.ivlan
+ else:
+ ivlan_id = 253
+ self.log.info('Connection to Netbox failed. Statically allocated Inner VLAN: ', ivlan_id)
+ vars.add('ivlan', ivlan_id)
+
+ # PE part
+ name = "L2VPN-"+str(endpoint.pe_device)+"-"+str(endpoint.id)
+ vars.add('name', name)
+ self.log.info('Name for PE instance: ', name)
+ vars.add('pe_device', endpoint.pe_device)
+ vars.add('bridge_group_name', str(endpoint.id))
+ # PE to Switch interface configuration:
+ vars.add('pe_interface_name', endpoint.pe_interface.interface_name)
+ vars.add('pe_interface_number', endpoint.pe_interface.interface_number)
+ pe_interface_description = "L2VPN interface to " + str(endpoint.sw_device) + ", interface " + str(endpoint.sw_pe_interface.interface_name) + str(endpoint.sw_pe_interface.interface_number)
+ vars.add('pe_interface_description', pe_interface_description)
+ self.log.info('pe_interface_description: ', pe_interface_description)
+ template.apply('l2vpn_pe-template', vars)
+
+ # Switch part
+ name = "L2VPN-"+str(endpoint.sw_device)+"-"+str(endpoint.id)
+ vars.add('name', name)
+ self.log.info('Name for Switch instance: ', name)
+ vars.add('sw_device', endpoint.sw_device)
+ vars.add('vlan_name', str(endpoint.id))
+ # Switch to PE interface configuration:
+ vars.add('sw_pe_interface_name', endpoint.sw_pe_interface.interface_name)
+ vars.add('sw_pe_interface_number', endpoint.sw_pe_interface.interface_number)
+ sw_pe_interface_description = "L2VPN interface to " + str(endpoint.pe_device) + ", interface " + str(endpoint.pe_interface.interface_name) + str(endpoint.pe_interface.interface_number)
+ vars.add('sw_pe_interface_description', sw_pe_interface_description)
+ self.log.info('sw_pe_interface_description: ', sw_pe_interface_description)
+ # Switch to CPE interface configuration:
+ vars.add('sw_cpe_interface_name', endpoint.sw_cpe_interface.interface_name)
+ vars.add('sw_cpe_interface_number', endpoint.sw_cpe_interface.interface_number)
+ sw_cpe_interface_description = "L2VPN interface to " + str(endpoint.cpe_device) + ", interface " + str(endpoint.cpe_interface.interface_name) + str(endpoint.cpe_interface.interface_number)
+ vars.add('sw_cpe_interface_description', sw_cpe_interface_description)
+ self.log.info('sw_cpe_interface_description: ', sw_cpe_interface_description)
+ template.apply('l2vpn_sw-template', vars)
+
+ # CPE part
+ name = "L2VPN-"+str(endpoint.cpe_device)+"-"+str(endpoint.id)
+ vars.add('name', name)
+ self.log.info('Name for CPE instance: ', name)
+ vars.add('cpe_device', endpoint.cpe_device)
+ vars.add('vlan_name', str(endpoint.id))
+ # CPE to Switch interface configuration:
+ vars.add('cpe_interface_name', endpoint.cpe_interface.interface_name)
+ vars.add('cpe_interface_number', endpoint.cpe_interface.interface_number)
+ cpe_interface_description = "L2VPN interface to " + str(endpoint.sw_device) + ", interface " + str(endpoint.sw_cpe_interface.interface_name) + str(endpoint.sw_cpe_interface.interface_number)
+ vars.add('cpe_interface_description', cpe_interface_description)
+ self.log.info('cpe_interface_description: ', cpe_interface_description)
+ template.apply('l2vpn_cpe-template', vars)
+
+
+class Main(ncs.application.Application):
+ def setup(self):
+ self.log.info('Main RUNNING')
+ self.register_service('l2vpn_cfs-servicepoint', ServiceCallbacks)
+
+ def teardown(self):
+ self.log.info('Main FINISHED')
diff --git a/l2vpn_cfs/src/Makefile b/l2vpn_cfs/src/Makefile
new file mode 100644
index 0000000..3a0f88c
--- /dev/null
+++ b/l2vpn_cfs/src/Makefile
@@ -0,0 +1,32 @@
+all: fxs
+.PHONY: all
+
+# Include standard NCS examples build definitions and rules
+include $(NCS_DIR)/src/ncs/build/include.ncs.mk
+
+SRC = $(wildcard yang/*.yang)
+DIRS = ../load-dir
+FXS = $(SRC:yang/%.yang=../load-dir/%.fxs)
+
+## Uncomment and patch the line below if you have a dependency to a NED
+## or to other YANG files
+# YANGPATH += ../..//src/ncsc-out/modules/yang \
+# ../..//src/yang
+
+NCSCPATH = $(YANGPATH:%=--yangpath %)
+YANGERPATH = $(YANGPATH:%=--path %)
+
+fxs: $(DIRS) $(FXS)
+
+$(DIRS):
+ mkdir -p $@
+
+../load-dir/%.fxs: yang/%.yang
+ $(NCSC) `ls $*-ann.yang > /dev/null 2>&1 && echo "-a $*-ann.yang"` \
+ --fail-on-warnings \
+ $(NCSCPATH) \
+ -c -o $@ $<
+
+clean:
+ rm -rf $(DIRS)
+.PHONY: clean
diff --git a/l2vpn_cfs/src/yang/l2vpn_cfs.yang b/l2vpn_cfs/src/yang/l2vpn_cfs.yang
new file mode 100644
index 0000000..eacc461
--- /dev/null
+++ b/l2vpn_cfs/src/yang/l2vpn_cfs.yang
@@ -0,0 +1,120 @@
+module l2vpn_cfs {
+
+ namespace "http://example.com/l2vpn_cfs";
+ prefix l2vpn_cfs;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import tailf-common {
+ prefix tailf;
+ }
+ import tailf-ncs {
+ prefix ncs;
+ }
+
+ description
+ "CFS, L2VPN service with QinQ in the switched network between PE and CPE";
+
+ revision 2024-07-11 {
+ description
+ "Initial revision.";
+ }
+
+ grouping interface_grouping {
+ leaf interface_name {
+ tailf:info "Interface name. For example TenGigE.";
+ type enumeration {
+ enum FastEthernet;
+ enum GigabitEthernet;
+ enum TenGigE;
+ enum TwentyFiveGigE;
+ enum FortyGigE;
+ enum FiftyGigE;
+ enum HundredGigE;
+ enum TwoHundredGigE;
+ enum FourHundredGigE;
+ enum EightHundredGigE;
+ enum xe;
+ }
+ }
+ leaf interface_number {
+ tailf:info "Interface number. For example 0 or 1/0";
+ type string {
+ pattern "[0-9]{1,2}(/[0-9]{1,2}){1,4}";
+ }
+ }
+ leaf interface_description {
+ tailf:info "Interface description.";
+ type string;
+ }
+ }
+
+ list l2vpn_cfs {
+ description "This is an RFS skeleton service";
+
+ key name;
+ leaf name {
+ tailf:info "Unique service id";
+ tailf:cli-allow-range;
+ type string;
+ }
+
+ uses ncs:service-data;
+ ncs:servicepoint l2vpn_cfs-servicepoint;
+
+ list endpoint {
+ key id;
+ leaf id {
+ tailf:info "Endpoint identifier. It should contain the id of the CPE.";
+ type string;
+ }
+
+ leaf pe_device {
+ type leafref {
+ path "/ncs:devices/ncs:device/ncs:name";
+ }
+ }
+
+ leaf sw_device {
+ type leafref {
+ path "/ncs:devices/ncs:device/ncs:name";
+ }
+ }
+
+ leaf cpe_device {
+ type leafref {
+ path "/ncs:devices/ncs:device/ncs:name";
+ }
+ }
+
+ container pe_interface {
+ uses interface_grouping;
+ }
+
+ container sw_pe_interface {
+ uses interface_grouping;
+ }
+
+ container sw_cpe_interface {
+ uses interface_grouping;
+ }
+
+ container cpe_interface {
+ uses interface_grouping;
+ }
+
+ leaf ovlan {
+ tailf:info "Outer VLAN eg 10";
+ type uint16;
+ default 50;
+ }
+
+ leaf ivlan {
+ tailf:info "Inner VLAN eg 20";
+ type uint16;
+ default 30;
+ }
+ }
+ }
+}
diff --git a/l2vpn_cfs/templates/l2vpn_cpe-template.xml b/l2vpn_cfs/templates/l2vpn_cpe-template.xml
new file mode 100644
index 0000000..d8e34bc
--- /dev/null
+++ b/l2vpn_cfs/templates/l2vpn_cpe-template.xml
@@ -0,0 +1,13 @@
+
+
+ {$name}
+ {$cpe_device}
+ {$ovlan}
+ {$ivlan}
+
+ {$cpe_interface_name}
+ {$cpe_interface_number}
+ {$cpe_interface_description}
+
+
+
diff --git a/l2vpn_cfs/templates/l2vpn_pe-template.xml b/l2vpn_cfs/templates/l2vpn_pe-template.xml
new file mode 100644
index 0000000..a7cc56c
--- /dev/null
+++ b/l2vpn_cfs/templates/l2vpn_pe-template.xml
@@ -0,0 +1,14 @@
+
+
+ {$name}
+ {$pe_device}
+ {$ovlan}
+ {$ivlan}
+ {$bridge_group_name}
+
+ {$pe_interface_name}
+ {$pe_interface_number}
+ {$pe_interface_description}
+
+
+
diff --git a/l2vpn_cfs/templates/l2vpn_sw-template.xml b/l2vpn_cfs/templates/l2vpn_sw-template.xml
new file mode 100644
index 0000000..4cda32b
--- /dev/null
+++ b/l2vpn_cfs/templates/l2vpn_sw-template.xml
@@ -0,0 +1,19 @@
+
+
+ {$name}
+ {$sw_device}
+ {$ovlan}
+ {$ivlan}
+ {$vlan_name}
+
+ {$sw_pe_interface_name}
+ {$sw_pe_interface_number}
+ {$sw_pe_interface_description}
+
+
+ {$sw_cpe_interface_name}
+ {$sw_cpe_interface_number}
+ {$sw_cpe_interface_description}
+
+
+
diff --git a/l2vpn_cfs/test/Makefile b/l2vpn_cfs/test/Makefile
new file mode 100644
index 0000000..32f02d6
--- /dev/null
+++ b/l2vpn_cfs/test/Makefile
@@ -0,0 +1,37 @@
+DIRS = external internal
+
+ifeq ($(BUILD_JOB),external)
+DIR = external
+endif
+
+ifeq ($(BUILD_JOB),internal_realhw)
+DIR = internal
+JOB_DIR = realhw
+endif
+
+ifeq ($(BUILD_JOB),internal_simulated)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+ifeq ($(BUILD_JOB),)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+all: test
+
+build:
+ $(MAKE) -C $(DIR) build JOB_DIR=$(JOB_DIR) || exit 1
+
+clean:
+ $(MAKE) -C $(DIR) clean JOB_DIR=$(JOB_DIR) || exit 1
+
+test:
+ $(MAKE) -C $(DIR) test JOB_DIR=$(JOB_DIR) || exit 1
+
+desc:
+ @echo "==Test Cases for NED=="
+ @for d in $(DIRS) ; do \
+ $(MAKE) -sC $$d desc || exit 1; \
+ done
diff --git a/l2vpn_cfs/test/internal/Makefile b/l2vpn_cfs/test/internal/Makefile
new file mode 100644
index 0000000..982aa57
--- /dev/null
+++ b/l2vpn_cfs/test/internal/Makefile
@@ -0,0 +1,21 @@
+DIRS = lux
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/l2vpn_cfs/test/internal/lux/Makefile b/l2vpn_cfs/test/internal/lux/Makefile
new file mode 100644
index 0000000..2558f02
--- /dev/null
+++ b/l2vpn_cfs/test/internal/lux/Makefile
@@ -0,0 +1,21 @@
+DIRS = service
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/l2vpn_cfs/test/internal/lux/service/Makefile b/l2vpn_cfs/test/internal/lux/service/Makefile
new file mode 100644
index 0000000..d2dc5be
--- /dev/null
+++ b/l2vpn_cfs/test/internal/lux/service/Makefile
@@ -0,0 +1,26 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+
+# Make sure the TARGET_DIR has got the following make targets:
+.PHONY: clean build start stop
+
+export TARGET_DIR=../../../../../..
+
+.PHONY: test
+test:
+ lux run.lux
+
+clean:
+ $(MAKE) -C $(TARGET_DIR) clean
+
+build:
+ $(MAKE) -C $(TARGET_DIR) build
+
+start:
+ $(MAKE) -C $(TARGET_DIR) start
+
+stop:
+ $(MAKE) -C $(TARGET_DIR) stop
diff --git a/l2vpn_cfs/test/internal/lux/service/dummy-device.xml b/l2vpn_cfs/test/internal/lux/service/dummy-device.xml
new file mode 100644
index 0000000..df09281
--- /dev/null
+++ b/l2vpn_cfs/test/internal/lux/service/dummy-device.xml
@@ -0,0 +1,11 @@
+
+
+
+ eth
+ 192.168.110.1
+
+ southbound-locked
+
+
+
+
diff --git a/l2vpn_cfs/test/internal/lux/service/dummy-service.xml b/l2vpn_cfs/test/internal/lux/service/dummy-service.xml
new file mode 100644
index 0000000..5df16cb
--- /dev/null
+++ b/l2vpn_cfs/test/internal/lux/service/dummy-service.xml
@@ -0,0 +1,12 @@
+
+
+
+ true
+
+
+
+ 33
+ eth
+ 127.0.0.1
+
+
diff --git a/l2vpn_cfs/test/internal/lux/service/pyvm.xml b/l2vpn_cfs/test/internal/lux/service/pyvm.xml
new file mode 100644
index 0000000..531521c
--- /dev/null
+++ b/l2vpn_cfs/test/internal/lux/service/pyvm.xml
@@ -0,0 +1,8 @@
+
+
+
+ ./logs/ncs-python-vm
+ level-info
+
+
+
diff --git a/l2vpn_cfs/test/internal/lux/service/run.lux b/l2vpn_cfs/test/internal/lux/service/run.lux
new file mode 100644
index 0000000..2df3bd0
--- /dev/null
+++ b/l2vpn_cfs/test/internal/lux/service/run.lux
@@ -0,0 +1,53 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+[global target_dir=../../../../../..]
+[config skip_unless=PYTHON]
+
+[shell top]
+ !make stop build
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ !rm ${target_dir}/ncs-cdb/*
+ ?SH-PROMPT:
+ !cp pyvm.xml ${target_dir}/ncs-cdb/.
+ ?SH-PROMPT:
+
+ !make start
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ [progress \nCreate a dummy device...\n]
+ !ncs_load -lm dummy-device.xml
+ ?SH-PROMPT:
+ [progress \nCreate a dummy device...ok\n]
+
+ [sleep 3]
+
+ [progress \nCreate the dummy service...\n]
+ !ncs_load -lm dummy-service.xml
+ ?SH-PROMPT:
+ [progress \nCreate the dummy service...ok\n]
+
+
+[shell log]
+ !cd ${target_dir}
+ ?SH-PROMPT:
+
+ [progress \nVerify that the service code has been invoked...\n]
+ !tail -f ./logs/ncs-python-vm-l2vpn_cfs.log
+ ?.*Worker RUNNING.*
+ ?.*Service create.*
+ [progress \nVerify that the service code has been invoked...ok\n]
+
+
+[cleanup]
+ !make stop
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
diff --git a/l2vpn_cpe_rfs/README b/l2vpn_cpe_rfs/README
new file mode 100644
index 0000000..eb40d6b
--- /dev/null
+++ b/l2vpn_cpe_rfs/README
@@ -0,0 +1,23 @@
+This is a generated Python package, made by:
+
+ ncs-make-package --service-skeleton python-and-template \
+ --component-class main.Main l2vpn_cpe_rfs
+
+It contains a dummy YANG model which implements a minimal Service
+and an Action that doesn't really do anything useful. They are
+there just to get you going.
+
+You will also find two test cases in:
+
+ test/internal/lux/service/
+ test/internal/lux/action/
+
+that you can run if you have the 'lux' testing tool.
+Your top Makefile also need to implement some Make targets
+as described in the Makefiles of the test cases.
+You can also just read the corresponding run.lux tests and
+do them manually if you wish.
+
+The 'lux' test tool can be obtained from:
+
+ https://github.com/hawk/lux.git
diff --git a/l2vpn_cpe_rfs/load-dir/l2vpn_cpe_rfs.fxs b/l2vpn_cpe_rfs/load-dir/l2vpn_cpe_rfs.fxs
new file mode 100644
index 0000000..e767c1f
Binary files /dev/null and b/l2vpn_cpe_rfs/load-dir/l2vpn_cpe_rfs.fxs differ
diff --git a/l2vpn_cpe_rfs/package-meta-data.xml b/l2vpn_cpe_rfs/package-meta-data.xml
new file mode 100644
index 0000000..79f1364
--- /dev/null
+++ b/l2vpn_cpe_rfs/package-meta-data.xml
@@ -0,0 +1,13 @@
+
+ l2vpn_cpe_rfs
+ 1.0
+ Generated Python package
+ 6.2
+
+
+ main
+
+ l2vpn_cpe_rfs.main.Main
+
+
+
diff --git a/l2vpn_cpe_rfs/python/l2vpn_cpe_rfs/__init__.py b/l2vpn_cpe_rfs/python/l2vpn_cpe_rfs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/l2vpn_cpe_rfs/python/l2vpn_cpe_rfs/main.py b/l2vpn_cpe_rfs/python/l2vpn_cpe_rfs/main.py
new file mode 100644
index 0000000..bd85477
--- /dev/null
+++ b/l2vpn_cpe_rfs/python/l2vpn_cpe_rfs/main.py
@@ -0,0 +1,65 @@
+# -*- mode: python; python-indent: 4 -*-
+import ncs
+from ncs.application import Service
+
+
+# ------------------------
+# SERVICE CALLBACK EXAMPLE
+# ------------------------
+class ServiceCallbacks(Service):
+
+ # The create() callback is invoked inside NCS FASTMAP and
+ # must always exist.
+ @Service.create
+ def cb_create(self, tctx, root, service, proplist):
+ self.log.info('Service create(service=', service._path, ')')
+
+ vars = ncs.template.Variables()
+ vars.add('DUMMY', '127.0.0.1')
+ template = ncs.template.Template(service)
+ template.apply('l2vpn_cpe_rfs-template', vars)
+
+ # The pre_modification() and post_modification() callbacks are optional,
+ # and are invoked outside FASTMAP. pre_modification() is invoked before
+ # create, update, or delete of the service, as indicated by the enum
+ # ncs_service_operation op parameter. Conversely
+ # post_modification() is invoked after create, update, or delete
+ # of the service. These functions can be useful e.g. for
+ # allocations that should be stored and existing also when the
+ # service instance is removed.
+
+ # @Service.pre_modification
+ # def cb_pre_modification(self, tctx, op, kp, root, proplist):
+ # self.log.info('Service premod(service=', kp, ')')
+
+ # @Service.post_modification
+ # def cb_post_modification(self, tctx, op, kp, root, proplist):
+ # self.log.info('Service postmod(service=', kp, ')')
+
+
+# ---------------------------------------------
+# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
+# ---------------------------------------------
+class Main(ncs.application.Application):
+ def setup(self):
+ # The application class sets up logging for us. It is accessible
+ # through 'self.log' and is a ncs.log.Log instance.
+ self.log.info('Main RUNNING')
+
+ # Service callbacks require a registration for a 'service point',
+ # as specified in the corresponding data model.
+ #
+ self.register_service('l2vpn_cpe_rfs-servicepoint', ServiceCallbacks)
+
+ # If we registered any callback(s) above, the Application class
+ # took care of creating a daemon (related to the service/action point).
+
+ # When this setup method is finished, all registrations are
+ # considered done and the application is 'started'.
+
+ def teardown(self):
+ # When the application is finished (which would happen if NCS went
+ # down, packages were reloaded or some error occurred) this teardown
+ # method will be called.
+
+ self.log.info('Main FINISHED')
diff --git a/l2vpn_cpe_rfs/src/Makefile b/l2vpn_cpe_rfs/src/Makefile
new file mode 100644
index 0000000..3a0f88c
--- /dev/null
+++ b/l2vpn_cpe_rfs/src/Makefile
@@ -0,0 +1,32 @@
+all: fxs
+.PHONY: all
+
+# Include standard NCS examples build definitions and rules
+include $(NCS_DIR)/src/ncs/build/include.ncs.mk
+
+SRC = $(wildcard yang/*.yang)
+DIRS = ../load-dir
+FXS = $(SRC:yang/%.yang=../load-dir/%.fxs)
+
+## Uncomment and patch the line below if you have a dependency to a NED
+## or to other YANG files
+# YANGPATH += ../..//src/ncsc-out/modules/yang \
+# ../..//src/yang
+
+NCSCPATH = $(YANGPATH:%=--yangpath %)
+YANGERPATH = $(YANGPATH:%=--path %)
+
+fxs: $(DIRS) $(FXS)
+
+$(DIRS):
+ mkdir -p $@
+
+../load-dir/%.fxs: yang/%.yang
+ $(NCSC) `ls $*-ann.yang > /dev/null 2>&1 && echo "-a $*-ann.yang"` \
+ --fail-on-warnings \
+ $(NCSCPATH) \
+ -c -o $@ $<
+
+clean:
+ rm -rf $(DIRS)
+.PHONY: clean
diff --git a/l2vpn_cpe_rfs/src/yang/l2vpn_cpe_rfs.yang b/l2vpn_cpe_rfs/src/yang/l2vpn_cpe_rfs.yang
new file mode 100644
index 0000000..61bc614
--- /dev/null
+++ b/l2vpn_cpe_rfs/src/yang/l2vpn_cpe_rfs.yang
@@ -0,0 +1,89 @@
+module l2vpn_cpe_rfs {
+
+ namespace "http://example.com/l2vpn_cpe_rfs";
+ prefix l2vpn_cpe_rfs;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import tailf-common {
+ prefix tailf;
+ }
+ import tailf-ncs {
+ prefix ncs;
+ }
+
+ description
+ "The CPE part of the L2VPN service with QinQ in the switched network between PE and CPE";
+
+ revision 2024-07-10 {
+ description
+ "Initial revision.";
+ }
+
+ grouping interface_grouping {
+ leaf interface_name {
+ tailf:info "Interface name. For example TenGigE.";
+ type enumeration {
+ enum FastEthernet;
+ enum GigabitEthernet;
+ enum TenGigE;
+ enum TwentyFiveGigE;
+ enum FortyGigE;
+ enum FiftyGigE;
+ enum HundredGigE;
+ enum TwoHundredGigE;
+ enum FourHundredGigE;
+ enum EightHundredGigE;
+ enum xe;
+ }
+ }
+ leaf interface_number {
+ tailf:info "Interface number. For example 0 or 1/0";
+ type string {
+ pattern "[0-9]{1,2}(/[0-9]{1,2}){1,4}";
+ }
+ }
+ leaf interface_description {
+ tailf:info "Interface description.";
+ type string;
+ }
+ }
+
+ list l2vpn_cpe_rfs {
+ description "This is an RFS skeleton service";
+
+ key name;
+ leaf name {
+ tailf:info "Unique service id";
+ tailf:cli-allow-range;
+ type string;
+ }
+
+ uses ncs:service-data;
+ ncs:servicepoint l2vpn_cpe_rfs-servicepoint;
+
+ leaf device {
+ tailf:info "The CPE device";
+ type leafref {
+ path "/ncs:devices/ncs:device/ncs:name";
+ }
+ }
+
+ leaf ovlan {
+ tailf:info "Outer VLAN eg 10";
+ type uint16;
+ mandatory true;
+ }
+
+ leaf ivlan {
+ tailf:info "Inner VLAN eg 20";
+ type uint16;
+ mandatory true;
+ }
+
+ container interface {
+ uses interface_grouping;
+ }
+ }
+}
diff --git a/l2vpn_cpe_rfs/templates/l2vpn_cpe_rfs-template.xml b/l2vpn_cpe_rfs/templates/l2vpn_cpe_rfs-template.xml
new file mode 100644
index 0000000..07e5f65
--- /dev/null
+++ b/l2vpn_cpe_rfs/templates/l2vpn_cpe_rfs-template.xml
@@ -0,0 +1,81 @@
+
+
+
+ {/device}
+
+
+
+
+ {/interface/interface_number}
+ {/interface/interface_description}
+
+
+ {/ovlan}
+
+
+
+ {/ovlan}
+ {/ivlan}
+
+
+
+
+
+ 2
+ symmetric
+
+
+
+
+ {/ivlan}
+
+
+
+
+
+
+
+ {/interface/interface_number}
+ {/interface/interface_description}
+
+
+ {/ovlan}
+
+
+
+ {/ovlan}
+ {/ivlan}
+
+
+
+
+
+ 2
+ symmetric
+
+
+
+
+ {/ivlan}
+
+
+
+
+
+
+
+ {/ivlan}
+
+
+
+ {/ovlan}.{/ovlan}.{/ovlan}.2
+ 255.255.255.0
+
+
+
+
+
+
+
+
+
diff --git a/l2vpn_cpe_rfs/test/Makefile b/l2vpn_cpe_rfs/test/Makefile
new file mode 100644
index 0000000..32f02d6
--- /dev/null
+++ b/l2vpn_cpe_rfs/test/Makefile
@@ -0,0 +1,37 @@
+DIRS = external internal
+
+ifeq ($(BUILD_JOB),external)
+DIR = external
+endif
+
+ifeq ($(BUILD_JOB),internal_realhw)
+DIR = internal
+JOB_DIR = realhw
+endif
+
+ifeq ($(BUILD_JOB),internal_simulated)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+ifeq ($(BUILD_JOB),)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+all: test
+
+build:
+ $(MAKE) -C $(DIR) build JOB_DIR=$(JOB_DIR) || exit 1
+
+clean:
+ $(MAKE) -C $(DIR) clean JOB_DIR=$(JOB_DIR) || exit 1
+
+test:
+ $(MAKE) -C $(DIR) test JOB_DIR=$(JOB_DIR) || exit 1
+
+desc:
+ @echo "==Test Cases for NED=="
+ @for d in $(DIRS) ; do \
+ $(MAKE) -sC $$d desc || exit 1; \
+ done
diff --git a/l2vpn_cpe_rfs/test/internal/Makefile b/l2vpn_cpe_rfs/test/internal/Makefile
new file mode 100644
index 0000000..982aa57
--- /dev/null
+++ b/l2vpn_cpe_rfs/test/internal/Makefile
@@ -0,0 +1,21 @@
+DIRS = lux
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/l2vpn_cpe_rfs/test/internal/lux/Makefile b/l2vpn_cpe_rfs/test/internal/lux/Makefile
new file mode 100644
index 0000000..2558f02
--- /dev/null
+++ b/l2vpn_cpe_rfs/test/internal/lux/Makefile
@@ -0,0 +1,21 @@
+DIRS = service
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/l2vpn_cpe_rfs/test/internal/lux/service/Makefile b/l2vpn_cpe_rfs/test/internal/lux/service/Makefile
new file mode 100644
index 0000000..d2dc5be
--- /dev/null
+++ b/l2vpn_cpe_rfs/test/internal/lux/service/Makefile
@@ -0,0 +1,26 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+
+# Make sure the TARGET_DIR has got the following make targets:
+.PHONY: clean build start stop
+
+export TARGET_DIR=../../../../../..
+
+.PHONY: test
+test:
+ lux run.lux
+
+clean:
+ $(MAKE) -C $(TARGET_DIR) clean
+
+build:
+ $(MAKE) -C $(TARGET_DIR) build
+
+start:
+ $(MAKE) -C $(TARGET_DIR) start
+
+stop:
+ $(MAKE) -C $(TARGET_DIR) stop
diff --git a/l2vpn_cpe_rfs/test/internal/lux/service/dummy-device.xml b/l2vpn_cpe_rfs/test/internal/lux/service/dummy-device.xml
new file mode 100644
index 0000000..df09281
--- /dev/null
+++ b/l2vpn_cpe_rfs/test/internal/lux/service/dummy-device.xml
@@ -0,0 +1,11 @@
+
+
+
+ eth
+ 192.168.110.1
+
+ southbound-locked
+
+
+
+
diff --git a/l2vpn_cpe_rfs/test/internal/lux/service/dummy-service.xml b/l2vpn_cpe_rfs/test/internal/lux/service/dummy-service.xml
new file mode 100644
index 0000000..92a928b
--- /dev/null
+++ b/l2vpn_cpe_rfs/test/internal/lux/service/dummy-service.xml
@@ -0,0 +1,12 @@
+
+
+
+ true
+
+
+
+ 33
+ eth
+ 127.0.0.1
+
+
diff --git a/l2vpn_cpe_rfs/test/internal/lux/service/pyvm.xml b/l2vpn_cpe_rfs/test/internal/lux/service/pyvm.xml
new file mode 100644
index 0000000..531521c
--- /dev/null
+++ b/l2vpn_cpe_rfs/test/internal/lux/service/pyvm.xml
@@ -0,0 +1,8 @@
+
+
+
+ ./logs/ncs-python-vm
+ level-info
+
+
+
diff --git a/l2vpn_cpe_rfs/test/internal/lux/service/run.lux b/l2vpn_cpe_rfs/test/internal/lux/service/run.lux
new file mode 100644
index 0000000..4fabfc7
--- /dev/null
+++ b/l2vpn_cpe_rfs/test/internal/lux/service/run.lux
@@ -0,0 +1,53 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+[global target_dir=../../../../../..]
+[config skip_unless=PYTHON]
+
+[shell top]
+ !make stop build
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ !rm ${target_dir}/ncs-cdb/*
+ ?SH-PROMPT:
+ !cp pyvm.xml ${target_dir}/ncs-cdb/.
+ ?SH-PROMPT:
+
+ !make start
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ [progress \nCreate a dummy device...\n]
+ !ncs_load -lm dummy-device.xml
+ ?SH-PROMPT:
+ [progress \nCreate a dummy device...ok\n]
+
+ [sleep 3]
+
+ [progress \nCreate the dummy service...\n]
+ !ncs_load -lm dummy-service.xml
+ ?SH-PROMPT:
+ [progress \nCreate the dummy service...ok\n]
+
+
+[shell log]
+ !cd ${target_dir}
+ ?SH-PROMPT:
+
+ [progress \nVerify that the service code has been invoked...\n]
+ !tail -f ./logs/ncs-python-vm-l2vpn_cpe_rfs.log
+ ?.*Worker RUNNING.*
+ ?.*Service create.*
+ [progress \nVerify that the service code has been invoked...ok\n]
+
+
+[cleanup]
+ !make stop
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
diff --git a/l2vpn_pe_rfs/README b/l2vpn_pe_rfs/README
new file mode 100644
index 0000000..7fe80fb
--- /dev/null
+++ b/l2vpn_pe_rfs/README
@@ -0,0 +1,23 @@
+This is a generated Python package, made by:
+
+ ncs-make-package --service-skeleton python-and-template \
+ --component-class main.Main l2vpn_pe_rfs
+
+It contains a dummy YANG model which implements a minimal Service
+and an Action that doesn't really do anything useful. They are
+there just to get you going.
+
+You will also find two test cases in:
+
+ test/internal/lux/service/
+ test/internal/lux/action/
+
+that you can run if you have the 'lux' testing tool.
+Your top Makefile also need to implement some Make targets
+as described in the Makefiles of the test cases.
+You can also just read the corresponding run.lux tests and
+do them manually if you wish.
+
+The 'lux' test tool can be obtained from:
+
+ https://github.com/hawk/lux.git
diff --git a/l2vpn_pe_rfs/load-dir/l2vpn_pe_rfs.fxs b/l2vpn_pe_rfs/load-dir/l2vpn_pe_rfs.fxs
new file mode 100644
index 0000000..5c5ea1a
Binary files /dev/null and b/l2vpn_pe_rfs/load-dir/l2vpn_pe_rfs.fxs differ
diff --git a/l2vpn_pe_rfs/package-meta-data.xml b/l2vpn_pe_rfs/package-meta-data.xml
new file mode 100644
index 0000000..090ee9d
--- /dev/null
+++ b/l2vpn_pe_rfs/package-meta-data.xml
@@ -0,0 +1,13 @@
+
+ l2vpn_pe_rfs
+ 1.0
+ Generated Python package
+ 6.2
+
+
+ main
+
+ l2vpn_pe_rfs.main.Main
+
+
+
diff --git a/l2vpn_pe_rfs/python/l2vpn_pe_rfs/__init__.py b/l2vpn_pe_rfs/python/l2vpn_pe_rfs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/l2vpn_pe_rfs/python/l2vpn_pe_rfs/main.py b/l2vpn_pe_rfs/python/l2vpn_pe_rfs/main.py
new file mode 100644
index 0000000..50e634f
--- /dev/null
+++ b/l2vpn_pe_rfs/python/l2vpn_pe_rfs/main.py
@@ -0,0 +1,65 @@
+# -*- mode: python; python-indent: 4 -*-
+import ncs
+from ncs.application import Service
+
+
+# ------------------------
+# SERVICE CALLBACK EXAMPLE
+# ------------------------
+class ServiceCallbacks(Service):
+
+ # The create() callback is invoked inside NCS FASTMAP and
+ # must always exist.
+ @Service.create
+ def cb_create(self, tctx, root, service, proplist):
+ self.log.info('Service create(service=', service._path, ')')
+
+ vars = ncs.template.Variables()
+ vars.add('DUMMY', '127.0.0.1')
+ template = ncs.template.Template(service)
+ template.apply('l2vpn_pe_rfs-template', vars)
+
+ # The pre_modification() and post_modification() callbacks are optional,
+ # and are invoked outside FASTMAP. pre_modification() is invoked before
+ # create, update, or delete of the service, as indicated by the enum
+ # ncs_service_operation op parameter. Conversely
+ # post_modification() is invoked after create, update, or delete
+ # of the service. These functions can be useful e.g. for
+ # allocations that should be stored and existing also when the
+ # service instance is removed.
+
+ # @Service.pre_modification
+ # def cb_pre_modification(self, tctx, op, kp, root, proplist):
+ # self.log.info('Service premod(service=', kp, ')')
+
+ # @Service.post_modification
+ # def cb_post_modification(self, tctx, op, kp, root, proplist):
+ # self.log.info('Service postmod(service=', kp, ')')
+
+
+# ---------------------------------------------
+# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
+# ---------------------------------------------
+class Main(ncs.application.Application):
+ def setup(self):
+ # The application class sets up logging for us. It is accessible
+ # through 'self.log' and is a ncs.log.Log instance.
+ self.log.info('Main RUNNING')
+
+ # Service callbacks require a registration for a 'service point',
+ # as specified in the corresponding data model.
+ #
+ self.register_service('l2vpn_pe_rfs-servicepoint', ServiceCallbacks)
+
+ # If we registered any callback(s) above, the Application class
+ # took care of creating a daemon (related to the service/action point).
+
+ # When this setup method is finished, all registrations are
+ # considered done and the application is 'started'.
+
+ def teardown(self):
+ # When the application is finished (which would happen if NCS went
+ # down, packages were reloaded or some error occurred) this teardown
+ # method will be called.
+
+ self.log.info('Main FINISHED')
diff --git a/l2vpn_pe_rfs/src/Makefile b/l2vpn_pe_rfs/src/Makefile
new file mode 100644
index 0000000..3a0f88c
--- /dev/null
+++ b/l2vpn_pe_rfs/src/Makefile
@@ -0,0 +1,32 @@
+all: fxs
+.PHONY: all
+
+# Include standard NCS examples build definitions and rules
+include $(NCS_DIR)/src/ncs/build/include.ncs.mk
+
+SRC = $(wildcard yang/*.yang)
+DIRS = ../load-dir
+FXS = $(SRC:yang/%.yang=../load-dir/%.fxs)
+
+## Uncomment and patch the line below if you have a dependency to a NED
+## or to other YANG files
+# YANGPATH += ../..//src/ncsc-out/modules/yang \
+# ../..//src/yang
+
+NCSCPATH = $(YANGPATH:%=--yangpath %)
+YANGERPATH = $(YANGPATH:%=--path %)
+
+fxs: $(DIRS) $(FXS)
+
+$(DIRS):
+ mkdir -p $@
+
+../load-dir/%.fxs: yang/%.yang
+ $(NCSC) `ls $*-ann.yang > /dev/null 2>&1 && echo "-a $*-ann.yang"` \
+ --fail-on-warnings \
+ $(NCSCPATH) \
+ -c -o $@ $<
+
+clean:
+ rm -rf $(DIRS)
+.PHONY: clean
diff --git a/l2vpn_pe_rfs/src/yang/l2vpn_pe_rfs.yang b/l2vpn_pe_rfs/src/yang/l2vpn_pe_rfs.yang
new file mode 100644
index 0000000..f01fa3e
--- /dev/null
+++ b/l2vpn_pe_rfs/src/yang/l2vpn_pe_rfs.yang
@@ -0,0 +1,94 @@
+module l2vpn_pe_rfs {
+
+ namespace "http://example.com/l2vpn_pe_rfs";
+ prefix l2vpn_pe_rfs;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import tailf-common {
+ prefix tailf;
+ }
+ import tailf-ncs {
+ prefix ncs;
+ }
+
+ description
+ "The PE part of the L2VPN service with QinQ to the switched network between PE and CPE";
+
+ revision 2024-07-10 {
+ description
+ "Initial revision.";
+ }
+
+ grouping interface_grouping {
+ leaf interface_name {
+ tailf:info "Interface name. For example TenGigE.";
+ type enumeration {
+ enum FastEthernet;
+ enum GigabitEthernet;
+ enum TenGigE;
+ enum TwentyFiveGigE;
+ enum FortyGigE;
+ enum FiftyGigE;
+ enum HundredGigE;
+ enum TwoHundredGigE;
+ enum FourHundredGigE;
+ enum EightHundredGigE;
+ enum xe;
+ }
+ }
+ leaf interface_number {
+ tailf:info "Interface number. For example 0 or 1/0";
+ type string {
+ pattern "[0-9]{1,2}(/[0-9]{1,2}){1,4}";
+ }
+ }
+ leaf interface_description {
+ tailf:info "Interface description.";
+ type string;
+ }
+ }
+
+ list l2vpn_pe_rfs {
+ description "This is an RFS skeleton service";
+
+ key name;
+ leaf name {
+ tailf:info "Unique service id";
+ tailf:cli-allow-range;
+ type string;
+ }
+
+ uses ncs:service-data;
+ ncs:servicepoint l2vpn_pe_rfs-servicepoint;
+
+ leaf device {
+ tailf:info "The PE device";
+ type leafref {
+ path "/ncs:devices/ncs:device/ncs:name";
+ }
+ }
+
+ leaf ovlan {
+ tailf:info "Outer VLAN eg 10";
+ type uint16;
+ mandatory true;
+ }
+
+ leaf ivlan {
+ tailf:info "Inner VLAN eg 20";
+ type uint16;
+ mandatory true;
+ }
+
+ leaf bridge_group_name {
+ tailf:info "Name of the L2VPN bridge group";
+ type string;
+ }
+
+ container interface {
+ uses interface_grouping;
+ }
+ }
+}
diff --git a/l2vpn_pe_rfs/templates/l2vpn_pe_rfs-template.xml b/l2vpn_pe_rfs/templates/l2vpn_pe_rfs-template.xml
new file mode 100644
index 0000000..59b4fc2
--- /dev/null
+++ b/l2vpn_pe_rfs/templates/l2vpn_pe_rfs-template.xml
@@ -0,0 +1,150 @@
+
+
+
+ {/device}
+
+
+
+
+
+
+ {/interface/interface_number}.{/ovlan}
+ {/interface/interface_description}
+ l2transport
+
+
+ {/ovlan}
+ {/ivlan}
+
+
+
+
+
+ 2
+ symmetric
+
+
+
+
+
+
+
+
+
+ {/interface/interface_number}.{/ovlan}
+ l2transport
+
+
+ {/ovlan}
+ {/ivlan}
+
+
+
+
+
+ 2
+ symmetric
+
+
+
+
+
+
+
+ {/ovlan}
+
+
+ {/ovlan}.{/ovlan}.{/ovlan}.1
+ 255.255.255.0
+
+
+
+
+
+
+
+ {/bridge_group_name}
+
+ {/bridge_group_name}
+
+ {/interface/interface_name}{/interface/interface_number}.{/ovlan}
+
+
+
+ BVI{/ovlan}
+
+
+
+
+
+
+
+
+
+
+
+ {/interface/interface_name}{/interface/interface_number}.{/ovlan}
+
+
+
+ {/interface/interface_description}
+
+
+ {/ovlan}
+ {/ivlan}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ BVI{/ovlan}
+
+
+
+ {/ovlan}.{/ovlan}.{/ovlan}.1
+ 255.255.255.0
+
+
+
+
+
+
+
+
+
+
+ {/bridge_group_name}
+
+
+ {/bridge_group_name}
+
+
+ {/interface/interface_name}{/interface/interface_number}.{/ovlan}
+
+
+
+
+ BVI{/ovlan}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/l2vpn_pe_rfs/test/Makefile b/l2vpn_pe_rfs/test/Makefile
new file mode 100644
index 0000000..32f02d6
--- /dev/null
+++ b/l2vpn_pe_rfs/test/Makefile
@@ -0,0 +1,37 @@
+DIRS = external internal
+
+ifeq ($(BUILD_JOB),external)
+DIR = external
+endif
+
+ifeq ($(BUILD_JOB),internal_realhw)
+DIR = internal
+JOB_DIR = realhw
+endif
+
+ifeq ($(BUILD_JOB),internal_simulated)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+ifeq ($(BUILD_JOB),)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+all: test
+
+build:
+ $(MAKE) -C $(DIR) build JOB_DIR=$(JOB_DIR) || exit 1
+
+clean:
+ $(MAKE) -C $(DIR) clean JOB_DIR=$(JOB_DIR) || exit 1
+
+test:
+ $(MAKE) -C $(DIR) test JOB_DIR=$(JOB_DIR) || exit 1
+
+desc:
+ @echo "==Test Cases for NED=="
+ @for d in $(DIRS) ; do \
+ $(MAKE) -sC $$d desc || exit 1; \
+ done
diff --git a/l2vpn_pe_rfs/test/internal/Makefile b/l2vpn_pe_rfs/test/internal/Makefile
new file mode 100644
index 0000000..982aa57
--- /dev/null
+++ b/l2vpn_pe_rfs/test/internal/Makefile
@@ -0,0 +1,21 @@
+DIRS = lux
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/l2vpn_pe_rfs/test/internal/lux/Makefile b/l2vpn_pe_rfs/test/internal/lux/Makefile
new file mode 100644
index 0000000..2558f02
--- /dev/null
+++ b/l2vpn_pe_rfs/test/internal/lux/Makefile
@@ -0,0 +1,21 @@
+DIRS = service
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/l2vpn_pe_rfs/test/internal/lux/service/Makefile b/l2vpn_pe_rfs/test/internal/lux/service/Makefile
new file mode 100644
index 0000000..d2dc5be
--- /dev/null
+++ b/l2vpn_pe_rfs/test/internal/lux/service/Makefile
@@ -0,0 +1,26 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+
+# Make sure the TARGET_DIR has got the following make targets:
+.PHONY: clean build start stop
+
+export TARGET_DIR=../../../../../..
+
+.PHONY: test
+test:
+ lux run.lux
+
+clean:
+ $(MAKE) -C $(TARGET_DIR) clean
+
+build:
+ $(MAKE) -C $(TARGET_DIR) build
+
+start:
+ $(MAKE) -C $(TARGET_DIR) start
+
+stop:
+ $(MAKE) -C $(TARGET_DIR) stop
diff --git a/l2vpn_pe_rfs/test/internal/lux/service/dummy-device.xml b/l2vpn_pe_rfs/test/internal/lux/service/dummy-device.xml
new file mode 100644
index 0000000..df09281
--- /dev/null
+++ b/l2vpn_pe_rfs/test/internal/lux/service/dummy-device.xml
@@ -0,0 +1,11 @@
+
+
+
+ eth
+ 192.168.110.1
+
+ southbound-locked
+
+
+
+
diff --git a/l2vpn_pe_rfs/test/internal/lux/service/dummy-service.xml b/l2vpn_pe_rfs/test/internal/lux/service/dummy-service.xml
new file mode 100644
index 0000000..9b48e30
--- /dev/null
+++ b/l2vpn_pe_rfs/test/internal/lux/service/dummy-service.xml
@@ -0,0 +1,12 @@
+
+
+
+ true
+
+
+
+ 33
+ eth
+ 127.0.0.1
+
+
diff --git a/l2vpn_pe_rfs/test/internal/lux/service/pyvm.xml b/l2vpn_pe_rfs/test/internal/lux/service/pyvm.xml
new file mode 100644
index 0000000..531521c
--- /dev/null
+++ b/l2vpn_pe_rfs/test/internal/lux/service/pyvm.xml
@@ -0,0 +1,8 @@
+
+
+
+ ./logs/ncs-python-vm
+ level-info
+
+
+
diff --git a/l2vpn_pe_rfs/test/internal/lux/service/run.lux b/l2vpn_pe_rfs/test/internal/lux/service/run.lux
new file mode 100644
index 0000000..63887d3
--- /dev/null
+++ b/l2vpn_pe_rfs/test/internal/lux/service/run.lux
@@ -0,0 +1,53 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+[global target_dir=../../../../../..]
+[config skip_unless=PYTHON]
+
+[shell top]
+ !make stop build
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ !rm ${target_dir}/ncs-cdb/*
+ ?SH-PROMPT:
+ !cp pyvm.xml ${target_dir}/ncs-cdb/.
+ ?SH-PROMPT:
+
+ !make start
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ [progress \nCreate a dummy device...\n]
+ !ncs_load -lm dummy-device.xml
+ ?SH-PROMPT:
+ [progress \nCreate a dummy device...ok\n]
+
+ [sleep 3]
+
+ [progress \nCreate the dummy service...\n]
+ !ncs_load -lm dummy-service.xml
+ ?SH-PROMPT:
+ [progress \nCreate the dummy service...ok\n]
+
+
+[shell log]
+ !cd ${target_dir}
+ ?SH-PROMPT:
+
+ [progress \nVerify that the service code has been invoked...\n]
+ !tail -f ./logs/ncs-python-vm-l2vpn_pe_rfs.log
+ ?.*Worker RUNNING.*
+ ?.*Service create.*
+ [progress \nVerify that the service code has been invoked...ok\n]
+
+
+[cleanup]
+ !make stop
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
diff --git a/l2vpn_sw_rfs/README b/l2vpn_sw_rfs/README
new file mode 100644
index 0000000..d027c67
--- /dev/null
+++ b/l2vpn_sw_rfs/README
@@ -0,0 +1,23 @@
+This is a generated Python package, made by:
+
+ ncs-make-package --service-skeleton python-and-template \
+ --component-class main.Main l2vpn_sw_rfs
+
+It contains a dummy YANG model which implements a minimal Service
+and an Action that doesn't really do anything useful. They are
+there just to get you going.
+
+You will also find two test cases in:
+
+ test/internal/lux/service/
+ test/internal/lux/action/
+
+that you can run if you have the 'lux' testing tool.
+Your top Makefile also need to implement some Make targets
+as described in the Makefiles of the test cases.
+You can also just read the corresponding run.lux tests and
+do them manually if you wish.
+
+The 'lux' test tool can be obtained from:
+
+ https://github.com/hawk/lux.git
diff --git a/l2vpn_sw_rfs/load-dir/l2vpn_sw_rfs.fxs b/l2vpn_sw_rfs/load-dir/l2vpn_sw_rfs.fxs
new file mode 100644
index 0000000..62f41ad
Binary files /dev/null and b/l2vpn_sw_rfs/load-dir/l2vpn_sw_rfs.fxs differ
diff --git a/l2vpn_sw_rfs/package-meta-data.xml b/l2vpn_sw_rfs/package-meta-data.xml
new file mode 100644
index 0000000..5713bbd
--- /dev/null
+++ b/l2vpn_sw_rfs/package-meta-data.xml
@@ -0,0 +1,13 @@
+
+ l2vpn_sw_rfs
+ 1.0
+ Generated Python package
+ 6.2
+
+
+ main
+
+ l2vpn_sw_rfs.main.Main
+
+
+
diff --git a/l2vpn_sw_rfs/python/l2vpn_sw_rfs/__init__.py b/l2vpn_sw_rfs/python/l2vpn_sw_rfs/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/l2vpn_sw_rfs/python/l2vpn_sw_rfs/main.py b/l2vpn_sw_rfs/python/l2vpn_sw_rfs/main.py
new file mode 100644
index 0000000..baa51d7
--- /dev/null
+++ b/l2vpn_sw_rfs/python/l2vpn_sw_rfs/main.py
@@ -0,0 +1,65 @@
+# -*- mode: python; python-indent: 4 -*-
+import ncs
+from ncs.application import Service
+
+
+# ------------------------
+# SERVICE CALLBACK EXAMPLE
+# ------------------------
+class ServiceCallbacks(Service):
+
+ # The create() callback is invoked inside NCS FASTMAP and
+ # must always exist.
+ @Service.create
+ def cb_create(self, tctx, root, service, proplist):
+ self.log.info('Service create(service=', service._path, ')')
+
+ vars = ncs.template.Variables()
+ vars.add('DUMMY', '127.0.0.1')
+ template = ncs.template.Template(service)
+ template.apply('l2vpn_sw_rfs-template', vars)
+
+ # The pre_modification() and post_modification() callbacks are optional,
+ # and are invoked outside FASTMAP. pre_modification() is invoked before
+ # create, update, or delete of the service, as indicated by the enum
+ # ncs_service_operation op parameter. Conversely
+ # post_modification() is invoked after create, update, or delete
+ # of the service. These functions can be useful e.g. for
+ # allocations that should be stored and existing also when the
+ # service instance is removed.
+
+ # @Service.pre_modification
+ # def cb_pre_modification(self, tctx, op, kp, root, proplist):
+ # self.log.info('Service premod(service=', kp, ')')
+
+ # @Service.post_modification
+ # def cb_post_modification(self, tctx, op, kp, root, proplist):
+ # self.log.info('Service postmod(service=', kp, ')')
+
+
+# ---------------------------------------------
+# COMPONENT THREAD THAT WILL BE STARTED BY NCS.
+# ---------------------------------------------
+class Main(ncs.application.Application):
+ def setup(self):
+ # The application class sets up logging for us. It is accessible
+ # through 'self.log' and is a ncs.log.Log instance.
+ self.log.info('Main RUNNING')
+
+ # Service callbacks require a registration for a 'service point',
+ # as specified in the corresponding data model.
+ #
+ self.register_service('l2vpn_sw_rfs-servicepoint', ServiceCallbacks)
+
+ # If we registered any callback(s) above, the Application class
+ # took care of creating a daemon (related to the service/action point).
+
+ # When this setup method is finished, all registrations are
+ # considered done and the application is 'started'.
+
+ def teardown(self):
+ # When the application is finished (which would happen if NCS went
+ # down, packages were reloaded or some error occurred) this teardown
+ # method will be called.
+
+ self.log.info('Main FINISHED')
diff --git a/l2vpn_sw_rfs/src/Makefile b/l2vpn_sw_rfs/src/Makefile
new file mode 100644
index 0000000..3a0f88c
--- /dev/null
+++ b/l2vpn_sw_rfs/src/Makefile
@@ -0,0 +1,32 @@
+all: fxs
+.PHONY: all
+
+# Include standard NCS examples build definitions and rules
+include $(NCS_DIR)/src/ncs/build/include.ncs.mk
+
+SRC = $(wildcard yang/*.yang)
+DIRS = ../load-dir
+FXS = $(SRC:yang/%.yang=../load-dir/%.fxs)
+
+## Uncomment and patch the line below if you have a dependency to a NED
+## or to other YANG files
+# YANGPATH += ../..//src/ncsc-out/modules/yang \
+# ../..//src/yang
+
+NCSCPATH = $(YANGPATH:%=--yangpath %)
+YANGERPATH = $(YANGPATH:%=--path %)
+
+fxs: $(DIRS) $(FXS)
+
+$(DIRS):
+ mkdir -p $@
+
+../load-dir/%.fxs: yang/%.yang
+ $(NCSC) `ls $*-ann.yang > /dev/null 2>&1 && echo "-a $*-ann.yang"` \
+ --fail-on-warnings \
+ $(NCSCPATH) \
+ -c -o $@ $<
+
+clean:
+ rm -rf $(DIRS)
+.PHONY: clean
diff --git a/l2vpn_sw_rfs/src/yang/l2vpn_sw_rfs.yang b/l2vpn_sw_rfs/src/yang/l2vpn_sw_rfs.yang
new file mode 100644
index 0000000..6bfae48
--- /dev/null
+++ b/l2vpn_sw_rfs/src/yang/l2vpn_sw_rfs.yang
@@ -0,0 +1,98 @@
+module l2vpn_sw_rfs {
+
+ namespace "http://example.com/l2vpn_sw_rfs";
+ prefix l2vpn_sw_rfs;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import tailf-common {
+ prefix tailf;
+ }
+ import tailf-ncs {
+ prefix ncs;
+ }
+
+ description
+ "The switch part of the L2VPN service with QinQ in the switched network between PE and CPE";
+
+ revision 2024-07-10 {
+ description
+ "Initial revision.";
+ }
+
+ grouping interface_grouping {
+ leaf interface_name {
+ tailf:info "Interface name. For example TenGigE.";
+ type enumeration {
+ enum FastEthernet;
+ enum GigabitEthernet;
+ enum TenGigE;
+ enum TwentyFiveGigE;
+ enum FortyGigE;
+ enum FiftyGigE;
+ enum HundredGigE;
+ enum TwoHundredGigE;
+ enum FourHundredGigE;
+ enum EightHundredGigE;
+ enum xe;
+ }
+ }
+ leaf interface_number {
+ tailf:info "Interface number. For example 0 or 1/0";
+ type string {
+ pattern "[0-9]{1,2}(/[0-9]{1,2}){1,4}";
+ }
+ }
+ leaf interface_description {
+ tailf:info "Interface description.";
+ type string;
+ }
+ }
+
+ list l2vpn_sw_rfs {
+ description "This is an RFS skeleton service";
+
+ key name;
+ leaf name {
+ tailf:info "Unique service id";
+ tailf:cli-allow-range;
+ type string;
+ }
+
+ uses ncs:service-data;
+ ncs:servicepoint l2vpn_sw_rfs-servicepoint;
+
+ leaf device {
+ tailf:info "The Switch device";
+ type leafref {
+ path "/ncs:devices/ncs:device/ncs:name";
+ }
+ }
+
+ leaf ovlan {
+ tailf:info "Outer VLAN eg 10";
+ type uint16;
+ mandatory true;
+ }
+
+ leaf ivlan {
+ tailf:info "Inner VLAN eg 20";
+ type uint16;
+ mandatory true;
+ }
+
+ leaf vlan_name {
+ tailf:info "Name of the L2VPN bridge group";
+ type string;
+ }
+
+ container pe_interface {
+ uses interface_grouping;
+ }
+
+ container cpe_interface {
+ uses interface_grouping;
+ }
+ }
+}
diff --git a/l2vpn_sw_rfs/templates/l2vpn_sw_rfs-template.xml b/l2vpn_sw_rfs/templates/l2vpn_sw_rfs-template.xml
new file mode 100644
index 0000000..cdea7ac
--- /dev/null
+++ b/l2vpn_sw_rfs/templates/l2vpn_sw_rfs-template.xml
@@ -0,0 +1,85 @@
+
+
+
+ {/device}
+
+
+
+ {/ovlan}
+ {/vlan_name}
+
+
+
+
+
+ {/cpe_interface/interface_number}
+ {/cpe_interface/interface_description}
+
+
+
+
+
+ {/ovlan}
+
+
+
+
+
+
+
+ {/cpe_interface/interface_number}
+ {/cpe_interface/interface_description}
+
+
+
+
+
+ {/ovlan}
+
+
+
+
+
+
+
+ {/pe_interface/interface_number}
+ {/pe_interface/interface_description}
+
+
+
+
+
+ {/ovlan}
+
+
+
+
+
+
+ {/pe_interface/interface_number}
+ {/pe_interface/interface_description}
+
+
+
+
+
+ {/ovlan}
+
+
+
+
+
+
+
+
+
diff --git a/l2vpn_sw_rfs/test/Makefile b/l2vpn_sw_rfs/test/Makefile
new file mode 100644
index 0000000..32f02d6
--- /dev/null
+++ b/l2vpn_sw_rfs/test/Makefile
@@ -0,0 +1,37 @@
+DIRS = external internal
+
+ifeq ($(BUILD_JOB),external)
+DIR = external
+endif
+
+ifeq ($(BUILD_JOB),internal_realhw)
+DIR = internal
+JOB_DIR = realhw
+endif
+
+ifeq ($(BUILD_JOB),internal_simulated)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+ifeq ($(BUILD_JOB),)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+all: test
+
+build:
+ $(MAKE) -C $(DIR) build JOB_DIR=$(JOB_DIR) || exit 1
+
+clean:
+ $(MAKE) -C $(DIR) clean JOB_DIR=$(JOB_DIR) || exit 1
+
+test:
+ $(MAKE) -C $(DIR) test JOB_DIR=$(JOB_DIR) || exit 1
+
+desc:
+ @echo "==Test Cases for NED=="
+ @for d in $(DIRS) ; do \
+ $(MAKE) -sC $$d desc || exit 1; \
+ done
diff --git a/l2vpn_sw_rfs/test/internal/Makefile b/l2vpn_sw_rfs/test/internal/Makefile
new file mode 100644
index 0000000..982aa57
--- /dev/null
+++ b/l2vpn_sw_rfs/test/internal/Makefile
@@ -0,0 +1,21 @@
+DIRS = lux
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/l2vpn_sw_rfs/test/internal/lux/Makefile b/l2vpn_sw_rfs/test/internal/lux/Makefile
new file mode 100644
index 0000000..2558f02
--- /dev/null
+++ b/l2vpn_sw_rfs/test/internal/lux/Makefile
@@ -0,0 +1,21 @@
+DIRS = service
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/l2vpn_sw_rfs/test/internal/lux/service/Makefile b/l2vpn_sw_rfs/test/internal/lux/service/Makefile
new file mode 100644
index 0000000..d2dc5be
--- /dev/null
+++ b/l2vpn_sw_rfs/test/internal/lux/service/Makefile
@@ -0,0 +1,26 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+
+# Make sure the TARGET_DIR has got the following make targets:
+.PHONY: clean build start stop
+
+export TARGET_DIR=../../../../../..
+
+.PHONY: test
+test:
+ lux run.lux
+
+clean:
+ $(MAKE) -C $(TARGET_DIR) clean
+
+build:
+ $(MAKE) -C $(TARGET_DIR) build
+
+start:
+ $(MAKE) -C $(TARGET_DIR) start
+
+stop:
+ $(MAKE) -C $(TARGET_DIR) stop
diff --git a/l2vpn_sw_rfs/test/internal/lux/service/dummy-device.xml b/l2vpn_sw_rfs/test/internal/lux/service/dummy-device.xml
new file mode 100644
index 0000000..df09281
--- /dev/null
+++ b/l2vpn_sw_rfs/test/internal/lux/service/dummy-device.xml
@@ -0,0 +1,11 @@
+
+
+
+ eth
+ 192.168.110.1
+
+ southbound-locked
+
+
+
+
diff --git a/l2vpn_sw_rfs/test/internal/lux/service/dummy-service.xml b/l2vpn_sw_rfs/test/internal/lux/service/dummy-service.xml
new file mode 100644
index 0000000..c6df86d
--- /dev/null
+++ b/l2vpn_sw_rfs/test/internal/lux/service/dummy-service.xml
@@ -0,0 +1,12 @@
+
+
+
+ true
+
+
+
+ 33
+ eth
+ 127.0.0.1
+
+
diff --git a/l2vpn_sw_rfs/test/internal/lux/service/pyvm.xml b/l2vpn_sw_rfs/test/internal/lux/service/pyvm.xml
new file mode 100644
index 0000000..531521c
--- /dev/null
+++ b/l2vpn_sw_rfs/test/internal/lux/service/pyvm.xml
@@ -0,0 +1,8 @@
+
+
+
+ ./logs/ncs-python-vm
+ level-info
+
+
+
diff --git a/l2vpn_sw_rfs/test/internal/lux/service/run.lux b/l2vpn_sw_rfs/test/internal/lux/service/run.lux
new file mode 100644
index 0000000..b248ee8
--- /dev/null
+++ b/l2vpn_sw_rfs/test/internal/lux/service/run.lux
@@ -0,0 +1,53 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+[global target_dir=../../../../../..]
+[config skip_unless=PYTHON]
+
+[shell top]
+ !make stop build
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ !rm ${target_dir}/ncs-cdb/*
+ ?SH-PROMPT:
+ !cp pyvm.xml ${target_dir}/ncs-cdb/.
+ ?SH-PROMPT:
+
+ !make start
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ [progress \nCreate a dummy device...\n]
+ !ncs_load -lm dummy-device.xml
+ ?SH-PROMPT:
+ [progress \nCreate a dummy device...ok\n]
+
+ [sleep 3]
+
+ [progress \nCreate the dummy service...\n]
+ !ncs_load -lm dummy-service.xml
+ ?SH-PROMPT:
+ [progress \nCreate the dummy service...ok\n]
+
+
+[shell log]
+ !cd ${target_dir}
+ ?SH-PROMPT:
+
+ [progress \nVerify that the service code has been invoked...\n]
+ !tail -f ./logs/ncs-python-vm-l2vpn_sw_rfs.log
+ ?.*Worker RUNNING.*
+ ?.*Service create.*
+ [progress \nVerify that the service code has been invoked...ok\n]
+
+
+[cleanup]
+ !make stop
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
diff --git a/l3vpn_cpe_rfs/templates/l3vpn_cpe_rfs-template.xml b/l3vpn_cpe_rfs/templates/l3vpn_cpe_rfs-template.xml
index 04a9c83..8179c05 100644
--- a/l3vpn_cpe_rfs/templates/l3vpn_cpe_rfs-template.xml
+++ b/l3vpn_cpe_rfs/templates/l3vpn_cpe_rfs-template.xml
@@ -16,7 +16,7 @@
-
+
{/interface/interface_number}
diff --git a/l3vpn_qinq_cfs/load-dir/l3vpn_qinq_cfs.fxs b/l3vpn_qinq_cfs/load-dir/l3vpn_qinq_cfs.fxs
deleted file mode 100644
index 5696a99..0000000
Binary files a/l3vpn_qinq_cfs/load-dir/l3vpn_qinq_cfs.fxs and /dev/null differ
diff --git a/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/main.py b/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/main.py
deleted file mode 100644
index d071e09..0000000
--- a/l3vpn_qinq_cfs/python/l3vpn_qinq_cfs/main.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# -*- mode: python; python-indent: 4 -*-
-import ncs
-from ncs.application import Service
-import requests
-
-class ServiceCallbacks(Service):
-
- @Service.create
- def cb_create(self, tctx, root, service, proplist):
- self.log.info('Service create(service=', service._path, ')')
-
- api_url="http://10.101.180.45/api/ipam/prefixes/1/available-ips/"
- headers = {"Authorization":"Token e657404c0e1f57481ad0a06c293e8fe794e720ac"}
-
- vars = ncs.template.Variables()
- template = ncs.template.Template(service)
- for endpoint in service.endpoint:
- # PE part
- response=requests.get(api_url, headers=headers, verify=False)
- self.log.info('Netbox response: ', response.json)
- ipv4_address = response.json["address"]
- self.log.info('Address: ', ipv4_address)
-
- name = "L3VPN-"+str(endpoint.pe_device)+"-"+str(endpoint.id)
- vars.add('name', name)
- vars.add('pe_device', endpoint.pe_device)
- vars.add('cpe_interface_address', endpoint.cpe_interface.ipv4_address)
- vars.add('pe_interface_name', endpoint.pe_interface.interface_name)
- vars.add('pe_interface_number', endpoint.pe_interface.interface_number)
- pe_interface_description = "L3VPN interface to " + str(endpoint.cpe_device) + ", interface " + str(endpoint.cpe_interface.interface_name) + str(endpoint.cpe_interface.interface_number)
- vars.add('pe_interface_description', pe_interface_description)
- vars.add('pe_ipv4_address', endpoint.pe_interface.ipv4_address)
- vars.add('pe_ipv4_mask', endpoint.pe_interface.ipv4_mask)
- self.log.info('ipv4_mask: ', endpoint.pe_interface.ipv4_mask)
- template.apply('l3vpn_qinq_pe-template', vars)
-
-
-class Main(ncs.application.Application):
- def setup(self):
- self.log.info('Main RUNNING')
- self.register_service('l3vpn_qinq_cfs-servicepoint', ServiceCallbacks)
-
- def teardown(self):
- self.log.info('Main FINISHED')
diff --git a/netbox_test/README b/netbox_test/README
new file mode 100644
index 0000000..e87b238
--- /dev/null
+++ b/netbox_test/README
@@ -0,0 +1,23 @@
+This is a generated Python package, made by:
+
+ ncs-make-package --service-skeleton python-and-template \
+ --component-class main.Main netbox_test
+
+It contains a dummy YANG model which implements a minimal Service
+and an Action that doesn't really do anything useful. They are
+there just to get you going.
+
+You will also find two test cases in:
+
+ test/internal/lux/service/
+ test/internal/lux/action/
+
+that you can run if you have the 'lux' testing tool.
+Your top Makefile also need to implement some Make targets
+as described in the Makefiles of the test cases.
+You can also just read the corresponding run.lux tests and
+do them manually if you wish.
+
+The 'lux' test tool can be obtained from:
+
+ https://github.com/hawk/lux.git
diff --git a/netbox_test/load-dir/netbox_test.fxs b/netbox_test/load-dir/netbox_test.fxs
new file mode 100644
index 0000000..2aa1278
Binary files /dev/null and b/netbox_test/load-dir/netbox_test.fxs differ
diff --git a/netbox_test/package-meta-data.xml b/netbox_test/package-meta-data.xml
new file mode 100644
index 0000000..67db70b
--- /dev/null
+++ b/netbox_test/package-meta-data.xml
@@ -0,0 +1,13 @@
+
+ netbox_test
+ 1.0
+ Generated Python package
+ 6.2
+
+
+ main
+
+ netbox_test.main.Main
+
+
+
diff --git a/netbox_test/python/netbox_test/__init__.py b/netbox_test/python/netbox_test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/netbox_test/python/netbox_test/main.py b/netbox_test/python/netbox_test/main.py
new file mode 100644
index 0000000..27a9454
--- /dev/null
+++ b/netbox_test/python/netbox_test/main.py
@@ -0,0 +1,50 @@
+# -*- mode: python; python-indent: 4 -*-
+import ncs
+from ncs.application import Service
+import requests
+import socket
+import struct
+
+class ServiceCallbacks(Service):
+ @Service.create
+ def cb_create(self, tctx, root, service, proplist):
+ self.log.info('Service create(service=', service._path, ')')
+
+ def cidr_to_netmask(cidr):
+ network, net_bits = cidr.split('/')
+ host_bits = 32 - int(net_bits)
+ netmask = socket.inet_ntoa(struct.pack('!I', (1 << 32) - (1 << host_bits)))
+ return network, netmask, net_bits
+
+ api_url="https://10.101.180.45/api/ipam/prefixes/1/available-ips/"
+ headers = {"Authorization":"Token e657404c0e1f57481ad0a06c293e8fe794e720ac", "Accept": "application/json", "Content-Type": "application/json"}
+ body = {}
+
+ response=requests.post(api_url, headers=headers, json=body, verify=False)
+ self.log.info('Netbox response, IP address reservation: ', response.json())
+ ipv4_cidr = response.json()["address"]
+ self.log.info('CIDR from Netbox: ', ipv4_cidr)
+ ipv4_address, ipv4_mask, ipv4_cidr_mask = cidr_to_netmask(ipv4_cidr)
+ self.log.info('Address: ', ipv4_address)
+ self.log.info('Net Mask: ', ipv4_mask)
+ self.log.info('CIDR Mask: ', ipv4_cidr_mask)
+
+ api_url="https://10.101.180.45/api/ipam/vlan-groups/1/available-vlans/"
+ body = {"name": service.ovlan_name}
+
+ response=requests.post(api_url, headers=headers, json=body, verify=False)
+ self.log.info('Netbox response, VLAN reservation: ', response.json())
+ vlan_id = response.json()["vid"]
+ self.log.info('VLAN from Netbox: ', vlan_id)
+
+ vars = ncs.template.Variables()
+ template = ncs.template.Template(service)
+ template.apply('netbox_test-template', vars)
+
+class Main(ncs.application.Application):
+ def setup(self):
+ self.log.info('Main RUNNING')
+ self.register_service('netbox_test-servicepoint', ServiceCallbacks)
+
+ def teardown(self):
+ self.log.info('Main FINISHED')
diff --git a/netbox_test/src/Makefile b/netbox_test/src/Makefile
new file mode 100644
index 0000000..3a0f88c
--- /dev/null
+++ b/netbox_test/src/Makefile
@@ -0,0 +1,32 @@
+all: fxs
+.PHONY: all
+
+# Include standard NCS examples build definitions and rules
+include $(NCS_DIR)/src/ncs/build/include.ncs.mk
+
+SRC = $(wildcard yang/*.yang)
+DIRS = ../load-dir
+FXS = $(SRC:yang/%.yang=../load-dir/%.fxs)
+
+## Uncomment and patch the line below if you have a dependency to a NED
+## or to other YANG files
+# YANGPATH += ../..//src/ncsc-out/modules/yang \
+# ../..//src/yang
+
+NCSCPATH = $(YANGPATH:%=--yangpath %)
+YANGERPATH = $(YANGPATH:%=--path %)
+
+fxs: $(DIRS) $(FXS)
+
+$(DIRS):
+ mkdir -p $@
+
+../load-dir/%.fxs: yang/%.yang
+ $(NCSC) `ls $*-ann.yang > /dev/null 2>&1 && echo "-a $*-ann.yang"` \
+ --fail-on-warnings \
+ $(NCSCPATH) \
+ -c -o $@ $<
+
+clean:
+ rm -rf $(DIRS)
+.PHONY: clean
diff --git a/netbox_test/src/yang/netbox_test.yang b/netbox_test/src/yang/netbox_test.yang
new file mode 100644
index 0000000..13dfa20
--- /dev/null
+++ b/netbox_test/src/yang/netbox_test.yang
@@ -0,0 +1,55 @@
+module netbox_test {
+
+ namespace "http://example.com/netbox_test";
+ prefix netbox_test;
+
+ import ietf-inet-types {
+ prefix inet;
+ }
+ import tailf-common {
+ prefix tailf;
+ }
+ import tailf-ncs {
+ prefix ncs;
+ }
+
+ description
+ "Bla bla...";
+
+ revision 2016-01-01 {
+ description
+ "Initial revision.";
+ }
+
+ list netbox_test {
+ description "This is an RFS skeleton service";
+
+ key name;
+ leaf name {
+ tailf:info "Unique service id";
+ tailf:cli-allow-range;
+ type string;
+ }
+
+ uses ncs:service-data;
+ ncs:servicepoint netbox_test-servicepoint;
+
+ // may replace this with other ways of refering to the devices.
+ leaf-list device {
+ type leafref {
+ path "/ncs:devices/ncs:device/ncs:name";
+ }
+ }
+
+ leaf ovlan_name {
+ tailf:info "VLAN name must be unigue for each reservation from same Netbox VLAN Group";
+ type string;
+ mandatory true;
+ }
+
+ // replace with your own stuff here
+ leaf dummy {
+ type inet:ipv4-address;
+ }
+ }
+}
diff --git a/netbox_test/templates/netbox_test-template.xml b/netbox_test/templates/netbox_test-template.xml
new file mode 100644
index 0000000..1c16c7d
--- /dev/null
+++ b/netbox_test/templates/netbox_test-template.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ {/device}
+
+
+
+
+
+
diff --git a/netbox_test/test/Makefile b/netbox_test/test/Makefile
new file mode 100644
index 0000000..32f02d6
--- /dev/null
+++ b/netbox_test/test/Makefile
@@ -0,0 +1,37 @@
+DIRS = external internal
+
+ifeq ($(BUILD_JOB),external)
+DIR = external
+endif
+
+ifeq ($(BUILD_JOB),internal_realhw)
+DIR = internal
+JOB_DIR = realhw
+endif
+
+ifeq ($(BUILD_JOB),internal_simulated)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+ifeq ($(BUILD_JOB),)
+DIR = internal
+JOB_DIR = simulated
+endif
+
+all: test
+
+build:
+ $(MAKE) -C $(DIR) build JOB_DIR=$(JOB_DIR) || exit 1
+
+clean:
+ $(MAKE) -C $(DIR) clean JOB_DIR=$(JOB_DIR) || exit 1
+
+test:
+ $(MAKE) -C $(DIR) test JOB_DIR=$(JOB_DIR) || exit 1
+
+desc:
+ @echo "==Test Cases for NED=="
+ @for d in $(DIRS) ; do \
+ $(MAKE) -sC $$d desc || exit 1; \
+ done
diff --git a/netbox_test/test/internal/Makefile b/netbox_test/test/internal/Makefile
new file mode 100644
index 0000000..982aa57
--- /dev/null
+++ b/netbox_test/test/internal/Makefile
@@ -0,0 +1,21 @@
+DIRS = lux
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/netbox_test/test/internal/lux/Makefile b/netbox_test/test/internal/lux/Makefile
new file mode 100644
index 0000000..2558f02
--- /dev/null
+++ b/netbox_test/test/internal/lux/Makefile
@@ -0,0 +1,21 @@
+DIRS = service
+
+build:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d build || exit 1; \
+ done
+
+clean:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d clean || exit 1; \
+ done
+
+test:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d test || exit 1; \
+ done
+
+desc:
+ @for d in $(DIRS) ; do \
+ $(MAKE) -C $$d desc || exit 1; \
+ done
diff --git a/netbox_test/test/internal/lux/service/Makefile b/netbox_test/test/internal/lux/service/Makefile
new file mode 100644
index 0000000..d2dc5be
--- /dev/null
+++ b/netbox_test/test/internal/lux/service/Makefile
@@ -0,0 +1,26 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+
+# Make sure the TARGET_DIR has got the following make targets:
+.PHONY: clean build start stop
+
+export TARGET_DIR=../../../../../..
+
+.PHONY: test
+test:
+ lux run.lux
+
+clean:
+ $(MAKE) -C $(TARGET_DIR) clean
+
+build:
+ $(MAKE) -C $(TARGET_DIR) build
+
+start:
+ $(MAKE) -C $(TARGET_DIR) start
+
+stop:
+ $(MAKE) -C $(TARGET_DIR) stop
diff --git a/netbox_test/test/internal/lux/service/dummy-device.xml b/netbox_test/test/internal/lux/service/dummy-device.xml
new file mode 100644
index 0000000..df09281
--- /dev/null
+++ b/netbox_test/test/internal/lux/service/dummy-device.xml
@@ -0,0 +1,11 @@
+
+
+
+ eth
+ 192.168.110.1
+
+ southbound-locked
+
+
+
+
diff --git a/netbox_test/test/internal/lux/service/dummy-service.xml b/netbox_test/test/internal/lux/service/dummy-service.xml
new file mode 100644
index 0000000..4e08e71
--- /dev/null
+++ b/netbox_test/test/internal/lux/service/dummy-service.xml
@@ -0,0 +1,12 @@
+
+
+
+ true
+
+
+
+ 33
+ eth
+ 127.0.0.1
+
+
diff --git a/netbox_test/test/internal/lux/service/pyvm.xml b/netbox_test/test/internal/lux/service/pyvm.xml
new file mode 100644
index 0000000..531521c
--- /dev/null
+++ b/netbox_test/test/internal/lux/service/pyvm.xml
@@ -0,0 +1,8 @@
+
+
+
+ ./logs/ncs-python-vm
+ level-info
+
+
+
diff --git a/netbox_test/test/internal/lux/service/run.lux b/netbox_test/test/internal/lux/service/run.lux
new file mode 100644
index 0000000..63b49a8
--- /dev/null
+++ b/netbox_test/test/internal/lux/service/run.lux
@@ -0,0 +1,53 @@
+#
+# The 'lux' test tool can be obtained from:
+#
+# https://github.com/hawk/lux.git
+#
+[global target_dir=../../../../../..]
+[config skip_unless=PYTHON]
+
+[shell top]
+ !make stop build
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ !rm ${target_dir}/ncs-cdb/*
+ ?SH-PROMPT:
+ !cp pyvm.xml ${target_dir}/ncs-cdb/.
+ ?SH-PROMPT:
+
+ !make start
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT:
+
+ [progress \nCreate a dummy device...\n]
+ !ncs_load -lm dummy-device.xml
+ ?SH-PROMPT:
+ [progress \nCreate a dummy device...ok\n]
+
+ [sleep 3]
+
+ [progress \nCreate the dummy service...\n]
+ !ncs_load -lm dummy-service.xml
+ ?SH-PROMPT:
+ [progress \nCreate the dummy service...ok\n]
+
+
+[shell log]
+ !cd ${target_dir}
+ ?SH-PROMPT:
+
+ [progress \nVerify that the service code has been invoked...\n]
+ !tail -f ./logs/ncs-python-vm-netbox_test.log
+ ?.*Worker RUNNING.*
+ ?.*Service create.*
+ [progress \nVerify that the service code has been invoked...ok\n]
+
+
+[cleanup]
+ !make stop
+ !echo ==$$?==
+ ?==0==
+ ?SH-PROMPT: