773311
From 2ee7fb81b5f396972ce279547456fd6d57891180 Mon Sep 17 00:00:00 2001
773311
From: Numan Siddique <numans@ovn.org>
773311
Date: Wed, 22 Apr 2020 18:03:57 +0530
773311
Subject: [PATCH 3/4] Support selection fields in load balancer.
773311
MIME-Version: 1.0
773311
Content-Type: text/plain; charset=UTF-8
773311
Content-Transfer-Encoding: 8bit
773311
773311
This patch add a new column 'selection_fields' in Load_Balancer
773311
table in NB DB. CMS can define a set of packet headers to use
773311
while selecting a backend. If this column is set, OVN will add the
773311
flow in group table with selection method as 'hash' with the set fields.
773311
Otherwise it will use the default 'dp_hash' selection method.
773311
773311
If a load balancer is configured with the selection_fields as
773311
selection_fields    : [ip_dst, ip_src, tp_dst, tp_src]
773311
773311
then with this patch, the modified ct_lb action will look like
773311
 - ct_lb(backends=IP1:P1,IP2:P1; hash_fields="ip_dst,ip_src,tp_dst,tp_src");
773311
773311
And the OF flow will look like
773311
 - group_id=2,type=select,selection_method=hash,
773311
   fields(ip_src,ip_dst,tcp_src,tcp_dst),bucket=bucket_id:0,weight:100,actions=ct(....
773311
773311
Change-Id: Iac595d50d77783fd28bcb1af7b63e9274b94f622
773311
Tested-by: Maciej Józefczyk <mjozefcz@redhat.com>
773311
Acked-by: Maciej Józefczyk <mjozefcz@redhat.com>
773311
Acked-by: Han Zhou <hzhou@ovn.org>
773311
Acked-by: Mark Michelson <mmichels@redhat.com>
773311
Signed-off-by: Numan Siddique <numans@ovn.org>
773311
---
773311
 NEWS                  |   6 ++
773311
 include/ovn/actions.h |   1 +
773311
 lib/actions.c         |  45 +++++++++--
773311
 northd/ovn-northd.c   |  30 ++++++--
773311
 ovn-nb.ovsschema      |  10 ++-
773311
 ovn-nb.xml            |  27 +++++++
773311
 tests/ovn-northd.at   |  24 +++---
773311
 tests/ovn.at          |  49 +++++++-----
773311
 tests/system-ovn.at   | 172 +++++++++++++++++++++++++++++++++++++++---
773311
 9 files changed, 311 insertions(+), 53 deletions(-)
773311
773311
diff --git a/NEWS b/NEWS
773311
index e77343c89..15c3453f8 100644
773311
--- a/NEWS
773311
+++ b/NEWS
773311
@@ -9,6 +9,12 @@ OVN v20.03.0 - 28 Feb 2020
773311
    - Added support for ECMP routes in OVN router.
773311
    - Added IPv6 Prefix Delegation support in OVN.
773311
    - OVN now uses OpenFlow 1.5.
773311
+   - Added support to choose selection methods - dp_hash or
773311
+     hash (with specified hash fields) for OVN load balancer
773311
+     backend selection. This is incompatible with older versions.
773311
+     Care should be taken while upgrading as the existing
773311
+     load balancer traffic will be affected if ovn-controllers
773311
+     are not stopped before uprading northd services.
773311
 
773311
    - OVN Interconnection:
773311
      * Support for L3 interconnection of multiple OVN deployments with tunnels
773311
diff --git a/include/ovn/actions.h b/include/ovn/actions.h
773311
index e3dec99b2..df11a5713 100644
773311
--- a/include/ovn/actions.h
773311
+++ b/include/ovn/actions.h
773311
@@ -252,6 +252,7 @@ struct ovnact_ct_lb {
773311
     struct ovnact_ct_lb_dst *dsts;
773311
     size_t n_dsts;
773311
     uint8_t ltable;             /* Logical table ID of next table. */
773311
+    char *hash_fields;
773311
 };
773311
 
773311
 struct ovnact_select_dst {
773311
diff --git a/lib/actions.c b/lib/actions.c
773311
index 605dbffe4..ee7ccae0d 100644
773311
--- a/lib/actions.c
773311
+++ b/lib/actions.c
773311
@@ -900,9 +900,18 @@ parse_ct_lb_action(struct action_context *ctx)
773311
     struct ovnact_ct_lb_dst *dsts = NULL;
773311
     size_t allocated_dsts = 0;
773311
     size_t n_dsts = 0;
773311
+    char *hash_fields = NULL;
773311
 
773311
-    if (lexer_match(ctx->lexer, LEX_T_LPAREN)) {
773311
-        while (!lexer_match(ctx->lexer, LEX_T_RPAREN)) {
773311
+    if (lexer_match(ctx->lexer, LEX_T_LPAREN) &&
773311
+        !lexer_match(ctx->lexer, LEX_T_RPAREN)) {
773311
+        if (!lexer_match_id(ctx->lexer, "backends") ||
773311
+            !lexer_force_match(ctx->lexer, LEX_T_EQUALS)) {
773311
+            lexer_syntax_error(ctx->lexer, "expecting backends");
773311
+            return;
773311
+        }
773311
+
773311
+        while (!lexer_match(ctx->lexer, LEX_T_SEMICOLON) &&
773311
+               !lexer_match(ctx->lexer, LEX_T_RPAREN)) {
773311
             struct ovnact_ct_lb_dst dst;
773311
             if (lexer_match(ctx->lexer, LEX_T_LSQUARE)) {
773311
                 /* IPv6 address and port */
773311
@@ -969,12 +978,27 @@ parse_ct_lb_action(struct action_context *ctx)
773311
             }
773311
             dsts[n_dsts++] = dst;
773311
         }
773311
+
773311
+        if (lexer_match_id(ctx->lexer, "hash_fields")) {
773311
+            if (!lexer_match(ctx->lexer, LEX_T_EQUALS) ||
773311
+                ctx->lexer->token.type != LEX_T_STRING ||
773311
+                lexer_lookahead(ctx->lexer) != LEX_T_RPAREN) {
773311
+                lexer_syntax_error(ctx->lexer, "invalid hash_fields");
773311
+                free(dsts);
773311
+                return;
773311
+            }
773311
+
773311
+            hash_fields = xstrdup(ctx->lexer->token.s);
773311
+            lexer_get(ctx->lexer);
773311
+            lexer_get(ctx->lexer);
773311
+        }
773311
     }
773311
 
773311
     struct ovnact_ct_lb *cl = ovnact_put_CT_LB(ctx->ovnacts);
773311
     cl->ltable = ctx->pp->cur_ltable + 1;
773311
     cl->dsts = dsts;
773311
     cl->n_dsts = n_dsts;
773311
+    cl->hash_fields = hash_fields;
773311
 }
773311
 
773311
 static void
773311
@@ -982,10 +1006,10 @@ format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s)
773311
 {
773311
     ds_put_cstr(s, "ct_lb");
773311
     if (cl->n_dsts) {
773311
-        ds_put_char(s, '(');
773311
+        ds_put_cstr(s, "(backends=");
773311
         for (size_t i = 0; i < cl->n_dsts; i++) {
773311
             if (i) {
773311
-                ds_put_cstr(s, ", ");
773311
+                ds_put_char(s, ',');
773311
             }
773311
 
773311
             const struct ovnact_ct_lb_dst *dst = &cl->dsts[i];
773311
@@ -1005,7 +1029,13 @@ format_CT_LB(const struct ovnact_ct_lb *cl, struct ds *s)
773311
             }
773311
         }
773311
         ds_put_char(s, ')');
773311
+
773311
+        if (cl->hash_fields) {
773311
+            ds_chomp(s, ')');
773311
+            ds_put_format(s, "; hash_fields=\"%s\")", cl->hash_fields);
773311
+        }
773311
     }
773311
+
773311
     ds_put_char(s, ';');
773311
 }
773311
 
773311
@@ -1052,7 +1082,11 @@ encode_CT_LB(const struct ovnact_ct_lb *cl,
773311
                             : MFF_LOG_DNAT_ZONE - MFF_REG0;
773311
 
773311
     struct ds ds = DS_EMPTY_INITIALIZER;
773311
-    ds_put_format(&ds, "type=select,selection_method=dp_hash");
773311
+    ds_put_format(&ds, "type=select,selection_method=%s",
773311
+                  cl->hash_fields ? "hash": "dp_hash");
773311
+    if (cl->hash_fields) {
773311
+        ds_put_format(&ds, ",fields(%s)", cl->hash_fields);
773311
+    }
773311
 
773311
     BUILD_ASSERT(MFF_LOG_CT_ZONE >= MFF_REG0);
773311
     BUILD_ASSERT(MFF_LOG_CT_ZONE < MFF_REG0 + FLOW_N_REGS);
773311
@@ -1094,6 +1128,7 @@ static void
773311
 ovnact_ct_lb_free(struct ovnact_ct_lb *ct_lb)
773311
 {
773311
     free(ct_lb->dsts);
773311
+    free(ct_lb->hash_fields);
773311
 }
773311
 
773311
 static void
773311
diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
773311
index dc647d7c5..b07e68cfa 100644
773311
--- a/northd/ovn-northd.c
773311
+++ b/northd/ovn-northd.c
773311
@@ -3108,7 +3108,7 @@ struct ovn_lb {
773311
     struct hmap_node hmap_node;
773311
 
773311
     const struct nbrec_load_balancer *nlb; /* May be NULL. */
773311
-
773311
+    char *selection_fields;
773311
     struct lb_vip *vips;
773311
     size_t n_vips;
773311
 };
773311
@@ -3336,6 +3336,15 @@ ovn_lb_create(struct northd_context *ctx, struct hmap *lbs,
773311
         n_vips++;
773311
     }
773311
 
773311
+    if (lb->nlb->n_selection_fields) {
773311
+        struct ds sel_fields = DS_EMPTY_INITIALIZER;
773311
+        for (size_t i = 0; i < lb->nlb->n_selection_fields; i++) {
773311
+            ds_put_format(&sel_fields, "%s,", lb->nlb->selection_fields[i]);
773311
+        }
773311
+        ds_chomp(&sel_fields, ',');
773311
+        lb->selection_fields = ds_steal_cstr(&sel_fields);
773311
+    }
773311
+
773311
     return lb;
773311
 }
773311
 
773311
@@ -3354,13 +3363,15 @@ ovn_lb_destroy(struct ovn_lb *lb)
773311
         free(lb->vips[i].backends);
773311
     }
