From a2f9f6d03c3fe7c4cd100e4da9b91e4b6710eaf3 Mon Sep 17 00:00:00 2001 From: "sunil.n" Date: Fri, 24 Oct 2025 11:58:52 +0530 Subject: [PATCH] Add SME Client to 5G Slice PRB Prediction Rapp Change-Id: I2ffd236189351d3a536b242852ea2364e602ef79 Signed-off-by: sunil.n Change-Id: Iabd2d60f902b9a00cf99e586db789bff0692d1e8 Signed-off-by: sunil.n Update README.md - sme_client.py information Change-Id: I72747badaf185b7d2fe3bd57b1d8211ad795cc32 Signed-off-by: sunil.n --- .../rapp-slice-prb-prediction/README.md | 109 ++++++++++++++ .../rapp-slice-prb-prediction/src/config.json | 27 ++++ .../rapp-slice-prb-prediction/src/models/README.md | 1 + .../rapp-slice-prb-prediction/src/sme_client.py | 164 +++++++++++++++++++++ 4 files changed, 301 insertions(+) create mode 100644 sample-rapp-generator/rapp-slice-prb-prediction/src/config.json create mode 100644 sample-rapp-generator/rapp-slice-prb-prediction/src/models/README.md create mode 100644 sample-rapp-generator/rapp-slice-prb-prediction/src/sme_client.py diff --git a/sample-rapp-generator/rapp-slice-prb-prediction/README.md b/sample-rapp-generator/rapp-slice-prb-prediction/README.md index b1b3a7b..622ddf2 100644 --- a/sample-rapp-generator/rapp-slice-prb-prediction/README.md +++ b/sample-rapp-generator/rapp-slice-prb-prediction/README.md @@ -5,6 +5,9 @@ Using 5G RAN Slice PRB Prediction Rapp, we can properly manage available RAN res ## Directory Structure - `src` - contains source code for the Rapp + - `config.json` - configuration file for database, RAPP, and SME settings + - `sme_client.py` - Service Management Environment client for service discovery + - `models/` - directory for trained AI/ML models and scalers - `data_generator.py` - generates data for training and testing the model - `Dockerfile` - contains instructions to build a Docker image for the Rapp @@ -78,3 +81,109 @@ Each slice has unique identifiers and generates KPIs based on its specific traff 4. **Database Storage**: Writes data to InfluxDB using batch processing for efficiency This generated data provides a comprehensive dataset for training the PRB prediction model with realistic network slice behavior patterns. + +## Configuration (`src/config.json`) + +The `config.json` file contains all necessary configuration parameters for the RAPP operation, organized into three main sections: + +### Database Configuration (DB) +```json +{ + "DB": { + "address": "http://localhost:8086", // InfluxDB server URL + "token": "", // InfluxDB authentication token + "org": "", // InfluxDB organization name + "bucket": "nssi_pm_bucket", // Database bucket name + "time_range": "-0", // Time range for queries + "measurements": "nssi_pm_bucket", // Measurement name + "window_size": 672, // Data window size for model + "field_names": [...], // KPI field names to monitor + "tag_slice_type": "sliceType", // Tag for slice type filtering + "tag_nssi_id": "measObjLdn" // Tag for NSSI identification + } +} +``` + +### RAPP Configuration +```json +{ + "RAPP": { + "interval": "1", // Processing interval + "ran_nssmf_address": "http://localhost:8080", // RAN NSSMF endpoint + "callback_uri": "http://localhost:8080/handleFileReadyNotification" + } +} +``` + +### Service Management Environment (SME) +```json +{ + "SME": { + "sme_discovery_endpoint": "http://localhost:31575/service-apis/v1/allServiceAPIs", + "invoker_id": "6a965002-ed7c-4f69-855c-ab9196f86e61", + "influxdb_api_name": "influxdb2-http", + "influxdb_resource_name": "root", + "ran_nssmf_api_name": "", + "ran_nssmf_resource_name": "" + } +} +``` + +## Service Discovery (`src/sme_client.py`) + +The `sme_client.py` module implements a Service Management Environment (SME) client that enables dynamic service discovery in the O-RAN ecosystem. This component allows the RAPP to locate and connect to available services without hardcoding endpoint information. + +### Key Features + +- **Dynamic Service Discovery**: Automatically discovers service endpoints by querying the SME +- **O-RAN Compliance**: Follows O-RAN Service-Based Architecture (SBA) principles +- **Flexible Resource Access**: Supports discovery of specific API resources within services +- **Error Handling**: Robust error handling for network and parsing issues + +### SMEClient Class + +The main class provides the following functionality: + +#### Initialization +```python +client = SMEClient(invoker_id, api_name, resource_name) +``` + +**Parameters:** +- `invoker_id`: Unique identifier for the service consumer +- `api_name`: Name of the API service to discover +- `resource_name`: Specific resource within the API to access + +#### Service Discovery Process + +1. **Query Construction**: Builds discovery query with API invoker ID and API name +2. **SME Request**: Sends HTTP GET request to SME discovery endpoint +3. **Response Parsing**: Extracts service information from nested JSON response +4. **URL Construction**: Builds complete HTTP URL for service access + +#### Usage Example +```python +# Initialize SME client for InfluxDB service +sme_client = SMEClient( + invoker_id="6a965002-ed7c-4f69-855c-ab9196f86e61", + api_name="influxdb2-http", + resource_name="root" +) + +# Discover service endpoint +service_url = sme_client.discover_service() +if service_url: + print(f"Discovered service at: {service_url}") + # Use the service URL for API calls +else: + print("Service discovery failed") +``` + +### SME Response Structure + +The SME returns a nested JSON structure containing: +- `serviceAPIDescriptions`: Array of available services +- `aefProfiles`: Application Exposure Function profiles +- `versions`: API version information +- `resources`: Available resources within each version +- `interfaceDescriptions`: Network endpoint details (IP, port) \ No newline at end of file diff --git a/sample-rapp-generator/rapp-slice-prb-prediction/src/config.json b/sample-rapp-generator/rapp-slice-prb-prediction/src/config.json new file mode 100644 index 0000000..9f467a0 --- /dev/null +++ b/sample-rapp-generator/rapp-slice-prb-prediction/src/config.json @@ -0,0 +1,27 @@ +{ + "DB": { + "address": "http://localhost:8086", + "token": "", + "org": "", + "bucket": "nssi_pm_bucket", + "time_range": "-0", + "measurements": "nssi_pm_bucket", + "window_size" : 672, + "field_names": ["RRU.PrbDl.SNSSAI", "DRB.PdcpSduVolumeDL.SNSSAI", "RRC.ConnEstabSucc.Cause"], + "tag_slice_type": "sliceType", + "tag_nssi_id": "measObjLdn" + }, + "RAPP": { + "interval": "1", + "ran_nssmf_address": "http://localhost:8080", + "callback_uri": "http://localhost:8080/handleFileReadyNotification" + }, + "SME": { + "sme_discovery_endpoint": "http://localhost:31575/service-apis/v1/allServiceAPIs", + "invoker_id": "6a965002-ed7c-4f69-855c-ab9196f86e61", + "influxdb_api_name": "influxdb2-http", + "influxdb_resource_name": "root", + "ran_nssmf_api_name": "", + "ran_nssmf_resource_name": "" + } +} \ No newline at end of file diff --git a/sample-rapp-generator/rapp-slice-prb-prediction/src/models/README.md b/sample-rapp-generator/rapp-slice-prb-prediction/src/models/README.md new file mode 100644 index 0000000..a83d717 --- /dev/null +++ b/sample-rapp-generator/rapp-slice-prb-prediction/src/models/README.md @@ -0,0 +1 @@ +models folder contains trained AI/ML models and scalers. \ No newline at end of file diff --git a/sample-rapp-generator/rapp-slice-prb-prediction/src/sme_client.py b/sample-rapp-generator/rapp-slice-prb-prediction/src/sme_client.py new file mode 100644 index 0000000..f662563 --- /dev/null +++ b/sample-rapp-generator/rapp-slice-prb-prediction/src/sme_client.py @@ -0,0 +1,164 @@ +""" +Service Management Environment (SME) Client + +This module implements a client for discovering services in an O-RAN Service Management Environment. +The SME provides service discovery capabilities that allow RAPP applications to dynamically locate +and connect to available services without hardcoding endpoint information. +""" + +import requests +import logging +import json + +# Configure logger for this module +logger = logging.getLogger(__name__) + +class SMEClient: + """ + Service Management Environment Client for O-RAN service discovery. + + This client enables RAPP applications to dynamically discover and connect to services + in an O-RAN ecosystem by querying the SME (Service Management Environment) for + service endpoint information. + + Attributes: + invoker_id (str): Identifier for the API invoker/service consumer + api_name (str): Name of the API to discover + resource_name (str): Specific resource name within the API + sme_discovery_endpoint (str): URL endpoint for SME service discovery + """ + + def __init__(self, invoker_id, api_name, resource_name): + """ + Initialize the SME Client with service discovery parameters. + + Args: + invoker_id (str): Unique identifier for the service consumer + api_name (str): Name of the API service to discover + resource_name (str): Specific resource within the API to access + + Raises: + FileNotFoundError: If config.json file is not found + json.JSONDecodeError: If config.json contains invalid JSON + KeyError: If required SME configuration is missing + """ + # Store service identification parameters + self.invoker_id = invoker_id + self.api_name = api_name + self.resource_name = resource_name + + # Load SME configuration from config.json file + with open('config.json', 'r') as f: + config = json.load(f) + + # Extract SME discovery endpoint from configuration + sme_config = config.get("SME", {}) + self.sme_discovery_endpoint = sme_config.get("sme_discovery_endpoint") + + def discover_service(self): + """ + Discover service endpoint by querying the SME discovery service. + + This method constructs a query with the API invoker ID and API name, + sends it to the SME discovery endpoint, and parses the response + to extract the service URI. + + Returns: + str: Complete HTTP URL for the discovered service, or None if discovery fails + + Raises: + requests.RequestException: If network request fails (handled internally) + """ + # Construct query parameters for service discovery + query = f"api-invoker-id=api_invoker_id_{self.invoker_id}&api-name={self.api_name}" + full_url = f"{self.sme_discovery_endpoint}?{query}" + logger.info(f"Full URL for service discovery: {full_url}") + + try: + # Make HTTP GET request to SME discovery endpoint + response = requests.get(full_url, headers={"Content-Type": "application/json"}) + + if response.status_code == 200: + logger.info("Service discovery successful.") + # Parse the JSON response to extract service URI + return self.parse_uri(response.json()) + else: + # Log error details for failed requests + logger.error(f"Failed to discover service. Status code: {response.status_code}") + logger.error(response.text) + return None + except requests.RequestException as e: + # Handle network-related errors (connection timeout, DNS resolution, etc.) + logger.error(f"Error during service discovery: {e}") + return None + + def parse_uri(self, response): + """ + Parse the SME service discovery response to extract the service URI. + + The SME response follows a nested structure containing service descriptions, + AEF (Application Exposure Function) profiles, versions, and resources. + This method navigates through this structure to find the specific resource + and construct the complete service URL. + + Args: + response (dict): JSON response from SME service discovery + + Returns: + str: Complete HTTP URL for the service resource, or None if parsing fails + + Expected Response Structure: + { + "serviceAPIDescriptions": [ + { + "aefProfiles": [ + { + "versions": [ + { + "resources": [ + { + "resourceName": "resource_name", + "uri": "/api/endpoint" + } + ] + } + ], + "interfaceDescriptions": [ + { + "ipv4Addr": "", + "port": 0 + } + ] + } + ] + } + ] + } + """ + try: + logger.debug("Parsing SME response to extract URI.") + + # Navigate through the nested response structure + service = response["serviceAPIDescriptions"][0] # Get first service description + profile = service["aefProfiles"][0] # Get first AEF profile + version = profile["versions"][0] # Get first API version + + # Find the specific resource by name within the version's resources + resource = next( + (res for res in version["resources"] if res["resourceName"] == self.resource_name), + None + ) + uri = resource["uri"] if resource else None # Extract resource URI if found + + # Extract network interface information + interface = profile["interfaceDescriptions"][0] # Get first interface description + ipv4_addr = interface.get("ipv4Addr") # Get IPv4 address + port = interface.get("port") # Get port number + + # Construct complete URL: http://ipv4:port/uri (or just http://ipv4:port if no URI) + return f"http://{ipv4_addr}:{port}{uri}" if uri else f"http://{ipv4_addr}:{port}" + + except (KeyError, IndexError, TypeError) as e: + # Handle various parsing errors (missing keys, empty lists, wrong data types) + logger.error(f"Error parsing URI: {e}") + return None -- 2.16.6