diff mbox series

[ovs-dev,v7,4/5] tests: Improve debuggability of tests.

Message ID 20201119051308.27913-5-blp@ovn.org
State New
Headers show
Series Add DDlog implementation of ovn-northd | expand

Commit Message

Ben Pfaff Nov. 19, 2020, 5:13 a.m. UTC
These changes should make it easier to debug various tests.

Signed-off-by: Ben Pfaff <blp@ovn.org>
---
 tests/ovn-controller-vtep.at |   6 +-
 tests/ovn-northd.at          | 129 ++++---------
 tests/ovn.at                 | 348 +++++++++++++++--------------------
 tests/ovs-macros.at          |  20 +-
 4 files changed, 213 insertions(+), 290 deletions(-)
diff mbox series

Patch

diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at
index 8b4c180b1669..0d1693682616 100644
--- a/tests/ovn-controller-vtep.at
+++ b/tests/ovn-controller-vtep.at
@@ -181,7 +181,7 @@  OVN_CONTROLLER_VTEP_START
 AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0 -- bind-ls br-vtep p1 300 lswitch0])
 # adds logical switch port in ovn-nb database, and sets the type and options.
 OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0])
-check ovn-sbctl wait-until Port_Binding br-vtep_lswitch0 chassis!='[[]]'
+wait_row_count Port_Binding 1 logical_port=br-vtep_lswitch0 chassis!='[[]]'
 # should see one binding, associated to chassis of 'br-vtep'.
 chassis_uuid=$(ovn-sbctl --columns=_uuid list Chassis br-vtep | cut -d ':' -f2 | tr -d ' ')
 AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Port_Binding br-vtep_lswitch0 | cut -d ':' -f2 | tr -d ' '], [0], [dnl
@@ -192,7 +192,7 @@  ${chassis_uuid}
 AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p0 200 lswitch1])
 # adds logical switch port in ovn-nb database for lswitch1.
 OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch1], [br-vtep], [lswitch1])
-check ovn-sbctl wait-until Port_Binding br-vtep_lswitch1 chassis!='[[]]'
+wait_row_count Port_Binding 1 logical_port=br-vtep_lswitch1 chassis!='[[]]'
 # This is allowed, but not recommended, to have two vlan_bindings (to different vtep logical switches)
 # from one vtep gateway physical port in one ovn-nb logical swithch.
 AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Port_Binding | cut -d ':' -f2 | tr -d ' ' | sort], [0], [dnl
@@ -203,7 +203,7 @@  ${chassis_uuid}
 
 # adds another logical switch port in ovn-nb database for lswitch0.
 OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0_dup], [br-vtep], [lswitch0])
-check ovn-sbctl wait-until Port_Binding br-vtep_lswitch0_dup chassis!='[[]]'
+wait_row_count Port_Binding 1 logical_port=br-vtep_lswitch0_dup chassis!='[[]]'
 # it is not allowed to have more than one ovn-nb logical port for the same
 # vtep logical switch on a vtep gateway chassis, so should still see only
 # two port_binding entries bound.
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
index 78b1ff728af3..972ff5c626a3 100644
--- a/tests/ovn-northd.at
+++ b/tests/ovn-northd.at
@@ -1078,7 +1078,7 @@  health_check @hc | uuidfilt], [0], [<0>
 
 wait_row_count Service_Monitor 0
 
-# create logical switches and ports
+AS_BOX([create logical switches and ports])
 ovn-nbctl ls-add sw0
 ovn-nbctl --wait=sb lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1 \
 "00:00:00:00:00:03 10.0.0.3"
@@ -1105,7 +1105,7 @@  OVS_WAIT_FOR_OUTPUT(
   (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
 ])
 
-# Delete the Load_Balancer_Health_Check
+AS_BOX([Delete the Load_Balancer_Health_Check])
 ovn-nbctl --wait=sb clear load_balancer . health_check
 wait_row_count Service_Monitor 0
 
@@ -1115,7 +1115,7 @@  OVS_WAIT_FOR_OUTPUT(
 [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
 ])
 
-# Create the Load_Balancer_Health_Check again.
+AS_BOX([Create the Load_Balancer_Health_Check again.])
 ovn-nbctl --wait=sb -- --id=@hc create \
 Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
 health_check @hc
@@ -1127,7 +1127,7 @@  AT_CHECK([cat lflows.txt | sed 's/table=..//'], [0], [dnl
   (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
 ])
 
-# Get the uuid of both the service_monitor
+AS_BOX([Get the uuid of both the service_monitor])
 sm_sw0_p1=$(fetch_column Service_Monitor _uuid logical_port=sw0-p1)
 sm_sw1_p1=$(fetch_column Service_Monitor _uuid logical_port=sw1-p1)
 
@@ -1137,7 +1137,7 @@  OVS_WAIT_FOR_OUTPUT(
 [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
 ])
 
-# Set the service monitor for sw1-p1 to offline
+AS_BOX([Set the service monitor for sw1-p1 to offline])
 check ovn-sbctl set service_monitor sw1-p1 status=offline
 wait_row_count Service_Monitor 1 logical_port=sw1-p1 status=offline
 check ovn-nbctl --wait=sb sync
@@ -1148,7 +1148,7 @@  OVS_WAIT_FOR_OUTPUT(
 [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80);)
 ])
 
