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.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 LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) SERVICE_ID = { 'contextId': {'contextUuid': {'uuid': 'admin'}}, 'cs_id': {'uuid': 'DEV1'}, } SERVICE = { 'cs_id': SERVICE_ID, 'serviceType': ServiceType.L3NM, 'serviceConfig': {'serviceConfig': ''}, 'serviceState': {'serviceState': ServiceStateEnum.PLANNED}, 'constraint': [ {'constraint_type': 'latency_ms', 'constraint_value': '100'}, {'constraint_type': 'hops', 'constraint_value': '5'}, ], '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' : 'EP5'} }, { 'topoId': { 'contextId': {'contextUuid': {'uuid': 'admin'}}, 'topoId': {'uuid': 'admin'} }, '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=GRPC_SERVICE_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=GRPC_SERVICE_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() string is empty.' 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() string is empty.' 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 = ' '.join([ '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 = ' '.join([ '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() string is empty.' 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() string is empty.' 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): # 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' service_client.CreateService(Service(**copy_service)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT msg = ' '.join([ '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 endpoint topology not found 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 = ' '.join([ '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() string is empty.' 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 = ' '.join([ '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() string is empty.' 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 = ' '.join([ '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 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'] = '' 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 wrong service id / context 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() string is empty.' assert e.value.details() == msg 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() string is empty.' 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))