Commit 5ae15a6c authored by Waleed Akbar's avatar Waleed Akbar
Browse files

feat: Refactor AI Analytics Engine test setup and deployment scripts

parent 1784774c
Loading
Loading
Loading
Loading
+0 −40
Original line number Diff line number Diff line
@@ -12,43 +12,3 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
AI Analytics Engine module.

Provides REST API for AI-driven SLA policy analysis and violation detection
using data from SIMAP (network topology/devices) and InfluxDB (telemetry metrics).

Module Structure:
    - ai_model: AI/ML processing logic and SLA policy definitions
    - api: Flask REST API endpoints
    - clients: External service clients (SIMAP, InfluxDB, Decision Engine)
    - config: Configuration management
    - tests: Test suite

Public API:
    - AIAnalyticsEngineAPI: Main orchestrator and Flask application
    - AIModelProcessor: AI/ML analysis engine
    - SLAPolicyConfig: SLA policy configuration data model
    - SimapDataFetcher: SIMAP client for device/topology data
    - InfluxDBFetcher: InfluxDB client for telemetry metrics
    - DecisionEngineClient: Decision engine notification client
    - create_ai_analytics_blueprint: Flask blueprint factory
"""

from .ai_model.ai_processor import AIModelProcessor
from .api.api_blueprint import create_ai_analytics_blueprint
from .engine import AIAnalyticsEngineAPI
from .clients.decision_client import DecisionEngineClient
from .clients.influxdb_fetcher import InfluxDBFetcher
from .clients.simap_fetcher import SimapDataFetcher
from .ai_model.sla_policy import SLAPolicyConfig

__all__ = [
    'AIAnalyticsEngineAPI',
    'AIModelProcessor',
    'DecisionEngineClient',
    'InfluxDBFetcher',
    'SimapDataFetcher',
    'SLAPolicyConfig',
    'create_ai_analytics_blueprint',
]
+12 −9
Original line number Diff line number Diff line
@@ -18,23 +18,26 @@
# Usage: ./run_test.sh

# Navigate to TFS root directory
cd "$(dirname "$0")/../../../../.."
cd "$(dirname "$0")"

# Set Python path to include TFS src and AI Analytics Engine
export PYTHONPATH="${PWD}/src:${PWD}/src/tests/mwc26-f5ga"
# export PYTHONPATH="${PWD}/src:${PWD}/src/tests/mwc26-f5ga"

# Activate virtual environment if not already activated
if [ -z "$VIRTUAL_ENV" ]; then
    if [ -d "$HOME/.env-simap" ]; then
        source "$HOME/.env-simap/bin/activate"
    fi
fi
# if [ -z "$VIRTUAL_ENV" ]; then
#     if [ -d "$HOME/.env-simap" ]; then
#         source "$HOME/.env-simap/bin/activate"
#     fi
# fi
echo "$PWD"
echo "Running AI Analytics Engine API tests..."

# Define log file path
LOG_FILE="${PWD}/src/tests/mwc26-f5ga/AI_analytics_engine/tests/test_api.log"
LOG_FILE="${PWD}/test_api_docker.log"
TEST_FILE="${PWD}/test_api_docker.py"

# Run the test with logging enabled and capture output
pytest src/tests/mwc26-f5ga/AI_analytics_engine/tests/test_api.py::test_analyze_endpoint \
pytest $TEST_FILE \
    -v -s \
    --log-cli-level=DEBUG \
    --log-file="${LOG_FILE}" \
+142 −0
Original line number Diff line number Diff line
# Copyright 2022-2025 ETSI 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.

"""
Test suite for AI Analytics Engine REST API running in Docker.

