Loading src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py +3 −3 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. Loading Loading @@ -26,7 +26,7 @@ from ..tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK LOGGER = logging.getLogger(__name__) class NSS_Service(Resource): @HTTP_AUTH.login_required # @HTTP_AUTH.login_required def get(self, slice_id : str): LOGGER.debug('GET Slice ID: {:s}'.format(str(slice_id))) try: Loading @@ -51,7 +51,7 @@ class NSS_Service(Resource): return response @HTTP_AUTH.login_required # @HTTP_AUTH.login_required def delete(self, slice_id : str): LOGGER.debug('DELETE Slice ID: {:s}'.format(str(slice_id))) try: Loading src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service_Match_Criteria.py 0 → 100644 +55 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 logging from typing import Dict from flask import request from flask.json import jsonify from flask_restful import Resource from werkzeug.exceptions import UnsupportedMediaType from context.client.ContextClient import ContextClient from slice.client.SliceClient import SliceClient from ..tools.Authentication import HTTP_AUTH from ..tools.HttpStatusCodes import ( HTTP_CREATED, ) from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) class NSS_Service_Match_Criteria(Resource): # @HTTP_AUTH.login_required def get(self): response = jsonify({"message": "All went well!"}) # TODO Return list of current network-slice-services return response # @HTTP_AUTH.login_required def post(self, slice_id: str, sdp_id: str): if not request.is_json: raise UnsupportedMediaType("JSON payload is required") request_data: Dict = request.json context_client = ContextClient() slice_request = IETFSliceHandler.create_match_criteria( request_data, slice_id, sdp_id, context_client ) slice_client = SliceClient() slice_client.UpdateSlice(slice_request) response = jsonify({}) response.status_code = HTTP_CREATED return response src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py +16 −90 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. Loading @@ -11,112 +11,38 @@ # 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 json import logging import ssl import uuid from typing import Dict from flask import request from flask.json import jsonify from flask_restful import Resource from flask import request from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import Slice, SliceStatusEnum, EndPointId, Constraint from common.tools.grpc.Tools import grpc_message_to_json from ..tools.Authentication import HTTP_AUTH from ..tools.HttpStatusCodes import HTTP_BADREQUEST, HTTP_OK, HTTP_CREATED, HTTP_SERVERERROR from werkzeug.exceptions import UnsupportedMediaType from slice.client.SliceClient import SliceClient from .bindings import load_json_data from .bindings.network_slice_services import NetworkSliceServices from ..tools.HttpStatusCodes import HTTP_CREATED from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) class NSS_Services(Resource): @HTTP_AUTH.login_required # @HTTP_AUTH.login_required def get(self): response = jsonify({"message": "All went well!"}) # TODO Return list of current network-slice-services return response @HTTP_AUTH.login_required # @HTTP_AUTH.login_required def post(self): if not request.is_json: raise UnsupportedMediaType('JSON payload is required') request_data = json.dumps(request.json) response = jsonify({}) response.status_code = HTTP_CREATED slices: NetworkSliceServices = load_json_data(request_data, NetworkSliceServices)[0] for ietf_slice in slices.slice_service: slice_request: Slice = Slice() # Meta information # TODO implement name and owner based on "tags" slice_request.slice_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME slice_request.slice_id.slice_uuid.uuid = ietf_slice.service_id() # TODO map with admin status of IETF Slice slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED list_endpoints = [] for sdp in ietf_slice.sdps().sdp: endpoint = EndPointId() endpoint.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME endpoint.device_id.device_uuid.uuid = sdp.node_id() endpoint.endpoint_uuid.uuid = sdp.sdp_id() list_endpoints.append(endpoint) slice_request.slice_endpoint_ids.extend(list_endpoints) # TODO Map connectivity_groups and connectivity constructs to real connections LOGGER.debug(f"Connection groups detected: {len(ietf_slice.connection_groups().connection_group())}") list_constraints = [] for cg in ietf_slice.connection_groups().connection_group: for cc in cg.connectivity_construct: if cc.slo_sle_policy.custom: with cc.slo_sle_policy.custom as slo: for metric_bound in slo.service_slo_sle_policy().metric_bounds().metric_bound: metric_type = str(metric_bound.metric_type()).casefold() if metric_type == "service-slo-two-way-bandwidth": # TODO fix to two way! constraint = Constraint() metric_unit = metric_bound.metric_unit().casefold() capacity = float(metric_bound.bound()) # Assuming capacity already in Gbps if metric_unit == "mbps": capacity /= 1E3 elif metric_unit != "gbps": LOGGER.warning(f"Invalided metric unit ({metric_bound.metric_unit()}), must be Mbps or Gbps") response.status_code = HTTP_SERVERERROR return response constraint.sla_capacity.capacity_gbps = capacity list_constraints.append(constraint) elif metric_type == "service-slo-one-way-delay": if metric_bound.metric_unit().casefold() == "ms": latency = int(metric_bound.bound()) else: LOGGER.warning(f"Invalided metric unit ({metric_bound.metric_unit()}), must be \"ms\" ") response.status_code = HTTP_SERVERERROR return response constraint = Constraint() constraint.sla_latency.e2e_latency_ms = latency list_constraints.append(constraint) elif metric_type == "service-slo-availability": availability = float(metric_bound.bound()) if availability > 100.0 or availability < 0.0: raise Exception(f'Slice SLO availability ({availability}) must be constrained [0,100]') constraint = Constraint() constraint.sla_availability.availability = availability # TODO not really necessary, remove after OFC2023 constraint.sla_availability.num_disjoint_paths = 0 constraint.sla_availability.all_active = False list_constraints.append(constraint) slice_request.slice_constraints.extend(list_constraints) LOGGER.debug(grpc_message_to_json(slice_request)) # TODO remove # TODO adding owner, needs to be recoded after updating the bindings owner = request.json["data"]["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["service-tags"][0]["value"] slice_request.slice_owner.owner_string = owner slice_request.slice_owner.owner_uuid.uuid = str(uuid.uuid5(uuid.NAMESPACE_DNS, owner)) raise UnsupportedMediaType("JSON payload is required") request_data: Dict = request.json slice_request = IETFSliceHandler.create_slice_service(request_data) slice_client = SliceClient() slice_client.CreateSlice(slice_request) response = jsonify({}) response.status_code = HTTP_CREATED return response src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_Connection_Groups.py 0 → 100644 +52 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 logging from typing import Dict from flask import request from flask.json import jsonify from flask_restful import Resource from werkzeug.exceptions import UnsupportedMediaType from context.client.ContextClient import ContextClient from ..tools.Authentication import HTTP_AUTH from ..tools.HttpStatusCodes import HTTP_CREATED from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) class NSS_Service_Connection_Groups(Resource): # @HTTP_AUTH.login_required def get(self): response = jsonify({"message": "All went well!"}) # TODO Return list of current network-slice-services return response # @HTTP_AUTH.login_required def post(self, slice_id: str): if not request.is_json: raise UnsupportedMediaType("JSON payload is required") request_data: Dict = request.json context_client = ContextClient() slice_request = IETFSliceHandler.create_connection_group( request_data, slice_id, context_client ) _ = context_client.SetSlice(slice_request) response = jsonify({}) response.status_code = HTTP_CREATED return response src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_SDPs.py 0 → 100644 +51 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 logging from typing import Dict from flask import request from flask.json import jsonify from flask_restful import Resource from werkzeug.exceptions import UnsupportedMediaType from context.client.ContextClient import ContextClient from ..tools.HttpStatusCodes import HTTP_CREATED from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) class NSS_Service_SDPs(Resource): # @HTTP_AUTH.login_required def get(self): response = jsonify({"message": "All went well!"}) # TODO Return list of current network-slice-services return response # @HTTP_AUTH.login_required def post(self, slice_id: str): if not request.is_json: raise UnsupportedMediaType("JSON payload is required") request_data: Dict = request.json context_client = ContextClient() slice_request = IETFSliceHandler.create_sdp( request_data, slice_id, context_client ) _ = context_client.SetSlice(slice_request) response = jsonify({}) response.status_code = HTTP_CREATED return response Loading
src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service.py +3 −3 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. Loading Loading @@ -26,7 +26,7 @@ from ..tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK LOGGER = logging.getLogger(__name__) class NSS_Service(Resource): @HTTP_AUTH.login_required # @HTTP_AUTH.login_required def get(self, slice_id : str): LOGGER.debug('GET Slice ID: {:s}'.format(str(slice_id))) try: Loading @@ -51,7 +51,7 @@ class NSS_Service(Resource): return response @HTTP_AUTH.login_required # @HTTP_AUTH.login_required def delete(self, slice_id : str): LOGGER.debug('DELETE Slice ID: {:s}'.format(str(slice_id))) try: Loading
src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Service_Match_Criteria.py 0 → 100644 +55 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 logging from typing import Dict from flask import request from flask.json import jsonify from flask_restful import Resource from werkzeug.exceptions import UnsupportedMediaType from context.client.ContextClient import ContextClient from slice.client.SliceClient import SliceClient from ..tools.Authentication import HTTP_AUTH from ..tools.HttpStatusCodes import ( HTTP_CREATED, ) from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) class NSS_Service_Match_Criteria(Resource): # @HTTP_AUTH.login_required def get(self): response = jsonify({"message": "All went well!"}) # TODO Return list of current network-slice-services return response # @HTTP_AUTH.login_required def post(self, slice_id: str, sdp_id: str): if not request.is_json: raise UnsupportedMediaType("JSON payload is required") request_data: Dict = request.json context_client = ContextClient() slice_request = IETFSliceHandler.create_match_criteria( request_data, slice_id, sdp_id, context_client ) slice_client = SliceClient() slice_client.UpdateSlice(slice_request) response = jsonify({}) response.status_code = HTTP_CREATED return response
src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services.py +16 −90 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. Loading @@ -11,112 +11,38 @@ # 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 json import logging import ssl import uuid from typing import Dict from flask import request from flask.json import jsonify from flask_restful import Resource from flask import request from common.Constants import DEFAULT_CONTEXT_NAME from common.proto.context_pb2 import Slice, SliceStatusEnum, EndPointId, Constraint from common.tools.grpc.Tools import grpc_message_to_json from ..tools.Authentication import HTTP_AUTH from ..tools.HttpStatusCodes import HTTP_BADREQUEST, HTTP_OK, HTTP_CREATED, HTTP_SERVERERROR from werkzeug.exceptions import UnsupportedMediaType from slice.client.SliceClient import SliceClient from .bindings import load_json_data from .bindings.network_slice_services import NetworkSliceServices from ..tools.HttpStatusCodes import HTTP_CREATED from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) class NSS_Services(Resource): @HTTP_AUTH.login_required # @HTTP_AUTH.login_required def get(self): response = jsonify({"message": "All went well!"}) # TODO Return list of current network-slice-services return response @HTTP_AUTH.login_required # @HTTP_AUTH.login_required def post(self): if not request.is_json: raise UnsupportedMediaType('JSON payload is required') request_data = json.dumps(request.json) response = jsonify({}) response.status_code = HTTP_CREATED slices: NetworkSliceServices = load_json_data(request_data, NetworkSliceServices)[0] for ietf_slice in slices.slice_service: slice_request: Slice = Slice() # Meta information # TODO implement name and owner based on "tags" slice_request.slice_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME slice_request.slice_id.slice_uuid.uuid = ietf_slice.service_id() # TODO map with admin status of IETF Slice slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED list_endpoints = [] for sdp in ietf_slice.sdps().sdp: endpoint = EndPointId() endpoint.topology_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_NAME endpoint.device_id.device_uuid.uuid = sdp.node_id() endpoint.endpoint_uuid.uuid = sdp.sdp_id() list_endpoints.append(endpoint) slice_request.slice_endpoint_ids.extend(list_endpoints) # TODO Map connectivity_groups and connectivity constructs to real connections LOGGER.debug(f"Connection groups detected: {len(ietf_slice.connection_groups().connection_group())}") list_constraints = [] for cg in ietf_slice.connection_groups().connection_group: for cc in cg.connectivity_construct: if cc.slo_sle_policy.custom: with cc.slo_sle_policy.custom as slo: for metric_bound in slo.service_slo_sle_policy().metric_bounds().metric_bound: metric_type = str(metric_bound.metric_type()).casefold() if metric_type == "service-slo-two-way-bandwidth": # TODO fix to two way! constraint = Constraint() metric_unit = metric_bound.metric_unit().casefold() capacity = float(metric_bound.bound()) # Assuming capacity already in Gbps if metric_unit == "mbps": capacity /= 1E3 elif metric_unit != "gbps": LOGGER.warning(f"Invalided metric unit ({metric_bound.metric_unit()}), must be Mbps or Gbps") response.status_code = HTTP_SERVERERROR return response constraint.sla_capacity.capacity_gbps = capacity list_constraints.append(constraint) elif metric_type == "service-slo-one-way-delay": if metric_bound.metric_unit().casefold() == "ms": latency = int(metric_bound.bound()) else: LOGGER.warning(f"Invalided metric unit ({metric_bound.metric_unit()}), must be \"ms\" ") response.status_code = HTTP_SERVERERROR return response constraint = Constraint() constraint.sla_latency.e2e_latency_ms = latency list_constraints.append(constraint) elif metric_type == "service-slo-availability": availability = float(metric_bound.bound()) if availability > 100.0 or availability < 0.0: raise Exception(f'Slice SLO availability ({availability}) must be constrained [0,100]') constraint = Constraint() constraint.sla_availability.availability = availability # TODO not really necessary, remove after OFC2023 constraint.sla_availability.num_disjoint_paths = 0 constraint.sla_availability.all_active = False list_constraints.append(constraint) slice_request.slice_constraints.extend(list_constraints) LOGGER.debug(grpc_message_to_json(slice_request)) # TODO remove # TODO adding owner, needs to be recoded after updating the bindings owner = request.json["data"]["ietf-network-slice-service:network-slice-services"]["slice-service"][0]["service-tags"][0]["value"] slice_request.slice_owner.owner_string = owner slice_request.slice_owner.owner_uuid.uuid = str(uuid.uuid5(uuid.NAMESPACE_DNS, owner)) raise UnsupportedMediaType("JSON payload is required") request_data: Dict = request.json slice_request = IETFSliceHandler.create_slice_service(request_data) slice_client = SliceClient() slice_client.CreateSlice(slice_request) response = jsonify({}) response.status_code = HTTP_CREATED return response
src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_Connection_Groups.py 0 → 100644 +52 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 logging from typing import Dict from flask import request from flask.json import jsonify from flask_restful import Resource from werkzeug.exceptions import UnsupportedMediaType from context.client.ContextClient import ContextClient from ..tools.Authentication import HTTP_AUTH from ..tools.HttpStatusCodes import HTTP_CREATED from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) class NSS_Service_Connection_Groups(Resource): # @HTTP_AUTH.login_required def get(self): response = jsonify({"message": "All went well!"}) # TODO Return list of current network-slice-services return response # @HTTP_AUTH.login_required def post(self, slice_id: str): if not request.is_json: raise UnsupportedMediaType("JSON payload is required") request_data: Dict = request.json context_client = ContextClient() slice_request = IETFSliceHandler.create_connection_group( request_data, slice_id, context_client ) _ = context_client.SetSlice(slice_request) response = jsonify({}) response.status_code = HTTP_CREATED return response
src/nbi/service/rest_server/nbi_plugins/ietf_network_slice/NSS_Services_SDPs.py 0 → 100644 +51 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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 logging from typing import Dict from flask import request from flask.json import jsonify from flask_restful import Resource from werkzeug.exceptions import UnsupportedMediaType from context.client.ContextClient import ContextClient from ..tools.HttpStatusCodes import HTTP_CREATED from .ietf_slice_handler import IETFSliceHandler LOGGER = logging.getLogger(__name__) class NSS_Service_SDPs(Resource): # @HTTP_AUTH.login_required def get(self): response = jsonify({"message": "All went well!"}) # TODO Return list of current network-slice-services return response # @HTTP_AUTH.login_required def post(self, slice_id: str): if not request.is_json: raise UnsupportedMediaType("JSON payload is required") request_data: Dict = request.json context_client = ContextClient() slice_request = IETFSliceHandler.create_sdp( request_data, slice_id, context_client ) _ = context_client.SetSlice(slice_request) response = jsonify({}) response.status_code = HTTP_CREATED return response