diff --git a/src/common/tests/MockServicerImpl_Context.py b/src/common/tests/MockServicerImpl_Context.py index 3f215d7c5ca63ae3ed278653c2aca25966810e41..e5b86bc585b568f2170702bbf608f3117646d475 100644 --- a/src/common/tests/MockServicerImpl_Context.py +++ b/src/common/tests/MockServicerImpl_Context.py @@ -135,16 +135,23 @@ class MockServicerImpl_Context(ContextServiceServicer): reply.topology_id.CopyFrom(_reply.topology_id) # pylint: disable=no-member reply.name = _reply.name if context_uuid == DEFAULT_CONTEXT_NAME and topology_uuid == DEFAULT_TOPOLOGY_NAME: - for device in self.obj_db.get_entries('device'): reply.devices.append(device) # pylint: disable=no-member - for link in self.obj_db.get_entries('link' ): reply.links .append(link ) # pylint: disable=no-member + for device in self.obj_db.get_entries('device'): + reply.devices.append(device) # pylint: disable=no-member + for link in self.obj_db.get_entries('link'): + reply.links.append(link) # pylint: disable=no-member + for optical_link in self.obj_db.get_entries('optical_link'): + reply.optical_links.append(optical_link) # pylint: disable=no-member else: # TODO: to be improved; Mock does not associate devices/links to topologies automatically for device_id in _reply.device_ids: device = self.obj_db.get_entry('device', device_id.device_uuid.uuid, context) - reply.devices.append(device) # pylint: disable=no-member + reply.devices.append(device) # pylint: disable=no-member for link_id in _reply.link_ids: link = self.obj_db.get_entry('link', link_id.link_uuid.uuid, context) - reply.links.append(link) # pylint: disable=no-member + reply.links.append(link) # pylint: disable=no-member + for optical_link_id in _reply.optical_link_ids: + optical_link = self.obj_db.get_entry('optical_link', optical_link_id.link_uuid.uuid, context) + reply.optical_links.append(optical_link) # pylint: disable=no-member LOGGER.debug('[GetTopologyDetails] reply={:s}'.format(grpc_message_to_json_string(reply))) return reply @@ -159,12 +166,22 @@ class MockServicerImpl_Context(ContextServiceServicer): db_topology = self.obj_db.get_entry(container_name, topology_uuid, context) device_uuids = set() - for device_id in request.device_ids: device_uuids.add(device_id.device_uuid.uuid) - for device_id in db_topology.device_ids: device_uuids.add(device_id.device_uuid.uuid) + for device_id in request.device_ids: + device_uuids.add(device_id.device_uuid.uuid) + for device_id in db_topology.device_ids: + device_uuids.add(device_id.device_uuid.uuid) link_uuids = set() - for link_id in request.link_ids: link_uuids.add(link_id.link_uuid.uuid) - for link_id in db_topology.link_ids: link_uuids.add(link_id.link_uuid.uuid) + for link_id in request.link_ids: + link_uuids.add(link_id.link_uuid.uuid) + for link_id in db_topology.link_ids: + link_uuids.add(link_id.link_uuid.uuid) + + optical_link_uuids = set() + for optical_link_id in request.optical_link_ids: + optical_link_uuids.add(optical_link_id.link_uuid.uuid) + for optical_link_id in db_topology.optical_link_ids: + optical_link_uuids.add(optical_link_id.link_uuid.uuid) rw_request = Topology() rw_request.CopyFrom(request) @@ -179,6 +196,11 @@ class MockServicerImpl_Context(ContextServiceServicer): for link_uuid in sorted(link_uuids): rw_request.link_ids.append(LinkId(**json_link_id(link_uuid))) + # pylint: disable=no-member + del rw_request.optical_link_ids[:] + for optical_link_uuid in sorted(optical_link_uuids): + rw_request.optical_link_ids.append(LinkId(**json_link_id(optical_link_uuid))) + request = rw_request reply,_ = self._set(request, container_name, topology_uuid, 'topology_id', TOPIC_TOPOLOGY) diff --git a/src/common/type_checkers/Assertions.py b/src/common/type_checkers/Assertions.py index d6f2078494c72665e21b254622b0dfe5d9ed4e72..2fa279de593e4edc09d93627219e24c49b7d5fd0 100644 --- a/src/common/type_checkers/Assertions.py +++ b/src/common/type_checkers/Assertions.py @@ -465,7 +465,7 @@ def validate_slice(message): assert 'owner_string' in message['slice_owner'] assert isinstance(message['slice_owner']['owner_string'], str) -def validate_topology(message, num_devices=None, num_links=None): +def validate_topology(message, num_devices=None, num_links=None, num_optical_links=None): assert isinstance(message, dict) assert len(message.keys()) == 5 assert 'topology_id' in message @@ -482,7 +482,7 @@ def validate_topology(message, num_devices=None, num_links=None): for link_id in message['link_ids']: validate_link_id(link_id) assert 'optical_link_ids' in message assert isinstance(message['optical_link_ids'], list) - #if num_links is not None: assert len(message['optical_link_ids']) == num_links + if num_optical_links is not None: assert len(message['optical_link_ids']) == num_optical_links for link_id in message['optical_link_ids']: validate_link_id(link_id) def validate_endpoint(message): @@ -575,6 +575,47 @@ def validate_link(message): assert 'link_type' in message validate_link_type_enum(message['link_type']) +def validate_optical_slot_map(message): + assert isinstance(message, dict) + for key, value in message.items(): + assert isinstance(key, str) + assert isinstance(value, int) + +def validate_optical_link_details(message): + assert isinstance(message, dict) + assert len(message.keys()) == 9 + assert 'length' in message + assert isinstance(message['length'], (int, float)) + assert 'src_port' in message + assert isinstance(message['src_port'], str) + assert 'dst_port' in message + assert isinstance(message['dst_port'], str) + assert 'local_peer_port' in message + assert isinstance(message['local_peer_port'], str) + assert 'remote_peer_port' in message + assert isinstance(message['remote_peer_port'], str) + assert 'used' in message + assert isinstance(message['used'], bool) + assert 'c_slots' in message + validate_optical_slot_map(message['c_slots']) + assert 'l_slots' in message + validate_optical_slot_map(message['l_slots']) + assert 's_slots' in message + validate_optical_slot_map(message['s_slots']) + +def validate_optical_link(message): + assert isinstance(message, dict) + assert len(message.keys()) == 4 + assert 'link_id' in message + validate_link_id(message['link_id']) + assert 'name' in message + assert isinstance(message['name'], str) + assert 'link_endpoint_ids' in message + assert isinstance(message['link_endpoint_ids'], list) + for endpoint_id in message['link_endpoint_ids']: validate_endpoint_id(endpoint_id) + assert 'optical_details' in message + validate_optical_link_details(message['optical_details']) + def validate_connection(message): assert isinstance(message, dict) assert len(message.keys()) in {4, 5} @@ -678,6 +719,13 @@ def validate_links(message): assert isinstance(message['links'], list) for link in message['links']: validate_link(link) +def validate_optical_links(message): + assert isinstance(message, dict) + assert len(message.keys()) == 1 + assert 'optical_links' in message + assert isinstance(message['optical_links'], list) + for optical_link in message['optical_links']: validate_optical_link(optical_link) + def validate_connections(message): assert isinstance(message, dict) assert len(message.keys()) == 1 diff --git a/src/context/service/database/Topology.py b/src/context/service/database/Topology.py index 836b77922d950efbf154ca1e81e39806e259b235..165ae525d57605cdf7de786210f563401f5b2466 100644 --- a/src/context/service/database/Topology.py +++ b/src/context/service/database/Topology.py @@ -51,6 +51,7 @@ def topology_list_objs(db_engine : Engine, request : ContextId) -> TopologyList: obj_list : List[TopologyModel] = session.query(TopologyModel)\ .options(selectinload(TopologyModel.topology_devices))\ .options(selectinload(TopologyModel.topology_links))\ + .options(selectinload(TopologyModel.topology_optical_links))\ .filter_by(context_uuid=context_uuid).all() return [obj.dump() for obj in obj_list] topologies = run_transaction(sessionmaker(bind=db_engine), callback) @@ -62,6 +63,7 @@ def topology_get(db_engine : Engine, request : TopologyId) -> Topology: obj : Optional[TopologyModel] = session.query(TopologyModel)\ .options(selectinload(TopologyModel.topology_devices))\ .options(selectinload(TopologyModel.topology_links))\ + .options(selectinload(TopologyModel.topology_optical_links))\ .filter_by(topology_uuid=topology_uuid).one_or_none() return None if obj is None else obj.dump() obj = run_transaction(sessionmaker(bind=db_engine), callback) diff --git a/src/nbi/service/tfs_api/Resources.py b/src/nbi/service/tfs_api/Resources.py index 1975d5bbb00398aabeb91630e7c88731c885d0d0..b3fef450e34a711cb99fb500dc9dfa5308a1a28e 100644 --- a/src/nbi/service/tfs_api/Resources.py +++ b/src/nbi/service/tfs_api/Resources.py @@ -337,6 +337,14 @@ class Link(_Resource): else: return format_grpc_to_json(self.context_client.RemoveLink(link_id)) +class OpticalLinks(_Resource): + def get(self): + return format_grpc_to_json(self.context_client.GetOpticalLinkList(Empty())) + +class OpticalLink(_Resource): + def get(self, link_uuid : str): + return format_grpc_to_json(self.context_client.GetOpticalLink(grpc_link_id(link_uuid))) + class ConnectionIds(_Resource): def get(self, context_uuid : str, service_uuid : str): return format_grpc_to_json(self.context_client.ListConnectionIds(grpc_service_id(context_uuid, service_uuid))) diff --git a/src/nbi/service/tfs_api/__init__.py b/src/nbi/service/tfs_api/__init__.py index d90e9fc40e1ed9f916b72426e200ff2cb6094845..a33a3a5259c01c72781f4473de16c5c59575188d 100644 --- a/src/nbi/service/tfs_api/__init__.py +++ b/src/nbi/service/tfs_api/__init__.py @@ -19,6 +19,7 @@ from .Resources import ( Device, DeviceIds, Devices, DummyContexts, Link, LinkIds, Links, + OpticalLink, OpticalLinks, PolicyRule, PolicyRuleIds, PolicyRules, Service, ServiceIds, Services, Slice, SliceIds, Slices, @@ -56,6 +57,8 @@ _RESOURCES = [ ('api.link_ids', LinkIds, '/link_ids'), ('api.links', Links, '/links'), ('api.link', Link, '/link/'), + ('api.optical_links', OpticalLinks, '/optical_links'), + ('api.optical_link', OpticalLink, '/optical_link/'), ('api.connection_ids', ConnectionIds, '/context//service//connection_ids'), ('api.connections', Connections, '/context//service//connections'), diff --git a/src/nbi/tests/data/tfs_api_dummy.json b/src/nbi/tests/data/tfs_api_dummy.json index d8f5137578629408556e3758c512f137fc633d6c..1322f7044f1f29063e9121a1a48a6f6d77fb7fa1 100644 --- a/src/nbi/tests/data/tfs_api_dummy.json +++ b/src/nbi/tests/data/tfs_api_dummy.json @@ -232,6 +232,32 @@ ] } ], + "optical_links": [ + { + "link_id": {"link_uuid": {"uuid": "OL:R1/502==R2/501"}}, "name": "OL:R1/502==R2/501", + "link_endpoint_ids": [ + { + "device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "502"}, + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}} + }, + { + "device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "501"}, + "topology_id": {"context_id": {"context_uuid": {"uuid": "admin"}}, "topology_uuid": {"uuid": "admin"}} + } + ], + "optical_details": { + "length": 1.0, + "src_port": "502", + "dst_port": "501", + "local_peer_port": "502", + "remote_peer_port": "501", + "used": true, + "c_slots": {"0": 0, "1": 0, "2": 0}, + "l_slots": {"0": 0, "1": 0, "2": 0}, + "s_slots": {"0": 0, "1": 0, "2": 0} + } + } + ], "services": [ { "service_id" : {"context_id": {"context_uuid": {"uuid": "admin"}}, "service_uuid": {"uuid": "SVC:R1/200==R2/200"}}, diff --git a/src/nbi/tests/test_tfs_api.py b/src/nbi/tests/test_tfs_api.py index 7c95048ea254a5e3d04e417322a2174d3cfade1f..25158fa2ae430d882fb0d106f110e98bd748ddb8 100644 --- a/src/nbi/tests/test_tfs_api.py +++ b/src/nbi/tests/test_tfs_api.py @@ -31,6 +31,7 @@ from common.type_checkers.Assertions import ( validate_context, validate_context_ids, validate_contexts, validate_device, validate_device_ids, validate_devices, validate_link, validate_link_ids, validate_links, + validate_optical_link, validate_optical_links, validate_service, validate_service_ids, validate_services, validate_slice, validate_slice_ids, validate_slices, validate_topologies, validate_topology, validate_topology_ids @@ -113,7 +114,7 @@ def test_rest_get_topology( context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_NAME) topology_uuid = urllib.parse.quote(DEFAULT_TOPOLOGY_NAME) reply = do_rest_get_request('/tfs-api/context/{:s}/topology/{:s}'.format(context_uuid, topology_uuid)) - validate_topology(reply, num_devices=3, num_links=6) + validate_topology(reply, num_devices=3, num_links=6, num_optical_links=1) # ----- Device --------------------------------------------------------------------------------------------------------- @@ -159,6 +160,20 @@ def test_rest_get_link( reply = do_rest_get_request('/tfs-api/link/{:s}'.format(link_uuid)) validate_link(reply) +def test_rest_get_optical_links( + nbi_application : NbiApplication # pylint: disable=redefined-outer-name +) -> None: + reply = do_rest_get_request('/tfs-api/optical_links') + validate_optical_links(reply) + assert len(reply['optical_links']) == 1 + +def test_rest_get_optical_link( + nbi_application : NbiApplication # pylint: disable=redefined-outer-name +) -> None: + link_uuid = urllib.parse.quote('OL:R1/502==R2/501', safe='') + reply = do_rest_get_request('/tfs-api/optical_link/{:s}'.format(link_uuid)) + validate_optical_link(reply) + # ----- Service --------------------------------------------------------------------------------------------------------