Commit 746c444a authored by Pablo Armingol's avatar Pablo Armingol
Browse files

feat: add NBI endpoints for optical slice and ipowdm services

parent 24c344dc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ def create_request(resource_value):
    """

    LOGGER.info("Creating request for resource_value: %s", resource_value)
    LOGGER.info("Resource value type: %s", type(resource_value))
    BaseDir = os.path.dirname(os.path.abspath(__file__))
    json_path = os.path.join(BaseDir, 'ipowdm.json')
    with open(json_path, 'r', encoding='utf-8') as f:
+4 −1
Original line number Diff line number Diff line
@@ -78,7 +78,10 @@ def create_tapi_request(resource_value):
        ep1["local-id"] = resource_data["output_sip"]
        
        LOGGER.info("Created TAPI request: %s", json.dumps(template, indent=2))
        return template
        
        # Extract URL if present
        url = resource_data.get("url", "")
        return template, url
        
    except (OSError, json.JSONDecodeError, KeyError) as e:
        LOGGER.error("Error creating TAPI request: %s", str(e), exc_info=True)
+49 −15
Original line number Diff line number Diff line
@@ -118,12 +118,41 @@ class TransportApiDriver(_Driver):
            for resource in resources:
                LOGGER.info('resource = {:s}'.format(str(resource)))
                
                resource_key = resource[0]
                
                # Handle optical slice resources
                if '/optical_slice/context/' in resource_key:
                    LOGGER.info('=' * 80)
                    LOGGER.info('OPTICAL SLICE RECEIVED')
                    LOGGER.info('=' * 80)
                    try:
                        optical_slice_data = json.loads(resource[1])
                        
                        # Extract URL from tapi-common:context
                        url = optical_slice_data.get('url', '')
                        LOGGER.info('URL: %s', url)
                        LOGGER.info('-' * 80)
                        
                        # Log the full optical slice data
                        LOGGER.info('Optical Slice Data:')
                        optical_slice = optical_slice_data.get('data', {})
                        LOGGER.info(json.dumps(optical_slice, indent=2))
                        LOGGER.info('=' * 80)
                        results.append(True)
                    except Exception as e:
                        LOGGER.error(f'Failed to parse optical slice data: {str(e)}')
                        results.append(e)
                    continue
                
                # Handle media channel resources
                elif '/media_channel/service/' in resource_key:
                    try:
                        # Import here to avoid circular dependency
                        from .TapiRequestBuilder import create_tapi_request
                        
                        # Generate complete TAPI request from resource data
                    tapi_request = create_tapi_request(resource)
                        tapi_request, url = create_tapi_request(resource)
                        LOGGER.info('URL: {:s}'.format(url))
                        LOGGER.info('Generated TAPI request: {:s}'.format(json.dumps(tapi_request, indent=2)))
                        
                        # For now, just log the request (HTTP POST is commented out in Tools.py)
@@ -134,6 +163,11 @@ class TransportApiDriver(_Driver):
                        LOGGER.error('Failed to create TAPI request: {:s}'.format(str(e)), exc_info=True)
                        results.append(e)
                
                else:
                    # Unknown resource type
                    LOGGER.warning(f'Unknown resource type: {resource_key}')
                    results.append(True)  # Don't fail, just log warning
                    
        return results

    @metered_subclass_method(METRICS_POOL)
+4 −0
Original line number Diff line number Diff line
@@ -46,6 +46,8 @@ from .vntm_recommend import register_vntm_recommend
from .well_known_meta import register_well_known
from .e2e_services import register_etsi_api
from .media_channel import register_media_channel
from .optical_slice import register_optical_slice
from .ipowdm import register_ipowdm
from .tapi import register_tapi

LOG_LEVEL = get_log_level()
@@ -102,6 +104,8 @@ register_vntm_recommend (nbi_app)
register_camara_qod      (nbi_app)
register_etsi_api        (nbi_app)
register_media_channel   (nbi_app)
register_optical_slice   (nbi_app)
register_ipowdm          (nbi_app)
register_tapi            (nbi_app)
LOGGER.info('All connectors registered')

+143 −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.

import logging
import json
from flask_restful import Resource, request
from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, Device, Service, ServiceTypeEnum, ServiceStatusEnum
from device.client.DeviceClient import DeviceClient
from service.client.ServiceClient import ServiceClient

LOGGER = logging.getLogger(__name__)

class IPoWDMService(Resource):
    def __init__(self):
        super().__init__()
        self.device_client = DeviceClient()
        self.service_client = ServiceClient()

    def post(self, serviceId: str):
        LOGGER.info("Received POST request for IPoWDM service: %s", serviceId)
        
        request_data = request.get_json()
        LOGGER.info("IPoWDM request data: %s", json.dumps(request_data, indent=2))
        
        # Validate required fields
        if 'src' not in request_data or 'dst' not in request_data:
            return {'status': 'error', 'message': 'Missing required fields: src and dst'}, 400
        
        # Extract key information
        src_endpoints = request_data.get('src', [])
        dst_endpoints = request_data.get('dst', [])
        bandwidth = request_data.get('bw', 100)
        device_id = request_data.get('device_id', 'TFS-PACKET')
        
        LOGGER.info(f"Service UUID: {serviceId}")
        LOGGER.info(f"Bandwidth: {bandwidth}")
        LOGGER.info(f"Source endpoints: {len(src_endpoints)}")
        LOGGER.info(f"Destination endpoints: {len(dst_endpoints)}")
        LOGGER.info(f"Device ID: {device_id}")
        
        # Create TFS Service for visibility in WebUI
        try:
            service = Service()
            service.service_id.service_uuid.uuid = serviceId
            service.service_id.context_id.context_uuid.uuid = "admin"
            service.service_type = ServiceTypeEnum.SERVICETYPE_L3NM
            service.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE
            service.name = f"IPoWDM-{serviceId}"
            
            # Create service in TFS
            service_response = self.service_client.CreateService(service)
            LOGGER.info("Created TFS IPoWDM service: %s", service_response)
            
        except Exception as e:
            LOGGER.error("Failed to create TFS IPoWDM service: %s", str(e), exc_info=True)
            return {'status': 'error', 'message': f'Failed to create TFS service: {str(e)}'}, 500
        
        # Send to device for processing
        try:
            device = Device()
            device.device_id.device_uuid.uuid = device_id
            
            # Create config rule with IPoWDM data
            config_rule = ConfigRule()
            config_rule.action = ConfigActionEnum.CONFIGACTION_SET
            config_rule.custom.resource_key = f'/ipowdm/service/{serviceId}'
            
            # Store the complete IPoWDM configuration
            config_rule.custom.resource_value = json.dumps(request_data)
            
            # Add rule and configure device
            device.device_config.config_rules.append(config_rule)
            self.device_client.ConfigureDevice(device)
            LOGGER.info("Configured device %s with IPoWDM service %s", device_id, serviceId)
            
        except Exception as e:
            LOGGER.error("Failed to configure device: %s", str(e))
            return {'status': 'error', 'message': f'Failed to configure device: {str(e)}'}, 500
        
        return {
            'status': 'success',
            'message': f'IPoWDM service created for {serviceId}',
            'serviceId': serviceId,
            'device_id': device_id
        }, 201

    def delete(self, serviceId: str):
        LOGGER.info("Received DELETE request for IPoWDM service: %s", serviceId)
        
        data = request.get_json() or {}
        device_id = data.get('device_id', 'TFS-PACKET')
        
        # Delete TFS Service
        try:
            from common.proto.context_pb2 import ServiceId
            
            service_id = ServiceId()
            service_id.service_uuid.uuid = serviceId
            service_id.context_id.context_uuid.uuid = "admin"
            
            # Delete service from TFS
            self.service_client.DeleteService(service_id)
            LOGGER.info("Deleted TFS IPoWDM service: %s", serviceId)
            
        except Exception as e:
            LOGGER.error("Failed to delete TFS IPoWDM service: %s", str(e), exc_info=True)
            return {'status': 'error', 'message': f'Failed to delete TFS service: {str(e)}'}, 500
        
        # Delete from device
        if device_id:
            try:
                device = Device()
                device.device_id.device_uuid.uuid = device_id
                
                # Create DELETE config rule
                config_rule = ConfigRule()
                config_rule.action = ConfigActionEnum.CONFIGACTION_DELETE
                config_rule.custom.resource_key = f'/ipowdm/service/{serviceId}'
                config_rule.custom.resource_value = serviceId
                
                device.device_config.config_rules.append(config_rule)
                self.device_client.ConfigureDevice(device)
                LOGGER.info("Deleted IPoWDM service from device %s", device_id)
                
            except Exception as e:
                LOGGER.warning("Failed to delete from device: %s", str(e))
        
        return {
            'status': 'success',
            'message': f'IPoWDM service deleted for {serviceId}',
            'serviceId': serviceId
        }, 200
Loading