Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Per-domain topology breakdown investigation #113

Merged
merged 27 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9a8fefa
Move topologies to their own subfolder
sajith May 11, 2023
4deafde
Update path to test topology files
sajith May 11, 2023
248f4b5
Ignore output file
sajith May 11, 2023
fa33a2c
Move test data paths to a common class
sajith May 11, 2023
61f761e
Correct a mistake
sajith May 11, 2023
5cbd969
Move all test data paths to a common class
sajith May 11, 2023
8252ea9
Reformat
sajith May 11, 2023
9d36b15
Use a more descriptive name
sajith May 11, 2023
5c12a4a
Print status
sajith May 11, 2023
c382809
Add tearDown
sajith May 11, 2023
52c6f17
Add a test for TopologyManager.get_domain_name()
sajith May 11, 2023
5e0e74f
Sort nodes
sajith May 11, 2023
4669598
Use AmLight topology to test
sajith May 11, 2023
06c4827
Regenerate graph
sajith May 11, 2023
522a1f3
Invalidate TEManager after each test
sajith May 11, 2023
7bd673a
Actually create a breakdown spanning all three domains
sajith May 11, 2023
33e7b81
Add a test case to investigate breakdowns
sajith May 12, 2023
d41bc8c
Add a debug print
sajith May 13, 2023
15a24e6
Fix typo
sajith May 13, 2023
8c93827
Fix typo again
sajith May 14, 2023
5d2f525
Initialize TEManager without topology data
sajith May 14, 2023
f608435
Do not generate a graph yet
sajith May 14, 2023
56b1390
Make topology optional to construct TEManager
sajith May 14, 2023
9ee94bf
Add a note about making topology data optional
sajith May 14, 2023
8f01547
Remove debug print in get_domain_name
sajith May 14, 2023
387b9df
Add a test for the "original" connection request
sajith May 16, 2023
405c106
Add a link to the follow-up issue
sajith May 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ dmypy.json
/tests/data/sdx-out.json
/tests/data/sdx.png
/tests/data/amlight.png
tests/data/topologies/amlight.png

/.coverage
/coverage.lcov
Expand Down
11 changes: 8 additions & 3 deletions src/sdx/pce/topology/temanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,14 @@ def __init__(self, topology_data, connection_data):
self.topology_manager = TopologyManager()
self.connection_handler = ConnectionHandler()

self.topology_manager.add_topology(topology_data)
# Making topology_data optional while investigating
# https://github.com/atlanticwave-sdx/sdx-controller/issues/145.
# TODO: a nicer thing to do would be to keep less state around.
if topology_data:
self.topology_manager.add_topology(topology_data)
self.graph = self.generate_graph_te()
else:
self.graph = None

print(f"TEManager: connection_data: {connection_data}")

Expand All @@ -39,8 +46,6 @@ def __init__(self, topology_data, connection_data):

print(f"TEManager: self.connection: {self.connection}")

self.graph = self.generate_graph_te()

