Commit fce9aa69 authored by Carlos Manso's avatar Carlos Manso
Browse files

Location and Resources requests

parent d48daba4
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;
  }
}

+100 −7
Original line number Diff line number Diff line
@@ -13,15 +13,14 @@
# 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,
    Empty, EventTypeEnum,
    Link, LinkEvent, LinkId, LinkIdList, LinkList,
    Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList,
    Slice, SliceEvent, SliceId, SliceIdList, SliceList,
    Device, DeviceEvent, DeviceFilter, DeviceId, DeviceIdList, DeviceList, Empty,
    EventTypeEnum, Link, LinkEvent, LinkId, LinkIdList, LinkList,
    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 +67,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 +218,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_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 +326,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:
            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 +401,33 @@ 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)))
        LOGGER.warning('type: {}'.format(type(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 -------------------------------------------------------------------------------------------------

+26 −2
Original line number Diff line number Diff line
@@ -12,18 +12,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from flask_restful import Resource
from flask_restful import Resource, request
from common.proto.context_pb2 import Empty
from context.client.ContextClient import ContextClient
from service.client.ServiceClient import ServiceClient
import logging
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)
    grpc_service_id, grpc_service, grpc_slice_id, grpc_topology_id)

LOGGER = logging.getLogger(__name__)

class _Resource(Resource):
    def __init__(self) -> None:
        super().__init__()
        self.client = ContextClient()
        self.service_client = ServiceClient()

class ContextIds(_Resource):
    def get(self):
@@ -60,6 +64,26 @@ class Services(_Resource):
class Service(_Resource):
    def get(self, context_uuid : str, service_uuid : str):
        return format_grpc_to_json(self.client.GetService(grpc_service_id(context_uuid, service_uuid)))
    def post(self, context_uuid : str, service_uuid : str):
        service = request.get_json()['services'][0]
        
        return format_grpc_to_json(self.service_client.CreateService(grpc_service(
            service_uuid = service['service_id']['service_uuid']['uuid'],
            service_type = service['service_type'],
            context_uuid = service['service_id']['context_id']['context_uuid']['uuid'],
            )))
    def put(self, context_uuid : str, service_uuid : str):
        service = request.get_json()['services'][0]

        return format_grpc_to_json(self.service_client.UpdateService(grpc_service(
            service_uuid = service['service_id']['service_uuid']['uuid'],
            service_type = service['service_type'],
            context_uuid = service['service_id']['context_id']['context_uuid']['uuid'],
            status       = service['service_status']['service_status'],
            endpoint_ids = service['service_endpoint_ids'],
            constraints  = service['service_constraints'],
            config_rules = service['service_config']['config_rules']
            )))        

class SliceIds(_Resource):
    def get(self, context_uuid : str):
+15 −2
Original line number Diff line number Diff line
@@ -13,17 +13,21 @@
# limitations under the License.

from flask.json import jsonify
from common.proto.context_pb2 import ConnectionId, ContextId, DeviceId, LinkId, ServiceId, SliceId, TopologyId
from common.proto.context_pb2 import ConnectionId, ContextId, DeviceId, LinkId, ServiceId, SliceId, TopologyId, Service
from common.proto.policy_pb2 import PolicyRuleId
from common.tools.grpc.Tools import grpc_message_to_json
from common.tools.object_factory.Connection import json_connection_id
from common.tools.object_factory.Context import json_context_id
from common.tools.object_factory.ConfigRule import json_config_rule
from common.tools.object_factory.Constraint import json_constraint_custom
from common.tools.object_factory.EndPoint import json_endpoint_id
from common.tools.object_factory.Device import json_device_id
from common.tools.object_factory.Link import json_link_id
from common.tools.object_factory.PolicyRule import json_policyrule_id
from common.tools.object_factory.Service import json_service_id
from common.tools.object_factory.Service import json_service_id, json_service
from common.tools.object_factory.Slice import json_slice_id
from common.tools.object_factory.Topology import json_topology_id
from common.proto.context_pb2 import ServiceStatusEnum


def format_grpc_to_json(grpc_reply):
@@ -44,6 +48,15 @@ def grpc_link_id(link_uuid):
def grpc_service_id(context_uuid, service_uuid):
    return ServiceId(**json_service_id(service_uuid, context_id=json_context_id(context_uuid)))

def grpc_service(service_uuid, service_type, context_uuid, status=None, endpoint_ids=None, constraints=None, config_rules=None):
    json_context = json_context_id(context_uuid)
    json_status = status if status else ServiceStatusEnum.SERVICESTATUS_PLANNED
    json_endpoints_ids = [json_endpoint_id(json_device_id(endpoint_id['device_id']['device_uuid']['uuid']), endpoint_id['endpoint_uuid']['uuid']) for endpoint_id in endpoint_ids] if endpoint_ids else []
    json_constraints = [json_constraint_custom(constraint['custom']['constraint_type'], constraint['custom']['constraint_value']) for constraint in constraints] if constraints else []
    json_config_rules = [json_config_rule(config_rule['action'], config_rule['custom']['resource_key'],config_rule['custom']['resource_value']) for config_rule in config_rules] if config_rules else []

    return Service(**json_service(service_uuid, service_type, json_context, json_status, json_endpoints_ids, json_constraints, json_config_rules))

def grpc_slice_id(context_uuid, slice_uuid):
    return SliceId(**json_slice_id(slice_uuid, context_id=json_context_id(context_uuid)))
    
+25 −14
Original line number Diff line number Diff line
@@ -17,12 +17,14 @@ from sqlalchemy import delete
#from sqlalchemy.dialects import postgresql
from sqlalchemy.dialects.postgresql import insert
from sqlalchemy.orm import Session
from typing import Dict, List, Optional
from typing import Dict, List, Optional, Set
from common.proto.context_pb2 import Constraint
from common.tools.grpc.Tools import grpc_message_to_json_string
from .models.ConstraintModel import ConstraintKindEnum, ServiceConstraintModel, SliceConstraintModel
from .models.enums.ConstraintAction import ORM_ConstraintActionEnum, grpc_to_enum__constraint_action
from .uuids._Builder import get_uuid_from_string
from .uuids.EndPoint import endpoint_get_uuid
import json

LOGGER = logging.getLogger(__name__)

@@ -40,8 +42,8 @@ def compose_constraints_data(
            'data'      : grpc_message_to_json_string(getattr(constraint, str_kind, {})),
            'created_at': now,
            'updated_at': now,
            'action'    : grpc_to_enum__constraint_action(constraint.action)
        }

        parent_kind,parent_uuid = '',None
        if service_uuid is not None:
            dict_constraint['service_uuid'] = service_uuid
@@ -58,9 +60,9 @@ def compose_constraints_data(
        if kind == ConstraintKindEnum.CUSTOM:
            constraint_name = '{:s}:{:s}:{:s}'.format(parent_kind, kind.value, constraint.custom.constraint_type)
        elif kind == ConstraintKindEnum.ENDPOINT_LOCATION:
            # _, _, endpoint_uuid = endpoint_get_uuid(constraint.endpoint_location.endpoint_id, allow_random=False)
            _, _, endpoint_uuid = endpoint_get_uuid(constraint.endpoint_location.endpoint_id, allow_random=False)
            location_kind = constraint.endpoint_location.location.WhichOneof('location')
            constraint_name = '{:s}:{:s}:{:s}:{:s}'.format(parent_kind, kind.value, '', location_kind)
            constraint_name = '{:s}:{:s}:{:s}:{:s}'.format(parent_kind, kind.value, endpoint_uuid, location_kind)
        elif kind == ConstraintKindEnum.ENDPOINT_PRIORITY:
            _, _, endpoint_uuid = endpoint_get_uuid(constraint.endpoint_priority.endpoint_id, allow_random=False)
            constraint_name = '{:s}:{:s}:{:s}'.format(parent_kind, kind.value, endpoint_uuid)
@@ -78,6 +80,7 @@ def compose_constraints_data(
        dict_constraint['constraint_uuid'] = constraint_uuid

        dict_constraints.append(dict_constraint)

    return dict_constraints

def upsert_constraints(
@@ -91,11 +94,16 @@ def upsert_constraints(
    else:
        MSG = 'DataModel cannot be identified (service_uuid={:s}, slice_uuid={:s})'
        raise Exception(MSG.format(str(service_uuid), str(slice_uuid)))

    uuids_to_upsert : Dict[str, int] = dict()
    rules_to_upsert : List[Dict] = list()
    uuids_to_delete : Set[str] = set()
    for constraint in constraints:
        constraint_uuid = constraint['constraint_uuid']
        constraint_action = constraint['action']

        if is_delete or constraint_action == ORM_ConstraintActionEnum.DELETE:
            uuids_to_delete.add(constraint_uuid)
        elif constraint_action == ORM_ConstraintActionEnum.SET:
            position = uuids_to_upsert.get(constraint_uuid)
            if position is None:
                # if not added, add it
@@ -104,10 +112,13 @@ def upsert_constraints(
            else:
                # if already added, update occurrence
                rules_to_upsert[position] = constraint
        else:
            MSG = 'Action for ConstraintRule({:s}) is not supported (service_uuid={:s}, slice_uuid={:s})'
            LOGGER.warning(MSG.format(str(constraint), str(service_uuid), str(slice_uuid)))
            # raise Exception(MSG.format(str_constraint, str(service_uuid), str(slice_uuid)))

    # Delete all constraints not in uuids_to_upsert
    delete_affected = False
    if len(uuids_to_upsert) > 0:
    if len(uuids_to_delete) > 0:
        stmt = delete(klass)
        if service_uuid is not None: stmt = stmt.where(klass.service_uuid == service_uuid)
        if slice_uuid   is not None: stmt = stmt.where(klass.slice_uuid   == slice_uuid  )
Loading