From 9578276b0e547f6f56058d9ec6122443e83803cf Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 28 Oct 2024 07:36:21 -0500 Subject: [PATCH 01/55] Remove old setuptools_scm configuration --- pyproject.toml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c267940d..7bc3f5df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,12 +67,6 @@ filterwarnings = [ "ignore:Type has been deprecated in version 0.1.5 and will be removed:DeprecationWarning", ] - -[tool.setuptools_scm] -# Write version info collected from git to a file. This happens when -# we run `python -m build`. -write_to = "src/sdx_pce/_version.py" - [tool.isort] profile = "black" src_paths = ["src", "tests", "scripts"] From efe27ad2644e7f7fd95c5050dca397dbcd829ff5 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 28 Oct 2024 10:02:37 -0500 Subject: [PATCH 02/55] Drop Python 3.8 compatibility import --- tests/__init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index bcd79e20..c43cab0d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,12 +1,7 @@ import pathlib import tempfile -try: - # Use stdlib modules with Python > 3.8. - from importlib.resources import files -except ImportError: - # Use compatibility library with Python 3.8. - from importlib_resources import files +from importlib.resources import files class TestData: From a41674bf761eded2805d6f648fc9abd2baacff53 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 28 Oct 2024 11:34:28 -0500 Subject: [PATCH 03/55] Add a test for requests containing VLAN ranges --- tests/test_te_manager.py | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 21e33ce3..ee60e8a9 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1524,6 +1524,55 @@ def test_disallowed_vlan(self): with self.assertRaises(TEError): self.temanager.generate_connection_breakdown(solution, connection_request) + def test_vlan_range(self): + """ + Test when requests are for a range like [n:m] + """ + + connection_request = json.loads( + """ + { + "name": "new-connection", + "id": "test-connection-id", + "endpoints": [ + { + "port_id": "urn:sdx:port:amlight.net:A1:1", + "vlan": "100:200" + }, + { + "port_id": "urn:sdx:port:amlight:B1:1", + "vlan": "100:200" + } + ] + } + """ + ) + + temanager = TEManager(topology_data=None) + + temanager.add_topology( + json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_USER_PORT.read_text()) + ) + + graph = temanager.generate_graph_te() + + traffic_matrix = temanager.generate_traffic_matrix(connection_request) + + print(f"Generated graph: '{graph}', traffic matrix: '{traffic_matrix}'") + + self.assertIsNotNone(graph) + self.assertIsNotNone(traffic_matrix) + + conn = temanager.requests_connectivity(traffic_matrix) + print(f"Graph connectivity: {conn}") + + solution = TESolver(graph, traffic_matrix).solve() + print(f"TESolver result: {solution}") + + self.assertIsNotNone(solution) + + self.temanager.generate_connection_breakdown(solution, connection_request) + def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ A helper to compare requested VLAN against the VLAN assignment From 366301e39155f35f5188586d05933c56f87993e2 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 28 Oct 2024 11:35:00 -0500 Subject: [PATCH 04/55] Use correct comment --- tests/test_te_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index ee60e8a9..59c61fb9 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1519,8 +1519,8 @@ def test_disallowed_vlan(self): # Although we have a solution (in terms of connectivity # between ports), we should not have a breakdown at this - # point, because we asked for a port that is not present on - # the port. + # point, because we asked for a VLAN tag that is not present + # on the port. with self.assertRaises(TEError): self.temanager.generate_connection_breakdown(solution, connection_request) From afcb698be9c8d369462418ae4ec589c4fbd5d58b Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 28 Oct 2024 11:45:31 -0500 Subject: [PATCH 05/55] Run isort --- tests/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/__init__.py b/tests/__init__.py index c43cab0d..0d1bf485 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,6 +1,5 @@ import pathlib import tempfile - from importlib.resources import files From 9163cec0bc373afe858a2dac037d0ce87c9eb4f8 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 30 Oct 2024 11:21:32 -0500 Subject: [PATCH 06/55] Attempt to reserve VLAN range --- src/sdx_pce/topology/temanager.py | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 86a83063..49d5aad3 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -927,6 +927,17 @@ def _find_common_vlan_on_link( ) return None + def _tag_is_vlan_range(self, tag: str) -> bool: + """ + Return True if tag is of the form `n:m` + """ + import re + + return bool(re.match(r"\d+:\d+", tag)) + + def _handle_vlan_range(self, tag): + print("HERE HERE HERE") + def _reserve_vlan( self, domain: str, @@ -995,6 +1006,29 @@ def _reserve_vlan( for vlan_tag, vlan_usage in vlan_table.items(): if vlan_usage is UNUSED_VLAN: available_tag = vlan_tag + elif self._tag_is_vlan_range(tag): + # expand the range. + start, end = map(int, tag.split(":")) + vlans = list(range(start, end + 1)) + + self._logger.debug(f"Attempting to reseve vlan range {vlans}") + + # Check if all VLANs in the range are available. + for vlan in vlans: + if vlan_table[vlan] is not UNUSED_VLAN: + raise Exception(f"VLAN {vlan} is in use; can't reserve {tag} range") + + # Mark range in use. + for vlan in vlans: + vlan_table[vlan] = request_id + + self._logger.debug( + f"reserve_vlan domain {domain}, after reservation: " + f"vlan_table: {vlan_table}, requested range: {tag}" + ) + + # Return the whole list to indicate success. + return vlans else: if tag in vlan_table: if vlan_table[tag] is UNUSED_VLAN: From d692fa6a2c68a3f32934d9b621677fd0cd2dc5df Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 30 Oct 2024 11:38:43 -0500 Subject: [PATCH 07/55] Check shape of breakdown --- tests/test_te_manager.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 59c61fb9..55b86896 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1571,7 +1571,14 @@ def test_vlan_range(self): self.assertIsNotNone(solution) - self.temanager.generate_connection_breakdown(solution, connection_request) + breakdown = self.temanager.generate_connection_breakdown( + solution, connection_request + ) + pprint.pprint(f"Breakdown: {breakdown}") + + self.assertIsNotNone(breakdown) + self.assertIsInstance(breakdown, dict) + self.assertEqual(len(breakdown), 1) def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ From 664fa1ad1b53a68b4f975da56036bd4b345c1d52 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 30 Oct 2024 11:43:42 -0500 Subject: [PATCH 08/55] Do regex match only against strings --- src/sdx_pce/topology/temanager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 49d5aad3..ac90410a 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -933,7 +933,10 @@ def _tag_is_vlan_range(self, tag: str) -> bool: """ import re - return bool(re.match(r"\d+:\d+", tag)) + if isinstance(tag, str): + return bool(re.match(r"\d+:\d+", tag)) + else: + return False def _handle_vlan_range(self, tag): print("HERE HERE HERE") From 40eaef765adc24c5d55eba0309000ace78e4a053 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 30 Oct 2024 11:44:40 -0500 Subject: [PATCH 09/55] Move import to top-level --- src/sdx_pce/topology/temanager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index ac90410a..1805b0c0 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -1,4 +1,5 @@ import logging +import re import threading from itertools import chain from typing import List, Optional @@ -931,8 +932,6 @@ def _tag_is_vlan_range(self, tag: str) -> bool: """ Return True if tag is of the form `n:m` """ - import re - if isinstance(tag, str): return bool(re.match(r"\d+:\d+", tag)) else: From 2d8d4dd5a3dd4d11eecc0a47adfe8e4ced1af9fc Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 31 Oct 2024 06:15:33 -0500 Subject: [PATCH 10/55] Use correct parameter in docstring --- src/sdx_pce/topology/temanager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 1805b0c0..7595085a 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -961,9 +961,10 @@ def _reserve_vlan( preferences. See the description of "vlan" under "Mandatory Attributes" section of the Service Provisioning Data Model Specification 1.0 for details. - :param upstream_vlan: a string that contains the upstream tag to use https://sdx-docs.readthedocs.io/en/latest/specs/provisioning-api-1.0.html#mandatory-attributes + :param upstream_egress_vlan: a string that contains the + upstream tag to use """ # with self._topology_lock: From a6c2428425fdb5b7d68bc9330ec2d557300805fa Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 31 Oct 2024 14:20:59 -0500 Subject: [PATCH 11/55] Use v2 topology --- tests/test_te_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 55b86896..c714054b 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1551,7 +1551,7 @@ def test_vlan_range(self): temanager = TEManager(topology_data=None) temanager.add_topology( - json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_USER_PORT.read_text()) + json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_v2.read_text()) ) graph = temanager.generate_graph_te() From 7bf169a4b5730e25b91b28aae6ad0fcdd8d31779 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 31 Oct 2024 14:21:33 -0500 Subject: [PATCH 12/55] Request connection between user ports --- tests/test_te_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index c714054b..4811e47a 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1536,11 +1536,11 @@ def test_vlan_range(self): "id": "test-connection-id", "endpoints": [ { - "port_id": "urn:sdx:port:amlight.net:A1:1", + "port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200" }, { - "port_id": "urn:sdx:port:amlight:B1:1", + "port_id": "urn:sdx:port:ampath.net:Ampath2:50", "vlan": "100:200" } ] From 38c2a9c574d408036a0bc92ec7beb52c903829a9 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 31 Oct 2024 14:21:52 -0500 Subject: [PATCH 13/55] Solve the mystery of wrong connection breakdown Turns out that temanager != self.temanager In order to avoid confusion, we probably should not have a self.temanager at all. --- tests/test_te_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 4811e47a..2ebd5d87 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1571,7 +1571,7 @@ def test_vlan_range(self): self.assertIsNotNone(solution) - breakdown = self.temanager.generate_connection_breakdown( + breakdown = temanager.generate_connection_breakdown( solution, connection_request ) pprint.pprint(f"Breakdown: {breakdown}") From 539cb2631c2eb2ebb0d10899590866dfe0bce811 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 31 Oct 2024 14:49:22 -0500 Subject: [PATCH 14/55] Return just the requested tag when a range is requested This way we can form a short name --- src/sdx_pce/topology/temanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 7595085a..a4509f61 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -1030,8 +1030,8 @@ def _reserve_vlan( f"vlan_table: {vlan_table}, requested range: {tag}" ) - # Return the whole list to indicate success. - return vlans + # Return the tag to indicate success. + return tag else: if tag in vlan_table: if vlan_table[tag] is UNUSED_VLAN: From a35ee7b985c4c26d5d9c905812574882b4aca8f9 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 1 Nov 2024 06:32:28 -0500 Subject: [PATCH 15/55] Remove stub method --- src/sdx_pce/topology/temanager.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index a4509f61..7a9e249a 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -937,9 +937,6 @@ def _tag_is_vlan_range(self, tag: str) -> bool: else: return False - def _handle_vlan_range(self, tag): - print("HERE HERE HERE") - def _reserve_vlan( self, domain: str, From b535ae6fffcc224d6397ebab1bf4e054815a0408 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 1 Nov 2024 13:58:16 -0500 Subject: [PATCH 16/55] Rename VLAN range test --- tests/test_te_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 2ebd5d87..e91a89fb 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1524,7 +1524,7 @@ def test_disallowed_vlan(self): with self.assertRaises(TEError): self.temanager.generate_connection_breakdown(solution, connection_request) - def test_vlan_range(self): + def test_vlan_range_one_domain(self): """ Test when requests are for a range like [n:m] """ From 0a36b7826f69caaa3630ec33f088af47b58a6ff9 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 1 Nov 2024 13:58:52 -0500 Subject: [PATCH 17/55] Update comment --- tests/test_te_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index e91a89fb..31cfd586 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1526,7 +1526,8 @@ def test_disallowed_vlan(self): def test_vlan_range_one_domain(self): """ - Test when requests are for a range like [n:m] + Test when requests are for a range like [n:m], just for one + domain. """ connection_request = json.loads( From ad32aefe51da03293e22b97d97c01d5853e96d47 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 1 Nov 2024 14:22:09 -0500 Subject: [PATCH 18/55] Add more asserts --- tests/test_te_manager.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 31cfd586..217099fc 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1581,6 +1581,32 @@ def test_vlan_range_one_domain(self): self.assertIsInstance(breakdown, dict) self.assertEqual(len(breakdown), 1) + ampath = breakdown.get("urn:sdx:topology:ampath.net") + self.assertIsNotNone(ampath) + + self.assertEqual(ampath.get("name"), "AMPATH_vlan_100:200_100:200") + self.assertEqual(ampath.get("dynamic_backup_path"), True) + + self.assertIsInstance(ampath.get("uni_a"), dict) + self.assertIsInstance(ampath.get("uni_a").get("tag"), dict) + self.assertIsInstance(ampath.get("uni_a").get("tag").get("value"), str) + self.assertEqual(ampath.get("uni_a").get("tag").get("value"), "100:200") + self.assertIsInstance(ampath.get("uni_a").get("tag").get("tag_type"), int) + self.assertIsInstance(ampath.get("uni_a").get("port_id"), str) + self.assertEqual( + ampath.get("uni_a").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:50" + ) + + self.assertIsInstance(ampath.get("uni_z"), dict) + self.assertIsInstance(ampath.get("uni_z").get("tag"), dict) + self.assertIsInstance(ampath.get("uni_z").get("tag").get("value"), str) + self.assertEqual(ampath.get("uni_z").get("tag").get("value"), "100:200") + self.assertIsInstance(ampath.get("uni_z").get("tag").get("tag_type"), int) + self.assertIsInstance(ampath.get("uni_z").get("port_id"), str) + self.assertEqual( + ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath2:50" + ) + def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ A helper to compare requested VLAN against the VLAN assignment From 8d69ed6585e8e75aca38850177e7b76eda67dcde Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 1 Nov 2024 14:54:08 -0500 Subject: [PATCH 19/55] Localize all TEManager instances in tests --- tests/test_te_manager.py | 124 +++++++++++++++++++++++---------------- 1 file changed, 73 insertions(+), 51 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 217099fc..8ba5b90f 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -17,26 +17,23 @@ class TEManagerTests(unittest.TestCase): Tests for topology related functions. """ - def setUp(self): - topology = json.loads(TestData.TOPOLOGY_FILE_AMLIGHT.read_text()) - self.temanager = TEManager(topology) - - def tearDown(self): - self.temanager = None - def test_expand_label_range(self): """ Test the _expand_label_range() method. """ + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT.read_text()) + ) + # Test case 1: Single label range label_range = [[100, 105], 110] - expanded_range = self.temanager._expand_label_range(label_range) + expanded_range = temanager._expand_label_range(label_range) expected_range = [100, 101, 102, 103, 104, 105, 110] self.assertEqual(expanded_range, expected_range) # Test case 2: Multiple label ranges label_ranges = [[200, 205], 309, "310-312"] - expanded_ranges = self.temanager._expand_label_range(label_ranges) + expanded_ranges = temanager._expand_label_range(label_ranges) expected_ranges = [ 200, 201, @@ -53,7 +50,7 @@ def test_expand_label_range(self): # Test case 3: Empty label range label_range = [] - expanded_range = self.temanager._expand_label_range(label_range) + expanded_range = temanager._expand_label_range(label_range) expected_range = [] self.assertEqual(expanded_range, expected_range) @@ -111,7 +108,12 @@ def test_find_common_vlan_on_link(self): def test_generate_solver_input(self): print("Test Convert Connection To Topology") request = json.loads(TestData.CONNECTION_REQ_AMLIGHT.read_text()) - connection = self._make_traffic_matrix_from_request(request) + + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT.read_text()) + ) + + connection = self._make_traffic_matrix_from_request(temanager, request) self.assertIsNotNone(connection) def test_connection_breakdown_none_input(self): @@ -124,9 +126,8 @@ def test_connection_breakdown_none_input(self): invalid_request = None with self.assertRaises(TEError): - self.temanager.generate_connection_breakdown( - invalid_solution, invalid_request - ) + temanager = TEManager(topology_data=None) + temanager.generate_connection_breakdown(invalid_solution, invalid_request) def test_connection_breakdown_tm(self): # Breaking down a traffic matrix. @@ -137,13 +138,17 @@ def test_connection_breakdown_tm(self): 1.0, ] - solution = self._make_tm_and_solve(request) + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT.read_text()) + ) + + solution = self._make_tm_and_solve(temanager, request) # We must have found a solution. self.assertIsNotNone(solution.connection_map) self.assertNotEqual(solution.cost, 0) - breakdown = self.temanager.generate_connection_breakdown(solution, request) + breakdown = temanager.generate_connection_breakdown(solution, request) print(f"Breakdown: {breakdown}") self.assertIsNotNone(breakdown) self.assertIsInstance(breakdown, dict) @@ -165,11 +170,15 @@ def test_connection_breakdown_two_similar_requests(self): 1.0, ] - solution = self._make_tm_and_solve(request) + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT.read_text()) + ) + + solution = self._make_tm_and_solve(temanager, request) self.assertIsNotNone(solution.connection_map) self.assertNotEqual(solution.cost, 0) - breakdown = self.temanager.generate_connection_breakdown(solution, request) + breakdown = temanager.generate_connection_breakdown(solution, request) print(f"Breakdown: {breakdown}") self.assertIsNotNone(breakdown) @@ -182,14 +191,16 @@ def test_connection_breakdown_two_similar_requests(self): def test_connection_breakdown_three_domains(self): # SDX already exists in the known topology from setUp # step. Add SAX topology. - sax_topology = json.loads(TestData.TOPOLOGY_FILE_SAX.read_text()) - self.temanager.add_topology(sax_topology) + temanager = TEManager(topology_data=None) - # Add ZAOXI topology as well. - zaoxi_topology = json.loads(TestData.TOPOLOGY_FILE_ZAOXI.read_text()) - self.temanager.add_topology(zaoxi_topology) + for topology_file in [ + TestData.TOPOLOGY_FILE_AMLIGHT, + TestData.TOPOLOGY_FILE_SAX, + TestData.TOPOLOGY_FILE_ZAOXI, + ]: + temanager.add_topology(json.loads(topology_file.read_text())) - topology_map = self.temanager.get_topology_map() + topology_map = temanager.get_topology_map() self.assertIsInstance(topology_map, dict) for num, val in enumerate(topology_map.values()): @@ -204,11 +215,11 @@ def test_connection_breakdown_three_domains(self): 1.0, ] - solution = self._make_tm_and_solve(request) + solution = self._make_tm_and_solve(temanager, request) self.assertIsNotNone(solution.connection_map) self.assertNotEqual(solution.cost, 0) - breakdown = self.temanager.generate_connection_breakdown(solution, request) + breakdown = temanager.generate_connection_breakdown(solution, request) print(f"Breakdown: {breakdown}") self.assertIsNotNone(breakdown) @@ -239,12 +250,14 @@ def test_connection_breakdown_three_domains_sax_connection(self): Test case added to investigate https://github.com/atlanticwave-sdx/sdx-controller/issues/146 """ - sax_topology = json.loads(TestData.TOPOLOGY_FILE_SAX.read_text()) - self.temanager.add_topology(sax_topology) + temanager = TEManager(topology_data=None) - # Add ZAOXI topology as well. - zaoxi_topology = json.loads(TestData.TOPOLOGY_FILE_ZAOXI.read_text()) - self.temanager.add_topology(zaoxi_topology) + for topology_file in [ + TestData.TOPOLOGY_FILE_AMLIGHT, + TestData.TOPOLOGY_FILE_SAX, + TestData.TOPOLOGY_FILE_ZAOXI, + ]: + temanager.add_topology(json.loads(topology_file.read_text())) request = [ { @@ -253,15 +266,15 @@ def test_connection_breakdown_three_domains_sax_connection(self): 1.0, ] - solution = self._make_tm_and_solve(request) + solution = self._make_tm_and_solve(temanager, request) - print(f"topology: {self.temanager.topology_manager.get_topology()}") - # print(f"topology_list: {self.temanager.topology_manager._topology_map}") + print(f"topology: {temanager.topology_manager.get_topology()}") + # print(f"topology_list: {temanager.topology_manager._topology_map}") self.assertIsNotNone(solution.connection_map) self.assertNotEqual(solution.cost, 0) - breakdown = self.temanager.generate_connection_breakdown(solution, request) + breakdown = temanager.generate_connection_breakdown(solution, request) print(f"Breakdown: {breakdown}") sax = breakdown.get("urn:sdx:topology:sax.net") @@ -283,13 +296,17 @@ def test_connection_breakdown_some_input(self): 14195698.0, ] - solution = self._make_tm_and_solve(request) + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT.read_text()) + ) + + solution = self._make_tm_and_solve(temanager, request) self.assertIsNone(solution.connection_map) self.assertEqual(solution.cost, 0) # If there's no solution, there should be no breakdown either. with self.assertRaises(TEError): - self.temanager.generate_connection_breakdown(solution, request) + temanager.generate_connection_breakdown(solution, request) def test_generate_graph_and_connection_with_sax_2_invalid(self): """ @@ -298,8 +315,9 @@ def test_generate_graph_and_connection_with_sax_2_invalid(self): TODO: Use a better name for this method. """ - topology = json.loads(TestData.TOPOLOGY_FILE_SAX_2.read_text()) - temanager = TEManager(topology) + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_SAX_2.read_text()) + ) self.assertIsNotNone(temanager) @@ -321,8 +339,9 @@ def test_generate_graph_and_connection_with_sax_2_valid(self): TODO: Use a better name for this method. """ - topology = json.loads(TestData.TOPOLOGY_FILE_SAX_2.read_text()) - temanager = TEManager(topology) + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_SAX_2.read_text()) + ) self.assertIsNotNone(temanager) @@ -379,9 +398,8 @@ def test_connection_amlight_v2(self): """ Test with just one topology/domain. """ - topology = json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_v2.read_text()) - temanager = TEManager(topology) + temanager = TEManager(topology_data=topology) graph = temanager.generate_graph_te() self.assertIsInstance(graph, nx.Graph) @@ -1130,35 +1148,39 @@ def test_connection_amlight_to_zaoxi_with_merged_topology(self): self.assertIsNotNone(breakdown.get("urn:sdx")) def test_generate_graph_and_connection(self): - graph = self.temanager.generate_graph_te() + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT.read_text()) + ) + + graph = temanager.generate_graph_te() print(f"graph: {graph}") self.assertIsNotNone(graph) self.assertIsInstance(graph, nx.Graph) request = json.loads(TestData.CONNECTION_REQ_AMLIGHT.read_text()) - tm = self.temanager.generate_traffic_matrix(request) + tm = temanager.generate_traffic_matrix(request) print(f"tm: {tm}") self.assertIsNotNone(tm) self.assertIsInstance(tm, TrafficMatrix) def _make_traffic_matrix_from_request( - self, connection_request: dict + self, temanager: TEManager, connection_request: dict ) -> TrafficMatrix: """ Make a traffic matrix out of a connection request dict. """ - graph = self.temanager.graph + graph = temanager.graph print(f"Generated networkx graph of the topology: {graph}") print(f"Graph nodes: {graph.nodes[0]}, edges: {graph.edges}") - traffic_matrix = self.temanager.generate_traffic_matrix(connection_request) + traffic_matrix = temanager.generate_traffic_matrix(connection_request) print(f"traffic_matrix: {traffic_matrix}") return traffic_matrix - def _make_tm_and_solve(self, request) -> ConnectionSolution: + def _make_tm_and_solve(self, temanager, request) -> ConnectionSolution: """ Make a traffic matrix from plain old style requests (used in the test methods below), and solve it. @@ -1168,7 +1190,7 @@ def _make_tm_and_solve(self, request) -> ConnectionSolution: tm = self._make_traffic_matrix_from_list(request) print(f"tm: {tm}") - graph = self.temanager.generate_graph_te() + graph = temanager.generate_graph_te() print(f"graph: {graph}") print(f"graph: {pprint.pformat(nx.to_dict_of_dicts(graph))}") @@ -1522,7 +1544,7 @@ def test_disallowed_vlan(self): # point, because we asked for a VLAN tag that is not present # on the port. with self.assertRaises(TEError): - self.temanager.generate_connection_breakdown(solution, connection_request) + temanager.generate_connection_breakdown(solution, connection_request) def test_vlan_range_one_domain(self): """ From fd90ad5bfe7b8bbe5afc1a58d558d44ffa819e24 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Mon, 4 Nov 2024 15:26:15 -0600 Subject: [PATCH 20/55] Load topology data in one step --- tests/test_te_manager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 8ba5b90f..7973a774 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1571,9 +1571,7 @@ def test_vlan_range_one_domain(self): """ ) - temanager = TEManager(topology_data=None) - - temanager.add_topology( + temanager = TEManager(topology_data= json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_v2.read_text()) ) From bf7bae4474a6226984a3f68ffa038f50509cd37f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 5 Nov 2024 20:50:41 -0600 Subject: [PATCH 21/55] Add a multi-domain multi-VLAN test stub --- tests/test_te_manager.py | 54 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 7973a774..75748256 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1571,8 +1571,8 @@ def test_vlan_range_one_domain(self): """ ) - temanager = TEManager(topology_data= - json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_v2.read_text()) + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_v2.read_text()) ) graph = temanager.generate_graph_te() @@ -1627,6 +1627,56 @@ def test_vlan_range_one_domain(self): ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath2:50" ) + def test_vlan_range_three_domains(self): + """ + Test when requests are for a range like [n:m], and port + allocations span multiple domains. + """ + + connection_request = json.loads( + """ + { + "name": "new-connection", + "id": "test-connection-id", + "endpoints": [ + { + "port_id": "urn:sdx:port:ampath.net:Ampath1:50", + "vlan": "100:200" + }, + { + "port_id": "urn:sdx:port:sax.net:Sax01:50", + "vlan": "100:200" + } + ] + } + """ + ) + + temanager = TEManager(topology_data=None) + + for topology_file in [ + TestData.TOPOLOGY_FILE_AMLIGHT_v2, + TestData.TOPOLOGY_FILE_ZAOXI_v2, + TestData.TOPOLOGY_FILE_SAX_v2, + ]: + temanager.add_topology(json.loads(topology_file.read_text())) + + temanager = TEManager( + topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_v2.read_text()) + ) + + graph = temanager.generate_graph_te() + traffic_matrix = temanager.generate_traffic_matrix(connection_request) + + # TODO: debug this: why is traffic_matrix None? + print(f"Generated graph: '{graph}', traffic matrix: '{traffic_matrix}'") + + self.assertIsNotNone(graph) + self.assertIsNotNone(traffic_matrix) + + conn = temanager.requests_connectivity(traffic_matrix) + print(f"Graph connectivity: {conn}") + def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ A helper to compare requested VLAN against the VLAN assignment From 068d613cec7fe701104a911c3cb07db13d40ab72 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 7 Nov 2024 19:38:24 -0500 Subject: [PATCH 22/55] Remove shadowed temanager --- tests/test_te_manager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 75748256..d75a38b0 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1661,10 +1661,6 @@ def test_vlan_range_three_domains(self): ]: temanager.add_topology(json.loads(topology_file.read_text())) - temanager = TEManager( - topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_v2.read_text()) - ) - graph = temanager.generate_graph_te() traffic_matrix = temanager.generate_traffic_matrix(connection_request) From 713f92bc630444c4078654bdef8c0780c59e4a68 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 7 Nov 2024 19:57:00 -0500 Subject: [PATCH 23/55] Trim trailing whitespace --- tests/test_te_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index d75a38b0..e00d2d8e 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1555,7 +1555,7 @@ def test_vlan_range_one_domain(self): connection_request = json.loads( """ { - "name": "new-connection", + "name": "new-connection", "id": "test-connection-id", "endpoints": [ { From fdce39a54390b5a16ddaaf480a33a40c28f8318e Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 7 Nov 2024 20:39:57 -0500 Subject: [PATCH 24/55] Use a dict to represent connection request --- tests/test_te_manager.py | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index e00d2d8e..f3ec266f 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1633,24 +1633,15 @@ def test_vlan_range_three_domains(self): allocations span multiple domains. """ - connection_request = json.loads( - """ - { - "name": "new-connection", - "id": "test-connection-id", - "endpoints": [ - { - "port_id": "urn:sdx:port:ampath.net:Ampath1:50", - "vlan": "100:200" - }, - { - "port_id": "urn:sdx:port:sax.net:Sax01:50", - "vlan": "100:200" - } - ] - } - """ - ) + connection_request = { + "name": "new-connection", + "id": "test-connection-id", + "endpoints": [ + {"port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200"}, + # {"port_id": "urn:sdx:port:tenet.ac.za:Tenet01:50", "vlan": "100:200"}, + {"port_id": "urn:sdx:port:sax.net:Sax01:50", "vlan": "100:200"}, + ], + } temanager = TEManager(topology_data=None) From 25e4f7312521dd1a3934e9cfc73101f9538d1dad Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 7 Nov 2024 20:40:32 -0500 Subject: [PATCH 25/55] Get a breakdown --- tests/test_te_manager.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index f3ec266f..51336386 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1664,6 +1664,30 @@ def test_vlan_range_three_domains(self): conn = temanager.requests_connectivity(traffic_matrix) print(f"Graph connectivity: {conn}") + solution = TESolver(graph, traffic_matrix).solve() + print(f"TESolver result: {solution}") + + self.assertIsNotNone(solution) + + breakdown = temanager.generate_connection_breakdown( + solution, connection_request + ) + print(f"{breakdown}") + + self.assertIsNotNone(breakdown) + self.assertIsInstance(breakdown, dict) + self.assertEqual(len(breakdown), 2) + + ampath = breakdown.get("urn:sdx:topology:ampath.net") + sax = breakdown.get("urn:sdx:topology:sax.net") + zaoxi = breakdown.get("urn:sdx:topology:zaoxi.net") + + # TODO: form a request that will result in a breakdown that + # spans all three domains. + self.assertIsNotNone(ampath) + self.assertIsNotNone(sax) + self.assertIsNone(zaoxi) + def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ A helper to compare requested VLAN against the VLAN assignment From 2214448a948e3bd85d3d386b8df4b068e4b87a77 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Thu, 7 Nov 2024 20:57:02 -0500 Subject: [PATCH 26/55] Add more assertions --- tests/test_te_manager.py | 63 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 51336386..6f15202c 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1688,6 +1688,69 @@ def test_vlan_range_three_domains(self): self.assertIsNotNone(sax) self.assertIsNone(zaoxi) + print(f"ampath: {ampath}") + print(f"sax: {sax}") + + self.assertIsNotNone(ampath) + + self.assertIsInstance(ampath.get("name"), str) + self.assertEqual(ampath.get("dynamic_backup_path"), True) + + self.assertIsInstance(ampath.get("uni_a"), dict) + self.assertIsInstance(ampath.get("uni_a").get("tag"), dict) + self.assertIsInstance(ampath.get("uni_a").get("tag").get("value"), str) + self.assertEqual(ampath.get("uni_a").get("tag").get("value"), "100:200") + self.assertIsInstance(ampath.get("uni_a").get("tag").get("tag_type"), int) + self.assertIsInstance(ampath.get("uni_a").get("port_id"), str) + self.assertEqual( + ampath.get("uni_a").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:50" + ) + + self.assertIsInstance(ampath.get("uni_z"), dict) + self.assertIsInstance(ampath.get("uni_z").get("tag"), dict) + + # self.assertIsInstance(ampath.get("uni_z").get("tag").get("value"), str) + # self.assertEqual(ampath.get("uni_z").get("tag").get("value"), "100:200") + + # TODO -- we need a range here. + t1 = ampath.get("uni_z").get("tag").get("value") + self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t1}") + + self.assertIsInstance(ampath.get("uni_z").get("tag").get("tag_type"), int) + self.assertIsInstance(ampath.get("uni_z").get("port_id"), str) + self.assertEqual( + ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:40" + ) + + self.assertIsInstance(sax.get("name"), str) + self.assertEqual(sax.get("dynamic_backup_path"), True) + + self.assertIsInstance(sax.get("uni_a"), dict) + self.assertIsInstance(sax.get("uni_a").get("tag"), dict) + + # self.assertIsInstance(sax.get("uni_a").get("tag").get("value"), str) + # self.assertEqual(sax.get("uni_a").get("tag").get("value"), "100:200") + + # TODO -- we need a range here as well. + t2 = ampath.get("uni_z").get("tag").get("value") + self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t2}") + + self.assertIsInstance(sax.get("uni_a").get("tag").get("tag_type"), int) + self.assertIsInstance(sax.get("uni_a").get("port_id"), str) + self.assertEqual( + sax.get("uni_a").get("port_id"), "urn:sdx:port:sax.net:Sax01:40" + ) + + self.assertIsInstance(sax.get("uni_z"), dict) + self.assertIsInstance(sax.get("uni_z").get("tag"), dict) + self.assertIsInstance(sax.get("uni_z").get("tag").get("value"), str) + self.assertEqual(sax.get("uni_z").get("tag").get("value"), "100:200") + self.assertIsInstance(sax.get("uni_z").get("tag").get("tag_type"), int) + self.assertIsInstance(sax.get("uni_z").get("port_id"), str) + self.assertEqual( + sax.get("uni_z").get("port_id"), "urn:sdx:port:sax.net:Sax01:50" + ) + def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ A helper to compare requested VLAN against the VLAN assignment From bdfb2e8fef5c8e080a3626c77431440f2e1308c9 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 8 Nov 2024 15:57:36 -0500 Subject: [PATCH 27/55] Try a connection request that is solvable --- tests/test_te_manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 6f15202c..3f9c8724 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1637,9 +1637,11 @@ def test_vlan_range_three_domains(self): "name": "new-connection", "id": "test-connection-id", "endpoints": [ - {"port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200"}, + {"port_id": "urn:sdx:port:ampath.net:Ampath1:1", "vlan": "100:200"}, + # {"port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200"}, # {"port_id": "urn:sdx:port:tenet.ac.za:Tenet01:50", "vlan": "100:200"}, - {"port_id": "urn:sdx:port:sax.net:Sax01:50", "vlan": "100:200"}, + # {"port_id": "urn:sdx:port:sax.net:Sax01:50", "vlan": "100:200"}, + {"port_id": "urn:sdx:port:sax.net:Sax01:41", "vlan": "100:200"}, ], } From 3a633daf67819d24422b7c505f076c52fd535292 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 8 Nov 2024 15:58:07 -0500 Subject: [PATCH 28/55] Do not validate port IDs until we have a known good request/solution --- tests/test_te_manager.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 3f9c8724..78534331 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1704,9 +1704,9 @@ def test_vlan_range_three_domains(self): self.assertEqual(ampath.get("uni_a").get("tag").get("value"), "100:200") self.assertIsInstance(ampath.get("uni_a").get("tag").get("tag_type"), int) self.assertIsInstance(ampath.get("uni_a").get("port_id"), str) - self.assertEqual( - ampath.get("uni_a").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:50" - ) + # self.assertEqual( + # ampath.get("uni_a").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:50" + # ) self.assertIsInstance(ampath.get("uni_z"), dict) self.assertIsInstance(ampath.get("uni_z").get("tag"), dict) @@ -1720,9 +1720,9 @@ def test_vlan_range_three_domains(self): self.assertIsInstance(ampath.get("uni_z").get("tag").get("tag_type"), int) self.assertIsInstance(ampath.get("uni_z").get("port_id"), str) - self.assertEqual( - ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:40" - ) + # self.assertEqual( + # ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:40" + # ) self.assertIsInstance(sax.get("name"), str) self.assertEqual(sax.get("dynamic_backup_path"), True) @@ -1739,9 +1739,9 @@ def test_vlan_range_three_domains(self): self.assertIsInstance(sax.get("uni_a").get("tag").get("tag_type"), int) self.assertIsInstance(sax.get("uni_a").get("port_id"), str) - self.assertEqual( - sax.get("uni_a").get("port_id"), "urn:sdx:port:sax.net:Sax01:40" - ) + # self.assertEqual( + # sax.get("uni_a").get("port_id"), "urn:sdx:port:sax.net:Sax01:40" + # ) self.assertIsInstance(sax.get("uni_z"), dict) self.assertIsInstance(sax.get("uni_z").get("tag"), dict) @@ -1749,9 +1749,9 @@ def test_vlan_range_three_domains(self): self.assertEqual(sax.get("uni_z").get("tag").get("value"), "100:200") self.assertIsInstance(sax.get("uni_z").get("tag").get("tag_type"), int) self.assertIsInstance(sax.get("uni_z").get("port_id"), str) - self.assertEqual( - sax.get("uni_z").get("port_id"), "urn:sdx:port:sax.net:Sax01:50" - ) + # self.assertEqual( + # sax.get("uni_z").get("port_id"), "urn:sdx:port:sax.net:Sax01:50" + # ) def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ From ac27d5851c18c7ca3372a4288707e8a47b2407e6 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Sat, 9 Nov 2024 07:30:47 -0500 Subject: [PATCH 29/55] Pass connection request around, not just request ID So that we can find out what VLAN range was requested --- src/sdx_pce/topology/temanager.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 7a9e249a..dde188d4 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -606,7 +606,7 @@ def generate_connection_breakdown( tagged_breakdown = self._reserve_vlan_breakdown( domain_breakdown=domain_breakdown, - request_id=solution.request_id, + connection_request=connection_request, ingress_user_port=ingress_user_port, egress_user_port=egress_user_port, ) @@ -678,7 +678,7 @@ def _get_ports_by_link(self, link: ConnectionPath): def _reserve_vlan_breakdown( self, domain_breakdown: dict, - request_id: str, + connection_request: dict, ingress_user_port=None, egress_user_port=None, ) -> Optional[VlanTaggedBreakdowns]: @@ -728,8 +728,13 @@ def _reserve_vlan_breakdown( upstream_egress = segment.get("egress_port") downstream_ingress = next_segment.get("ingress_port") + upstream_egress_vlan = self._find_common_vlan_on_link( - domain, upstream_egress["id"], next_domain, downstream_ingress["id"] + domain, + upstream_egress["id"], + next_domain, + downstream_ingress["id"], + connection_request, ) self._logger.info( f"upstream_egress_vlan: {upstream_egress_vlan}; upstream_egress: {upstream_egress}; downstream_ingress: {downstream_ingress}" @@ -897,7 +902,12 @@ def _reserve_vlan_on_path(self, domain_breakdown, selected_vlan): assert False, "Not implemented" def _find_common_vlan_on_link( - self, domain, upstream_egress, next_domain, downstream_ingress + self, + domain, + upstream_egress, + next_domain, + downstream_ingress, + connection_request=None, ) -> Optional[str]: """ Find a common VLAN on the inter-domain link. From 4b74b850ff56f92ceba5ecaf7c51cbbffa0d94ef Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Sat, 9 Nov 2024 07:31:26 -0500 Subject: [PATCH 30/55] Generate a request ID for old-style connection requests --- src/sdx_pce/topology/temanager.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index dde188d4..9a50901a 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -712,6 +712,18 @@ def _reserve_vlan_breakdown( # if not, assuming vlan translation on the domain border port + self._logger.info(f"Assigning VLANs for request: {connection_request}") + + # TODO: Generating a request_id locally is a workaround until + # we get rid of the old style connection request. + if isinstance(connection_request, dict): + request_id = connection_request.get("id") + else: + from uuid import uuid4 + + request_id = uuid4() + self._logger.warning(f"Generated ID {request_id} for old style request") + self._logger.info( f"reserve_vlan_breakdown: domain_breakdown: {domain_breakdown}" ) From e7c77158bf6025384dc5eb2cf7b012327fe620a1 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Sat, 9 Nov 2024 08:06:27 -0500 Subject: [PATCH 31/55] Find common inter-domain VLAN ranges --- src/sdx_pce/topology/temanager.py | 44 ++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 9a50901a..e24b481f 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -938,6 +938,47 @@ def _find_common_vlan_on_link( downstream_vlan_table.keys() ) + self._logger.info( + f"Looking for common VLANS for connection_request: {connection_request}" + ) + + # TODO: shouldn't we update VLAN allocation tables here? + + # TODO: This is a work-around to find out if a VLAN range was + # specified in the connection request, and then handle it + # accordingly. This code could probably be simplified if we + # use a "proper" data structure to represent the original + # connection request internally. + if connection_request and isinstance(connection_request, dict): + ingress_vlans_str = connection_request.get("ingress_port").get("vlan_range") + egress_vlans_str = connection_request.get("egress_port").get("vlan_range") + + self._logger.info( + f"Found ingress_vlans: {ingress_vlans_str}, " + f"egress_vlans: {egress_vlans_str}" + ) + + if self._tag_is_vlan_range(ingress_vlans_str) and self._tag_is_vlan_range( + egress_vlans_str + ): + start, end = map(int, ingress_vlans_str.split(":")) + vlans = list(range(start, end + 1)) + + for vlan in vlans: + if upstream_vlan_table[vlan] is not UNUSED_VLAN: + raise Exception( + f"Upstream VLAN {vlan} is in use; can't reserve {tag} range" + ) + + if downstream_vlan_table[vlan] is not UNUSED_VLAN: + raise Exception( + f"Downstream VLAN {vlan} is in use; can't reserve {tag} range" + ) + + # We'll simply assume that both ingress and egress + # ranges are the same. + return ingress_vlans_str + for vlan in common_vlans: if ( upstream_vlan_table[vlan] is UNUSED_VLAN @@ -946,7 +987,8 @@ def _find_common_vlan_on_link( return vlan self._logger.warning( - f"No common VLAN found between {domain} and {next_domain} for ports {upstream_egress} and {downstream_ingress}" + f"No common VLAN found between {domain} and {next_domain} " + f"for ports {upstream_egress} and {downstream_ingress}" ) return None From 354c1011e4e3b086c9b5ec0afa250ffde1a7f234 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 14:19:22 -0600 Subject: [PATCH 32/55] Use correct variable --- src/sdx_pce/topology/temanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index e24b481f..77265326 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -967,12 +967,12 @@ def _find_common_vlan_on_link( for vlan in vlans: if upstream_vlan_table[vlan] is not UNUSED_VLAN: raise Exception( - f"Upstream VLAN {vlan} is in use; can't reserve {tag} range" + f"Upstream VLAN {vlan} is in use; can't reserve {ingress_vlans_str} range" ) if downstream_vlan_table[vlan] is not UNUSED_VLAN: raise Exception( - f"Downstream VLAN {vlan} is in use; can't reserve {tag} range" + f"Downstream VLAN {vlan} is in use; can't reserve {egress_vlans_str} range" ) # We'll simply assume that both ingress and egress From 180ae9c5534c8eba9c089b4024fb29933e10d873 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 14:23:52 -0600 Subject: [PATCH 33/55] Do the check in two steps --- src/sdx_pce/topology/temanager.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 77265326..90fb1709 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -958,9 +958,16 @@ def _find_common_vlan_on_link( f"egress_vlans: {egress_vlans_str}" ) - if self._tag_is_vlan_range(ingress_vlans_str) and self._tag_is_vlan_range( - egress_vlans_str - ): + # Do we have a range of VLANs to handle? + if self._tag_is_vlan_range(ingress_vlans_str): + + # It is quite unlikely that we'll ever get to this + # error state, but this is worth checking anyway. + if not self._tag_is_vlan_range(egress_vlans_str): + raise Exception( + f"VLAN range requested on ingress, but not on egress" + ) + start, end = map(int, ingress_vlans_str.split(":")) vlans = list(range(start, end + 1)) From 25601d358652a5ca2b1b1052781429bc8b66e8cc Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 14:41:43 -0600 Subject: [PATCH 34/55] Split error string --- src/sdx_pce/topology/temanager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 90fb1709..628ad2d4 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -974,12 +974,14 @@ def _find_common_vlan_on_link( for vlan in vlans: if upstream_vlan_table[vlan] is not UNUSED_VLAN: raise Exception( - f"Upstream VLAN {vlan} is in use; can't reserve {ingress_vlans_str} range" + f"Upstream VLAN {vlan} is in use; " + f"can't reserve {ingress_vlans_str} range" ) if downstream_vlan_table[vlan] is not UNUSED_VLAN: raise Exception( - f"Downstream VLAN {vlan} is in use; can't reserve {egress_vlans_str} range" + f"Downstream VLAN {vlan} is in use; " + f"can't reserve {egress_vlans_str} range" ) # We'll simply assume that both ingress and egress From b106f841489f59afc25de742241a28a0e780a1c2 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 14:43:17 -0600 Subject: [PATCH 35/55] Add hints to error string --- src/sdx_pce/topology/temanager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 628ad2d4..c59d003e 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -965,7 +965,8 @@ def _find_common_vlan_on_link( # error state, but this is worth checking anyway. if not self._tag_is_vlan_range(egress_vlans_str): raise Exception( - f"VLAN range requested on ingress, but not on egress" + f"VLAN range {ingress_vlans_str} requested on ingress, " + f"but not on egress (egress: {egress_vlans_str}" ) start, end = map(int, ingress_vlans_str.split(":")) From 5ee60e8434e9de328dcfa812c35c2aa54f087ac3 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 14:44:30 -0600 Subject: [PATCH 36/55] Update comment --- src/sdx_pce/topology/temanager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index c59d003e..cbfbd0eb 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -944,11 +944,11 @@ def _find_common_vlan_on_link( # TODO: shouldn't we update VLAN allocation tables here? - # TODO: This is a work-around to find out if a VLAN range was - # specified in the connection request, and then handle it - # accordingly. This code could probably be simplified if we - # use a "proper" data structure to represent the original - # connection request internally. + # TODO: The block below is a work-around to find out if a VLAN + # range was specified in the connection request, and then + # handle it accordingly. This code could probably be + # simplified if we use a "proper" data structure to represent + # the original connection request internally. if connection_request and isinstance(connection_request, dict): ingress_vlans_str = connection_request.get("ingress_port").get("vlan_range") egress_vlans_str = connection_request.get("egress_port").get("vlan_range") From f94efb8e307f2c34f7d75e0da1d79302d2460ab5 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 14:46:58 -0600 Subject: [PATCH 37/55] Update comment --- src/sdx_pce/topology/temanager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index cbfbd0eb..8b5c0a2a 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -961,6 +961,10 @@ def _find_common_vlan_on_link( # Do we have a range of VLANs to handle? if self._tag_is_vlan_range(ingress_vlans_str): + # Both ingress and egress ranges should be the same + # for inter-domain links, since we (currently) infer + # it from the original request. + # # It is quite unlikely that we'll ever get to this # error state, but this is worth checking anyway. if not self._tag_is_vlan_range(egress_vlans_str): @@ -985,8 +989,6 @@ def _find_common_vlan_on_link( f"can't reserve {egress_vlans_str} range" ) - # We'll simply assume that both ingress and egress - # ranges are the same. return ingress_vlans_str for vlan in common_vlans: From 112ffbc7e49cd5e625c27cefa6de5f7f9636d6c3 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 14:48:05 -0600 Subject: [PATCH 38/55] Remove TODO The comment is not quite a TODO, just an explanation about the way things are. --- src/sdx_pce/topology/temanager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 8b5c0a2a..cb4d05e3 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -944,11 +944,11 @@ def _find_common_vlan_on_link( # TODO: shouldn't we update VLAN allocation tables here? - # TODO: The block below is a work-around to find out if a VLAN - # range was specified in the connection request, and then - # handle it accordingly. This code could probably be - # simplified if we use a "proper" data structure to represent - # the original connection request internally. + # The block below is a work-around to find out if a VLAN range + # was specified in the connection request, and then handle it + # accordingly. This code could probably be simplified if we + # use a "proper" data structure to represent the original + # connection request internally. if connection_request and isinstance(connection_request, dict): ingress_vlans_str = connection_request.get("ingress_port").get("vlan_range") egress_vlans_str = connection_request.get("egress_port").get("vlan_range") From 4c5200794dd4e5408d1a02314a40a8f2a27f1b84 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 14:54:32 -0600 Subject: [PATCH 39/55] Rename test --- tests/test_te_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 78534331..b27cfc08 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1627,7 +1627,7 @@ def test_vlan_range_one_domain(self): ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath2:50" ) - def test_vlan_range_three_domains(self): + def test_vlan_range_two_domains(self): """ Test when requests are for a range like [n:m], and port allocations span multiple domains. From 8d4d969ccf03033542ed7914d35d9b341e9b120f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 15:37:04 -0600 Subject: [PATCH 40/55] Reduce log verbosity --- src/sdx_pce/topology/temanager.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index cb4d05e3..a8997f4b 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -1063,7 +1063,7 @@ def _reserve_vlan( vlan_table = domain_table.get(port_id) - self._logger.debug(f"reserve_vlan domain: {domain} vlan_table: {vlan_table}") + # self._logger.debug(f"reserve_vlan domain: {domain} vlan_table: {vlan_table}") if vlan_table is None: self._logger.warning( @@ -1098,10 +1098,10 @@ def _reserve_vlan( for vlan in vlans: vlan_table[vlan] = request_id - self._logger.debug( - f"reserve_vlan domain {domain}, after reservation: " - f"vlan_table: {vlan_table}, requested range: {tag}" - ) + # self._logger.debug( + # f"reserve_vlan domain {domain}, after reservation: " + # f"vlan_table: {vlan_table}, requested range: {tag}" + # ) # Return the tag to indicate success. return tag @@ -1120,10 +1120,10 @@ def _reserve_vlan( # mark the tag as in-use. vlan_table[available_tag] = request_id - self._logger.debug( - f"reserve_vlan domain {domain}, after reservation: " - f"vlan_table: {vlan_table}, available_tag: {available_tag}" - ) + # self._logger.debug( + # f"reserve_vlan domain {domain}, after reservation: " + # f"vlan_table: {vlan_table}, available_tag: {available_tag}" + # ) return available_tag From 06b6ee66f2ec43ec46e00501db0cedbbad7939bb Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 16:28:53 -0600 Subject: [PATCH 41/55] Use a request that results in connections spanning three domains --- tests/test_te_manager.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index b27cfc08..a5dba0db 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1627,21 +1627,18 @@ def test_vlan_range_one_domain(self): ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath2:50" ) - def test_vlan_range_two_domains(self): + def test_vlan_range_three_domains(self): """ Test when requests are for a range like [n:m], and port allocations span multiple domains. """ connection_request = { - "name": "new-connection", - "id": "test-connection-id", + "name": "new-connection-three-domains", + "id": "test-connection-id-three-domains", "endpoints": [ - {"port_id": "urn:sdx:port:ampath.net:Ampath1:1", "vlan": "100:200"}, - # {"port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200"}, - # {"port_id": "urn:sdx:port:tenet.ac.za:Tenet01:50", "vlan": "100:200"}, - # {"port_id": "urn:sdx:port:sax.net:Sax01:50", "vlan": "100:200"}, - {"port_id": "urn:sdx:port:sax.net:Sax01:41", "vlan": "100:200"}, + {"port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200"}, + {"port_id": "urn:sdx:port:tenet.ac.za:Tenet01:50", "vlan": "100:200"}, ], } @@ -1657,7 +1654,6 @@ def test_vlan_range_two_domains(self): graph = temanager.generate_graph_te() traffic_matrix = temanager.generate_traffic_matrix(connection_request) - # TODO: debug this: why is traffic_matrix None? print(f"Generated graph: '{graph}', traffic matrix: '{traffic_matrix}'") self.assertIsNotNone(graph) @@ -1678,20 +1674,19 @@ def test_vlan_range_two_domains(self): self.assertIsNotNone(breakdown) self.assertIsInstance(breakdown, dict) - self.assertEqual(len(breakdown), 2) + self.assertEqual(len(breakdown), 3) ampath = breakdown.get("urn:sdx:topology:ampath.net") sax = breakdown.get("urn:sdx:topology:sax.net") - zaoxi = breakdown.get("urn:sdx:topology:zaoxi.net") + tenet = breakdown.get("urn:sdx:topology:tenet.ac.za") - # TODO: form a request that will result in a breakdown that - # spans all three domains. self.assertIsNotNone(ampath) self.assertIsNotNone(sax) - self.assertIsNone(zaoxi) + self.assertIsNotNone(tenet) print(f"ampath: {ampath}") print(f"sax: {sax}") + print(f"tenet: {tenet}") self.assertIsNotNone(ampath) From bab638b1e2c2fd75660f2c167bd7ebbed7c09632 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 16:29:37 -0600 Subject: [PATCH 42/55] Knock a couple of TODOs off --- tests/test_te_manager.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index a5dba0db..92a2e133 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1709,7 +1709,6 @@ def test_vlan_range_three_domains(self): # self.assertIsInstance(ampath.get("uni_z").get("tag").get("value"), str) # self.assertEqual(ampath.get("uni_z").get("tag").get("value"), "100:200") - # TODO -- we need a range here. t1 = ampath.get("uni_z").get("tag").get("value") self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t1}") @@ -1728,7 +1727,6 @@ def test_vlan_range_three_domains(self): # self.assertIsInstance(sax.get("uni_a").get("tag").get("value"), str) # self.assertEqual(sax.get("uni_a").get("tag").get("value"), "100:200") - # TODO -- we need a range here as well. t2 = ampath.get("uni_z").get("tag").get("value") self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t2}") From 46d38e66b7a0b20cc50e4b8960e9e5633e043a76 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 16:34:03 -0600 Subject: [PATCH 43/55] Update asserts --- tests/test_te_manager.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 92a2e133..dd399313 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1688,53 +1688,53 @@ def test_vlan_range_three_domains(self): print(f"sax: {sax}") print(f"tenet: {tenet}") - self.assertIsNotNone(ampath) - + # Check Ampath part of the breakdown. self.assertIsInstance(ampath.get("name"), str) self.assertEqual(ampath.get("dynamic_backup_path"), True) self.assertIsInstance(ampath.get("uni_a"), dict) self.assertIsInstance(ampath.get("uni_a").get("tag"), dict) + self.assertIsInstance(ampath.get("uni_a").get("tag").get("value"), str) self.assertEqual(ampath.get("uni_a").get("tag").get("value"), "100:200") self.assertIsInstance(ampath.get("uni_a").get("tag").get("tag_type"), int) self.assertIsInstance(ampath.get("uni_a").get("port_id"), str) - # self.assertEqual( - # ampath.get("uni_a").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:50" - # ) + self.assertEqual( + ampath.get("uni_a").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:50" + ) self.assertIsInstance(ampath.get("uni_z"), dict) self.assertIsInstance(ampath.get("uni_z").get("tag"), dict) - - # self.assertIsInstance(ampath.get("uni_z").get("tag").get("value"), str) - # self.assertEqual(ampath.get("uni_z").get("tag").get("value"), "100:200") + self.assertIsInstance(ampath.get("uni_z").get("tag").get("value"), str) + self.assertEqual(ampath.get("uni_z").get("tag").get("value"), "100:200") t1 = ampath.get("uni_z").get("tag").get("value") self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t1}") self.assertIsInstance(ampath.get("uni_z").get("tag").get("tag_type"), int) self.assertIsInstance(ampath.get("uni_z").get("port_id"), str) - # self.assertEqual( - # ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:40" - # ) + self.assertEqual( + ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:40" + ) + # Check SAX part of the breakdown. self.assertIsInstance(sax.get("name"), str) self.assertEqual(sax.get("dynamic_backup_path"), True) self.assertIsInstance(sax.get("uni_a"), dict) self.assertIsInstance(sax.get("uni_a").get("tag"), dict) - # self.assertIsInstance(sax.get("uni_a").get("tag").get("value"), str) - # self.assertEqual(sax.get("uni_a").get("tag").get("value"), "100:200") + self.assertIsInstance(sax.get("uni_a").get("tag").get("value"), str) + self.assertEqual(sax.get("uni_a").get("tag").get("value"), "100:200") t2 = ampath.get("uni_z").get("tag").get("value") self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t2}") self.assertIsInstance(sax.get("uni_a").get("tag").get("tag_type"), int) self.assertIsInstance(sax.get("uni_a").get("port_id"), str) - # self.assertEqual( - # sax.get("uni_a").get("port_id"), "urn:sdx:port:sax.net:Sax01:40" - # ) + self.assertEqual( + sax.get("uni_a").get("port_id"), "urn:sdx:port:sax.net:Sax01:40" + ) self.assertIsInstance(sax.get("uni_z"), dict) self.assertIsInstance(sax.get("uni_z").get("tag"), dict) @@ -1742,9 +1742,9 @@ def test_vlan_range_three_domains(self): self.assertEqual(sax.get("uni_z").get("tag").get("value"), "100:200") self.assertIsInstance(sax.get("uni_z").get("tag").get("tag_type"), int) self.assertIsInstance(sax.get("uni_z").get("port_id"), str) - # self.assertEqual( - # sax.get("uni_z").get("port_id"), "urn:sdx:port:sax.net:Sax01:50" - # ) + self.assertEqual( + sax.get("uni_z").get("port_id"), "urn:sdx:port:sax.net:Sax01:41" + ) def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ From a8338b6cc3b6b913c2185edd18288cdf8d7b7a5b Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 16:36:04 -0600 Subject: [PATCH 44/55] Check tenet part of the breakdown --- tests/test_te_manager.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index dd399313..3eeb49c6 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1746,6 +1746,35 @@ def test_vlan_range_three_domains(self): sax.get("uni_z").get("port_id"), "urn:sdx:port:sax.net:Sax01:41" ) + # Check Tenet part of the breakdown. + self.assertIsInstance(tenet.get("name"), str) + self.assertEqual(tenet.get("dynamic_backup_path"), True) + + self.assertIsInstance(tenet.get("uni_a"), dict) + self.assertIsInstance(tenet.get("uni_a").get("tag"), dict) + + self.assertIsInstance(tenet.get("uni_a").get("tag").get("value"), str) + self.assertEqual(tenet.get("uni_a").get("tag").get("value"), "100:200") + + t2 = ampath.get("uni_z").get("tag").get("value") + self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t2}") + + self.assertIsInstance(tenet.get("uni_a").get("tag").get("tag_type"), int) + self.assertIsInstance(tenet.get("uni_a").get("port_id"), str) + self.assertEqual( + tenet.get("uni_a").get("port_id"), "urn:sdx:port:tenet.ac.za:Tenet01:41" + ) + + self.assertIsInstance(tenet.get("uni_z"), dict) + self.assertIsInstance(tenet.get("uni_z").get("tag"), dict) + self.assertIsInstance(tenet.get("uni_z").get("tag").get("value"), str) + self.assertEqual(tenet.get("uni_z").get("tag").get("value"), "100:200") + self.assertIsInstance(tenet.get("uni_z").get("tag").get("tag_type"), int) + self.assertIsInstance(tenet.get("uni_z").get("port_id"), str) + self.assertEqual( + tenet.get("uni_z").get("port_id"), "urn:sdx:port:tenet.ac.za:Tenet01:50" + ) + def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ A helper to compare requested VLAN against the VLAN assignment From e1cd951c0dbcabff59b0235784b92d68df66be36 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 16:36:59 -0600 Subject: [PATCH 45/55] Use a different name and id in test request --- tests/test_te_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 3eeb49c6..cc6d75f1 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1634,8 +1634,8 @@ def test_vlan_range_three_domains(self): """ connection_request = { - "name": "new-connection-three-domains", - "id": "test-connection-id-three-domains", + "name": "vlan-range-three-domains", + "id": "id-vlan-range-three-domains", "endpoints": [ {"port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200"}, {"port_id": "urn:sdx:port:tenet.ac.za:Tenet01:50", "vlan": "100:200"}, From e794f1929f552dc1b9d1b96da0993bb6e7956aab Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Tue, 12 Nov 2024 16:38:14 -0600 Subject: [PATCH 46/55] Print before assert --- tests/test_te_manager.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index cc6d75f1..745e1a19 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1680,14 +1680,14 @@ def test_vlan_range_three_domains(self): sax = breakdown.get("urn:sdx:topology:sax.net") tenet = breakdown.get("urn:sdx:topology:tenet.ac.za") - self.assertIsNotNone(ampath) - self.assertIsNotNone(sax) - self.assertIsNotNone(tenet) - print(f"ampath: {ampath}") print(f"sax: {sax}") print(f"tenet: {tenet}") + self.assertIsNotNone(ampath) + self.assertIsNotNone(sax) + self.assertIsNotNone(tenet) + # Check Ampath part of the breakdown. self.assertIsInstance(ampath.get("name"), str) self.assertEqual(ampath.get("dynamic_backup_path"), True) From cb503487f6dc475e9217a30d90c360b91c95e602 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 13 Nov 2024 10:16:11 -0600 Subject: [PATCH 47/55] Use simpler dict comparison in multi-domain vlan range test --- tests/test_te_manager.py | 137 ++++++++++++--------------------------- 1 file changed, 40 insertions(+), 97 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 745e1a19..17d75c71 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1676,104 +1676,47 @@ def test_vlan_range_three_domains(self): self.assertIsInstance(breakdown, dict) self.assertEqual(len(breakdown), 3) - ampath = breakdown.get("urn:sdx:topology:ampath.net") - sax = breakdown.get("urn:sdx:topology:sax.net") - tenet = breakdown.get("urn:sdx:topology:tenet.ac.za") - - print(f"ampath: {ampath}") - print(f"sax: {sax}") - print(f"tenet: {tenet}") - - self.assertIsNotNone(ampath) - self.assertIsNotNone(sax) - self.assertIsNotNone(tenet) - - # Check Ampath part of the breakdown. - self.assertIsInstance(ampath.get("name"), str) - self.assertEqual(ampath.get("dynamic_backup_path"), True) - - self.assertIsInstance(ampath.get("uni_a"), dict) - self.assertIsInstance(ampath.get("uni_a").get("tag"), dict) - - self.assertIsInstance(ampath.get("uni_a").get("tag").get("value"), str) - self.assertEqual(ampath.get("uni_a").get("tag").get("value"), "100:200") - self.assertIsInstance(ampath.get("uni_a").get("tag").get("tag_type"), int) - self.assertIsInstance(ampath.get("uni_a").get("port_id"), str) - self.assertEqual( - ampath.get("uni_a").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:50" - ) - - self.assertIsInstance(ampath.get("uni_z"), dict) - self.assertIsInstance(ampath.get("uni_z").get("tag"), dict) - self.assertIsInstance(ampath.get("uni_z").get("tag").get("value"), str) - self.assertEqual(ampath.get("uni_z").get("tag").get("value"), "100:200") - - t1 = ampath.get("uni_z").get("tag").get("value") - self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t1}") - - self.assertIsInstance(ampath.get("uni_z").get("tag").get("tag_type"), int) - self.assertIsInstance(ampath.get("uni_z").get("port_id"), str) - self.assertEqual( - ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:40" - ) - - # Check SAX part of the breakdown. - self.assertIsInstance(sax.get("name"), str) - self.assertEqual(sax.get("dynamic_backup_path"), True) - - self.assertIsInstance(sax.get("uni_a"), dict) - self.assertIsInstance(sax.get("uni_a").get("tag"), dict) - - self.assertIsInstance(sax.get("uni_a").get("tag").get("value"), str) - self.assertEqual(sax.get("uni_a").get("tag").get("value"), "100:200") - - t2 = ampath.get("uni_z").get("tag").get("value") - self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t2}") - - self.assertIsInstance(sax.get("uni_a").get("tag").get("tag_type"), int) - self.assertIsInstance(sax.get("uni_a").get("port_id"), str) - self.assertEqual( - sax.get("uni_a").get("port_id"), "urn:sdx:port:sax.net:Sax01:40" - ) - - self.assertIsInstance(sax.get("uni_z"), dict) - self.assertIsInstance(sax.get("uni_z").get("tag"), dict) - self.assertIsInstance(sax.get("uni_z").get("tag").get("value"), str) - self.assertEqual(sax.get("uni_z").get("tag").get("value"), "100:200") - self.assertIsInstance(sax.get("uni_z").get("tag").get("tag_type"), int) - self.assertIsInstance(sax.get("uni_z").get("port_id"), str) - self.assertEqual( - sax.get("uni_z").get("port_id"), "urn:sdx:port:sax.net:Sax01:41" - ) - - # Check Tenet part of the breakdown. - self.assertIsInstance(tenet.get("name"), str) - self.assertEqual(tenet.get("dynamic_backup_path"), True) - - self.assertIsInstance(tenet.get("uni_a"), dict) - self.assertIsInstance(tenet.get("uni_a").get("tag"), dict) - - self.assertIsInstance(tenet.get("uni_a").get("tag").get("value"), str) - self.assertEqual(tenet.get("uni_a").get("tag").get("value"), "100:200") - - t2 = ampath.get("uni_z").get("tag").get("value") - self.assertTrue(temanager._tag_is_vlan_range(t1), f"range expected, got {t2}") - - self.assertIsInstance(tenet.get("uni_a").get("tag").get("tag_type"), int) - self.assertIsInstance(tenet.get("uni_a").get("port_id"), str) - self.assertEqual( - tenet.get("uni_a").get("port_id"), "urn:sdx:port:tenet.ac.za:Tenet01:41" - ) + expected_breakdown = { + "urn:sdx:topology:ampath.net": { + "name": "AMPATH_vlan_100:200_100:200", + "dynamic_backup_path": True, + "uni_a": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:ampath.net:Ampath1:50", + }, + "uni_z": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:ampath.net:Ampath1:40", + }, + }, + "urn:sdx:topology:sax.net": { + "name": "SAX_vlan_100:200_100:200", + "dynamic_backup_path": True, + "uni_a": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:sax.net:Sax01:40", + }, + "uni_z": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:sax.net:Sax01:41", + }, + }, + "urn:sdx:topology:tenet.ac.za": { + "name": "TENET_vlan_100:200_100:200", + "dynamic_backup_path": True, + "uni_a": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:tenet.ac.za:Tenet01:41", + }, + "uni_z": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:tenet.ac.za:Tenet01:50", + }, + }, + } - self.assertIsInstance(tenet.get("uni_z"), dict) - self.assertIsInstance(tenet.get("uni_z").get("tag"), dict) - self.assertIsInstance(tenet.get("uni_z").get("tag").get("value"), str) - self.assertEqual(tenet.get("uni_z").get("tag").get("value"), "100:200") - self.assertIsInstance(tenet.get("uni_z").get("tag").get("tag_type"), int) - self.assertIsInstance(tenet.get("uni_z").get("port_id"), str) - self.assertEqual( - tenet.get("uni_z").get("port_id"), "urn:sdx:port:tenet.ac.za:Tenet01:50" - ) + self.maxDiff = None + self.assertEqual(breakdown, expected_breakdown) def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ From 7037082b68b0305a872c068e4add2236cf5b90c2 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 13 Nov 2024 10:19:57 -0600 Subject: [PATCH 48/55] Use simpler dict comparison in single-domain vlan range test --- tests/test_te_manager.py | 46 +++++++++++++++------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 17d75c71..4d3014b4 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1595,37 +1595,25 @@ def test_vlan_range_one_domain(self): breakdown = temanager.generate_connection_breakdown( solution, connection_request ) - pprint.pprint(f"Breakdown: {breakdown}") - - self.assertIsNotNone(breakdown) - self.assertIsInstance(breakdown, dict) - self.assertEqual(len(breakdown), 1) - - ampath = breakdown.get("urn:sdx:topology:ampath.net") - self.assertIsNotNone(ampath) - - self.assertEqual(ampath.get("name"), "AMPATH_vlan_100:200_100:200") - self.assertEqual(ampath.get("dynamic_backup_path"), True) + print(f"Breakdown: {breakdown}") - self.assertIsInstance(ampath.get("uni_a"), dict) - self.assertIsInstance(ampath.get("uni_a").get("tag"), dict) - self.assertIsInstance(ampath.get("uni_a").get("tag").get("value"), str) - self.assertEqual(ampath.get("uni_a").get("tag").get("value"), "100:200") - self.assertIsInstance(ampath.get("uni_a").get("tag").get("tag_type"), int) - self.assertIsInstance(ampath.get("uni_a").get("port_id"), str) - self.assertEqual( - ampath.get("uni_a").get("port_id"), "urn:sdx:port:ampath.net:Ampath1:50" - ) + expected_breakdown = { + "urn:sdx:topology:ampath.net": { + "name": "AMPATH_vlan_100:200_100:200", + "dynamic_backup_path": True, + "uni_a": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:ampath.net:Ampath1:50", + }, + "uni_z": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:ampath.net:Ampath2:50", + }, + } + } - self.assertIsInstance(ampath.get("uni_z"), dict) - self.assertIsInstance(ampath.get("uni_z").get("tag"), dict) - self.assertIsInstance(ampath.get("uni_z").get("tag").get("value"), str) - self.assertEqual(ampath.get("uni_z").get("tag").get("value"), "100:200") - self.assertIsInstance(ampath.get("uni_z").get("tag").get("tag_type"), int) - self.assertIsInstance(ampath.get("uni_z").get("port_id"), str) - self.assertEqual( - ampath.get("uni_z").get("port_id"), "urn:sdx:port:ampath.net:Ampath2:50" - ) + self.maxDiff = None + self.assertEqual(breakdown, expected_breakdown) def test_vlan_range_three_domains(self): """ From 2f156c9e08cbb6f2a185995f899102ecbbcb61f2 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 13 Nov 2024 10:22:56 -0600 Subject: [PATCH 49/55] Simplify connection request in single-domain vlan range test --- tests/test_te_manager.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 4d3014b4..b7deb4f9 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1552,24 +1552,14 @@ def test_vlan_range_one_domain(self): domain. """ - connection_request = json.loads( - """ - { - "name": "new-connection", - "id": "test-connection-id", - "endpoints": [ - { - "port_id": "urn:sdx:port:ampath.net:Ampath1:50", - "vlan": "100:200" - }, - { - "port_id": "urn:sdx:port:ampath.net:Ampath2:50", - "vlan": "100:200" - } - ] - } - """ - ) + connection_request = { + "name": "vlan-range-one-domain", + "id": "id-vlan-range-one-domain", + "endpoints": [ + {"port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200"}, + {"port_id": "urn:sdx:port:ampath.net:Ampath2:50", "vlan": "100:200"}, + ], + } temanager = TEManager( topology_data=json.loads(TestData.TOPOLOGY_FILE_AMLIGHT_v2.read_text()) From 77fc449f361645ec543f6a71310468af29c5caab Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 13 Nov 2024 10:24:35 -0600 Subject: [PATCH 50/55] Remove redundant asserts --- tests/test_te_manager.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index b7deb4f9..a39f41de 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1650,10 +1650,6 @@ def test_vlan_range_three_domains(self): ) print(f"{breakdown}") - self.assertIsNotNone(breakdown) - self.assertIsInstance(breakdown, dict) - self.assertEqual(len(breakdown), 3) - expected_breakdown = { "urn:sdx:topology:ampath.net": { "name": "AMPATH_vlan_100:200_100:200", From e04b89214b707289479c1dc00e5da1cc1d1d0b48 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 13 Nov 2024 10:41:45 -0600 Subject: [PATCH 51/55] Add a test for anomalous result for later investigation --- tests/test_te_manager.py | 92 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index a39f41de..139312fa 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1692,6 +1692,98 @@ def test_vlan_range_three_domains(self): self.maxDiff = None self.assertEqual(breakdown, expected_breakdown) + def test_vlan_range_three_domains_anomaly(self): + """ + Test when requests are for a range like [n:m], and port + allocations span multiple domains. + + The anomaly is that we're not getting the port we requested on + egress. This test is added so that we can investigate why + that happens. + """ + + connection_request = { + "name": "vlan-range-three-domains", + "id": "id-vlan-range-three-domains", + "endpoints": [ + {"port_id": "urn:sdx:port:ampath.net:Ampath1:50", "vlan": "100:200"}, + {"port_id": "urn:sdx:port:tenet.ac.za:Tenet01:1", "vlan": "100:200"}, + ], + } + + temanager = TEManager(topology_data=None) + + for topology_file in [ + TestData.TOPOLOGY_FILE_AMLIGHT_v2, + TestData.TOPOLOGY_FILE_ZAOXI_v2, + TestData.TOPOLOGY_FILE_SAX_v2, + ]: + temanager.add_topology(json.loads(topology_file.read_text())) + + graph = temanager.generate_graph_te() + traffic_matrix = temanager.generate_traffic_matrix(connection_request) + + print(f"Generated graph: '{graph}', traffic matrix: '{traffic_matrix}'") + + self.assertIsNotNone(graph) + self.assertIsNotNone(traffic_matrix) + + conn = temanager.requests_connectivity(traffic_matrix) + print(f"Graph connectivity: {conn}") + + solution = TESolver(graph, traffic_matrix).solve() + print(f"TESolver result: {solution}") + + self.assertIsNotNone(solution) + + breakdown = temanager.generate_connection_breakdown( + solution, connection_request + ) + print(f"{breakdown}") + + expected_breakdown = { + "urn:sdx:topology:ampath.net": { + "name": "AMPATH_vlan_100:200_100:200", + "dynamic_backup_path": True, + "uni_a": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:ampath.net:Ampath1:50", + }, + "uni_z": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:ampath.net:Ampath1:40", + }, + }, + "urn:sdx:topology:sax.net": { + "name": "SAX_vlan_100:200_100:200", + "dynamic_backup_path": True, + "uni_a": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:sax.net:Sax01:40", + }, + "uni_z": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:sax.net:Sax01:41", + }, + }, + "urn:sdx:topology:tenet.ac.za": { + "name": "TENET_vlan_100:200_100:200", + "dynamic_backup_path": True, + "uni_a": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:tenet.ac.za:Tenet01:41", + }, + "uni_z": { + "tag": {"value": "100:200", "tag_type": 1}, + "port_id": "urn:sdx:port:tenet.ac.za:Tenet01:50", + }, + }, + } + + # # TODO: disabling this check for now. Will follow-up later. + # self.maxDiff = None + # self.assertEqual(breakdown, expected_breakdown) + def _vlan_meets_request(self, requested_vlan: str, assigned_vlan: int) -> bool: """ A helper to compare requested VLAN against the VLAN assignment From 3b4ce3b3149b0a68d4f5adec6a41714d7fca53f1 Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Wed, 13 Nov 2024 10:47:05 -0600 Subject: [PATCH 52/55] Pacify the linter for now --- tests/test_te_manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_te_manager.py b/tests/test_te_manager.py index 139312fa..1ba9a1df 100644 --- a/tests/test_te_manager.py +++ b/tests/test_te_manager.py @@ -1780,6 +1780,9 @@ def test_vlan_range_three_domains_anomaly(self): }, } + # Use the variable, just to silence the linter. + self.assertIsInstance(expected_breakdown, dict) + # # TODO: disabling this check for now. Will follow-up later. # self.maxDiff = None # self.assertEqual(breakdown, expected_breakdown) From 6a92ebfca8492e5f3245fb21a2d686b0079cc124 Mon Sep 17 00:00:00 2001 From: Yufeng Xin Date: Wed, 13 Nov 2024 20:13:40 -0500 Subject: [PATCH 53/55] throw an exception for failed valn assignment, instead of returning None, https://github.com/atlanticwave-sdx/sdx-controller/issues/356 --- src/sdx_pce/topology/temanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index a8997f4b..259fce00 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -820,7 +820,7 @@ def _reserve_vlan_breakdown( f"Can't proceed. Rolling back reservations." ) self.unreserve_vlan(request_id=request_id) - return None + raise TEError(f"Can't find a vlan assignment for: {connection_request}", 410) ingress_port_id = ingress_port["id"] egress_port_id = egress_port["id"] From 860373d6f14da687e01d38884eb5951c2f7cc87e Mon Sep 17 00:00:00 2001 From: Yufeng Xin Date: Wed, 13 Nov 2024 20:16:14 -0500 Subject: [PATCH 54/55] black --- src/sdx_pce/topology/temanager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sdx_pce/topology/temanager.py b/src/sdx_pce/topology/temanager.py index 259fce00..c28b8bb9 100644 --- a/src/sdx_pce/topology/temanager.py +++ b/src/sdx_pce/topology/temanager.py @@ -820,7 +820,9 @@ def _reserve_vlan_breakdown( f"Can't proceed. Rolling back reservations." ) self.unreserve_vlan(request_id=request_id) - raise TEError(f"Can't find a vlan assignment for: {connection_request}", 410) + raise TEError( + f"Can't find a vlan assignment for: {connection_request}", 410 + ) ingress_port_id = ingress_port["id"] egress_port_id = egress_port["id"] From fe7c20f1413d0d2a01febce49bf42897be5e772f Mon Sep 17 00:00:00 2001 From: Sajith Sasidharan Date: Fri, 15 Nov 2024 09:09:55 -0600 Subject: [PATCH 55/55] Bump version up --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6e52efb3..d2167dd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi" [project] name = "sdx-pce" -version = "3.0.0.dev4" +version = "3.0.0.dev5" description = "Heuristic and Optimal Algorithms for CSP and TE Computation" authors = [ { name = "Yufeng Xin", email = "yxin@renci.org" },