Loading src/device/service/drivers/ietf_l3vpn/templates/tools.py +1 −0 Original line number Diff line number Diff line Loading @@ -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: Loading src/device/service/drivers/transport_api/TapiRequestBuilder.py +4 −1 Original line number Diff line number Diff line Loading @@ -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) Loading src/device/service/drivers/transport_api/TransportApiDriver.py +49 −15 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading src/nbi/service/app.py +4 −0 Original line number Diff line number Diff line Loading @@ -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() Loading Loading @@ -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') Loading src/nbi/service/ipowdm/Resources.py 0 → 100644 +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
src/device/service/drivers/ietf_l3vpn/templates/tools.py +1 −0 Original line number Diff line number Diff line Loading @@ -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: Loading
src/device/service/drivers/transport_api/TapiRequestBuilder.py +4 −1 Original line number Diff line number Diff line Loading @@ -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) Loading
src/device/service/drivers/transport_api/TransportApiDriver.py +49 −15 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading
src/nbi/service/app.py +4 −0 Original line number Diff line number Diff line Loading @@ -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() Loading Loading @@ -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') Loading
src/nbi/service/ipowdm/Resources.py 0 → 100644 +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