[ovs-dev,4/4] ovn: Add IPv6 capability to ovn-nbctl lb-add

Message ID 20170906210839.26091-5-mmichels@redhat.com
State New
Headers show
Series
  • ovn: Add support for IPv6 load balancers
Related show

Commit Message

Mark Michelson Sept. 6, 2017, 9:08 p.m.
ovn-nbctl will now accept IPv6 addresses for load balancer VIPs and
desetination addresses.

In addition, the ovn-nbctl lb-list, lr-lb-list, and ls-lb-list have been
modified to be able to fit IPv6 addresses on screen.

Signed-off-by: Mark Michelson <mmichels@redhat.com>
---
 ovn/utilities/ovn-nbctl.c | 175 +++++++++++++++------
 tests/ovn-nbctl.at        | 379 ++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 451 insertions(+), 103 deletions(-)

Patch

diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index 46ede4e50..93e1bfc81 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -1548,40 +1548,76 @@  nbctl_lb_add(struct ctl_context *ctx)
         }
     }
 
-    ovs_be32 ipv4 = 0;
-    ovs_be16 port = 0;
-    char *error = ip_parse_port(lb_vip, &ipv4, &port);
+    struct sockaddr_storage ss_vip;
+    char *error;
+    error = ipv46_parse(lb_vip, PORT_OPTIONAL, &ss_vip);
     if (error) {
         free(error);
-        if (!ip_parse(lb_vip, &ipv4)) {
-            ctl_fatal("%s: should be an IPv4 address (or an IPv4 address "
-                    "and a port number with : as a separator).", lb_vip);
+        ctl_fatal("%s: should be an IP address (or an IP address "
+                  "and a port number with : as a separator).", lb_vip);
+    }
+
+    char lb_vip_normalized[INET6_ADDRSTRLEN + 8];
+    char normalized_ip[INET6_ADDRSTRLEN];
+    if (ss_vip.ss_family == AF_INET) {
+        struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *, &ss_vip);
+        inet_ntop(AF_INET, &sin->sin_addr, normalized_ip,
+                  sizeof normalized_ip);
+        if (sin->sin_port) {
+            is_vip_with_port = true;
+            snprintf(lb_vip_normalized, sizeof lb_vip_normalized, "%s:%d",
+                     normalized_ip, ntohs(sin->sin_port));
+        } else {
+            is_vip_with_port = false;
+            ovs_strlcpy(lb_vip_normalized, normalized_ip,
+                        sizeof lb_vip_normalized);
         }
-
-        if (is_update_proto) {
-            ctl_fatal("Protocol is unnecessary when no port of vip "
-                    "is given.");
+    } else {
+        struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *,
+                                                 &ss_vip);
+        inet_ntop(AF_INET6, &sin6->sin6_addr, normalized_ip,
+                  sizeof normalized_ip);
+        if (sin6->sin6_port) {
+            is_vip_with_port = true;
+            snprintf(lb_vip_normalized, sizeof lb_vip_normalized, "[%s]:%d",
+                     normalized_ip, ntohs(sin6->sin6_port));
+        } else {
+            is_vip_with_port = false;
+            ovs_strlcpy(lb_vip_normalized, normalized_ip,
+                        sizeof lb_vip_normalized);
         }
-        is_vip_with_port = false;
+    }
+
+    if (!is_vip_with_port && is_update_proto) {
+        ctl_fatal("Protocol is unnecessary when no port of vip "
+                  "is given.");
     }
 
     char *token = NULL, *save_ptr = NULL;
     struct ds lb_ips_new = DS_EMPTY_INITIALIZER;
     for (token = strtok_r(lb_ips, ",", &save_ptr);
             token != NULL; token = strtok_r(NULL, ",", &save_ptr)) {
-        if (is_vip_with_port) {
-            error = ip_parse_port(token, &ipv4, &port);
-            if (error) {
-                free(error);
-                ds_destroy(&lb_ips_new);
-                ctl_fatal("%s: should be an IPv4 address and a port "
+        struct sockaddr_storage ss_dst;
+
+        error = ipv46_parse(token, is_vip_with_port
+                ? PORT_REQUIRED
+                : PORT_FORBIDDEN,
+                &ss_dst);
+
+        if (error) {
+            free(error);
+            if (is_vip_with_port) {
+                ctl_fatal("%s: should be an IP address and a port "
                         "number with : as a separator.", token);
+            } else {
+                ctl_fatal("%s: should be an IP address.", token);
             }
-        } else {
-            if (!ip_parse(token, &ipv4)) {
-                ds_destroy(&lb_ips_new);
-                ctl_fatal("%s: should be an IPv4 address.", token);
-            }
+        }
+
+        if (ss_vip.ss_family != ss_dst.ss_family) {
+            ds_destroy(&lb_ips_new);
+            ctl_fatal("%s: IP address family is different from VIP %s.",
+                    token, lb_vip_normalized);
         }
         ds_put_format(&lb_ips_new, "%s%s",
                 lb_ips_new.length ? "," : "", token);
@@ -1591,19 +1627,19 @@  nbctl_lb_add(struct ctl_context *ctx)
     if (!add_duplicate) {
         lb = lb_by_name_or_uuid(ctx, lb_name, false);
         if (lb) {
-            if (smap_get(&lb->vips, lb_vip)) {
+            if (smap_get(&lb->vips, lb_vip_normalized)) {
                 if (!may_exist) {
                     ds_destroy(&lb_ips_new);
                     ctl_fatal("%s: a load balancer with this vip (%s) "
-                            "already exists", lb_name, lb_vip);
+                            "already exists", lb_name, lb_vip_normalized);
                 }
                 /* Update the vips. */
                 smap_replace(CONST_CAST(struct smap *, &lb->vips),
-                        lb_vip, ds_cstr(&lb_ips_new));
+                        lb_vip_normalized, ds_cstr(&lb_ips_new));
             } else {
                 /* Add the new vips. */
                 smap_add(CONST_CAST(struct smap *, &lb->vips),
-                        lb_vip, ds_cstr(&lb_ips_new));
+                        lb_vip_normalized, ds_cstr(&lb_ips_new));
             }
 
             /* Update the load balancer. */
@@ -1623,7 +1659,7 @@  nbctl_lb_add(struct ctl_context *ctx)
     nbrec_load_balancer_set_name(lb, lb_name);
     nbrec_load_balancer_set_protocol(lb, lb_proto);
     smap_add(CONST_CAST(struct smap *, &lb->vips),
-            lb_vip, ds_cstr(&lb_ips_new));
+            lb_vip_normalized, ds_cstr(&lb_ips_new));
     nbrec_load_balancer_set_vips(lb, &lb->vips);
     ds_destroy(&lb_ips_new);
 }
@@ -1665,32 +1701,49 @@  nbctl_lb_del(struct ctl_context *ctx)
 
 static void
 lb_info_add_smap(const struct nbrec_load_balancer *lb,
-                 struct smap *lbs)
+                 struct smap *lbs, int vip_width)
 {
     struct ds key = DS_EMPTY_INITIALIZER;
     struct ds val = DS_EMPTY_INITIALIZER;
     char *error, *protocol;
-    ovs_be32 ipv4 = 0;
-    ovs_be16 port = 0;
 
     const struct smap_node **nodes = smap_sort(&lb->vips);
     if (nodes) {
         for (int i = 0; i < smap_count(&lb->vips); i++) {
             const struct smap_node *node = nodes[i];
             protocol = lb->protocol;
-            error = ip_parse_port(node->key, &ipv4, &port);
+
+            struct sockaddr_storage ss;
+            error = ipv46_parse(node->key, PORT_OPTIONAL, &ss);
             if (error) {
+                VLOG_WARN("%s", error);
                 free(error);
-                protocol = "tcp/udp";
+                continue;
+            }
+
+            if (ss.ss_family == AF_INET) {
+                struct sockaddr_in *sin = ALIGNED_CAST(struct sockaddr_in *,
+                                                       &ss);
+                if (!sin->sin_port) {
+                    protocol = "tcp/udp";
+                }
+            } else {
+                struct sockaddr_in6 *sin6 = ALIGNED_CAST(struct sockaddr_in6 *,
+                                                         &ss);
+                if (!sin6->sin6_port) {
+                    protocol = "tcp/udp";
+                }
             }
 
             i == 0 ? ds_put_format(&val,
-                        UUID_FMT "    %-20.16s%-11.7s%-25.21s%s",
+                        UUID_FMT "    %-20.16s%-11.7s%-*.*s%s",
                         UUID_ARGS(&lb->header_.uuid),
                         lb->name, protocol,
+                        vip_width + 4, vip_width,
                         node->key, node->value)
-                   : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
+                   : ds_put_format(&val, "\n%60s%-11.7s%-*.*s%s",
                         "", protocol,
+                        vip_width + 4, vip_width,
                         node->key, node->value);
         }
 
@@ -1704,12 +1757,12 @@  lb_info_add_smap(const struct nbrec_load_balancer *lb,
 }
 
 static void
-lb_info_print(struct ctl_context *ctx, struct smap *lbs)
+lb_info_print(struct ctl_context *ctx, struct smap *lbs, int vip_width)
 {
     const struct smap_node **nodes = smap_sort(lbs);
     if (nodes) {
-        ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-25.21s%s\n",
-                "UUID", "LB", "PROTO", "VIP", "IPs");
+        ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-*.*s%s\n",
+                "UUID", "LB", "PROTO", vip_width + 4, vip_width, "VIP", "IPs");
         for (size_t i = 0; i < smap_count(lbs); i++) {
             const struct smap_node *node = nodes[i];
             ds_put_format(&ctx->output, "%s\n", node->value);
@@ -1719,21 +1772,45 @@  lb_info_print(struct ctl_context *ctx, struct smap *lbs)
     }
 }
 
+static int
+lb_get_max_vip_length(const struct nbrec_load_balancer *lb, int vip_width)
+{
+    const struct smap_node *node;
+    int max_length = vip_width;
+
+    SMAP_FOR_EACH (node, &lb->vips) {
+        size_t keylen = strlen(node->key);
+        if (max_length < keylen) {
+            max_length = keylen;
+        }
+    }
+
+    return max_length;
+}
+
 static void
 lb_info_list_all(struct ctl_context *ctx,
                  const char *lb_name, bool lb_check)
 {
     const struct nbrec_load_balancer *lb;
     struct smap lbs = SMAP_INITIALIZER(&lbs);
+    int vip_width = 0;
+
+    NBREC_LOAD_BALANCER_FOR_EACH (lb, ctx->idl) {
+        if (lb_check && strcmp(lb->name, lb_name)) {
+            continue;
+        }
+        vip_width = lb_get_max_vip_length(lb, vip_width);
+    }
 
     NBREC_LOAD_BALANCER_FOR_EACH(lb, ctx->idl) {
         if (lb_check && strcmp(lb->name, lb_name)) {
             continue;
         }
-        lb_info_add_smap(lb, &lbs);
+        lb_info_add_smap(lb, &lbs, vip_width);
     }
 
-    lb_info_print(ctx, &lbs);
+    lb_info_print(ctx, &lbs, vip_width);
     smap_destroy(&lbs);
 }
 
@@ -1832,15 +1909,21 @@  nbctl_lr_lb_list(struct ctl_context *ctx)
     const char *lr_name = ctx->argv[1];
     const struct nbrec_logical_router *lr;
     struct smap lbs = SMAP_INITIALIZER(&lbs);
+    int vip_width = 0;
 
     lr = lr_by_name_or_uuid(ctx, lr_name, true);
     for (int i = 0; i < lr->n_load_balancer; i++) {
         const struct nbrec_load_balancer *lb
             = lr->load_balancer[i];
-        lb_info_add_smap(lb, &lbs);
+        vip_width = lb_get_max_vip_length(lb, vip_width);
+    }
+    for (int i = 0; i < lr->n_load_balancer; i++) {
+        const struct nbrec_load_balancer *lb
+            = lr->load_balancer[i];
+        lb_info_add_smap(lb, &lbs, vip_width);
     }
 
-    lb_info_print(ctx, &lbs);
+    lb_info_print(ctx, &lbs, vip_width);
     smap_destroy(&lbs);
 }
 
@@ -1929,15 +2012,21 @@  nbctl_ls_lb_list(struct ctl_context *ctx)
     const char *ls_name = ctx->argv[1];
     const struct nbrec_logical_switch *ls;
     struct smap lbs = SMAP_INITIALIZER(&lbs);
+    int vip_width = 0;
 
     ls = ls_by_name_or_uuid(ctx, ls_name, true);
     for (int i = 0; i < ls->n_load_balancer; i++) {
         const struct nbrec_load_balancer *lb
             = ls->load_balancer[i];
-        lb_info_add_smap(lb, &lbs);
+        vip_width = lb_get_max_vip_length(lb, vip_width);
+    }
+    for (int i = 0; i < ls->n_load_balancer; i++) {
+        const struct nbrec_load_balancer *lb
+            = ls->load_balancer[i];
+        lb_info_add_smap(lb, &lbs, vip_width);
     }
 
-    lb_info_print(ctx, &lbs);
+    lb_info_print(ctx, &lbs, vip_width);
     smap_destroy(&lbs);
 }
 
diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
index 354b8df96..3f39af2a3 100644
--- a/tests/ovn-nbctl.at
+++ b/tests/ovn-nbctl.at
@@ -399,47 +399,43 @@  OVN_NBCTL_TEST_START
 
 dnl Add two LBs.
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80a 192.168.10.10:80,192.168.10.20:80 tcp], [1], [],
-[ovn-nbctl: 30.0.0.10:80a: should be an IPv4 address (or an IPv4 address and a port number with : as a separator).
+[ovn-nbctl: 30.0.0.10:80a: should be an IP address (or an IP address and a port number with : as a separator).
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:a80 192.168.10.10:80,192.168.10.20:80 tcp], [1], [],
-[ovn-nbctl: 30.0.0.10:a80: should be an IPv4 address (or an IPv4 address and a port number with : as a separator).
+[ovn-nbctl: 30.0.0.10:a80: should be an IP address (or an IP address and a port number with : as a separator).
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10: 192.168.10.10:80,192.168.10.20:80 tcp], [1], [],
-[ovn-nbctl: 30.0.0.10:: should be an IPv4 address (or an IPv4 address and a port number with : as a separator).
+[ovn-nbctl: 30.0.0.10:: should be an IP address (or an IP address and a port number with : as a separator).
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20 tcp], [1], [],
-[ovn-nbctl: 192.168.10.20: should be an IPv4 address and a port number with : as a separator.
+[ovn-nbctl: 192.168.10.20: should be an IP address and a port number with : as a separator.
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.1a 192.168.10.10:80,192.168.10.20:80], [1], [],
-[ovn-nbctl: 30.0.0.1a: should be an IPv4 address (or an IPv4 address and a port number with : as a separator).
+[ovn-nbctl: 30.0.0.1a: should be an IP address (or an IP address and a port number with : as a separator).
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0 192.168.10.10:80,192.168.10.20:80], [1], [],
-[ovn-nbctl: 30.0.0: should be an IPv4 address (or an IPv4 address and a port number with : as a separator).
+[ovn-nbctl: 192.168.10.10:80: should be an IP address.
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10,192.168.10.20:80], [1], [],
-[ovn-nbctl: 192.168.10.20:80: should be an IPv4 address.
+[ovn-nbctl: 192.168.10.20:80: should be an IP address.
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:a80], [1], [],
-[ovn-nbctl: 192.168.10.10:a80: should be an IPv4 address.
+[ovn-nbctl: 192.168.10.10:a80: should be an IP address.
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:], [1], [],
-[ovn-nbctl: 192.168.10.10:: should be an IPv4 address.
+[ovn-nbctl: 192.168.10.10:: should be an IP address.
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.1a], [1], [],
-[ovn-nbctl: 192.168.10.1a: should be an IPv4 address.
-])
-
-AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10], [1], [],
-[ovn-nbctl: 192.168.10: should be an IPv4 address.
+[ovn-nbctl: 192.168.10.1a: should be an IP address.
 ])
 
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10 tcp], [1], [],
@@ -454,9 +450,9 @@  dnl Add ips to lb
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 ,,,192.168.10.10:80,,,,,])
 AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 ,,,192.168.10.10:80,,,,192.168.10.20:80,,,,])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80
-<1>    lb1                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80
+<1>    lb1                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
 ])
 AT_CHECK([ovn-nbctl lb-del lb0])
 AT_CHECK([ovn-nbctl lb-del lb1])