-# Set the service monitor for sw0-p1 to offline
+AS_BOX([Set the service monitor for sw0-p1 to offline])
 ovn-sbctl set service_monitor $sm_sw0_p1 status=offline
 
 wait_row_count Service_Monitor 1 logical_port=sw0-p1 status=offline
@@ -1164,7 +1164,7 @@  OVS_WAIT_FOR_OUTPUT(
   (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(drop;)
 ])
 
-# Set the service monitor for sw0-p1 and sw1-p1 to online
+AS_BOX([Set the service monitor for sw0-p1 and sw1-p1 to online])
 ovn-sbctl set service_monitor $sm_sw0_p1 status=online
 ovn-sbctl set service_monitor $sm_sw1_p1 status=online
 
@@ -1177,7 +1177,7 @@  OVS_WAIT_FOR_OUTPUT(
 [  (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
 ])
 
-# Set the service monitor for sw1-p1 to error
+AS_BOX([Set the service monitor for sw1-p1 to error])
 ovn-sbctl set service_monitor $sm_sw1_p1 status=error
 wait_row_count Service_Monitor 1 logical_port=sw1-p1 status=error
 check ovn-nbctl --wait=sb sync
@@ -1188,10 +1188,10 @@  AT_CHECK([cat lflows.txt | sed 's/table=..//'], [0], [dnl
   (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80);)
 ])
 
-# Add one more vip to lb1
+AS_BOX([Add one more vip to lb1])
 check ovn-nbctl set load_balancer . vip:10.0.0.40\\:1000=10.0.0.3:1000,20.0.0.3:80
 
-# create health_check for new vip - 10.0.0.40
+AS_BOX([create health_check for new vip - 10.0.0.40])
 AT_CHECK(
   [ovn-nbctl --wait=sb \
           -- --id=@hc create Load_Balancer_Health_Check vip=10.0.0.40\\:1000 \
@@ -1215,7 +1215,7 @@  OVS_WAIT_FOR_OUTPUT(
   (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000);)
 ])
 
-# Set the service monitor for sw1-p1 to online
+AS_BOX([Set the service monitor for sw1-p1 to online])
 check ovn-sbctl set service_monitor sw1-p1 status=online
 
 wait_row_count Service_Monitor 1 logical_port=sw1-p1 status=online
@@ -1229,7 +1229,7 @@  OVS_WAIT_FOR_OUTPUT(
   (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
 ])
 
