Commit babbd442 authored by Giulio Carota's avatar Giulio Carota
Browse files

refactor using github hooks

parent 0e1dff54
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -64,8 +64,8 @@ PyYAML==6.0.2
pyzmq==26.4.0
referencing==0.36.2
requests==2.32.3
shortuuid==1.0.13
rpds-py==0.24.0
shortuuid==1.0.13
six==1.17.0
soupsieve==2.6
stack-data==0.6.3
+87 −46
Original line number Diff line number Diff line
@@ -9,27 +9,36 @@
##

from typing import Dict
from src import logger

import shortuuid
import time
from pydantic import ValidationError
from src.network.core.network_interface import NetworkManagementInterface
from src.network.clients.oai.schemas import CamaraQoDSessionInfo, OaiAsSessionWithQosSubscription,CamaraTrafficInfluence, TrafficInfluSub

from src import logger
from src.network.clients.oai.common import (
    oai_as_session_with_qos_post,
    oai_as_session_with_qos_get,
    OaiHttpError,
    OaiNetworkError,
    oai_as_session_with_qos_delete,
    oai_traffic_influence_post,
    oai_as_session_with_qos_get,
    oai_as_session_with_qos_post,
    oai_traffic_influence_delete,
    oai_traffic_influence_post,
    oai_traffic_influence_put,
    OaiHttpError,
    OaiNetworkError
)

from src.network.clients.oai.utils import camara_qod_to_as_session_with_qos, as_session_with_qos_to_camara_qod, camara_ti_to_3gpp_ti
from src.network.clients.oai.schemas import (
    CamaraQoDSessionInfo,
    CamaraTrafficInfluence,
    OaiAsSessionWithQosSubscription,
)
from src.network.clients.oai.utils import (
    as_session_with_qos_to_camara_qod,
    camara_qod_to_as_session_with_qos,
    camara_ti_to_3gpp_ti,
)
from src.network.core.network_interface import NetworkManagementInterface

log = logger.get_logger(__name__)


class OaiNefClient(NetworkManagementInterface):
    def __init__(self, base_url: str, scs_as_id: str = None):
        """
@@ -42,7 +51,9 @@ class OaiNefClient(NetworkManagementInterface):
            super().__init__()
            self.base_url = base_url
            self.scs_as_id = shortuuid.uuid()
            log.info(f"Initialized OaiNefClient with base_url: {self.base_url} and scs_as_id: {self.scs_as_id}")
            log.info(
                f"Initialized OaiNefClient with base_url: {self.base_url} and scs_as_id: {self.scs_as_id}"
            )
        except Exception as e:
            log.error(f"Failed to initialize OaiNefClient: {e}")
            raise e
@@ -59,14 +70,18 @@ class OaiNefClient(NetworkManagementInterface):

            # convert CAMARA QoD to NEF AsSessionWithQos model and do POST
            nef_req = camara_qod_to_as_session_with_qos(qod_input)
            nef_res = oai_as_session_with_qos_post(self.base_url, self.scs_as_id, nef_req)
            nef_res = oai_as_session_with_qos_post(
                self.base_url, self.scs_as_id, nef_req
            )

            # retrieve the NEF resource id
            if "self" in nef_res.keys():
                nef_url = nef_res["self"]
                nef_id = nef_url.split("subscriptions/")[1]
            else:
                raise OaiNetworkError("No valid ID for the created resource was returned")
                raise OaiNetworkError(
                    "No valid ID for the created resource was returned"
                )

            # create QoD session detail and return info with resource Id
            qod_input.sessionId = nef_id
@@ -80,10 +95,11 @@ class OaiNefClient(NetworkManagementInterface):
        except KeyError as e:
            raise OaiNetworkError(f"Missing field in QoD Session Info data: {e}") from e
        except OaiHttpError as e:
            raise OaiNetworkError(f"The network could not enable the QoD Session. It returned {e}") from e
            raise OaiNetworkError(
                f"The network could not enable the QoD Session. It returned {e}"
            ) from e
        except OaiNetworkError as e:
            raise

            raise e

    def get_qod_session(self, session_id: str) -> Dict:
        """
