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
Loading
Loading
Loading
Loading
+11 −5
Original line number Original line Diff line number Diff line
@@ -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:


        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
+5 −3
Original line number Original line Diff line number Diff line
@@ -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,7 +168,9 @@ 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)))
                    if sample[2] is not None:
                        # Skip not-a-number (e.g., division by zero) samples
                        self._out_samples.put_nowait(sample)
                        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
+80 −0
Original line number Original line Diff line number Diff line
# 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
+19 −14
Original line number Original line Diff line number Diff line
@@ -29,23 +29,28 @@ class NetworkInstanceStaticRouteHandler(_Handler):
        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
        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'
        prefix     = get_str(resource_value, 'prefix')          # '172.0.1.0/24'


        identifier = 'openconfig-policy-types:STATIC'
        if ':' not in identifier:
        name = 'static'
            identifier = 'openconfig-policy-types:{:s}'.format(identifier)

        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):
        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,9 +81,9 @@ 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)


+4 −0
Original line number Original line Diff line number Diff line
@@ -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()
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 = {
    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 = {
    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,
}
}