Skip to content
Snippets Groups Projects
Commit eda84a16 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Device - gNMI OpenConfig Driver:

- Corrected cleanup of interfaces in internal libyang yang validator data model
- Add libyang examples
- Updated libyang python bindings to 2.8.4
parent 87e715aa
No related branches found
No related tags found
1 merge request!299Resolve "(CTTC) Fix CI/CD pipeline end-to-end tests not passing"
......@@ -24,7 +24,7 @@ Flask-HTTPAuth==4.5.0
Flask-RESTful==0.3.9
ipaddress
Jinja2==3.0.3
libyang==2.8.0
libyang==2.8.4
macaddress
ncclient==0.6.15
numpy<2.0.0
......
# Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json, libyang, logging, os
from typing import Dict
logging.basicConfig(level=logging.DEBUG)
LOGGER = logging.getLogger(__name__)
YANG_BASE_PATH = '/home/tfs/tfs-ctrl/src/device/service/drivers/gnmi_openconfig/git/openconfig/public'
YANG_SEARCH_PATHS = ':'.join([
os.path.join(YANG_BASE_PATH, 'release'),
os.path.join(YANG_BASE_PATH, 'third_party'),
])
YANG_MODULES = [
'iana-if-type',
'openconfig-bgp-types',
'openconfig-vlan-types',
'openconfig-interfaces',
'openconfig-if-8021x',
'openconfig-if-aggregate',
'openconfig-if-ethernet-ext',
'openconfig-if-ethernet',
'openconfig-if-ip-ext',
'openconfig-if-ip',
'openconfig-if-poe',
'openconfig-if-sdn-ext',
'openconfig-if-tunnel',
'openconfig-vlan',
'openconfig-types',
'openconfig-policy-types',
'openconfig-mpls-types',
'openconfig-network-instance-types',
'openconfig-network-instance',
'openconfig-platform',
'openconfig-platform-controller-card',
'openconfig-platform-cpu',
'openconfig-platform-ext',
'openconfig-platform-fabric',
'openconfig-platform-fan',
'openconfig-platform-integrated-circuit',
'openconfig-platform-linecard',
'openconfig-platform-pipeline-counters',
'openconfig-platform-port',
'openconfig-platform-psu',
'openconfig-platform-software',
'openconfig-platform-transceiver',
'openconfig-platform-types',
]
class YangHandler:
def __init__(self) -> None:
self._yang_context = libyang.Context(YANG_SEARCH_PATHS)
self._loaded_modules = set()
for yang_module_name in YANG_MODULES:
LOGGER.info('Loading module: {:s}'.format(str(yang_module_name)))
self._yang_context.load_module(yang_module_name).feature_enable_all()
self._loaded_modules.add(yang_module_name)
self._data_path_instances = dict()
def get_data_paths(self) -> Dict[str, libyang.DNode]:
return self._data_path_instances
def get_data_path(self, path : str) -> libyang.DNode:
data_path_instance = self._data_path_instances.get(path)
if data_path_instance is None:
data_path_instance = self._yang_context.create_data_path(path)
self._data_path_instances[path] = data_path_instance
return data_path_instance
def destroy(self) -> None:
self._yang_context.destroy()
def main():
yang_handler = YangHandler()
LOGGER.info('YangHandler Data (before):')
for path, dnode in yang_handler.get_data_paths().items():
LOGGER.info('|-> {:s}: {:s}'.format(str(path), json.dumps(dnode.print_dict())))
if_name = 'eth1'
sif_index = 0
enabled = True
address_ip = '172.16.0.1'
address_ip2 = '192.168.0.1'
address_prefix = 24
mtu = 1500
yang_ifs : libyang.DContainer = yang_handler.get_data_path('/openconfig-interfaces:interfaces')
yang_if_path = 'interface[name="{:s}"]'.format(if_name)
yang_if : libyang.DContainer = yang_ifs.create_path(yang_if_path)
yang_if.create_path('config/name', if_name)
yang_if.create_path('config/enabled', enabled)
yang_if.create_path('config/mtu', mtu )
yang_sifs : libyang.DContainer = yang_if.create_path('subinterfaces')
yang_sif_path = 'subinterface[index="{:d}"]'.format(sif_index)
yang_sif : libyang.DContainer = yang_sifs.create_path(yang_sif_path)
yang_sif.create_path('config/index', sif_index)
yang_sif.create_path('config/enabled', enabled )
yang_ipv4 : libyang.DContainer = yang_sif.create_path('openconfig-if-ip:ipv4')
yang_ipv4.create_path('config/enabled', enabled)
yang_ipv4_addrs : libyang.DContainer = yang_ipv4.create_path('addresses')
yang_ipv4_addr_path = 'address[ip="{:s}"]'.format(address_ip)
yang_ipv4_addr : libyang.DContainer = yang_ipv4_addrs.create_path(yang_ipv4_addr_path)
yang_ipv4_addr.create_path('config/ip', address_ip )
yang_ipv4_addr.create_path('config/prefix-length', address_prefix)
yang_ipv4_addr_path2 = 'address[ip="{:s}"]'.format(address_ip2)
yang_ipv4_addr2 : libyang.DContainer = yang_ipv4_addrs.create_path(yang_ipv4_addr_path2)
yang_ipv4_addr2.create_path('config/ip', address_ip2 )
yang_ipv4_addr2.create_path('config/prefix-length', address_prefix)
str_data = yang_if.print_mem('json')
json_data = json.loads(str_data)
json_data = json_data['openconfig-interfaces:interface'][0]
str_data = json.dumps(json_data, indent=4)
LOGGER.info('Resulting Request (before unlink): {:s}'.format(str_data))
yang_ipv4_addr2.unlink()
root_node : libyang.DContainer = yang_handler.get_data_path('/openconfig-interfaces:interfaces')
LOGGER.info('root_node={:s}'.format(str(root_node.print_mem('json'))))
for s in root_node.siblings():
LOGGER.info('sibling: {:s}'.format(str(s)))
PATH_TMPL = '/openconfig-interfaces:interfaces/interface[name="{:s}"]/subinterfaces/subinterface[index="{:d}"]'
yang_sif = root_node.find_path(PATH_TMPL.format(if_name, sif_index))
if yang_sif is not None:
LOGGER.info('yang_sif={:s}'.format(str(yang_sif.print_mem('json'))))
yang_sif.unlink()
yang_sif.free()
str_data = yang_if.print_mem('json')
json_data = json.loads(str_data)
json_data = json_data['openconfig-interfaces:interface'][0]
str_data = json.dumps(json_data, indent=4)
LOGGER.info('Resulting Request (after unlink): {:s}'.format(str_data))
yang_handler.destroy()
if __name__ == '__main__':
main()
......@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import json, libyang, logging, queue
import json, libyang, logging
from typing import Any, Dict, List, Tuple
from ._Handler import _Handler
from .Tools import get_bool, get_int, get_str
......@@ -34,6 +34,22 @@ class InterfaceHandler(_Handler):
PATH_TMPL = '/interfaces/interface[name={:s}]/subinterfaces/subinterface[index={:d}]'
str_path = PATH_TMPL.format(if_name, sif_index)
str_data = json.dumps({})
root_node : libyang.DContainer = yang_handler.get_data_path(
'/openconfig-interfaces:interfaces'
)
yang_sif = root_node.find_path('/'.join([
'', # add slash at the beginning
'openconfig-interfaces:interfaces',
'interface[name="{:s}"]'.format(if_name),
'subinterfaces',
'subinterface[index="{:d}"]'.format(sif_index),
]))
if yang_sif is not None:
LOGGER.info('Deleting: {:s}'.format(str(yang_sif.print_mem('json'))))
yang_sif.unlink()
yang_sif.free()
return str_path, str_data
enabled = get_bool(resource_value, 'enabled', True) # True/False
......@@ -43,81 +59,43 @@ class InterfaceHandler(_Handler):
address_prefix = get_int (resource_value, 'address_prefix') # 24
mtu = get_int (resource_value, 'mtu' ) # 1500
objects_to_free = queue.LifoQueue[libyang.DContainer]()
yang_ifs : libyang.DContainer = yang_handler.get_data_path('/openconfig-interfaces:interfaces')
objects_to_free.put_nowait(yang_ifs)
yang_if_path = 'interface[name="{:s}"]'.format(if_name)
yang_if : libyang.DContainer = yang_ifs.create_path(yang_if_path)
objects_to_free.put_nowait(yang_if)
yang_if.create_path('config/name', if_name )
if enabled is not None: yang_if.create_path('config/enabled', enabled)
if mtu is not None: yang_if.create_path('config/mtu', mtu)
yang_sifs : libyang.DContainer = yang_if.create_path('subinterfaces')
objects_to_free.put_nowait(yang_sifs)
yang_sif_path = 'subinterface[index="{:d}"]'.format(sif_index)
yang_sif : libyang.DContainer = yang_sifs.create_path(yang_sif_path)
objects_to_free.put_nowait(yang_sif)
yang_sif.create_path('config/index', sif_index)
if enabled is not None: yang_sif.create_path('config/enabled', enabled)
if vlan_id is not None:
yang_subif_vlan : libyang.DContainer = yang_sif.create_path('openconfig-vlan:vlan')
objects_to_free.put_nowait(yang_subif_vlan)
yang_subif_vlan.create_path('match/single-tagged/config/vlan-id', vlan_id)
yang_ipv4 : libyang.DContainer = yang_sif.create_path('openconfig-if-ip:ipv4')
objects_to_free.put_nowait(yang_ipv4)
if enabled is not None: yang_ipv4.create_path('config/enabled', enabled)
if address_ip is not None and address_prefix is not None:
yang_ipv4_addrs : libyang.DContainer = yang_ipv4.create_path('addresses')
objects_to_free.put_nowait(yang_ipv4_addrs)
yang_ipv4_addr_path = 'address[ip="{:s}"]'.format(address_ip)
yang_ipv4_addr : libyang.DContainer = yang_ipv4_addrs.create_path(yang_ipv4_addr_path)
objects_to_free.put_nowait(yang_ipv4_addr)
yang_ipv4_addr.create_path('config/ip', address_ip)
yang_ipv4_addr.create_path('config/prefix-length', address_prefix)
LOGGER.info('YangHandler Data:')
for path, dnode in yang_handler.get_data_paths().items():
LOGGER.debug('|-> {:s}: {:s}'.format(str(path), json.dumps(dnode.print_dict())))
LOGGER.info('|-> {:s}: {:s}'.format(str(path), json.dumps(dnode.print_dict())))
str_path = '/interfaces/interface[name={:s}]'.format(if_name)
str_data = yang_if.print_mem('json')
json_data = json.loads(str_data)
json_data = json_data['openconfig-interfaces:interface'][0]
str_data = json.dumps(json_data)
# List elements to release:
LOGGER.warning('Objects to release:')
#LOGGER.warning('Releasing...')
while not objects_to_free.empty():
LOGGER.warning('Getting...')
try:
obj = objects_to_free.get_nowait()
if obj is None:
LOGGER.warning('Item is None')
continue
LOGGER.warning('Releasing[type]: {:s}'.format(str(type(obj))))
LOGGER.warning('Releasing: {:s} => {:s}'.format(
str(obj.path()), str(obj.print_mem('json'))
))
#try:
# LOGGER.warning('Freeing...')
# obj.free()
# LOGGER.warning('Free done')
#except:
# LOGGER.exception('Something went wrong...')
# is_error = True
# break
except queue.Empty:
LOGGER.warning('No more objects...')
continue
#LOGGER.warning('Release done')
return str_path, str_data
def parse(
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment