diff --git a/src/context/service/database/Constraint.py b/src/context/service/database/Constraint.py index 3a73f6589f9332aa4c84f8f296f2cb56db3048bf..fdc84eceb575ba49ff244ca383afadd933cb1218 100644 --- a/src/context/service/database/Constraint.py +++ b/src/context/service/database/Constraint.py @@ -58,9 +58,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, endpoint_uuid, location_kind) + constraint_name = '{:s}:{:s}:{:s}:{:s}'.format(parent_kind, kind.value, '', 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) diff --git a/src/context/service/database/Device.py b/src/context/service/database/Device.py index 3e106bc158ab804c7eada7284e9d1b883eb66264..e7aa4170e389b4cf12a361ba8ef3785c9bb041f3 100644 --- a/src/context/service/database/Device.py +++ b/src/context/service/database/Device.py @@ -32,6 +32,8 @@ from .models.enums.KpiSampleType import grpc_to_enum__kpi_sample_type from .uuids.Device import device_get_uuid from .uuids.EndPoint import endpoint_get_uuid from .ConfigRule import compose_config_rules_data, upsert_config_rules +from common.tools.grpc.Tools import grpc_message_to_json +import json LOGGER = logging.getLogger(__name__) @@ -110,14 +112,15 @@ def device_set(db_engine : Engine, request : Device) -> Tuple[Dict, bool]: kpi_sample_types = [grpc_to_enum__kpi_sample_type(kst) for kst in endpoint.kpi_sample_types] endpoints_data.append({ - 'endpoint_uuid' : endpoint_uuid, - 'device_uuid' : endpoint_device_uuid, - 'topology_uuid' : endpoint_topology_uuid, - 'name' : endpoint_name, - 'endpoint_type' : endpoint.endpoint_type, - 'kpi_sample_types': kpi_sample_types, - 'created_at' : now, - 'updated_at' : now, + 'endpoint_uuid' : endpoint_uuid, + 'device_uuid' : endpoint_device_uuid, + 'topology_uuid' : endpoint_topology_uuid, + 'name' : endpoint_name, + 'endpoint_type' : endpoint.endpoint_type, + 'kpi_sample_types' : kpi_sample_types, + 'endpoint_location': json.dumps(grpc_message_to_json(endpoint.endpoint_location)), + 'created_at' : now, + 'updated_at' : now, }) if endpoint_topology_uuid not in topology_uuids: diff --git a/src/context/service/database/models/EndPointModel.py b/src/context/service/database/models/EndPointModel.py index a079f9900e39fdf3a4329e604f4e596e7f5d1f89..f5be89b5a9af5ef41378230f6e1b19ac7fd8f67c 100644 --- a/src/context/service/database/models/EndPointModel.py +++ b/src/context/service/database/models/EndPointModel.py @@ -18,18 +18,20 @@ from sqlalchemy.orm import relationship from typing import Dict from .enums.KpiSampleType import ORM_KpiSampleTypeEnum from ._Base import _Base +import json class EndPointModel(_Base): __tablename__ = 'endpoint' - endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True) - device_uuid = Column(ForeignKey('device.device_uuid', ondelete='CASCADE' ), nullable=False, index=True) - topology_uuid = Column(ForeignKey('topology.topology_uuid', ondelete='RESTRICT'), nullable=False, index=True) - name = Column(String, nullable=False) - endpoint_type = Column(String, nullable=False) - kpi_sample_types = Column(ARRAY(Enum(ORM_KpiSampleTypeEnum), dimensions=1)) - created_at = Column(DateTime, nullable=False) - updated_at = Column(DateTime, nullable=False) + endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True) + device_uuid = Column(ForeignKey('device.device_uuid', ondelete='CASCADE' ), nullable=False, index=True) + topology_uuid = Column(ForeignKey('topology.topology_uuid', ondelete='RESTRICT'), nullable=False, index=True) + name = Column(String, nullable=False) + endpoint_type = Column(String, nullable=False) + kpi_sample_types = Column(ARRAY(Enum(ORM_KpiSampleTypeEnum), dimensions=1)) + created_at = Column(DateTime, nullable=False) + updated_at = Column(DateTime, nullable=False) + endpoint_location = Column(String, nullable=True) device = relationship('DeviceModel', back_populates='endpoints') # lazy='selectin' topology = relationship('TopologyModel', lazy='selectin') @@ -46,10 +48,11 @@ class EndPointModel(_Base): def dump(self) -> Dict: return { - 'endpoint_id' : self.dump_id(), - 'name' : self.name, - 'endpoint_type' : self.endpoint_type, - 'kpi_sample_types': [kst.value for kst in self.kpi_sample_types], + 'endpoint_id' : self.dump_id(), + 'name' : self.name, + 'endpoint_type' : self.endpoint_type, + 'kpi_sample_types' : [kst.value for kst in self.kpi_sample_types], + 'endpoint_location': json.loads(self.endpoint_location) } def dump_name(self) -> Dict: diff --git a/src/context/service/database/models/ServiceModel.py b/src/context/service/database/models/ServiceModel.py index ef6e1b06aaaa616ede6f9633e4e0d7fc0aabf336..e047438bd1a9112b6c09fd0def696aa666c4c439 100644 --- a/src/context/service/database/models/ServiceModel.py +++ b/src/context/service/database/models/ServiceModel.py @@ -32,7 +32,7 @@ class ServiceModel(_Base): created_at = Column(DateTime, nullable=False) updated_at = Column(DateTime, nullable=False) - context = relationship('ContextModel', back_populates='services', lazy='selectin') + context = relationship('ContextModel', back_populates='services', lazy='selectin',) service_endpoints = relationship('ServiceEndPointModel') # lazy='selectin', back_populates='service' constraints = relationship('ConstraintModel', passive_deletes=True) # lazy='selectin', back_populates='service' config_rules = relationship('ConfigRuleModel', passive_deletes=True) # lazy='selectin', back_populates='service' @@ -79,7 +79,7 @@ class ServiceEndPointModel(_Base): __tablename__ = 'service_endpoint' service_uuid = Column(ForeignKey('service.service_uuid', ondelete='CASCADE' ), primary_key=True) - endpoint_uuid = Column(ForeignKey('endpoint.endpoint_uuid', ondelete='RESTRICT'), primary_key=True, index=True) + endpoint_uuid = Column(ForeignKey('endpoint.endpoint_uuid', ondelete='RESTRICT'), primary_key=True, index=True, nullable=True) position = Column(Integer, nullable=False) service = relationship('ServiceModel', back_populates='service_endpoints') # lazy='selectin' diff --git a/src/device/service/Tools.py b/src/device/service/Tools.py index cd3af07e3324e50ff43eb5e653c4c46771a5507e..149b4d5327566d509d0d552c028eb3a3bc4b3698 100644 --- a/src/device/service/Tools.py +++ b/src/device/service/Tools.py @@ -16,12 +16,13 @@ import json, logging from typing import Any, Dict, List, Optional, Tuple, Union from common.Constants import DEFAULT_CONTEXT_NAME, DEFAULT_TOPOLOGY_NAME from common.method_wrappers.ServiceExceptions import InvalidArgumentException -from common.proto.context_pb2 import ConfigActionEnum, Device, DeviceConfig, Link +from common.proto.context_pb2 import ConfigActionEnum, Device, DeviceConfig, Link, Location from common.proto.device_pb2 import MonitoringSettings from common.proto.kpi_sample_types_pb2 import KpiSampleType from common.tools.grpc.ConfigRules import update_config_rule_custom from common.tools.grpc.Tools import grpc_message_to_json from context.client.ContextClient import ContextClient +from google.protobuf import json_format from .driver_api._Driver import _Driver, RESOURCE_ENDPOINTS from .monitoring.MonitoringLoops import MonitoringLoops from .ErrorMessages import ( @@ -194,6 +195,10 @@ def populate_endpoints( device_endpoint.kpi_sample_types.append(kpi_sample_type) monitoring_loops.add_resource_key(device_uuid, endpoint_uuid, kpi_sample_type, monitor_resource_key) + location = resource_value.get('location', {}) + if location: + json_format.Parse(json.dumps(location), device_endpoint.endpoint_location) + elif resource_key.startswith('/links/link'): # create sub-link _sub_link_uuid = resource_value['uuid'] diff --git a/src/device/service/drivers/emulated/Tools.py b/src/device/service/drivers/emulated/Tools.py index 4770cc6e6d2b2ccbf86d1e3764e62f03b48837e2..de1acf4cf4463560003cf9b8d21b3743fd450d6c 100644 --- a/src/device/service/drivers/emulated/Tools.py +++ b/src/device/service/drivers/emulated/Tools.py @@ -43,4 +43,8 @@ def compose_resource_endpoint(endpoint_data : Dict[str, Any]) -> Tuple[str, Any] sample_types[endpoint_sample_type] = monitoring_resource_key endpoint_resource_value = {'uuid': endpoint_uuid, 'type': endpoint_type, 'sample_types': sample_types} + + if 'location' in endpoint_data: + endpoint_resource_value['location'] = endpoint_data['location'] + return endpoint_resource_key, endpoint_resource_value diff --git a/src/service/requirements.in b/src/service/requirements.in index bb321e22a1bd56df4193d06c0481acc44640ea7e..41034109feb6edd109557676013a85bf2866ed70 100644 --- a/src/service/requirements.in +++ b/src/service/requirements.in @@ -17,4 +17,4 @@ anytree==2.8.0 networkx==2.6.3 pydot==1.4.2 redis==4.1.2 -geopy==2.3.0 +geopy==2.3.0 \ No newline at end of file diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index 767ba2a58ac73673df5e0c24c2c3e952da471fac..c2fe6356269b983ca7b74ed9c7236fb728fedcd3 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -26,6 +26,7 @@ from .service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory from .task_scheduler.TaskScheduler import TasksScheduler from .tools.ContextGetters import get_service from .tools.GeodesicDistance import gps_distance +import copy LOGGER = logging.getLogger(__name__) @@ -90,6 +91,15 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): _service : Optional[Service] = get_service(context_client, request.service_id) service = Service() service.CopyFrom(request if _service is None else _service) + + if service.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_ACTIVE: + self.DeleteService(service.service_id, context) + new_service = copy.deepcopy(service) + del new_service.service_endpoint_ids[:] + del new_service.service_constraints[:] + del new_service.service_config.config_rules[:] + self.CreateService(new_service , context) + if service.service_type == ServiceTypeEnum.SERVICETYPE_UNKNOWN: # pylint: disable=no-member service.service_type = request.service_type # pylint: disable=no-member service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED # pylint: disable=no-member @@ -106,6 +116,24 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): for config_rule in request.service_config.config_rules: service.service_config.config_rules.add().CopyFrom(config_rule) # pylint: disable=no-member + + for constraint in request.service_constraints: + if constraint.WhichOneof('constraint') == 'endpoint_location': + context_client = ContextClient() + device_list = context_client.ListDevices(Empty()) + service_location = constraint.endpoint_location.location + distances = {} + for device in device_list.devices: + for endpoint in device.device_endpoints: + if endpoint.endpoint_location.HasField('gps_position'): + distance = gps_distance(service_location.gps_position, endpoint.endpoint_location.gps_position) + distances[distance] = endpoint.endpoint_id + + min_distance = min(distances) + closer_endpoint_id = distances[min_distance] + service.service_endpoint_ids.append(closer_endpoint_id) + # pathcomp_request.services[0].service_endpoint_ids.extend(closer_endpoint_ids) + service_id_with_uuids = context_client.SetService(service) service_with_uuids = context_client.GetService(service_id_with_uuids) diff --git a/src/webui/service/templates/device/detail.html b/src/webui/service/templates/device/detail.html index 1b4b43f5ad12956ae8bb2b1a843ce5e57ef29a2c..6fd058ad073187ff1c8da9fa92573905351fec9f 100644 --- a/src/webui/service/templates/device/detail.html +++ b/src/webui/service/templates/device/detail.html @@ -62,6 +62,7 @@ <th scope="col">Endpoint UUID</th> <th scope="col">Name</th> <th scope="col">Type</th> + <th scope="col">Position</th> </tr> </thead> <tbody> @@ -76,6 +77,9 @@ <td> {{ endpoint.endpoint_type }} </td> + <td> + {{ endpoint.endpoint_location }} + </td> </tr> {% endfor %} </tbody>