@@ -92,7 +108,9 @@ class OaiNefClient(NetworkManagementInterface):
        OAI NEF GET /3gpp-as-session-with-qos/v1/{scs_as_id}/subscriptions/{subscriptionId}
        """
        try:
            res = oai_as_session_with_qos_get(self.base_url, self.scs_as_id, session_id=session_id)
            res = oai_as_session_with_qos_get(
                self.base_url, self.scs_as_id, session_id=session_id
            )
            nef_res = OaiAsSessionWithQosSubscription(**res)
            qod_info = as_session_with_qos_to_camara_qod(nef_res)

@@ -102,9 +120,11 @@ class OaiNefClient(NetworkManagementInterface):
        except ValidationError as e:
            raise OaiNetworkError("Could not validate network response data") from e
        except OaiHttpError as e:
            raise OaiNetworkError(f"The network could not enable the QoD Session. It returned {e}") from e
            raise OaiNetworkError(
                f"The network could not enable the QoD Session. It returned {e}"
            ) from e
        except OaiNetworkError as e:
            raise
            raise e

    def delete_qod_session(self, session_id: str) -> None:
        """
@@ -113,14 +133,18 @@ class OaiNefClient(NetworkManagementInterface):
        OAI NEF DELETE /3gpp-as-session-with-qos/v1/{scs_as_id}/subscriptions/{subscriptionId}
        """
        try:
            oai_as_session_with_qos_delete(self.base_url, self.scs_as_id, session_id=session_id)
            oai_as_session_with_qos_delete(
                self.base_url, self.scs_as_id, session_id=session_id
            )

            log.info(f"QoD session deleted successfully [id={session_id}]")

        except OaiHttpError as e:
            raise OaiNetworkError(f"The network could not enable the QoD Session. It returned {e}") from e
            raise OaiNetworkError(
                f"The network could not enable the QoD Session. It returned {e}"
            ) from e
        except OaiNetworkError as e:
            raise
            raise e

    # implementation of the NetworkManagementInterface Traffic Influence Methods
    def create_traffic_influence_resource(self, traffic_influence_info):
@@ -136,7 +160,9 @@ class OaiNefClient(NetworkManagementInterface):
                nef_url = nef_res["self"]
                nef_id = nef_url.split("subscriptions/")[1]
            else:
                raise OaiNetworkError("No valid ID for the created resource was returned")
                raise OaiNetworkError(
                    "No valid ID for the created resource was returned"
                )

            # create TI session detail and return info with resource Id
            ti_input.trafficInfluenceID = nef_id
@@ -148,12 +174,15 @@ class OaiNefClient(NetworkManagementInterface):
        except ValidationError as e:
            raise OaiNetworkError("Could not validate Traffic Influence data") from e
        except KeyError as e:
            raise OaiNetworkError(f"Missing field in Traffic Influence data: {e}") from e
            raise OaiNetworkError(
                f"Missing field in Traffic Influence data: {e}"
            ) from e
        except OaiHttpError as e:
            raise OaiNetworkError(f"The network could not enable the Traffic Influence Session. It returned {e}") from e
            raise OaiNetworkError(
                f"The network could not enable the Traffic Influence Session. It returned {e}"
            ) from e
        except OaiNetworkError as e:
            raise

            raise e

    def delete_traffic_influence_resource(self, session_id):
        """
