Loading src/nbi/.gitlab-ci.yml +1 −0 Original line number Diff line number Diff line Loading @@ -115,6 +115,7 @@ unit_test nbi: - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_ietf_network.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_network.xml" - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_ietf_l3vpn.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_l3vpn.xml" - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_etsi_bwm.py --junitxml=/opt/results/${IMAGE_NAME}_report_etsi_bwm.xml" - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_camara_qod.py --junitxml=/opt/results/${IMAGE_NAME}_report_camara_qod.xml" - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' after_script: Loading src/nbi/service/app.py +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ from common.Settings import ( wait_for_environment_variables ) from .NbiApplication import NbiApplication from .camara_qod import register_camara_qod from .etsi_bwm import register_etsi_bwm_api from .health_probes import register_health_probes from .ietf_acl import register_ietf_acl Loading Loading @@ -96,6 +97,7 @@ register_ietf_acl (nbi_app) register_qkd_app (nbi_app) #register_topology_updates(nbi_app) # does not work; check if eventlet-grpc side effects register_vntm_recommend (nbi_app) register_camara_qod (nbi_app) LOGGER.info('All connectors registered') nbi_app.dump_configuration() Loading src/nbi/service/rest_server/nbi_plugins/camara_qod/Resources.py→src/nbi/service/camara_qod/Resources.py +9 −8 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ from typing import Dict from uuid import uuid4 from flask.json import jsonify from flask_restful import Resource, request from common.proto.context_pb2 import QoSProfileId, Uuid,Empty from common.proto.context_pb2 import Empty, QoSProfileId, Uuid from common.Constants import DEFAULT_CONTEXT_NAME from context.client.ContextClient import ContextClient from qos_profile.client.QoSProfileClient import QoSProfileClient Loading Loading @@ -89,13 +89,12 @@ class ProfileDetail(_Resource): if exc.code() == grpc.StatusCode.NOT_FOUND: LOGGER.warning(f"QoSProfile not found: {qos_profile_id}") return {"error": f"QoSProfile {qos_profile_id} not found"}, 404 LOGGER.error(f"gRPC error while fetching QoSProfile: {exc}") LOGGER.exception("gRPC error while fetching QoSProfile") return {"error": "Internal Server Error"}, 500 except: LOGGER.exception(f"Error while fetching QoSProfile") LOGGER.exception("Error while fetching QoSProfile") return {"error": "Internal Server Error"}, 500 def put(self, qos_profile_id): try: request_data = request.get_json() # get the json to do the update Loading @@ -105,13 +104,13 @@ class ProfileDetail(_Resource): qos_profile_updated = self.qos_profile_client.UpdateQoSProfile(qos_profile) # update the profile in the grpc server return grpc_message_to_qos_table_data(qos_profile_updated), 200 except KeyError as e: LOGGER.error(f"Missing required key: {e}") LOGGER.exception("Missing required key") return {"error": f"Missing required key: {str(e)}"}, 400 except grpc._channel._InactiveRpcError as exc: if exc.code() == grpc.StatusCode.NOT_FOUND: LOGGER.warning(f"QoSProfile not found for update: {qos_profile_id}") return {"error": f"QoSProfile {qos_profile_id} not found"}, 404 LOGGER.error(f"gRPC error while updating QoSProfile: {exc}") LOGGER.exception("gRPC error while updating QoSProfile") return {"error": "Internal Server Error"}, 500 except: LOGGER.exception(f"Error in PUT /profiles/{qos_profile_id}") Loading @@ -134,6 +133,7 @@ class ProfileDetail(_Resource): return {"error": "Internal Server Error"}, 500 ### SESSION ########################################################## class QodInfo(_Resource): def post(self): if not request.is_json: Loading Loading @@ -164,6 +164,7 @@ class QodInfo(_Resource): return jsonify({'error': 'Internal Server Error', 'message': 'An unexpected error occurred'}), 500 return response #didnt work return jsonify(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 Loading @@ -180,6 +181,7 @@ class QodInfoID(_Resource): if exc.code()==grpc.StatusCode.NOT_FOUND: LOGGER.warning(f"Qod Session not found: {sessionId}") return {"error": f"Qod Session {sessionId} not found"}, 404 def put(self, sessionId: str): try: request_data: Dict = request.get_json() Loading Loading @@ -212,8 +214,7 @@ class QodInfoID(_Resource): except: LOGGER.exception(f"Error in PUT /sessions/{sessionId}") return {"error": "Internal Server Error"}, 500 def delete(self, sessionId: str): self.service_client.DeleteService(grpc_service_id(DEFAULT_CONTEXT_NAME, sessionId)) return{"Session Deleted"} src/nbi/service/rest_server/nbi_plugins/camara_qod/Tools.py→src/nbi/service/camara_qod/Tools.py +14 −12 Original line number Diff line number Diff line Loading @@ -14,30 +14,32 @@ import json, logging, re, time from flask.json import jsonify from netaddr import IPAddress, IPNetwork from uuid import uuid4 from common.proto.context_pb2 import ( ContextId, Empty, EndPointId, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Service,ConfigRule, ConfigRule_Custom, ConfigActionEnum) ConfigActionEnum, ConfigRule, ConfigRule_Custom, ContextId, Empty, EndPointId, QoSProfileId, Service, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Uuid ) from common.proto.qos_profile_pb2 import ( QoSProfileValueUnitPair, QoSProfile,QoDConstraintsRequest ) from common.tools.grpc.ConfigRules import update_config_rule_custom from common.tools.grpc.Tools import grpc_message_to_json from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Service import json_service_id from uuid import uuid4 from nbi.service.rest_server.nbi_plugins.ietf_network.bindings.networks import network from qos_profile.client.QoSProfileClient import QoSProfileClient from context.client.ContextClient import ContextClient from common.proto.context_pb2 import QoSProfileId, Uuid,Empty,ServiceId from common.proto.context_pb2 import Uuid, QoSProfileId from common.proto.qos_profile_pb2 import QoSProfileValueUnitPair, QoSProfile,QoDConstraintsRequest import logging from netaddr import IPAddress, IPNetwork from qos_profile.client.QoSProfileClient import QoSProfileClient LOGGER = logging.getLogger(__name__) ENDPOINT_SETTINGS_KEY = '/device[{:s}]/endpoint[{:s}]/vlan[{:d}]/settings' DEVICE_SETTINGS_KEY = '/device[{:s}]/settings' RE_CONFIG_RULE_IF_SUBIF = re.compile(r'^\/interface\[([^\]]+)\]\/subinterface\[([^\]]+)\]$') MEC_CONSIDERED_FIELDS = ['device', 'applicationServer', 'qosProfile', 'sessionId', 'duration', 'startedAt', 'expiresAt', 'qosStatus'] MEC_CONSIDERED_FIELDS = [ 'device', 'applicationServer', 'qosProfile', 'sessionId', 'duration', 'startedAt', 'expiresAt', 'qosStatus' ] def __init__(self) -> None: super().__init__() Loading src/nbi/service/rest_server/nbi_plugins/camara_qod/__init__.py→src/nbi/service/camara_qod/__init__.py +22 −13 Original line number Diff line number Diff line Loading @@ -12,20 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. from nbi.service.rest_server.RestServer import RestServer from nbi.service.NbiApplication import NbiApplication 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) ('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>'), ] def register_camara_qod(rest_server : RestServer): for endpoint_name, resource_class, resource_url in RESOURCES: rest_server.add_resource(resource_class, URL_PREFIX + resource_url, endpoint=endpoint_name) def register_camara_qod(nbi_app : NbiApplication): nbi_app.add_rest_api_resource( QodInfo, URL_PREFIX + '/sessions', endpoint='camara.qod_session_info' ) nbi_app.add_rest_api_resource( QodInfoID, URL_PREFIX + '/bw_allocations', endpoint='/sessions/<sessionId>' ) nbi_app.add_rest_api_resource( ProfileList, URL_PREFIX + '/bw_allocations', endpoint='/profiles' ) nbi_app.add_rest_api_resource( ProfileDetail, URL_PREFIX + '/bw_allocations', endpoint='/profiles/<string:qos_profile_id>' ) Loading
src/nbi/.gitlab-ci.yml +1 −0 Original line number Diff line number Diff line Loading @@ -115,6 +115,7 @@ unit_test nbi: - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_ietf_network.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_network.xml" - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_ietf_l3vpn.py --junitxml=/opt/results/${IMAGE_NAME}_report_ietf_l3vpn.xml" - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_etsi_bwm.py --junitxml=/opt/results/${IMAGE_NAME}_report_etsi_bwm.xml" - docker exec -i $IMAGE_NAME bash -c "coverage run --append -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_camara_qod.py --junitxml=/opt/results/${IMAGE_NAME}_report_camara_qod.xml" - docker exec -i $IMAGE_NAME bash -c "coverage report --include='${IMAGE_NAME}/*' --show-missing" coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' after_script: Loading
src/nbi/service/app.py +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ from common.Settings import ( wait_for_environment_variables ) from .NbiApplication import NbiApplication from .camara_qod import register_camara_qod from .etsi_bwm import register_etsi_bwm_api from .health_probes import register_health_probes from .ietf_acl import register_ietf_acl Loading Loading @@ -96,6 +97,7 @@ register_ietf_acl (nbi_app) register_qkd_app (nbi_app) #register_topology_updates(nbi_app) # does not work; check if eventlet-grpc side effects register_vntm_recommend (nbi_app) register_camara_qod (nbi_app) LOGGER.info('All connectors registered') nbi_app.dump_configuration() Loading
src/nbi/service/rest_server/nbi_plugins/camara_qod/Resources.py→src/nbi/service/camara_qod/Resources.py +9 −8 Original line number Diff line number Diff line Loading @@ -18,7 +18,7 @@ from typing import Dict from uuid import uuid4 from flask.json import jsonify from flask_restful import Resource, request from common.proto.context_pb2 import QoSProfileId, Uuid,Empty from common.proto.context_pb2 import Empty, QoSProfileId, Uuid from common.Constants import DEFAULT_CONTEXT_NAME from context.client.ContextClient import ContextClient from qos_profile.client.QoSProfileClient import QoSProfileClient Loading Loading @@ -89,13 +89,12 @@ class ProfileDetail(_Resource): if exc.code() == grpc.StatusCode.NOT_FOUND: LOGGER.warning(f"QoSProfile not found: {qos_profile_id}") return {"error": f"QoSProfile {qos_profile_id} not found"}, 404 LOGGER.error(f"gRPC error while fetching QoSProfile: {exc}") LOGGER.exception("gRPC error while fetching QoSProfile") return {"error": "Internal Server Error"}, 500 except: LOGGER.exception(f"Error while fetching QoSProfile") LOGGER.exception("Error while fetching QoSProfile") return {"error": "Internal Server Error"}, 500 def put(self, qos_profile_id): try: request_data = request.get_json() # get the json to do the update Loading @@ -105,13 +104,13 @@ class ProfileDetail(_Resource): qos_profile_updated = self.qos_profile_client.UpdateQoSProfile(qos_profile) # update the profile in the grpc server return grpc_message_to_qos_table_data(qos_profile_updated), 200 except KeyError as e: LOGGER.error(f"Missing required key: {e}") LOGGER.exception("Missing required key") return {"error": f"Missing required key: {str(e)}"}, 400 except grpc._channel._InactiveRpcError as exc: if exc.code() == grpc.StatusCode.NOT_FOUND: LOGGER.warning(f"QoSProfile not found for update: {qos_profile_id}") return {"error": f"QoSProfile {qos_profile_id} not found"}, 404 LOGGER.error(f"gRPC error while updating QoSProfile: {exc}") LOGGER.exception("gRPC error while updating QoSProfile") return {"error": "Internal Server Error"}, 500 except: LOGGER.exception(f"Error in PUT /profiles/{qos_profile_id}") Loading @@ -134,6 +133,7 @@ class ProfileDetail(_Resource): return {"error": "Internal Server Error"}, 500 ### SESSION ########################################################## class QodInfo(_Resource): def post(self): if not request.is_json: Loading Loading @@ -164,6 +164,7 @@ class QodInfo(_Resource): return jsonify({'error': 'Internal Server Error', 'message': 'An unexpected error occurred'}), 500 return response #didnt work return jsonify(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 Loading @@ -180,6 +181,7 @@ class QodInfoID(_Resource): if exc.code()==grpc.StatusCode.NOT_FOUND: LOGGER.warning(f"Qod Session not found: {sessionId}") return {"error": f"Qod Session {sessionId} not found"}, 404 def put(self, sessionId: str): try: request_data: Dict = request.get_json() Loading Loading @@ -212,8 +214,7 @@ class QodInfoID(_Resource): except: LOGGER.exception(f"Error in PUT /sessions/{sessionId}") return {"error": "Internal Server Error"}, 500 def delete(self, sessionId: str): self.service_client.DeleteService(grpc_service_id(DEFAULT_CONTEXT_NAME, sessionId)) return{"Session Deleted"}
src/nbi/service/rest_server/nbi_plugins/camara_qod/Tools.py→src/nbi/service/camara_qod/Tools.py +14 −12 Original line number Diff line number Diff line Loading @@ -14,30 +14,32 @@ import json, logging, re, time from flask.json import jsonify from netaddr import IPAddress, IPNetwork from uuid import uuid4 from common.proto.context_pb2 import ( ContextId, Empty, EndPointId, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Service,ConfigRule, ConfigRule_Custom, ConfigActionEnum) ConfigActionEnum, ConfigRule, ConfigRule_Custom, ContextId, Empty, EndPointId, QoSProfileId, Service, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Uuid ) from common.proto.qos_profile_pb2 import ( QoSProfileValueUnitPair, QoSProfile,QoDConstraintsRequest ) from common.tools.grpc.ConfigRules import update_config_rule_custom from common.tools.grpc.Tools import grpc_message_to_json from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Service import json_service_id from uuid import uuid4 from nbi.service.rest_server.nbi_plugins.ietf_network.bindings.networks import network from qos_profile.client.QoSProfileClient import QoSProfileClient from context.client.ContextClient import ContextClient from common.proto.context_pb2 import QoSProfileId, Uuid,Empty,ServiceId from common.proto.context_pb2 import Uuid, QoSProfileId from common.proto.qos_profile_pb2 import QoSProfileValueUnitPair, QoSProfile,QoDConstraintsRequest import logging from netaddr import IPAddress, IPNetwork from qos_profile.client.QoSProfileClient import QoSProfileClient LOGGER = logging.getLogger(__name__) ENDPOINT_SETTINGS_KEY = '/device[{:s}]/endpoint[{:s}]/vlan[{:d}]/settings' DEVICE_SETTINGS_KEY = '/device[{:s}]/settings' RE_CONFIG_RULE_IF_SUBIF = re.compile(r'^\/interface\[([^\]]+)\]\/subinterface\[([^\]]+)\]$') MEC_CONSIDERED_FIELDS = ['device', 'applicationServer', 'qosProfile', 'sessionId', 'duration', 'startedAt', 'expiresAt', 'qosStatus'] MEC_CONSIDERED_FIELDS = [ 'device', 'applicationServer', 'qosProfile', 'sessionId', 'duration', 'startedAt', 'expiresAt', 'qosStatus' ] def __init__(self) -> None: super().__init__() Loading
src/nbi/service/rest_server/nbi_plugins/camara_qod/__init__.py→src/nbi/service/camara_qod/__init__.py +22 −13 Original line number Diff line number Diff line Loading @@ -12,20 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. from nbi.service.rest_server.RestServer import RestServer from nbi.service.NbiApplication import NbiApplication 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) ('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>'), ] def register_camara_qod(rest_server : RestServer): for endpoint_name, resource_class, resource_url in RESOURCES: rest_server.add_resource(resource_class, URL_PREFIX + resource_url, endpoint=endpoint_name) def register_camara_qod(nbi_app : NbiApplication): nbi_app.add_rest_api_resource( QodInfo, URL_PREFIX + '/sessions', endpoint='camara.qod_session_info' ) nbi_app.add_rest_api_resource( QodInfoID, URL_PREFIX + '/bw_allocations', endpoint='/sessions/<sessionId>' ) nbi_app.add_rest_api_resource( ProfileList, URL_PREFIX + '/bw_allocations', endpoint='/profiles' ) nbi_app.add_rest_api_resource( ProfileDetail, URL_PREFIX + '/bw_allocations', endpoint='/profiles/<string:qos_profile_id>' )