This module tests the /api/v1/analyze endpoint by connecting to the
AI-Engine Docker container exposed on port 8084.
"""

import logging
import time

import pytest
import requests

# Configure logging for tests
logging.basicConfig(
    level=logging.DEBUG,
    format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s"
)
LOGGER = logging.getLogger(__name__)

# Test server configuration - Docker container exposed port
TEST_HOST = '127.0.0.1'
TEST_PORT = 8084  # Docker container port mapping: 8084->8080

BASE_URL = f'http://{TEST_HOST}:{TEST_PORT}'



@pytest.fixture(scope='module')
def ai_engine_server():
    """
    Fixture to verify the AI Analytics Engine Docker container is running.
    
    Checks connectivity to the Docker container and yields control to tests.
    Assumes the container is already running (docker run -p 8084:8080 ai-engine:latest).
    """
    LOGGER.info("Checking AI Analytics Engine Docker container availability")
    
    # Wait for server to be ready
    max_retries = 15
    for i in range(max_retries):
        try:
            LOGGER.debug(f"Checking Docker container connectivity... ({i+1}/{max_retries})")
            response = requests.get(f'{BASE_URL}/api/v1/config', timeout=2)
            if response.status_code == 200:
                LOGGER.info("AI Analytics Engine Docker container is ready")
                break
        except requests.exceptions.RequestException as e:
            LOGGER.debug(f"Container not ready yet: {e}")
            if i < max_retries - 1:
                time.sleep(2)
            else:
                raise RuntimeError(
                    f"Failed to connect to AI Analytics Engine Docker container at {BASE_URL}. "
                    f"Ensure container is running: docker run -p 8084:8080 ai-engine:latest"
                )
    
    yield
    
    LOGGER.info("AI Analytics Engine Docker test fixture cleanup complete")



def test_analyze_endpoint(ai_engine_server):
    """
    Test POST /api/v1/analyze endpoint.

    Validates that the analyze endpoint:
    - Accepts valid SLA policy JSON payload
    - Returns appropriate status codes (200 for success, 503 for service unavailable)
    - Returns JSON response with status and message fields
    """

    LOGGER.info(">>>>>> Starting test_case test_analyze_endpoint: POST /api/v1/analyze endpoint")
    
    # Prepare test payload with SLA policy configuration
    payload = {
        "simap_id": "E2E-L1",
        "sla_metrics": {
            "latency_threshold_ms": 0,
            "bandwidth_utilization": 0.0
        },
        "history_window_size_sec":      600,
        "forecast_sample_interval_sec": 5,
        "forecast_sample_count":        120,
    }

    LOGGER.info(f"Sending analyze request with payload: {payload}")
    
    # Send POST request to analyze endpoint
    response = requests.post(
        f'{BASE_URL}/api/v1/analyze',
        json=payload,
        timeout=10
    )

    # Add condition to validate response status code and content
    
    LOGGER.info(f"Analyze response status: {response.status_code}")
    
    # Parse JSON response
    data = response.json()
    LOGGER.info(f"Analyze response body: {data}")
    
    # Validate response structure
    assert 'status' in data, "Response missing 'status' field"
    assert 'message' in data, "Response missing 'message' field"
    
    # Accept either success (200) or service unavailable (503)
    # 503 is expected if SIMAP server or InfluxDB are not running
    if response.status_code == 200:
        LOGGER.info("Analysis completed successfully")
        assert data['status'] == 'success', f"Expected status 'success', got '{data['status']}'"
        assert 'data' in data, "Successful response missing 'data' field"
    elif response.status_code == 503:
        # LOGGER.error("External service unavailable (expected if SIMAP/InfluxDB not running)")
        assert data['status'] == 'error', f"Expected status 'error' for 503, got '{data['status']}'"
        pytest.fail("External service unavailable (expected if SIMAP/InfluxDB not running)")
    elif response.status_code == 400:
        LOGGER.error(f"Bad request: {data['message']}")
        assert data['status'] == 'error', f"Expected status 'error' for 400, got '{data['status']}'"
        pytest.fail(f"Bad request: {data['message']}")
    else:
        pytest.fail(f"Unexpected status code: {response.status_code}")
    
    LOGGER.info("Analyze endpoint test passed!")
    LOGGER.info("<<<<<< Finished test_case test_analyze_endpoint")
+12 −12
Original line number Diff line number Diff line
@@ -5,23 +5,23 @@ echo "Building SIMAP Server..."
cd ~/tfs-ctrl/
docker buildx build -t simap-server:mock -f ./src/tests/tools/simap_server/Dockerfile .

# echo "Building NCE-FAN Controller..."
# cd ~/tfs-ctrl/
# docker buildx build -t nce-fan-ctrl:mock -f ./src/tests/tools/mock_nce_fan_ctrl/Dockerfile .
echo "Building NCE-FAN Controller..."
cd ~/tfs-ctrl/
docker buildx build -t nce-fan-ctrl:mock -f ./src/tests/tools/mock_nce_fan_ctrl/Dockerfile .

# echo "Building NCE-T Controller..."
# cd ~/tfs-ctrl/
# docker buildx build -t nce-t-ctrl:mock -f ./src/tests/tools/mock_nce_t_ctrl/Dockerfile .
echo "Building NCE-T Controller..."
cd ~/tfs-ctrl/
docker buildx build -t nce-t-ctrl:mock -f ./src/tests/tools/mock_nce_t_ctrl/Dockerfile .

echo "Building AI Analytics Engine..."
cd ~/tfs-ctrl/
docker buildx build -t ai-engine:latest -f ./src/tests/mwc26-f5ga/AI_analytics_engine/Dockerfile .


# echo "Cleaning up..."
echo "Cleaning up..."
docker rm --force simap-server
# docker rm --force nce-fan-ctrl
# docker rm --force nce-t-ctrl
docker rm --force nce-fan-ctrl
docker rm --force nce-t-ctrl
docker rm --force ai-engine

# echo "Deploying support services..."
@@ -29,8 +29,9 @@ docker run --detach --name simap-server --publish 8080:8080 \
  -e INFLUXDB_HOST=10.254.0.9 \
  -e INFLUXDB_PORT=8181 \
  simap-server:mock
# docker run --detach --name nce-fan-ctrl    --publish 8081:8080 --env SIMAP_ADDRESS=172.17.0.1 --env SIMAP_PORT=8080 nce-fan-ctrl:mock
# docker run --detach --name nce-t-ctrl      --publish 8082:8080 --env SIMAP_ADDRESS=172.17.0.1 --env SIMAP_PORT=8080 nce-t-ctrl:mock

docker run --detach --name nce-fan-ctrl    --publish 8081:8080 --env SIMAP_ADDRESS=172.17.0.1 --env SIMAP_PORT=8080 nce-fan-ctrl:mock
docker run --detach --name nce-t-ctrl      --publish 8082:8080 --env SIMAP_ADDRESS=172.17.0.1 --env SIMAP_PORT=8080 nce-t-ctrl:mock

echo "Deploying AI Analytics Engine..."
docker run --detach --name ai-engine --publish 8084:8080 \
@@ -43,7 +44,6 @@ docker run --detach --name ai-engine --publish 8084:8080 \
# NOTE: If testing, run client (src/tests/tools/simap_server/run_client.sh) to manually populate SIMAP Server with telemetry data.



sleep 2
docker ps -a
echo "Deployment complete."
+0 −20
Original line number Diff line number Diff line

docker rm --force ai-engine 2>/dev/null || true

echo "Building AI Analytics Engine..."
cd ~/tfs-ctrl/
docker buildx build -t ai-engine:latest -f ./src/tests/mwc26-f5ga/AI_analytics_engine/Dockerfile .

echo "Deploying AI Analytics Engine..."
docker run --detach --name ai-engine \
  --publish 8084:8080 \
  --env SIMAP_SERVER_ADDRESS=172.17.0.1 \
  --env SIMAP_SERVER_PORT=8080 \
  --env SIMAP_SERVER_USERNAME=admin \
  --env SIMAP_SERVER_PASSWORD=admin \
  ai-engine:latest
# docker run --detach --name traffic-changer --publish 8083:8080 traffic-changer:mock

sleep 2
docker ps -a
echo "Deployment complete."
Loading