Commit 2cc1f0e9 authored by Lluis Gifre Renom's avatar Lluis Gifre Renom
Browse files

Merge branch 'feat/service-location' into 'develop'

First implementation of location-aware services

See merge request !89
parents 579ef5ea e1816a8b
Loading
Loading
Loading
Loading
+16 −9
Original line number Diff line number Diff line
@@ -494,6 +494,12 @@ message ConfigRule {


// ----- Constraint ----------------------------------------------------------------------------------------------------
enum ConstraintActionEnum {
  CONSTRAINTACTION_UNDEFINED = 0;
  CONSTRAINTACTION_SET       = 1;
  CONSTRAINTACTION_DELETE    = 2;
}

message Constraint_Custom {
  string constraint_type = 1;
  string constraint_value = 2;
@@ -564,16 +570,17 @@ message Constraint_Exclusions {
}

message Constraint {
  ConstraintActionEnum action = 1;
  oneof constraint {
    Constraint_Custom custom = 1;
    Constraint_Schedule schedule = 2;
    Constraint_EndPointLocation endpoint_location = 3;
    Constraint_EndPointPriority endpoint_priority = 4;
    Constraint_SLA_Capacity sla_capacity = 5;
    Constraint_SLA_Latency sla_latency = 6;
    Constraint_SLA_Availability sla_availability = 7;
    Constraint_SLA_Isolation_level sla_isolation = 8;
    Constraint_Exclusions exclusions = 9;
    Constraint_Custom custom = 2;
    Constraint_Schedule schedule = 3;
    Constraint_EndPointLocation endpoint_location = 4;
    Constraint_EndPointPriority endpoint_priority = 5;
    Constraint_SLA_Capacity sla_capacity = 6;
    Constraint_SLA_Latency sla_latency = 7;
    Constraint_SLA_Availability sla_availability = 8;
    Constraint_SLA_Isolation_level sla_isolation = 9;
    Constraint_Exclusions exclusions = 10;
  }
}

+98 −5
Original line number Diff line number Diff line
@@ -13,15 +13,15 @@
# limitations under the License.

import grpc, json, logging
from typing import Any, Dict, Iterator, List
from typing import Any, Dict, Iterator, List, Set
from common.proto.context_pb2 import (
    Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList,
    Context, ContextEvent, ContextId, ContextIdList, ContextList,
    Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList,
    Device, DeviceEvent, DeviceFilter, DeviceId, DeviceIdList, DeviceList,
    Empty, EventTypeEnum,
    Link, LinkEvent, LinkId, LinkIdList, LinkList,
    Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList,
    Slice, SliceEvent, SliceId, SliceIdList, SliceList,
    Service, ServiceEvent, ServiceFilter, ServiceId, ServiceIdList, ServiceList,
    Slice, SliceEvent, SliceFilter, SliceId, SliceIdList, SliceList,
    Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList)
from common.proto.context_pb2_grpc import ContextServiceServicer
from common.tests.MockMessageBroker import (
@@ -68,10 +68,19 @@ def del_entry(
    del container[entry_uuid]
    return Empty()

def select_entries(database : Dict[str, Dict[str, Any]], container_name : str, entry_uuids : Set[str]) -> List[Any]:
    if len(entry_uuids) == 0: return get_entries(database, container_name)
    container = get_container(database, container_name)
    return [
        container[entry_uuid]
        for entry_uuid in sorted(container.keys())
        if entry_uuid in entry_uuids
    ]

class MockServicerImpl_Context(ContextServiceServicer):
    def __init__(self):
        LOGGER.info('[__init__] Creating Servicer...')
        self.database : Dict[str, Any] = {}
        self.database : Dict[str, Dict[str, Any]] = {}
        self.msg_broker = MockMessageBroker()
        LOGGER.info('[__init__] Servicer Created')

@@ -210,6 +219,33 @@ class MockServicerImpl_Context(ContextServiceServicer):
        LOGGER.info('[GetDeviceEvents] request={:s}'.format(grpc_message_to_json_string(request)))
        for message in self.msg_broker.consume({TOPIC_DEVICE}): yield DeviceEvent(**json.loads(message.content))

    def SelectDevice(self, request : DeviceFilter, context : grpc.ServicerContext) -> DeviceList:
        LOGGER.info('[SelectDevice] request={:s}'.format(grpc_message_to_json_string(request)))
        container_entry_uuids : Dict[str, Set[str]] = {}
        container_name = 'device'
        for device_id in request.device_ids.device_ids:
            device_uuid = device_id.device_uuid.uuid
            container_entry_uuids.setdefault(container_name, set()).add(device_uuid)

        exclude_endpoints = not request.include_endpoints
        exclude_config_rules = not request.include_config_rules
        exclude_components  = not request.include_components

        devices = list()
        for container_name in sorted(container_entry_uuids.keys()):
             entry_uuids = container_entry_uuids[container_name]
        for device in select_entries(self.database, container_name, entry_uuids):
            reply_device = Device()
            reply_device.CopyFrom(device)
            if exclude_endpoints:    del reply_device.device_endpoints [:] # pylint: disable=no-member
            if exclude_config_rules: del reply_device.device_config.config_rules[:] # pylint: disable=no-member
            if exclude_components:   del reply_device.component[:] # pylint: disable=no-member
            devices.append(reply_device)
                
        reply = DeviceList(devices=devices) 
        LOGGER.info('[SelectDevice] reply={:s}'.format(grpc_message_to_json_string(reply)))
        return reply


    # ----- Link -------------------------------------------------------------------------------------------------------

@@ -291,6 +327,37 @@ class MockServicerImpl_Context(ContextServiceServicer):
        LOGGER.info('[GetSliceEvents] request={:s}'.format(grpc_message_to_json_string(request)))
        for message in self.msg_broker.consume({TOPIC_SLICE}): yield SliceEvent(**json.loads(message.content))

    def SelectSlice(self, request : SliceFilter, context : grpc.ServicerContext) -> SliceList:
        LOGGER.info('[SelectSlice] request={:s}'.format(grpc_message_to_json_string(request)))
        container_entry_uuids : Dict[str, Set[str]] = {}
        for slice_id in request.slice_ids.slice_ids:
            container_name = 'slice[{:s}]'.format(str(slice_id.context_id.context_uuid.uuid))
            slice_uuid = slice_id.slice_uuid.uuid
            container_entry_uuids.setdefault(container_name, set()).add(slice_uuid)
            
        exclude_endpoint_ids = not request.include_endpoint_ids
        exclude_constraints  = not request.include_constraints
        exclude_service_ids  = not request.include_service_ids
        exclude_subslice_ids = not request.include_subslice_ids 
        exclude_config_rules = not request.include_config_rules
        
        slices = list()
        for container_name in sorted(container_entry_uuids.keys()):
            entry_uuids = container_entry_uuids[container_name]
            for eslice in select_entries(self.database, container_name, entry_uuids):
                reply_slice = Slice()
                reply_slice.CopyFrom(eslice)
                if exclude_endpoint_ids: del reply_slice.service_endpoint_ids[:] # pylint: disable=no-member
                if exclude_constraints : del reply_slice.service_constraints[:] # pylint: disable=no-member
                if exclude_service_ids : del reply_slice.slice_service_ids[:] # pylint: disable=no-member
                if exclude_subslice_ids : del reply_slice.slice_subslice_ids[:] # pylint: disable=no-member
                if exclude_config_rules: del reply_slice.slice_config .config_rules[:] # pylint: disable=no-member
                slices.append(reply_slice)
                
        reply = SliceList(slices=slices)
        LOGGER.info('[SelectSlice] reply={:s}'.format(grpc_message_to_json_string(reply)))
        return reply


    # ----- Service ----------------------------------------------------------------------------------------------------

@@ -335,6 +402,32 @@ class MockServicerImpl_Context(ContextServiceServicer):
        LOGGER.info('[GetServiceEvents] request={:s}'.format(grpc_message_to_json_string(request)))
        for message in self.msg_broker.consume({TOPIC_SERVICE}): yield ServiceEvent(**json.loads(message.content))

    def SelectService(self, request : ServiceFilter, context : grpc.ServicerContext) -> ServiceList:
        LOGGER.info('[SelectService] request={:s}'.format(grpc_message_to_json_string(request)))
        container_entry_uuids : Dict[str, Set[str]] = {}
        for service_id in request.service_ids.service_ids:
            container_name = 'service[{:s}]'.format(str(service_id.context_id.context_uuid.uuid))
            service_uuid = service_id.service_uuid.uuid
            container_entry_uuids.setdefault(container_name, set()).add(service_uuid)
            
        exclude_endpoint_ids = not request.include_endpoint_ids
        exclude_constraints  = not request.include_constraints
        exclude_config_rules = not request.include_config_rules
        
        services = list()
        for container_name in sorted(container_entry_uuids.keys()):
            entry_uuids = container_entry_uuids[container_name]
            for service in select_entries(self.database, container_name, entry_uuids):
                reply_service = Service()
                reply_service.CopyFrom(service)
                if exclude_endpoint_ids: del reply_service.service_endpoint_ids[:] # pylint: disable=no-member
                if exclude_constraints : del reply_service.service_constraints[:] # pylint: disable=no-member
                if exclude_config_rules: del reply_service.service_config.config_rules[:] # pylint: disable=no-member
                services.append(reply_service)
                
        reply = ServiceList(services=services) 
        LOGGER.info('[SelectService] reply={:s}'.format(grpc_message_to_json_string(reply)))
        return reply

    # ----- Connection -------------------------------------------------------------------------------------------------

+2 −1
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ def json_endpoint_ids(

def json_endpoint(
        device_id : Dict, endpoint_uuid : str, endpoint_type : str, topology_id : Optional[Dict] = None,
        kpi_sample_types : List[int] = []
        kpi_sample_types : List[int] = [], location : Optional[Dict] = None
    ):

    result = {
@@ -38,6 +38,7 @@ def json_endpoint(
        'endpoint_type': endpoint_type,
    }
    if len(kpi_sample_types) > 0: result['kpi_sample_types'] = copy.deepcopy(kpi_sample_types)
    if location: result['endpoint_location'] = copy.deepcopy(location)
    return result

def json_endpoints(
+30 −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 copy
from typing import Dict, Optional


def json_gps_position(latitude : float, longitude : float):
    return {'latitude': latitude, 'longitude': longitude}

def json_location(region : Optional[str] = None, gps_position : Optional[Dict] = None):
    if not region and not gps_position:
        raise Exception('One of "region" or "gps_position" arguments must be filled')
    if region:
        result = {'region': region}
    else:
        result = {'gps_position': copy.deepcopy(gps_position)}

    return result
+0 −1
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ from .Tools import (
    format_grpc_to_json, grpc_connection_id, grpc_context_id, grpc_device_id, grpc_link_id, grpc_policy_rule_id,
    grpc_service_id, grpc_slice_id, grpc_topology_id)


class _Resource(Resource):
    def __init__(self) -> None:
        super().__init__()
Loading