Commit ee2963cf authored by Shayan Hajipour's avatar Shayan Hajipour
Browse files

feat: ietf-slice operations draft added to NBI to support the CAMARA PoC

parent c31a36bf
Loading
Loading
Loading
Loading
+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.
@@ -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:
@@ -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:
+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
+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.
@@ -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
+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
+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