-# Associate lb1 to sw1
+AS_BOX([Associate lb1 to sw1])
 check ovn-nbctl --wait=sb ls-lb-add sw1 lb1
 AT_CAPTURE_FILE([sbflows11])
 OVS_WAIT_FOR_OUTPUT(
@@ -1239,7 +1239,7 @@  OVS_WAIT_FOR_OUTPUT(
   (ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
 ])
 
-# Now create lb2 same as lb1 but udp protocol.
+AS_BOX([Now create lb2 same as lb1 but udp protocol.])
 check ovn-nbctl lb-add lb2 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80 udp
 check ovn-nbctl --wait=sb set load_balancer lb2 ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
 check ovn-nbctl --wait=sb set load_balancer lb2 ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
@@ -1254,12 +1254,13 @@  check ovn-nbctl --wait=sb sync
 
 wait_row_count Service_Monitor 5
 
-# Change the svc_monitor_mac. This should get reflected in service_monitor table rows.
+AS_BOX([Change the svc_monitor_mac.])
+# This should get reflected in service_monitor table rows.
 check ovn-nbctl set NB_Global . options:svc_monitor_mac="fe:a0:65:a2:01:03"
 
 wait_row_count Service_Monitor 5 src_mac='"fe:a0:65:a2:01:03"'
 
-# Change the source ip for 10.0.0.3 backend ip in lb2
+AS_BOX([Change the source ip for 10.0.0.3 backend ip in lb2])
 check ovn-nbctl --wait=sb set load_balancer lb2 ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.100
 
 wait_row_count Service_Monitor 1 logical_port=sw0-p1 src_ip=10.0.0.100
@@ -1772,28 +1773,12 @@  AT_CAPTURE_FILE([sw0flows])
 ovn-sbctl dump-flows sw1 > sw1flows
 AT_CAPTURE_FILE([sw1flows])
 
-AT_CHECK([grep "ls_in_acl" sw0flows | grep pg0 | sort], [0], [dnl
-  table=7 (ls_in_acl          ), priority=2002 , dnl
-match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=6); };)
-])
-
-AT_CHECK([grep "ls_in_acl" sw1flows | grep pg0 | sort], [0], [dnl
-  table=7 (ls_in_acl          ), priority=2002 , dnl
-match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=6); };)
-])
-
-AT_CHECK([grep "ls_out_acl" sw0flows | grep pg0 | sort], [0], [dnl
-  table=5 (ls_out_acl         ), priority=2003 , dnl
-match=(outport == @pg0 && ip6 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-])
-
-AT_CHECK([grep "ls_out_acl" sw1flows | grep pg0 | sort], [0], [dnl
-  table=5 (ls_out_acl         ), priority=2003 , dnl
-match=(outport == @pg0 && ip6 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+AT_CHECK(
+  [grep -E 'ls_(in|out)_acl' sw0flows sw1flows | grep pg0 | sort], [0], [dnl
+sw0flows:  table=5 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw0flows:  table=7 (ls_in_acl          ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=6); };)
+sw1flows:  table=5 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw1flows:  table=7 (ls_in_acl          ), priority=2002 , match=(inport == @pg0 && ip4 && tcp && tcp.dst == 80), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=egress,table=6); };)
 ])
 
 AS_BOX([2])
@@ -1805,22 +1790,11 @@  AT_CAPTURE_FILE([sw0flows2])
 ovn-sbctl dump-flows sw1 > sw1flows2
 AT_CAPTURE_FILE([sw1flows2])
 
