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

Device component - gNMI OpenConfig driver:

- Added missing NetworkInstanceProtocol handler
- Corrected NetworkInstanceStaticRoute handler
- Corrected division-by-zero bug in DeltaSampleCache
- Reduced levels of log messages
parent d231fa16
No related branches found
No related tags found
2 merge requests!294Release TeraFlowSDN 4.0,!172Resolve "(CTTC) Extend gNMI-OpenConfig SBI driver"
...@@ -13,13 +13,15 @@ ...@@ -13,13 +13,15 @@
# limitations under the License. # limitations under the License.
import copy import copy
from typing import Any, Dict, Tuple, Union from typing import Any, Dict, Optional, Tuple, Union
class DeltaSampleCache: class DeltaSampleCache:
def __init__(self) -> None: def __init__(self) -> None:
self._previous_samples : Dict[str, Tuple[float, Union[int, float]]] = dict() self._previous_samples : Dict[str, Tuple[float, Union[int, float]]] = dict()
def get_delta(self, path : str, current_timestamp : float, current_value : Any) -> None: def get_delta(
self, path : str, current_timestamp : float, current_value : Any
) -> Optional[Tuple[float, Optional[Any]]]:
previous_sample = copy.deepcopy(self._previous_samples.get(path)) previous_sample = copy.deepcopy(self._previous_samples.get(path))
self._previous_samples[path] = current_timestamp, current_value self._previous_samples[path] = current_timestamp, current_value
...@@ -30,6 +32,10 @@ class DeltaSampleCache: ...@@ -30,6 +32,10 @@ class DeltaSampleCache:
delta_value = max(0, current_value - previous_value) delta_value = max(0, current_value - previous_value)
delay = current_timestamp - previous_timestamp delay = current_timestamp - previous_timestamp
delta_sample = current_timestamp, delta_value / delay if delay < 1.e-12:
# return a special value meaning, at that timestamp,
return delta_sample # computed value is not a number, e.g., division by zero
# also, recover previuos samples to do not miss any packet/byte
self._previous_samples[path] = previous_sample
return current_timestamp, None
return current_timestamp, delta_value / delay
...@@ -134,7 +134,7 @@ class MonitoringThread(threading.Thread): ...@@ -134,7 +134,7 @@ class MonitoringThread(threading.Thread):
self._response_iterator = self._stub.Subscribe(request_iterator, metadata=metadata, timeout=timeout) self._response_iterator = self._stub.Subscribe(request_iterator, metadata=metadata, timeout=timeout)
for subscribe_response in self._response_iterator: for subscribe_response in self._response_iterator:
str_subscribe_response = grpc_message_to_json_string(subscribe_response) str_subscribe_response = grpc_message_to_json_string(subscribe_response)
self._logger.warning('[run] subscribe_response={:s}'.format(str_subscribe_response)) self._logger.debug('[run] subscribe_response={:s}'.format(str_subscribe_response))
update = subscribe_response.update update = subscribe_response.update
timestamp_device = float(update.timestamp) / 1.e9 timestamp_device = float(update.timestamp) / 1.e9
timestamp_local = datetime.timestamp(datetime.utcnow()) timestamp_local = datetime.timestamp(datetime.utcnow())
...@@ -168,8 +168,10 @@ class MonitoringThread(threading.Thread): ...@@ -168,8 +168,10 @@ class MonitoringThread(threading.Thread):
sample = (timestamp, str_path, value) sample = (timestamp, str_path, value)
else: else:
sample = (delta_sample[0], str_path, delta_sample[1]) sample = (delta_sample[0], str_path, delta_sample[1])
self._logger.warning('[run] sample={:s}'.format(str(sample))) self._logger.debug('[run] sample={:s}'.format(str(sample)))
self._out_samples.put_nowait(sample) if sample[2] is not None:
# Skip not-a-number (e.g., division by zero) samples
self._out_samples.put_nowait(sample)
except grpc.RpcError as e: except grpc.RpcError as e:
if e.code() != grpc.StatusCode.CANCELLED: raise # pylint: disable=no-member if e.code() != grpc.StatusCode.CANCELLED: raise # pylint: disable=no-member
if e.details() != 'Locally cancelled by application!': raise # pylint: disable=no-member if e.details() != 'Locally cancelled by application!': raise # pylint: disable=no-member
......
# Copyright 2022-2024 ETSI OSG/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
from typing import Any, Dict, List, Tuple
from ._Handler import _Handler
from .Tools import get_str
from .YangHandler import YangHandler
LOGGER = logging.getLogger(__name__)
class NetworkInstanceProtocolHandler(_Handler):
def get_resource_key(self) -> str: return '/network_instance/protocols'
def get_path(self) -> str:
return '/openconfig-network-instance:network-instances/network-instance/protocols/protocol'
def compose(
self, resource_key : str, resource_value : Dict, yang_handler : YangHandler, delete : bool = False
) -> Tuple[str, str]:
ni_name = get_str(resource_value, 'name' ) # test-svc
identifier = get_str(resource_value, 'identifier') # 'STATIC'
proto_name = get_str(resource_value, 'protocol_name') # 'STATIC'
if ':' not in identifier:
identifier = 'openconfig-policy-types:{:s}'.format(identifier)
PATH_TMPL = '/network-instances/network-instance[name={:s}]/protocols/protocol[identifier={:s}][name={:s}]'
str_path = PATH_TMPL.format(ni_name, identifier, proto_name)
if delete:
str_data = json.dumps({})
return str_path, str_data
#str_data = json.dumps({
# 'identifier': identifier, 'name': name,
# 'config': {'identifier': identifier, 'name': name, 'enabled': True},
# 'static_routes': {'static': [{
# 'prefix': prefix,
# 'config': {'prefix': prefix},
# 'next_hops': {
# 'next-hop': [{
# 'index': next_hop_index,
# 'config': {'index': next_hop_index, 'next_hop': next_hop}
# }]
# }
# }]}
#})
yang_nis : libyang.DContainer = yang_handler.get_data_path('/openconfig-network-instance:network-instances')
yang_ni : libyang.DContainer = yang_nis.create_path('network-instance[name="{:s}"]'.format(ni_name))
yang_ni_prs : libyang.DContainer = yang_ni.create_path('protocols')
yang_ni_pr_path = 'protocol[identifier="{:s}"][name="{:s}"]'.format(identifier, proto_name)
yang_ni_pr : libyang.DContainer = yang_ni_prs.create_path(yang_ni_pr_path)
yang_ni_pr.create_path('config/identifier', identifier)
yang_ni_pr.create_path('config/name', proto_name)
yang_ni_pr.create_path('config/enabled', True )
str_data = yang_ni_pr.print_mem('json')
LOGGER.warning('[compose] str_data = {:s}'.format(str(str_data)))
json_data = json.loads(str_data)
json_data = json_data['openconfig-network-instance:protocol'][0]
str_data = json.dumps(json_data)
return str_path, str_data
def parse(
self, json_data : Dict, yang_handler : YangHandler
) -> List[Tuple[str, Dict[str, Any]]]:
LOGGER.warning('[parse] json_data = {:s}'.format(str(json_data)))
response = []
return response
...@@ -28,24 +28,29 @@ class NetworkInstanceStaticRouteHandler(_Handler): ...@@ -28,24 +28,29 @@ class NetworkInstanceStaticRouteHandler(_Handler):
def compose( def compose(
self, resource_key : str, resource_value : Dict, yang_handler : YangHandler, delete : bool = False self, resource_key : str, resource_value : Dict, yang_handler : YangHandler, delete : bool = False
) -> Tuple[str, str]: ) -> Tuple[str, str]:
ni_name = get_str(resource_value, 'name' ) # test-svc ni_name = get_str(resource_value, 'name' ) # test-svc
prefix = get_str(resource_value, 'prefix') # '172.0.1.0/24' identifier = get_str(resource_value, 'identifier') # 'STATIC'
proto_name = get_str(resource_value, 'protocol_name') # 'STATIC'
prefix = get_str(resource_value, 'prefix') # '172.0.1.0/24'
if ':' not in identifier:
identifier = 'openconfig-policy-types:{:s}'.format(identifier)
identifier = 'openconfig-policy-types:STATIC'
name = 'static'
if delete: if delete:
PATH_TMPL = '/network-instances/network-instance[name={:s}]/protocols' PATH_TMPL = '/network-instances/network-instance[name={:s}]/protocols'
PATH_TMPL += '/protocol[identifier={:s}][name={:s}]/static-routes/static[prefix={:s}]' PATH_TMPL += '/protocol[identifier={:s}][name={:s}]/static-routes/static[prefix={:s}]'
str_path = PATH_TMPL.format(ni_name, identifier, name, prefix) str_path = PATH_TMPL.format(ni_name, identifier, proto_name, prefix)
str_data = json.dumps({}) str_data = json.dumps({})
return str_path, str_data return str_path, str_data
next_hop = get_str(resource_value, 'next_hop' ) # '172.0.0.1' next_hop = get_str(resource_value, 'next_hop') # '172.0.0.1'
metric = get_int(resource_value, 'metric' ) # 20 metric = get_int(resource_value, 'metric' ) # 20
next_hop_index = get_str(resource_value, 'next_hop_index') # AUTO_1_172-0-0-1 index = get_str(resource_value, 'index' ) # AUTO_1_172-0-0-1
if index is None:
index = 'AUTO_{:d}_{:s}'.format(metric, next_hop)
PATH_TMPL = '/network-instances/network-instance[name={:s}]/protocols/protocol[identifier={:s}][name={:s}]' PATH_TMPL = '/network-instances/network-instance[name={:s}]/protocols/protocol[identifier={:s}][name={:s}]'
str_path = PATH_TMPL.format(ni_name, identifier, name) str_path = PATH_TMPL.format(ni_name, identifier, proto_name)
#str_data = json.dumps({ #str_data = json.dumps({
# 'identifier': identifier, 'name': name, # 'identifier': identifier, 'name': name,
# 'config': {'identifier': identifier, 'name': name, 'enabled': True}, # 'config': {'identifier': identifier, 'name': name, 'enabled': True},
...@@ -64,10 +69,10 @@ class NetworkInstanceStaticRouteHandler(_Handler): ...@@ -64,10 +69,10 @@ class NetworkInstanceStaticRouteHandler(_Handler):
yang_nis : libyang.DContainer = yang_handler.get_data_path('/openconfig-network-instance:network-instances') yang_nis : libyang.DContainer = yang_handler.get_data_path('/openconfig-network-instance:network-instances')
yang_ni : libyang.DContainer = yang_nis.create_path('network-instance[name="{:s}"]'.format(ni_name)) yang_ni : libyang.DContainer = yang_nis.create_path('network-instance[name="{:s}"]'.format(ni_name))
yang_ni_prs : libyang.DContainer = yang_ni.create_path('protocols') yang_ni_prs : libyang.DContainer = yang_ni.create_path('protocols')
yang_ni_pr_path = 'protocol[identifier="{:s}"][name="{:s}"]'.format(identifier, name) yang_ni_pr_path = 'protocol[identifier="{:s}"][name="{:s}"]'.format(identifier, proto_name)
yang_ni_pr : libyang.DContainer = yang_ni_prs.create_path(yang_ni_pr_path) yang_ni_pr : libyang.DContainer = yang_ni_prs.create_path(yang_ni_pr_path)
yang_ni_pr.create_path('config/identifier', identifier) yang_ni_pr.create_path('config/identifier', identifier)
yang_ni_pr.create_path('config/name', name ) yang_ni_pr.create_path('config/name', proto_name)
yang_ni_pr.create_path('config/enabled', True ) yang_ni_pr.create_path('config/enabled', True )
yang_ni_pr_srs : libyang.DContainer = yang_ni_pr.create_path('static-routes') yang_ni_pr_srs : libyang.DContainer = yang_ni_pr.create_path('static-routes')
...@@ -76,11 +81,11 @@ class NetworkInstanceStaticRouteHandler(_Handler): ...@@ -76,11 +81,11 @@ class NetworkInstanceStaticRouteHandler(_Handler):
yang_ni_pr_sr.create_path('config/prefix', prefix) yang_ni_pr_sr.create_path('config/prefix', prefix)
yang_ni_pr_sr_nhs : libyang.DContainer = yang_ni_pr_sr.create_path('next-hops') yang_ni_pr_sr_nhs : libyang.DContainer = yang_ni_pr_sr.create_path('next-hops')
yang_ni_pr_sr_nh_path = 'next-hop[index="{:s}"]'.format(next_hop_index) yang_ni_pr_sr_nh_path = 'next-hop[index="{:s}"]'.format(index)
yang_ni_pr_sr_nh : libyang.DContainer = yang_ni_pr_sr_nhs.create_path(yang_ni_pr_sr_nh_path) yang_ni_pr_sr_nh : libyang.DContainer = yang_ni_pr_sr_nhs.create_path(yang_ni_pr_sr_nh_path)
yang_ni_pr_sr_nh.create_path('config/index', next_hop_index) yang_ni_pr_sr_nh.create_path('config/index', index)
yang_ni_pr_sr_nh.create_path('config/next-hop', next_hop) yang_ni_pr_sr_nh.create_path('config/next-hop', next_hop)
yang_ni_pr_sr_nh.create_path('config/metric', metric) yang_ni_pr_sr_nh.create_path('config/metric', metric)
str_data = yang_ni_pr.print_mem('json') str_data = yang_ni_pr.print_mem('json')
LOGGER.warning('[compose] str_data = {:s}'.format(str(str_data))) LOGGER.warning('[compose] str_data = {:s}'.format(str(str_data)))
......
...@@ -21,6 +21,7 @@ from .Interface import InterfaceHandler ...@@ -21,6 +21,7 @@ from .Interface import InterfaceHandler
from .InterfaceCounter import InterfaceCounterHandler from .InterfaceCounter import InterfaceCounterHandler
from .NetworkInstance import NetworkInstanceHandler from .NetworkInstance import NetworkInstanceHandler
from .NetworkInstanceInterface import NetworkInstanceInterfaceHandler from .NetworkInstanceInterface import NetworkInstanceInterfaceHandler
from .NetworkInstanceProtocol import NetworkInstanceProtocolHandler
from .NetworkInstanceStaticRoute import NetworkInstanceStaticRouteHandler from .NetworkInstanceStaticRoute import NetworkInstanceStaticRouteHandler
from .Tools import get_schema from .Tools import get_schema
from .YangHandler import YangHandler from .YangHandler import YangHandler
...@@ -32,6 +33,7 @@ ifaceh = InterfaceHandler() ...@@ -32,6 +33,7 @@ ifaceh = InterfaceHandler()
ifctrh = InterfaceCounterHandler() ifctrh = InterfaceCounterHandler()
nih = NetworkInstanceHandler() nih = NetworkInstanceHandler()
niifh = NetworkInstanceInterfaceHandler() niifh = NetworkInstanceInterfaceHandler()
niph = NetworkInstanceProtocolHandler()
nisrh = NetworkInstanceStaticRouteHandler() nisrh = NetworkInstanceStaticRouteHandler()
ALL_RESOURCE_KEYS = [ ALL_RESOURCE_KEYS = [
...@@ -59,6 +61,7 @@ RESOURCE_KEY_TO_HANDLER = { ...@@ -59,6 +61,7 @@ RESOURCE_KEY_TO_HANDLER = {
ifctrh.get_resource_key() : ifctrh, ifctrh.get_resource_key() : ifctrh,
nih.get_resource_key() : nih, nih.get_resource_key() : nih,
niifh.get_resource_key() : niifh, niifh.get_resource_key() : niifh,
niph.get_resource_key() : niph,
nisrh.get_resource_key() : nisrh, nisrh.get_resource_key() : nisrh,
} }
...@@ -68,6 +71,7 @@ PATH_TO_HANDLER = { ...@@ -68,6 +71,7 @@ PATH_TO_HANDLER = {
ifctrh.get_path() : ifctrh, ifctrh.get_path() : ifctrh,
nih.get_path() : nih, nih.get_path() : nih,
niifh.get_path() : niifh, niifh.get_path() : niifh,
niph.get_path() : niph,
nisrh.get_path() : nisrh, nisrh.get_path() : nisrh,
} }
......
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