mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
test: infamy: Allow tests to refine the topology matching process
To run LAG tests on hardware, we have to make sure that the chosen links are running at the same speed. We do not want to require a specific link speed from the logical topology, as the real requirement is merely that the two (or however many) links are of the _same_ speed, not any _particular_ speed. Rather than having to complicate the topology matcher, let it take care of the common pattern of matching requirements to provided features, then let tests pass custom node/edge matchers for the complicated cases.
This commit is contained in:
+6
-2
@@ -65,7 +65,9 @@ def test_argument(option, **kwargs):
|
||||
|
||||
|
||||
class Env(object):
|
||||
def __init__(self, ltop=None, args=None, argv=sys.argv[1::], environ=os.environ):
|
||||
def __init__(self, ltop=None, args=None, argv=sys.argv[1::], environ=os.environ,
|
||||
nodes_compatible=topology.compatible,
|
||||
edge_mappings=topology.edge_mappings):
|
||||
if "INFAMY_ARGS" in environ:
|
||||
argv = shlex.split(environ["INFAMY_ARGS"]) + argv
|
||||
|
||||
@@ -89,7 +91,9 @@ class Env(object):
|
||||
|
||||
ldot = pydot.graph_from_dot_file(top_path)[0]
|
||||
self.ltop = topology.Topology(ldot)
|
||||
if not self.ltop.map_to(self.ptop):
|
||||
if not self.ltop.map_to(self.ptop,
|
||||
nodes_compatible=nodes_compatible,
|
||||
edge_mappings=edge_mappings):
|
||||
raise tap.TestSkip()
|
||||
|
||||
print(repr(self.ltop))
|
||||
|
||||
+19
-18
@@ -14,25 +14,17 @@ def _qstrip(text):
|
||||
return text
|
||||
|
||||
def compatible(physical, logical):
|
||||
provides_set = set(physical.get("provides", "").split())
|
||||
requires_set = set(logical.get("requires", "").split())
|
||||
return logical["requires"].issubset(physical["provides"])
|
||||
|
||||
return requires_set.issubset(provides_set)
|
||||
|
||||
def map_edges(les, pes):
|
||||
def edge_mappings(les, pes):
|
||||
les = les.values()
|
||||
pes = pes.values()
|
||||
|
||||
for perm in permutations(pes, len(les)):
|
||||
candidate = tuple(zip(les, perm))
|
||||
if all(map(lambda pair: compatible(pair[1], pair[0]), candidate)):
|
||||
return candidate
|
||||
yield candidate
|
||||
|
||||
def match_node(pn, ln):
|
||||
return compatible(pn, ln)
|
||||
|
||||
def match_edge(pes, les):
|
||||
return map_edges(les, pes) is not None
|
||||
|
||||
class Topology:
|
||||
def __init__(self, dotg):
|
||||
@@ -46,6 +38,9 @@ class Topology:
|
||||
|
||||
repr(n.get_attributes())
|
||||
attrs = { _qstrip(k): _qstrip(v) for k, v in n.get_attributes().items() if k != "label" }
|
||||
for attr in ("requires", "provides"):
|
||||
attrs[attr] = set(attrs.get(attr, "").split())
|
||||
|
||||
self.g.add_node(name, **attrs)
|
||||
|
||||
for e in self.dotg.get_edges():
|
||||
@@ -55,6 +50,10 @@ class Topology:
|
||||
attrs = {_qstrip(k): _qstrip(v) for k, v in e.get_attributes().items()}
|
||||
attrs[sn] = sp
|
||||
attrs[dn] = dp
|
||||
|
||||
for attr in ("requires", "provides"):
|
||||
attrs[attr] = set(attrs.get(attr, "").split())
|
||||
|
||||
self.g.add_edge(sn, dn, **attrs)
|
||||
|
||||
def __repr__(self):
|
||||
@@ -73,13 +72,15 @@ class Topology:
|
||||
|
||||
return out
|
||||
|
||||
def map_to(self, phy):
|
||||
def map_to(self, phy,
|
||||
nodes_compatible=compatible, edge_mappings=edge_mappings):
|
||||
mapper = isomorphism.MultiGraphMatcher(phy.g, self.g,
|
||||
edge_match=match_edge,
|
||||
node_match=match_node)
|
||||
edge_match=lambda pes, les: any(edge_mappings(les, pes)),
|
||||
node_match=nodes_compatible)
|
||||
if not mapper.subgraph_is_monomorphic():
|
||||
return False
|
||||
|
||||
# breakpoint()
|
||||
self.phy = phy
|
||||
self.mapping = {}
|
||||
|
||||
@@ -93,7 +94,7 @@ class Topology:
|
||||
les = self.g.get_edge_data(lsrc, ldst)
|
||||
pes = self.phy.g.get_edge_data(psrc, pdst)
|
||||
|
||||
for le, pe in map_edges(les, pes):
|
||||
for le, pe in next(edge_mappings(les, pes)):
|
||||
self.mapping[lsrc][le[lsrc]] = pe[psrc]
|
||||
self.mapping[ldst][le[ldst]] = pe[pdst]
|
||||
|
||||
@@ -139,15 +140,15 @@ class Topology:
|
||||
return None
|
||||
|
||||
def get_mgmt_link(self, src, dst):
|
||||
return self.get_link(src, dst, lambda e: compatible(e, {"requires": "mgmt"}))
|
||||
return self.get_link(src, dst, lambda e: compatible(e, {"requires": {"mgmt"}}))
|
||||
|
||||
def get_ctrl(self):
|
||||
ns = self.get_nodes(lambda _, attrs: compatible(attrs, {"requires": "controller"}))
|
||||
ns = self.get_nodes(lambda _, attrs: compatible(attrs, {"requires": {"controller"}}))
|
||||
assert len(ns) == 1
|
||||
return ns[0]
|
||||
|
||||
def get_infixen(self):
|
||||
return self.get_nodes(lambda _, attrs: compatible(attrs, {"requires": "infix"}))
|
||||
return self.get_nodes(lambda _, attrs: compatible(attrs, {"requires": {"infix"}}))
|
||||
|
||||
|
||||
def get_attr(self, name, default=None):
|
||||
|
||||
Reference in New Issue
Block a user