-AT_CHECK([grep "ls_out_acl" sw0flows2 | grep pg0 | sort], [0], [dnl
-  table=5 (ls_out_acl         ), priority=2002 , dnl
-match=(outport == @pg0 && ip4 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-  table=5 (ls_out_acl         ), priority=2003 , dnl
-match=(outport == @pg0 && ip6 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-])
-
-AT_CHECK([grep "ls_out_acl" sw1flows2 | grep pg0 | sort], [0], [dnl
-  table=5 (ls_out_acl         ), priority=2002 , dnl
-match=(outport == @pg0 && ip4 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-  table=5 (ls_out_acl         ), priority=2003 , dnl
-match=(outport == @pg0 && ip6 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+AT_CHECK([grep "ls_out_acl" sw0flows2 sw1flows2 | grep pg0 | sort], [0], [dnl
+sw0flows2:  table=5 (ls_out_acl         ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw0flows2:  table=5 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw1flows2:  table=5 (ls_out_acl         ), priority=2002 , match=(outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw1flows2:  table=5 (ls_out_acl         ), priority=2003 , match=(outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
 ])
 
 AS_BOX([3])
@@ -1832,42 +1806,19 @@  AT_CAPTURE_FILE([sw0flows3])
 ovn-sbctl dump-flows sw1 > sw1flows3
 AT_CAPTURE_FILE([sw1flows3])
 
-AT_CHECK([grep "ls_out_acl" sw0flows3 | grep pg0 | sort], [0], [dnl
-  table=5 (ls_out_acl         ), priority=2001 , dnl
-match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
-  table=5 (ls_out_acl         ), priority=2001 , dnl
-match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
-  table=5 (ls_out_acl         ), priority=2002 , dnl
-match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), dnl
-action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-  table=5 (ls_out_acl         ), priority=2002 , dnl
-match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-  table=5 (ls_out_acl         ), priority=2003 , dnl
-match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), dnl
-action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-  table=5 (ls_out_acl         ), priority=2003 , dnl
-match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-])
-
-AT_CHECK([grep "ls_out_acl" sw1flows3 | grep pg0 | sort], [0], [dnl
-  table=5 (ls_out_acl         ), priority=2001 , dnl
-match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
-  table=5 (ls_out_acl         ), priority=2001 , dnl
-match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
-  table=5 (ls_out_acl         ), priority=2002 , dnl
-match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), dnl
-action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-  table=5 (ls_out_acl         ), priority=2002 , dnl
-match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-  table=5 (ls_out_acl         ), priority=2003 , dnl
-match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), dnl
-action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
-  table=5 (ls_out_acl         ), priority=2003 , dnl
-match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), dnl
-action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+AT_CHECK([grep "ls_out_acl" sw0flows3 sw1flows3 | grep pg0 | sort], [0], [dnl
+sw0flows3:  table=5 (ls_out_acl         ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
+sw0flows3:  table=5 (ls_out_acl         ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
+sw0flows3:  table=5 (ls_out_acl         ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw0flows3:  table=5 (ls_out_acl         ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw0flows3:  table=5 (ls_out_acl         ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw0flows3:  table=5 (ls_out_acl         ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw1flows3:  table=5 (ls_out_acl         ), priority=2001 , match=(reg0[[7]] == 1 && (outport == @pg0 && ip)), action=(reg0[[1]] = 1; next;)
+sw1flows3:  table=5 (ls_out_acl         ), priority=2001 , match=(reg0[[8]] == 1 && (outport == @pg0 && ip)), action=(next;)
+sw1flows3:  table=5 (ls_out_acl         ), priority=2002 , match=((reg0[[10]] == 1) && outport == @pg0 && ip4 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw1flows3:  table=5 (ls_out_acl         ), priority=2002 , match=((reg0[[9]] == 1) && outport == @pg0 && ip4 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw1flows3:  table=5 (ls_out_acl         ), priority=2003 , match=((reg0[[10]] == 1) && outport == @pg0 && ip6 && udp), action=(ct_commit { ct_label.blocked = 1; };  reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
+sw1flows3:  table=5 (ls_out_acl         ), priority=2003 , match=((reg0[[9]] == 1) && outport == @pg0 && ip6 && udp), action=(reg0 = 0; reject { /* eth.dst <-> eth.src; ip.dst <-> ip.src; is implicit. */ outport <-> inport; next(pipeline=ingress,table=20); };)
 ])
 AT_CLEANUP
 ])
diff --git a/tests/ovn.at b/tests/ovn.at
index 6a81ec6f07a5..b359e69f767d 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -4911,6 +4911,8 @@  ovs-vsctl -- add-port br-int vif2 -- \
 wait_for_ports_up
 check ovn-nbctl --wait=hv sync
 
+ovs-sbctl dump-flows > sbflows
+AT_CAPTURE_FILE([sbflows])
 
 # Send ip packets between the two ports.
 
@@ -4922,36 +4924,11 @@  dst_ip=`ip_to_hex 172 16 1 2`
 packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
 as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
 
-
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
 #Disable router R1
 ovn-nbctl --wait=hv set Logical_Router R1 enabled=false
 
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list logical_flow
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-
+ovs-sbctl dump-flows > sbflows2
+AT_CAPTURE_FILE([sbflows2])
 
 as hv1 ovs-appctl netdev-dummy/receive vif1 $packet
 
@@ -5059,6 +5036,9 @@  OVN_POPULATE_ARP
 wait_for_ports_up
 check ovn-nbctl --wait=hv sync
 
+ovn-sbctl dump-flows > sbflows
+AT_CAPTURE_FILE([sbflows])
+
 # Send ip packets between foo1 and alice1
 src_mac="f00000010203"
 dst_mac="000000010203"
@@ -5075,25 +5055,6 @@  dst_ip=`ip_to_hex 172 16 2 2`
 packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
 
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-
-echo "------ hv1 dump ----------"
-as hv1 ovs-ofctl dump-flows br-int
-echo "------ hv2 dump ----------"
-as hv2 ovs-ofctl dump-flows br-int
-
 # Packet to Expect at bob1
 src_mac="000000010205"
 dst_mac="f00000010205"
@@ -6506,7 +6467,8 @@  ovs-vsctl -- add-port br-int vif2 -- \
 # Allow some time for ovn-northd and ovn-controller to catch up.
 wait_for_ports_up
 check ovn-nbctl --wait=hv sync
-
+ovn-nbctl dump-flows > sbflows
+AT_CAPTURE_FILE([sbflows])
 
 for i in 1 2; do
     : > vif$i.expected
@@ -7110,6 +7072,9 @@  wait_for_ports_up
 check ovn-nbctl --wait=hv sync
 sleep 1
 
+ovn-nbctl dump-flows > sbflows
+AT_CAPTURE_FILE([sbflows])
+
 for i in 1 2; do
     : > $i.expected
 done
@@ -7123,11 +7088,6 @@  na_packet=fa163e940598fa163ea1f9ae86dd6000000000203afffd81ce49a9480000f8163efffe
 as hv1 ovs-appctl netdev-dummy/receive vif1 $ns_packet
 echo $na_packet >> 1.expected
 
-echo "------ hv1 dump ------"
-as hv1 ovs-vsctl show
-as hv1 ovs-ofctl -O OpenFlow13 show br-int
-as hv1 ovs-ofctl -O OpenFlow13 dump-flows br-int
-
 for i in 1 2; do
     OVN_CHECK_PACKETS([hv1/vif$i-tx.pcap], [$i.expected])
 done
@@ -7753,29 +7713,8 @@  ovs-vsctl -- add-port br-int hv1-ls2lp2 -- \
 wait_for_ports_up
 check ovn-nbctl --wait=hv sync
 
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
-
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-ovn-sbctl list chassis
-ovn-sbctl list encap
-echo "---------------------"
-
-echo "------Flows dump-----"
-as hv1
-ovs-ofctl dump-flows
-echo "---------------------"
+ovn-sbctl dump-flows > sbflows
+AT_CAPTURE_FILE([sbflows])
 
 src_mac="f00000000003"
 dst_mac="f00000000001"
@@ -10794,38 +10733,42 @@  check ovn-nbctl --wait=hv sync
 
 # Check that there is a logical flow in logical switch foo's pipeline
 # to set the outport to rp-foo with the condition is_chassis_redirect.
-ovn-sbctl dump-flows foo
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl dump-flows foo | grep ls_in_l2_lkup | \
+ovn-sbctl dump-flows foo > sbflows
+AT_CAPTURE_FILE([sbflows])
+OVS_WAIT_UNTIL([test 1 = `grep ls_in_l2_lkup sbflows | \
 grep rp-foo | grep is_chassis_resident | grep priority=50 -c`])
 
-echo "---------NB dump-----"
-ovn-nbctl show
-echo "---------------------"
-ovn-nbctl list logical_router
-echo "---------------------"
-ovn-nbctl list nat
-echo "---------------------"
-ovn-nbctl list logical_router_port
-echo "---------------------"
+(echo "---------NB dump-----"
+ ovn-nbctl show
+ echo "---------------------"
+ ovn-nbctl list logical_router
+ echo "---------------------"
+ ovn-nbctl list nat
+ echo "---------------------"
+ ovn-nbctl list logical_router_port
+ echo "---------------------") > nbdump
+AT_CAPTURE_FILE([nbdump])
 
-echo "---------SB dump-----"
-ovn-sbctl list datapath_binding
-echo "---------------------"
-ovn-sbctl list port_binding
-echo "---------------------"
-ovn-sbctl dump-flows
-echo "---------------------"
-ovn-sbctl list chassis
-echo "---------------------"
+(echo "---------SB dump-----"
+ ovn-sbctl list datapath_binding
+ echo "---------------------"
+ ovn-sbctl list port_binding
+ echo "---------------------"
+ ovn-sbctl list chassis
+ echo "---------------------") > sbdump
+AT_CAPTURE_FILE([sbdump])
 
 for chassis in hv1 hv2 hv3; do
-    as $chassis
-    echo "------ $chassis dump ----------"
-    ovs-vsctl show br-int
-    ovs-ofctl show br-int
-    ovs-ofctl dump-flows br-int
-    echo "--------------------------"
+    (as $chassis
+     echo "------ $chassis dump ----------"
+     ovs-vsctl show
+     ovs-ofctl show br-int
+     ovs-ofctl dump-flows br-int
+     echo "--------------------------") > ${chassis}dump
 done
+AT_CAPTURE_FILE([hv1dump])
+AT_CAPTURE_FILE([hv2dump])
+AT_CAPTURE_FILE([hv3dump])
 
 foo1_ip=$(ip_to_hex 192 168 1 2)
 gw_ip=$(ip_to_hex 172 16 1 6)
@@ -11279,6 +11222,9 @@  OVN_POPULATE_ARP
 wait_for_ports_up
 check ovn-nbctl --wait=hv sync
 
+ovn-sbctl dump-flows > sbflows
+AT_CAPTURE_FILE([sbflows])
+
 # Send ip packets between foo1 and alice1
 src_mac="f00000010203"
 dst_mac="000000010203"
@@ -16532,12 +16478,9 @@  spa=$(ip_to_hex 10 0 0 10)
 tpa=$(ip_to_hex 10 0 0 10)
 send_garp 1 1 $eth_src $eth_dst $spa $tpa
 
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
-logical_port=sw0-vir) = x$hv1_ch_uuid], [0], [])
-
-AT_CHECK([test x$(ovn-sbctl --bare --columns virtual_parent find port_binding \
-logical_port=sw0-vir) = xsw0-p1])
-
+wait_row_count Port_Binding 1 logical_port=sw0-vir chassis=$hv1_ch_uuid
+check_row_count Port_Binding 1 logical_port=sw0-vir virtual_parent=sw0-p1
+check ovn-nbctl --wait=hv sync
 
 # There should be an arp resolve flow to resolve the virtual_ip with the
 # sw0-p1's MAC.
@@ -21798,26 +21741,25 @@  ovn-nbctl set Logical_Router $gw_uuid options:chassis=hv1
 ovn-nbctl --wait=hv sync
 
 # And ensure that ECMP symmetric reply flows are present only on hv1
-AT_CHECK([
-    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=15 | \
-    grep "priority=100" | \
-    grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c)
-])
-AT_CHECK([
-    test 1 -eq $(as hv1 ovs-ofctl dump-flows br-int table=21 | \
-    grep "priority=200" | \
-    grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
-])
+as hv1 ovs-ofctl dump-flows br-int > hv1flows
+AT_CAPTURE_FILE([hv1flows])
+as hv2 ovs-ofctl dump-flows br-int > hv2flows
+AT_CAPTURE_FILE([hv2flows])
 
 AT_CHECK([
-    test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=15 | \
-    grep "priority=100" | \
-    grep "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))" -c)
-])
-AT_CHECK([
-    test 0 -eq $(as hv2 ovs-ofctl dump-flows br-int table=21 | \
-    grep "priority=200" | \
-    grep "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]" -c)
+    for hv in 1 2; do
+        grep table=15 hv${hv}flows | \
+        grep "priority=100" | \
+        grep -c "ct(commit,zone=NXM_NX_REG11\\[[0..15\\]],exec(move:NXM_OF_ETH_SRC\\[[\\]]->NXM_NX_CT_LABEL\\[[32..79\\]],load:0x[[0-9]]->NXM_NX_CT_LABEL\\[[80..95\\]]))"
+
+        grep table=21 hv${hv}flows | \
+        grep "priority=200" | \
+        grep -c "actions=move:NXM_NX_CT_LABEL\\[[32..79\\]]->NXM_OF_ETH_DST\\[[\\]]"
+    done; :], [0], [dnl
+1
+1
+0
+0
 ])
 
 OVN_CLEANUP([hv1], [hv2])
@@ -21945,22 +21887,22 @@  as hv1
 ovs-vsctl add-br br-phys
 ovn_attach n1 br-phys 192.168.0.1
 
-ovn-nbctl ls-add sw0
-ovn-nbctl lsp-add sw0 sw0-p1
-ovn-nbctl lsp-set-addresses sw0-p1 "10:14:00:00:00:03 10.0.0.3"
-ovn-nbctl lsp-set-port-security sw0-p1 "10:14:00:00:00:03 10.0.0.3"
+check ovn-nbctl ls-add sw0
+check ovn-nbctl lsp-add sw0 sw0-p1
+check ovn-nbctl lsp-set-addresses sw0-p1 "10:14:00:00:00:03 10.0.0.3"
+check ovn-nbctl lsp-set-port-security sw0-p1 "10:14:00:00:00:03 10.0.0.3"
 
-ovn-nbctl lsp-add sw0 sw0-p2
-ovn-nbctl lsp-set-addresses sw0-p2 "10:14:00:00:00:04 10.0.0.4"
-ovn-nbctl lsp-set-port-security sw0-p2 "10:14:00:00:00:04 10.0.0.4"
+check ovn-nbctl lsp-add sw0 sw0-p2
+check ovn-nbctl lsp-set-addresses sw0-p2 "10:14:00:00:00:04 10.0.0.4"
+check ovn-nbctl lsp-set-port-security sw0-p2 "10:14:00:00:00:04 10.0.0.4"
 
-ovn-nbctl lsp-add sw0 sw0-p3
-ovn-nbctl lsp-set-addresses sw0-p3 "10:14:00:00:00:05 10.0.0.5"
-ovn-nbctl lsp-set-port-security sw0-p3 "10:14:00:00:00:05 10.0.0.5"
+check ovn-nbctl lsp-add sw0 sw0-p3
+check ovn-nbctl lsp-set-addresses sw0-p3 "10:14:00:00:00:05 10.0.0.5"
+check ovn-nbctl lsp-set-port-security sw0-p3 "10:14:00:00:00:05 10.0.0.5"
 
-ovn-nbctl lsp-add sw0 sw0-p4
-ovn-nbctl lsp-set-addresses sw0-p4 "10:14:00:00:00:06 10.0.0.6"
-ovn-nbctl lsp-set-port-security sw0-p4 "10:14:00:00:00:06 10.0.0.6"
+check ovn-nbctl lsp-add sw0 sw0-p4
+check ovn-nbctl lsp-set-addresses sw0-p4 "10:14:00:00:00:06 10.0.0.6"
+check ovn-nbctl lsp-set-port-security sw0-p4 "10:14:00:00:00:06 10.0.0.6"
 
 as hv1
 ovs-vsctl -- add-port br-int hv1-vif1 -- \
@@ -21986,83 +21928,99 @@  ovs-vsctl -- add-port br-int hv1-vif4 -- \
 
 wait_for_ports_up
 
-ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
-ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
-ovn-nbctl --wait=hv sync
+check ovn-nbctl pg-add pg0 sw0-p1 sw0-p2
+check ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
+check ovn-nbctl --wait=hv sync
 
-OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=2")])
+# wait_conj_id_count COUNT ["ID COUNT [MATCH]"]...
+#
+# Waits until COUNT flows matching against conj_id appear in the
+# table 45 on hv1's br-int bridge.  Makes the flows available in
+# "hv1flows", which will be logged on error.
+#
+# In addition, for each quoted "ID COUNT" or "ID COUNT MATCH",
+# verifies that there are COUNT flows in table 45 that match
+# aginst conj_id=ID and (if MATCH) is nonempty, match MATCH.
+wait_conj_id_count() {
+  AT_CAPTURE_FILE([hv1flows])
+  local retval
+  case $1 in
+      (0) retval=1 ;;
+      (*) retval=0 ;;
+  esac
+
+  echo "waiting for $1 conj_id flows..."
+  OVS_WAIT_FOR_OUTPUT_UNQUOTED(
+    [ovs-ofctl dump-flows br-int > hv1flows
+     grep table=45 hv1flows | grep -c conj_id],
+    [$retval], [$1
+])
+
+  shift
+  for arg; do
+    set -- $arg; id=$1 count=$2 match=$3
+    echo "checking that there are $count ${match:+$match }flows with conj_id=$id..."
+    AT_CHECK_UNQUOTED(
+      [grep table=45 hv1flows | grep "$match" | grep -c conj_id=$id],
+      [0], [$count
+])
+  done
+}
 
-# Add sw0-p3 to the port group pg0. The conj_id should be 2.
-ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3
-OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=2")])
+AS_BOX([Add sw0-p3 to the port group pg0. The conj_id should be 2.])
+check ovn-nbctl --wait=hv pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3
+wait_conj_id_count 1 "2 1"
 
-# Add sw0p4 to the port group pg0. The conj_id should be 2.
-ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3 sw0-p4
-OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=2")])
+AS_BOX([Add sw0p4 to the port group pg0. The conj_id should be 2.])
+check ovn-nbctl --wait=hv pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3 sw0-p4
+wait_conj_id_count 1 "2 1"
 
-# Add another ACL with conjunction.
-ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && udp.dst >= 80 && udp.dst <= 82" allow
-OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep tcp | grep -c "conj_id=2")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep udp | grep -c "conj_id=3")])
+AS_BOX([Add another ACL with conjunction.])
+check ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && udp.dst >= 80 && udp.dst <= 82" allow
+wait_conj_id_count 2 "2 1 tcp" "3 1 udp"
 
-# Delete tcp ACL.
-ovn-nbctl acl-del pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82"
-OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep udp | grep -c "conj_id=3")])
+AS_BOX([Delete tcp ACL.])
+check ovn-nbctl --wait=hv acl-del pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82"
+wait_conj_id_count 1 "3 1 udp"
 
-# Add back the tcp ACL.
-ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
-OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
+AS_BOX([Add back the tcp ACL.])
+check ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
+wait_conj_id_count 2 "3 1 udp" "4 1 tcp"
 AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep udp | grep -c "conj_id=3")])
 AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep tcp | grep -c "conj_id=4")])
 
-ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && inport == @pg0 && ip4 && tcp.dst >= 84 && tcp.dst <= 86" allow
-OVS_WAIT_UNTIL([test 3 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep udp | grep -c "conj_id=3")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep tcp | grep -c "conj_id=4")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep tcp | grep -c "conj_id=5")])
+AS_BOX([Add another tcp ACL.])
+check ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && inport == @pg0 && ip4 && tcp.dst >= 84 && tcp.dst <= 86" allow
+wait_conj_id_count 3 "3 1 udp" "4 1 tcp" "5 1 tcp"
 
-ovn-nbctl clear port_group pg0 acls
-OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
+AS_BOX([Clear ACLs.])
+check ovn-nbctl --wait=hv clear port_group pg0 acls
+wait_conj_id_count 0
 
-ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
-ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && udp.dst >= 80 && udp.dst <= 82" allow
-OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep tcp | grep -c "conj_id=6")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep udp | grep -c "conj_id=7")])
+AS_BOX([Add TCP ACL.])
+check ovn-nbctl --wait=hv acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && tcp.dst >= 80 && tcp.dst <= 82" allow
+check ovn-nbctl acl-add pg0 to-lport 1002 "outport == @pg0 && ip4 && udp.dst >= 80 && udp.dst <= 82" allow
+wait_conj_id_count 2 "6 1 tcp" "7 1 udp"
 
