Commit c5b1bbeb authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Merge branch...

Merge branch 'feat/105-implement-sbi-driver-for-flex-scale-transceivers-and-flex-scale-mg-ons' into 'develop'

Resolve "Implement SBI driver for FLEX-SCALE Transceivers and FLEX-SCALE MG-ONs"

Closes #105

See merge request !191
parents 7d7503e0 6e8e9aae
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -15,12 +15,12 @@
import grpc, logging
from common.Constants import ServiceNameEnum
from common.Settings import get_service_host, get_service_port_grpc
from common.proto.context_pb2 import Device, DeviceConfig, DeviceId, Empty
from common.proto.context_pb2 import Device, DeviceConfig, DeviceId, Empty, MyConfig, MyConfigId
from common.proto.device_pb2 import MonitoringSettings
from common.proto.device_pb2_grpc import DeviceServiceStub
from common.tools.client.RetryDecorator import retry, delay_exponential
from common.tools.grpc.Tools import grpc_message_to_json_string

from common.proto.openconfig_device_pb2_grpc import OpenConfigServiceStub
LOGGER = logging.getLogger(__name__)
MAX_RETRIES = 15
DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0)
@@ -34,12 +34,14 @@ class DeviceClient:
        LOGGER.debug('Creating channel to {:s}...'.format(str(self.endpoint)))
        self.channel = None
        self.stub = None
        self.openconfig_stub=None
        self.connect()
        LOGGER.debug('Channel created')

    def connect(self):
        self.channel = grpc.insecure_channel(self.endpoint)
        self.stub = DeviceServiceStub(self.channel)
        self.openconfig_stub=OpenConfigServiceStub(self.channel)

    def close(self):
        if self.channel is not None: self.channel.close()
@@ -80,3 +82,9 @@ class DeviceClient:
        response = self.stub.MonitorDeviceKpi(request)
        LOGGER.debug('MonitorDeviceKpi result: {:s}'.format(grpc_message_to_json_string(response)))
        return response

    def ConfigureOpticalDevice(self, request : MyConfig) -> MyConfigId:
        LOGGER.debug('ConfigureOpticalDevice request: {:s}'.format(grpc_message_to_json_string(request)))
        response = self.openconfig_stub.ConfigureOpticalDevice(request)
        LOGGER.debug('ConfigureOpticalDevice result: {:s}'.format(grpc_message_to_json_string(response)))
        return response
+4 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ from common.tools.service.GenericGrpcService import GenericGrpcService
from .driver_api.DriverInstanceCache import DriverInstanceCache
from .DeviceServiceServicerImpl import DeviceServiceServicerImpl
from .monitoring.MonitoringLoops import MonitoringLoops
from .OpenConfigServicer import OpenConfigServicer
from common.proto.openconfig_device_pb2_grpc import add_OpenConfigServiceServicer_to_server

# Custom gRPC settings
# Multiple clients might keep connections alive waiting for RPC methods to be executed.
@@ -31,10 +33,12 @@ class DeviceService(GenericGrpcService):
        super().__init__(port, max_workers=GRPC_MAX_WORKERS, cls_name=cls_name)
        self.monitoring_loops = MonitoringLoops()
        self.device_servicer = DeviceServiceServicerImpl(driver_instance_cache, self.monitoring_loops)
        self.openconfig_device_servicer=OpenConfigServicer(driver_instance_cache,self.monitoring_loops)

    def install_servicers(self):
        self.monitoring_loops.start()
        add_DeviceServiceServicer_to_server(self.device_servicer, self.server)
        add_OpenConfigServiceServicer_to_server(self.openconfig_device_servicer,self.server)

    def stop(self):
        super().stop()
+14 −4
Original line number Diff line number Diff line
@@ -20,7 +20,8 @@ from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, get_env_var_name
from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, safe_and_metered_rpc_method
from common.method_wrappers.ServiceExceptions import NotFoundException, OperationFailedException
from common.proto.context_pb2 import (
    Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link)
    Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link, MyConfig, MyConfigId
)
from common.proto.device_pb2 import MonitoringSettings
from common.proto.device_pb2_grpc import DeviceServiceServicer
from common.tools.context_queries.Device import get_device
@@ -29,6 +30,7 @@ from context.client.ContextClient import ContextClient
from .driver_api._Driver import _Driver
from .driver_api.DriverInstanceCache import DriverInstanceCache, get_driver
from .monitoring.MonitoringLoops import MonitoringLoops
from .drivers.oc_driver.OCDriver import OCDriver
from .ErrorMessages import ERROR_MISSING_DRIVER, ERROR_MISSING_KPI
from .Tools import (
    check_connect_rules, check_no_endpoints, compute_rules_to_add_delete, configure_rules, deconfigure_rules,
@@ -58,6 +60,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
        device_uuid = request.device_id.device_uuid.uuid

        connection_config_rules = check_connect_rules(request.device_config)
        if (request.device_drivers[0]!= 9) :
            check_no_endpoints(request.device_endpoints)
            
        t1 = time.time()
@@ -139,6 +142,13 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
                # ZTP is not deployed; assume the device is ready while onboarding and set them as enabled.
                device.device_operational_status = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED
           
            # temporary line 
            if (request.device_drivers[0]== 9 and len(request.device_endpoints)>0):
             
                for endpoint in request.device_endpoints:
                    #endpoint.endpoint_id.device_id.CopyFrom(device.device_id)
                    pass
                device.device_endpoints.extend(request.device_endpoints)
            device_id = context_client.SetDevice(device)

            t10 = time.time()
+134 −0
Original line number Diff line number Diff line
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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 grpc, logging, os, time
from typing import Dict
from prometheus_client import Histogram
from common.Constants import ServiceNameEnum
from common.Settings import ENVVAR_SUFIX_SERVICE_HOST, get_env_var_name
from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, safe_and_metered_rpc_method
from common.method_wrappers.ServiceExceptions import NotFoundException, OperationFailedException
from common.proto.context_pb2 import (
    Device, DeviceConfig, DeviceDriverEnum, DeviceId, DeviceOperationalStatusEnum, Empty, Link, MyConfig, MyConfigId,
    MyConfig, MyConfigList
)
from common.proto.device_pb2 import MonitoringSettings
from common.proto.device_pb2_grpc import DeviceServiceServicer
from common.tools.context_queries.Device import get_device
from common.tools.mutex_queues.MutexQueues import MutexQueues
from context.client.ContextClient import ContextClient
from .driver_api._Driver import _Driver
from .driver_api.DriverInstanceCache import DriverInstanceCache, get_driver
from .monitoring.MonitoringLoops import MonitoringLoops
from .drivers.oc_driver.OCDriver import OCDriver
from .ErrorMessages import ERROR_MISSING_DRIVER, ERROR_MISSING_KPI
from .Tools import extract_resources
from .Tools import (
    check_connect_rules, check_no_endpoints, compute_rules_to_add_delete, configure_rules, deconfigure_rules,
    get_device_controller_uuid, populate_config_rules, populate_endpoint_monitoring_resources, populate_endpoints,
    populate_initial_config_rules, subscribe_kpi, unsubscribe_kpi, update_endpoints)

LOGGER = logging.getLogger(__name__)

METRICS_POOL = MetricsPool('Device', 'RPC')

METRICS_POOL_DETAILS = MetricsPool('Device', 'execution', labels={
    'driver': '', 'operation': '', 'step': '',
})

class OpenConfigServicer(DeviceServiceServicer):
    def __init__(self, driver_instance_cache : DriverInstanceCache, monitoring_loops : MonitoringLoops) -> None:
        LOGGER.debug('Creating Servicer...')
        self.driver_instance_cache = driver_instance_cache
        self.monitoring_loops = monitoring_loops
        self.mutex_queues = MutexQueues()
        LOGGER.debug('Servicer Created')
    
    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def AddOpenConfigDevice(self, request : MyConfig, context : grpc.ServicerContext) -> DeviceId:
           
            device_uuid = request.device_id.device_uuid.uuid
            device_type=request.device_type
            ocdriver=OCDriver()
            connection_config_rules = check_connect_rules(request.device_config)
            check_no_endpoints(request.device_endpoints)

            context_client = ContextClient()
            device = get_device(context_client, device_uuid, rw_copy=True)
            if device is None:
                # not in context, create blank one to get UUID, and populate it below
                device = Device()
                device.device_id.CopyFrom(request.device_id)            # pylint: disable=no-member
                device.name = request.name
                device.device_type = request.device_type
                device.device_operational_status = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED
                device.device_drivers.extend(request.device_drivers)    # pylint: disable=no-member
                device.device_config.CopyFrom(request.device_config)    # pylint: disable=no-member
                device_id = context_client.SetDevice(device)
                device = get_device(context_client, device_id.device_uuid.uuid, rw_copy=True)

            # update device_uuid to honor UUID provided by Context
            device_uuid = device.device_id.device_uuid.uuid
            LOGGER.debug('device type %s',device)
            t2 = time.time()
            
            self.mutex_queues.wait_my_turn(device_uuid)
            t3 = time.time()
            #temp fix to solve the error
            #todo check what to pass here
            resources_to_get = []
            try:
                #driver : _Driver = get_driver(self.driver_instance_cache, device)
                results_getconfig=ocdriver.GetConfig(resource_keys=resources_to_get,device_uuid=device_uuid)
                #results_getconfig = driver.GetConfig(resources_to_get,device_uuid)
            except Exception as error :
                LOGGER.debug("error %s",error)    

    @safe_and_metered_rpc_method(METRICS_POOL, LOGGER)
    def ConfigureOpticalDevice (self, request : MyConfig, context:grpc.ServicerContext) -> Empty:
        LOGGER.info('Setting from ConfigureOpticalDevice with Flows %s',request)
        #device_id = request.myconfig_id
        #device_uuid = device_id.myconfig_uuid
        device_uuid = request.myconfig_id.myconfig_uuid
        LOGGER.info("device uuid {}".format(device_uuid))
        resources=[]
        result=None
        config = eval(request.config)
        filter_fields= ["frequency", "target-output-power", "interface", "operational-mode"]
        try:
            context_client = ContextClient()
           
            device = get_device(
                context_client, device_uuid, rw_copy=True, include_endpoints=True, include_components=False,
                include_config_rules=False)
           
            if device is None:
                LOGGER.info("device is none")
                raise NotFoundException('Device', device_uuid, extra_details='loading in ConfigureDevice')
            resources,conditions=extract_resources(config=config,device=device)
            driver : _Driver = get_driver(self.driver_instance_cache, device)
            LOGGER.info("resource  %s conditions %s",resources,conditions)
       
            result = driver.SetConfig(resources=resources,conditions=conditions)
            #todo
            #add a control with the NETCONF get
            #driver.GetConfig(resource_keys=filter_fields)
            
        except Exception as e:
                LOGGER.info("error in configuring %s",e)    

        
        LOGGER.info("result %s",result)
        return Empty()
 
 No newline at end of file
+95 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from uuid import UUID, uuid4, uuid5
import json, logging
from typing import Any, Dict, List, Optional, Tuple, Union
from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME
@@ -27,9 +28,29 @@ from .ErrorMessages import (
    ERROR_BAD_RESOURCE, ERROR_DELETE, ERROR_GET, ERROR_GET_INIT, ERROR_MISSING_KPI, ERROR_SAMPLETYPE, ERROR_SET,
    ERROR_SUBSCRIBE, ERROR_UNSUBSCRIBE, ERROR_UNSUP_RESOURCE
)
from .drivers.oc_driver.OCDriver import OCDriver
from common.method_wrappers.ServiceExceptions import NotFoundException
from common.type_checkers.Checkers import chk_length, chk_type
from common.proto.context_pb2 import EndPoint

LOGGER = logging.getLogger(__name__)


def get_endpoint_matching(device : Device, endpoint_uuid_or_name : str) -> EndPoint:
    for endpoint in device.device_endpoints:
        choices = {endpoint.endpoint_id.endpoint_uuid.uuid, endpoint.name}
        if endpoint_uuid_or_name in choices: return endpoint

    device_uuid = device.device_id.device_uuid.uuid
    extra_details = 'Device({:s})'.format(str(device_uuid))
    raise NotFoundException('Endpoint', endpoint_uuid_or_name, extra_details=extra_details)

def get_device_endpoint_uuids(endpoint : Tuple[str, str, Optional[str]]) -> Tuple[str, str]:
    chk_type('endpoint', endpoint, (tuple, list))
    chk_length('endpoint', endpoint, min_length=2, max_length=3)
    device_uuid, endpoint_uuid = endpoint[0:2] # ignore topology_uuid by now
    return device_uuid, endpoint_uuid

def check_connect_rules(device_config : DeviceConfig) -> Dict[str, Any]:
    connection_config_rules = dict()
    unexpected_config_rules = list()
@@ -434,3 +455,77 @@ def update_endpoints(src_device : Device, dst_device : Device) -> None:
            dst_topology_id = dst_endpoint_id.topology_id
            if len(src_topology_uuid) > 0: dst_topology_id.topology_uuid.uuid = src_topology_uuid
            if len(src_context_uuid) > 0: dst_topology_id.context_id.context_uuid.uuid = src_context_uuid

def oc_default_endpoints(
    device : Device, driver : _Driver, monitoring_loops : MonitoringLoops,
    new_sub_devices : Dict[str, Device], new_sub_links : Dict[str, Link]
) -> List[str]:
    
    pass
#def get_enpoint_name (device:Device,endpoint_id:str):
#    str(UUID(str_uuid_or_name))

def get_edit_target (device:Device,is_opticalband:bool)-> str:
    if (is_opticalband): return "optical-band" 
    else :
        if device.device_type =='optical-roadm': return 'media-channel'
        else : return 'optical-channel'

def extract_resources (config : dict, device : Device)-> list:
    conditions={}
    resources=[]
    resources.append({"resource_key":"channel_namespace","value":config["channel_namespace"] if "channel_namespace" in config else None})
    resources.append({"resource_key":'add_transceiver',"value":config['add_transceiver'] if 'add_transceiver' in config else None})
    resources.append({"resource_key":"interface","value":config["update_interface"] if 'update_interface' in config else None})
    is_opticalband=config['is_opticalband'] if 'is_opticalband' in config else False
    conditions["is_opticalband"]=is_opticalband
    conditions["edit_type"]=get_edit_target(device=device,is_opticalband=is_opticalband)
    if ('flow' in config):
        #for tuple_value in config['flow'][device.name]:
        source_vals = []
        dest_vals = []
        for tuple_value in config['flow']:
            source_port=None 
            destination_port=None
            #resources.append({"resource_key":"source_port","value":source_port})
            #resources.append({"resource_key":"destination_port","value":destination_port})
            source_port_uuid,destination_port_uuid=tuple_value
            if (source_port_uuid !='0'):
                src_endpoint_obj = get_endpoint_matching(device, source_port_uuid)
                source_port = src_endpoint_obj.name
            source_vals.append(source_port)
            if (destination_port_uuid !='0'):
                dst_endpoint_obj = get_endpoint_matching(device, destination_port_uuid)
                destination_port = dst_endpoint_obj.name
            dest_vals.append(destination_port)
        resources.append({"resource_key":"source_port","value":source_vals})
        resources.append({"resource_key":"destination_port","value":dest_vals})
    if ('new_config' in config):
        lower_frequency=None
        upper_frequency=None
        resources.append({"resource_key":"target-output-power","value":config["new_config"]["target-output-power"] if "target-output-power" in config["new_config"] else None })
        #resources.append({"resource_key":"frequency","value":config["new_config"]["frequency"] if "frequency" in config else config["new_config"]["freqency"]})
        resources.append({"resource_key":"frequency","value":config["new_config"]["frequency"] if "frequency" in config["new_config"] else None})
        resources.append({"resource_key":"operational-mode","value":config["new_config"]["operational-mode"] if "operational-mode" in config["new_config"] else None})
        resources.append({"resource_key":"line-port","value":config["new_config"]["line-port"] if "line-port" in config["new_config"] else None})
        
        resources.append({"resource_key":"name","value":config['new_config']['band_type'] if 'band_type' in config['new_config'] else None})
        resources.append({"resource_key":"optical-band-parent","value":config["new_config"]["ob_id"] if "ob_id" in config["new_config"] else None })
        resources.append({"resource_key":"channel_name","value":config["new_config"]["name"] if "name" in config["new_config"] else None})
    
        if not is_opticalband :
            if 'frequency' in config['new_config'] and 'band' in config['new_config'] and  conditions["edit_type"] == 'media-channel':
                lower_frequency= int(int(config['new_config']['frequency']) - (int(config['new_config']['band'])/2))
                upper_frequency= int(int(config['new_config']['frequency']) + (int(config['new_config']['band'])/2))
                
                #lower_frequency= (config['new_config']['frequency']- config['new_config']['band'])/2
                #upper_frequency=(config['new_config']['frequency']+ config['new_config']['band'])/2
                resources.append({"resource_key":"index","value":config["new_config"]["flow_id"] if "flow_id" in config["new_config"] else None})
        else :
            lower_frequency=config['new_config']['low-freq'] if "low-freq" in config['new_config'] else None
            upper_frequency=config['new_config']['up-freq'] if 'up-freq' in config['new_config'] else None
            resources.append({"resource_key":"index","value":config["new_config"]["ob_id"] if "ob_id" in config["new_config"] else None})
                
        resources.append({"resource_key":"lower-frequency","value":lower_frequency})    
        resources.append({"resource_key":"upper-frequency","value":upper_frequency})    
    return [resources,conditions]    
Loading