ebb439
From 0f2bc62c05f039f3311aebf33f7c01f49caabc5f Mon Sep 17 00:00:00 2001
ebb439
From: Ben Pfaff <blp@ovn.org>
ebb439
Date: Wed, 21 Oct 2020 15:01:35 -0700
ebb439
Subject: [PATCH 03/10] tests: Introduce new testing helpers.
ebb439
ebb439
These simplify a lot of otherwise harder to understand checks within
ebb439
the tests.  This commit should show how valuable they are, although I'm
ebb439
sure scope remains to use them in more places.
ebb439
ebb439
Signed-off-by: Ben Pfaff <blp@ovn.org>
ebb439
Acked-by: Numan Siddique <numans@ovn.org>
ebb439
ebb439
(cherry-picked from master commit 4afe409e95c72187a8f7a755fa19b17237d14818)
ebb439
Conflicts:
ebb439
	tests/ovn-northd.at
ebb439
---
ebb439
 tests/ovn-controller.at |   8 +-
ebb439
 tests/ovn-ic.at         |  31 +--
ebb439
 tests/ovn-macros.at     | 121 ++++++++++
ebb439
 tests/ovn-nbctl.at      |  12 +-
ebb439
 tests/ovn-northd.at     | 520 ++++++++++------------------------------
ebb439
 tests/ovn.at            | 495 +++++++++++++-------------------------
ebb439
 tests/ovs-macros.at     |  41 +++-
ebb439
 7 files changed, 466 insertions(+), 762 deletions(-)
ebb439
ebb439
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
ebb439
index d8061345f..014a97760 100644
ebb439
--- a/tests/ovn-controller.at
ebb439
+++ b/tests/ovn-controller.at
ebb439
@@ -201,9 +201,7 @@ OVS_WAIT_UNTIL([
ebb439
 ])
ebb439
 
ebb439
 # Only one Chassis_Private record should exist.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    test $(ovn-sbctl --columns _uuid list chassis_private | wc -l) -eq 1
ebb439
-])
ebb439
+wait_row_count Chassis_Private 1
ebb439
 
ebb439
 # Simulate system-id changing while ovn-controller is disconnected from the
ebb439
 # SB.
ebb439
@@ -227,9 +225,7 @@ OVS_WAIT_UNTIL([
ebb439
 ])
ebb439
 
ebb439
 # Only one Chassis_Private record should exist.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    test $(ovn-sbctl --columns _uuid list chassis_private | wc -l) -eq 1
ebb439
-])
ebb439
+wait_row_count Chassis_Private 1
ebb439
 
ebb439
 # Gracefully terminate daemons
ebb439
 OVN_CLEANUP_SBOX([hv])
ebb439
diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
ebb439
index 6fb00319a..1d40ce958 100644
ebb439
--- a/tests/ovn-ic.at
ebb439
+++ b/tests/ovn-ic.at
ebb439
@@ -5,7 +5,7 @@ ovn_init_ic_db
ebb439
 ovn_start az1
ebb439
 ovn_start az2
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test `ovn-ic-sbctl show | wc -l` -eq 2])
ebb439
+wait_row_count ic-sb:Availability_Zone 2
ebb439
 AT_CHECK([ovn-ic-sbctl show], [0], [dnl
ebb439
 availability-zone az1
ebb439
 availability-zone az2
ebb439
@@ -39,32 +39,21 @@ AT_CHECK([ovn-ic-nbctl ts-add ts1])
ebb439
 AT_CHECK([ovn-ic-nbctl ts-add ts2])
ebb439
 
ebb439
 # Check ISB
ebb439
-OVS_WAIT_UNTIL([ovn-ic-sbctl list datapath | grep ts2])
ebb439
-AT_CHECK([ovn-ic-sbctl -f csv -d bare --no-headings --columns transit_switch list datapath | sort], [0], [dnl
ebb439
-ts1
ebb439
-ts2
ebb439
-])
ebb439
+wait_row_count ic-sb:Datapath_Binding 1 transit_switch=ts1
ebb439
+wait_row_count ic-sb:Datapath_Binding 1 transit_switch=ts2
ebb439
+check_column "ts1 ts2" ic-sb:Datapath_Binding transit_switch
ebb439
+check_column "ts1 ts2" nb:Logical_Switch name
ebb439
 
ebb439
-# Check NB
ebb439
-AT_CHECK([ovn-nbctl -f csv -d bare --no-headings --columns name list logical_switch | sort], [0], [dnl
ebb439
-ts1
ebb439
-ts2
ebb439
-])
ebb439
 
ebb439
 # Check SB DP key
ebb439
-ts1_key=$(ovn-ic-sbctl -f csv -d bare --no-headings --columns tunnel_key find datapath transit_switch=ts1)
ebb439
-sb_ts1_key=$(ovn-sbctl -f csv -d bare --no-headings --columns tunnel_key find datapath_binding external_ids:interconn-ts=ts1)
ebb439
-AT_CHECK([test $ts1_key = $sb_ts1_key])
ebb439
+ts1_key=$(fetch_column ic-sb:Datapath_Binding tunnel_key transit_switch=ts1)
ebb439
+check_column "$ts1_key" Datapath_Binding tunnel_key external_ids:interconn-ts=ts1
ebb439
 
ebb439
 # Test delete
ebb439
 AT_CHECK([ovn-ic-nbctl ts-del ts1])
ebb439
-OVS_WAIT_WHILE([ovn-ic-sbctl list datapath | grep ts1])
ebb439
-AT_CHECK([ovn-ic-sbctl -f csv -d bare --no-headings --columns transit_switch list datapath], [0], [dnl
ebb439
-ts2
ebb439
-])
ebb439
-AT_CHECK([ovn-nbctl -f csv -d bare --no-headings --columns name list logical_switch | sort], [0], [dnl
ebb439
-ts2
ebb439
-])
ebb439
+wait_row_count ic-sb:Datapath_Binding 0 transit_switch=ts1
ebb439
+check_column ts2 ic-sb:Datapath_Binding transit_switch
ebb439
+check_column ts2 nb:Logical_Switch name
ebb439
 
ebb439
 OVN_CLEANUP_IC([az1])
ebb439
 
ebb439
diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at
ebb439
index a6719be83..be596caf3 100644
ebb439
--- a/tests/ovn-macros.at
ebb439
+++ b/tests/ovn-macros.at
ebb439
@@ -286,4 +286,125 @@ ovn_populate_arp__() {
ebb439
 }
ebb439
 m4_divert_pop([PREPARE_TESTS])
ebb439
 
ebb439
+OVS_START_SHELL_HELPERS
ebb439
+# check COMMAND...
ebb439
+#
ebb439
+# Runs COMMAND and checks that it succeeds without any output.
ebb439
+check() {
ebb439
+    echo "$@"
ebb439
+    AT_CHECK(["$@"])
ebb439
+}
ebb439
+
ebb439
+parse_db() {
ebb439
+    case $1 in
ebb439
+        (*:*) echo ${1%%:*} ;;
ebb439
+        (*) echo sb ;;
ebb439
+    esac
ebb439
+}
ebb439
+
ebb439
+parse_table() {
ebb439
+    case $1 in
ebb439
+        (*:*) echo ${1##*:} ;;
ebb439
+        (*) echo $1 ;;
ebb439
+    esac
ebb439
+}
ebb439
+
ebb439
+# count_rows TABLE [CONDITION...]
ebb439
+#
ebb439
+# Prints the number of rows in TABLE (that satisfy CONDITION).
ebb439
+# Uses the southbound db by default; set DB=nb for the northbound database.
ebb439
+count_rows() {
ebb439
+    local db=$(parse_db $1) table=$(parse_table $1); shift
ebb439
+    ovn-${db}ctl --format=table --no-headings find $table "$@" | wc -l
ebb439
+}
ebb439
+
ebb439
+# check_row_count [DATABASE:]TABLE COUNT [CONDITION...]
ebb439
+#
ebb439
+# Checks that TABLE contains COUNT rows (that satisfy CONDITION).
ebb439
+# The default DATABASE is "sb".
ebb439
+check_row_count() {
ebb439
+    local db=$(parse_db $1) table=$(parse_table $1); shift
ebb439
+    local count=$1; shift
ebb439
+    local found=$(count_rows $db:$table "$@")
ebb439
+    echo
ebb439
+    echo "Checking for $count rows in $db $table${1+ with $*}... found $found"
ebb439
+    if test "$count" != "$found"; then
ebb439
+        ovn-${db}ctl list $table
ebb439
+        AT_FAIL_IF([:])
ebb439
+    fi
ebb439
+}
ebb439
+
ebb439
+# wait_row_count [DATABASE:]TABLE COUNT [CONDITION...]
ebb439
+#
ebb439
+# Waits until TABLE contains COUNT rows (that satisfy CONDITION).
ebb439
+# The default DATABASE is "sb".
ebb439
+wait_row_count() {
ebb439
+    local db=$(parse_db $1) table=$(parse_table $1); shift
ebb439
+    local count=$1; shift
ebb439
+    local a=$1 b=$2 c=$3
ebb439
+    echo "Waiting until $count rows in $db $table${1+ with $*}..."
ebb439
+    OVS_WAIT_UNTIL([test $count = $(count_rows $db:$table $a $b $c)],[
ebb439
+      echo "$db table $table has the following rows. $(count_rows $db:$table $a $b $c) rows match instead of expected $count:"
ebb439
+      ovn-${db}ctl list $table])
ebb439
+}
ebb439
+
ebb439
+# fetch_column [DATABASE:]TABLE COLUMN [CONDITION...]
ebb439
+#
ebb439
+# Fetches and prints all the values of COLUMN in the rows of TABLE
ebb439
+# (that satisfy CONDITION), sorting the results lexicographically.
ebb439
+# The default DATABASE is "sb".
ebb439
+fetch_column() {
ebb439
+    local db=$(parse_db $1) table=$(parse_table $1) column=${2-_uuid}; shift; shift
ebb439
+    # Using "echo" removes spaces and newlines.
ebb439
+    echo $(ovn-${db}ctl --bare --columns $column find $table "$@" | sort)
ebb439
+}
ebb439
+
ebb439
+# check_column EXPECTED [DATABASE:]TABLE COLUMN [CONDITION...]
ebb439
+#
ebb439
+# Fetches all of the values of COLUMN in the rows of TABLE (that
ebb439
+# satisfy CONDITION), and compares them against EXPECTED (ignoring
ebb439
+# order).
ebb439
+#
ebb439
+# The default DATABASE is "sb".
ebb439
+check_column() {
ebb439
+    local expected=$1 db=$(parse_db $2) table=$(parse_table $2) column=${3-_uuid}; shift; shift; shift
ebb439
+    local found=$(ovn-${db}ctl --bare --columns $column find $table "$@")
ebb439
+
ebb439
+    # Sort the expected and found values.
ebb439
+    local found=$(for d in $found; do echo $d; done | sort)
ebb439
+    local expected=$(for d in $expected; do echo $d; done | sort)
ebb439
+
ebb439
+    echo
ebb439
+    echo "Checking values in $db $table${1+ with $*} against $expected... found $found"
ebb439
+    if test "$found" != "$expected"; then
ebb439
+        ovn-${db}ctl list $table
ebb439
+        AT_FAIL_IF([:])
ebb439
+    fi
ebb439
+}
ebb439
+
ebb439
+# wait_column EXPECTED [DATABASE:]TABLE [COLUMN [CONDITION...]]
ebb439
+#
ebb439
+# Wait until all of the values of COLUMN in the rows of TABLE (that
ebb439
+# satisfy CONDITION) equal EXPECTED (ignoring order).
ebb439
+#
ebb439
+# The default DATABASE is "sb".
ebb439
+#
ebb439
+# COLUMN defaults to _uuid if unspecified.
ebb439
+wait_column() {
ebb439
+    local expected=$(for d in $1; do echo $d; done | sort)
ebb439
+    local db=$(parse_db $2) table=$(parse_table $2) column=${3-_uuid}; shift; shift; shift
ebb439
+    local a=$1 b=$2 c=$3
ebb439
+
ebb439
+    echo
ebb439
+    echo "Waiting until $column in $db $table${1+ with $*} is $expected..."
ebb439
+    OVS_WAIT_UNTIL([
ebb439
+      found=$(ovn-${db}ctl --bare --columns $column find $table $a $b $c)
ebb439
+      found=$(for d in $found; do echo $d; done | sort)
ebb439
+      test "$expected" = "$found"
ebb439
+    ], [
ebb439
+      echo "$column in $db table $table has value $found, from the following rows:"
ebb439
+      ovn-${db}ctl list $table])
ebb439
+}
ebb439
+OVS_END_SHELL_HELPERS
ebb439
+
ebb439
 m4_define([OVN_POPULATE_ARP], [AT_CHECK(ovn_populate_arp__, [0], [ignore])])
ebb439
diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
ebb439
index 3dbedc843..79d580d3f 100644
ebb439
--- a/tests/ovn-nbctl.at
ebb439
+++ b/tests/ovn-nbctl.at
ebb439
@@ -536,18 +536,12 @@ snat             30.0.0.1                            192.168.1.0/24
ebb439
 snat             fd01::1                             fd11::/64
ebb439
 ])
ebb439
 
ebb439
-AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0],
ebb439
-[0
ebb439
-])
ebb439
+check_row_count nb:NAT 0 options:stateless=true
ebb439
 AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat_and_snat 40.0.0.2 192.168.1.4])
ebb439
-AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0],
ebb439
-[1
ebb439
-])
ebb439
+check_row_count nb:NAT 1 options:stateless=true
ebb439
 
ebb439
 AT_CHECK([ovn-nbctl --stateless lr-nat-add lr0 dnat_and_snat fd21::1 fd11::2])
ebb439
-AT_CHECK([ovn-nbctl --bare --columns=options list nat | grep stateless=true| wc -l], [0],
ebb439
-[2
ebb439
-])
ebb439
+check_row_count nb:NAT 2 options:stateless=true
ebb439
 
ebb439
 AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat fd21::1])
ebb439
 
ebb439
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
ebb439
index 94007fcac..49d74b08b 100644
ebb439
--- a/tests/ovn-northd.at
ebb439
+++ b/tests/ovn-northd.at
ebb439
@@ -22,130 +22,47 @@ nb_gwc1_uuid=`ovn-nbctl --bare --columns _uuid find Gateway_Chassis name="alice_
ebb439
 
ebb439
 # With the new ha_chassis_group table added, there should be no rows in
ebb439
 # gateway_chassis table in SB DB.
ebb439
-AT_CHECK([ovn-sbctl list gateway_chassis | wc -l], [0], [0
ebb439
-])
ebb439
-
ebb439
-# There should be one ha_chassis_group with the name "alice"
ebb439
-ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="alice"`
ebb439
-
ebb439
-AT_CHECK([test $ha_chassi_grp_name = alice])
ebb439
-
ebb439
-ha_chgrp_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group name=alice`
ebb439
-
ebb439
-AT_CHECK([ovn-sbctl --bare --columns ha_chassis_group find port_binding \
ebb439
-logical_port="cr-alice" | grep $ha_chgrp_uuid | wc -l], [0], [1
ebb439
-])
ebb439
+check_row_count Gateway_Chassis 0
ebb439
 
ebb439
 # There should be one ha_chassis_group with the name "alice"
ebb439
-ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="alice"`
ebb439
-
ebb439
-AT_CHECK([test $ha_chassi_grp_name = alice])
ebb439
-
ebb439
-ha_chgrp_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group name=alice`
ebb439
-
ebb439
-AT_CHECK([ovn-sbctl --bare --columns ha_chassis_group find port_binding \
ebb439
-logical_port="cr-alice" | grep $ha_chgrp_uuid | wc -l], [0], [1
ebb439
-])
ebb439
-
ebb439
-ha_ch=`ovn-sbctl --bare --columns ha_chassis  find ha_chassis_group`
ebb439
-# Trim the spaces.
ebb439
-ha_ch=`echo $ha_ch | sed 's/ //g'`
ebb439
-
ebb439
-ha_ch_list=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
ebb439
-do
ebb439
-    ha_ch_list="$ha_ch_list $i"
ebb439
-done
ebb439
+check_row_count HA_Chassis_Group 1 name=alice
ebb439
+ha_chgrp_uuid=$(fetch_column HA_Chassis_Group _uuid name=alice)
ebb439
+check_row_count Port_Binding 1 logical_port=cr-alice ha_chassis_group=$ha_chgrp_uuid
ebb439
 
ebb439
-# Trim the spaces.
ebb439
-ha_ch_list=`echo $ha_ch_list | sed 's/ //g'`
ebb439
-
ebb439
-AT_CHECK([test "$ha_ch_list" = "$ha_ch"])
ebb439
+ha_ch=$(fetch_column HA_Chassis_Group ha_chassis name=alice)
ebb439
+check_column "$ha_ch" HA_Chassis _uuid
ebb439
 
ebb439
 # Delete chassis - gw2 in SB DB.
ebb439
 # ovn-northd should not recreate ha_chassis rows
ebb439
 # repeatedly when gw2 is deleted.
ebb439
 ovn-sbctl chassis-del gw2
ebb439
 
ebb439
-ha_ch_list_1=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
ebb439
-do
ebb439
-    ha_ch_list_1="$ha_ch_list_1 $i"
ebb439
-done
ebb439
-
ebb439
-# Trim the spaces.
ebb439
-ha_ch_list_1=`echo $ha_ch_list_1 | sed 's/ //g'`
ebb439
-
ebb439
-ha_ch_list_2=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
ebb439
-do
ebb439
-    ha_ch_list_2="$ha_ch_list_2 $i"
ebb439
-done
ebb439
-
ebb439
-# Trim the spaces.
ebb439
-ha_ch_list_2=`echo $ha_ch_list_2 | sed 's/ //g'`
ebb439
-
ebb439
-AT_CHECK([test "$ha_ch_list_1" = "$ha_ch_list_2"])
ebb439
+ha_ch_list=$(fetch_column HA_Chassis _uuid)
ebb439
+check_column "$ha_ch_list" HA_Chassis _uuid
ebb439
 
ebb439
 # Add back the gw2 chassis
ebb439
 ovn-sbctl chassis-add gw2 geneve 1.2.4.8
ebb439
 
ebb439
 # delete the 2nd Gateway_Chassis on NBDB for alice port
ebb439
-gw_ch=`ovn-sbctl --bare --columns gateway_chassis find port_binding \
ebb439
-logical_port="cr-alice"`
ebb439
-AT_CHECK([test "$gw_ch" = ""])
ebb439
+check_column '' Port_Binding gateway_chassis logical_port=cr-alice
ebb439
 
ebb439
-ha_ch=`ovn-sbctl --bare --columns ha_chassis  find ha_chassis_group`
ebb439
-ha_ch=`echo $ha_ch | sed 's/ //g'`
ebb439
-# Trim the spaces.
ebb439
-echo "ha ch in grp = $ha_ch"
ebb439
-
ebb439
-ha_ch_list=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
ebb439
-do
ebb439
-    ha_ch_list="$ha_ch_list $i"
ebb439
-done
ebb439
-
ebb439
-# Trim the spaces.
ebb439
-ha_ch_list=`echo $ha_ch_list | sed 's/ //g'`
ebb439
-
ebb439
-AT_CHECK([test "$ha_ch_list" = "$ha_ch"])
ebb439
+ha_ch=$(fetch_column HA_Chassis_Group ha_chassis)
ebb439
+check_column "$ha_ch" HA_Chassis _uuid
ebb439
 
ebb439
 # delete the 2nd Gateway_Chassis on NBDB for alice port
ebb439
 ovn-nbctl --wait=sb set Logical_Router_Port alice gateway_chassis=${nb_gwc1_uuid}
ebb439
 
ebb439
 # There should be only 1 row in ha_chassis SB DB table.
ebb439
-AT_CHECK([ovn-sbctl --bare --columns _uuid list ha_chassis | wc -l], [0], [1
ebb439
-])
ebb439
-
ebb439
-AT_CHECK([ovn-sbctl list gateway_chassis | wc -l], [0], [0
ebb439
-])
ebb439
-
ebb439
-# There should be only 1 row in ha_chassis SB DB table.
ebb439
-AT_CHECK([ovn-sbctl --bare --columns _uuid list ha_chassis | wc -l], [0], [1
ebb439
-])
ebb439
+check_row_count HA_Chassis 1
ebb439
+check_row_count Gateway_Chassis 0
ebb439
 
ebb439
 # delete all the gateway_chassis on NBDB for alice port
ebb439
-
ebb439
 ovn-nbctl --wait=sb clear Logical_Router_Port alice gateway_chassis
ebb439
 
ebb439
 # expect that the ha_chassis doesn't exist anymore
ebb439
-AT_CHECK([ovn-sbctl list gateway_chassis | wc -l], [0], [0
ebb439
-])
ebb439
-
ebb439
-AT_CHECK([ovn-sbctl list ha_chassis | wc -l], [0], [0
ebb439
-])
ebb439
-
ebb439
-AT_CHECK([ovn-sbctl list ha_chassis_group | wc -l], [0], [0
ebb439
-])
ebb439
-
ebb439
-# expect that the ha_chassis doesn't exist anymore
ebb439
-AT_CHECK([ovn-sbctl list ha_chassis | wc -l], [0], [0
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl list ha_chassis_group | wc -l], [0], [0
ebb439
-])
ebb439
+check_row_count HA_Chassis 0
ebb439
+check_row_count Gateway_Chassis 0
ebb439
+check_row_count Ha_Chassis_Group 0
ebb439
 
ebb439
 AT_CLEANUP
ebb439
 
ebb439
@@ -202,11 +119,11 @@ ovn_start
ebb439
 
ebb439
 ovn-nbctl ls-add S1
ebb439
 ovn-nbctl --wait=sb lsp-add S1 S1-vm1
ebb439
-AT_CHECK([test x`ovn-nbctl lsp-get-up S1-vm1` = xdown])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=S1-vm1 'up!=true'
ebb439
 
ebb439
 ovn-sbctl chassis-add hv1 geneve 127.0.0.1
ebb439
 ovn-sbctl lsp-bind S1-vm1 hv1
ebb439
-AT_CHECK([test x`ovn-nbctl lsp-get-up S1-vm1` = xup])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=S1-vm1 'up=true'
ebb439
 
ebb439
 AT_CLEANUP
ebb439
 
ebb439
@@ -382,8 +299,7 @@ as northd start_daemon ovn-northd --unixctl="$ovs_base"/northd/ovn-northd.ctl --
ebb439
 ovn-nbctl ls-add sw
ebb439
 ovn-nbctl --wait=sb lsp-add sw p1
ebb439
 # northd created with unixctl option successfully created port_binding entry
ebb439
-AT_CHECK([ovn-sbctl --bare --columns datapath find port_binding logical_port="p1" | wc -l], [0], [1
ebb439
-])
ebb439
+check_row_count Port_Binding 1 logical_port=p1
ebb439
 AT_CHECK([ovn-nbctl --wait=sb lsp-del p1])
ebb439
 
ebb439
 # ovs-appctl exit with unixctl option
ebb439
@@ -392,15 +308,13 @@ OVS_APP_EXIT_AND_WAIT_BY_TARGET(["$ovs_base"/northd/ovn-northd.ctl], ["$ovs_base
ebb439
 # Check no port_binding entry for new port as ovn-northd is not running
ebb439
 ovn-nbctl lsp-add sw p2
ebb439
 ovn-nbctl --timeout=10 --wait=sb sync
ebb439
-AT_CHECK([ovn-sbctl --bare --columns datapath find port_binding logical_port="p2" | wc -l], [0], [0
ebb439
-])
ebb439
+check_row_count Port_Binding 0 logical_port=p2
ebb439
 
ebb439
 # test default unixctl path
ebb439
 as northd start_daemon ovn-northd --ovnnb-db=unix:"$ovs_base"/ovn-nb/ovn-nb.sock --ovnsb-db=unix:"$ovs_base"/ovn-sb/ovn-sb.sock
ebb439
 ovn-nbctl --wait=sb lsp-add sw p3
ebb439
 # northd created with default unixctl path successfully created port_binding entry
ebb439
-AT_CHECK([ovn-sbctl --bare --columns datapath find port_binding logical_port="p3" | wc -l], [0], [1
ebb439
-])
ebb439
+check_row_count Port_Binding 1 logical_port=p3
ebb439
 
ebb439
 as ovn-sb
ebb439
 OVS_APP_EXIT_AND_WAIT([ovsdb-server])
ebb439
@@ -419,28 +333,22 @@ ovn-nbctl --wait=sb ha-chassis-group-add hagrp1
ebb439
 # ovn-northd should not create HA chassis group and HA chassis rows
ebb439
 # unless the HA chassis group in OVN NB DB is associated to
ebb439
 # a logical router port or logical port of type external.
ebb439
-AT_CHECK([ovn-sbctl --bare --columns name find ha_chassis_group name="hagrp1" \
ebb439
-| wc -l], [0], [0
ebb439
-])
ebb439
+check_row_count HA_Chassis_Group 0 name=hagrp1
ebb439
 
ebb439
 ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch1 30
ebb439
 ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch2 20
ebb439
 ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch3 10
ebb439
 
ebb439
 # There should be no HA_Chassis rows in SB DB.
ebb439
-AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
ebb439
-| grep -v '-' | wc -l ], [0], [0
ebb439
-])
ebb439
+check_row_count HA_Chassis 0
ebb439
 
ebb439
 # Add chassis ch1.
ebb439
 ovn-sbctl chassis-add ch1 geneve 127.0.0.2
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list chassis | grep ch1 | wc -l`])
ebb439
+wait_row_count Chassis 1 name=ch1
ebb439
 
ebb439
 # There should be no HA_Chassis rows
ebb439
-AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
ebb439
-| grep -v '-' | wc -l ], [0], [0
ebb439
-])
ebb439
+check_row_count HA_Chassis 0
ebb439
 
ebb439
 # Create a logical router port and attach ha chassis group.
ebb439
 ovn-nbctl lr-add lr0
ebb439
@@ -449,44 +357,21 @@ ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
ebb439
 hagrp1_uuid=`ovn-nbctl --bare --columns _uuid find ha_chassis_group name=hagrp1`
ebb439
 ovn-nbctl set logical_router_port lr0-public ha_chassis_group=$hagrp1_uuid
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1 name=hagrp1
ebb439
 
ebb439
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+check_row_count HA_Chassis 3
ebb439
 
ebb439
 # Make sure that ovn-northd doesn't recreate the ha_chassis
ebb439
 # records if the chassis record is missing in SB DB.
ebb439
-
ebb439
-ha_ch_list_1=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
ebb439
-do
ebb439
-    ha_ch_list_1="$ha_ch_list_1 $i"
ebb439
-done
ebb439
-
ebb439
-# Trim the spaces.
ebb439
-ha_ch_list_1=`echo $ha_ch_list_1 | sed 's/ //g'`
ebb439
-
ebb439
-ha_ch_list_2=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
ebb439
-do
ebb439
-    ha_ch_list_2="$ha_ch_list_2 $i"
ebb439
-done
ebb439
-
ebb439
-# Trim the spaces.
ebb439
-ha_ch_list_2=`echo $ha_ch_list_2 | sed 's/ //g'`
ebb439
-
ebb439
-AT_CHECK([test "$ha_ch_list_1" = "$ha_ch_list_2"])
ebb439
+ha_ch_list=$(fetch_column HA_Chassis _uuid)
ebb439
+check_column "$ha_ch_list" HA_Chassis _uuid
ebb439
 
ebb439
 # 2 HA chassis should be created with 'chassis' column empty because
ebb439
 # we have not added hv1 and hv2 chassis to the SB DB.
ebb439
-AT_CHECK([test 2 = `ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
ebb439
-| grep -v '-' | wc -l`])
ebb439
+check_row_count HA_Chassis 2 'chassis=[[]]'
ebb439
 
ebb439
 # We should have 1 ha chassis with 'chassis' column set for hv1
ebb439
-AT_CHECK([test 1 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | awk '{print $3}' \
ebb439
-| grep '-' | wc -l`])
ebb439
+check_row_count HA_Chassis 1 'chassis!=[[]]'
ebb439
 
ebb439
 # Create another logical router port and associate to the same ha_chasis_group
ebb439
 ovn-nbctl lr-add lr1
ebb439
@@ -495,94 +380,68 @@ ovn-nbctl lrp-add lr1 lr1-public 00:00:20:20:12:14 182.168.0.100/24
ebb439
 ovn-nbctl set logical_router_port lr1-public ha_chassis_group=$hagrp1_uuid
ebb439
 
ebb439
 # We should still have 1 HA chassis group and 3 HA chassis in SB DB.
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1 name=hagrp1
ebb439
+check_row_count HA_Chassis 3
ebb439
 
ebb439
 # Change the priority of ch1 - ha chassis in NB DB. It should get
ebb439
 # reflected in SB DB.
ebb439
 ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch1 100
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns priority find \
ebb439
-ha_chassis | grep 100 | wc -l`])
ebb439
+wait_row_count HA_Chassis 1 priority=100
ebb439
 
ebb439
 # Delete ch1 HA chassis in NB DB.
ebb439
 ovn-nbctl --wait=sb ha-chassis-group-remove-chassis hagrp1 ch1
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis 2
ebb439
 
ebb439
 # Add back the ha chassis
ebb439
 ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch1 40
ebb439
-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis 3
ebb439
 
ebb439
 # Delete lr0-public. We should still have 1 HA chassis group and
ebb439
 # 3 HA chassis in SB DB.
ebb439
 ovn-nbctl --wait=sb lrp-del lr0-public
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1 name=hagrp1
ebb439
+wait_row_count HA_Chassis 3
ebb439
 
ebb439
 # Delete lr1-public. There should be no HA chassis group in SB DB.
ebb439
 ovn-nbctl --wait=sb lrp-del lr1-public
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | grep chassis | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 0 name=hagrp1
ebb439
+wait_row_count HA_Chassis 0
ebb439
 
ebb439
 # Add lr0-public again
ebb439
 ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
ebb439
 ovn-nbctl set logical_router_port lr0-public ha_chassis_group=$hagrp1_uuid
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1 name=hagrp1
ebb439
+wait_row_count HA_Chassis 3
ebb439
 
ebb439
 # Create a Gateway chassis. ovn-northd should ignore this.
ebb439
 ovn-nbctl lrp-set-gateway-chassis lr0-public ch-1 20
ebb439
 
ebb439
 # There should be only 1 HA chassis group in SB DB with the
ebb439
 # name hagrp1.
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group | wc -l`])
ebb439
-
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1
ebb439
+wait_row_count HA_Chassis_Group 1 name=hagrp1
ebb439
+wait_row_count HA_Chassis 3
ebb439
 
ebb439
 # Now delete HA chassis group. ovn-northd should create HA chassis group
ebb439
 # with the Gateway chassis name
ebb439
 ovn-nbctl clear logical_router_port lr0-public ha_chassis_group
ebb439
 ovn-nbctl ha-chassis-group-del hagrp1
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="lr0-public" | wc -l`])
ebb439
-
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid \
ebb439
-find ha_chassis | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 0 name=hagrp1
ebb439
+wait_row_count HA_Chassis_Group 1 name=lr0-public
ebb439
+wait_row_count HA_Chassis 1
ebb439
 
ebb439
 ovn-nbctl lrp-set-gateway-chassis lr0-public ch2 10
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="lr0-public" | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1 name=lr0-public
ebb439
 
ebb439
 ovn-sbctl --bare --columns _uuid find ha_chassis
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis 2
ebb439
 
ebb439
 # Test if 'ref_chassis' column is properly set or not in
ebb439
 # SB DB ha_chassis_group.
ebb439
@@ -601,35 +460,23 @@ ovn-nbctl lsp-set-addresses sw0-lr0 router
ebb439
 ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
ebb439
 
ebb439
 ovn-sbctl lsp-bind sw0-p1 comp1
ebb439
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p1` = xup])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=sw0-p1 up=true
ebb439
 
ebb439
-comp1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp1"`
ebb439
-comp2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp2"`
ebb439
-ch2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp1"`
ebb439
+comp1_ch_uuid=$(fetch_column Chassis _uuid name=comp1)
ebb439
+comp2_ch_uuid=$(fetch_column Chassis _uuid name=comp2)
ebb439
+ch2_ch_uuid=$comp1_ch_uuid
ebb439
 
ebb439
 echo "comp1_ch_uuid = $comp1_ch_uuid"
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$comp1_ch_uuid" = "$ref_ch_list"])
ebb439
+wait_column "$comp1_ch_uuid" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # unbind sw0-p1
ebb439
 ovn-sbctl lsp-unbind sw0-p1
ebb439
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p1` = xdown])
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "" = "$ref_ch_list"])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=sw0-p1 up=false
ebb439
+wait_column "" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Bind sw0-p1 in comp2
ebb439
 ovn-sbctl lsp-bind sw0-p1 comp2
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$comp2_ch_uuid" = "$ref_ch_list"])
ebb439
+wait_column "$comp2_ch_uuid" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 ovn-nbctl ls-add sw1
ebb439
 ovn-nbctl lsp-add sw1 sw1-p1
ebb439
@@ -643,14 +490,10 @@ ovn-nbctl lsp-set-options sw1-lr1 router-port=lr1-sw1
ebb439
 # Bind sw1-p1 in comp1.
ebb439
 ovn-sbctl lsp-bind sw1-p1 comp1
ebb439
 # Wait until sw1-p1 is up
ebb439
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw1-p1` = xup])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=sw1-p1 up=true
ebb439
 
ebb439
 # sw1-p1 is not connected to lr0. So comp1 should not be in 'ref_chassis'
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$comp2_ch_uuid" = "$ref_ch_list"])
ebb439
+wait_column "$comp2_ch_uuid" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Now attach sw0 to lr1
ebb439
 ovn-nbctl lrp-add lr1 lr1-sw0 00:00:20:20:12:16 10.0.0.10/24
ebb439
@@ -661,30 +504,14 @@ ovn-nbctl lsp-set-options sw0-lr1 router-port=lr1-sw0
ebb439
 
ebb439
 # Both comp1 and comp2 should be in 'ref_chassis' as sw1 is indirectly
ebb439
 # connected to lr0
ebb439
-exp_ref_ch_list=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list chassis | sort`
ebb439
-do
ebb439
-    if test $i = $comp1_ch_uuid; then
ebb439
-        exp_ref_ch_list="${exp_ref_ch_list}$i"
ebb439
-    elif test $i = $comp2_ch_uuid; then
ebb439
-        exp_ref_ch_list="${exp_ref_ch_list}$i"
ebb439
-    fi
ebb439
-done
ebb439
-
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$exp_ref_ch_list" = "$ref_ch_list"])
ebb439
+exp_ref_ch_list="$comp1_ch_uuid $comp2_ch_uuid"
ebb439
+
ebb439
+wait_column "$exp_ref_ch_list" HA_Chassis_Group ref_chassis 
ebb439
 
ebb439
 # Unind sw1-p1. comp2 should not be in the ref_chassis.
ebb439
 ovn-sbctl lsp-unbind sw1-p1
ebb439
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw1-p1` = xdown])
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$comp2_ch_uuid" = "$ref_ch_list"])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=sw1-p1 up=false
ebb439
+wait_column "$comp2_ch_uuid" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Create sw2 and attach it to lr2
ebb439
 ovn-nbctl ls-add sw2
ebb439
@@ -699,14 +526,10 @@ ovn-nbctl lsp-set-options sw2-lr2 router-port=lr2-sw2
ebb439
 # Bind sw2-p1 to comp1
ebb439
 ovn-sbctl lsp-bind sw2-p1 comp1
ebb439
 # Wait until sw2-p1 is up
ebb439
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw2-p1` = xup])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=sw2-p1 up=true
ebb439
 
ebb439
 # sw2-p1 is not connected to lr0. So comp1 should not be in 'ref_chassis'
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$comp2_ch_uuid" = "$ref_ch_list"])
ebb439
+wait_column "$comp2_ch_uuid" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Now attach sw1 to lr2. With this sw2-p1 is indirectly connected to lr0.
ebb439
 ovn-nbctl lrp-add lr2 lr2-sw1 00:00:20:20:12:18 20.0.0.10/24
ebb439
@@ -717,53 +540,32 @@ ovn-nbctl lsp-set-options sw1-lr2 router-port=lr2-sw1
ebb439
 
ebb439
 # sw2-p1 is indirectly connected to lr0. So comp1 (and comp2) should be in
ebb439
 # 'ref_chassis'
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$exp_ref_ch_list" = "$ref_ch_list"])
ebb439
+wait_column "$exp_ref_ch_list" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Create sw0-p2 and bind it to comp1
ebb439
 ovn-nbctl lsp-add sw0 sw0-p2
ebb439
 ovn-sbctl lsp-bind sw0-p2 comp1
ebb439
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p2` = xup])
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$exp_ref_ch_list" = "$ref_ch_list"])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=sw0-p2 up=true
ebb439
+wait_column "$exp_ref_ch_list" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # unbind sw0-p2
ebb439
 ovn-sbctl lsp-unbind sw0-p2
ebb439
-OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p2` = xdown])
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$exp_ref_ch_list" = "$ref_ch_list"])
ebb439
+wait_row_count nb:Logical_Switch_Port 1 name=sw0-p2 up=false
ebb439
+wait_column "$exp_ref_ch_list" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Delete lr1-sw0. comp1 should be deleted from ref_chassis as there is no link
ebb439
 # from sw1 and sw2 to lr0.
ebb439
 ovn-nbctl lrp-del lr1-sw0
ebb439
 
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$comp2_ch_uuid" = "$ref_ch_list"])
ebb439
+wait_column "$comp2_ch_uuid" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Set redirect-chassis option to lr0-public. It should be ignored.
ebb439
 ovn-nbctl set logical_router_port lr0-public options:redirect-chassis=ch1
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1
ebb439
+wait_row_count HA_Chassis_Group 1 name=lr0-public
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="lr0-public" | wc -l`])
ebb439
-
ebb439
-ovn-sbctl --bare --columns _uuid find ha_chassis
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis 2
ebb439
 
ebb439
 # Delete the gateway chassis. HA chassis group should be created in SB DB
ebb439
 # for the redirect-chassis option.
ebb439
@@ -809,8 +611,8 @@ ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch3 10
ebb439
 # ovn-northd should not create HA chassis group and HA chassis rows
ebb439
 # unless the HA chassis group in OVN NB DB is associated to
ebb439
 # a logical router port or logical port of type external.
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group |  wc -l`])
ebb439
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 0
ebb439
+check_row_count HA_Chassis 0
ebb439
 
ebb439
 hagrp1_uuid=`ovn-nbctl --bare --columns _uuid find ha_chassis_group \
ebb439
 name=hagrp1`
ebb439
@@ -819,69 +621,50 @@ name=hagrp1`
ebb439
 # So ha_chassis_group should be ignored.
ebb439
 ovn-nbctl set logical_switch_port sw0-pext1 ha_chassis_group=$hagrp1_uuid
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | grep chassis | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 0 name=hagrp1
ebb439
+check_row_count HA_Chassis 0
ebb439
 
ebb439
 # Set the type of sw0-pext1 to external
ebb439
 ovn-nbctl lsp-set-type sw0-pext1 external
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1 name=hagrp1
ebb439
+check_row_count HA_Chassis 3
ebb439
 
ebb439
 sb_hagrp1_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group \
ebb439
 name=hagrp1`
ebb439
 
ebb439
-AT_CHECK([test "$sb_hagrp1_uuid" = `ovn-sbctl --bare --columns \
ebb439
-ha_chassis_group find port_binding logical_port=sw0-pext1`])
ebb439
+check_row_count Port_Binding 1 logical_port=sw0-pext1 ha_chassis_group=$sb_hagrp1_uuid
ebb439
 
ebb439
 # Set the type of sw0-pext2 to external and associate ha_chassis_group
ebb439
 ovn-nbctl lsp-set-type sw0-pext2 external
ebb439
 ovn-nbctl set logical_switch_port sw0-pext2 ha_chassis_group=$hagrp1_uuid
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis |
ebb439
-grep -v chassis-name | wc -l`])
ebb439
-AT_CHECK([test "$sb_hagrp1_uuid" = `ovn-sbctl --bare --columns \
ebb439
-ha_chassis_group find port_binding logical_port=sw0-pext1`])
ebb439
-
ebb439
-OVS_WAIT_UNTIL([test "$sb_hagrp1_uuid" = `ovn-sbctl --bare --columns \
ebb439
-ha_chassis_group find port_binding logical_port=sw0-pext2`])
ebb439
+wait_row_count HA_Chassis_Group 1 name=hagrp1
ebb439
+check_row_count HA_Chassis 3
ebb439
+check_row_count Port_Binding 1 logical_port=sw0-pext1 ha_chassis_group=$sb_hagrp1_uuid
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-pext2 ha_chassis_group=$sb_hagrp1_uuid
ebb439
 
ebb439
 # sw0-p1 is a normal port. So ha_chassis_group should not be set
ebb439
 # in port_binding.
ebb439
 ovn-nbctl --wait=sb set logical_switch_port sw0-p1 \
ebb439
 ha_chassis_group=$hagrp1_uuid
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
ebb439
-logical_port=sw0-p1) = x], [0], [])
ebb439
+wait_row_count Port_Binding 0 logical_port=sw0-p1 'chassis!=[[]]'
ebb439
 
ebb439
 # Clear ha_chassis_group for sw0-pext1
ebb439
 ovn-nbctl --wait=sb clear logical_switch_port sw0-pext1 ha_chassis_group
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
ebb439
-logical_port=sw0-pext1) = x], [0], [])
ebb439
+wait_row_count Port_Binding 0 logical_port=sw0-pext1 'chassis!=[[]]'
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="hagrp1" | wc -l`])
ebb439
-
ebb439
-AT_CHECK([test 3 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1 name=hagrp1
ebb439
+wait_row_count HA_Chassis 3
ebb439
 
ebb439
 # Clear ha_chassis_group for sw0-pext2
ebb439
 ovn-nbctl --wait=sb clear logical_switch_port sw0-pext2 ha_chassis_group
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding \
ebb439
-logical_port=sw0-pext2) = x], [0], [])
ebb439
-
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group |  wc -l`])
ebb439
-AT_CHECK([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
ebb439
+wait_row_count Port_Binding 0 logical_port=sw0-pext2 'chassis!=[[]]'
ebb439
+wait_row_count HA_Chassis_Group 0
ebb439
+check_row_count HA_Chassis 0
ebb439
 
ebb439
 as ovn-sb
ebb439
 OVS_APP_EXIT_AND_WAIT([ovsdb-server])
ebb439
@@ -969,17 +752,11 @@ ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
ebb439
 
ebb439
 ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1
ebb439
 
ebb439
-uuid=`ovn-sbctl --columns=_uuid --bare find Port_Binding logical_port=cr-R1-S1`
ebb439
-echo "CR-LRP UUID is: " $uuid
ebb439
-
ebb439
 ovn-nbctl lrp-set-redirect-type R1-S1 bridged
ebb439
-OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0], [bridged
ebb439
-])
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-R1-S1 options:redirect-type=bridged
ebb439
 
ebb439
 ovn-nbctl lrp-set-redirect-type R1-S1 overlay
ebb439
-OVS_WAIT_UNTIL([ovn-sbctl get Port_Binding ${uuid} options:redirect-type], [0], [overlay
ebb439
-])
ebb439
-
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-R1-S1 options:redirect-type=overlay
ebb439
 AT_CLEANUP
ebb439
 
ebb439
 AT_SETUP([ovn -- check stateless dnat_and_snat rule])
ebb439
@@ -998,9 +775,6 @@ ovn-nbctl --wait=sb lsp-set-options S1-R1 router-port=R1-S1
ebb439
 
ebb439
 ovn-nbctl lrp-set-gateway-chassis R1-S1 gw1
ebb439
 
ebb439
-uuid=`ovn-sbctl --columns=_uuid --bare find Port_Binding logical_port=cr-R1-S1`
ebb439
-echo "CR-LRP UUID is: " $uuid
ebb439
-
ebb439
 # IPV4
ebb439
 ovn-nbctl lr-nat-add R1 dnat_and_snat  172.16.1.1 50.0.0.11
ebb439
 
ebb439
@@ -1359,37 +1133,32 @@ ovn-nbctl lb-add lb1 10.0.0.10:80 10.0.0.3:80,20.0.0.3:80
ebb439
 ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1
ebb439
 ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
ebb439
+wait_row_count Service_Monitor 0
ebb439
 
ebb439
 ovn-nbctl --wait=sb -- --id=@hc create \
ebb439
 Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
ebb439
 health_check @hc
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
ebb439
+wait_row_count Service_Monitor 0
ebb439
 
ebb439
 # create logical switches and ports
ebb439
 ovn-nbctl ls-add sw0
ebb439
 ovn-nbctl --wait=sb lsp-add sw0 sw0-p1 -- lsp-set-addresses sw0-p1 \
ebb439
 "00:00:00:00:00:03 10.0.0.3"
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | wc -l`])
ebb439
+wait_row_count Service_Monitor 0
ebb439
 
ebb439
 ovn-nbctl ls-add sw1
ebb439
 ovn-nbctl --wait=sb lsp-add sw1 sw1-p1 -- lsp-set-addresses sw1-p1 \
ebb439
 "02:00:00:00:00:03 20.0.0.3"
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l`])
ebb439
+wait_row_count Service_Monitor 0
ebb439
 
ebb439
 ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.2
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | wc -l`])
ebb439
+wait_row_count Service_Monitor 1
ebb439
 
ebb439
 ovn-nbctl --wait=sb set load_balancer . ip_port_mappings:20.0.0.3=sw1-p1:20.0.0.2
ebb439
-
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l`])
ebb439
+wait_row_count Service_Monitor 2
ebb439
 
ebb439
 ovn-nbctl --wait=sb ls-lb-add sw0 lb1
ebb439
 
ebb439
@@ -1400,7 +1169,7 @@ AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
 
ebb439
 # Delete the Load_Balancer_Health_Check
ebb439
 ovn-nbctl --wait=sb clear load_balancer . health_check
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
ebb439
+wait_row_count Service_Monitor 0
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
ebb439
 AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
@@ -1412,8 +1181,7 @@ ovn-nbctl --wait=sb -- --id=@hc create \
ebb439
 Load_Balancer_Health_Check vip="10.0.0.10\:80" -- add Load_Balancer . \
ebb439
 health_check @hc
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l`])
ebb439
+wait_row_count Service_Monitor 2
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
ebb439
 AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
@@ -1427,9 +1195,7 @@ sm_sw1_p1=`ovn-sbctl --bare --columns _uuid find service_monitor logical_port=sw
ebb439
 # Set the service monitor for sw1-p1 to offline
ebb439
 ovn-sbctl set service_monitor $sm_sw1_p1 status=offline
ebb439
 
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1`
ebb439
-    test "$status" = "offline"])
ebb439
+wait_row_count Service_Monitor 1 logical_port=sw1-p1 status=offline
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
ebb439
 AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
@@ -1439,9 +1205,7 @@ AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
 # Set the service monitor for sw0-p1 to offline
ebb439
 ovn-sbctl set service_monitor $sm_sw0_p1 status=offline
ebb439
 
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw0-p1`
ebb439
-    test "$status" = "offline"])
ebb439
+wait_row_count Service_Monitor 1 logical_port=sw0-p1 status=offline
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
ebb439
 AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
@@ -1457,9 +1221,7 @@ AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
 ovn-sbctl set service_monitor $sm_sw0_p1 status=online
ebb439
 ovn-sbctl set service_monitor $sm_sw1_p1 status=online
ebb439
 
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1`
ebb439
-    test "$status" = "online"])
ebb439
+wait_row_count Service_Monitor 1 logical_port=sw1-p1 status=online
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
ebb439
 AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
@@ -1468,9 +1230,7 @@ AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
 
ebb439
 # Set the service monitor for sw1-p1 to error
ebb439
 ovn-sbctl set service_monitor $sm_sw1_p1 status=error
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1`
ebb439
-    test "$status" = "error"])
ebb439
+wait_row_count Service_Monitor 1 logical_port=sw1-p1 status=error
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
ebb439
 | grep priority=120 > lflows.txt
ebb439
@@ -1492,16 +1252,9 @@ health_check @hc
ebb439
 #    * 10.0.0.3:1000
ebb439
 #    * 20.0.0.3:80
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 3 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l`])
ebb439
-
ebb439
-# There should be 2 rows with logical_port=sw0-p1
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor logical_port=sw0-p1 | sed '/^$/d' | wc -l`])
ebb439
-
ebb439
-# There should be 1 row1 with port=1000
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor port=1000 | sed '/^$/d' | wc -l`])
ebb439
+wait_row_count Service_Monitor 3
ebb439
+wait_row_count Service_Monitor 2 logical_port=sw0-p1
ebb439
+wait_row_count Service_Monitor 1 port=1000
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
ebb439
 AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
@@ -1512,9 +1265,7 @@ AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
 # Set the service monitor for sw1-p1 to online
ebb439
 ovn-sbctl set service_monitor $sm_sw1_p1 status=online
ebb439
 
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    status=`ovn-sbctl --bare --columns status find service_monitor logical_port=sw1-p1`
ebb439
-    test "$status" = "online"])
ebb439
+wait_row_count Service_Monitor 1 logical_port=sw1-p1 status=online
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
ebb439
 AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
@@ -1542,26 +1293,23 @@ ovn-nbctl ls-lb-add sw0 lb2
ebb439
 ovn-nbctl ls-lb-add sw1 lb2
ebb439
 ovn-nbctl lr-lb-add lr0 lb2
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 5 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l`])
ebb439
+wait_row_count Service_Monitor 5
ebb439
 
ebb439
 # Change the svc_monitor_mac. This should get reflected in service_monitor table rows.
ebb439
 ovn-nbctl set NB_Global . options:svc_monitor_mac="fe:a0:65:a2:01:03"
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 5 = `ovn-sbctl --bare --columns src_mac find \
ebb439
-service_monitor | grep "fe:a0:65:a2:01:03" | wc -l`])
ebb439
+wait_row_count Service_Monitor 5 src_mac='"fe:a0:65:a2:01:03"'
ebb439
 
ebb439
 # Change the source ip for 10.0.0.3 backend ip in lb2
ebb439
 ovn-nbctl --wait=sb set load_balancer $lb2_uuid ip_port_mappings:10.0.0.3=sw0-p1:10.0.0.100
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns src_ip find \
ebb439
-service_monitor logical_port=sw0-p1 | grep "10.0.0.100" | wc -l`])
ebb439
+wait_row_count Service_Monitor 1 logical_port=sw0-p1 src_ip=10.0.0.100
ebb439
 
ebb439
 ovn-nbctl --wait=sb lb-del lb1
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find service_monitor | sed '/^$/d' | wc -l`])
ebb439
+wait_row_count Service_Monitor 2
ebb439
 
ebb439
 ovn-nbctl --wait=sb lb-del lb2
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
ebb439
+wait_row_count Service_Monitor 0
ebb439
 
ebb439
 AT_CLEANUP
ebb439
 
ebb439
@@ -1638,22 +1386,14 @@ AT_CHECK([ovn-nbctl --wait=sb sync], [0])
ebb439
 
ebb439
 # Ports are bound on different datapaths so it's expected that they both
ebb439
 # get tunnel_key == 1.
ebb439
-AT_CHECK([test 1 = $(ovn-sbctl --bare --columns tunnel_key find \
ebb439
-port_binding logical_port=lsp1)])
ebb439
-AT_CHECK([test 1 = $(ovn-sbctl --bare --columns tunnel_key find \
ebb439
-port_binding logical_port=lsp2)])
ebb439
+check_column 1 Port_Binding tunnel_key logical_port=lsp1
ebb439
+check_column 1 Port_Binding tunnel_key logical_port=lsp2
ebb439
 
ebb439
 ovn-nbctl lsp-del lsp2 -- lsp-add ls1 lsp2
ebb439
 AT_CHECK([ovn-nbctl --wait=sb sync], [0])
ebb439
 
ebb439
-AT_CHECK([test 1 = $(ovn-sbctl --bare --columns tunnel_key find \
ebb439
-port_binding logical_port=lsp1)])
ebb439
-AT_CHECK([test 2 = $(ovn-sbctl --bare --columns tunnel_key find \
ebb439
-port_binding logical_port=lsp2)])
ebb439
-
ebb439
-# ovn-northd should allocate a new tunnel_key for lsp1 or lsp2 to maintain
ebb439
-# unique DB indices.
ebb439
-AT_CHECK([test ${pb1_key} != ${pb2_key}])
ebb439
+check_column 1 Port_Binding tunnel_key logical_port=lsp1
ebb439
+check_column 2 Port_Binding tunnel_key logical_port=lsp2
ebb439
 
ebb439
 AT_CLEANUP
ebb439
 
ebb439
@@ -1679,7 +1419,7 @@ AT_CHECK([ovn-nbctl --wait=sb sync], [0])
ebb439
 ovn-nbctl lsp-del lsp2 -- lsp-add ls1 lsp2
ebb439
 AT_CHECK([ovn-nbctl --wait=sb sync], [0])
ebb439
 
ebb439
-AT_CHECK([test 0 = $(ovn-sbctl list Ha_Chassis_Group | wc -l)])
ebb439
+check_row_count HA_Chassis_Group 0
ebb439
 
ebb439
 AT_CLEANUP
ebb439
 
ebb439
@@ -1745,20 +1485,14 @@ ls2_key=$(ovn-sbctl --columns tunnel_key --bare list Datapath_Binding ls2)
ebb439
 # Add lsp1 & lsp2 to a port group. This should generate two entries in the
ebb439
 # SB (one per logical switch).
ebb439
 ovn-nbctl --wait=sb pg-add pg_test lsp1 lsp2
ebb439
-AT_CHECK([test 2 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
ebb439
-AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls1_key}_pg_test], [0], [dnl
ebb439
-lsp1
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls2_key}_pg_test], [0], [dnl
ebb439
-lsp2
ebb439
-])
ebb439
+wait_row_count Port_Group 2
ebb439
+check_row_count Port_Group 1 name=${ls1_key}_pg_test
ebb439
+check_row_count Port_Group 1 name=${ls2_key}_pg_test
ebb439
 
ebb439
 # Delete logical switch ls1. This should remove the associated SB Port_Group.
ebb439
 ovn-nbctl --wait=sb ls-del ls1
ebb439
-AT_CHECK([test 1 = $(ovn-sbctl --columns _uuid list Port_Group | grep uuid -c)])
ebb439
-AT_CHECK([ovn-sbctl --columns ports --bare find Port_Group name=${ls2_key}_pg_test], [0], [dnl
ebb439
-lsp2
ebb439
-])
ebb439
+wait_row_count Port_Group 1
ebb439
+check_row_count Port_Group 1 name=${ls2_key}_pg_test
ebb439
 
ebb439
 AT_CLEANUP
ebb439
 
ebb439
diff --git a/tests/ovn.at b/tests/ovn.at
ebb439
index 616af83bd..ba17246d4 100644
ebb439
--- a/tests/ovn.at
ebb439
+++ b/tests/ovn.at
ebb439
@@ -2063,20 +2063,12 @@ get_lsp_uuid () {
ebb439
 # explictly
ebb439
 
ebb439
 # For Chassis hv1
ebb439
-AT_CHECK_UNQUOTED([ovn-sbctl  --column encap list port_binding lp11], [0], [dnl
ebb439
-encap               : [[]]
ebb439
-])
ebb439
-AT_CHECK_UNQUOTED([ovn-sbctl  --column encap list port_binding lp12], [0], [dnl
ebb439
-encap               : [[]]
ebb439
-])
ebb439
+check_row_count Port_Binding 1 logical_port=lp11 'encap=[[]]'
ebb439
+check_row_count Port_Binding 1 logical_port=lp12 'encap=[[]]'
ebb439
 
ebb439
 # For Chassis hv2
ebb439
-AT_CHECK_UNQUOTED([ovn-sbctl  --column encap list port_binding lp21], [0], [dnl
ebb439
-encap               : [[]]
ebb439
-])
ebb439
-AT_CHECK_UNQUOTED([ovn-sbctl  --column encap list port_binding lp22], [0], [dnl
ebb439
-encap               : [[]]
ebb439
-])
ebb439
+check_row_count Port_Binding 1 logical_port=lp21 'encap=[[]]'
ebb439
+check_row_count Port_Binding 1 logical_port=lp22 'encap=[[]]'
ebb439
 
ebb439
 # Bind the ports to the encap-ip
ebb439
 for i in 1 2; do
ebb439
@@ -2092,26 +2084,14 @@ sleep 1
ebb439
 # ports to be bound to geneve tunnels.
ebb439
 
ebb439
 # For Chassis 1
ebb439
-encap_rec=`ovn-sbctl --data=bare --no-heading --column _uuid find encap chassis_name=hv1 type=geneve ip=192.168.0.1`
ebb439
-
ebb439
-AT_CHECK_UNQUOTED([ovn-sbctl  --column encap list port_binding lp11], [0], [dnl
ebb439
-encap               : ${encap_rec}
ebb439
-])
ebb439
-
ebb439
-AT_CHECK_UNQUOTED([ovn-sbctl  --column encap list port_binding lp12], [0], [dnl
ebb439
-encap               : ${encap_rec}
ebb439
-])
ebb439
+encap_rec=$(fetch_column Encap _uuid chassis_name=hv1 type=geneve ip=192.168.0.1)
ebb439
+check_row_count Port_Binding 1 logical_port=lp11 encap=$encap_rec
ebb439
+check_row_count Port_Binding 1 logical_port=lp12 encap=$encap_rec
ebb439
 
ebb439
 # For Chassis 2
ebb439
-encap_rec=`ovn-sbctl --data=bare --no-heading --column _uuid find encap chassis_name=hv2 type=geneve ip=192.168.0.2`
ebb439
-
ebb439
-AT_CHECK_UNQUOTED([ovn-sbctl  --column encap list port_binding lp21], [0], [dnl
ebb439
-encap               : ${encap_rec}
ebb439
-])
ebb439
-
ebb439
-AT_CHECK_UNQUOTED([ovn-sbctl  --column encap list port_binding lp22], [0], [dnl
ebb439
-encap               : ${encap_rec}
ebb439
-])
ebb439
+encap_rec=$(fetch_column Encap _uuid chassis_name=hv2 type=geneve ip=192.168.0.2)
ebb439
+check_row_count Port_Binding 1 logical_port=lp21 encap=$encap_rec
ebb439
+check_row_count Port_Binding 1 logical_port=lp22 encap=$encap_rec
ebb439
 
ebb439
 # Pre-populate the hypervisors' ARP tables so that we don't lose any
ebb439
 # packets for ARP resolution (native tunneling doesn't queue packets
ebb439
@@ -4149,7 +4129,7 @@ ovn-nbctl --wait=hv set logical_router lr0 options:always_learn_from_arp_request
ebb439
 
ebb439
 test_arp 11 $sha $spa $tpa
ebb439
 sleep 1
ebb439
-AT_CHECK([ovn-sbctl find mac_binding ip="192.168.1.100"], [0], [])
ebb439
+check_row_count MAC_Binding 0 ip="192.168.1.100"
ebb439
 
ebb439
 # When always_learn_from_arp_request=true, the new mac-binding will be learned.
ebb439
 ovn-nbctl --wait=hv set logical_router lr0 options:always_learn_from_arp_request=true
ebb439
@@ -4174,7 +4154,7 @@ ovn-nbctl --wait=hv set logical_router lr0 options:always_learn_from_arp_request
ebb439
 
ebb439
 sha=f00000000012
ebb439
 test_arp 12 $sha $spa $tpa
ebb439
-OVS_WAIT_UNTIL([ovn-sbctl find mac_binding ip="192.168.1.100" | grep f0:00:00:00:00:12])
ebb439
+wait_row_count MAC_Binding 1 ip="192.168.1.100" mac='"f0:00:00:00:00:12"'
ebb439
 ovn-nbctl --wait=hv sync
ebb439
 # give to the hv the time to send queued ip packets
ebb439
 sleep 1
ebb439
@@ -8131,18 +8111,18 @@ ovn-nbctl lsp-add ls0 lp0
ebb439
 ovn-nbctl lsp-add ls0 lp1
ebb439
 ovn-nbctl lsp-set-addresses lp0 "f0:00:00:00:00:01 192.168.0.1"
ebb439
 ovn-nbctl lsp-set-addresses lp1 "f0:00:00:00:00:02 192.168.0.2"
ebb439
-dp_uuid=`ovn-sbctl find datapath | grep uuid | cut -f2 -d ":" | cut -f2 -d " "`
ebb439
+dp_uuid=$(fetch_column Datapath_Binding _uuid)
ebb439
 ovn-sbctl create MAC_Binding ip=10.0.0.1 datapath=$dp_uuid logical_port=lp0 mac="mac1"
ebb439
 ovn-sbctl create MAC_Binding ip=10.0.0.1 datapath=$dp_uuid logical_port=lp1 mac="mac2"
ebb439
 ovn-sbctl find MAC_Binding
ebb439
 # Delete port lp0 and check that its MAC_Binding is deleted.
ebb439
 ovn-nbctl lsp-del lp0
ebb439
 ovn-sbctl find MAC_Binding
ebb439
-OVS_WAIT_UNTIL([test `ovn-sbctl find MAC_Binding logical_port=lp0 | wc -l` = 0])
ebb439
+wait_row_count MAC_Binding 0 logical_port=lp0
ebb439
 # Delete logical switch ls0 and check that its MAC_Binding is deleted.
ebb439
 ovn-nbctl ls-del ls0
ebb439
 ovn-sbctl find MAC_Binding
ebb439
-OVS_WAIT_UNTIL([test `ovn-sbctl find MAC_Binding | wc -l` = 0])
ebb439
+wait_row_count MAC_Binding 0
ebb439
 
ebb439
 OVN_CLEANUP([hv1])
ebb439
 
ebb439
@@ -8209,61 +8189,62 @@ AT_CHECK([ovn-nbctl lsp-add ls0 parent1])
ebb439
 AT_CHECK([ovn-nbctl lsp-add ls0 parent2])
ebb439
 AT_CHECK([ovn-nbctl ls-add ls1])
ebb439
 
ebb439
-dnl When a tag is provided, no allocation is done
ebb439
+AS_BOX([requested tag for parent1])
ebb439
 AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c0 parent1 3])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c0], [0], [3
ebb439
-])
ebb439
+c0_tag=$(ovn-nbctl lsp-get-tag c0)
ebb439
+echo c0_tag=$c0_tag
ebb439
+check test "$c0_tag" = 3
ebb439
 dnl The same 'tag' gets created in southbound database.
ebb439
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
ebb439
-logical_port="c0"], [0], [3
ebb439
-])
ebb439
+check_row_count Port_Binding 1 logical_port=c0 tag=$c0_tag
ebb439
 
ebb439
-dnl Allocate tags and see it getting created in both NB and SB
ebb439
+AS_BOX([tag allocation 1 for parent1])
ebb439
 AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c1 parent1 0])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c1], [0], [1
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
ebb439
-logical_port="c1"], [0], [1
ebb439
-])
ebb439
+c1_tag=$(ovn-nbctl lsp-get-tag c1)
ebb439
+echo c1_tag=$c1_tag
ebb439
+check test "$c1_tag" != "$c0_tag"
ebb439
+check_row_count Port_Binding 1 logical_port=c1 tag=$c1_tag
ebb439
 
ebb439
+AS_BOX([tag allocation 2 for parent1])
ebb439
 AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c2 parent1 0])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c2], [0], [2
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
ebb439
-logical_port="c2"], [0], [2
ebb439
-])
ebb439
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c3 parent1 0])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c3], [0], [4
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
ebb439
-logical_port="c3"], [0], [4
ebb439
-])
ebb439
+c2_tag=$(ovn-nbctl lsp-get-tag c2)
ebb439
+echo c2_tag=$c2_tag
ebb439
+check test "$c2_tag" != "$c0_tag"
ebb439
+check test "$c2_tag" != "$c1_tag"
ebb439
+check_row_count Port_Binding 1 logical_port=c2 tag=$c2_tag
ebb439
 
ebb439
-dnl A different parent.
ebb439
+AS_BOX([tag allocation 3 for parent1])
ebb439
+AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c3 parent1 0])
ebb439
+c3_tag=$(ovn-nbctl lsp-get-tag c3)
ebb439
+echo c3_tag=$c3_tag
ebb439
+check test "$c3_tag" != "$c0_tag"
ebb439
+check test "$c3_tag" != "$c1_tag"
ebb439
+check test "$c3_tag" != "$c2_tag"
ebb439
+check_row_count Port_Binding 1 logical_port=c3 tag=$c3_tag
ebb439
+
ebb439
+AS_BOX([tag allocation 1 for parent2])
ebb439
 AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c4 parent2 0])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c4], [0], [1
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
ebb439
-logical_port="c4"], [0], [1
ebb439
-])
ebb439
+c4_tag=$(ovn-nbctl lsp-get-tag c4)
ebb439
+echo c4_tag=$c4_tag
ebb439
+check_row_count Port_Binding 1 logical_port=c4 tag=$c4_tag
ebb439
 
ebb439
+AS_BOX([tag allocation 2 for parent2])
ebb439
 AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c5 parent2 0])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
ebb439
-logical_port="c5"], [0], [2
ebb439
-])
ebb439
+c5_tag=$(ovn-nbctl lsp-get-tag c5)
ebb439
+echo c5_tag=$c5_tag
ebb439
+check test "$c5_tag" != "$c4_tag"
ebb439
+check_row_count Port_Binding 1 logical_port=c5 tag=$c5_tag
ebb439
 
ebb439
-dnl Delete a logical port and create a new one.
ebb439
+AS_BOX([delete and add tag allocation for parent1])
ebb439
 AT_CHECK([ovn-nbctl --wait=sb lsp-del c1])
ebb439
 AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c6 parent1 0])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c6], [0], [1
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
ebb439
-logical_port="c6"], [0], [1
ebb439
-])
ebb439
-
ebb439
-dnl Restart northd to see that the same allocation remains.
ebb439
+c6_tag=$(ovn-nbctl lsp-get-tag c6)
ebb439
+echo c6_tag=$c6_tag
ebb439
+check_row_count Port_Binding 1 logical_port=c6 tag=$c6_tag
ebb439
+check test "$c6_tag" != "$c0_tag"
ebb439
+check test "$c6_tag" != "$c2_tag"
ebb439
+check test "$c6_tag" != "$c3_tag"
ebb439
+
ebb439
+AS_BOX([restart northd and make sure tag allocation is stable]) 
ebb439
 as northd
ebb439
 OVS_APP_EXIT_AND_WAIT([ovn-northd])
ebb439
 start_daemon ovn-northd \
ebb439
@@ -8272,30 +8253,30 @@ start_daemon ovn-northd \
ebb439
 
ebb439
 dnl Create a switch to make sure that ovn-northd has run through the main loop.
ebb439
 AT_CHECK([ovn-nbctl --wait=sb ls-add ls-dummy])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c0], [0], [3
ebb439
-])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c6], [0], [1
ebb439
-])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c2], [0], [2
ebb439
-])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c3], [0], [4
ebb439
-])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c4], [0], [1
ebb439
-])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2
ebb439
+
ebb439
+AT_CHECK_UNQUOTED([
ebb439
+    for lsp in c0 c2 c3 c4 c5 c6; do
ebb439
+        ovn-nbctl lsp-get-tag $lsp
ebb439
+    done], [0],
ebb439
+[$c0_tag
ebb439
+$c2_tag
ebb439
+$c3_tag
ebb439
+$c4_tag
ebb439
+$c5_tag
ebb439
+$c6_tag
ebb439
 ])
ebb439
 
ebb439
 dnl Create a switch port with a tag that has already been allocated.
ebb439
 dnl It should go through fine with a duplicate tag.
ebb439
-AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c7 parent2 2])
ebb439
+AS_BOX([request duplicate tag])
ebb439
+AT_CHECK([ovn-nbctl --wait=sb lsp-add ls1 c7 parent2 $c5_tag])
ebb439
 AT_CHECK([ovn-nbctl lsp-get-tag c7], [0], [2
ebb439
 ])
ebb439
-AT_CHECK([ovn-sbctl --data=bare --no-heading --columns=tag find port_binding \
ebb439
-logical_port="c7"], [0], [2
ebb439
-])
ebb439
-AT_CHECK([ovn-nbctl lsp-get-tag c5], [0], [2
ebb439
-])
ebb439
+check_row_count Port_Binding 1 logical_port=c7 tag=$c5_tag
ebb439
+check_row_count Port_Binding 1 logical_port=c5 tag=$c5_tag
ebb439
+check_row_count Port_Binding 2 parent_port=parent2 tag=$c5_tag
ebb439
 
ebb439
+AS_BOX([tag_request without parent_name])
ebb439
 AT_CHECK([ovn-nbctl ls-add ls2])
ebb439
 dnl When there is no parent_name provided (for say, 'localnet'), 'tag_request'
ebb439
 dnl gets copied to 'tag'
ebb439
@@ -8702,8 +8683,7 @@ check_tos 0
ebb439
 
ebb439
 # Mark DSCP with a valid value
ebb439
 qos_id=$(ovn-nbctl --wait=hv -- --id=@lp1-qos create QoS priority=100 action=dscp=48 match="inport\=\=\"lp1\"\ &&\ is_chassis_resident(\"lp1\")" direction="from-lport" -- set Logical_Switch lsw0 qos_rules=@lp1-qos)
ebb439
-AT_CHECK([as hv ovn-nbctl qos-list lsw0 | wc -l], [0], [1
ebb439
-])
ebb439
+as hv check_row_count nb:QoS 1
ebb439
 check_tos 48
ebb439
 
ebb439
 # check at hv without qos meter
ebb439
@@ -8737,8 +8717,7 @@ check_tos 63
ebb439
 
ebb439
 # Disable DSCP marking
ebb439
 ovn-nbctl --wait=hv qos-del lsw0
ebb439
-AT_CHECK([as hv ovn-nbctl qos-list lsw0 | wc -l], [0], [0
ebb439
-])
ebb439
+as hv check_row_count nb:QoS 0
ebb439
 check_tos 0
ebb439
 
ebb439
 # check at hv without qos meter
ebb439
@@ -8747,8 +8726,7 @@ AT_CHECK([as hv ovs-ofctl dump-flows br-int -O OpenFlow13 | grep meter | wc -l],
ebb439
 
ebb439
 # check meter with chassis not resident
ebb439
 ovn-nbctl qos-add lsw0 to-lport 1001 'inport=="lp3" && is_chassis_resident("lp3")' rate=11123 burst=111230
ebb439
-AT_CHECK([as hv ovn-nbctl qos-list lsw0 | wc -l], [0], [1
ebb439
-])
ebb439
+as hv check_row_count nb:QoS 1
ebb439
 
ebb439
 # check no meter table
ebb439
 AT_CHECK([as hv ovs-ofctl dump-flows br-int -O OpenFlow13 | grep meter | wc -l], [0], [0
ebb439
@@ -10217,12 +10195,8 @@ AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore])
ebb439
 # hv1 should be in 'ref_chassis' of the ha_chasssi_group as logical
ebb439
 # switch 'foo' can reach the router 'R1' (which has gw router port)
ebb439
 # via foo1 -> foo -> R0 -> join -> R1
ebb439
-hv1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv1"`
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$hv1_ch_uuid" = "$ref_ch_list"])
ebb439
+hv1_ch_uuid=$(fetch_column Chassis _uuid name=hv1)
ebb439
+wait_column "$hv1_ch_uuid" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Allow some time for ovn-northd and ovn-controller to catch up.
ebb439
 # XXX This should be more systematic.
ebb439
@@ -10635,13 +10609,11 @@ expected=${dst_mac}${src_mac}08004500001c000000003f110100${src_ip}${dst_ip}00351
ebb439
 echo $expected >> hv2-vif1.expected
ebb439
 OVN_CHECK_PACKETS([hv2/vif1-tx.pcap], [hv2-vif1.expected])
ebb439
 
ebb439
-AT_CHECK([ovn-sbctl --bare --columns _uuid find Port_Binding logical_port=cr-alice | wc -l], [0], [1
ebb439
-])
ebb439
+check_row_count Port_Binding 1 logical_port=cr-alice
ebb439
 
ebb439
 ovn-nbctl --timeout=3 --wait=sb remove Logical_Router_Port alice options redirect-chassis
ebb439
 
ebb439
-AT_CHECK([ovn-sbctl find Port_Binding logical_port=cr-alice | wc -l], [0], [0
ebb439
-])
ebb439
+check_row_count Port_Binding 0 logical_port=cr-alice
ebb439
 
ebb439
 OVN_CLEANUP([hv1],[hv2],[hv3])
ebb439
 
ebb439
@@ -11083,11 +11055,9 @@ as hv4 reset_pcap_file br-ex_n2 hv4/br-ex_n2
ebb439
 ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 hv4 40
ebb439
 
ebb439
 # Wait till cr-alice is claimed by hv4
ebb439
-hv4_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=hv4)
ebb439
+hv4_chassis=$(fetch_column Chassis _uuid name=hv4)
ebb439
 # check that the chassis redirect port has been claimed by the gw1 chassis
ebb439
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
ebb439
-logical_port=cr-alice | grep $hv4_chassis | wc -l], [0],[[1
ebb439
-]])
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-alice chassis=$hv4_chassis 
ebb439
 
ebb439
 # Reset the pcap file for hv2/br-ex_n2. From now on ovn-controller in hv2
ebb439
 # should not send GARPs for the router ports.
ebb439
@@ -11440,7 +11410,7 @@ packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111
ebb439
 # Send the first packet to trigger a ARP response and population of
ebb439
 # mac_bindings table.
ebb439
 as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
ebb439
-OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding ip="10.0.0.2" | wc -l` -gt 0])
ebb439
+wait_row_count MAC_Binding 1 ip="10.0.0.2"
ebb439
 ovn-nbctl --wait=hv sync
ebb439
 
ebb439
 # Packet to Expect at 'alice1'
ebb439
@@ -11671,31 +11641,13 @@ ovn-sbctl find Port_Binding type=chassisredirect
ebb439
 echo "-------------------------------------------"
ebb439
 
ebb439
 # There should be one ha_chassis_group with the name "outside"
ebb439
-ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
ebb439
-ha_chassis_group name="outside"`
ebb439
-
ebb439
-AT_CHECK([test $ha_chassi_grp_name = outside])
ebb439
+check_row_count HA_Chassis_Group 1 name=outside
ebb439
 
ebb439
 # There should be 2 ha_chassis rows in SB DB.
ebb439
-AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | awk '{print $3}' \
ebb439
-| grep '-' | wc -l ], [0], [2
ebb439
-])
ebb439
-
ebb439
-ha_ch=`ovn-sbctl --bare --columns ha_chassis  find ha_chassis_group`
ebb439
-# Trim the spaces.
ebb439
-ha_ch=`echo $ha_ch | sed 's/ //g'`
ebb439
+check_row_count HA_Chassis 2 'chassis!=[[]]'
ebb439
 
ebb439
-ha_ch_list=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
ebb439
-do
ebb439
-    ha_ch_list="$ha_ch_list $i"
ebb439
-done
ebb439
-
ebb439
-# Trim the spaces.
ebb439
-ha_ch_list=`echo $ha_ch_list | sed 's/ //g'`
ebb439
-
ebb439
-AT_CHECK([test "$ha_ch_list" = "$ha_ch"])
ebb439
+ha_ch=$(fetch_column HA_Chassis_Group ha_chassis)
ebb439
+check_column "$ha_ch" HA_Chassis _uuid 
ebb439
 
ebb439
 for chassis in gw1 gw2 hv1 hv2; do
ebb439
     as $chassis
ebb439
@@ -11738,8 +11690,8 @@ as hv1 ovs-ofctl dump-flows br-int table=32
ebb439
 echo "--- hv2 ---"
ebb439
 as hv2 ovs-ofctl dump-flows br-int table=32
ebb439
 
ebb439
-gw1_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw1)
ebb439
-gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
ebb439
+gw1_chassis=$(fetch_column Chassis _uuid name=gw1)
ebb439
+gw2_chassis=$(fetch_column Chassis _uuid name=gw2)
ebb439
 
ebb439
 OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
ebb439
 grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport \
ebb439
@@ -11767,29 +11719,12 @@ OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=9 | grep arp_tpa=192.16
ebb439
 ]])
ebb439
 
ebb439
 # check that the chassis redirect port has been claimed by the gw1 chassis
ebb439
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
ebb439
-logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
ebb439
-]])
ebb439
-
ebb439
-hv1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv1"`
ebb439
-hv2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv2"`
ebb439
-
ebb439
-exp_ref_ch_list=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list chassis | sort`
ebb439
-do
ebb439
-    if test $i = $hv1_ch_uuid; then
ebb439
-        exp_ref_ch_list="${exp_ref_ch_list}$i"
ebb439
-    elif test $i = $hv2_ch_uuid; then
ebb439
-        exp_ref_ch_list="${exp_ref_ch_list}$i"
ebb439
-    fi
ebb439
-done
ebb439
-
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$exp_ref_ch_list" = "$ref_ch_list"])
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-outside chassis=$gw1_chassis
ebb439
 
ebb439
+hv1_ch_uuid=$(fetch_column Chassis _uuid name=hv1)
ebb439
+hv2_ch_uuid=$(fetch_column Chassis _uuid name=hv2)
ebb439
+exp_ref_ch_list="$hv1_ch_uuid $hv2_ch_uuid"
ebb439
+wait_column "$exp_ref_ch_list" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # at this point, we invert the priority of the gw chassis between gw1 and gw2
ebb439
 
ebb439
@@ -11815,9 +11750,7 @@ grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport \
ebb439
 ])
ebb439
 
ebb439
 # check that the chassis redirect port has been reclaimed by the gw2 chassis
ebb439
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
ebb439
-logical_port=cr-outside | grep $gw2_chassis | wc -l], [0],[[1
ebb439
-]])
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-outside chassis=$gw2_chassis
ebb439
 
ebb439
 # check BFD enablement on tunnel ports from gw1 #########
ebb439
 as gw1
ebb439
@@ -11889,9 +11822,7 @@ grep 00:00:02:01:02:04 | wc -l], [0], [[0
ebb439
 ]])
ebb439
 
ebb439
 # check that the chassis redirect port has been reclaimed by the gw1 chassis
ebb439
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
ebb439
-logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
ebb439
-]])
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-outside chassis=$gw1_chassis
ebb439
 
ebb439
 ovn-nbctl --wait=hv set NB_Global . options:"bfd-min-rx"=2000
ebb439
 as gw2
ebb439
@@ -11924,11 +11855,7 @@ done
ebb439
 # reference to hv1.
ebb439
 as hv1 ovs-vsctl del-port hv1-vif1
ebb439
 
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$hv2_ch_uuid" = "$ref_ch_list"])
ebb439
+wait_column "$hv2_ch_uuid" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Delete the inside2 vif.
ebb439
 ovn-sbctl show
ebb439
@@ -11937,19 +11864,14 @@ echo "Deleting hv2-vif1"
ebb439
 as hv2 ovs-vsctl del-port hv2-vif1
ebb439
 
ebb439
 # ref_chassis of ha_chassis_group should be empty
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     exp_ref_ch_list=""
ebb439
-     test "$exp_ref_ch_list" = "$ref_ch_list"])
ebb439
+wait_column '' HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Delete the Gateway_Chassis for lrp - outside
ebb439
 ovn-nbctl clear Logical_Router_Port outside gateway_chassis
ebb439
 
ebb439
 # There shoud be no ha_chassis_group rows in SB DB.
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group | wc -l`])
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 0
ebb439
+wait_row_count HA_Chassis 0
ebb439
 
ebb439
 ovn-nbctl remove NB_Global . options "bfd-min-rx"
ebb439
 ovn-nbctl remove NB_Global . options "bfd-min-tx"
ebb439
@@ -11967,16 +11889,13 @@ ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 gw2 20
ebb439
 # ovn-northd should not create HA chassis group and HA chassis rows
ebb439
 # unless the HA chassis group in OVN NB DB is associated to
ebb439
 # a logical router port.
ebb439
-OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
ebb439
+wait_row_count HA_Chassis 0
ebb439
 
ebb439
 # Associate hagrp1 to outside logical router port
ebb439
 ovn-nbctl set Logical_Router_Port outside ha_chassis_group=$hagrp1_uuid
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid \
ebb439
-find ha_chassis_group | wc -l`])
ebb439
-
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | \
ebb439
-grep -v chassis-name | wc -l`])
ebb439
+wait_row_count HA_Chassis_Group 1
ebb439
+wait_row_count HA_Chassis 2
ebb439
 
ebb439
 OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
ebb439
 grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport \
ebb439
@@ -12018,24 +11937,10 @@ for i in 1 2; do
ebb439
         ofport-request=1
ebb439
 done
ebb439
 
ebb439
-hv1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv1"`
ebb439
-hv2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv2"`
ebb439
-
ebb439
-exp_ref_ch_list=''
ebb439
-for i in `ovn-sbctl --bare --columns _uuid list chassis | sort`
ebb439
-do
ebb439
-    if test $i = $hv1_ch_uuid; then
ebb439
-        exp_ref_ch_list="${exp_ref_ch_list}$i"
ebb439
-    elif test $i = $hv2_ch_uuid; then
ebb439
-        exp_ref_ch_list="${exp_ref_ch_list}$i"
ebb439
-    fi
ebb439
-done
ebb439
-
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
ebb439
-     # Trim the spaces.
ebb439
-     ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
ebb439
-     test "$exp_ref_ch_list" = "$ref_ch_list"])
ebb439
+hv1_ch_uuid=$(fetch_column Chassis _uuid name=hv1)
ebb439
+hv2_ch_uuid=$(fetch_column Chassis _uuid name=hv2)
ebb439
+exp_ref_ch_list="$hv1_ch_uuid $hv2_ch_uuid"
ebb439
+wait_column "$exp_ref_ch_list" HA_Chassis_Group ref_chassis
ebb439
 
ebb439
 # Increase the priority of gw2
ebb439
 ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 gw2 40
ebb439
@@ -12123,9 +12028,7 @@ grep 00:00:02:01:02:04 | wc -l], [0], [[0
ebb439
 ]])
ebb439
 
ebb439
 # check that the chassis redirect port has been reclaimed by the gw1 chassis
ebb439
-OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
ebb439
-logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
ebb439
-]])
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-outside chassis=$gw1_chassis
ebb439
 
ebb439
 OVN_CLEANUP([gw1],[gw2],[hv1],[hv2])
ebb439
 
ebb439
@@ -12392,22 +12295,14 @@ gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
ebb439
 ovn-sbctl destroy Chassis $gw2_chassis
ebb439
 
ebb439
 # Wait for the gw2_chassis row is recreated.
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns=_uuid find Chassis name=gw2 | wc -l`])
ebb439
-
ebb439
-gw1_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw1)
ebb439
-gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
ebb439
+wait_row_count Chassis 1 name=gw2
ebb439
 
ebb439
 # When gw2 chassis row is destroyed, it gets recreated. There
ebb439
 # is a small window in which gw2 may claim the cr-outside port if
ebb439
 # it has not established bfd tunnel with gw1.
ebb439
 # So make sure that, cr-outside is claimed by gw1 finally.
ebb439
-OVS_WAIT_WHILE(
ebb439
-    [cr_outside_ch=`ovn-sbctl --bare --columns=chassis find Port_binding logical_port=cr-outside`
ebb439
-     test $cr_outside_ch = $gw2_chassis])
ebb439
-
ebb439
-OVS_WAIT_UNTIL(
ebb439
-    [cr_outside_ch=`ovn-sbctl --bare --columns=chassis find Port_binding logical_port=cr-outside`
ebb439
-     test $cr_outside_ch = $gw1_chassis])
ebb439
+gw1_chassis=$(fetch_column Chassis _uuid name=gw1)
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-outside chassis=$gw1_chassis
ebb439
 
ebb439
 OVN_CLEANUP([gw1],[gw2],[hv1])
ebb439
 
ebb439
@@ -12520,11 +12415,9 @@ AT_CHECK([ovn-sbctl dump-flows lr0_ip6 | grep nd_na_router | \
ebb439
 wc -l], [0], [4
ebb439
 ])
ebb439
 
ebb439
-cr_uuid=`ovn-sbctl find port_binding logical_port=cr-ip6_public | grep _uuid | cut -f2 -d ":"`
ebb439
-
ebb439
 # Get the redirect chassis uuid.
ebb439
-chassis_uuid=`ovn-sbctl list chassis hv1 | grep _uuid | cut -f2 -d ":"`
ebb439
-OVS_WAIT_UNTIL([test $chassis_uuid = `ovn-sbctl get port_binding $cr_uuid chassis`])
ebb439
+chassis_uuid=$(fetch_column Chassis _uuid name=hv1)
ebb439
+wait_row_count Port_Binding 1 logical_port=cr-ip6_public chassis=$chassis_uuid
ebb439
 
ebb439
 trim_zeros() {
ebb439
     sed 's/\(00\)\{1,\}$//'
ebb439
@@ -12672,10 +12565,10 @@ ovn-nbctl lsp-set-options lsp0 requested-chassis=hv1
ebb439
 ovn-nbctl --wait=hv --timeout=3 sync
ebb439
 
ebb439
 # Retrieve hv1 and hv2 chassis UUIDs from southbound database
ebb439
-ovn-sbctl wait-until chassis hv1
ebb439
-ovn-sbctl wait-until chassis hv2
ebb439
-hv1_uuid=$(ovn-sbctl --bare --columns _uuid find chassis name=hv1)
ebb439
-hv2_uuid=$(ovn-sbctl --bare --columns _uuid find chassis name=hv2)
ebb439
+wait_row_count Chassis 1 name=hv1
ebb439
+wait_row_count Chassis 1 name=hv2
ebb439
+hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
ebb439
+hv2_uuid=$(fetch_column Chassis _uuid name=hv2)
ebb439
 
ebb439
 # (1) Chassis hv2 should not bind lsp0 when requested-chassis is hv1.
ebb439
 echo "verifying that hv2 does not bind lsp0 when hv2 physical/logical mapping is added"
ebb439
@@ -12683,7 +12576,7 @@ as hv2
ebb439
 ovs-vsctl set interface hv2-vif0 external-ids:iface-id=lsp0
ebb439
 
ebb439
 OVS_WAIT_UNTIL([test 1 = $(grep -c "Not claiming lport lsp0" hv2/ovn-controller.log)])
ebb439
-AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x], [0], [])
ebb439
+wait_row_count Port_Binding 1 logical_port=lsp0 'chassis=[[]]'
ebb439
 
ebb439
 # (2) Chassis hv2 should not add flows in OFTABLE_PHY_TO_LOG and OFTABLE_LOG_TO_PHY tables.
ebb439
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1], [])
ebb439
@@ -12695,7 +12588,7 @@ as hv1
ebb439
 ovs-vsctl set interface hv1-vif0 external-ids:iface-id=lsp0
ebb439
 
ebb439
 OVS_WAIT_UNTIL([test 1 = $(grep -c "Claiming lport lsp0" hv1/ovn-controller.log)])
ebb439
-AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x"$hv1_uuid"], [0], [])
ebb439
+check_column "$hv1_uuid" Port_Binding chassis logical_port=lsp0
ebb439
 
ebb439
 # (4) Chassis hv1 should add flows in OFTABLE_PHY_TO_LOG and OFTABLE_LOG_TO_PHY tables.
ebb439
 as hv1 ovs-ofctl dump-flows br-int
ebb439
@@ -12708,7 +12601,7 @@ echo "verifying that lsp0 binding moves when requested-chassis is changed"
ebb439
 
ebb439
 ovn-nbctl lsp-set-options lsp0 requested-chassis=hv2
ebb439
 OVS_WAIT_UNTIL([test 1 = $(grep -c "Releasing lport lsp0 from this chassis" hv1/ovn-controller.log)])
ebb439
-OVS_WAIT_UNTIL([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x"$hv2_uuid"])
ebb439
+wait_column "$hv2_uuid" Port_Binding chassis logical_port=lsp0
ebb439
 
ebb439
 # (6) Chassis hv2 should add flows and hv1 should not.
ebb439
 AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ignore])
ebb439
@@ -12735,23 +12628,23 @@ ovs-vsctl add-br br-phys
ebb439
 ovn_attach n1 br-phys 192.168.0.11
ebb439
 ovs-vsctl -- add-port br-int hv1-vif0 -- set Interface hv1-vif0 ofport-request=1
ebb439
 
ebb439
-ovn-sbctl wait-until chassis hv1
ebb439
-hv1_hostname=$(ovn-sbctl --bare --columns hostname find Chassis name=hv1)
ebb439
+wait_row_count Chassis 1 name=hv1
ebb439
+hv1_hostname=$(fetch Chassis hostname name=hv1)
ebb439
 echo "hv1_hostname=${hv1_hostname}"
ebb439
 ovn-nbctl --wait=hv --timeout=3 lsp-set-options lsp0 requested-chassis=${hv1_hostname}
ebb439
 as hv1 ovs-vsctl set interface hv1-vif0 external-ids:iface-id=lsp0
ebb439
 
ebb439
-hv1_uuid=$(ovn-sbctl --bare --columns _uuid find Chassis name=hv1)
ebb439
+hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
ebb439
 echo "hv1_uuid=${hv1_uuid}"
ebb439
 OVS_WAIT_UNTIL([test 1 = $(grep -c "Claiming lport lsp0" hv1/ovn-controller.log)])
ebb439
-AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x"$hv1_uuid"], [0], [])
ebb439
+wait_column "$hv1_uuid" Port_Binding chassis logical_port=lsp0
ebb439
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [0], [ignore])
ebb439
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep actions=output:1], [0], [ignore])
ebb439
 
ebb439
 ovn-nbctl --wait=hv --timeout=3 lsp-set-options lsp0 requested-chassis=non-existant-chassis
ebb439
 OVS_WAIT_UNTIL([test 1 = $(grep -c "Releasing lport lsp0 from this chassis" hv1/ovn-controller.log)])
ebb439
 ovn-nbctl --wait=hv --timeout=3 sync
ebb439
-AT_CHECK([test x$(ovn-sbctl --bare --columns chassis find port_binding logical_port=lsp0) = x], [0], [])
ebb439
+wait_column '' Port_Binding chasssi logical_port=lsp0
ebb439
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1], [])
ebb439
 AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep output], [1], [])
ebb439
 
ebb439
@@ -13589,29 +13482,17 @@ ovn-nbctl --id=@p get Logical_Switch_Port lp3 -- add Port_Group pg2 ports @p
ebb439
 ovn-nbctl --wait=sb sync
ebb439
 
ebb439
 dnl Check if port group address sets were populated with ports' addresses
ebb439
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
ebb439
-         [0], [[["10.0.0.1", "10.0.0.2"]]
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl get Address_Set pg2_ip4 addresses],
ebb439
-         [0], [[["10.0.0.2", "10.0.0.3"]]
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
ebb439
-         [0], [[["2001:db8::1", "2001:db8::2"]]
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl get Address_Set pg2_ip6 addresses],
ebb439
-         [0], [[["2001:db8::2", "2001:db8::3"]]
ebb439
-])
ebb439
+check_column '10.0.0.1 10.0.0.2' Address_Set addresses name=pg1_ip4
ebb439
+check_column '10.0.0.2 10.0.0.3' Address_Set addresses name=pg2_ip4
ebb439
+check_column '2001:db8::1 2001:db8::2' Address_Set addresses name=pg1_ip6
ebb439
+check_column '2001:db8::2 2001:db8::3' Address_Set addresses name=pg2_ip6
ebb439
 
ebb439
 ovn-nbctl --wait=sb lsp-set-addresses lp1 \
ebb439
     "02:00:00:00:00:01 10.0.0.11 2001:db8::11"
ebb439
 
ebb439
 dnl Check if updated address got propagated to the port group address sets
ebb439
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip4 addresses],
ebb439
-         [0], [[["10.0.0.11", "10.0.0.2"]]
ebb439
-])
ebb439
-AT_CHECK([ovn-sbctl get Address_Set pg1_ip6 addresses],
ebb439
-         [0], [[["2001:db8::11", "2001:db8::2"]]
ebb439
-])
ebb439
+check_column '10.0.0.11 10.0.0.2' Address_Set addresses name=pg1_ip4
ebb439
+check_column '2001:db8::11 2001:db8::2' Address_Set addresses name=pg1_ip6
ebb439
 
ebb439
 AT_CLEANUP
ebb439
 
ebb439
@@ -16098,8 +15979,8 @@ ovn-nbctl lr-nat-add lr0 dnat_and_snat 172.24.4.100 10.0.0.10
ebb439
 ovn-nbctl lr-nat-add lr1 dnat_and_snat 172.24.4.200 20.0.0.10
ebb439
 
ebb439
 # Check that the MAC_Binding entries have been properly created
ebb439
-OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding logical_port="lr0-pub" ip="172.24.4.200" | wc -l` -gt 0])
ebb439
-OVS_WAIT_UNTIL([test `ovn-sbctl find mac_binding logical_port="lr1-pub" ip="172.24.4.100" | wc -l` -gt 0])
ebb439
+wait_row_count MAC_Binding 1 logical_port=lr0-pub ip=172.24.4.200
ebb439
+wait_row_count MAC_Binding 1 logical_port=lr1-pub ip=172.24.4.100
ebb439
 
ebb439
 # Check that the GARPs went also to the external physical network
ebb439
 # Wait until at least 4 packets have arrived and copy them to a separate file as
ebb439
@@ -17307,10 +17188,7 @@ send_igmp_v3_report hv1-vif1 hv1 \
ebb439
     /dev/null
ebb439
 
ebb439
 # Check IGMP_Group table on both HV.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c`
ebb439
-    test "${total_entries}" = "1"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 1 address=239.0.1.68
ebb439
 
ebb439
 # Send traffic and make sure it gets forwarded only on the port that joined.
ebb439
 as hv1 reset_pcap_file hv1-vif1 hv1/vif1
ebb439
@@ -17335,10 +17213,7 @@ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
ebb439
 
ebb439
 # Flush IGMP groups.
ebb439
 ovn-sbctl ip-multicast-flush sw1
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c`
ebb439
-    test "${total_entries}" = "0"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 0 address=239.0.1.68
ebb439
 
ebb439
 # Check that traffic for 224.0.0.X is flooded even if some hosts register for
ebb439
 # it.
ebb439
@@ -17349,10 +17224,7 @@ send_igmp_v3_report hv1-vif1 hv1 \
ebb439
     /dev/null
ebb439
 
ebb439
 # Check that the IGMP Group is learned.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "224.0.0.42" -c`
ebb439
-    test "${total_entries}" = "1"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 1 address=224.0.0.42
ebb439
 
ebb439
 # Send traffic and make sure it gets flooded to all ports.
ebb439
 as hv1 reset_pcap_file hv1-vif1 hv1/vif1
ebb439
@@ -17443,10 +17315,7 @@ send_igmp_v3_report hv2-vif3 hv2 \
ebb439
     /dev/null
ebb439
 
ebb439
 # Check that the IGMP Group is learned by all switches.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "239.0.1.68" -c`
ebb439
-    test "${total_entries}" = "2"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 2 address=239.0.1.68
ebb439
 
ebb439
 # Send traffic from sw3 and make sure it is relayed by rtr.
ebb439
 # to ports that joined.
ebb439
@@ -17933,10 +17802,7 @@ send_mld_v2_report hv2-vif1 hv2 \
ebb439
     /dev/null
ebb439
 
ebb439
 # Check that the IP multicast group is learned on both hv.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "ff0a:dead:beef::1" -c`
ebb439
-    test "${total_entries}" = "2"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 2 address='"ff0a:dead:beef::1"'
ebb439
 
ebb439
 # Send traffic and make sure it gets forwarded only on the two ports that
ebb439
 # joined.
ebb439
@@ -17969,10 +17835,7 @@ send_mld_v2_report hv1-vif1 hv1 \
ebb439
     /dev/null
ebb439
 
ebb439
 # Check IGMP_Group table on both HV.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "ff0a:dead:beef::1" -c`
ebb439
-    test "${total_entries}" = "1"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 1 address='"ff0a:dead:beef::1"'
ebb439
 
ebb439
 # Send traffic and make sure it gets forwarded only on the port that joined.
ebb439
 as hv1 reset_pcap_file hv1-vif1 hv1/vif1
ebb439
@@ -18001,10 +17864,7 @@ OVN_CHECK_PACKETS([hv2/vif3-tx.pcap], [expected_empty])
ebb439
 
ebb439
 # Flush IP multicast groups.
ebb439
 ovn-sbctl ip-multicast-flush sw1
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep " ff0a:dead:beef::1" -c`
ebb439
-    test "${total_entries}" = "0"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 0 address='"ff0a:dead:beef::1"'
ebb439
 
ebb439
 # Check that traffic for "all-hosts" is flooded even if some hosts register
ebb439
 # for it.
ebb439
@@ -18015,10 +17875,7 @@ send_mld_v2_report hv1-vif1 hv1 \
ebb439
     /dev/null
ebb439
 
ebb439
 # Check that the Multicast Group is learned.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "ff02::1" -c`
ebb439
-    test "${total_entries}" = "1"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 1 address='"ff02::1"'
ebb439
 
ebb439
 # Send traffic and make sure it gets flooded to all ports.
ebb439
 as hv1 reset_pcap_file hv1-vif1 hv1/vif1
ebb439
@@ -18115,10 +17972,7 @@ send_mld_v2_report hv2-vif3 hv2 \
ebb439
     /dev/null
ebb439
 
ebb439
 # Check that the IGMP Group is learned by all switches.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "ff0a:dead:beef::1" -c`
ebb439
-    test "${total_entries}" = "2"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 2 address='"ff0a:dead:beef::1"'
ebb439
 
ebb439
 # Send traffic from sw3 and make sure it is relayed by rtr.
ebb439
 # to ports that joined.
ebb439
@@ -18169,10 +18023,7 @@ send_mld_v2_report hv1-vif4 hv1 \
ebb439
     /dev/null
ebb439
 
ebb439
 # Check that the Multicast Group is learned by all switches.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "ff0a:dead:beef::1" -c`
ebb439
-    test "${total_entries}" = "3"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 3 address='"ff0a:dead:beef::1"'
ebb439
 
ebb439
 # Send traffic from sw3 and make sure it is relayed by rtr
ebb439
 # to ports that joined.
ebb439
@@ -18281,10 +18132,7 @@ send_mld_v2_report hv1-vif2 hv1 \
ebb439
     expected_reports
ebb439
 
ebb439
 # Check that the IP multicast group is learned.
ebb439
-OVS_WAIT_UNTIL([
ebb439
-    total_entries=`ovn-sbctl find IGMP_Group | grep "ff0a:dead:beef::1" -c`
ebb439
-    test "${total_entries}" = "1"
ebb439
-])
ebb439
+wait_row_count IGMP_Group 1 address='"ff0a:dead:beef::1"'
ebb439
 
ebb439
 # Send traffic from sw1-p21
ebb439
 send_ip_multicast_pkt hv2-vif1 hv2 \
ebb439
@@ -19005,14 +18853,14 @@ ip_to_hex() {
ebb439
 #     ip - 10.0.0.30
ebb439
 #     mac - 50:54:00:00:00:03
ebb439
 
ebb439
-AT_CHECK([test 0 = `ovn-sbctl list mac_binding | wc -l`])
ebb439
+check_row_count MAC_Binding 0
ebb439
 eth_src=505400000003
ebb439
 eth_dst=ffffffffffff
ebb439
 spa=$(ip_to_hex 10 0 0 30)
ebb439
 tpa=$(ip_to_hex 10 0 0 30)
ebb439
 send_garp 1 1 $eth_src $eth_dst $spa $tpa
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid list mac_binding | wc -l`])
ebb439
+wait_row_count MAC_Binding 1
ebb439
 
ebb439
 AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
ebb439
 list mac_binding], [0], [lr0-sw0
ebb439
@@ -19052,7 +18900,7 @@ send_garp 1 1 $eth_src $eth_dst $spa $tpa
ebb439
 # should be updated.
ebb439
 OVS_WAIT_UNTIL([test 2 = `cat hv1/ovn-controller.log | grep NXT_PACKET_IN2 | wc -l`])
ebb439
 
ebb439
-AT_CHECK([test 1 = `ovn-sbctl --bare --columns _uuid list mac_binding | wc -l`])
ebb439
+check_row_count MAC_Binding 1
ebb439
 
ebb439
 AT_CHECK([ovn-sbctl --format=csv --bare --columns logical_port,ip,mac \
ebb439
 list mac_binding], [0], [lr0-sw0
ebb439
@@ -19634,8 +19482,7 @@ ovn-nbctl --wait=hv lrp-set-gateway-chassis lr0-public hv1 20
ebb439
 OVN_POPULATE_ARP
ebb439
 ovn-nbctl --wait=hv sync
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l`])
ebb439
+wait_row_count Service_Monitor 2
ebb439
 
ebb439
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
ebb439
 AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
@@ -19661,8 +19508,7 @@ OVS_WAIT_UNTIL(
ebb439
 grep "405400000003${svc_mon_src_mac}" | wc -l`]
ebb439
 )
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 2 = `ovn-sbctl --bare --columns status find \
ebb439
-service_monitor | grep offline | wc -l`])
ebb439
+wait_row_count Service_Monitor 2 status=offline
ebb439
 
ebb439
 OVS_WAIT_UNTIL(
ebb439
     [test 2 = `$PYTHON "$ovs_srcdir/utilities/ovs-pcap.in" hv1/vif1-tx.pcap | \
ebb439
@@ -19689,33 +19535,26 @@ AT_CHECK([cat lflows.txt], [0], [dnl
ebb439
 # Delete sw0-p1
ebb439
 ovn-nbctl lsp-del sw0-p1
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l)])
ebb439
+wait_row_count Service_Monitor 1
ebb439
 
ebb439
 # Add back sw0-p1 but without any IP address.
ebb439
 ovn-nbctl lsp-add sw0 sw0-p1
ebb439
 ovn-nbctl lsp-set-addresses sw0-p1 "50:54:00:00:00:03" -- \
ebb439
 lsp-set-port-security sw0-p1 "50:54:00:00:00:03"
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 2 = $(ovn-sbctl --bare --columns status find \
ebb439
-service_monitor | grep offline | wc -l)])
ebb439
+wait_row_count Service_Monitor 2 status=offline
ebb439
 
ebb439
 ovn-nbctl lsp-del sw0-p1
ebb439
 ovn-nbctl lsp-del sw1-p1
ebb439
-OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l)])
ebb439
+wait_row_count Service_Monitor 0
ebb439
 
ebb439
 # Add back sw0-p1 but without any address set.
ebb439
 ovn-nbctl lsp-add sw0 sw0-p1
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 1 = $(ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l)])
ebb439
 
ebb439
-OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns status find \
ebb439
-service_monitor | grep offline | wc -l)])
ebb439
-
ebb439
-OVS_WAIT_UNTIL([test 0 = $(ovn-sbctl --bare --columns status find \
ebb439
-service_monitor | grep online | wc -l)])
ebb439
+wait_row_count Service_Monitor 1
ebb439
+wait_row_count Service_Monitor 0 status=offline
ebb439
+wait_row_count Service_Monitor 0 status=online
ebb439
 
ebb439
 OVN_CLEANUP([hv1], [hv2])
ebb439
 AT_CLEANUP
ebb439
@@ -19826,9 +19665,7 @@ ovn-nbctl --wait=hv sync
ebb439
 
ebb439
 # And now for the anticlimax. We need to ensure that there is no
ebb439
 # service monitor in the southbound db.
ebb439
-
ebb439
-AT_CHECK([test 0 = `ovn-sbctl --bare --columns _uuid find \
ebb439
-service_monitor | sed '/^$/d' | wc -l`])
ebb439
+check_row_count Service_Monitor 0
ebb439
 
ebb439
 # Let's also be sure the warning message about SCTP load balancers is
ebb439
 # is in the ovn-northd log
ebb439
@@ -22107,9 +21944,9 @@ as hv1 ovs-vsctl \
ebb439
     -- set Interface vif1 external_ids:iface-id=lsp
ebb439
 
ebb439
 # Wait for port to be bound.
ebb439
-OVS_WAIT_UNTIL([test $(ovn-sbctl --columns _uuid --bare list chassis hv1 | wc -l) -eq 1])
ebb439
-ch=$(ovn-sbctl --columns _uuid --bare list chassis hv1)
ebb439
-OVS_WAIT_UNTIL([test $(ovn-sbctl --columns chassis --bare list port_binding lsp | grep $ch -c) -eq 1])
ebb439
+wait_row_count Chassis 1 name=hv1
ebb439
+ch=$(fetch_column Chassis _uuid name=hv1)
ebb439
+wait_row_count Port_Binding 1 logical_port=lsp chassis=$ch
ebb439
 
ebb439
 # Pause ovn-controller.
ebb439
 as hv1 ovn-appctl -t ovn-controller debug/pause
ebb439
@@ -22821,9 +22658,9 @@ as hv1 ovs-vsctl \
ebb439
     -- set Interface vif1 external_ids:iface-id=sw0-p1
ebb439
 
ebb439
 # Wait for port to be bound.
ebb439
-OVS_WAIT_UNTIL([test $(ovn-sbctl --columns _uuid --bare list chassis hv1 | wc -l) -eq 1])
ebb439
-ch=$(ovn-sbctl --columns _uuid --bare list chassis hv1)
ebb439
-OVS_WAIT_UNTIL([test $(ovn-sbctl --columns chassis --bare list port_binding sw0-p1 | grep $ch -c) -eq 1])
ebb439
+wait_row_count Chassis 1 name=hv1
ebb439
+ch=$(fetch_column Chassis _uuid name=hv1)
ebb439
+wait_row_count Port_Binding 1 logical_port=sw0-p1 chassis=$ch
ebb439
 
ebb439
 as hv1 ovs-vsctl add-br br-temp
ebb439
 as hv1 ovs-vsctl \
ebb439
@@ -22833,13 +22670,13 @@ as hv1 ovs-vsctl \
ebb439
 ovn-nbctl --wait=hv sync
ebb439
 
ebb439
 # hv1 ovn-controller should not bind sw0-p2.
ebb439
-AT_CHECK([test $(ovn-sbctl --columns chassis --bare list port_binding sw0-p2 | grep $ch -c) -eq 0])
ebb439
+check_row_count Port_Binding 0 logical_port=sw0-p2 chassis=$c
ebb439
 
ebb439
 # Trigger recompute and sw0-p2 should not be claimed.
ebb439
 as hv1 ovn-appctl -t ovn-controller recompute
ebb439
 ovn-nbctl --wait=hv sync
ebb439
 
ebb439
-AT_CHECK([test $(ovn-sbctl --columns chassis --bare list port_binding sw0-p2 | grep $ch -c) -eq 0])
ebb439
+check_row_count Port_Binding 0 logical_port=sw0-p2 chassis=$ch
ebb439
 
ebb439
 ovn-sbctl --columns chassis --bare list port_binding sw0-p2
ebb439
 
ebb439
diff --git a/tests/ovs-macros.at b/tests/ovs-macros.at
ebb439
index 3dcf8f96d..856f5d2d7 100644
ebb439
--- a/tests/ovs-macros.at
ebb439
+++ b/tests/ovs-macros.at
ebb439
@@ -23,9 +23,11 @@ dnl that can ordinarily be run only within AT_SETUP...AT_CLEANUP.
ebb439
 m4_define([OVS_START_SHELL_HELPERS],
ebb439
   [m4_ifdef([AT_ingroup], [m4_fatal([$0: AT_SETUP and OVS_DEFINE_SHELL_HELPERS may not nest])])
ebb439
    m4_define([AT_ingroup])
ebb439
+   m4_define([AT_capture_files])
ebb439
    m4_divert_push([PREPARE_TESTS])])
ebb439
 m4_define([OVS_END_SHELL_HELPERS], [
ebb439
    m4_divert_pop([PREPARE_TESTS])
ebb439
+   m4_undefine([AT_capture_files])
ebb439
    m4_undefine([AT_ingroup])])
ebb439
 
ebb439
 m4_divert_push([PREPARE_TESTS])
ebb439
@@ -231,21 +233,52 @@ ovs_wait_failed () {
ebb439
 ovs_wait "AS_ESCAPE([$3])" "AS_ESCAPE([$4])"
ebb439
 ])
ebb439
 
ebb439
-dnl OVS_WAIT_UNTIL(COMMAND)
ebb439
+dnl OVS_WAIT_UNTIL(COMMAND[, IF-FAILED])
ebb439
 dnl
ebb439
 dnl Executes shell COMMAND in a loop until it returns
ebb439
 dnl zero return code.  If COMMAND did not return
ebb439
 dnl zero code within reasonable time limit, then
ebb439
-dnl the test fails.
ebb439
+dnl the test fails.  In that case, runs IF-FAILED
ebb439
+dnl before aborting.
ebb439
 m4_define([OVS_WAIT_UNTIL],
ebb439
   [OVS_WAIT([$1], [$2], [AT_LINE], [until $1])])
ebb439
 
ebb439
-dnl OVS_WAIT_WHILE(COMMAND)
ebb439
+dnl OVS_WAIT_FOR_OUTPUT(COMMAND, EXIT-STATUS, STDOUT, STDERR)
ebb439
+dnl
ebb439
+dnl Executes shell COMMAND in a loop until it exits with status EXIT-STATUS,
ebb439
+dnl prints STDOUT on stdout, and prints STDERR on stderr.  If this doesn't
ebb439
+dnl happen within a reasonable time limit, then the test fails.
ebb439
+m4_define([OVS_WAIT_FOR_OUTPUT], [dnl
ebb439
+wait_expected_status=m4_if([$2], [], [0], [$2])
ebb439
+AT_DATA([wait-expected-stdout], [$3])
ebb439
+AT_DATA([wait-expected-stderr], [$4])
ebb439
+ovs_wait_command() {
ebb439
+    $1
ebb439
+}
ebb439
+ovs_wait_cond() {
ebb439
+    ovs_wait_command >wait-stdout 2>wait-stderr
ebb439
+    wait_status=$?
ebb439
+    (test $wait_status = $wait_expected_status &&
ebb439
+     $at_diff wait-expected-stdout wait-stdout &&
ebb439
+     $at_diff wait-expected-stderr wait-stderr) >/dev/null 2>&1
ebb439
+}
ebb439
+ovs_wait_failed () {
ebb439
+    if test $wait_status != $wait_expected_status; then
ebb439
+        echo "exit status $wait_status != expected $wait_expected_status"
ebb439
+    fi
ebb439
+    $at_diff wait-expected-stdout wait-stdout
ebb439
+    $at_diff wait-expected-stderr wait-stderr
ebb439
+}
ebb439
+ovs_wait "AS_ESCAPE([AT_LINE])" "for output from AS_ESCAPE([$1])"
ebb439
+])
ebb439
+    
ebb439
+dnl OVS_WAIT_WHILE(COMMAND[, IF-FAILED])
ebb439
 dnl
ebb439
 dnl Executes shell COMMAND in a loop until it returns
ebb439
 dnl non-zero return code.  If COMMAND did not return
ebb439
 dnl non-zero code within reasonable time limit, then
ebb439
-dnl the test fails.
ebb439
+dnl the test fails.  In that case, runs IF-FAILED
ebb439
+dnl before aborting.
ebb439
 m4_define([OVS_WAIT_WHILE],
ebb439
   [OVS_WAIT([if $1; then return 1; else return 0; fi], [$2],
ebb439
             [AT_LINE], [while $1])])
ebb439
-- 
ebb439
2.28.0
ebb439