-# Flush the lflow cache.
+AS_BOX([Flush lflow cache.])
 as hv1 ovn-appctl -t ovn-controller flush-lflow-cache
-OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=2")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=3")])
-
-# Disable lflow caching.
+wait_conj_id_count 2 "2 1" "3 1"
 
+AS_BOX([Disable lflow caching.])
 as hv1 ovs-vsctl set open . external_ids:ovn-enable-lflow-cache=false
 
-# Wait until ovn-enble-lflow-cache is processed by ovn-controller.
-OVS_WAIT_UNTIL([
-    test $(ovn-sbctl get chassis hv1 other_config:ovn-enable-lflow-cache) = '"false"'
-])
-
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=2")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=3")])
+AS_BOX([Wait until ovn-enble-lflow-cache is processed by ovn-controller.])
+wait_row_count Chassis 1 name=hv1 other_config:ovn-enable-lflow-cache=false
+wait_conj_id_count 2 "2 1" "3 1"
 
-# Remove port sw0-p4 from port group.
-ovn-nbctl pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3
-OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=4")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=5")])
+AS_BOX([Remove port sw0-p4 from port group.])
+check ovn-nbctl --wait=hv pg-set-ports pg0 sw0-p1 sw0-p2 sw0-p3
+wait_conj_id_count 2 "4 1" "5 1"
 
