Loading my_deploy.sh +1 −1 Original line number Diff line number Diff line Loading @@ -134,7 +134,7 @@ export CRDB_PASSWORD="tfs123" export CRDB_DEPLOY_MODE="single" # Disable flag for dropping database, if it exists. export CRDB_DROP_DATABASE_IF_EXISTS="YES" export CRDB_DROP_DATABASE_IF_EXISTS="" # Disable flag for re-deploying CockroachDB from scratch. export CRDB_REDEPLOY="" Loading src/nbi/service/rest_server/nbi_plugins/camara_qod/Resources.py +21 −27 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 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. Loading @@ -11,31 +11,26 @@ # 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. from flask.json import jsonify from flask_restful import Resource, request from enum import Enum import grpc._channel from qos_profile.client.QoSProfileClient import QoSProfileClient from werkzeug.exceptions import UnsupportedMediaType from common.proto.context_pb2 import QoSProfileId, Uuid,Empty from common.proto.qos_profile_pb2 import QoDConstraintsRequest import copy, grpc, grpc._channel, logging from typing import Dict from uuid import uuid4 import grpc, logging import copy, deepmerge, json, logging from typing import Dict from flask.json import jsonify from flask_restful import Resource, request from werkzeug.exceptions import UnsupportedMediaType from common.proto.context_pb2 import QoSProfileId, Uuid,Empty from common.Constants import DEFAULT_CONTEXT_NAME from context.client.ContextClient import ContextClient from qos_profile.client.QoSProfileClient import QoSProfileClient from service.client.ServiceClient import ServiceClient from .Tools import ( format_grpc_to_json, grpc_context_id, grpc_service_id, QOD_2_service, service_2_qod,grpc_message_to_qos_table_data,create_qos_profile_from_json format_grpc_to_json, grpc_context_id, grpc_service_id, QOD_2_service, service_2_qod, grpc_message_to_qos_table_data, create_qos_profile_from_json ) LOGGER = logging.getLogger(__name__) #Initiate the QoSProfileClient class _Resource(Resource): def __init__(self) -> None: super().__init__() Loading @@ -56,13 +51,13 @@ class ProfileList(_Resource): except Exception as e: LOGGER.error(e) # track if there is an error raise e # Send to gRPC server using CreateQosProfile done by Shayan # Send to gRPC server using CreateQosProfile try: qos_profile_created = self.qos_profile_client.CreateQoSProfile(qos_profile) except Exception as e: LOGGER.info(e) LOGGER.error(e) raise e #gRPC message back to JSON using the helper function created by shayan #gRPC message back to JSON using the helper function qos_profile_data = grpc_message_to_qos_table_data(qos_profile_created) LOGGER.info(f'qos_profile_data{qos_profile_data}') return jsonify(qos_profile_data) Loading Loading @@ -140,8 +135,7 @@ class ProfileDetail(_Resource): return {"error": "Internal Server Error"}, 500 ###SESSION########################################################## LOGGER = logging.getLogger(__name__) class qodinfo(_Resource): class QodInfo(_Resource): def post(self): if not request.is_json: return (jsonify({'error': 'Unsupported Media Type', 'message': 'JSON payload is required'}), 415) Loading @@ -163,18 +157,18 @@ class qodinfo(_Resource): response = format_grpc_to_json(self.service_client.CreateService(stripped_service)) response = format_grpc_to_json(self.service_client.UpdateService(service)) except Exception as e: # pylint: disable=broad-except LOGGER.error(f"Unexpected error: {str(e)}", exc_info=True) return jsonify({'error': 'Internal Server Error', 'message': 'An unexpected error occurred'}), 500 LOGGER.error(f"error related to response: {response}") return e return response def get(self): service_list = self.client.ListServices(grpc_context_id(DEFAULT_CONTEXT_NAME)) #return context id as json qod_info = [service_2_qod(service) for service in service_list.services] #iterating over service list LOGGER.info(f"error related to qod_info: {qod_info}") return qod_info return jsonify(qod_info), 200 class qodinfoId(_Resource): class QodInfoID(_Resource): def get(self, sessionId: str): try: Loading src/nbi/service/rest_server/nbi_plugins/camara_qod/Tools.py +7 −23 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 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. Loading Loading @@ -91,7 +91,7 @@ def ip_withoutsubnet(ip_withsubnet,neededip): return IPAddress(neededip) in network def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: def QOD_2_service(client,qod_info: dict,qos_profile_id: int,duration: int) -> Service: qos_profile_client = QoSProfileClient() service = Service() service_config_rules = service.service_config.config_rules Loading @@ -110,8 +110,8 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: a_ip = qod_info['device'].get('ipv4Address') z_ip = qod_info['applicationServer'].get('ipv4Address') LOGGER.info('a_ip = {:s}'.format(str(a_ip))) LOGGER.info('z_ip = {:s}'.format(str(z_ip))) LOGGER.debug('a_ip = {:s}'.format(str(a_ip))) LOGGER.debug('z_ip = {:s}'.format(str(z_ip))) if a_ip and z_ip: devices = client.ListDevices(Empty()).devices Loading @@ -133,9 +133,9 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: if not match_subif: continue address_ip =json.loads(cr.custom.resource_value).get('address_ip') LOGGER.info('cr..address_ip = {:s}'.format(str(address_ip))) LOGGER.debug('cr..address_ip = {:s}'.format(str(address_ip))) short_port_name = match_subif.groups()[0] LOGGER.info('short_port_name = {:s}'.format(str(short_port_name))) LOGGER.debug('short_port_name = {:s}'.format(str(short_port_name))) ip_interface_name_dict[address_ip] = short_port_name Loading @@ -145,7 +145,7 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: ep_id.endpoint_uuid.uuid = device_endpoint_uuids.get(short_port_name , '') ep_id.device_id.device_uuid.uuid = device.device_id.device_uuid.uuid service.service_endpoint_ids.append(ep_id) LOGGER.info(f"the ip address{ep_id}") LOGGER.debug(f"the ip address{ep_id}") #LOGGER.info('ip_interface_name_dict = {:s}'.format(str(ip_interface_name_dict))) Loading @@ -158,17 +158,6 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: qod_info["context"]='admin' service.service_id.service_uuid.uuid = qod_info['sessionID'] service.service_id.context_id.context_uuid.uuid = qod_info["context"] #try: # id = QoSProfileId(qos_profile_id=Uuid(uuid=qos_profile_id)) # id= QoSProfileId() # id.qos_profile_id.uuid="qos_profile_id" # qos_profile = qos_profile_client.GetQoSProfile(id) #except grpc._channel._InactiveRpcError as exc: # if exc.code() == grpc.StatusCode.NOT_FOUND: # return {"error": f"QoSProfile {qos_profile_id} not found"}, 404 #if qos_profile.qos_profile_id: # qos_profile_id = qod_info.get('qos_profile_id') service.name = qod_info.get('qos_profile_id', qos_profile_id) current_time = time.time() duration_days = duration # days as i saw it in the proto files Loading @@ -186,11 +175,6 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: cs.qos_profile.qos_profile_name.CopyFrom(qos_profile_client.name) #i copied this from the qosprofile LOGGER.info(f'the cs : {cs}') service.service_constraints.append(cs) #qos_profile = QoSProfile() #qos_profile.qos_profile_id.qos_profile_id.uuid = qod_info['qosProfile'] #if 'qosProfile' in qos_profile_list: # qos_profile = QoSProfile() # qos_profile.qos_profile_id.qos_profile_id.uuid = qod_info['qosProfile'] return service Loading src/nbi/service/rest_server/nbi_plugins/camara_qod/__init__.py +6 −7 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 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. Loading @@ -13,16 +13,15 @@ # limitations under the License. from nbi.service.rest_server.RestServer import RestServer from .Resources import ProfileList, ProfileDetail, qodinfo, qodinfoId from .Resources import ProfileList, ProfileDetail, QodInfo, QodInfoID URL_PREFIX = '/camara/qod/v0' # Use 'path' type since some identifiers might contain char '/' and Flask is unable to recognize them in 'string' type. RESOURCES = [ # (endpoint_name, resource_class, resource_url) # TODO: Add appropriate endpoints ('camara.qod_session_info', qodinfo, '/sessions'), ('camara.qod_info_session_id', qodinfoId, '/sessions/<sessionId>'), ('camara.qod_session_info', QodInfo, '/sessions'), ('camara.qod_info_session_id', QodInfoID, '/sessions/<sessionId>'), ('camara.qod.profile_list', ProfileList, '/profiles'), ('camara.qod.profile_detail', ProfileDetail, '/profiles/<string:qos_profile_id>'), ] Loading src/nbi/tests/test_camara_qod_profile.py +1 −2 Original line number Diff line number Diff line Loading @@ -19,10 +19,9 @@ import requests logging.basicConfig(level=logging.DEBUG) LOGGER = logging.getLogger() BASE_URL = 'http://10.1.7.197/camara/qod/v0' def test_create_profile(): BASE_URL = 'http://10.1.7.197/camara/qod/v0' BASE_URL = 'http://localhost/camara/qod/v0' qos_profile_data={ "name": "QCI_2_voice", "description": "QoS profile for video streaming", Loading Loading
my_deploy.sh +1 −1 Original line number Diff line number Diff line Loading @@ -134,7 +134,7 @@ export CRDB_PASSWORD="tfs123" export CRDB_DEPLOY_MODE="single" # Disable flag for dropping database, if it exists. export CRDB_DROP_DATABASE_IF_EXISTS="YES" export CRDB_DROP_DATABASE_IF_EXISTS="" # Disable flag for re-deploying CockroachDB from scratch. export CRDB_REDEPLOY="" Loading
src/nbi/service/rest_server/nbi_plugins/camara_qod/Resources.py +21 −27 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 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. Loading @@ -11,31 +11,26 @@ # 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. from flask.json import jsonify from flask_restful import Resource, request from enum import Enum import grpc._channel from qos_profile.client.QoSProfileClient import QoSProfileClient from werkzeug.exceptions import UnsupportedMediaType from common.proto.context_pb2 import QoSProfileId, Uuid,Empty from common.proto.qos_profile_pb2 import QoDConstraintsRequest import copy, grpc, grpc._channel, logging from typing import Dict from uuid import uuid4 import grpc, logging import copy, deepmerge, json, logging from typing import Dict from flask.json import jsonify from flask_restful import Resource, request from werkzeug.exceptions import UnsupportedMediaType from common.proto.context_pb2 import QoSProfileId, Uuid,Empty from common.Constants import DEFAULT_CONTEXT_NAME from context.client.ContextClient import ContextClient from qos_profile.client.QoSProfileClient import QoSProfileClient from service.client.ServiceClient import ServiceClient from .Tools import ( format_grpc_to_json, grpc_context_id, grpc_service_id, QOD_2_service, service_2_qod,grpc_message_to_qos_table_data,create_qos_profile_from_json format_grpc_to_json, grpc_context_id, grpc_service_id, QOD_2_service, service_2_qod, grpc_message_to_qos_table_data, create_qos_profile_from_json ) LOGGER = logging.getLogger(__name__) #Initiate the QoSProfileClient class _Resource(Resource): def __init__(self) -> None: super().__init__() Loading @@ -56,13 +51,13 @@ class ProfileList(_Resource): except Exception as e: LOGGER.error(e) # track if there is an error raise e # Send to gRPC server using CreateQosProfile done by Shayan # Send to gRPC server using CreateQosProfile try: qos_profile_created = self.qos_profile_client.CreateQoSProfile(qos_profile) except Exception as e: LOGGER.info(e) LOGGER.error(e) raise e #gRPC message back to JSON using the helper function created by shayan #gRPC message back to JSON using the helper function qos_profile_data = grpc_message_to_qos_table_data(qos_profile_created) LOGGER.info(f'qos_profile_data{qos_profile_data}') return jsonify(qos_profile_data) Loading Loading @@ -140,8 +135,7 @@ class ProfileDetail(_Resource): return {"error": "Internal Server Error"}, 500 ###SESSION########################################################## LOGGER = logging.getLogger(__name__) class qodinfo(_Resource): class QodInfo(_Resource): def post(self): if not request.is_json: return (jsonify({'error': 'Unsupported Media Type', 'message': 'JSON payload is required'}), 415) Loading @@ -163,18 +157,18 @@ class qodinfo(_Resource): response = format_grpc_to_json(self.service_client.CreateService(stripped_service)) response = format_grpc_to_json(self.service_client.UpdateService(service)) except Exception as e: # pylint: disable=broad-except LOGGER.error(f"Unexpected error: {str(e)}", exc_info=True) return jsonify({'error': 'Internal Server Error', 'message': 'An unexpected error occurred'}), 500 LOGGER.error(f"error related to response: {response}") return e return response def get(self): service_list = self.client.ListServices(grpc_context_id(DEFAULT_CONTEXT_NAME)) #return context id as json qod_info = [service_2_qod(service) for service in service_list.services] #iterating over service list LOGGER.info(f"error related to qod_info: {qod_info}") return qod_info return jsonify(qod_info), 200 class qodinfoId(_Resource): class QodInfoID(_Resource): def get(self, sessionId: str): try: Loading
src/nbi/service/rest_server/nbi_plugins/camara_qod/Tools.py +7 −23 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 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. Loading Loading @@ -91,7 +91,7 @@ def ip_withoutsubnet(ip_withsubnet,neededip): return IPAddress(neededip) in network def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: def QOD_2_service(client,qod_info: dict,qos_profile_id: int,duration: int) -> Service: qos_profile_client = QoSProfileClient() service = Service() service_config_rules = service.service_config.config_rules Loading @@ -110,8 +110,8 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: a_ip = qod_info['device'].get('ipv4Address') z_ip = qod_info['applicationServer'].get('ipv4Address') LOGGER.info('a_ip = {:s}'.format(str(a_ip))) LOGGER.info('z_ip = {:s}'.format(str(z_ip))) LOGGER.debug('a_ip = {:s}'.format(str(a_ip))) LOGGER.debug('z_ip = {:s}'.format(str(z_ip))) if a_ip and z_ip: devices = client.ListDevices(Empty()).devices Loading @@ -133,9 +133,9 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: if not match_subif: continue address_ip =json.loads(cr.custom.resource_value).get('address_ip') LOGGER.info('cr..address_ip = {:s}'.format(str(address_ip))) LOGGER.debug('cr..address_ip = {:s}'.format(str(address_ip))) short_port_name = match_subif.groups()[0] LOGGER.info('short_port_name = {:s}'.format(str(short_port_name))) LOGGER.debug('short_port_name = {:s}'.format(str(short_port_name))) ip_interface_name_dict[address_ip] = short_port_name Loading @@ -145,7 +145,7 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: ep_id.endpoint_uuid.uuid = device_endpoint_uuids.get(short_port_name , '') ep_id.device_id.device_uuid.uuid = device.device_id.device_uuid.uuid service.service_endpoint_ids.append(ep_id) LOGGER.info(f"the ip address{ep_id}") LOGGER.debug(f"the ip address{ep_id}") #LOGGER.info('ip_interface_name_dict = {:s}'.format(str(ip_interface_name_dict))) Loading @@ -158,17 +158,6 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: qod_info["context"]='admin' service.service_id.service_uuid.uuid = qod_info['sessionID'] service.service_id.context_id.context_uuid.uuid = qod_info["context"] #try: # id = QoSProfileId(qos_profile_id=Uuid(uuid=qos_profile_id)) # id= QoSProfileId() # id.qos_profile_id.uuid="qos_profile_id" # qos_profile = qos_profile_client.GetQoSProfile(id) #except grpc._channel._InactiveRpcError as exc: # if exc.code() == grpc.StatusCode.NOT_FOUND: # return {"error": f"QoSProfile {qos_profile_id} not found"}, 404 #if qos_profile.qos_profile_id: # qos_profile_id = qod_info.get('qos_profile_id') service.name = qod_info.get('qos_profile_id', qos_profile_id) current_time = time.time() duration_days = duration # days as i saw it in the proto files Loading @@ -186,11 +175,6 @@ def QOD_2_service(client,qod_info: dict,qos_profile_id,duration) -> Service: cs.qos_profile.qos_profile_name.CopyFrom(qos_profile_client.name) #i copied this from the qosprofile LOGGER.info(f'the cs : {cs}') service.service_constraints.append(cs) #qos_profile = QoSProfile() #qos_profile.qos_profile_id.qos_profile_id.uuid = qod_info['qosProfile'] #if 'qosProfile' in qos_profile_list: # qos_profile = QoSProfile() # qos_profile.qos_profile_id.qos_profile_id.uuid = qod_info['qosProfile'] return service Loading
src/nbi/service/rest_server/nbi_plugins/camara_qod/__init__.py +6 −7 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/) # Copyright 2022-2024 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. Loading @@ -13,16 +13,15 @@ # limitations under the License. from nbi.service.rest_server.RestServer import RestServer from .Resources import ProfileList, ProfileDetail, qodinfo, qodinfoId from .Resources import ProfileList, ProfileDetail, QodInfo, QodInfoID URL_PREFIX = '/camara/qod/v0' # Use 'path' type since some identifiers might contain char '/' and Flask is unable to recognize them in 'string' type. RESOURCES = [ # (endpoint_name, resource_class, resource_url) # TODO: Add appropriate endpoints ('camara.qod_session_info', qodinfo, '/sessions'), ('camara.qod_info_session_id', qodinfoId, '/sessions/<sessionId>'), ('camara.qod_session_info', QodInfo, '/sessions'), ('camara.qod_info_session_id', QodInfoID, '/sessions/<sessionId>'), ('camara.qod.profile_list', ProfileList, '/profiles'), ('camara.qod.profile_detail', ProfileDetail, '/profiles/<string:qos_profile_id>'), ] Loading
src/nbi/tests/test_camara_qod_profile.py +1 −2 Original line number Diff line number Diff line Loading @@ -19,10 +19,9 @@ import requests logging.basicConfig(level=logging.DEBUG) LOGGER = logging.getLogger() BASE_URL = 'http://10.1.7.197/camara/qod/v0' def test_create_profile(): BASE_URL = 'http://10.1.7.197/camara/qod/v0' BASE_URL = 'http://localhost/camara/qod/v0' qos_profile_data={ "name": "QCI_2_voice", "description": "QoS profile for video streaming", Loading