@@ -162,14 +191,18 @@ class OaiNefClient(NetworkManagementInterface):
        OAI NEF DELETE /3gpp-traffic-influence/v1/{scs_as_id}/subscriptions/{subscriptionId}
        """
        try:
            oai_traffic_influence_delete(self.base_url, self.scs_as_id, session_id=session_id)
            oai_traffic_influence_delete(
                self.base_url, self.scs_as_id, session_id=session_id
            )

            log.info(f"TI session deleted successfully [id={session_id}]")

        except OaiHttpError as e:
            raise OaiNetworkError(f"The network could not delete the TI session. It returned {e}") from e
            raise OaiNetworkError(
                f"The network could not delete the TI session. It returned {e}"
            ) from e
        except OaiNetworkError as e:
            raise
            raise e

    def put_traffic_influence_resource(self, resource_id, traffic_influence_info):
        try:
@@ -177,17 +210,25 @@ class OaiNefClient(NetworkManagementInterface):

            # convert CAMARA TI to NEF TrafficInflSub model and do POST
            nef_req = camara_ti_to_3gpp_ti(qod_input)
            updated_res = oai_traffic_influence_put(self.base_url, self.scs_as_id, resource_id, nef_req)
            updated_res = oai_traffic_influence_put(
                self.base_url, self.scs_as_id, resource_id, nef_req
            )

            log.info(f"Traffic Influence resource updated successfully [id={resource_id}]")
            log.info(
                f"Traffic Influence resource updated successfully [id={resource_id}]"
            )

            return qod_input
            return updated_res

        except ValidationError as e:
            raise OaiNetworkError("Could not validate Traffic Influence data") from e
        except KeyError as e:
            raise OaiNetworkError(f"Missing field in Traffic Influence data: {e}") from e
            raise OaiNetworkError(
                f"Missing field in Traffic Influence data: {e}"
            ) from e
        except OaiHttpError as e:
            raise OaiNetworkError(f"The network could not update the Traffic Influence Session. It returned {e}") from e
            raise OaiNetworkError(
                f"The network could not update the Traffic Influence Session. It returned {e}"
            ) from e
        except OaiNetworkError as e:
            raise
            raise e
+28 −13
Original line number Diff line number Diff line
@@ -9,21 +9,21 @@
##


import requests
from pydantic import BaseModel

from src.network.clients.errors import NetworkPlatformError

import json
import requests

def _make_request(method: str, url: str, data=None):
    try:
        headers = None
        if method == 'POST' or method == 'PUT':
        if method == "POST" or method == "PUT":
            headers = {
                "Content-Type": "application/json",
                "accept": "application/json",
            }
        elif method == 'GET':
        elif method == "GET":
            headers = {
                "accept": "application/json",
            }
@@ -38,7 +38,9 @@ def _make_request(method: str, url: str, data=None):


# QoD methods
def oai_as_session_with_qos_post(base_url: str, scs_as_id: str, model_payload: BaseModel) -> dict:
def oai_as_session_with_qos_post(
    base_url: str, scs_as_id: str, model_payload: BaseModel
) -> dict:
    data = model_payload.model_dump_json(exclude_none=True)
    url = oai_as_session_with_qos_build_url(base_url, scs_as_id)
    return _make_request("POST", url, data=data)
@@ -53,39 +55,52 @@ def oai_as_session_with_qos_delete(base_url: str, scs_as_id: str, session_id: st
    url = oai_as_session_with_qos_build_url(base_url, scs_as_id, session_id)
    return _make_request("DELETE", url)

def oai_as_session_with_qos_build_url(base_url: str, scs_as_id: str, session_id: str = None):

def oai_as_session_with_qos_build_url(
    base_url: str, scs_as_id: str, session_id: str = None
):
    url = f"{base_url}/3gpp-as-session-with-qos/v1/{scs_as_id}/subscriptions"
    if session_id != None and len(session_id) > 0:
    if session_id is not None and len(session_id) > 0:
        return f"{url}/{session_id}"
    else:
        return url


## Traffic Influence methods
def oai_traffic_influence_post(base_url: str, scs_as_id: str, model_payload: BaseModel) -> dict:
def oai_traffic_influence_post(
    base_url: str, scs_as_id: str, model_payload: BaseModel
) -> dict:
    data = model_payload.model_dump_json(exclude_none=True)
    url = oai_traffic_influence_build_url(base_url, scs_as_id)
    return _make_request("POST", url, data=data)


def oai_traffic_influence_delete(base_url: str, scs_as_id: str, session_id: str):
    url = oai_traffic_influence_build_url(base_url, scs_as_id, session_id)
    return _make_request("DELETE", url)

def oai_traffic_influence_put(base_url: str, scs_as_id: str, session_id: str, model_payload: BaseModel) -> dict:

def oai_traffic_influence_put(
    base_url: str, scs_as_id: str, session_id: str, model_payload: BaseModel
) -> dict:
    data = model_payload.model_dump_json(exclude_none=True)
    url = oai_traffic_influence_build_url(base_url, scs_as_id, session_id)
    return _make_request("PUT", url, data=data)


def oai_traffic_influence_build_url(base_url: str, scs_as_id: str, session_id: str = None):
def oai_traffic_influence_build_url(
    base_url: str, scs_as_id: str, session_id: str = None
):
    url = f"{base_url}/3gpp-traffic-influence/v1/{scs_as_id}/subscriptions"
    if session_id != None and len(session_id) > 0:
    if session_id is not None and len(session_id) > 0:
        return f"{url}/{session_id}"
    else:
        return url


class OaiHttpError(Exception):
    pass


class OaiNetworkError(NetworkPlatformError):
    pass
+34 −19
Original line number Diff line number Diff line
@@ -8,9 +8,10 @@
#   - Giulio Carota (giulio.carota@eurecom.fr)
##

from pydantic import BaseModel, Field, AnyHttpUrl
from typing import List, Optional

from pydantic import BaseModel, Field


class Snssai(BaseModel):
    sst: int = Field(default=1)
@@ -21,10 +22,12 @@ class TrafficFilter(BaseModel):
    flowId: int
    flowDescriptions: List[str]


class OaiAsSessionWithQosSubscription(BaseModel):
    """
    Represents the model to create an AsSessionWithQoS resource inside the OAI NEF.
    """

    supportedFeatures: str = Field(default="12")
    dnn: str = Field(default="oai")
    snssai: Snssai
@@ -37,14 +40,16 @@ class OaiAsSessionWithQosSubscription(BaseModel):

    def add_flow_descriptor(self, flow_desriptor: str):
        self.flowInfo = list()
        self.flowInfo.append(TrafficFilter(
            flowId=len(self.flowInfo)+1,
            flowDescriptions=[flow_desriptor]
        ))
        self.flowInfo.append(
            TrafficFilter(
                flowId=len(self.flowInfo) + 1, flowDescriptions=[flow_desriptor]
            )
        )

    def add_snssai(self, sst: int, sd: str = None):
        self.snssai = Snssai(sst=sst, sd=sd)


class PortRange(BaseModel):
    from_: int = Field(alias="from")
    to: int
@@ -52,31 +57,38 @@ class PortRange(BaseModel):
    class Config:
        populate_by_name = True


class Ports(BaseModel):
    ranges: Optional[List[PortRange]] = None
    ports: Optional[List[int]] = None


class Ipv4Address(BaseModel):
    publicAddress: str
    publicPort: Optional[int] = None


class Device(BaseModel):
    phoneNumber: Optional[str] = None
    networkAccessIdentifier: Optional[str] = None
    ipv4Address: Optional[Ipv4Address] = None
    ipv6Address: Optional[str] = None


class ApplicationServer(BaseModel):
    ipv4Address: Optional[str] = None
    ipv6Address: Optional[str] = None


class SinkCredential(BaseModel):
    credentialType: Optional[str] = None


class CamaraQoDSessionInfo(BaseModel):
    """
    Represents the input data for creating a QoD session.
    """

    duration: int
    qosProfile: str
    applicationServer: ApplicationServer
@@ -94,7 +106,6 @@ class CamaraQoDSessionInfo(BaseModel):
    qosStatus: Optional[str] = None
    statusInfo: Optional[str] = None


    class Config:
        populate_by_name = True

@@ -113,7 +124,6 @@ class CamaraQoDSessionInfo(BaseModel):
    def add_server_ipv4(self, ipv4: str):
        self.applicationServer = ApplicationServer(ipv4Address=ipv4)


    def add_ue_ipv4(self, ipv4: str):
        if self.device is None:
            self.device = Device()
@@ -123,20 +133,25 @@ class CamaraQoDSessionInfo(BaseModel):

## traffic_influence schemas


class SourceTrafficFilters(BaseModel):
    sourcePort: int


class DestinationTrafficFilters(BaseModel):
    destinationPort: int
    destinationProtocol: str


class TrafficRoute(BaseModel):
    dnai: str


class NotificationSink(BaseModel):
    sink: Optional[str] = None
    sinkCredential: Optional[SinkCredential] = None


class TrafficInfluSub(BaseModel):  # Replace with a meaningful name
    afServiceId: str
    afAppId: str
@@ -150,20 +165,20 @@ class TrafficInfluSub(BaseModel): # Replace with a meaningful name

    def add_flow_descriptor(self, flow_desriptor: str):
        self.trafficFilters = list()
        self.trafficFilters.append(TrafficFilter(
            flowId=len(self.trafficFilters)+1,
            flowDescriptions=[flow_desriptor]
        ))
        self.trafficFilters.append(
            TrafficFilter(
                flowId=len(self.trafficFilters) + 1, flowDescriptions=[flow_desriptor]
            )
        )

    def add_traffic_route(self, dnai: str):
        self.trafficRoutes = list()
        self.trafficRoutes.append(TrafficRoute(
            dnai=dnai
        ))
        self.trafficRoutes.append(TrafficRoute(dnai=dnai))

    def add_snssai(self, sst: int, sd: str = None):
        self.snssai = Snssai(sst=sst, sd=sd)


class CamaraTrafficInfluence(BaseModel):
    trafficInfluenceID: Optional[str] = None
    apiConsumerId: str
+24 −13
Original line number Diff line number Diff line
@@ -9,10 +9,17 @@
##


from src.network.clients.oai.schemas import CamaraQoDSessionInfo, OaiAsSessionWithQosSubscription, CamaraTrafficInfluence, TrafficInfluSub
from pydantic import BaseModel
from src.network.clients.oai.schemas import (
    CamaraQoDSessionInfo,
    CamaraTrafficInfluence,
    OaiAsSessionWithQosSubscription,
    TrafficInfluSub,
)

def camara_qod_to_as_session_with_qos(qod_input: CamaraQoDSessionInfo) -> OaiAsSessionWithQosSubscription :

def camara_qod_to_as_session_with_qos(
    qod_input: CamaraQoDSessionInfo,
) -> OaiAsSessionWithQosSubscription:
    device_ip = qod_input.retrieve_ue_ipv4()
    server_ip = qod_input.retrieve_app_ipv4()

@@ -37,7 +44,9 @@ def camara_qod_to_as_session_with_qos(qod_input: CamaraQoDSessionInfo) -> OaiAsS
    return nef_req


def as_session_with_qos_to_camara_qod(nef_input: OaiAsSessionWithQosSubscription) -> CamaraQoDSessionInfo :
def as_session_with_qos_to_camara_qod(
    nef_input: OaiAsSessionWithQosSubscription,
) -> CamaraQoDSessionInfo:
    # create the camara qod model

    qod_info = CamaraQoDSessionInfo.construct()
@@ -57,7 +66,9 @@ def as_session_with_qos_to_camara_qod(nef_input: OaiAsSessionWithQosSubscription
def camara_ti_to_3gpp_ti(ti_input: CamaraTrafficInfluence) -> TrafficInfluSub:

    device_ip = ti_input.retrieve_ue_ipv4()
    server_ip = ti_input.appInstanceId #assume that the instance id corresponds to its IPv4 address
    server_ip = (
        ti_input.appInstanceId
    )  # assume that the instance id corresponds to its IPv4 address
    sink_url = ti_input.notificationSink.sink
    edge_zone = ti_input.edgeCloudZoneId

Loading