+AS_BOX([Recompute.])
 as hv1 ovn-appctl -t ovn-controller recompute
 
-OVS_WAIT_UNTIL([test 2 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id")])
-OVS_WAIT_UNTIL([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=2")])
-AT_CHECK([test 1 = $(as hv1 ovs-ofctl dump-flows br-int table=45 | grep -c "conj_id=3")])
+wait_conj_id_count 2 "2 1" "3 1"
 
 OVN_CLEANUP([hv1])
 
diff --git a/tests/ovs-macros.at b/tests/ovs-macros.at
index 7e9e8c034041..8cdc0d640cc2 100644
--- a/tests/ovs-macros.at
+++ b/tests/ovs-macros.at
@@ -250,14 +250,16 @@  m4_define([OVS_WAIT_UNTIL],
   [OVS_WAIT([$1], [$2], [AT_LINE], [until $1])])
 
 dnl OVS_WAIT_FOR_OUTPUT(COMMAND, EXIT-STATUS, STDOUT, STDERR)
+dnl OVS_WAIT_FOR_OUTPUT_UNQUOTED(COMMAND, EXIT-STATUS, STDOUT, STDERR)
 dnl
 dnl Executes shell COMMAND in a loop until it exits with status EXIT-STATUS,
 dnl prints STDOUT on stdout, and prints STDERR on stderr.  If this doesn't
 dnl happen within a reasonable time limit, then the test fails.
-m4_define([OVS_WAIT_FOR_OUTPUT], [dnl
+dnl
+dnl The UNQUOTED version expands shell $variables, $(command)s, and so on.
+dnl The plain version does not
+m4_define([OVS_WAIT_FOR_OUTPUT__], [dnl
 wait_expected_status=m4_if([$2], [], [0], [$2])
-AT_DATA([wait-expected-stdout], [$3])
-AT_DATA([wait-expected-stderr], [$4])
 ovs_wait_command() {
     $1
 }
@@ -277,6 +279,18 @@  ovs_wait_failed () {
 }
 ovs_wait "AS_ESCAPE([AT_LINE])" "for output from AS_ESCAPE([$1])"
 ])
+m4_define([OVS_WAIT_FOR_OUTPUT], [dnl
+AT_DATA([wait-expected-stdout], [$3])
+AT_DATA([wait-expected-stderr], [$4])
+OVS_WAIT_FOR_OUTPUT__([$1], [$2])
+])
+m4_define([OVS_WAIT_FOR_OUTPUT_UNQUOTED], [dnl
+cat > wait-expected-stdout <<EOF
+$3[]EOF
+cat > wait-expected-stderr <<EOF
+$4[]EOF
+OVS_WAIT_FOR_OUTPUT__([$1], [$2])
+])
     
 dnl OVS_WAIT_WHILE(COMMAND[, IF-FAILED])
 dnl