def add_topology(self, topology_data: dict):
"""
Add a new topology to TEManager.
Expand Down
22 changes: 22 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import pathlib


class TestData:
TEST_DATA_DIR = pathlib.Path(__file__).parent / "data"

TOPOLOGY_DATA_DIR = TEST_DATA_DIR / "topologies"

TOPOLOGY_FILE_SDX = TEST_DATA_DIR / "sdx.json"

TOPOLOGY_FILE_ZAOXI = TOPOLOGY_DATA_DIR / "zaoxi.json"
TOPOLOGY_FILE_SAX = TOPOLOGY_DATA_DIR / "sax.json"
TOPOLOGY_FILE_AMLIGHT = TOPOLOGY_DATA_DIR / "amlight.json"

TOPOLOGY_FILE_AMLIGHT_IMG = TOPOLOGY_DATA_DIR / "amlight.png"

CONNECTION_REQ_AMLIGHT_SAX = TEST_DATA_DIR / "test_request.json"
CONNECTION_REQ_FILE = TEST_DATA_DIR / "test_request_amlight.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"
CONNECTION_REQ_FILE_SAX_2_VALID = TEST_DATA_DIR / "sax-2-request-valid.json"
18 changes: 18 additions & 0 deletions tests/data/test_request_amlight.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:1",
"name": "Novi100:1"
},
"ingress_port":
{
"id": "urn:sdx:port:amlight.net:B1:2",
"name": "Novi100:2"
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
151 changes: 123 additions & 28 deletions tests/test_te_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,26 @@
)
from sdx.pce.topology.temanager import TEManager

from . import TestData


class TEManagerTests(unittest.TestCase):
"""
Tests for topology related functions.
"""

TEST_DATA_DIR = pathlib.Path(__file__).parent / "data"
TOPOLOGY_FILE_SDX = TEST_DATA_DIR / "sdx.json"
TOPOLOGY_FILE_ZAOXI = TEST_DATA_DIR / "zaoxi.json"
TOPOLOGY_FILE_SAX = TEST_DATA_DIR / "sax.json"

CONNECTION_REQ_FILE = TEST_DATA_DIR / "test_request.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"
CONNECTION_REQ_FILE_SAX_2_VALID = TEST_DATA_DIR / "sax-2-request-valid.json"

def setUp(self):
with open(self.TOPOLOGY_FILE_SDX, "r", encoding="utf-8") as fp:
with open(TestData.TOPOLOGY_FILE_AMLIGHT, "r", encoding="utf-8") as fp:
topology_data = json.load(fp)

with open(self.CONNECTION_REQ_FILE, "r", encoding="utf-8") as fp:
with open(TestData.CONNECTION_REQ_FILE, "r", encoding="utf-8") as fp:
connection_data = json.load(fp)

self.temanager = TEManager(topology_data, connection_data)

def tearDown(self):
self.temanager = None

def test_generate_solver_input(self):
print("Test Convert Connection To Topology")
connection = self._make_connection()
Expand All @@ -55,7 +49,7 @@ def test_connection_breakdown_simple(self):
# representing connection requests, still works.
request = [
{
"1": [[1, 2], [3, 4]],
"1": [[0, 1], [1, 2]],
},
1.0,
]
Expand All @@ -68,7 +62,7 @@ def test_connection_breakdown_tm(self):
# Breaking down a traffic matrix.
request = [
{
"1": [[1, 2], [3, 4]],
"1": [[0, 1], [1, 2]],
},
1.0,
]
Expand All @@ -88,15 +82,15 @@ def test_connection_breakdown_tm(self):
# Make sure that breakdown contains domains as keys, and dicts
# as values. The domain name is a little goofy, because the
# topology we have is goofy.
link = breakdown.get("urn:ogf:network:sdx")
link = breakdown.get("urn:ogf:network:sdx:topology:amlight.net")
self.assertIsInstance(link, dict)

def test_connection_breakdown_two_similar_requests(self):
# Solving and breaking down two similar connection requests.
request = [
{
"1": [[1, 2], [3, 4]],
"2": [[1, 2], [3, 4]],
"1": [[0, 1], [1, 2]],
"2": [[0, 1], [1, 2]],
},
1.0,
]
Expand All @@ -112,22 +106,26 @@ def test_connection_breakdown_two_similar_requests(self):
self.assertIsInstance(breakdown, dict)
self.assertEqual(len(breakdown), 1)

link = breakdown.get("urn:ogf:network:sdx:topology:amlight.net")
self.assertIsInstance(link, dict)

def test_connection_breakdown_three_domains(self):
# SDX already exists in the known topology from setUp
# step. Add SAX topology.
with open(self.TOPOLOGY_FILE_SAX, "r", encoding="utf-8") as fp:
with open(TestData.TOPOLOGY_FILE_SAX, "r", encoding="utf-8") as fp:
topology_data = json.load(fp)
self.temanager.add_topology(topology_data)

# Add ZAOXI topology as well.
with open(self.TOPOLOGY_FILE_SAX, "r", encoding="utf-8") as fp:
with open(TestData.TOPOLOGY_FILE_ZAOXI, "r", encoding="utf-8") as fp:
topology_data = json.load(fp)
self.temanager.add_topology(topology_data)

request = [
{
"1": [[1, 2], [3, 4]],
"2": [[1, 2], [3, 5]],
"3": [[7, 8], [8, 9]],
},
1.0,
]
Expand All @@ -141,7 +139,63 @@ def test_connection_breakdown_three_domains(self):

self.assertIsNotNone(breakdown)
self.assertIsInstance(breakdown, dict)
self.assertEqual(len(breakdown), 1)
self.assertEqual(len(breakdown), 3)

amlight = breakdown.get("urn:ogf:network:sdx:topology:amlight.net")
print(f"amlight: {amlight}")
self.assertIsInstance(amlight, dict)
self.assertIsInstance(amlight.get("ingress_port"), dict)
self.assertIsInstance(amlight.get("egress_port"), dict)

sax = breakdown.get("urn:ogf:network:sdx:topology:sax.net")
print(f"sax: {sax}")
self.assertIsInstance(sax, dict)
self.assertIsInstance(sax.get("ingress_port"), dict)
self.assertIsInstance(sax.get("egress_port"), dict)

zaoxi = breakdown.get("urn:ogf:network:sdx:topology:zaoxi.net")
print(f"zaoxi: {zaoxi}")
self.assertIsInstance(zaoxi, dict)
self.assertIsInstance(zaoxi.get("ingress_port"), dict)
self.assertIsInstance(zaoxi.get("egress_port"), dict)

def test_connection_breakdown_three_domains_sax_connection(self):
"""
Test case added to investigate
https://github.com/atlanticwave-sdx/sdx-controller/issues/146
"""
with open(TestData.TOPOLOGY_FILE_SAX, "r", encoding="utf-8") as fp:
topology_data = json.load(fp)
self.temanager.add_topology(topology_data)

# Add ZAOXI topology as well.
with open(TestData.TOPOLOGY_FILE_ZAOXI, "r", encoding="utf-8") as fp:
topology_data = json.load(fp)
self.temanager.add_topology(topology_data)

request = [
{
"1": [[6, 9]],
},
1.0,
]

solution = self._make_tm_and_solve(request)

print(f"topology: {self.temanager.topology_manager.topology}")
print(f"topology_list: {self.temanager.topology_manager.topology_list}")

self.assertIsNotNone(solution.connection_map)
self.assertNotEqual(solution.cost, 0)

breakdown = self.temanager.generate_connection_breakdown(solution)
print(f"Breakdown: {breakdown}")

sax = breakdown.get("urn:ogf:network:sdx:topology:sax.net")
print(f"Breakdown, SAX: {sax}")

zaoxi = breakdown.get("urn:ogf:network:sdx:topology:zaoxi.net")
print(f"Breakdown, ZAOXI: {zaoxi}")

def test_connection_breakdown_some_input(self):
# The set of requests below should fail to find a solution,
Expand Down Expand Up @@ -171,10 +225,12 @@ def test_generate_graph_and_connection_with_sax_2_invalid(self):

TODO: Use a better name for this method.
"""
with open(self.TOPOLOGY_FILE_SAX_2, "r", encoding="utf-8") as fp:
with open(TestData.TOPOLOGY_FILE_SAX_2, "r", encoding="utf-8") as fp:
topology_data = json.load(fp)

