diff --git a/install_development_dependencies.sh b/install_development_dependencies.sh index f995c4a748c792a0d0e86d200e8d0af27c8c000e..f46b5b0081e4190ac0b84f4ea2abd7213b2a30f9 100755 --- a/install_development_dependencies.sh +++ b/install_development_dependencies.sh @@ -7,7 +7,7 @@ pip install --upgrade pip setuptools wheel pip-tools pylint pytest pytest-benchm echo "" > requirements.in #TODO: include here your component -COMPONENTS="compute context device monitoring centralizedattackdetector opticalcentralizedattackdetector opticalattackmitigator dbscanserving" +COMPONENTS="compute context device service monitoring centralizedattackdetector opticalcentralizedattackdetector opticalattackmitigator dbscanserving" # compiling dependencies from all components for component in $COMPONENTS diff --git a/src/common/tests/PytestGenerateTests.py b/src/common/tests/PytestGenerateTests.py new file mode 100644 index 0000000000000000000000000000000000000000..1c5abcf45b2f3e2d14fd31a5b3c1ee5d434f10b3 --- /dev/null +++ b/src/common/tests/PytestGenerateTests.py @@ -0,0 +1,42 @@ +# Create a set of tests enabling to run tests as follows ... +# from common.tests.PytestGenerateTests import pytest_generate_tests # pylint: disable=unused-import +# +# scenario1 = ('basic', {'attribute': 'value'}) +# scenario2 = ('advanced', {'attribute': 'value2'}) +# +# class TestSampleWithScenarios: +# scenarios = [scenario1, scenario2] +# +# def test_demo1(self, attribute): +# assert isinstance(attribute, str) +# +# def test_demo2(self, attribute): +# assert isinstance(attribute, str) +# +# ... and run them as: +# $ pytest --log-level=INFO --verbose my_test.py +# =================== test session starts =================== +# platform linux -- Python 3.9.6, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /home/.../.pyenv/.../bin/python3.9 +# cachedir: .pytest_cache +# benchmark: 3.4.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 +# calibration_precision=10 warmup=False warmup_iterations=100000) +# rootdir: /home/.../tests +# plugins: benchmark-3.4.1 +# collected 4 items +# +# my_test.py::TestSampleWithScenarios::test_demo1[basic] PASSED [ 25%] +# my_test.py::TestSampleWithScenarios::test_demo2[basic] PASSED [ 50%] +# my_test.py::TestSampleWithScenarios::test_demo1[advanced] PASSED [ 75%] +# my_test.py::TestSampleWithScenarios::test_demo2[advanced] PASSED [100%] +# +# ==================== 4 passed in 0.02s ==================== + +def pytest_generate_tests(metafunc): + idlist = [] + argvalues = [] + for scenario in metafunc.cls.scenarios: + idlist.append(scenario[0]) + items = scenario[1].items() + argnames = [x[0] for x in items] + argvalues.append([x[1] for x in items]) + metafunc.parametrize(argnames, argvalues, ids=idlist, scope='class') diff --git a/src/common/tests/__init__.py b/src/common/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/service/_old_code_backup/ServiceServiceServicerImpl.py b/src/service/_old_code_backup/ServiceServiceServicerImpl.py deleted file mode 100644 index b1f370abc1d3eed1b3b756bf31b95c01209539fd..0000000000000000000000000000000000000000 --- a/src/service/_old_code_backup/ServiceServiceServicerImpl.py +++ /dev/null @@ -1,277 +0,0 @@ -from typing import Dict -import grpc, logging -from prometheus_client import Counter, Histogram -from common.database.api.Database import Database -from common.exceptions.ServiceException import ServiceException -from service.proto.context_pb2 import Empty -from service.proto.service_pb2 import ConnectionList, Service, ServiceId, ServiceList -from service.proto.service_pb2_grpc import ServiceServiceServicer -from service.service.Tools import check_service_id_request, check_service_request - -LOGGER = logging.getLogger(__name__) - -GETSERVICELIST_COUNTER_STARTED = Counter ('service_getservicelist_counter_started', - 'Service:GetServiceList counter of requests started' ) -GETSERVICELIST_COUNTER_COMPLETED = Counter ('service_getservicelist_counter_completed', - 'Service:GetServiceList counter of requests completed') -GETSERVICELIST_COUNTER_FAILED = Counter ('service_getservicelist_counter_failed', - 'Service:GetServiceList counter of requests failed' ) -GETSERVICELIST_HISTOGRAM_DURATION = Histogram('service_getservicelist_histogram_duration', - 'Service:GetServiceList histogram of request duration') - -CREATESERVICE_COUNTER_STARTED = Counter ('service_createservice_counter_started', - 'Service:CreateService counter of requests started' ) -CREATESERVICE_COUNTER_COMPLETED = Counter ('service_createservice_counter_completed', - 'Service:CreateService counter of requests completed') -CREATESERVICE_COUNTER_FAILED = Counter ('service_createservice_counter_failed', - 'Service:CreateService counter of requests failed' ) -CREATESERVICE_HISTOGRAM_DURATION = Histogram('service_createservice_histogram_duration', - 'Service:CreateService histogram of request duration') - -UPDATESERVICE_COUNTER_STARTED = Counter ('service_updateservice_counter_started', - 'Service:UpdateService counter of requests started' ) -UPDATESERVICE_COUNTER_COMPLETED = Counter ('service_updateservice_counter_completed', - 'Service:UpdateService counter of requests completed') -UPDATESERVICE_COUNTER_FAILED = Counter ('service_updateservice_counter_failed', - 'Service:UpdateService counter of requests failed' ) -UPDATESERVICE_HISTOGRAM_DURATION = Histogram('service_updateservice_histogram_duration', - 'Service:UpdateService histogram of request duration') - -DELETESERVICE_COUNTER_STARTED = Counter ('service_deleteservice_counter_started', - 'Service:DeleteService counter of requests started' ) -DELETESERVICE_COUNTER_COMPLETED = Counter ('service_deleteservice_counter_completed', - 'Service:DeleteService counter of requests completed') -DELETESERVICE_COUNTER_FAILED = Counter ('service_deleteservice_counter_failed', - 'Service:DeleteService counter of requests failed' ) -DELETESERVICE_HISTOGRAM_DURATION = Histogram('service_deleteservice_histogram_duration', - 'Service:DeleteService histogram of request duration') - -GETSERVICEBYID_COUNTER_STARTED = Counter ('service_getservicebyid_counter_started', - 'Service:GetServiceById counter of requests started' ) -GETSERVICEBYID_COUNTER_COMPLETED = Counter ('service_getservicebyid_counter_completed', - 'Service:GetServiceById counter of requests completed') -GETSERVICEBYID_COUNTER_FAILED = Counter ('service_getservicebyid_counter_failed', - 'Service:GetServiceById counter of requests failed' ) -GETSERVICEBYID_HISTOGRAM_DURATION = Histogram('service_getservicebyid_histogram_duration', - 'Service:GetServiceById histogram of request duration') - -GETCONNECTIONLIST_COUNTER_STARTED = Counter ('service_getconnectionlist_counter_started', - 'Service:GetConnectionList counter of requests started' ) -GETCONNECTIONLIST_COUNTER_COMPLETED = Counter ('service_getconnectionlist_counter_completed', - 'Service:GetConnectionList counter of requests completed') -GETCONNECTIONLIST_COUNTER_FAILED = Counter ('service_getconnectionlist_counter_failed', - 'Service:GetConnectionList counter of requests failed' ) -GETCONNECTIONLIST_HISTOGRAM_DURATION = Histogram('service_getconnectionlist_histogram_duration', - 'Service:GetConnectionList histogram of request duration') - -class ServiceServiceServicerImpl(ServiceServiceServicer): - def __init__(self, database : Database): - LOGGER.debug('Creating Servicer...') - self.database = database - LOGGER.debug('Servicer Created') - - @GETSERVICELIST_HISTOGRAM_DURATION.time() - def GetServiceList(self, request : Empty, grpc_context : grpc.ServicerContext) -> ServiceList: - GETSERVICELIST_COUNTER_STARTED.inc() - try: - LOGGER.debug('GetServiceList request: {}'.format(str(request))) - - # ----- Validate request data and pre-conditions ----------------------------------------------------------- - - # ----- Retrieve data from the database -------------------------------------------------------------------- - db_context_uuids = self.database.contexts.get() - json_services = [] - for db_context_uuid in db_context_uuids: - db_context = self.database.context(db_context_uuid) - json_services.extend(db_context.dump_services()) - - # ----- Compose reply -------------------------------------------------------------------------------------- - reply = ServiceList(cs=json_services) - LOGGER.debug('GetServiceList reply: {}'.format(str(reply))) - GETSERVICELIST_COUNTER_COMPLETED.inc() - return reply - except ServiceException as e: # pragma: no cover (ServiceException not thrown) - LOGGER.exception('GetServiceList exception') - GETSERVICELIST_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover - LOGGER.exception('GetServiceList exception') - GETSERVICELIST_COUNTER_FAILED.inc() - grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) - - @CREATESERVICE_HISTOGRAM_DURATION.time() - def CreateService(self, request : Service, grpc_context : grpc.ServicerContext) -> ServiceId: - CREATESERVICE_COUNTER_STARTED.inc() - try: - LOGGER.debug('CreateService request: {}'.format(str(request))) - - # ----- Validate request data and pre-conditions ----------------------------------------------------------- - context_id, service_id, service_type, service_config, service_state, db_endpoints, constraint_tuples = \ - check_service_request('CreateService', request, self.database, LOGGER) - - # ----- Implement changes in the database ------------------------------------------------------------------ - db_context = self.database.context(context_id) - db_service = db_context.service(service_id) - db_service.create(service_type, service_config, service_state) - - for db_endpoint in db_endpoints: - service_endpoint_id = '{}:{}/{}'.format( - db_endpoint.topology_uuid, db_endpoint.device_uuid, db_endpoint.endpoint_uuid) - db_service.endpoint(service_endpoint_id).create(db_endpoint) - - for cons_type,cons_value in constraint_tuples: db_service.constraint(cons_type).create(cons_value) - - # ----- Compose reply -------------------------------------------------------------------------------------- - reply = ServiceId(**db_service.dump_id()) - LOGGER.debug('CreateService reply: {}'.format(str(reply))) - CREATESERVICE_COUNTER_COMPLETED.inc() - return reply - except ServiceException as e: - LOGGER.exception('CreateService exception') - CREATESERVICE_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover - LOGGER.exception('CreateService exception') - CREATESERVICE_COUNTER_FAILED.inc() - grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) - - @UPDATESERVICE_HISTOGRAM_DURATION.time() - def UpdateService(self, request : Service, grpc_context : grpc.ServicerContext) -> ServiceId: - UPDATESERVICE_COUNTER_STARTED.inc() - try: - LOGGER.debug('UpdateService request: {}'.format(str(request))) - - # ----- Validate request data and pre-conditions ----------------------------------------------------------- - context_id, service_id, service_type, service_config, service_state, db_endpoints, constraint_tuples = \ - check_service_request('UpdateService', request, self.database, LOGGER) - - # ----- Implement changes in the database ------------------------------------------------------------------ - db_context = self.database.context(context_id) - db_service = db_context.service(service_id) - - # Update service attributes - db_service.update(update_attributes={ - 'service_type' : service_type, - 'service_config': service_config, - 'service_state' : service_state, - }) - - # Update service constraints; first add missing, then remove existing, but not added to Service - db_service_constraint_types = set(db_service.constraints.get()) - for constraint_type,constraint_value in constraint_tuples: - if constraint_type in db_service_constraint_types: - db_service.constraint(constraint_type).update(update_attributes={ - 'constraint_value': constraint_value - }) - else: - db_service.constraint(constraint_type).create(constraint_value) - db_service_constraint_types.discard(constraint_type) - - for constraint_type in db_service_constraint_types: - db_service.constraint(constraint_type).delete() - - # Update service endpoints; first add missing, then remove existing, but not added to Service - db_service_endpoint_uuids = set(db_service.endpoints.get()) - for db_endpoint in db_endpoints: - service_endpoint_id = '{}:{}/{}'.format( - db_endpoint.topology_uuid, db_endpoint.device_uuid, db_endpoint.endpoint_uuid) - if service_endpoint_id not in db_service_endpoint_uuids: - db_service.endpoint(service_endpoint_id).create(db_endpoint) - db_service_endpoint_uuids.discard(service_endpoint_id) - - for db_service_endpoint_uuid in db_service_endpoint_uuids: - db_service.endpoint(db_service_endpoint_uuid).delete() - - # ----- Compose reply -------------------------------------------------------------------------------------- - reply = ServiceId(**db_service.dump_id()) - LOGGER.debug('UpdateService reply: {}'.format(str(reply))) - UPDATESERVICE_COUNTER_COMPLETED.inc() - return reply - except ServiceException as e: - LOGGER.exception('UpdateService exception') - UPDATESERVICE_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover - LOGGER.exception('UpdateService exception') - UPDATESERVICE_COUNTER_FAILED.inc() - grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) - - @DELETESERVICE_HISTOGRAM_DURATION.time() - def DeleteService(self, request : ServiceId, grpc_context : grpc.ServicerContext) -> Empty: - DELETESERVICE_COUNTER_STARTED.inc() - try: - LOGGER.debug('DeleteService request: {}'.format(str(request))) - - # ----- Validate request data and pre-conditions ----------------------------------------------------------- - context_id, service_id = check_service_id_request('DeleteService', request, self.database, LOGGER) - - # ----- Implement changes in the database ------------------------------------------------------------------ - db_context = self.database.context(context_id) - db_service = db_context.service(service_id) - db_service.delete() - - # ----- Compose reply -------------------------------------------------------------------------------------- - reply = Empty() - LOGGER.debug('DeleteService reply: {}'.format(str(reply))) - DELETESERVICE_COUNTER_COMPLETED.inc() - return reply - except ServiceException as e: - LOGGER.exception('DeleteService exception') - DELETESERVICE_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover - LOGGER.exception('DeleteService exception') - DELETESERVICE_COUNTER_FAILED.inc() - grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) - - @GETSERVICEBYID_HISTOGRAM_DURATION.time() - def GetServiceById(self, request : ServiceId, grpc_context : grpc.ServicerContext) -> Service: - GETSERVICEBYID_COUNTER_STARTED.inc() - try: - LOGGER.debug('GetServiceById request: {}'.format(str(request))) - - # ----- Validate request data and pre-conditions ----------------------------------------------------------- - context_id, service_id = check_service_id_request('GetServiceById', request, self.database, LOGGER) - - # ----- Retrieve data from the database -------------------------------------------------------------------- - db_context = self.database.context(context_id) - db_service = db_context.service(service_id) - - # ----- Compose reply -------------------------------------------------------------------------------------- - reply = Service(**db_service.dump()) - LOGGER.debug('GetServiceById reply: {}'.format(str(reply))) - GETSERVICEBYID_COUNTER_COMPLETED.inc() - return reply - except ServiceException as e: - LOGGER.exception('GetServiceById exception') - GETSERVICEBYID_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover - LOGGER.exception('GetServiceById exception') - GETSERVICEBYID_COUNTER_FAILED.inc() - grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) - - @GETCONNECTIONLIST_HISTOGRAM_DURATION.time() - def GetConnectionList(self, request : Empty, grpc_context : grpc.ServicerContext) -> ConnectionList: - GETCONNECTIONLIST_COUNTER_STARTED.inc() - try: - LOGGER.debug('GetConnectionList request: {}'.format(str(request))) - - # ----- Validate request data and pre-conditions ----------------------------------------------------------- - - # ----- Retrieve data from the database -------------------------------------------------------------------- - raise ServiceException(grpc.StatusCode.UNIMPLEMENTED, 'RPC GetConnectionList() not implemented') - - # ----- Compose reply -------------------------------------------------------------------------------------- - #reply = ConnectionList() - #LOGGER.debug('GetConnectionList reply: {}'.format(str(reply))) - #GETCONNECTIONLIST_COUNTER_COMPLETED.inc() - #return reply - except ServiceException as e: - LOGGER.exception('GetConnectionList exception') - GETCONNECTIONLIST_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover - LOGGER.exception('GetConnectionList exception') - GETCONNECTIONLIST_COUNTER_FAILED.inc() - grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) diff --git a/src/service/_old_code_backup/Tools.py b/src/service/_old_code_backup/Tools.py deleted file mode 100644 index 62d602c058a55cdd229050cf3ba6301b4519fdb3..0000000000000000000000000000000000000000 --- a/src/service/_old_code_backup/Tools.py +++ /dev/null @@ -1,143 +0,0 @@ -import grpc, logging -from typing import Dict, List, Set, Tuple -from common.Checkers import chk_options, chk_string -from common.database.api.Database import Database -from common.database.api.context.Constants import DEFAULT_TOPOLOGY_ID -from common.database.api.context.topology.device.Endpoint import Endpoint -from common.database.api.context.service.ServiceState import ServiceState, servicestate_enum_values, \ - to_servicestate_enum -from common.database.api.context.service.ServiceType import ServiceType, servicetype_enum_values, to_servicetype_enum -from common.exceptions.ServiceException import ServiceException -from common.tools.service.DeviceCheckers import check_device_endpoint_exists -from common.tools.service.EndpointIdCheckers import check_endpoint_id -from common.tools.service.EnumCheckers import check_enum -from common.tools.service.ServiceCheckers import check_service_exists, check_service_not_exists -from service.proto.context_pb2 import Constraint -from service.proto.service_pb2 import Service, ServiceId - -# For each method name, define acceptable service types. Empty set means accept all. -ACCEPTED_SERVICE_TYPES : Dict[str, Set[ServiceType]] = { - 'CreateService': set([ServiceType.L2NM, ServiceType.L3NM, ServiceType.TAPI_CONNECTIVITY_SERVICE]), - 'UpdateService': set([ServiceType.L2NM, ServiceType.L3NM, ServiceType.TAPI_CONNECTIVITY_SERVICE]), -} - -# For each method name, define acceptable service states. Empty set means accept all. -ACCEPTED_SERVICE_STATES : Dict[str, Set[ServiceState]] = { - 'CreateService': set([ServiceState.PLANNED]), - 'UpdateService': set([ServiceState.PLANNED, ServiceState.ACTIVE, ServiceState.PENDING_REMOVAL]), -} - -def _check_service_exists(method_name : str, database : Database, context_id : str, service_id : str): - if method_name in ['CreateService']: - check_service_not_exists(database, context_id, service_id) - elif method_name in ['UpdateService', 'DeleteService', 'GetServiceById']: - check_service_exists(database, context_id, service_id) - else: # pragma: no cover (test requires malforming the code) - msg = 'Unexpected condition [_check_service_exists(method_name={}, context_id={}, service_id={})]' - msg = msg.format(str(method_name), str(context_id), str(service_id)) - raise ServiceException(grpc.StatusCode.UNIMPLEMENTED, msg) - -def check_service_type(method_name : str, value : str) -> ServiceType: - return check_enum('ServiceType', method_name, value, to_servicetype_enum, ACCEPTED_SERVICE_TYPES) - -def check_service_state(method_name : str, value : str) -> ServiceState: - return check_enum('ServiceState', method_name, value, to_servicestate_enum, ACCEPTED_SERVICE_STATES) - -def check_service_constraint( - logger : logging.Logger, constraint_number : int, parent_name : str, constraint : Constraint, - add_constraints : Dict[str, Dict[str, Set[str]]]) -> Tuple[str, str]: - - try: - constraint_type = chk_string('constraint[#{}].constraint_type'.format(constraint_number), - constraint.constraint_type, - allow_empty=False) - constraint_value = chk_string('constraint[#{}].constraint_value'.format(constraint_number), - constraint.constraint_value, - allow_empty=False) - except Exception as e: - logger.exception('Invalid arguments:') - raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e)) - - if constraint_type in add_constraints: - msg = 'Duplicated ConstraintType({}) in {}.' - msg = msg.format(constraint_type, parent_name) - raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, msg) - - add_constraints[constraint_type] = constraint_value - return constraint_type, constraint_value - -def check_service_request( - method_name : str, request : Service, database : Database, logger : logging.Logger - ) -> Tuple[str, str, ServiceType, str, ServiceState, List[Endpoint], List[Tuple[str, str]]]: - - # ----- Parse attributes ------------------------------------------------------------------------------------------- - try: - context_id = chk_string ('service.cs_id.contextId.contextUuid.uuid', - request.cs_id.contextId.contextUuid.uuid, - allow_empty=False) - service_id = chk_string ('service.cs_id.cs_id.uuid', - request.cs_id.cs_id.uuid, - allow_empty=False) - service_type = chk_options('service.serviceType', - request.serviceType, - servicetype_enum_values()) - service_config = chk_string ('service.serviceConfig.serviceConfig', - request.serviceConfig.serviceConfig, - allow_empty=True) - service_state = chk_options('service.serviceState.serviceState', - request.serviceState.serviceState, - servicestate_enum_values()) - except Exception as e: - logger.exception('Invalid arguments:') - raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e)) - - service_type = check_service_type(method_name, service_type) - service_state = check_service_state(method_name, service_state) - - # ----- Check if service exists in database ------------------------------------------------------------------------ - _check_service_exists(method_name, database, context_id, service_id) - - # ----- Parse constraints ------------------------------------------------------------------------------------------ - add_constraints : Dict[str, str] = {} - constraint_tuples : List[Tuple[str, str]] = [] - for constraint_number,constraint in enumerate(request.constraint): - parent_name = 'Constraint(#{}) of Context({})/Service({})'.format(constraint_number, context_id, service_id) - constraint_type, constraint_value = check_service_constraint( - logger, constraint_number, parent_name, constraint, add_constraints) - constraint_tuples.append((constraint_type, constraint_value)) - - # ----- Parse endpoints and check if they exist in the database as device endpoints -------------------------------- - add_topology_devices_endpoints : Dict[str, Dict[str, Set[str]]] = {} - db_endpoints : List[Endpoint] = [] - for endpoint_number,endpoint_id in enumerate(request.endpointList): - parent_name = 'Endpoint(#{}) of Context({})/Service({})'.format(endpoint_number, context_id, service_id) - - ep_topology_id, ep_device_id, ep_port_id = check_endpoint_id( - logger, endpoint_number, parent_name, endpoint_id, add_topology_devices_endpoints, - predefined_context_id=context_id, acceptable_context_ids=set([context_id])) - - db_endpoint = check_device_endpoint_exists( - database, parent_name, context_id, ep_topology_id, ep_device_id, ep_port_id) - db_endpoints.append(db_endpoint) - - return context_id, service_id, service_type, service_config, service_state, db_endpoints, constraint_tuples - -def check_service_id_request( - method_name : str, request : ServiceId, database : Database, logger : logging.Logger) -> Tuple[str, str]: - - # ----- Parse attributes ------------------------------------------------------------------------------------------- - try: - context_id = chk_string ('service_id.contextId.contextUuid.uuid', - request.contextId.contextUuid.uuid, - allow_empty=False) - service_id = chk_string ('service_id.cs_id.uuid', - request.cs_id.uuid, - allow_empty=False) - except Exception as e: - logger.exception('Invalid arguments:') - raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e)) - - # ----- Check if service exists in database ------------------------------------------------------------------------ - _check_service_exists(method_name, database, context_id, service_id) - - return context_id, service_id diff --git a/src/service/_old_code_backup/test_unitary.py b/src/service/_old_code_backup/test_unitary.py deleted file mode 100644 index fb7d1465d3308261e0f2bdc5bc534f67d89fae1e..0000000000000000000000000000000000000000 --- a/src/service/_old_code_backup/test_unitary.py +++ /dev/null @@ -1,364 +0,0 @@ -import copy, grpc, logging, pytest -from google.protobuf.json_format import MessageToDict -from common.database.Factory import get_database, DatabaseEngineEnum -from common.database.api.Database import Database -from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID -from common.database.tests.script import populate_example -from common.tests.Assertions import validate_empty, validate_service, validate_service_id, \ - validate_service_list_is_empty, validate_service_list_is_not_empty -from service.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD -from service.client.ServiceClient import ServiceClient -from service.proto.context_pb2 import Empty -from service.proto.service_pb2 import Service, ServiceId, ServiceStateEnum, ServiceType -from service.service.ServiceService import ServiceService - -port = 10000 + GRPC_SERVICE_PORT # avoid privileged ports - -LOGGER = logging.getLogger(__name__) -LOGGER.setLevel(logging.DEBUG) - -# use "copy.deepcopy" to prevent propagating forced changes during tests -CONTEXT_ID = {'contextUuid': {'uuid': DEFAULT_CONTEXT_ID}} -TOPOLOGY_ID = {'contextId': copy.deepcopy(CONTEXT_ID), 'topoId': {'uuid': DEFAULT_TOPOLOGY_ID}} -SERVICE_ID = {'contextId': copy.deepcopy(CONTEXT_ID), 'cs_id': {'uuid': 'DEV1'}} -SERVICE = { - 'cs_id': copy.deepcopy(SERVICE_ID), - 'serviceType': ServiceType.L3NM, - 'serviceConfig': {'serviceConfig': '<config/>'}, - 'serviceState': {'serviceState': ServiceStateEnum.PLANNED}, - 'constraint': [ - {'constraint_type': 'latency_ms', 'constraint_value': '100'}, - {'constraint_type': 'hops', 'constraint_value': '5'}, - ], - 'endpointList' : [ - {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV1'}}, 'port_id': {'uuid' : 'EP5'}}, - {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV2'}}, 'port_id': {'uuid' : 'EP5'}}, - {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': {'device_id': {'uuid': 'DEV3'}}, 'port_id': {'uuid' : 'EP5'}}, - ] -} - -@pytest.fixture(scope='session') -def database(): - _database = get_database(engine=DatabaseEngineEnum.INMEMORY) - populate_example(_database, add_services=False) - return _database - -@pytest.fixture(scope='session') -def service_service(database): - _service = ServiceService( - database, port=port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD) - _service.start() - yield _service - _service.stop() - -@pytest.fixture(scope='session') -def service_client(service_service): - _client = ServiceClient(address='127.0.0.1', port=port) - yield _client - _client.close() - -def test_get_services_empty(service_client : ServiceClient): - # should work - validate_service_list_is_empty(MessageToDict( - service_client.GetServiceList(Empty()), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) - -def test_create_service_wrong_service_attributes(service_client : ServiceClient): - # should fail with wrong service context - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['cs_id']['contextId']['contextUuid']['uuid'] = '' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'service.cs_id.contextId.contextUuid.uuid() is out of range: '\ - 'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).' - assert e.value.details() == msg - - # should fail with service context does not exist - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['cs_id']['contextId']['contextUuid']['uuid'] = 'wrong-context' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.NOT_FOUND - msg = 'Context(wrong-context) does not exist in the database.' - assert e.value.details() == msg - - # should fail with wrong service id - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['cs_id']['cs_id']['uuid'] = '' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'service.cs_id.cs_id.uuid() is out of range: '\ - 'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).' - assert e.value.details() == msg - - # should fail with wrong service type - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['serviceType'] = ServiceType.UNKNOWN - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'Method(CreateService) does not accept ServiceType(UNKNOWN). '\ - 'Permitted values for Method(CreateService) are '\ - 'ServiceType([\'L2NM\', \'L3NM\', \'TAPI_CONNECTIVITY_SERVICE\']).' - assert e.value.details() == msg - - # should fail with wrong service state - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['serviceState']['serviceState'] = ServiceStateEnum.PENDING_REMOVAL - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'Method(CreateService) does not accept ServiceState(PENDING_REMOVAL). '\ - 'Permitted values for Method(CreateService) are '\ - 'ServiceState([\'PLANNED\']).' - assert e.value.details() == msg - -def test_create_service_wrong_constraint(service_client : ServiceClient): - # should fail with wrong constraint type - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['constraint'][0]['constraint_type'] = '' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'constraint[#0].constraint_type() is out of range: '\ - 'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).' - assert e.value.details() == msg - - # should fail with wrong constraint value - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['constraint'][0]['constraint_value'] = '' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'constraint[#0].constraint_value() is out of range: '\ - 'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).' - assert e.value.details() == msg - - # should fail with dupplicated constraint type - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['constraint'][1] = copy_service['constraint'][0] - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'Duplicated ConstraintType(latency_ms) in Constraint(#1) of Context(admin)/Service(DEV1).' - assert e.value.details() == msg - -def test_create_service_wrong_endpoint(service_client : ServiceClient, database : Database): - # should fail with wrong endpoint context - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['endpointList'][0]['topoId']['contextId']['contextUuid']['uuid'] = 'wrong-context' - print(copy_service) - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'Context(wrong-context) in Endpoint(#0) of '\ - 'Context(admin)/Service(DEV1) mismatches acceptable Contexts({\'admin\'}). '\ - 'Optionally, leave field empty to use predefined Context(admin).' - assert e.value.details() == msg - - # should fail with wrong endpoint topology - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['endpointList'][0]['topoId']['topoId']['uuid'] = 'wrong-topo' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'Context(admin)/Topology(wrong-topo) in Endpoint(#0) of '\ - 'Context(admin)/Service(DEV1) mismatches acceptable Topologies({\'admin\'}). '\ - 'Optionally, leave field empty to use predefined Topology(admin).' - assert e.value.details() == msg - - # should fail with endpoint device is empty - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['endpointList'][0]['dev_id']['device_id']['uuid'] = '' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'endpoint_id[#0].dev_id.device_id.uuid() is out of range: '\ - 'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).' - assert e.value.details() == msg - - # should fail with endpoint device not found - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['endpointList'][0]['dev_id']['device_id']['uuid'] = 'wrong-device' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.NOT_FOUND - msg = 'Context(admin)/Topology(admin)/Device(wrong-device) in Endpoint(#0) of '\ - 'Context(admin)/Service(DEV1) does not exist in the database.' - assert e.value.details() == msg - - # should fail with endpoint device duplicated - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['endpointList'][1] = copy_service['endpointList'][0] - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'Duplicated Context(admin)/Topology(admin)/Device(DEV1) in Endpoint(#1) of '\ - 'Context(admin)/Service(DEV1).' - assert e.value.details() == msg - - # should fail with endpoint port is empty - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['endpointList'][0]['port_id']['uuid'] = '' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'endpoint_id[#0].port_id.uuid() is out of range: '\ - 'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).' - assert e.value.details() == msg - - # should fail with endpoint port not found - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service = copy.deepcopy(SERVICE) - copy_service['endpointList'][0]['port_id']['uuid'] = 'wrong-port' - service_client.CreateService(Service(**copy_service)) - assert e.value.code() == grpc.StatusCode.NOT_FOUND - msg = 'Context(admin)/Topology(admin)/Device(DEV1)/Port(wrong-port) in Endpoint(#0) of '\ - 'Context(admin)/Service(DEV1) does not exist in the database.' - assert e.value.details() == msg - -def test_get_service_does_not_exist(service_client : ServiceClient): - # should fail with service context does not exist - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service_id = copy.deepcopy(SERVICE_ID) - copy_service_id['contextId']['contextUuid']['uuid'] = 'wrong-context' - service_client.GetServiceById(ServiceId(**copy_service_id)) - assert e.value.code() == grpc.StatusCode.NOT_FOUND - msg = 'Context(wrong-context) does not exist in the database.' - assert e.value.details() == msg - - # should fail with service does not exist - with pytest.raises(grpc._channel._InactiveRpcError) as e: - service_client.GetServiceById(ServiceId(**SERVICE_ID)) - assert e.value.code() == grpc.StatusCode.NOT_FOUND - msg = 'Context(admin)/Service(DEV1) does not exist in the database.' - assert e.value.details() == msg - -def test_update_service_does_not_exist(service_client : ServiceClient): - # should fail with service does not exist - with pytest.raises(grpc._channel._InactiveRpcError) as e: - service_client.UpdateService(Service(**SERVICE)) - assert e.value.code() == grpc.StatusCode.NOT_FOUND - msg = 'Context(admin)/Service(DEV1) does not exist in the database.' - assert e.value.details() == msg - -def test_create_service(service_client : ServiceClient): - # should work - validate_service_id(MessageToDict( - service_client.CreateService(Service(**SERVICE)), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) - -def test_create_service_already_exists(service_client : ServiceClient): - # should fail with service already exists - with pytest.raises(grpc._channel._InactiveRpcError) as e: - service_client.CreateService(Service(**SERVICE)) - assert e.value.code() == grpc.StatusCode.ALREADY_EXISTS - msg = 'Context(admin)/Service(DEV1) already exists in the database.' - assert e.value.details() == msg - -def test_get_service(service_client : ServiceClient): - # should work - validate_service(MessageToDict( - service_client.GetServiceById(ServiceId(**SERVICE_ID)), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) - -def test_update_service(service_client : ServiceClient): - # should work - copy_service = copy.deepcopy(SERVICE) - copy_service['serviceConfig']['serviceConfig'] = '<newconfig/>' - copy_service['serviceState']['serviceState'] = ServiceStateEnum.ACTIVE - copy_service['constraint'] = [ - {'constraint_type': 'latency_ms', 'constraint_value': '200'}, - {'constraint_type': 'bandwidth_gbps', 'constraint_value': '100'}, - ] - copy_service['endpointList'] = [ - { - 'topoId': {'contextId': {'contextUuid': {'uuid': 'admin'}}, 'topoId': {'uuid': 'admin'}}, - 'dev_id': {'device_id': {'uuid': 'DEV1'}}, - 'port_id': {'uuid' : 'EP5'} - }, - { - 'topoId': {'contextId': {'contextUuid': {'uuid': 'admin'}}, 'topoId': {'uuid': 'admin'}}, - 'dev_id': {'device_id': {'uuid': 'DEV2'}}, - 'port_id': {'uuid' : 'EP6'} - }, - ] - validate_service_id(MessageToDict( - service_client.UpdateService(Service(**copy_service)), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) - -def test_delete_service_wrong_service_id(service_client : ServiceClient): - # should fail with service context is empty - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service_id = copy.deepcopy(SERVICE_ID) - copy_service_id['contextId']['contextUuid']['uuid'] = '' - service_client.DeleteService(ServiceId(**copy_service_id)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'service_id.contextId.contextUuid.uuid() is out of range: '\ - 'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).' - assert e.value.details() == msg - - # should fail with service context does not exist - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service_id = copy.deepcopy(SERVICE_ID) - copy_service_id['contextId']['contextUuid']['uuid'] = 'wrong-context' - service_client.DeleteService(ServiceId(**copy_service_id)) - assert e.value.code() == grpc.StatusCode.NOT_FOUND - msg = 'Context(wrong-context) does not exist in the database.' - assert e.value.details() == msg - - # should fail with service id is empty - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service_id = copy.deepcopy(SERVICE_ID) - copy_service_id['cs_id']['uuid'] = '' - service_client.DeleteService(ServiceId(**copy_service_id)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'service_id.cs_id.uuid() is out of range: '\ - 'allow_empty(False) min_length(None) max_length(None) allowed_lengths(None).' - assert e.value.details() == msg - - # should fail with service id is empty - with pytest.raises(grpc._channel._InactiveRpcError) as e: - copy_service_id = copy.deepcopy(SERVICE_ID) - copy_service_id['cs_id']['uuid'] = 'wrong-service' - service_client.DeleteService(ServiceId(**copy_service_id)) - assert e.value.code() == grpc.StatusCode.NOT_FOUND - msg = 'Context(admin)/Service(wrong-service) does not exist in the database.' - assert e.value.details() == msg - -def test_delete_service(service_client : ServiceClient): - # should work - validate_empty(MessageToDict( - service_client.DeleteService(ServiceId(**SERVICE_ID)), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) - -def test_get_services_empty_2(service_client : ServiceClient): - # should work - validate_service_list_is_empty(MessageToDict( - service_client.GetServiceList(Empty()), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) - -def test_create_service_empty_endpoints(service_client : ServiceClient): - # should work - copy_service = copy.deepcopy(SERVICE) - copy_service['endpointList'][0]['topoId']['contextId']['contextUuid']['uuid'] = '' - copy_service['endpointList'][0]['topoId']['topoId']['uuid'] = '' - validate_service_id(MessageToDict( - service_client.CreateService(Service(**copy_service)), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) - -def test_get_services_full(service_client : ServiceClient): - # should work - validate_service_list_is_not_empty(MessageToDict( - service_client.GetServiceList(Empty()), - including_default_value_fields=True, preserving_proto_field_name=True, - use_integers_for_enums=False)) diff --git a/src/service/service/service_handlers/Tools.py b/src/service/service/service_handlers/Tools.py index 4186abf25869f69884ddba0862739713d45bba38..1bd4b8f1f6643e621feff84aceeb401dac120348 100644 --- a/src/service/service/service_handlers/Tools.py +++ b/src/service/service/service_handlers/Tools.py @@ -1,5 +1,4 @@ import json -from copy import deepcopy from typing import Any, Dict, Union from service.proto.context_pb2 import ConfigActionEnum @@ -16,13 +15,24 @@ def config_rule_delete(resource_key : str, resource_value : Union[str, Dict[str, def constraint(constraint_type, constraint_value): return {'constraint_type': str(constraint_type), 'constraint_value': str(constraint_value)} -def endpoint_id(device_id, endpoint_uuid, topology_id=None): - result = {'device_id': deepcopy(device_id), 'endpoint_uuid': {'uuid': endpoint_uuid}} - if topology_id is not None: result['topology_id'] = deepcopy(topology_id) +def context_id(context_uuid): + return {'context_uuid': {'uuid': context_uuid}} + +def topology_id(topology_uuid, context_uuid=None): + result = {'topology_uuid': {'uuid': topology_uuid}} + if context_uuid is not None: result['context_id'] = context_id(context_uuid) + return result + +def device_id(device_uuid): + return {'device_uuid': {'uuid': device_uuid}} + +def endpoint_id(device_uuid, endpoint_uuid, context_uuid=None, topology_uuid=None): + result = {'device_id': device_id(device_uuid), 'endpoint_uuid': {'uuid': endpoint_uuid}} + if topology_id is not None: result['topology_id'] = topology_id(topology_uuid, context_uuid=context_uuid) return result -def endpoint(device_id, endpoint_uuid, endpoint_type, topology_id=None): +def endpoint(device_uuid, endpoint_uuid, endpoint_type, context_uuid=None, topology_uuid=None): return { - 'endpoint_id': endpoint_id(device_id, endpoint_uuid, topology_id=topology_id), + 'endpoint_id': endpoint_id(device_uuid, endpoint_uuid, context_uuid=context_uuid, topology_uuid=topology_uuid), 'endpoint_type': endpoint_type, } diff --git a/src/service/service/service_handlers/__init__.py b/src/service/service/service_handlers/__init__.py index f4359f1fd15b8979a80a3532500546c9099cddef..73777c52ec1459adc398b9e2a6e1ddb53339a911 100644 --- a/src/service/service/service_handlers/__init__.py +++ b/src/service/service/service_handlers/__init__.py @@ -1,5 +1,6 @@ from ..service_handler_api.FilterFields import FilterFieldEnum, ORM_DeviceDriverEnum, ORM_ServiceTypeEnum from .l3nm_emulated.L3NMEmulatedServiceHandler import L3NMEmulatedServiceHandler +from .l3nm_openconfig.L3NMOpenConfigServiceHandler import L3NMOpenConfigServiceHandler SERVICE_HANDLERS = [ (L3NMEmulatedServiceHandler, [ @@ -8,4 +9,10 @@ SERVICE_HANDLERS = [ FilterFieldEnum.DEVICE_DRIVER : ORM_DeviceDriverEnum.UNDEFINED, } ]), + (L3NMOpenConfigServiceHandler, [ + { + FilterFieldEnum.SERVICE_TYPE : ORM_ServiceTypeEnum.L3NM, + FilterFieldEnum.DEVICE_DRIVER : ORM_DeviceDriverEnum.OPENCONFIG, + } + ]), ] diff --git a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py new file mode 100644 index 0000000000000000000000000000000000000000..eb1914fc301cfd759cf55910634f8023eec442ce --- /dev/null +++ b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py @@ -0,0 +1,209 @@ +import anytree, json, logging +from typing import Any, Dict, List, Optional, Tuple, Union +from common.orm.Database import Database +from common.orm.HighLevel import get_object +from common.orm.backend.Tools import key_to_str +from common.type_checkers.Checkers import chk_length, chk_type +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from device.proto.context_pb2 import Device +from service.service.database.ConfigModel import ORM_ConfigActionEnum, get_config_rules +from service.service.database.ContextModel import ContextModel +from service.service.database.DeviceModel import DeviceModel +from service.service.database.ServiceModel import ServiceModel +from service.service.service_handler_api._ServiceHandler import _ServiceHandler +from service.service.service_handler_api.AnyTreeTools import TreeNode, delete_subnode, get_subnode, set_subnode_value +from service.service.service_handlers.Tools import config_rule_set, config_rule_delete + +LOGGER = logging.getLogger(__name__) + +class L3NMOpenConfigServiceHandler(_ServiceHandler): + def __init__( # pylint: disable=super-init-not-called + self, db_service : ServiceModel, database : Database, context_client : ContextClient, + device_client : DeviceClient, **settings + ) -> None: + self.__db_service = db_service + self.__database = database + self.__context_client = context_client # pylint: disable=unused-private-member + self.__device_client = device_client + + self.__db_context : ContextModel = get_object(self.__database, ContextModel, self.__db_service.context_fk) + str_service_key = key_to_str([self.__db_context.context_uuid, self.__db_service.service_uuid]) + db_config = get_config_rules(self.__database, str_service_key, 'running') + self.__resolver = anytree.Resolver(pathattr='name') + self.__config = TreeNode('.') + for action, resource_key, resource_value in db_config: + if action == ORM_ConfigActionEnum.SET: + try: + resource_value = json.loads(resource_value) + except: # pylint: disable=bare-except + pass + set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) + elif action == ORM_ConfigActionEnum.DELETE: + delete_subnode(self.__resolver, self.__config, resource_key) + + def SetEndpoint(self, endpoints : List[Tuple[str, str, Optional[str]]]) -> List[Union[bool, Exception]]: + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + service_uuid = self.__db_service.service_uuid + network_instance_name = '{:s}-NetInst'.format(service_uuid) + network_interface_name = '{:s}-NetIf'.format(service_uuid) + network_subinterface_name = '{:s}-NetSubIf'.format(service_uuid) + + settings : TreeNode = get_subnode(self.__resolver, self.__config, 'settings', None) + if settings is None: raise Exception('Unable to retrieve service settings') + json_settings : Dict = settings.value + route_distinguisher = json_settings.get('route_distinguisher', '0:0') # '60001:801' + mtu = json_settings.get('mtu', 1450 ) # 1512 + address_families = json_settings.get('address_families', [] ) # ['IPV4'] + + results = [] + for endpoint in endpoints: + try: + chk_type('endpoint', endpoint, (tuple, list)) + chk_length('endpoint', endpoint, min_length=2, max_length=3) + if len(endpoint) == 2: + device_uuid, endpoint_uuid = endpoint + else: + device_uuid, endpoint_uuid, _ = endpoint # ignore topology_uuid by now + + endpoint_settings_uri = 'device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) + endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) + if endpoint_settings is None: + raise Exception('Unable to retrieve service settings for endpoint({:s})'.format( + str(endpoint_settings_uri))) + json_endpoint_settings : Dict = endpoint_settings.value + router_id = json_endpoint_settings.get('router_id', '0.0.0.0') # '10.95.0.10' + sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 + + db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True) + json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True) + json_device_config : Dict = json_device.setdefault('device_config', {}) + json_device_config_rules : List = json_device_config.setdefault('config_rules', []) + json_device_config_rules.extend([ + config_rule_set( + '/network_instance[{:s}]'.format(network_instance_name), { + 'name': network_instance_name, 'type': 'L3VRF', 'router_id': router_id, + 'route_distinguisher': route_distinguisher, 'address_families': address_families, + }), + config_rule_set( + '/interface[{:s}]'.format(endpoint_uuid), { + 'name': endpoint_uuid, 'description': network_interface_name, 'mtu': mtu, + }), + config_rule_set( + '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), { + 'name': endpoint_uuid, 'index': sub_interface_index, + 'description': network_subinterface_name, 'mtu': mtu, + }), + config_rule_set( + '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, endpoint_uuid), { + 'name': network_instance_name, 'id': endpoint_uuid, + }), + ]) + self.__device_client.ConfigureDevice(Device(**json_device)) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + def DeleteEndpoint(self, endpoints : List[Tuple[str, str, Optional[str]]]) -> List[Union[bool, Exception]]: + chk_type('endpoints', endpoints, list) + if len(endpoints) == 0: return [] + + service_uuid = self.__db_service.service_uuid + network_instance_name = '{:s}-NetInst'.format(service_uuid) + + results = [] + for endpoint in endpoints: + try: + chk_type('endpoint', endpoint, (tuple, list)) + chk_length('endpoint', endpoint, min_length=2, max_length=3) + if len(endpoint) == 2: + device_uuid, endpoint_uuid = endpoint + else: + device_uuid, endpoint_uuid, _ = endpoint # ignore topology_uuid by now + + endpoint_settings_uri = 'device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) + endpoint_settings : TreeNode = get_subnode(self.__resolver, self.__config, endpoint_settings_uri, None) + if endpoint_settings is None: + raise Exception('Unable to retrieve service settings for endpoint({:s})'.format( + str(endpoint_settings_uri))) + json_endpoint_settings : Dict = endpoint_settings.value + sub_interface_index = json_endpoint_settings.get('sub_interface_index', 0 ) # 1 + + db_device : DeviceModel = get_object(self.__database, DeviceModel, device_uuid, raise_if_not_found=True) + json_device = db_device.dump(include_config_rules=False, include_drivers=True, include_endpoints=True) + json_device_config : Dict = json_device.setdefault('device_config', {}) + json_device_config_rules : List = json_device_config.setdefault('config_rules', []) + json_device_config_rules.extend([ + config_rule_delete( + '/network_instance[{:s}]/interface[{:s}]'.format(network_instance_name, endpoint_uuid), { + 'name': network_instance_name, 'id': endpoint_uuid + }), + config_rule_delete( + '/interface[{:s}]/subinterface[{:d}]'.format(endpoint_uuid, sub_interface_index), { + 'name': endpoint_uuid, 'index': sub_interface_index, + }), + config_rule_delete( + '/network_instance[{:s}]'.format(network_instance_name), { + 'name': network_instance_name + }), + ]) + self.__device_client.ConfigureDevice(Device(**json_device)) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteEndpoint({:s})'.format(str(endpoint))) + results.append(e) + + return results + + def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[SetConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('constraints', constraints, list) + if len(constraints) == 0: return [] + + msg = '[DeleteConstraint] Method not implemented. Constraints({:s}) are being ignored.' + LOGGER.warning(msg.format(str(constraints))) + return [True for _ in range(len(constraints))] + + def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + resource_key, resource_value = resource + resource_value = json.loads(resource_value) + set_subnode_value(self.__resolver, self.__config, resource_key, resource_value) + results.append(True) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to SetConfig({:s})'.format(str(resource))) + results.append(e) + + return results + + def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: + chk_type('resources', resources, list) + if len(resources) == 0: return [] + + results = [] + for resource in resources: + try: + resource_key, _ = resource + delete_subnode(self.__resolver, self.__config, resource_key) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Unable to DeleteConfig({:s})'.format(str(resource))) + results.append(e) + + return results diff --git a/src/service/service/service_handlers/l3nm_openconfig/__init__.py b/src/service/service/service_handlers/l3nm_openconfig/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/service/tests/.gitignore b/src/service/tests/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f2f83148dc3aa48945967df7297805c06296dde9 --- /dev/null +++ b/src/service/tests/.gitignore @@ -0,0 +1,2 @@ +# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc. +ServiceHandler_L3NM_OC.py diff --git a/src/service/tests/CommonObjects.py b/src/service/tests/CommonObjects.py index b704bad95e09f9bcfa2970359cc377925000f72c..2b51dcd2322e70dff9a5229e65e6a220708a834f 100644 --- a/src/service/tests/CommonObjects.py +++ b/src/service/tests/CommonObjects.py @@ -1,8 +1,5 @@ -import operator from copy import deepcopy from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID -from service.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum -from service.service.service_handlers.Tools import config_rule_set # use "deepcopy" to prevent propagating forced changes during tests @@ -22,41 +19,3 @@ TOPOLOGY = { 'device_ids': [], 'link_ids': [], } - -DEVICE_EMU1_UUID = 'EMU-1' -DEVICE_EMU1_ID = {'device_uuid': {'uuid': DEVICE_EMU1_UUID}} -DEVICE_EMU1 = { - 'device_id': deepcopy(DEVICE_EMU1_ID), - 'device_type': 'emulated', - 'device_config': {'config_rules': []}, - 'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED, - 'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED], - 'device_endpoints': [], -} - -DEVICE_EMU2_UUID = 'EMU-2' -DEVICE_EMU2_ID = {'device_uuid': {'uuid': DEVICE_EMU2_UUID}} -DEVICE_EMU2 = { - 'device_id': deepcopy(DEVICE_EMU2_ID), - 'device_type': 'emulated', - 'device_config': {'config_rules': []}, - 'device_operational_status': DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED, - 'device_drivers': [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED], - 'device_endpoints': [], -} - -DEVICE_EMU_ENDPOINTS = [] -for endpoint_uuid in ['EP1', 'EP2', 'EP3', 'EP100']: - DEVICE_EMU_ENDPOINTS.append((endpoint_uuid, '10Gbps', [])) - -DEVICE_EMU_CONNECT_RULES = [ - config_rule_set('_connect/address', '127.0.0.1'), - config_rule_set('_connect/port', '0'), - config_rule_set('_connect/settings', {'endpoints': [ - { - 'uuid': endpoint_uuid, 'type': endpoint_type, - 'sample_types': list(map(operator.attrgetter('value'), endpoint_sample_types)), - } - for endpoint_uuid,endpoint_type,endpoint_sample_types in DEVICE_EMU_ENDPOINTS - ]}), -] diff --git a/src/service/tests/ServiceHandler_L3NM_EMU.py b/src/service/tests/ServiceHandler_L3NM_EMU.py new file mode 100644 index 0000000000000000000000000000000000000000..c01fac5e3ea10490bced5155e693817f6ddea41b --- /dev/null +++ b/src/service/tests/ServiceHandler_L3NM_EMU.py @@ -0,0 +1,106 @@ +from copy import deepcopy +from service.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum, ServiceStatusEnum, ServiceTypeEnum +from service.service.service_handlers.Tools import config_rule_set, constraint, device_id, endpoint_id +from .CommonObjects import CONTEXT_ID + +# use "deepcopy" to prevent propagating forced changes during tests + +SERVICE_HANDLER_NAME = 'l3nm_emulated' + +SERVICE_UUID = 'SVC_L3NM_EMU' + +SERVICE_ID = { + 'context_id': deepcopy(CONTEXT_ID), + 'service_uuid': {'uuid': SERVICE_UUID} +} + +SERVICE_DESCRIPTOR = { + 'service_id': deepcopy(SERVICE_ID), + 'service_type': ServiceTypeEnum.SERVICETYPE_L3NM, + 'service_endpoint_ids' : [], + 'service_constraints': [], + 'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED}, + 'service_config': {'config_rules': []}, +} + +DEVICE1_UUID = 'EMULATED-ROUTER-1' +DEVICE2_UUID = 'EMULATED-ROUTER-2' + +DEVICE_ATTRIBUTES = { # device_uuid => {device_settings} + DEVICE1_UUID: { + 'type' : 'emulated', + 'address' : '10.95.86.155', + 'port' : '830', + 'drivers' : [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED], + 'endpoint': 'EP100', + }, + DEVICE2_UUID: { + 'type' : 'emulated', + 'address' : '10.96.86.149', + 'port' : '830', + 'drivers' : [DeviceDriverEnum.DEVICEDRIVER_UNDEFINED], + 'endpoint': 'EP100', + }, +} + +SERVICE_DEVICE_UUIDS = [DEVICE1_UUID, DEVICE2_UUID] + +SERVICE_ENDPOINT_IDS = [ + endpoint_id(device_uuid, DEVICE_ATTRIBUTES[device_uuid]['endpoint']) + for device_uuid in SERVICE_DEVICE_UUIDS +] + +SERVICE_CONFIG_RULES = [ + config_rule_set( + 'settings', { + 'route_distinguisher': '60001:801', 'mtu': 1512, 'address_families': ['IPV4'] + }), + config_rule_set( + 'device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE1_UUID, DEVICE_ATTRIBUTES[DEVICE1_UUID]['endpoint']), { + 'router_id': '10.0.0.1', 'sub_interface_index': 1, + }), + config_rule_set( + 'device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE2_UUID, DEVICE_ATTRIBUTES[DEVICE2_UUID]['endpoint']), { + 'router_id': '10.0.0.2', 'sub_interface_index': 1, + }), +] + +SERVICE_CONSTRAINTS = [ + constraint('latency_ms', 15.2), + constraint('jitter_us', 1.2), +] + +def get_device_descriptor(device_uuid, enabled=True): + device_operational_status = ( + DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED \ + if enabled else \ + DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED) + return { + 'device_id': device_id(device_uuid), + 'device_type': DEVICE_ATTRIBUTES[device_uuid]['type'], + 'device_config': {'config_rules': []}, + 'device_operational_status': device_operational_status, + 'device_drivers': DEVICE_ATTRIBUTES[device_uuid]['drivers'], + 'device_endpoints': [], + } + +def get_connect_rules(device_uuid): + return [ + config_rule_set('_connect/address', '127.0.0.1'), + config_rule_set('_connect/port', '0'), + config_rule_set('_connect/settings', {'endpoints': [ + {'uuid': endpoint_uuid, 'type': '10Gbps', 'sample_types': []} + for endpoint_uuid in ['EP1', 'EP2', 'EP3', 'EP100'] + ]}), + ] + +TEST_SERVICE_HANDLER = (SERVICE_HANDLER_NAME, { + 'service_id' : SERVICE_ID, + 'service_descriptor' : SERVICE_DESCRIPTOR, + 'service_endpoint_ids' : SERVICE_ENDPOINT_IDS, + 'service_config_rules' : SERVICE_CONFIG_RULES, + 'service_constraints' : SERVICE_CONSTRAINTS, + 'service_device_uuids' : SERVICE_DEVICE_UUIDS, + 'get_device_descriptor' : get_device_descriptor, + 'get_connect_rules' : get_connect_rules, +}) diff --git a/src/service/tests/ServiceHandlersToTest.py b/src/service/tests/ServiceHandlersToTest.py new file mode 100644 index 0000000000000000000000000000000000000000..484ae3e45491f31a065ad27b49f86e6338af7a51 --- /dev/null +++ b/src/service/tests/ServiceHandlersToTest.py @@ -0,0 +1,15 @@ +# Add/comment in this file the service handlers to be tested by the unitry tests. + +SERVICE_HANDLERS_TO_TEST = [] + +try: + from service.tests.ServiceHandler_L3NM_EMU import TEST_SERVICE_HANDLER + SERVICE_HANDLERS_TO_TEST.append(TEST_SERVICE_HANDLER) +except ImportError: + pass + +try: + from service.tests.ServiceHandler_L3NM_OC import TEST_SERVICE_HANDLER + SERVICE_HANDLERS_TO_TEST.append(TEST_SERVICE_HANDLER) +except ImportError: + pass diff --git a/src/service/tests/Service_L3NM_EMU.py b/src/service/tests/Service_L3NM_EMU.py deleted file mode 100644 index 8cd3e9a1b8ed7d9fcdb7bf30744faa6c301592ec..0000000000000000000000000000000000000000 --- a/src/service/tests/Service_L3NM_EMU.py +++ /dev/null @@ -1,50 +0,0 @@ -from copy import deepcopy -from service.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum -from .CommonObjects import CONTEXT_ID -from service.service.service_handlers.Tools import config_rule_set, constraint, endpoint_id - -# use "deepcopy" to prevent propagating forced changes during tests - -SERVICE_L3NM_EMU_UUID = 'SVC_L3NM_EMU' -SERVICE_L3NM_EMU_ID = { - 'context_id': deepcopy(CONTEXT_ID), - 'service_uuid': {'uuid': SERVICE_L3NM_EMU_UUID} -} -SERVICE_L3NM_EMU = { - 'service_id': deepcopy(SERVICE_L3NM_EMU_ID), - 'service_type': ServiceTypeEnum.SERVICETYPE_L3NM, - 'service_endpoint_ids' : [], - 'service_constraints': [], - 'service_status': {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED}, - 'service_config': {'config_rules': []}, -} - -DEVICE1_UUID = 'EMU-1' -DEVICE1_ID = {'device_uuid': {'uuid': DEVICE1_UUID}} -DEVICE1_ENDPOINT_UUID = 'EP100' - -DEVICE2_UUID = 'EMU-2' -DEVICE2_ID = {'device_uuid': {'uuid': DEVICE2_UUID}} -DEVICE2_ENDPOINT_UUID = 'EP100' - -SERVICE_L3NM_EMU_ENDPOINT_IDS = [ - endpoint_id(DEVICE1_ID, DEVICE1_ENDPOINT_UUID), - endpoint_id(DEVICE2_ID, DEVICE2_ENDPOINT_UUID), -] - -SERVICE_L3NM_EMU_CONSTRAINTS = [ - constraint('latency_ms', 15.2), - constraint('jitter_us', 1.2), -] - -SERVICE_L3NM_EMU_CONFIG_RULES = [ - config_rule_set('settings', { - 'route_distinguisher': '60001:801', 'mtu': 1512, 'address_families': ['IPV4'] - }), - config_rule_set('device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE1_UUID, DEVICE1_ENDPOINT_UUID), { - 'router_id': '10.0.0.1', 'sub_interface_index': 1, - }), - config_rule_set('device[{:s}]/endpoint[{:s}]/settings'.format(DEVICE2_UUID, DEVICE2_ENDPOINT_UUID), { - 'router_id': '10.0.0.2', 'sub_interface_index': 1, - }), -] diff --git a/src/service/tests/test_unitary.py b/src/service/tests/test_unitary.py index 5489ee2a59545b31c2d9ff44956a0c67414a4b24..e03f2017ad2acaf91f6e4186fc7c502f08b7ec0c 100644 --- a/src/service/tests/test_unitary.py +++ b/src/service/tests/test_unitary.py @@ -1,10 +1,11 @@ -import copy, grpc, logging, operator, os, pytest +import copy, grpc, logging, os, pytest from typing import Tuple from google.protobuf.json_format import MessageToDict from common.orm.Database import Database from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBackendEnum from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum from common.message_broker.MessageBroker import MessageBroker +from common.tests.PytestGenerateTests import pytest_generate_tests # (required) pylint: disable=unused-import from context.Config import ( GRPC_SERVICE_PORT as CONTEXT_GRPC_SERVICE_PORT, GRPC_MAX_WORKERS as CONTEXT_GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD as CONTEXT_GRPC_GRACE_PERIOD) @@ -32,11 +33,7 @@ from service.proto.context_pb2 import Service, ServiceId from service.service.ServiceService import ServiceService from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory from service.service.service_handlers import SERVICE_HANDLERS -from service.tests.Service_L3NM_EMU import ( - SERVICE_L3NM_EMU, SERVICE_L3NM_EMU_CONFIG_RULES, SERVICE_L3NM_EMU_CONSTRAINTS, SERVICE_L3NM_EMU_ENDPOINT_IDS, - SERVICE_L3NM_EMU_ID) -#from device.service.MonitoringLoops import MonitoringLoops -from .CommonObjects import CONTEXT, DEVICE_EMU1, DEVICE_EMU2, DEVICE_EMU_CONNECT_RULES, TOPOLOGY +from .CommonObjects import CONTEXT, TOPOLOGY LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) @@ -144,126 +141,130 @@ def grpc_message_to_json_string(message): return str(MessageToDict( message, including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False)) - -def test_prepare_environment( - context_client : ContextClient, device_client : DeviceClient): # pylint: disable=redefined-outer-name - - context_client.SetContext(Context(**CONTEXT)) - context_client.SetTopology(Topology(**TOPOLOGY)) - - DEVICE_EMU1_WITH_CONNECT_RULES = copy.deepcopy(DEVICE_EMU1) - DEVICE_EMU1_WITH_CONNECT_RULES['device_config']['config_rules'].extend(DEVICE_EMU_CONNECT_RULES) - device_client.AddDevice(Device(**DEVICE_EMU1_WITH_CONNECT_RULES)) - - DEVICE_EMU2_WITH_CONNECT_RULES = copy.deepcopy(DEVICE_EMU2) - DEVICE_EMU2_WITH_CONNECT_RULES['device_config']['config_rules'].extend(DEVICE_EMU_CONNECT_RULES) - device_client.AddDevice(Device(**DEVICE_EMU2_WITH_CONNECT_RULES)) - - -def test_service_create_error_cases( - context_client : ContextClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient, # pylint: disable=redefined-outer-name - service_service : ServiceService): # pylint: disable=redefined-outer-name - - with pytest.raises(grpc.RpcError) as e: - SERVICE_WITH_ENDPOINTS = copy.deepcopy(SERVICE_L3NM_EMU) - SERVICE_WITH_ENDPOINTS['service_endpoint_ids'].extend(SERVICE_L3NM_EMU_ENDPOINT_IDS) - service_client.CreateService(Service(**SERVICE_WITH_ENDPOINTS)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg_head = 'service.service_endpoint_ids([' - msg_tail = ']) is invalid; RPC method CreateService does not accept Endpoints. '\ - 'Endpoints should be configured after creating the service.' - except_msg = str(e.value.details()) - assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) - - with pytest.raises(grpc.RpcError) as e: - SERVICE_WITH_CONFIG_RULES = copy.deepcopy(SERVICE_L3NM_EMU) - SERVICE_WITH_CONFIG_RULES['service_config']['config_rules'].extend(SERVICE_L3NM_EMU_CONFIG_RULES) - service_client.CreateService(Service(**SERVICE_WITH_CONFIG_RULES)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg_head = 'service.service_config.config_rules([' - msg_tail = ']) is invalid; RPC method CreateService does not accept Config Rules. '\ - 'Config Rules should be configured after creating the service.' - except_msg = str(e.value.details()) - assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) - - with pytest.raises(grpc.RpcError) as e: - SERVICE_WITH_CONSTRAINTS = copy.deepcopy(SERVICE_L3NM_EMU) - SERVICE_WITH_CONSTRAINTS['service_constraints'].extend(SERVICE_L3NM_EMU_CONSTRAINTS) - service_client.CreateService(Service(**SERVICE_WITH_CONSTRAINTS)) - assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg_head = 'service.service_constraints([' - msg_tail = ']) is invalid; RPC method CreateService does not accept Constraints. '\ - 'Constraints should be configured after creating the service.' - except_msg = str(e.value.details()) - assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) - - -def test_service_create_correct( - context_client : ContextClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient, # pylint: disable=redefined-outer-name - service_service : ServiceService): # pylint: disable=redefined-outer-name - - service_client.CreateService(Service(**SERVICE_L3NM_EMU)) - #driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID) # we know the driver exists now - #assert driver is not None - - -def test_service_get_created( - context_client : ContextClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient, # pylint: disable=redefined-outer-name - service_service : ServiceService): # pylint: disable=redefined-outer-name - - service_data = context_client.GetService(ServiceId(**SERVICE_L3NM_EMU_ID)) - LOGGER.info('service_data = {:s}'.format(grpc_message_to_json_string(service_data))) - - -def test_service_update( - context_client : ContextClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient, # pylint: disable=redefined-outer-name - service_service : ServiceService): # pylint: disable=redefined-outer-name - - # Configure - SERVICE_WITH_SETTINGS = copy.deepcopy(SERVICE_L3NM_EMU) - SERVICE_WITH_SETTINGS['service_endpoint_ids'].extend(SERVICE_L3NM_EMU_ENDPOINT_IDS) - SERVICE_WITH_SETTINGS['service_config']['config_rules'].extend(SERVICE_L3NM_EMU_CONFIG_RULES) - SERVICE_WITH_SETTINGS['service_constraints'].extend(SERVICE_L3NM_EMU_CONSTRAINTS) - service_client.UpdateService(Service(**SERVICE_WITH_SETTINGS)) - - for endpoint_id in SERVICE_L3NM_EMU_ENDPOINT_IDS: - device_id = endpoint_id['device_id'] - device_data = context_client.GetDevice(DeviceId(**device_id)) - for i,config_rule in enumerate(device_data.device_config.config_rules): - LOGGER.info('device_data[{:s}][#{:d}] => {:s}'.format( - str(device_id), i, grpc_message_to_json_string(config_rule))) - - # Deconfigure - SERVICE_WITH_SETTINGS = copy.deepcopy(SERVICE_L3NM_EMU) - SERVICE_WITH_SETTINGS['service_endpoint_ids'].extend([]) # remove endpoints - service_client.UpdateService(Service(**SERVICE_WITH_SETTINGS)) - - for endpoint_id in SERVICE_L3NM_EMU_ENDPOINT_IDS: - device_id = endpoint_id['device_id'] - device_data = context_client.GetDevice(DeviceId(**device_id)) - for i,config_rule in enumerate(device_data.device_config.config_rules): - LOGGER.info('device_data[{:s}][#{:d}] => {:s}'.format( - str(device_id), i, grpc_message_to_json_string(config_rule))) - - -def test_service_get_updated( - context_client : ContextClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient, # pylint: disable=redefined-outer-name - service_service : ServiceService): # pylint: disable=redefined-outer-name - - service_data = context_client.GetService(ServiceId(**SERVICE_L3NM_EMU_ID)) - LOGGER.info('service_data = {:s}'.format(grpc_message_to_json_string(service_data))) - - -def test_service_delete( - context_client : ContextClient, # pylint: disable=redefined-outer-name - service_client : ServiceClient, # pylint: disable=redefined-outer-name - service_service : ServiceService): # pylint: disable=redefined-outer-name - - service_client.DeleteService(ServiceId(**SERVICE_L3NM_EMU_ID)) - #driver : _Driver = device_service.driver_instance_cache.get(DEVICE_EMU_UUID, {}) - #assert driver is None +try: + from .ServiceHandlersToTest import SERVICE_HANDLERS_TO_TEST +except ImportError: + LOGGER.exception('Unable to load service handlers, nothing will be tested.') + SERVICE_HANDLERS_TO_TEST = [] + +class TestServiceHandlers: + scenarios = SERVICE_HANDLERS_TO_TEST + + def test_prepare_environment( + self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, + service_device_uuids, get_device_descriptor, get_connect_rules, + context_client : ContextClient, # pylint: disable=redefined-outer-name + device_client : DeviceClient): # pylint: disable=redefined-outer-name + + context_client.SetContext(Context(**CONTEXT)) + context_client.SetTopology(Topology(**TOPOLOGY)) + + for device_uuid in service_device_uuids: + device_with_connect_rules = copy.deepcopy(get_device_descriptor(device_uuid)) + device_with_connect_rules['device_config']['config_rules'].extend(get_connect_rules(device_uuid)) + device_client.AddDevice(Device(**device_with_connect_rules)) + + def test_service_create_error_cases( + self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, + service_device_uuids, get_device_descriptor, get_connect_rules, + service_client : ServiceClient): # pylint: disable=redefined-outer-name + + with pytest.raises(grpc.RpcError) as e: + service_with_endpoints = copy.deepcopy(service_descriptor) + service_with_endpoints['service_endpoint_ids'].extend(service_endpoint_ids) + service_client.CreateService(Service(**service_with_endpoints)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg_head = 'service.service_endpoint_ids([' + msg_tail = ']) is invalid; RPC method CreateService does not accept Endpoints. '\ + 'Endpoints should be configured after creating the service.' + except_msg = str(e.value.details()) + assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) + + with pytest.raises(grpc.RpcError) as e: + service_with_config_rules = copy.deepcopy(service_descriptor) + service_with_config_rules['service_config']['config_rules'].extend(service_config_rules) + service_client.CreateService(Service(**service_with_config_rules)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg_head = 'service.service_config.config_rules([' + msg_tail = ']) is invalid; RPC method CreateService does not accept Config Rules. '\ + 'Config Rules should be configured after creating the service.' + except_msg = str(e.value.details()) + assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) + + with pytest.raises(grpc.RpcError) as e: + service_with_constraints = copy.deepcopy(service_descriptor) + service_with_constraints['service_constraints'].extend(service_constraints) + service_client.CreateService(Service(**service_with_constraints)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg_head = 'service.service_constraints([' + msg_tail = ']) is invalid; RPC method CreateService does not accept Constraints. '\ + 'Constraints should be configured after creating the service.' + except_msg = str(e.value.details()) + assert except_msg.startswith(msg_head) and except_msg.endswith(msg_tail) + + + def test_service_create_correct( + self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, + service_device_uuids, get_device_descriptor, get_connect_rules, + service_client : ServiceClient): # pylint: disable=redefined-outer-name + + service_client.CreateService(Service(**service_descriptor)) + + + def test_service_get_created( + self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, + service_device_uuids, get_device_descriptor, get_connect_rules, + context_client : ContextClient): # pylint: disable=redefined-outer-name + + service_data = context_client.GetService(ServiceId(**service_id)) + LOGGER.info('service_data = {:s}'.format(grpc_message_to_json_string(service_data))) + + + def test_service_update( + self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, + service_device_uuids, get_device_descriptor, get_connect_rules, + context_client : ContextClient, # pylint: disable=redefined-outer-name + service_client : ServiceClient): # pylint: disable=redefined-outer-name + + # Configure + service_with_settings = copy.deepcopy(service_descriptor) + service_with_settings['service_endpoint_ids'].extend(service_endpoint_ids) + service_with_settings['service_config']['config_rules'].extend(service_config_rules) + service_with_settings['service_constraints'].extend(service_constraints) + service_client.UpdateService(Service(**service_with_settings)) + + for endpoint_id in service_endpoint_ids: + device_id = endpoint_id['device_id'] + device_data = context_client.GetDevice(DeviceId(**device_id)) + for i,config_rule in enumerate(device_data.device_config.config_rules): + LOGGER.info('device_data[{:s}][#{:d}] => {:s}'.format( + str(device_id), i, grpc_message_to_json_string(config_rule))) + + # Deconfigure + service_with_settings = copy.deepcopy(service_descriptor) + service_with_settings['service_endpoint_ids'].extend([]) # remove endpoints + service_client.UpdateService(Service(**service_with_settings)) + + for endpoint_id in service_endpoint_ids: + device_id = endpoint_id['device_id'] + device_data = context_client.GetDevice(DeviceId(**device_id)) + for i,config_rule in enumerate(device_data.device_config.config_rules): + LOGGER.info('device_data[{:s}][#{:d}] => {:s}'.format( + str(device_id), i, grpc_message_to_json_string(config_rule))) + + + def test_service_get_updated( + self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, + service_device_uuids, get_device_descriptor, get_connect_rules, + context_client : ContextClient): # pylint: disable=redefined-outer-name + + service_data = context_client.GetService(ServiceId(**service_id)) + LOGGER.info('service_data = {:s}'.format(grpc_message_to_json_string(service_data))) + + + def test_service_delete( + self, service_id, service_descriptor, service_endpoint_ids, service_config_rules, service_constraints, + service_device_uuids, get_device_descriptor, get_connect_rules, + service_client : ServiceClient): # pylint: disable=redefined-outer-name + + service_client.DeleteService(ServiceId(**service_id))