From fbd3c841558ce1e757d5fa6bc3bfe45dc8b4103c Mon Sep 17 00:00:00 2001 From: Brad Cowie Date: Wed, 8 Jul 2026 14:02:22 +1200 Subject: [PATCH 1/3] Fix hashing of FakeOF objects --- clib/fakeoftable.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/clib/fakeoftable.py b/clib/fakeoftable.py index d07afade97..8f40b9cc23 100644 --- a/clib/fakeoftable.py +++ b/clib/fakeoftable.py @@ -232,7 +232,7 @@ def table_state(self, dp_id): def hash_table(self, dp_id): """Return a hash of a single FakeOFTable""" - return self.tables[dp_id].__hash__() + return hash(self.tables[dp_id]) class FakeOFTable: @@ -252,12 +252,26 @@ def __init__(self, dp_id, num_tables=1, requires_tfm=True): def table_state(self): """Return tuple of table hash & table str""" - table_str = str(self.tables) - return (hash(frozenset(table_str)), table_str) + return (hash(self), str(self)) def __hash__(self): """Return a host of the tables""" - return hash(frozenset(str(self.tables))) + return hash( + tuple( + tuple( + sorted( + table, + key=lambda x: ( + x.priority, + tuple( + (k, str(v)) for k, v in sorted(x.match_values.items()) + ), + ), + ) + ) + for table in self.tables + ) + ) def _apply_groupmod(self, ofmsg): """Maintain group table.""" @@ -1051,10 +1065,10 @@ def __hash__(self): return hash( ( self.priority, - self.match_values, - self.match_masks, + tuple(sorted(self.match_values.items())), + tuple(sorted(self.match_masks.items())), self.out_port, - self.instructions, + str(self.instructions), ) ) From 8a77728472fafc055b0d31ea5ae3e77a9f529b53 Mon Sep 17 00:00:00 2001 From: Brad Cowie Date: Wed, 8 Jul 2026 14:02:57 +1200 Subject: [PATCH 2/3] Print FakeOFTable flowmods in order of priority then match --- clib/fakeoftable.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/clib/fakeoftable.py b/clib/fakeoftable.py index 8f40b9cc23..cad3e73355 100644 --- a/clib/fakeoftable.py +++ b/clib/fakeoftable.py @@ -877,7 +877,20 @@ def __str__(self): string = "" for table_id, table in enumerate(self.tables): string += "\n----- Table %u -----\n" % (table_id) - string += "\n".join(sorted([str(flowmod) for flowmod in table])) + string += "\n".join( + [ + str(flowmod) + for flowmod in sorted( + table, + key=lambda x: ( + x.priority, + tuple( + (k, str(v)) for k, v in sorted(x.match_values.items()) + ), + ), + ) + ] + ) return string def sort_tables(self): From d36e58b966e5a977cea1f617dd6289fcc9b44ea2 Mon Sep 17 00:00:00 2001 From: Brad Cowie Date: Wed, 1 Jul 2026 14:49:27 +1200 Subject: [PATCH 3/3] Fix crash in _check_table_difference() when table hashes aren't equal --- clib/valve_test_lib.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/clib/valve_test_lib.py b/clib/valve_test_lib.py index 2ad3d6be8b..749467039e 100644 --- a/clib/valve_test_lib.py +++ b/clib/valve_test_lib.py @@ -781,11 +781,7 @@ def _check_table_difference(self, before_hash, before_str, dp_id): diff = difflib.unified_diff( before_str.splitlines(), after_str.splitlines() ) - self.assertEqual( - before_hash, - after_hash, - msg="%s != %s\n".join(diff) % (before_hash, after_hash), - ) + self.assertEqual(before_hash, after_hash, msg="\n" + "\n".join(diff)) def _verify_redundant_safe_offset_ofmsgs(self, ofmsgs, dp_id, offset=1): """