with open(self.CONNECTION_REQ_FILE_SAX_2_INVALID, "r", encoding="utf-8") as fp:
with open(
TestData.CONNECTION_REQ_FILE_SAX_2_INVALID, "r", encoding="utf-8"
) as fp:
connection_data = json.load(fp)

temanager = TEManager(topology_data, connection_data)
Expand All @@ -197,10 +253,12 @@ def test_generate_graph_and_connection_with_sax_2_valid(self):

TODO: Use a better name for this method.
"""
with open(self.TOPOLOGY_FILE_SAX_2, "r", encoding="utf-8") as fp:
with open(TestData.TOPOLOGY_FILE_SAX_2, "r", encoding="utf-8") as fp:
topology_data = json.load(fp)

with open(self.CONNECTION_REQ_FILE_SAX_2_VALID, "r", encoding="utf-8") as fp:
with open(
TestData.CONNECTION_REQ_FILE_SAX_2_VALID, "r", encoding="utf-8"
) as fp:
connection_data = json.load(fp)

temanager = TEManager(topology_data, connection_data)
Expand Down Expand Up @@ -233,6 +291,42 @@ def test_generate_graph_and_connection_with_sax_2_valid(self):
self.assertIsNone(solution.connection_map, None)
self.assertEqual(solution.cost, 0.0)

def test_connection_amlight_to_zaoxi(self):
"""
Exercise a connection request between Amlight and Zaoxi.