773311
     free(lb->vips);
773311
+    free(lb->selection_fields);
773311
 }
773311
 
773311
 static void build_lb_vip_ct_lb_actions(struct lb_vip *lb_vip,
773311
-                                       struct ds *action)
773311
+                                       struct ds *action,
773311
+                                       char *selection_fields)
773311
 {
773311
     if (lb_vip->health_check) {
773311
-        ds_put_cstr(action, "ct_lb(");
773311
+        ds_put_cstr(action, "ct_lb(backends=");
773311
 
773311
         size_t n_active_backends = 0;
773311
         for (size_t k = 0; k < lb_vip->n_backends; k++) {
773311
@@ -3384,7 +3395,13 @@ static void build_lb_vip_ct_lb_actions(struct lb_vip *lb_vip,
773311
             ds_put_cstr(action, ");");
773311
         }
773311
     } else {
773311
-        ds_put_format(action, "ct_lb(%s);", lb_vip->backend_ips);
773311
+        ds_put_format(action, "ct_lb(backends=%s);", lb_vip->backend_ips);
773311
+    }
773311
+
773311
+    if (selection_fields && selection_fields[0]) {
773311
+        ds_chomp(action, ';');
773311
+        ds_chomp(action, ')');
773311
+        ds_put_format(action, "; hash_fields=\"%s\");", selection_fields);
773311
     }
773311
 }
773311
 
773311
@@ -5660,7 +5677,7 @@ build_lb_rules(struct ovn_datapath *od, struct hmap *lflows, struct ovn_lb *lb)
773311
 
773311
         /* New connections in Ingress table. */
773311
         struct ds action = DS_EMPTY_INITIALIZER;
773311
-        build_lb_vip_ct_lb_actions(lb_vip, &action);
773311
+        build_lb_vip_ct_lb_actions(lb_vip, &action, lb->selection_fields);
773311
 
773311
         struct ds match = DS_EMPTY_INITIALIZER;
773311
         ds_put_format(&match, "ct.new && %s.dst == %s", ip_match, lb_vip->vip);
773311
@@ -9290,7 +9307,8 @@ build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
773311
             for (size_t j = 0; j < lb->n_vips; j++) {
773311
                 struct lb_vip *lb_vip = &lb->vips[j];
773311
                 ds_clear(&actions);
773311
-                build_lb_vip_ct_lb_actions(lb_vip, &actions);
773311
+                build_lb_vip_ct_lb_actions(lb_vip, &actions,
773311
+                                           lb->selection_fields);
773311
 
773311
                 if (!sset_contains(&all_ips, lb_vip->vip)) {
773311
                     sset_add(&all_ips, lb_vip->vip);
773311
diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema
773311
index 949f6258b..359201c26 100644
773311
--- a/ovn-nb.ovsschema
773311
+++ b/ovn-nb.ovsschema
773311
@@ -1,7 +1,7 @@
773311
 {
773311
     "name": "OVN_Northbound",
773311
-    "version": "5.22.0",
773311
-    "cksum": "170077561 25417",
773311
+    "version": "5.23.0",
773311
+    "cksum": "3367447924 25747",
773311
     "tables": {
773311
         "NB_Global": {
773311
             "columns": {
773311
@@ -179,6 +179,12 @@
773311
                 "ip_port_mappings": {
773311
                     "type": {"key": "string", "value": "string",
773311
                              "min": 0, "max": "unlimited"}},
773311
+                "selection_fields": {
773311
+                    "type": {"key": {"type": "string",
773311
+                             "enum": ["set",
773311
+                                ["eth_src", "eth_dst", "ip_src", "ip_dst",
773311
+                                 "tp_src", "tp_dst"]]},
773311
+                             "min": 0, "max": "unlimited"}},
773311
                 "external_ids": {
773311
                     "type": {"key": "string", "value": "string",
773311
                              "min": 0, "max": "unlimited"}}},
773311
diff --git a/ovn-nb.xml b/ovn-nb.xml
773311
index 045c63fb0..55f0ef9f6 100644
773311
--- a/ovn-nb.xml
773311
+++ b/ovn-nb.xml
773311
@@ -1497,6 +1497,33 @@
773311
       

773311
     </column>
773311
 
773311
+    <column name="selection_fields">
773311
+      

773311
+        OVN native load balancers are supported using the OpenFlow groups
773311
+        of type select. OVS supports two selection methods:
773311
+        dp_hash and hash (with optional fields
773311
+        specified) in selecting the buckets of a group.
773311
+        Please see the OVS documentation (man ovs-ofctl)
773311
+        for more details on the selection methods. Each endpoint IP (and port
773311
+        if set) is mapped to a bucket in the group flow.
773311
+      

773311
+
773311
+      

773311
+        CMS can choose the hash selection method by setting the
773311
+        selection fields in this column. ovs-vswitchd uses the
773311
+        specified fields in generating the hash.
773311
+      

773311
+
773311
+      

773311
+        dp_hash selection method uses the assistance of
773311
+        datapath to calculate the hash and it is expected to be
773311
+        faster than hash selection method. So CMS should take
773311
+        this into consideration before using the hash method.
773311
+        Please consult the OVS documentation and OVS sources for the
773311
+        implementation details.
773311
+      

773311
+    </column>
773311
+
773311
     <group title="Common Columns">
773311
       <column name="external_ids">
773311
         See External IDs at the beginning of this document.
773311
diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at
773311
index 569390cee..e6a8c04da 100644
773311
--- a/tests/ovn-northd.at
773311
+++ b/tests/ovn-northd.at
773311
@@ -1119,7 +1119,7 @@ ovn-nbctl --wait=sb ls-lb-add sw0 lb1
773311
 
773311
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
773311
 ])
773311
 
773311
 # Delete the Load_Balancer_Health_Check
773311
@@ -1128,7 +1128,7 @@ OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list service_monitor |  wc -l`])
773311
 
773311
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
773311
 ])
773311
 
773311
 # Create the Load_Balancer_Health_Check again.
773311
@@ -1141,7 +1141,7 @@ service_monitor | sed '/^$/d' | wc -l`])
773311
 
773311
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
773311
 ])
773311
 
773311
 # Get the uuid of both the service_monitor
773311
@@ -1157,7 +1157,7 @@ OVS_WAIT_UNTIL([
773311
 
773311
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80);)
773311
 ])
773311
 
773311
 # Set the service monitor for sw0-p1 to offline
773311
@@ -1187,7 +1187,7 @@ OVS_WAIT_UNTIL([
773311
 
773311
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
773311
 ])
773311
 
773311
 # Set the service monitor for sw1-p1 to error
773311
@@ -1199,7 +1199,7 @@ OVS_WAIT_UNTIL([
773311
 ovn-sbctl dump-flows sw0 | grep "ip4.dst == 10.0.0.10 && tcp.dst == 80" \
773311
 | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80);)
773311
 ])
773311
 
773311
 # Add one more vip to lb1
773311
@@ -1229,8 +1229,8 @@ service_monitor port=1000 | sed '/^$/d' | wc -l`])
773311
 
773311
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80);)
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000);)
773311
 ])
773311
 
773311
 # Set the service monitor for sw1-p1 to online
773311
@@ -1242,16 +1242,16 @@ OVS_WAIT_UNTIL([
773311
 
773311
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
773311
 ])
773311
 
773311
 # Associate lb1 to sw1
773311
 ovn-nbctl --wait=sb ls-lb-add sw1 lb1
773311
 ovn-sbctl dump-flows sw1 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(10.0.0.3:1000,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.40 && tcp.dst == 1000), action=(ct_lb(backends=10.0.0.3:1000,20.0.0.3:80);)
773311
 ])
773311
 
773311
 # Now create lb2 same as lb1 but udp protocol.
773311
diff --git a/tests/ovn.at b/tests/ovn.at
773311
index 5fb100ad4..ae3b44cb3 100644
773311
--- a/tests/ovn.at
773311
+++ b/tests/ovn.at
773311
@@ -968,29 +968,42 @@ ct_lb();
773311
     encodes as ct(table=19,zone=NXM_NX_REG13[0..15],nat)
773311
     has prereqs ip
773311
 ct_lb(192.168.1.2:80, 192.168.1.3:80);
773311
+    Syntax error at `192.168.1.2' expecting backends.
773311
+ct_lb(backends=192.168.1.2:80,192.168.1.3:80);
773311
     encodes as group:1
773311
     uses group: id(1), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15]))
773311
     has prereqs ip
773311
-ct_lb(192.168.1.2, 192.168.1.3, );
773311
-    formats as ct_lb(192.168.1.2, 192.168.1.3);
773311
+ct_lb(backends=192.168.1.2, 192.168.1.3, );
773311
+    formats as ct_lb(backends=192.168.1.2,192.168.1.3);
773311
     encodes as group:2
773311
     uses group: id(2), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3),commit,table=19,zone=NXM_NX_REG13[0..15]))
773311
     has prereqs ip
773311
-ct_lb(fd0f::2, fd0f::3, );
773311
-    formats as ct_lb(fd0f::2, fd0f::3);
773311
+ct_lb(backends=fd0f::2, fd0f::3, );
773311
+    formats as ct_lb(backends=fd0f::2,fd0f::3);
773311
     encodes as group:3
773311
     uses group: id(3), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15]))
773311
     has prereqs ip
773311
 
773311
-ct_lb(192.168.1.2:);
773311
+ct_lb(backends=192.168.1.2:);
773311
     Syntax error at `)' expecting port number.
773311
-ct_lb(192.168.1.2:123456);
773311
+ct_lb(backends=192.168.1.2:123456);
773311
     Syntax error at `123456' expecting port number.
773311
-ct_lb(foo);
773311
+ct_lb(backends=foo);
773311
     Syntax error at `foo' expecting IP address.
773311
-ct_lb([192.168.1.2]);
773311
+ct_lb(backends=[192.168.1.2]);
773311
     Syntax error at `192.168.1.2' expecting IPv6 address.
773311
 
773311
+ct_lb(backends=192.168.1.2:80,192.168.1.3:80; hash_fields=eth_src,eth_dst,ip_src);
773311
+    Syntax error at `eth_src' invalid hash_fields.
773311
+ct_lb(backends=192.168.1.2:80,192.168.1.3:80; hash_fields="eth_src,eth_dst,ip_src");
773311
+    encodes as group:4
773311
+    uses group: id(4), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=192.168.1.2:80),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=192.168.1.3:80),commit,table=19,zone=NXM_NX_REG13[0..15]))
773311
+    has prereqs ip
773311
+ct_lb(backends=fd0f::2,fd0f::3; hash_fields="eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst");
773311
+    encodes as group:5
773311
+    uses group: id(5), name(type=select,selection_method=hash,fields(eth_src,eth_dst,ip_src,ip_dst,tp_src,tp_dst),bucket=bucket_id=0,weight:100,actions=ct(nat(dst=fd0f::2),commit,table=19,zone=NXM_NX_REG13[0..15]),bucket=bucket_id=1,weight:100,actions=ct(nat(dst=fd0f::3),commit,table=19,zone=NXM_NX_REG13[0..15]))
773311
+    has prereqs ip
773311
+
773311
 # ct_next
773311
 ct_next;
773311
     encodes as ct(table=19,zone=NXM_NX_REG13[0..15])
773311
@@ -1491,13 +1504,13 @@ handle_svc_check(reg0);
773311
 # select
773311
 reg9[16..31] = select(1=50, 2=100, 3, );
773311
     formats as reg9[16..31] = select(1=50, 2=100, 3=100);
773311
-    encodes as group:4
773311
-    uses group: id(4), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:50,actions=load:1->xreg4[16..31],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xreg4[16..31],resubmit(,19),bucket=bucket_id=2,weight:100,actions=load:3->xreg4[16..31],resubmit(,19))
773311
+    encodes as group:6
773311
+    uses group: id(6), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:50,actions=load:1->xreg4[16..31],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xreg4[16..31],resubmit(,19),bucket=bucket_id=2,weight:100,actions=load:3->xreg4[16..31],resubmit(,19))
773311
 
773311
 reg0 = select(1, 2);
773311
     formats as reg0 = select(1=100, 2=100);
773311
-    encodes as group:5
773311
-    uses group: id(5), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=load:1->xxreg0[96..127],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xxreg0[96..127],resubmit(,19))
773311
+    encodes as group:7
773311
+    uses group: id(7), name(type=select,selection_method=dp_hash,bucket=bucket_id=0,weight:100,actions=load:1->xxreg0[96..127],resubmit(,19),bucket=bucket_id=1,weight:100,actions=load:2->xxreg0[96..127],resubmit(,19))
773311
 
773311
 reg0 = select(1=, 2);
773311
     Syntax error at `,' expecting weight.
773311
@@ -1513,12 +1526,12 @@ reg0[0..14] = select(1, 2, 3);
773311
     cannot use 15-bit field reg0[0..14] for "select", which requires at least 16 bits.
773311
 
773311
 fwd_group(liveness="true", childports="eth0", "lsp1");
773311
-    encodes as group:6
773311
-    uses group: id(6), name(type=select,selection_method=dp_hash,bucket=watch_port:5,load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=watch_port:17,load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
773311
+    encodes as group:8
773311
+    uses group: id(8), name(type=select,selection_method=dp_hash,bucket=watch_port:5,load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=watch_port:17,load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
773311
 
773311
 fwd_group(childports="eth0", "lsp1");
773311
-    encodes as group:7
773311
-    uses group: id(7), name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
773311
+    encodes as group:9
773311
+    uses group: id(9), name(type=select,selection_method=dp_hash,bucket=load=0x5->NXM_NX_REG15[0..15],resubmit(,64),bucket=load=0x17->NXM_NX_REG15[0..15],resubmit(,64))
773311
 
773311
 fwd_group(childports=eth0);
773311
     Syntax error at `eth0' expecting logical switch port.
773311
@@ -17916,12 +17929,12 @@ service_monitor | sed '/^$/d' | wc -l`])
773311
 
773311
 ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
773311
+  table=10(ls_in_stateful     ), priority=120  , match=(ct.new && ip4.dst == 10.0.0.10 && tcp.dst == 80), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
773311
 ])
773311
 
773311
 ovn-sbctl dump-flows lr0 | grep ct_lb | grep priority=120 > lflows.txt
773311
 AT_CHECK([cat lflows.txt], [0], [dnl
773311
-  table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb(10.0.0.3:80,20.0.0.3:80);)
773311
+  table=6 (lr_in_dnat         ), priority=120  , match=(ct.new && ip && ip4.dst == 10.0.0.10 && tcp && tcp.dst == 80 && is_chassis_resident("cr-lr0-public")), action=(ct_lb(backends=10.0.0.3:80,20.0.0.3:80);)
773311
 ])
773311
 
773311
 # get the svc monitor mac.
773311
diff --git a/tests/system-ovn.at b/tests/system-ovn.at
773311
index 117f1e835..9a5ef1ec3 100644
773311
--- a/tests/system-ovn.at
773311
+++ b/tests/system-ovn.at
773311
@@ -1095,15 +1095,15 @@ ovn-nbctl lsp-add bar bar3 \
773311
 -- lsp-set-addresses bar3 "f0:00:0f:01:02:05 172.16.1.4"
773311
 
773311
 # Config OVN load-balancer with a VIP.
773311
-uuid=`ovn-nbctl  create load_balancer vips:30.0.0.1="172.16.1.2,172.16.1.3,172.16.1.4"`
773311
-ovn-nbctl set logical_switch foo load_balancer=$uuid
773311
+ovn-nbctl lb-add lb1 30.0.0.1 "172.16.1.2,172.16.1.3,172.16.1.4"
773311
+ovn-nbctl ls-lb-add foo lb1
773311
 
773311
 # Create another load-balancer with another VIP.
773311
-uuid=`ovn-nbctl create load_balancer vips:30.0.0.3="172.16.1.2,172.16.1.3,172.16.1.4"`
773311
-ovn-nbctl add logical_switch foo load_balancer $uuid
773311
+lb2_uuid=`ovn-nbctl create load_balancer name=lb2 vips:30.0.0.3="172.16.1.2,172.16.1.3,172.16.1.4"`
773311
+ovn-nbctl ls-lb-add foo lb2
773311
 
773311
 # Config OVN load-balancer with another VIP (this time with ports).
773311
-ovn-nbctl set load_balancer $uuid vips:'"30.0.0.2:8000"'='"172.16.1.2:80,172.16.1.3:80,172.16.1.4:80"'
773311
+ovn-nbctl set load_balancer $lb2_uuid vips:'"30.0.0.2:8000"'='"172.16.1.2:80,172.16.1.3:80,172.16.1.4:80"'
773311
 
773311
 # Wait for ovn-controller to catch up.
773311
 ovn-nbctl --wait=hv sync
773311
@@ -1157,6 +1157,82 @@ tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(s
773311
 tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
773311
 ])
773311
 
773311
+# Configure selection_fields.
773311
+ovn-nbctl set load_balancer $lb2_uuid selection_fields="ip_src,ip_dst,tp_src,tp_dst"
773311
+OVS_WAIT_UNTIL([
773311
+    test $(ovs-ofctl dump-groups br-int | \
773311
+    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 2
773311
+])
773311
+
773311
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
773311
+
773311
+dnl Test load-balancing that includes L4 ports in NAT.
773311
+for i in `seq 1 20`; do
773311
+    echo Request $i
773311
+    NS_CHECK_EXEC([foo1], [wget 30.0.0.2:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
773311
+done
773311
+
773311
+dnl Each server should have at least one connection.
773311
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(30.0.0.2) | \
773311
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
773311
+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.2,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
773311
+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.3,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
773311
+tcp,orig=(src=192.168.1.2,dst=30.0.0.2,sport=<cleared>,dport=<cleared>),reply=(src=172.16.1.4,dst=192.168.1.2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
773311
+])
773311
+
773311
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
773311
+
773311
+echo "foo" > foo
773311
+for i in `seq 1 20`; do
773311
+    echo Request $i
773311
+    ip netns exec foo1 nc -p 30000 30.0.0.2 8000 < foo
773311
+done
773311
+
773311
+dnl Only one backend should be chosen.
773311
+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 -c) -eq 1])
773311
+
773311
+ovn-nbctl set load_balancer $lb2_uuid selection_fields="ip_src"
773311
+OVS_WAIT_UNTIL([
773311
+    test $(ovs-ofctl dump-groups br-int | \
773311
+    grep "selection_method=hash,fields=ip_src" -c) -eq 2
773311
+])
773311
+
773311
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
773311
+for i in `seq 1 20`; do
773311
+    echo Request $i
773311
+    ip netns exec foo1 nc 30.0.0.2 8000 < foo
773311
+done
773311
+
773311
+dnl Only one backend should be chosen as eth_src and ip_src is fixed.
773311
+bar1_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep 172.16.1.2 -c)
773311
+bar2_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep 172.16.1.3 -c)
773311
+bar3_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep 172.16.1.4 -c)
773311
+
773311
+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep 172.16.1 -c) -ne 0])
773311
+
773311
+if [[ "$bar1_ct" == "20" ]]; then
773311
+    AT_CHECK([test $bar1_ct -eq 20])
773311
+    AT_CHECK([test $bar2_ct -eq 0])
773311
+    AT_CHECK([test $bar3_ct -eq 0])
773311
+else
773311
+    AT_CHECK([test $bar1_ct -eq 0])
773311
+fi
773311
+
773311
+if [[ "$bar2_ct" == "20" ]]; then
773311
+    AT_CHECK([test $bar1_ct -eq 20])
773311
+    AT_CHECK([test $bar2_ct -eq 0])
773311
+    AT_CHECK([test $bar3_ct -eq 0])
773311
+else
773311
+    AT_CHECK([test $bar2_ct -eq 0])
773311
+fi
773311
+
773311
+if [[ "$bar3_ct" == "20" ]]; then
773311
+    AT_CHECK([test $bar1_ct -eq 20])
773311
+    AT_CHECK([test $bar2_ct -eq 0])
773311
+    AT_CHECK([test $bar3_ct -eq 0])
773311
+else
773311
+    AT_CHECK([test $bar3_ct -eq 0])
773311
+fi
773311
 
773311
 OVS_APP_EXIT_AND_WAIT([ovn-controller])
773311
 
773311
@@ -1246,11 +1322,11 @@ uuid=`ovn-nbctl  create load_balancer vips:\"fd03::1\"=\"fd02::2,fd02::3,fd02::4
773311
 ovn-nbctl set logical_switch foo load_balancer=$uuid
773311
 
773311
 # Create another load-balancer with another VIP.
773311
-uuid=`ovn-nbctl create load_balancer vips:\"fd03::3\"=\"fd02::2,fd02::3,fd02::4\"`
773311
-ovn-nbctl add logical_switch foo load_balancer $uuid
773311
+lb2_uuid=`ovn-nbctl create load_balancer vips:\"fd03::3\"=\"fd02::2,fd02::3,fd02::4\"`
773311
+ovn-nbctl add logical_switch foo load_balancer $lb2_uuid
773311
 
773311
 # Config OVN load-balancer with another VIP (this time with ports).
773311
-ovn-nbctl set load_balancer $uuid vips:'"[[fd03::2]]:8000"'='"@<:@fd02::2@:>@:80,@<:@fd02::3@:>@:80,@<:@fd02::4@:>@:80"'
773311
+ovn-nbctl set load_balancer $lb2_uuid vips:'"[[fd03::2]]:8000"'='"@<:@fd02::2@:>@:80,@<:@fd02::3@:>@:80,@<:@fd02::4@:>@:80"'
773311
 
773311
 # Wait for ovn-controller to catch up.
773311
 ovn-nbctl --wait=hv sync
773311
@@ -1304,7 +1380,83 @@ tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd
773311
 tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
773311
 ])
773311
 
773311
+# Configure selection_fields.
773311
+ovn-nbctl set load_balancer $lb2_uuid selection_fields="ip_src,ip_dst,tp_src,tp_dst"
773311
+OVS_WAIT_UNTIL([
773311
+    test $(ovs-ofctl dump-groups br-int | \
773311
+    grep "selection_method=hash,fields(ip_src,ip_dst,tcp_src,tcp_dst)" -c) -eq 2
773311
+])
773311
+
773311
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
773311
+
773311
+dnl Test load-balancing that includes L4 ports in NAT.
773311
+for i in `seq 1 20`; do
773311
+    echo Request $i
773311
+    NS_CHECK_EXEC([foo1], [wget http://[[fd03::2]]:8000 -t 5 -T 1 --retry-connrefused -v -o wget$i.log])
773311
+done
773311
+
773311
+dnl Each server should have at least one connection.
773311
+AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(fd03::2) | grep -v fe80 | \
773311
+sed -e 's/zone=[[0-9]]*/zone=<cleared>/'], [0], [dnl
773311
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::2,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
773311
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::3,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
773311
+tcp,orig=(src=fd01::2,dst=fd03::2,sport=<cleared>,dport=<cleared>),reply=(src=fd02::4,dst=fd01::2,sport=<cleared>,dport=<cleared>),zone=<cleared>,protoinfo=(state=<cleared>)
773311
+])
773311
+
773311
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
773311
+
773311
+echo "foo" > foo
773311
+for i in `seq 1 20`; do
773311
+    echo Request $i
773311
+    ip netns exec foo1 nc -6 -p 30000 fd03::2 8000 < foo
773311
+done
773311
+
773311
+# Only one backend should be chosen. Since the source port is fixed,
773311
+# there should be only one conntrack entry.
773311
+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep fd03::2 -c) -eq 1])
773311
+
773311
+ovn-nbctl set load_balancer $lb2_uuid selection_fields="eth_src,ip_src"
773311
+OVS_WAIT_UNTIL([
773311
+    test $(ovs-ofctl dump-groups br-int | \
773311
+    grep "selection_method=hash,fields(eth_src,ip_src)" -c) -eq 2
773311
+])
773311
+
773311
+AT_CHECK([ovs-appctl dpctl/flush-conntrack])
773311
+for i in `seq 1 20`; do
773311
+    echo Request $i
773311
+    ip netns exec foo1 nc -6 fd03::2 8000 < foo
773311
+done
773311
 
773311
+dnl Only one backend should be chosen as eth_src and ip_src is fixed.
773311
+bar1_ct=$(ovs-appctl dpctl/dump-conntrack | grep fd03::2 | grep fd02::2 -c)
773311
+bar2_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep fd02::3 -c)
773311
+bar3_ct=$(ovs-appctl dpctl/dump-conntrack | grep 30.0.0.2 | grep fd02::4 -c)
773311
+
773311
+AT_CHECK([test $(ovs-appctl dpctl/dump-conntrack | grep fd03::2 | grep fd02 -c) -ne 0])
773311
+
773311
+if [[ "$bar1_ct" == "20" ]]; then
773311
+    AT_CHECK([test $bar1_ct -eq 20])
773311
+    AT_CHECK([test $bar2_ct -eq 0])
773311
+    AT_CHECK([test $bar3_ct -eq 0])
773311
+else
773311
+    AT_CHECK([test $bar1_ct -eq 0])
773311
+fi
773311
+
773311
+if [[ "$bar2_ct" == "20" ]]; then
773311
+    AT_CHECK([test $bar1_ct -eq 20])
773311
+    AT_CHECK([test $bar2_ct -eq 0])
773311
+    AT_CHECK([test $bar3_ct -eq 0])
773311
+else
773311
+    AT_CHECK([test $bar2_ct -eq 0])
773311
+fi
773311
+
773311
+if [[ "$bar3_ct" == "20" ]]; then
773311
+    AT_CHECK([test $bar1_ct -eq 20])
773311
+    AT_CHECK([test $bar2_ct -eq 0])
773311
+    AT_CHECK([test $bar3_ct -eq 0])
773311
+else
773311
+    AT_CHECK([test $bar3_ct -eq 0])
773311
+fi
773311
 OVS_APP_EXIT_AND_WAIT([ovn-controller])
773311
 
773311
 as ovn-sb
773311
@@ -3448,7 +3600,7 @@ service_monitor | sed '/^$/d' | grep online | wc -l`])
773311
 
773311
 OVS_WAIT_UNTIL(
773311
     [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt
773311
-     test 1 = `cat lflows.txt | grep "ct_lb(10.0.0.3:80,20.0.0.3:80)" | wc -l`]
773311
+     test 1 = `cat lflows.txt | grep "ct_lb(backends=10.0.0.3:80,20.0.0.3:80)" | wc -l`]
773311
 )
773311
 
773311
 # From sw0-p2 send traffic to vip - 10.0.0.10
773311
@@ -3474,7 +3626,7 @@ service_monitor logical_port=sw0-p1 | sed '/^$/d' | grep offline | wc -l`])
773311
 
773311
 OVS_WAIT_UNTIL(
773311
     [ovn-sbctl dump-flows sw0 | grep ct_lb | grep priority=120 | grep "ip4.dst == 10.0.0.10" > lflows.txt
773311
-     test 1 = `cat lflows.txt | grep "ct_lb(20.0.0.3:80)" | wc -l`]
773311
+     test 1 = `cat lflows.txt | grep "ct_lb(backends=20.0.0.3:80)" | wc -l`]
773311
 )
773311
 
773311
 ovs-appctl dpctl/flush-conntrack
773311
-- 
773311
2.26.2
773311