## 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
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
--- /dev/null
+{
+ "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
--- /dev/null
+"""
+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