@@ -464,51 +460,51 @@  AT_CHECK([ovn-nbctl lb-del lb1])
 AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80])
 AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 tcp])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
 ])
 
 dnl Update the VIP of the lb1.
 AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:8080
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:8080
 ])
 
 AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:80 192.168.10.10:80,192.168.10.20:8080 udp])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:8080
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 udp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:8080
 ])
 
 dnl Config lb1 with another VIP.
 AT_CHECK([ovn-nbctl lb-add lb1 30.0.0.20:80 192.168.10.10:80 udp])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:8080
-                                                            udp        30.0.0.20:80             192.168.10.10:80
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 udp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:8080
+                                                            udp        30.0.0.20:80    192.168.10.10:80
 ])
 
 AT_CHECK([ovn-nbctl lb-del lb1 30.0.0.20:80])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:8080
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 udp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:8080
 ])
 
 dnl Add LBs whose vip is just an IP address.
 AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.30 192.168.10.10])
 AT_CHECK([ovn-nbctl lb-add lb3 30.0.0.30 192.168.10.10])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:8080
-<2>    lb2                 tcp/udp    30.0.0.30                192.168.10.10
-<3>    lb3                 tcp/udp    30.0.0.30                192.168.10.10
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 udp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:8080
+<2>    lb2                 tcp/udp    30.0.0.30       192.168.10.10
+<3>    lb3                 tcp/udp    30.0.0.30       192.168.10.10
 ])
 AT_CHECK([ovn-nbctl lb-del lb2 30.0.0.30])
 AT_CHECK([ovn-nbctl lb-del lb3 30.0.0.30])
