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: