diff --git a/run_tests_locally.sh b/run_tests_locally.sh
index dd5259a8d541ce8d17992cca3fbd71d081a07740..6d735fbb5aac299a9b2a9f4443689b0a4a339e3f 100755
--- a/run_tests_locally.sh
+++ b/run_tests_locally.sh
@@ -27,7 +27,7 @@ rm -f $COVERAGEFILE
coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
device/tests/test_unitary_driverapi.py \
- device/tests/test_unitary_service.py
+ device/tests/test_unitary.py
#coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \
# service/tests/test_unitary.py
diff --git a/src/device/tests/test_unitary_service.py b/src/device/_old_code/test_unitary.py
similarity index 100%
rename from src/device/tests/test_unitary_service.py
rename to src/device/_old_code/test_unitary.py
diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py
index 7b174851c9f7a76ffe76b725a066158434056ea0..e92e67715118ffa9a59034ba3b32d3d8fce5b320 100644
--- a/src/device/service/DeviceServiceServicerImpl.py
+++ b/src/device/service/DeviceServiceServicerImpl.py
@@ -48,29 +48,31 @@ class DeviceServiceServicerImpl(DeviceServiceServicer):
def ConfigureDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId:
device_id = request.device_id
device_uuid = device_id.device_uuid.uuid
- config_name = 'running'
+ #config_name = 'running'
self.data_cache.sync_device_from_context(device_uuid)
db_device,_ = self.data_cache.set_device(request)
+ #resources_to_set = List[Tuple[str: resource_key, any: resource_value]]
+ #resources_to_delete = List[Tuple[str: resource_key]]
+ #resources_to_subscribe = List[str: resource_key, float: sampling_rate]
+ #resources_to_unsubscribe = List[Tuple[str: resource_key]]
+
+ # Compute "difference" between config field in request and config from Context
# Compute list of changes between device_config in context, and device_config in request
- set_changes : List[Tuple[str, Any]] = []
- delete_changes : List[Tuple[str, Any]] = []
- subscriptions : List[Tuple[str, Any]] = []
- unsubscriptions : List[Tuple[str, Any]] = []
driver_filter_fields = self.data_cache.get_device_driver_filter_fields(device_uuid)
driver : _Driver = self.driver_instance_cache.get(device_uuid, **driver_filter_fields)
driver.Connect()
- result = driver.SetConfig(set_changes)
- # check result
- result = driver.DeleteConfig(delete_changes)
- # check result
- result = driver.SubscribeState(subscriptions)
- # check result
- result = driver.UnsubscribeState(unsubscriptions)
- # check result
+ #results_setconfig = driver.SetConfig(resources_to_set)
+ ## check result
+ #results_deleteconfig = driver.DeleteConfig(resources_to_delete)
+ ## check result
+ #results_subscribestate = driver.SubscribeState(resources_to_subscribe)
+ ## check result
+ #results_unsubscribestate = driver.UnsubscribeState(resources_to_unsubscribe)
+ ## check result
self.data_cache.sync_device_to_context(device_uuid)
return DeviceId(**db_device.dump_id())
diff --git a/src/device/service/__main__.py b/src/device/service/__main__.py
index 8c331036dbc3d6d386e8cbffe74c0fe7a275f3b7..b9b82b335e2a9ef76e784e6277944d1e08b4283a 100644
--- a/src/device/service/__main__.py
+++ b/src/device/service/__main__.py
@@ -40,6 +40,8 @@ def main():
# Initialize DataCache
data_cache = DataCache(context_service_host, context_service_port)
+ # TODO: start monitoring loops to periodically report to Monitoring the collected data
+
# Initialize Driver framework
driver_factory = DriverFactory(DRIVERS)
driver_instance_cache = DriverInstanceCache(driver_factory)
diff --git a/src/device/tests/test_unitary.py b/src/device/tests/test_unitary.py
new file mode 100644
index 0000000000000000000000000000000000000000..8d9591dd6492395a44adc25e4a54eaebc8ff9121
--- /dev/null
+++ b/src/device/tests/test_unitary.py
@@ -0,0 +1,270 @@
+import copy, grpc, logging, pytest
+from google.protobuf.json_format import MessageToDict
+from common.database.Factory import get_database, DatabaseEngineEnum
+from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID
+from common.database.api.context.topology.device.OperationalStatus import OperationalStatus
+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
+
+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}}
+DEVICE_ID = {'device_id': {'uuid': 'DEV1'}}
+DEVICE = {
+ 'device_id': copy.deepcopy(DEVICE_ID),
+ 'device_type': 'ROADM',
+ 'device_config': {'device_config': ''},
+ 'devOperationalStatus': OperationalStatus.ENABLED.value,
+ 'endpointList' : [
+ {
+ 'port_id': {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': copy.deepcopy(DEVICE_ID), 'port_id': {'uuid' : 'EP2'}},
+ 'port_type': 'WDM'
+ },
+ {
+ 'port_id': {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': copy.deepcopy(DEVICE_ID), 'port_id': {'uuid' : 'EP3'}},
+ 'port_type': 'WDM'
+ },
+ {
+ 'port_id': {'topoId': copy.deepcopy(TOPOLOGY_ID), 'dev_id': copy.deepcopy(DEVICE_ID), 'port_id': {'uuid' : 'EP4'}},
+ 'port_type': 'WDM'
+ },
+ ]
+}
+
+@pytest.fixture(scope='session')
+def device_database():
+ _database = get_database(engine=DatabaseEngineEnum.INMEMORY)
+ return _database
+
+@pytest.fixture(scope='session')
+def device_service(device_database):
+ _service = DeviceService(
+ device_database, port=port, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD)
+ _service.start()
+ yield _service
+ _service.stop()
+
+@pytest.fixture(scope='session')
+def device_client(device_service):
+ _client = DeviceClient(address='127.0.0.1', port=port)
+ yield _client
+ _client.close()
+
+def test_add_device_wrong_attributes(device_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'] = ''
+ device_client.AddDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'device.device_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 device type is empty
+ with pytest.raises(grpc._channel._InactiveRpcError) as e:
+ copy_device = copy.deepcopy(DEVICE)
+ copy_device['device_type'] = ''
+ device_client.AddDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'device.device_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 device operational status
+ with pytest.raises(grpc._channel._InactiveRpcError) as e:
+ copy_device = copy.deepcopy(DEVICE)
+ copy_device['devOperationalStatus'] = OperationalStatus.KEEP_STATE.value
+ device_client.AddDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'Method(AddDevice) does not accept OperationalStatus(KEEP_STATE). '\
+ 'Permitted values for Method(AddDevice) are OperationalStatus([\'DISABLED\', \'ENABLED\']).'
+ assert e.value.details() == msg
+
+def test_add_device_wrong_endpoint(device_client : DeviceClient):
+ # should fail with unsupported endpoint 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)
+ device_client.AddDevice(request)
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'Context(wrong-context) in Endpoint(#0) of '\
+ 'Context(admin)/Topology(admin)/Device(DEV1) mismatches acceptable Contexts({\'admin\'}). '\
+ 'Optionally, leave field empty to use predefined Context(admin).'
+ assert e.value.details() == msg
+
+ # should fail with unsupported endpoint 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'
+ device_client.AddDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'Context(admin)/Topology(wrong-topo) in Endpoint(#0) of '\
+ 'Context(admin)/Topology(admin)/Device(DEV1) mismatches acceptable Topologies({\'admin\'}). '\
+ 'Optionally, leave field empty to use predefined Topology(admin).'
+ assert e.value.details() == msg
+
+ # 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'
+ device_client.AddDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'Context(admin)/Topology(admin)/Device(wrong-device) in Endpoint(#0) of '\
+ 'Context(admin)/Topology(admin)/Device(DEV1) mismatches acceptable Devices({\'DEV1\'}). '\
+ 'Optionally, leave field empty to use predefined Device(DEV1).'
+ assert e.value.details() == msg
+
+ # should fail with endpoint 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'] = ''
+ device_client.AddDevice(Device(**copy_device))
+ 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 type is empty
+ with pytest.raises(grpc._channel._InactiveRpcError) as e:
+ copy_device = copy.deepcopy(DEVICE)
+ copy_device['endpointList'][0]['port_type'] = ''
+ device_client.AddDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'endpoint[#0].port_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 duplicate 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'] = 'EP2'
+ device_client.AddDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'Duplicated Context(admin)/Topology(admin)/Device(DEV1)/Port(EP2) in Endpoint(#1) of '\
+ 'Context(admin)/Topology(admin)/Device(DEV1).'
+ assert e.value.details() == msg
+
+def test_add_device(device_client : DeviceClient):
+ # should work
+ validate_device_id(MessageToDict(
+ device_client.AddDevice(Device(**DEVICE)),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))
+
+def test_add_device_duplicate(device_client : DeviceClient):
+ # should fail with device already exists
+ with pytest.raises(grpc._channel._InactiveRpcError) as e:
+ device_client.AddDevice(Device(**DEVICE))
+ assert e.value.code() == grpc.StatusCode.ALREADY_EXISTS
+ msg = 'Context(admin)/Topology(admin)/Device(DEV1) already exists in the database.'
+ assert e.value.details() == msg
+
+def test_delete_device_empty_uuid(device_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'] = ''
+ device_client.DeleteDevice(DeviceId(**copy_device_id))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'device_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
+
+def test_delete_device_not_found(device_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'
+ device_client.DeleteDevice(DeviceId(**copy_device_id))
+ assert e.value.code() == grpc.StatusCode.NOT_FOUND
+ msg = 'Context(admin)/Topology(admin)/Device(wrong-device-id) does not exist in the database.'
+ assert e.value.details() == msg
+
+def test_delete_device(device_client : DeviceClient):
+ # should work
+ validate_empty(MessageToDict(
+ device_client.DeleteDevice(DeviceId(**DEVICE_ID)),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))
+
+def test_configure_device_empty_device_uuid(device_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'] = ''
+ device_client.ConfigureDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'device.device_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
+
+def test_configure_device_not_found(device_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'
+ device_client.ConfigureDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.NOT_FOUND
+ msg = 'Context(admin)/Topology(admin)/Device(wrong-device-id) does not exist in the database.'
+ assert e.value.details() == msg
+
+def test_add_device_default_endpoint_context_topology_device(device_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(
+ device_client.AddDevice(Device(**copy_device)),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))
+
+def test_configure_device_wrong_attributes(device_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'
+ device_client.ConfigureDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ msg = 'Device(DEV1) has Type(ROADM) in the database. Cannot be changed to Type(wrong-type).'
+ assert e.value.details() == msg
+
+ # should fail with endpoints cannot be modified
+ with pytest.raises(grpc._channel._InactiveRpcError) as e:
+ copy_device = copy.deepcopy(DEVICE)
+ device_client.ConfigureDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT
+ assert e.value.details() == 'Endpoints belonging to Device(DEV1) cannot be modified.'
+
+ # should fail with any change detected
+ with pytest.raises(grpc._channel._InactiveRpcError) as e:
+ copy_device = copy.deepcopy(DEVICE)
+ copy_device['device_config']['device_config'] = ''
+ copy_device['devOperationalStatus'] = OperationalStatus.KEEP_STATE.value
+ copy_device['endpointList'].clear()
+ device_client.ConfigureDevice(Device(**copy_device))
+ assert e.value.code() == grpc.StatusCode.ABORTED
+ msg = 'Any change has been requested for Device(DEV1). '\
+ 'Either specify a new configuration or a new device operational status.'
+ assert e.value.details() == msg
+
+def test_configure_device(device_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(
+ device_client.ConfigureDevice(Device(**copy_device)),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))