@@ -516,11 +512,11 @@  AT_CHECK([ovn-nbctl lb-del lb3 30.0.0.30])
 AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp])
 AT_CHECK([ovn-nbctl --add-duplicate lb-add lb2 30.0.0.10:8080 192.168.10.10:80,192.168.10.20:80 tcp])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:8080
-<2>    lb2                 tcp        30.0.0.10:8080           192.168.10.10:80,192.168.10.20:80
-<3>    lb2                 tcp        30.0.0.10:8080           192.168.10.10:80,192.168.10.20:80
+UUID                                    LB                  PROTO      VIP               IPs
+<0>    lb0                 tcp        30.0.0.10:80      192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 udp        30.0.0.10:80      192.168.10.10:80,192.168.10.20:8080
+<2>    lb2                 tcp        30.0.0.10:8080    192.168.10.10:80,192.168.10.20:80
+<3>    lb2                 tcp        30.0.0.10:8080    192.168.10.10:80,192.168.10.20:80
 ])
 
 dnl If there are multiple load balancers with the same name, use a UUID to update/delete.
@@ -538,9 +534,9 @@  AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:9090 192.168.10.10:8080,192
 AT_CHECK([ovn-nbctl lb-del lb0 30.0.0.10:80])
 AT_CHECK([ovn-nbctl lb-del lb1])
 AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb2                 tcp        30.0.0.10:8080           192.168.10.10:80,192.168.10.20:80
-<1>    lb2                 tcp        30.0.0.10:8080           192.168.10.10:80,192.168.10.20:80
+UUID                                    LB                  PROTO      VIP               IPs
+<0>    lb2                 tcp        30.0.0.10:8080    192.168.10.10:80,192.168.10.20:80
+<1>    lb2                 tcp        30.0.0.10:8080    192.168.10.10:80,192.168.10.20:80
 ])
 
 dnl Add load balancer to logical switch.
@@ -557,17 +553,17 @@  AT_CHECK([ovn-nbctl ls-lb-add ls0 lb2], [1], [],
 AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3])
 
 AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<2>    lb3                 tcp/udp    30.0.0.10                192.168.10.10,192.168.10.20
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 udp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<2>    lb3                 tcp/udp    30.0.0.10       192.168.10.10,192.168.10.20
 ])
 
 AT_CHECK([ovn-nbctl ls-lb-del ls0 lb0])
 AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb3                 tcp/udp    30.0.0.10                192.168.10.10,192.168.10.20
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb1                 udp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb3                 tcp/udp    30.0.0.10       192.168.10.10,192.168.10.20
 ])
 
 AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1])
@@ -593,17 +589,17 @@  AT_CHECK([ovn-nbctl lr-lb-add lr0 lb2], [1], [],
 AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3])
 
 AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb0                 tcp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<2>    lb3                 tcp/udp    30.0.0.10                192.168.10.10,192.168.10.20
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb0                 tcp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb1                 udp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<2>    lb3                 tcp/udp    30.0.0.10       192.168.10.10,192.168.10.20
 ])
 
 AT_CHECK([ovn-nbctl lr-lb-del lr0 lb0])
 AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
-UUID                                    LB                  PROTO      VIP                      IPs
-<0>    lb1                 udp        30.0.0.10:80             192.168.10.10:80,192.168.10.20:80
-<1>    lb3                 tcp/udp    30.0.0.10                192.168.10.10,192.168.10.20
+UUID                                    LB                  PROTO      VIP             IPs
+<0>    lb1                 udp        30.0.0.10:80    192.168.10.10:80,192.168.10.20:80
+<1>    lb3                 tcp/udp    30.0.0.10       192.168.10.10,192.168.10.20
 ])
 
 AT_CHECK([ovn-nbctl lr-lb-del lr0 lb1])
@@ -620,7 +616,270 @@  AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [])
 
 OVN_NBCTL_TEST_STOP
 AT_CLEANUP
+dnl
+dnl ---------------------------------------------------------------------
+
+AT_SETUP([ovn-nbctl - LBs IPv6])
+OVN_NBCTL_TEST_START
+
+dnl A bunch of commands that should fail
+AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80a [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [],
+[ovn-nbctl: [[ae0f::10]]:80a: should be an IP address (or an IP address and a port number with : as a separator).
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:a80 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [],
+[ovn-nbctl: [[ae0f::10]]:a80: should be an IP address (or an IP address and a port number with : as a separator).
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]: [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [],
+[ovn-nbctl: [[ae0f::10]]:: should be an IP address (or an IP address and a port number with : as a separator).
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,fd0f::20 tcp], [1], [],
+[ovn-nbctl: fd0f::20: should be an IP address and a port number with : as a separator.
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10fff [[fd0f::10]]:80,fd0f::20 tcp], [1], [],
+[ovn-nbctl: ae0f::10fff: should be an IP address (or an IP address and a port number with : as a separator).
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:80,[[fd0f::20]]:80], [1], [],
+[ovn-nbctl: [[fd0f::10]]:80: should be an IP address.
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10,[[fd0f::20]]:80], [1], [],
+[ovn-nbctl: [[fd0f::20]]:80: should be an IP address.
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:a80], [1], [],
+[ovn-nbctl: [[fd0f::10]]:a80: should be an IP address.
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:], [1], [],
+[ovn-nbctl: [[fd0f::10]]:: should be an IP address.
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::1001a], [1], [],
+[ovn-nbctl: fd0f::1001a: should be an IP address.
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10 tcp], [1], [],
+[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
+])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 [[fd0f::10]]:900 tcp], [1], [],
+[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
+])
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 192.168.10.10], [1], [],
+[ovn-nbctl: 192.168.10.10: IP address family is different from VIP ae0f::10.
+])
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 192.168.10.10], [1], [],
+[ovn-nbctl: 192.168.10.10: IP address family is different from VIP ae0f::10.
+])
+
+AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 192.168.10.10:80], [1], [],
+[ovn-nbctl: 192.168.10.10:80: IP address family is different from VIP [[ae0f::10]]:80.
+])
+
+AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 ae0f::10], [1], [],
+[ovn-nbctl: ae0f::10: IP address family is different from VIP 30.0.0.10.
+])
+
+AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10:80 [[ae0f::10]]:80], [1], [],
+[ovn-nbctl: [[ae0f::10]]:80: IP address family is different from VIP 30.0.0.10:80.
+])
+
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f::10 fd0f::10])
+AT_CHECK([ovn-nbctl lb-add lb0 ae0f:0000:0000:0000:0000:0000:0000:0010 fd0f::20],
+[1], [], [ovn-nbctl: lb0: a load balancer with this vip (ae0f::10) already exists
+])
+
+AT_CHECK([ovn-nbctl lb-del lb0])
+
+dnl Add ips to lb
+AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 ,,,[[fd0f::10]]:80,,,,,])
+AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 ,,,[[fd0f::10]]:80,,,,[[fd0f::20]]:80,,,,])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80
+<1>    lb1                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+])
+AT_CHECK([ovn-nbctl lb-del lb0])
+AT_CHECK([ovn-nbctl lb-del lb1])
+
+
+AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80])
+AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+])
+
+dnl Update the VIP of the lb1.
+AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:8080
+])
+
+AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:8080 udp])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 udp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:8080
+])
+
+dnl Config lb1 with another VIP.
+AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::20]]:80 [[fd0f::10]]:80 udp])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 udp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:8080
+                                                            udp        [[ae0f::20]]:80    [[fd0f::10]]:80
+])
+
+AT_CHECK([ovn-nbctl lb-del lb1 [[ae0f::20]]:80])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 udp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:8080
+])
 
