diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index 65d05db904b65a22c202881fdc50bb0496269701..6d540b4945df8516697c957316294a452186ddb1 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -36,7 +36,7 @@ from .database.EndPoint import endpoint_list_names from .database.Link import link_delete, link_get, link_list_ids, link_list_objs, link_set from .database.PolicyRule import ( policyrule_delete, policyrule_get, policyrule_list_ids, policyrule_list_objs, policyrule_set) -from .database.Service import service_delete, service_get, service_list_ids, service_list_objs, service_select, service_set +from .database.Service import service_delete, service_get, service_list_ids, service_list_objs, service_select, service_set, service_unset from .database.Slice import slice_delete, slice_get, slice_list_ids, slice_list_objs, slice_select, slice_set, slice_unset from .database.Topology import ( topology_delete, topology_get, topology_get_details, topology_list_ids, topology_list_objs, topology_set) @@ -231,6 +231,14 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': service_id}) return ServiceId(**service_id) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) + def UnsetService(self, request : Service, context : grpc.ServicerContext) -> ServiceId: + service_id,updated = service_unset(self.db_engine, request) + if updated: + event_type = EventTypeEnum.EVENTTYPE_UPDATE + notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': service_id}) + return ServiceId(**service_id) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemoveService(self, request : ServiceId, context : grpc.ServicerContext) -> Empty: service_id,deleted = service_delete(self.db_engine, request) diff --git a/src/context/service/database/Service.py b/src/context/service/database/Service.py index 32484a3095c3d937392f580597339fe047d36e3f..b6916dc3a19fef4bde3aff93300e63f360b362c0 100644 --- a/src/context/service/database/Service.py +++ b/src/context/service/database/Service.py @@ -13,11 +13,12 @@ # limitations under the License. import datetime, logging +from sqlalchemy import and_ from sqlalchemy.dialects.postgresql import insert from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, selectinload, sessionmaker from sqlalchemy_cockroachdb import run_transaction -from typing import Dict, List, Optional, Tuple +from typing import Dict, List, Optional, Set, Tuple from common.proto.context_pb2 import ContextId, Service, ServiceFilter, ServiceId from common.method_wrappers.ServiceExceptions import InvalidArgumentException, NotFoundException from common.tools.object_factory.Context import json_context_id @@ -146,6 +147,45 @@ def service_set(db_engine : Engine, request : Service) -> Tuple[Dict, bool]: updated = run_transaction(sessionmaker(bind=db_engine), callback) return json_service_id(service_uuid, json_context_id(context_uuid)),updated +def service_unset(db_engine : Engine, request : Service) -> Tuple[Dict, bool]: + raw_context_uuid = request.service_id.context_id.context_uuid.uuid + raw_service_uuid = request.service_id.service_uuid.uuid + raw_service_name = request.name + service_name = raw_service_uuid if len(raw_service_name) == 0 else raw_service_name + context_uuid,service_uuid = service_get_uuid(request.service_id, service_name=service_name, allow_random=False) + + service_endpoint_uuids : Set[str] = set() + for i,endpoint_id in enumerate(request.service_endpoint_ids): + endpoint_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + if len(endpoint_context_uuid) == 0: endpoint_context_uuid = context_uuid + if endpoint_context_uuid not in {raw_context_uuid, context_uuid}: + raise InvalidArgumentException( + 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), + endpoint_context_uuid, + ['should be == request.service_id.context_id.context_uuid.uuid({:s})'.format(raw_context_uuid)]) + service_endpoint_uuids.add(endpoint_get_uuid(endpoint_id, allow_random=False)[2]) + + now = datetime.datetime.utcnow() + constraints = compose_constraints_data(request.service_constraints, now, service_uuid=service_uuid) + config_rules = compose_config_rules_data(request.service_config.config_rules, now, service_uuid=service_uuid) + + def callback(session : Session) -> bool: + num_deletes = 0 + if len(service_endpoint_uuids) > 0: + num_deletes += session.query(ServiceEndPointModel)\ + .filter(and_( + ServiceEndPointModel.service_uuid == service_uuid, + ServiceEndPointModel.endpoint_uuid.in_(service_endpoint_uuids) + )).delete() + + changed_constraints = upsert_constraints(session, constraints, is_delete=True, service_uuid=service_uuid) + changed_config_rules = upsert_config_rules(session, config_rules, is_delete=True, service_uuid=service_uuid) + + return num_deletes > 0 or changed_constraints or changed_config_rules + + updated = run_transaction(sessionmaker(bind=db_engine), callback) + return json_service_id(service_uuid, json_context_id(context_uuid)),updated + def service_delete(db_engine : Engine, request : ServiceId) -> Tuple[Dict, bool]: context_uuid,service_uuid = service_get_uuid(request, allow_random=False) def callback(session : Session) -> bool: