Commit ce70c158 authored by Konstantinos Togias's avatar Konstantinos Togias
Browse files

Fix QoD gaps T5, T7, T8 and T11 from the end-to-end review

- Open5GCore looks up the QoS profile in the case it validated it in (T5).
- QoD sends the device IPv6 address itself as ueIpv6Addr (T11).
- Open5GS and Open5GCore reject an unsupported profile with an error the
  adapter reports as ValidationError (T11).
- QoD uses the control path's notification_url as notificationDestination (T7).
- A QoD device without an IPv4 or IPv6 address is rejected with
  ValidationError before the NEF is called (T8).

Original-Author: Igli <kociaj4@gmail.com>
Reviewed-Commit: 6a1ae35edff047c58599a735b19996551eb8ecd3
parent c366fcb7
Loading
Loading
Loading
Loading
+10 −6
Changes for src/openop_tfsdk/contract/adapters/network.py: 10 added lines, 6 removed lines.
Original line number Diff line number Diff line
@@ -40,7 +40,7 @@ from openop_tfsdk.network.adapters.open5gcore.client import (
)
from openop_tfsdk.network.adapters.open5gs.client import NetworkManager as Open5gsClient
from openop_tfsdk.network.core import schemas
from openop_tfsdk.network.core.common import CoreHttpError
from openop_tfsdk.network.core.common import CoreHttpError, CoreValidationError

log = structlog.get_logger(__name__)

@@ -108,11 +108,13 @@ def _address(network) -> str:
    return str(network)


def _qod_session(request: CapabilityLifecycleRequest) -> dict:
def _qod_session(request: CapabilityLifecycleRequest, notification_url: Optional[str]) -> dict:
    target = request.parameters.target
    parameters = request.parameters.parameters
    if target.device is None or target.application_server is None:
        raise ValidationError("qod_session needs a target device and application server")
    if not (target.device.ipv4 or target.device.ipv6):
        raise ValidationError("qod_session needs a device IPv4 or IPv6 address")

    session = {
        "device": _device(target.device),
@@ -130,8 +132,10 @@ def _qod_session(request: CapabilityLifecycleRequest) -> dict:
        session["devicePorts"] = {"ports": [filters.source.port]}
    if filters and filters.destination and filters.destination.port:
        session["applicationServerPorts"] = {"ports": [filters.destination.port]}
    if parameters.extra.get("sink"):
        session["sink"] = parameters.extra["sink"]
    # Without a sink the client sends notificationDestination "None".
    sink = notification_url or parameters.extra.get("sink")
    if sink:
        session["sink"] = sink
    return session


@@ -214,7 +218,7 @@ class NetworkCapabilityAdapter:
                state=LifecycleState.ACTIVE,
                external_id=_resource_id(resource.get("trafficInfluenceID")),
            )
        session = self._call("create_qod_session", _qod_session(request))
        session = self._call("create_qod_session", _qod_session(request, self._notification_url))
        return CapabilityLifecycleResult(
            operation_id=request.operation_id,
            capability_kind=request.capability_kind,
@@ -321,7 +325,7 @@ class NetworkCapabilityAdapter:
    def _call(self, method: str, *args):
        try:
            return getattr(self._client, method)(*args)
        except (pydantic.ValidationError, OaiValidationError) as error:
        except (pydantic.ValidationError, OaiValidationError, CoreValidationError) as error:
            log.error(
                "backend_request_invalid",
                method=method,
+3 −4
Changes for src/openop_tfsdk/network/adapters/open5gcore/client.py: 3 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -6,13 +6,12 @@
# Contributors:
#   - Manar Zaboub (manar.zaboub@fokus.fraunhofer.de)
##
from pydantic import ValidationError

from openop_tfsdk import logger
from openop_tfsdk.network.core.base_network_client import (
    BaseNetworkClient,
    build_flows,
)
from openop_tfsdk.network.core.common import CoreValidationError

from ...core import schemas

@@ -48,7 +47,7 @@ class NetworkManager(BaseNetworkClient):

        if qos_key not in qos_support_map:
            supported = ", ".join(qos_support_map.keys())
            raise ValidationError(
            raise CoreValidationError(
                f"Unsupported QoS profile '{session_info.qosProfile.root}'. "
                f"Supported profiles for Open5GCore are: {supported}"
            )
@@ -58,6 +57,6 @@ class NetworkManager(BaseNetworkClient):
        session_info: schemas.CreateSession,
        subscription: schemas.AsSessionWithQoSSubscription,
    ) -> None:
        flow_id = qos_support_map[session_info.qosProfile.root]
        flow_id = qos_support_map[session_info.qosProfile.root.strip().lower()]
        subscription.flowInfo = build_flows(flow_id, session_info)
        subscription.ueIpv4Addr = "192.168.6.1"  # ToDo
+3 −4
Changes for src/openop_tfsdk/network/adapters/open5gs/client.py: 3 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -7,13 +7,12 @@
#   - Ferran Cañellas (ferran.canellas@i2cat.net)
#   - Panagiotis Pavlidis (p.pavlidis@iit.demokritos.gr)
##
from pydantic import ValidationError

from openop_tfsdk import logger
from openop_tfsdk.network.core.base_network_client import (
    BaseNetworkClient,
    build_flows,
)
from openop_tfsdk.network.core.common import CoreValidationError

from ...core import schemas

@@ -47,7 +46,7 @@ class NetworkManager(BaseNetworkClient):

    def core_specific_qod_validation(self, session_info: schemas.CreateSession):
        if session_info.qosProfile.root not in flow_id_mapping.keys():
            raise ValidationError(
            raise CoreValidationError(
                f"Open5Gs only supports these qos-profiles: {', '.join(flow_id_mapping.keys())}"
            )

@@ -65,7 +64,7 @@ class NetworkManager(BaseNetworkClient):
    ) -> None:
        """Check core specific elements that required for location retrieval in NEF."""
        if retrieve_location_request.device is None:
            raise ValidationError(
            raise CoreValidationError(
                "Open5GS requires a device to be specified for location retrieval in NEF."
            )

+4 −1
Changes for src/openop_tfsdk/network/core/base_network_client.py: 4 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -168,13 +168,16 @@ class BaseNetworkClient:
        device_ipv4 = None
        if valid_session_info.device.ipv4Address:
            device_ipv4 = valid_session_info.device.ipv4Address.root.publicAddress.root
        device_ipv6 = None
        if valid_session_info.device.ipv6Address:
            device_ipv6 = valid_session_info.device.ipv6Address.root

        self.core_specific_qod_validation(valid_session_info)
        subscription = schemas.AsSessionWithQoSSubscription(
            notificationDestination=str(valid_session_info.sink),
            qosReference=valid_session_info.qosProfile.root,
            ueIpv4Addr=device_ipv4,
            ueIpv6Addr=valid_session_info.device.ipv6Address,
            ueIpv6Addr=device_ipv6,
            usageThreshold=schemas.UsageThreshold(duration=valid_session_info.duration),
        )
        self.add_core_specific_qod_parameters(valid_session_info, subscription)
+4 −0
Changes for src/openop_tfsdk/network/core/common.py: 4 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -61,6 +61,10 @@ class CoreHttpError(Exception):
    pass


class CoreValidationError(Exception):
    pass


# Monitoring Event Methods
def monitoring_event_post(base_url: str, scs_as_id: str, model_payload: BaseModel) -> dict:
    data = model_payload.model_dump_json(exclude_none=True, by_alias=True)