import copy, grpc, logging, pytest from src.common.database.api.context.OperationalStatus import OperationalStatus from google.protobuf.json_format import MessageToDict from common.database.Factory import get_database, DatabaseEngineEnum from common.tests.Assertions import validate_device_id, validate_empty from device.client.DeviceClient import DeviceClient from device.proto.context_pb2 import Device, DeviceId from device.service.DeviceService import DeviceService from device.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) DEVICE_ID = {'device_id': {'uuid': 'test-device-001'}} DEVICE = { 'device_id': {'device_id': {'uuid': 'test-device-001'}}, 'device_type': 'ROADM', 'device_config': {'device_config': ''}, 'devOperationalStatus': 1, 'endpointList' : [ { 'port_id': { 'topoId': { 'contextId': {'contextUuid': {'uuid': 'admin'}}, 'topoId': {'uuid': 'admin'} }, 'dev_id': {'device_id': {'uuid': 'test-device-001'}}, 'port_id': {'uuid' : 'port-101'} }, 'port_type': 'LINE' }, { 'port_id': { 'topoId': { 'contextId': {'contextUuid': {'uuid': 'admin'}}, 'topoId': {'uuid': 'admin'} }, 'dev_id': {'device_id': {'uuid': 'test-device-001'}}, 'port_id': {'uuid' : 'port-102'} }, 'port_type': 'LINE' }, ] } @pytest.fixture(scope='session') def service(): database = get_database(engine=DatabaseEngineEnum.INMEMORY, filepath='data/topo_nsfnet.json') _service = DeviceService( 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 client(service): _client = DeviceClient(address='127.0.0.1', port=GRPC_SERVICE_PORT) yield _client _client.close() def test_create_empty_device_uuid(client : DeviceClient): # should fail with device uuid is empty with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['device_id']['device_id']['uuid'] = '' client.AddDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == 'device_uuid() string is empty.' def test_create_empty_device_type(client : DeviceClient): # should fail with device type is empty with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['device_type'] = '' client.AddDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == 'device_type() string is empty.' def test_create_wrong_device_operational_status(client : DeviceClient): # should fail with wrong device operational status with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['devOperationalStatus'] = OperationalStatus.KEEP_STATE.value client.AddDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT msg = ' '.join([ 'Device has to be created with either ENABLED/DISABLED Operational State.', 'Use KEEP_STATE only in configure Device methods.', ]) assert e.value.details() == msg def test_create_endpoint_wrong_context(client : DeviceClient): # should fail with unsupported context with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['endpointList'][0]['port_id']['topoId']['contextId']['contextUuid']['uuid'] = 'wrong-context' request = Device(**copy_device) LOGGER.warning('request = {}'.format(request)) client.AddDevice(request) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT msg = ' '.join([ 'Unsupported context_id(wrong-context) in endpoint #0.', 'Only default context_id(admin) is currently supported.', 'Optionally, leave field empty to use default context_id.', ]) assert e.value.details() == msg def test_create_endpoint_wrong_topology(client : DeviceClient): # should fail with unsupported topology with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['endpointList'][0]['port_id']['topoId']['topoId']['uuid'] = 'wrong-topo' client.AddDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT msg = ' '.join([ 'Unsupported topology_id(wrong-topo) in endpoint #0.', 'Only default topology_id(admin) is currently supported.', 'Optionally, leave field empty to use default topology_id.', ]) assert e.value.details() == msg def test_create_endpoint_wrong_device(client : DeviceClient): # should fail with wrong endpoint device with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['endpointList'][0]['port_id']['dev_id']['device_id']['uuid'] = 'wrong-device' client.AddDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT msg = ' '.join([ 'Wrong device_id(wrong-device) in endpoint #0.', 'Parent specified in message is device_id(test-device-001).', 'Optionally, leave field empty to use parent device_id.', ]) assert e.value.details() == msg def test_create_empty_port_uuid(client : DeviceClient): # should fail with port uuid is empty with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['endpointList'][0]['port_id']['port_id']['uuid'] = '' client.AddDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == 'port_uuid() string is empty.' def test_create_empty_port_type(client : DeviceClient): # should fail with port type is empty with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['endpointList'][0]['port_type'] = '' client.AddDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == 'port_type() string is empty.' def test_create_duplicate_port(client : DeviceClient): # should fail with uplicate port in device with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['endpointList'][1]['port_id']['port_id']['uuid'] = 'port-101' client.AddDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.ALREADY_EXISTS assert e.value.details() == 'Duplicated port_id(port-101) in device_id(test-device-001).' def test_create(client : DeviceClient): # should work validate_device_id(MessageToDict( client.AddDevice(Device(**DEVICE)), including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False)) def test_create_duplicate(client : DeviceClient): # should fail with device already exists with pytest.raises(grpc._channel._InactiveRpcError) as e: client.AddDevice(Device(**DEVICE)) assert e.value.code() == grpc.StatusCode.ALREADY_EXISTS assert e.value.details() == 'device_uuid(test-device-001) already exists.' def test_delete_empty_uuid(client : DeviceClient): # should fail with device uuid is empty with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device_id = copy.deepcopy(DEVICE_ID) copy_device_id['device_id']['uuid'] = '' client.DeleteDevice(DeviceId(**copy_device_id)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == 'device_uuid() string is empty.' def test_delete_device_not_found(client : DeviceClient): # should fail with device not found with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device_id = copy.deepcopy(DEVICE_ID) copy_device_id['device_id']['uuid'] = 'wrong-device-id' client.DeleteDevice(DeviceId(**copy_device_id)) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == 'device_uuid(wrong-device-id) does not exist.' def test_delete(client : DeviceClient): # should work validate_empty(MessageToDict( client.DeleteDevice(DeviceId(**DEVICE_ID)), including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False)) def test_configure_empty_device_uuid(client : DeviceClient): # should fail with device uuid is empty with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['device_id']['device_id']['uuid'] = '' client.ConfigureDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == 'device_uuid() string is empty.' def test_configure_device_not_found(client : DeviceClient): # should fail with device not found with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['device_id']['device_id']['uuid'] = 'wrong-device-id' client.ConfigureDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.NOT_FOUND assert e.value.details() == 'device_uuid(wrong-device-id) does not exist.' def test_create_device_default_endpoint_context_topology(client : DeviceClient): # should work copy_device = copy.deepcopy(DEVICE) copy_device['endpointList'][0]['port_id']['topoId']['contextId']['contextUuid']['uuid'] = '' copy_device['endpointList'][0]['port_id']['topoId']['topoId']['uuid'] = '' copy_device['endpointList'][0]['port_id']['dev_id']['device_id']['uuid'] = '' validate_device_id(MessageToDict( client.AddDevice(Device(**copy_device)), including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False)) def test_configure_wrong_device_type(client : DeviceClient): # should fail with device type is wrong with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['device_type'] = 'wrong-type' client.ConfigureDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == 'Device(test-device-001) has Type(ROADM). Cannot be changed to Type(wrong-type).' def test_configure_with_endpoints(client : DeviceClient): # should fail with endpoints cannot be modified with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) client.ConfigureDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT assert e.value.details() == 'Endpoints belonging to Device(test-device-001) cannot be modified.' def test_configure_no_change(client : DeviceClient): # should fail with any change detected with pytest.raises(grpc._channel._InactiveRpcError) as e: copy_device = copy.deepcopy(DEVICE) copy_device['devOperationalStatus'] = OperationalStatus.KEEP_STATE.value copy_device['endpointList'].clear() client.ConfigureDevice(Device(**copy_device)) assert e.value.code() == grpc.StatusCode.ABORTED msg = ' '.join([ 'Any change has been requested for Device(test-device-001).', 'Either specify a new configuration or a new device state.', ]) assert e.value.details() == msg def test_configure(client : DeviceClient): # should work copy_device = copy.deepcopy(DEVICE) copy_device['device_config']['device_config'] = '' copy_device['devOperationalStatus'] = OperationalStatus.DISABLED.value copy_device['endpointList'].clear() validate_device_id(MessageToDict( client.ConfigureDevice(Device(**copy_device)), including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False))