+dnl Add LBs whose vip is just an IP address.
+AT_CHECK([ovn-nbctl lb-add lb2 ae0f::30 fd0f::10])
+AT_CHECK([ovn-nbctl lb-add lb3 ae0f::30 fd0f::10])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 udp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:8080
+<2>    lb2                 tcp/udp    ae0f::30         fd0f::10
+<3>    lb3                 tcp/udp    ae0f::30         fd0f::10
+])
+AT_CHECK([ovn-nbctl lb-del lb2 ae0f::30])
+AT_CHECK([ovn-nbctl lb-del lb3 ae0f::30])
+
+AT_CHECK([ovn-nbctl lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp])
+AT_CHECK([ovn-nbctl --add-duplicate lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP                IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80      [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 udp        [[ae0f::10]]:80      [[fd0f::10]]:80,[[fd0f::20]]:8080
+<2>    lb2                 tcp        [[ae0f::10]]:8080    [[fd0f::10]]:80,[[fd0f::20]]:80
+<3>    lb2                 tcp        [[ae0f::10]]:8080    [[fd0f::10]]:80,[[fd0f::20]]:80
+])
+
+dnl If there are multiple load balancers with the same name, use a UUID to update/delete.
+AT_CHECK([ovn-nbctl lb-add lb2 [[ae0f::10]]:8080 [[fd0f::10]]:80,[[fd0f::20]]:80 tcp], [1], [],
+[ovn-nbctl: Multiple load balancers named 'lb2'.  Use a UUID.
+])
+
+AT_CHECK([ovn-nbctl lb-del lb2], [1], [],
+[ovn-nbctl: Multiple load balancers named 'lb2'.  Use a UUID.
+])
+
+AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp])
+AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:8080 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp])
+AT_CHECK([ovn-nbctl --may-exist lb-add lb1 [[ae0f::10]]:9090 [[fd0f::10]]:8080,[[fd0f::20]]:8080 udp])
+AT_CHECK([ovn-nbctl lb-del lb0 [[ae0f::10]]:80])
+AT_CHECK([ovn-nbctl lb-del lb1])
+AT_CHECK([ovn-nbctl lb-list | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP                IPs
+<0>    lb2                 tcp        [[ae0f::10]]:8080    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb2                 tcp        [[ae0f::10]]:8080    [[fd0f::10]]:80,[[fd0f::20]]:80
+])
+
+dnl Add load balancer to logical switch.
+AT_CHECK([ovn-nbctl ls-add ls0])
+AT_CHECK([ovn-nbctl lb-add lb0 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80])
+AT_CHECK([ovn-nbctl lb-add lb1 [[ae0f::10]]:80 [[fd0f::10]]:80,[[fd0f::20]]:80 udp])
+AT_CHECK([ovn-nbctl lb-add lb3 ae0f::10 fd0f::10,fd0f::20])
+AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0])
+AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1])
+AT_CHECK([ovn-nbctl --may-exist ls-lb-add ls0 lb1])
+AT_CHECK([ovn-nbctl ls-lb-add ls0 lb2], [1], [],
+[ovn-nbctl: Multiple load balancers named 'lb2'.  Use a UUID.
+])
+AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3])
+
+AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 udp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<2>    lb3                 tcp/udp    ae0f::10         fd0f::10,fd0f::20
+])
+
+AT_CHECK([ovn-nbctl ls-lb-del ls0 lb0])
+AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb1                 udp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb3                 tcp/udp    ae0f::10         fd0f::10,fd0f::20
+])
+
+AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1])
+AT_CHECK([ovn-nbctl ls-lb-del ls0 lb3])
+AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [])
+AT_CHECK([ovn-nbctl --if-exists ls-lb-del ls0 lb1])
+
+dnl Remove all load balancers from logical switch.
+AT_CHECK([ovn-nbctl ls-lb-add ls0 lb0])
+AT_CHECK([ovn-nbctl ls-lb-add ls0 lb1])
+AT_CHECK([ovn-nbctl ls-lb-add ls0 lb3])
+AT_CHECK([ovn-nbctl ls-lb-del ls0])
+AT_CHECK([ovn-nbctl ls-lb-list ls0 | ${PERL} $srcdir/uuidfilt.pl], [0], [])
+
+dnl Add load balancer to logical router.
+AT_CHECK([ovn-nbctl lr-add lr0])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1])
+AT_CHECK([ovn-nbctl --may-exist lr-lb-add lr0 lb1])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb2], [1], [],
+[ovn-nbctl: Multiple load balancers named 'lb2'.  Use a UUID.
+])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3])
+
+AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb0                 tcp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb1                 udp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<2>    lb3                 tcp/udp    ae0f::10         fd0f::10,fd0f::20
+])
+
+AT_CHECK([ovn-nbctl lr-lb-del lr0 lb0])
+AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID                                    LB                  PROTO      VIP              IPs
+<0>    lb1                 udp        [[ae0f::10]]:80    [[fd0f::10]]:80,[[fd0f::20]]:80
+<1>    lb3                 tcp/udp    ae0f::10         fd0f::10,fd0f::20
+])
+
+AT_CHECK([ovn-nbctl lr-lb-del lr0 lb1])
+AT_CHECK([ovn-nbctl lr-lb-del lr0 lb3])
+AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [])
+AT_CHECK([ovn-nbctl --if-exists lr-lb-del lr0 lb1])
+
+dnl Remove all load balancers from logical router.
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb0])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb1])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb3])
+AT_CHECK([ovn-nbctl lr-lb-del lr0])
+AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [])
+
+OVN_NBCTL_TEST_STOP
+AT_CLEANUP
 dnl ---------------------------------------------------------------------
 
 AT_SETUP([ovn-nbctl - basic logical router commands])