diff --git a/src/common/tools/context_queries/CheckType.py b/src/common/tools/context_queries/CheckType.py new file mode 100644 index 0000000000000000000000000000000000000000..f53ad16906336182311d1d98fec428f1472bf748 --- /dev/null +++ b/src/common/tools/context_queries/CheckType.py @@ -0,0 +1,28 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +from typing import Union +from common.DeviceTypes import DeviceTypeEnum + +def device_type_is_datacenter(device_type : Union[str, DeviceTypeEnum]) -> bool: + return device_type in { + DeviceTypeEnum.DATACENTER, DeviceTypeEnum.DATACENTER.value, + DeviceTypeEnum.EMULATED_DATACENTER, DeviceTypeEnum.EMULATED_DATACENTER.value + } + +def device_type_is_network(device_type : Union[str, DeviceTypeEnum]) -> bool: + return device_type in {DeviceTypeEnum.NETWORK, DeviceTypeEnum.NETWORK.value} + +def endpoint_type_is_border(endpoint_type : str) -> bool: + return str(endpoint_type).endswith('/border') diff --git a/src/common/tools/context_queries/Context.py b/src/common/tools/context_queries/Context.py new file mode 100644 index 0000000000000000000000000000000000000000..cf0d3be2b7c1890e486492ad55add19a17591353 --- /dev/null +++ b/src/common/tools/context_queries/Context.py @@ -0,0 +1,25 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +from common.proto.context_pb2 import Context, Empty +from common.tools.object_factory.Context import json_context +from context.client.ContextClient import ContextClient + +def create_context( + context_client : ContextClient, context_uuid : str +) -> None: + existing_context_ids = context_client.ListContextIds(Empty()) + existing_context_uuids = {context_id.context_uuid.uuid for context_id in existing_context_ids.context_ids} + if context_uuid in existing_context_uuids: return + context_client.SetContext(Context(**json_context(context_uuid))) diff --git a/src/common/tools/context_queries/Device.py b/src/common/tools/context_queries/Device.py new file mode 100644 index 0000000000000000000000000000000000000000..e5b205d46185e12fa51a2cbd8146342abe5bed38 --- /dev/null +++ b/src/common/tools/context_queries/Device.py @@ -0,0 +1,59 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +from typing import List, Set +from common.proto.context_pb2 import ContextId, Device, Empty, Topology, TopologyId +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient + +def get_existing_device_uuids(context_client : ContextClient) -> Set[str]: + existing_device_ids = context_client.ListDeviceIds(Empty()) + existing_device_uuids = {device_id.device_uuid.uuid for device_id in existing_device_ids.device_ids} + return existing_device_uuids + +def add_device_to_topology( + context_client : ContextClient, context_id : ContextId, topology_uuid : str, device_uuid : str +) -> bool: + topology_id = TopologyId(**json_topology_id(topology_uuid, context_id=context_id)) + topology_ro = context_client.GetTopology(topology_id) + device_uuids = {device_id.device_uuid.uuid for device_id in topology_ro.device_ids} + if device_uuid in device_uuids: return False # already existed + + topology_rw = Topology() + topology_rw.CopyFrom(topology_ro) + topology_rw.device_ids.add().device_uuid.uuid = device_uuid # pylint: disable=no-member + context_client.SetTopology(topology_rw) + return True + +def get_uuids_of_devices_in_topology( + context_client : ContextClient, context_id : ContextId, topology_uuid : str +) -> List[str]: + topology_id = TopologyId(**json_topology_id(topology_uuid, context_id=context_id)) + topology = context_client.GetTopology(topology_id) + device_uuids = [device_id.device_uuid.uuid for device_id in topology.device_ids] + return device_uuids + +def get_devices_in_topology( + context_client : ContextClient, context_id : ContextId, topology_uuid : str +) -> List[Device]: + device_uuids = get_uuids_of_devices_in_topology(context_client, context_id, topology_uuid) + + all_devices = context_client.ListDevices(Empty()) + devices_in_topology = list() + for device in all_devices.devices: + device_uuid = device.device_id.device_uuid.uuid + if device_uuid not in device_uuids: continue + devices_in_topology.append(device) + + return devices_in_topology diff --git a/src/common/tools/context_queries/InterDomain.py b/src/common/tools/context_queries/InterDomain.py new file mode 100644 index 0000000000000000000000000000000000000000..0e97e3e8a24f196486f91e8a8b26c8d535405b4d --- /dev/null +++ b/src/common/tools/context_queries/InterDomain.py @@ -0,0 +1,150 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging +from typing import Dict, List, Set, Tuple +from common.Constants import DEFAULT_CONTEXT_UUID, INTERDOMAIN_TOPOLOGY_UUID +from common.DeviceTypes import DeviceTypeEnum +from common.proto.context_pb2 import ContextId, Device, Empty, EndPointId, ServiceTypeEnum, Slice +from common.proto.pathcomp_pb2 import PathCompRequest +from common.tools.context_queries.CheckType import device_type_is_network +from common.tools.context_queries.Device import get_devices_in_topology, get_uuids_of_devices_in_topology +from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.object_factory.Context import json_context_id +from context.client.ContextClient import ContextClient +from pathcomp.frontend.client.PathCompClient import PathCompClient + +LOGGER = logging.getLogger(__name__) + +ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_UUID)) +DATACENTER_DEVICE_TYPES = {DeviceTypeEnum.DATACENTER, DeviceTypeEnum.EMULATED_DATACENTER} + +def get_local_device_uuids(context_client : ContextClient) -> Set[str]: + topologies = context_client.ListTopologies(ADMIN_CONTEXT_ID) + topologies = {topology.topology_id.topology_uuid.uuid : topology for topology in topologies.topologies} + + local_topology_uuids = set(topologies.keys()) + local_topology_uuids.discard(INTERDOMAIN_TOPOLOGY_UUID) + + local_device_uuids = set() + for local_topology_uuid in local_topology_uuids: + topology_device_ids = topologies[local_topology_uuid].device_ids + topology_device_uuids = {device_id.device_uuid.uuid for device_id in topology_device_ids} + local_device_uuids.update(topology_device_uuids) + + return local_device_uuids + +def get_local_domain_devices(context_client : ContextClient) -> List[Device]: + local_device_uuids = get_local_device_uuids(context_client) + all_devices = context_client.ListDevices(Empty()) + local_domain_devices = list() + for device in all_devices.devices: + if not device_type_is_network(device.device_type): continue + device_uuid = device.device_id.device_uuid.uuid + if device_uuid not in local_device_uuids: continue + local_domain_devices.append(device) + return local_domain_devices + +def is_multi_domain(context_client : ContextClient, endpoint_ids : List[EndPointId]) -> bool: + local_device_uuids = get_local_device_uuids(context_client) + remote_endpoint_ids = [ + endpoint_id + for endpoint_id in endpoint_ids + if endpoint_id.device_id.device_uuid.uuid not in local_device_uuids + ] + LOGGER.info('remote_endpoint_ids = {:s}'.format(str(remote_endpoint_ids))) + is_multi_domain_ = len(remote_endpoint_ids) > 0 + LOGGER.info('is_multi_domain = {:s}'.format(str(is_multi_domain_))) + return is_multi_domain_ + +def compute_interdomain_path( + pathcomp_client : PathCompClient, slice_ : Slice +) -> List[Tuple[str, List[EndPointId]]]: + context_uuid = slice_.slice_id.context_id.context_uuid.uuid + slice_uuid = slice_.slice_id.slice_uuid.uuid + + pathcomp_req = PathCompRequest() + pathcomp_req.shortest_path.Clear() # pylint: disable=no-member + pathcomp_req_svc = pathcomp_req.services.add() # pylint: disable=no-member + pathcomp_req_svc.service_id.context_id.context_uuid.uuid = context_uuid + pathcomp_req_svc.service_id.service_uuid.uuid = slice_uuid + pathcomp_req_svc.service_type = ServiceTypeEnum.SERVICETYPE_L2NM + + for endpoint_id in slice_.slice_endpoint_ids: + service_endpoint_id = pathcomp_req_svc.service_endpoint_ids.add() + service_endpoint_id.CopyFrom(endpoint_id) + + constraint_bw = pathcomp_req_svc.service_constraints.add() + constraint_bw.custom.constraint_type = 'bandwidth[gbps]' + constraint_bw.custom.constraint_value = '10.0' + + constraint_lat = pathcomp_req_svc.service_constraints.add() + constraint_lat.custom.constraint_type = 'latency[ms]' + constraint_lat.custom.constraint_value = '100.0' + + LOGGER.info('pathcomp_req = {:s}'.format(grpc_message_to_json_string(pathcomp_req))) + pathcomp_rep = pathcomp_client.Compute(pathcomp_req) + LOGGER.info('pathcomp_rep = {:s}'.format(grpc_message_to_json_string(pathcomp_rep))) + + service = next(iter([ + service + for service in pathcomp_rep.services + if service.service_id == pathcomp_req_svc.service_id + ]), None) + if service is None: + str_service_id = grpc_message_to_json_string(pathcomp_req_svc.service_id) + raise Exception('Service({:s}) not found'.format(str_service_id)) + + connection = next(iter([ + connection + for connection in pathcomp_rep.connections + if connection.service_id == pathcomp_req_svc.service_id + ]), None) + if connection is None: + str_service_id = grpc_message_to_json_string(pathcomp_req_svc.service_id) + raise Exception('Connection for Service({:s}) not found'.format(str_service_id)) + + domain_list : List[str] = list() + domain_to_endpoint_ids : Dict[str, List[EndPointId]] = dict() + for endpoint_id in connection.path_hops_endpoint_ids: + device_uuid = endpoint_id.device_id.device_uuid.uuid + #endpoint_uuid = endpoint_id.endpoint_uuid.uuid + if device_uuid not in domain_to_endpoint_ids: domain_list.append(device_uuid) + domain_to_endpoint_ids.setdefault(device_uuid, []).append(endpoint_id) + + return [ + (domain_uuid, domain_to_endpoint_ids.get(domain_uuid)) + for domain_uuid in domain_list + ] + +def compute_traversed_domains( + context_client : ContextClient, interdomain_path : List[Tuple[str, List[EndPointId]]] +) -> List[Tuple[str, Device, bool, List[EndPointId]]]: + + local_device_uuids = get_local_device_uuids(context_client) + interdomain_devices = get_devices_in_topology(context_client, ADMIN_CONTEXT_ID, INTERDOMAIN_TOPOLOGY_UUID) + interdomain_devices = { + device.device_id.device_uuid.uuid : device + for device in interdomain_devices + } + + traversed_domains : List[Tuple[str, Device, bool, List[EndPointId]]] = list() + for device_uuid, endpoint_ids in interdomain_path: + abstract_device = interdomain_devices[device_uuid] + if abstract_device.device_type in DATACENTER_DEVICE_TYPES: continue + is_local_domain = device_uuid not in local_device_uuids + domain = (device_uuid, abstract_device, is_local_domain, endpoint_ids) + traversed_domains.append(domain) + + return traversed_domains diff --git a/src/common/tools/context_queries/Link.py b/src/common/tools/context_queries/Link.py new file mode 100644 index 0000000000000000000000000000000000000000..abc5fa91af8d24c8a3cdf18fda0e7680da9143a7 --- /dev/null +++ b/src/common/tools/context_queries/Link.py @@ -0,0 +1,59 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +from typing import List, Set +from common.proto.context_pb2 import ContextId, Empty, Link, Topology, TopologyId +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient + +def get_existing_link_uuids(context_client : ContextClient) -> Set[str]: + existing_link_ids = context_client.ListLinkIds(Empty()) + existing_link_uuids = {link_id.link_uuid.uuid for link_id in existing_link_ids.link_ids} + return existing_link_uuids + +def add_link_to_topology( + context_client : ContextClient, context_id : ContextId, topology_uuid : str, link_uuid : str +) -> bool: + topology_id = TopologyId(**json_topology_id(topology_uuid, context_id=context_id)) + topology_ro = context_client.GetTopology(topology_id) + link_uuids = {link_id.link_uuid.uuid for link_id in topology_ro.link_ids} + if link_uuid in link_uuids: return False # already existed + + topology_rw = Topology() + topology_rw.CopyFrom(topology_ro) + topology_rw.link_ids.add().link_uuid.uuid = link_uuid # pylint: disable=no-member + context_client.SetTopology(topology_rw) + return True + +def get_uuids_of_links_in_topology( + context_client : ContextClient, context_id : ContextId, topology_uuid : str +) -> List[str]: + topology_id = TopologyId(**json_topology_id(topology_uuid, context_id=context_id)) + topology = context_client.GetTopology(topology_id) + link_uuids = [link_id.link_uuid.uuid for link_id in topology.link_ids] + return link_uuids + +def get_links_in_topology( + context_client : ContextClient, context_id : ContextId, topology_uuid : str +) -> List[Link]: + link_uuids = get_uuids_of_links_in_topology(context_client, context_id, topology_uuid) + + all_links = context_client.ListLinks(Empty()) + links_in_topology = list() + for link in all_links.links: + link_uuid = link.link_id.link_uuid.uuid + if link_uuid not in link_uuids: continue + links_in_topology.append(link) + + return links_in_topology diff --git a/src/common/tools/context_queries/Topology.py b/src/common/tools/context_queries/Topology.py new file mode 100644 index 0000000000000000000000000000000000000000..fcf1b96bb51571a71ab35fb743f8154f02e2d200 --- /dev/null +++ b/src/common/tools/context_queries/Topology.py @@ -0,0 +1,41 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +from typing import List +from common.proto.context_pb2 import ContextId, Topology +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Topology import json_topology +from context.client.ContextClient import ContextClient + +def create_topology( + context_client : ContextClient, context_uuid : str, topology_uuid : str +) -> None: + context_id = ContextId(**json_context_id(context_uuid)) + existing_topology_ids = context_client.ListTopologyIds(context_id) + existing_topology_uuids = {topology_id.topology_uuid.uuid for topology_id in existing_topology_ids.topology_ids} + if topology_uuid in existing_topology_uuids: return + context_client.SetTopology(Topology(**json_topology(topology_uuid, context_id=context_id))) + +def create_missing_topologies( + context_client : ContextClient, context_id : ContextId, topology_uuids : List[str] +) -> None: + # Find existing topologies within own context + existing_topology_ids = context_client.ListTopologyIds(context_id) + existing_topology_uuids = {topology_id.topology_uuid.uuid for topology_id in existing_topology_ids.topology_ids} + + # Create topologies within provided context + for topology_uuid in topology_uuids: + if topology_uuid in existing_topology_uuids: continue + grpc_topology = Topology(**json_topology(topology_uuid, context_id=context_id)) + context_client.SetTopology(grpc_topology) diff --git a/src/common/tools/context_queries/__init__.py b/src/common/tools/context_queries/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..70a33251242c51f49140e596b8208a19dd5245f7 --- /dev/null +++ b/src/common/tools/context_queries/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/common/tools/object_factory/Slice.py b/src/common/tools/object_factory/Slice.py new file mode 100644 index 0000000000000000000000000000000000000000..6ab666aa6ed379eb0b8948b1178aa13069d70bf4 --- /dev/null +++ b/src/common/tools/object_factory/Slice.py @@ -0,0 +1,48 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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, List, Optional +from common.proto.context_pb2 import SliceStatusEnum + +def get_slice_uuid(a_endpoint_id : Dict, z_endpoint_id : Dict) -> str: + return 'slc:{:s}/{:s}=={:s}/{:s}'.format( + a_endpoint_id['device_id']['device_uuid']['uuid'], a_endpoint_id['endpoint_uuid']['uuid'], + z_endpoint_id['device_id']['device_uuid']['uuid'], z_endpoint_id['endpoint_uuid']['uuid']) + +def json_slice_id(slice_uuid : str, context_id : Optional[Dict] = None) -> Dict: + result = {'slice_uuid': {'uuid': slice_uuid}} + if context_id is not None: result['context_id'] = copy.deepcopy(context_id) + return result + +def json_slice_owner(owner_uuid : str, owner_string : str) -> Dict: + return {'owner_uuid': {'uuid': owner_uuid}, 'owner_string': owner_string} + +def json_slice( + slice_uuid : str, context_id : Optional[Dict] = None, + status : SliceStatusEnum = SliceStatusEnum.SLICESTATUS_PLANNED, endpoint_ids : List[Dict] = [], + constraints : List[Dict] = [], config_rules : List[Dict] = [], service_ids : List[Dict] = [], + subslice_ids : List[Dict] = [], owner : Optional[Dict] = None): + + result = { + 'slice_id' : json_slice_id(slice_uuid, context_id=context_id), + 'slice_status' : {'slice_status': status}, + 'slice_endpoint_ids': copy.deepcopy(endpoint_ids), + 'slice_constraints' : copy.deepcopy(constraints), + 'slice_config' : {'config_rules': copy.deepcopy(config_rules)}, + 'slice_service_ids' : copy.deepcopy(service_ids), + 'slice_subslice_ids': copy.deepcopy(subslice_ids), + } + if owner is not None: result['slice_owner'] = owner + return result