TODO: doesn't work as expected yet; see note at the bottom.
"""
connection_request = json.loads(TestData.CONNECTION_REQ_AMLIGHT_SAX.read_text())
print(f"connection_request: {connection_request}")

temanager = TEManager(topology_data=None, connection_data=connection_request)

for path in (
TestData.TOPOLOGY_FILE_AMLIGHT,
TestData.TOPOLOGY_FILE_SAX,
TestData.TOPOLOGY_FILE_ZAOXI,
):
topology = json.loads(path.read_text())
temanager.add_topology(topology)

graph = temanager.generate_graph_te()
traffic_matrix = temanager.generate_connection_te()

print(f"Generated graph: '{graph}', traffic matrix: '{traffic_matrix}'")

self.assertIsNotNone(graph)
self.assertIsNotNone(traffic_matrix)

solution = TESolver(graph, traffic_matrix).solve()
print(f"TESolver result: {solution}")

# TODO: why can't we find a solution for this now? This test
# request evidently used to work prior to PCE refactoring.
#
# See https://github.com/atlanticwave-sdx/pce/issues/114
self.assertIsNone(solution.connection_map)

def test_generate_graph_and_connection(self):
graph = self.temanager.generate_graph_te()
tm = self.temanager.generate_connection_te()
Expand Down Expand Up @@ -266,11 +360,12 @@ def _make_tm_and_solve(self, request) -> ConnectionSolution:
tm = self._make_traffic_matrix(request)
print(f"tm: {tm}")

print(f"graph: {self.temanager.graph}")
print(f"graph: {pprint.pformat(nx.to_dict_of_dicts(self.temanager.graph))}")
graph = self.temanager.generate_graph_te()
print(f"graph: {graph}")
print(f"graph: {pprint.pformat(nx.to_dict_of_dicts(graph))}")

# Find a connection solution.
solver = TESolver(self.temanager.graph, tm)
solver = TESolver(graph, tm)
print(f"solver: {solver}")

solution = solver.solve()
Expand Down
10 changes: 5 additions & 5 deletions tests/test_te_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from sdx.pce.utils.random_connection_generator import RandomConnectionGenerator
from sdx.pce.utils.random_topology_generator import RandomTopologyGenerator

TEST_DATA_DIR = pathlib.Path(__file__).parent.joinpath("data")
from . import TestData


class TESolverTests(unittest.TestCase):
Expand Down Expand Up @@ -99,8 +99,8 @@ def test_mc_solve_more_connections_than_nodes(self):
self.assertEqual(solution.cost, 0.0)

def test_mc_solve_5(self):
edge_list_file = TEST_DATA_DIR.joinpath("test_five_node_topology.txt")
traffic_matrix_file = TEST_DATA_DIR.joinpath("test_five_node_request.json")
edge_list_file = TestData.TEST_DATA_DIR / "test_five_node_topology.txt"
traffic_matrix_file = TestData.TEST_DATA_DIR / "test_five_node_request.json"

graph = nx.read_edgelist(
edge_list_file,
Expand Down Expand Up @@ -133,12 +133,12 @@ def test_mc_solve_5(self):

@unittest.skipIf(not can_read_dot_file(), reason="Can't read dot file")
def test_mc_solve_geant2012(self):
topology_file = TEST_DATA_DIR.joinpath("Geant2012.dot")
topology_file = TestData.TEST_DATA_DIR / "Geant2012.dot"
graph = read_dot_file(topology_file)

self.assertNotEqual(graph, None, "Could not read dot file")

connection_file = TEST_DATA_DIR.joinpath("test_connection.json")
connection_file = TestData.TEST_DATA_DIR / "test_connection.json"
with open(connection_file) as fp:
tm = TrafficMatrix.from_dict(json.load(fp))

Expand Down
Loading