Skip to content

Commit

Permalink
Merge pull request #177 from atlanticwave-sdx/fix-get-port-when-break…
Browse files Browse the repository at this point in the history
…down

Update getting port when breakdown
  • Loading branch information
YufengXin authored May 15, 2024
2 parents 32a8b5d + 636bb72 commit cf507ef
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 30 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ dependencies = [
"prtpy",
"pydot",
"dataclasses-json",
"sdx-datamodel @ git+https://github.com/atlanticwave-sdx/datamodel@2.0.6.dev1"
"sdx-datamodel @ git+https://github.com/atlanticwave-sdx/datamodel@2.0.6.rc1"
]

[project.urls]
Expand Down
24 changes: 23 additions & 1 deletion src/sdx_pce/topology/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@


class TopologyManager:

"""
Manager for topology operations.
Expand Down Expand Up @@ -316,6 +315,29 @@ def update_element_property_json(self, data, element, element_id, property, valu
data["version"] = self.new_version(ver, sub_ver, True)
data["timestamp"] = datetime.datetime.now().isoformat()

def get_port_by_id(self, port_id: str):
"""
Given port id, returns a Port.
"""
for node in self.get_topology().get_nodes():
for port in node.ports:
if port.id == port_id:
return port.to_dict()
return None

def are_two_ports_same_domain(self, port1_id: str, port2_id: str):
"""
Check if two ports are in the same domain.
"""
node1 = self.get_topology().get_node_by_port(port1_id)
node2 = self.get_topology().get_node_by_port(port2_id)
if node1 is None or node2 is None:
return False

domain1 = self.get_domain_name(node1.id)
domain2 = self.get_domain_name(node2.id)
return domain1 == domain2

def update_node_property(self):
pass

Expand Down
102 changes: 93 additions & 9 deletions src/sdx_pce/topology/temanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,9 @@ def get_links_on_path(self, solution: ConnectionSolution) -> list:

return result

def generate_connection_breakdown(self, solution: ConnectionSolution) -> dict:
def generate_connection_breakdown(
self, solution: ConnectionSolution, connection_request: dict
) -> dict:
"""
Take a connection solution and generate a breakdown.
"""
Expand Down Expand Up @@ -404,21 +406,83 @@ def generate_connection_breakdown(self, solution: ConnectionSolution) -> dict:
# may lead to incorrect results. Dicts are lexically ordered,
# and that may break some assumptions about the order in which
# we form and traverse the breakdown.

request_format_is_tm = isinstance(connection_request, list)
self._logger.info(
f"connection_requst: {connection_request}; type:{type(request_format_is_tm)}"
)
if not request_format_is_tm:
self._logger.info(
f'connection_requst ingress_port: {connection_request["ingress_port"]["id"]}'
)
self._logger.info(
f'connection_requst egress_port: {connection_request["egress_port"]["id"]}'
)
# flag to indicate if the request ingress and egress ports belong to the same domain
same_domain_user_port_flag = (
self.topology_manager.are_two_ports_same_domain(
connection_request["ingress_port"]["id"],
connection_request["egress_port"]["id"],
)
)
for domain, links in breakdown.items():
self._logger.info(
f"Creating domain_breakdown: domain: {domain}, links: {links}"
)
segment = {}

if first:
first = False
# ingress port for this domain is on the first link.
ingress_port, _ = self._get_ports_by_link(links[0])
if (
not request_format_is_tm
and connection_request["ingress_port"]["id"]
not in self.topology_manager.get_port_map()
):
self._logger.warning(
f"Port {connection_request['ingress_port']['id']} not found in port map, it's a user port"
)
ingress_port_id = connection_request["ingress_port"]["id"]
ingress_port = self.topology_manager.get_port_by_id(ingress_port_id)
else:
ingress_port, _ = self._get_ports_by_link(links[0])

# egress port for this domain is on the last link.
egress_port, next_ingress_port = self._get_ports_by_link(links[-1])
if (
not request_format_is_tm
and same_domain_user_port_flag
and connection_request["egress_port"]["id"]
not in self.topology_manager.get_port_map()
):
self._logger.warning(
f"Port {connection_request['egress_port']['id']} not found in port map, it's a user port"
)
egress_port_id = connection_request["egress_port"]["id"]
egress_port = self.topology_manager.get_port_by_id(egress_port_id)
_, next_ingress_port = self._get_ports_by_link(links[-1])
else:
egress_port, next_ingress_port = self._get_ports_by_link(links[-1])
self._logger.info(
f"ingress_port:{ingress_port}, egress_port:{egress_port},next_ingress_port:{next_ingress_port}"
)
elif i == len(breakdown) - 1:
ingress_port = next_ingress_port
_, egress_port = self._get_ports_by_link(links[-1])
if (
not request_format_is_tm
and connection_request["egress_port"]["id"]
not in self.topology_manager.get_port_map()
):
self._logger.warning(
f"Port {connection_request['egress_port']['id']} not found in port map, it's a user port"
)
egress_port_id = connection_request["egress_port"]["id"]
egress_port = self.topology_manager.get_port_by_id(egress_port_id)
else:
_, egress_port = self._get_ports_by_link(links[-1])

self._logger.info(f"links[-1]: {links[-1]}")
self._logger.info(
f"ingress_port:{ingress_port}, egress_port:{egress_port}"
)
else:
ingress_port = next_ingress_port
egress_port, next_ingress_port = self._get_ports_by_link(links[-1])
Expand Down Expand Up @@ -540,7 +604,7 @@ def _reserve_vlan_breakdown(
ingress_port = segment.get("ingress_port")
egress_port = segment.get("egress_port")

self._logger.info(
self._logger.debug(
f"VLAN reservation: domain: {domain}, "
f"ingress_port: {ingress_port}, egress_port: {egress_port}"
)
Expand All @@ -554,6 +618,26 @@ def _reserve_vlan_breakdown(
ingress_port_id = ingress_port.get("id")
egress_port_id = egress_port.get("id")

# TODO: what to do when a port is not in the port map which only has all the ports on links?
# User facing ports need clarification from the custermers.
if (
ingress_port_id not in self.topology_manager.get_port_map()
and ingress_vlan is None
):
self._logger.warning(
f"Port {ingress_port_id} not found in port map, it's a user port, by default uses the OXP vlan"
)
ingress_vlan = egress_vlan

if (
egress_port_id not in self.topology_manager.get_port_map()
and egress_vlan is None
):
self._logger.warning(
f"Port {egress_port_id} not found in port map, it's a user port, by default uses the OXP vlan"
)
egress_vlan = ingress_vlan

self._logger.info(
f"VLAN reservation: domain: {domain}, "
f"ingress_vlan: {ingress_vlan}, egress_vlan: {egress_vlan}"
Expand Down Expand Up @@ -628,7 +712,7 @@ def _reserve_vlan(self, domain: str, port: dict, request_id: str, tag=None):
# pass

port_id = port.get("id")
self._logger.info(f"reserve_vlan domain: {domain} port_id: {port_id}")
self._logger.debug(f"reserve_vlan domain: {domain} port_id: {port_id}")

if port_id is None:
return None
Expand All @@ -642,7 +726,7 @@ def _reserve_vlan(self, domain: str, port: dict, request_id: str, tag=None):

vlan_table = domain_table.get(port_id)

self._logger.info(f"reserve_vlan domain: {domain} vlan_table: {vlan_table}")
self._logger.debug(f"reserve_vlan domain: {domain} vlan_table: {vlan_table}")

# TODO: figure out when vlan_table can be None
if vlan_table is None:
Expand All @@ -667,7 +751,7 @@ def _reserve_vlan(self, domain: str, port: dict, request_id: str, tag=None):
# mark the tag as in-use.
vlan_table[available_tag] = request_id

self._logger.info(
self._logger.debug(
f"reserve_vlan domain {domain}, after reservation: "
f"vlan_table: {vlan_table}, available_tag: {available_tag}"
)
Expand Down
7 changes: 7 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class TestData:
TOPOLOGY_FILE_SAX = TOPOLOGY_DIR / "sax.json"
TOPOLOGY_FILE_AMLIGHT = TOPOLOGY_DIR / "amlight.json"
TOPOLOGY_FILE_SDX = TOPOLOGY_DIR / "sdx.json"
TOPOLOGY_FILE_AMLIGHT_USER_PORT = TOPOLOGY_DIR / "amlight_user_port.json"

REQUESTS_DIR = files("sdx_datamodel") / "data" / "requests"
CONNECTION_REQ = REQUESTS_DIR / "test_request.json"
Expand All @@ -30,6 +31,12 @@ class TestData:
TEST_DATA_DIR = pathlib.Path(__file__).parent / "data"

CONNECTION_REQ_AMLIGHT = TEST_DATA_DIR / "test_request_amlight.json"
CONNECTION_REQ_AMLIGHT_USER_PORT = (
TEST_DATA_DIR / "test_request_amlight_user_port.json"
)
CONNECTION_REQ_AMLIGHT_ZAOXI_USER_PORT = (
TEST_DATA_DIR / "test_request_amlight_zaoxi_user_port.json"
)

TOPOLOGY_FILE_SAX_2 = TEST_DATA_DIR / "sax-2.json"
CONNECTION_REQ_FILE_SAX_2_INVALID = TEST_DATA_DIR / "sax-2-request-invalid.json"
Expand Down
18 changes: 18 additions & 0 deletions tests/data/test_request_amlight_user_port.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"id": "id",
"name": "AMLight",
"start_time": "2000-01-23T04:56:07.000Z",
"end_time": "2000-01-23T04:56:07.000Z",
"bandwidth_required": 100,
"latency_required": 20,
"egress_port":
{
"id": "urn:sdx:port:amlight.net:A1:3",
"name": "Novi100:1"
},
"ingress_port":
{
"id": "urn:sdx:port:amlight.net:B1:2",
"name": "Novi100:2"
}
}
20 changes: 20 additions & 0 deletions tests/data/test_request_amlight_zaoxi_user_port.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"id": "285eea4b-1e86-4d54-bd75-f14b8cb4a63a",
"name": "Test connection request",
"start_time": "2000-01-23T04:56:07.000Z",
"end_time": "2000-01-23T04:56:07.000Z",
"bandwidth_required": 10,
"latency_required": 300,
"egress_port": {
"id": "urn:sdx:port:amlight.net:A1:3",
"name": "Novi100:1",
"node": "urn:sdx:node:amlight.net:A1",
"status": "up"
},
"ingress_port": {
"id": "urn:ogf:network:sdx:port:zaoxi:B2:1",
"name": "Novi100:2",
"node": "urn:ogf:network:sdx:node:zaoxi:B2",
"status": "up"
}
}
Loading

0 comments on commit cf507ef

Please sign in to comment.