From patchwork Wed Sep 28 17:13:14 2016
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
X-Patchwork-Submitter: Gurucharan Shetty
X-Patchwork-Id: 676318
Return-Path:
X-Original-To: incoming@patchwork.ozlabs.org
Delivered-To: patchwork-incoming@bilbo.ozlabs.org
Received: from archives.nicira.com (archives.nicira.com [96.126.127.54])
by ozlabs.org (Postfix) with ESMTP id 3skkn96CDVz9s5w
for ;
Thu, 29 Sep 2016 03:13:33 +1000 (AEST)
Received: from archives.nicira.com (localhost [127.0.0.1])
by archives.nicira.com (Postfix) with ESMTP id C6FA81062D;
Wed, 28 Sep 2016 10:13:30 -0700 (PDT)
X-Original-To: dev@openvswitch.org
Delivered-To: dev@openvswitch.org
Received: from mx1e4.cudamail.com (mx1.cudamail.com [69.90.118.67])
by archives.nicira.com (Postfix) with ESMTPS id C848810203
for ; Wed, 28 Sep 2016 10:13:29 -0700 (PDT)
Received: from bar5.cudamail.com (unknown [192.168.21.12])
by mx1e4.cudamail.com (Postfix) with ESMTPS id 1170E1E02B5
for ; Wed, 28 Sep 2016 11:13:29 -0600 (MDT)
X-ASG-Debug-ID: 1475082807-09eadd519f12b360001-byXFYA
Received: from mx1-pf1.cudamail.com ([192.168.24.1]) by bar5.cudamail.com
with ESMTP id Pu4E1pslqjKNvH9s (version=TLSv1.2
cipher=ECDHE-RSA-AES256-GCM-SHA384
bits=256 verify=NO) for ;
Wed, 28 Sep 2016 11:13:28 -0600 (MDT)
X-Barracuda-Envelope-From: guru@ovn.org
X-Barracuda-RBL-Trusted-Forwarder: 192.168.24.1
Received: from unknown (HELO relay8-d.mail.gandi.net) (217.70.183.201)
by mx1-pf1.cudamail.com with ESMTPS (DHE-RSA-AES256-SHA encrypted);
28 Sep 2016 17:13:27 -0000
Received-SPF: pass (mx1-pf1.cudamail.com: SPF record at ovn.org designates
217.70.183.201 as permitted sender)
X-Barracuda-Apparent-Source-IP: 217.70.183.201
X-Barracuda-RBL-IP: 217.70.183.201
Received: from mfilter6-d.gandi.net (mfilter6-d.gandi.net [217.70.178.135])
by relay8-d.mail.gandi.net (Postfix) with ESMTP id AE09F405A2
for ; Wed, 28 Sep 2016 19:13:24 +0200 (CEST)
X-Virus-Scanned: Debian amavisd-new at mfilter6-d.gandi.net
Received: from relay8-d.mail.gandi.net ([IPv6:::ffff:217.70.183.201])
by mfilter6-d.gandi.net (mfilter6-d.gandi.net [::ffff:10.0.15.180])
(amavisd-new, port 10024)
with ESMTP id Wrk4S5eKdMh3 for ;
Wed, 28 Sep 2016 19:13:17 +0200 (CEST)
X-Originating-IP: 209.85.215.48
Received: from mail-lf0-f48.google.com (mail-lf0-f48.google.com
[209.85.215.48]) (Authenticated sender: guru@ovn.org)
by relay8-d.mail.gandi.net (Postfix) with ESMTPSA id F21F1405A1
for ; Wed, 28 Sep 2016 19:13:16 +0200 (CEST)
Received: by mail-lf0-f48.google.com with SMTP id g62so59654526lfe.3
for ; Wed, 28 Sep 2016 10:13:16 -0700 (PDT)
X-Gm-Message-State:
AE9vXwOxq8EkokiEJM8dEzwxyjOvo/D20fr7xthE5b0EraF+hPxQerPLZ67UyNQGv3pZL0UdLaVukE10x67QeA==
X-Received: by 10.25.18.157 with SMTP id 29mr10926030lfs.10.1475082795460;
Wed, 28 Sep 2016 10:13:15 -0700 (PDT)
MIME-Version: 1.0
Received: by 10.114.200.5 with HTTP; Wed, 28 Sep 2016 10:13:14 -0700 (PDT)
In-Reply-To:
<1474463641-75314-1-git-send-email-nickcooper-zhangtonghao@opencloud.tech>
References:
<1474463641-75314-1-git-send-email-nickcooper-zhangtonghao@opencloud.tech>
X-CudaMail-Envelope-Sender: guru@ovn.org
From: Guru Shetty
Date: Wed, 28 Sep 2016 10:13:14 -0700
X-Gmail-Original-Message-ID:
Message-ID:
X-CudaMail-Whitelist-To: dev@openvswitch.org
X-CudaMail-MID: CM-E1-927048193
X-CudaMail-DTE: 092816
X-CudaMail-Originating-IP: 217.70.183.201
To: nickcooper-zhangtonghao
X-ASG-Orig-Subj: [##CM-E1-927048193##]Re: [PATCH v8] ovn-nbctl: Add LB
commands.
X-Barracuda-Connect: UNKNOWN[192.168.24.1]
X-Barracuda-Start-Time: 1475082808
X-Barracuda-Encrypted: ECDHE-RSA-AES256-GCM-SHA384
X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi
X-ASG-Whitelist: Header =?UTF-8?B?eFwtY3VkYW1haWxcLXdoaXRlbGlzdFwtdG8=?=
X-Barracuda-BRTS-Status: 1
X-Virus-Scanned: by bsmtpd at cudamail.com
X-Content-Filtered-By: Mailman/MimeDel 2.1.16
Cc: ovs dev
Subject: Re: [ovs-dev] [PATCH v8] ovn-nbctl: Add LB commands.
X-BeenThere: dev@openvswitch.org
X-Mailman-Version: 2.1.16
Precedence: list
List-Id:
List-Unsubscribe: ,
List-Archive:
List-Post:
List-Help:
List-Subscribe: ,
Errors-To: dev-bounces@openvswitch.org
Sender: "dev"
On 21 September 2016 at 06:14, nickcooper-zhangtonghao <
nickcooper-zhangtonghao@opencloud.tech> wrote:
> This patch provides the command line to create a load balancer.
> You can create a load balancer independently and add it to multiple
> switches or routers. A single load balancer can have multiple vips.
> Add a name column for the load balancer. With --add-duplicate,
> the command really creates a new load balancer with a duplicate name.
> This name has no special meaning or purpose other than to provide
> convenience for human interaction with the ovn-nb database.
> This patch also provides the unit tests and the documentation.
>
> Signed-off-by: nickcooper-zhangtonghao opencloud.tech>
>
Thank you for the extensive unit tests. This looks good to me. I think
there is a bit of duplicate code
in nbctl_lb_list_router, nbctl_lb_list_switch and nbctl_lb_list_all that
could use a helper function. Would you mind looking at it?
On that lines, do you think the following command extensions are useful:
ls-lb-list switch lb
lr-lb-list router lb
We are filtering the load-balancers in a switch and a router by 'lb'.
Potentially, one could just call lb-list lb instead? I don't have strong
feelings either way.
There were a few lines with more than 78 characters and some alignment
issues. Please add the following incremental.
node->key, node->value);
}
@@ -1581,11 +1582,12 @@ nbctl_lb_list_switch(struct ctl_context *ctx,
struct smap *lbs,
protocol = "tcp/udp";
}
- i == 0 ? ds_put_format(&val, UUID_FMT "
%-20.16s%-11.7s%-25.21s%s",
+ i == 0 ? ds_put_format(&val,
+ UUID_FMT " %-20.16s%-11.7s%-25.21s%s",
UUID_ARGS(&lb->header_.uuid),
lb->name, protocol,
node->key, node->value)
- : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
+ : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
"", protocol,
node->key, node->value);
}
@@ -1630,11 +1632,12 @@ nbctl_lb_list_all(struct ctl_context *ctx, struct
smap *lbs,
protocol = "tcp/udp";
}
- i == 0 ? ds_put_format(&val, UUID_FMT "
%-20.16s%-11.7s%-25.21s%s",
+ i == 0 ? ds_put_format(&val,
+ UUID_FMT " %-20.16s%-11.7s%-25.21s%s",
UUID_ARGS(&lb->header_.uuid),
lb->name, protocol,
node->key, node->value)
- : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
+ : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
"", protocol,
node->key, node->value);
> ---
> lib/packets.c | 17 ++
> lib/packets.h | 10 +
> ovn/ovn-nb.ovsschema | 5 +-
> ovn/ovn-nb.xml | 6 +
> ovn/utilities/ovn-nbctl.8.xml | 109 ++++++++
> ovn/utilities/ovn-nbctl.c | 588 ++++++++++++++++++++++++++++++
> +++++++++++-
> tests/ovn-nbctl.at | 228 ++++++++++++++++
> 7 files changed, 960 insertions(+), 3 deletions(-)
>
> diff --git a/lib/packets.c b/lib/packets.c
> index e4c29d5..a8b1654 100644
> --- a/lib/packets.c
> +++ b/lib/packets.c
> @@ -427,6 +427,23 @@ ip_parse(const char *s, ovs_be32 *ip)
> return inet_pton(AF_INET, s, ip) == 1;
> }
>
> +/* Parses string 's', which must be an IP address with a port number.
> + * Stores the IP address into '*ip' and port number to '*port'. */
> +char * OVS_WARN_UNUSED_RESULT
> +ip_parse_port(const char *s, ovs_be32 *ip, ovs_be16 *port)
> +{
> + int n = 0;
> + if (!ovs_scan_len(s, &n, IP_PORT_SCAN_FMT,
> + IP_PORT_SCAN_ARGS(ip, port))) {
> + return xasprintf("%s: invalid IP address or port number", s);
> + }
> +
> + if (s[n]) {
> + return xasprintf("%s: invalid IP address or port number", s);
> + }
> + return NULL;
> +}
> +
> /* Parses string 's', which must be an IP address with an optional
> netmask or
> * CIDR prefix length. Stores the IP address into '*ip', netmask into
> '*mask',
> * (255.255.255.255, if 's' lacks a netmask), and number of scanned
> characters
> diff --git a/lib/packets.h b/lib/packets.h
> index dcfcd04..21bd35c 100644
> --- a/lib/packets.h
> +++ b/lib/packets.h
> @@ -537,6 +537,14 @@ mpls_lse_to_bos(ovs_be32 mpls_lse)
> &((uint8_t *) ip)[2], \
> &((uint8_t *) ip)[3]
>
> +#define IP_PORT_SCAN_FMT "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8":%"SCNu16
> +#define IP_PORT_SCAN_ARGS(ip, port) \
> + ((void) (ovs_be32) *(ip), &((uint8_t *) ip)[0]), \
> + &((uint8_t *) ip)[1], \
> + &((uint8_t *) ip)[2], \
> + &((uint8_t *) ip)[3], \
> + ((void) (ovs_be16) *(port), (uint16_t *) port)
> +
> /* Returns true if 'netmask' is a CIDR netmask, that is, if it consists
> of N
> * high-order 1-bits and 32-N low-order 0-bits. */
> static inline bool
> @@ -558,6 +566,8 @@ ip_is_local_multicast(ovs_be32 ip)
> int ip_count_cidr_bits(ovs_be32 netmask);
> void ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *);
> bool ip_parse(const char *s, ovs_be32 *ip);
> +char *ip_parse_port(const char *s, ovs_be32 *ip, ovs_be16 *port)
> + OVS_WARN_UNUSED_RESULT;
> char *ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
> OVS_WARN_UNUSED_RESULT;
> char *ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
> diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
> index b7e70aa..5f2f2bf 100644
> --- a/ovn/ovn-nb.ovsschema
> +++ b/ovn/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
> {
> "name": "OVN_Northbound",
> - "version": "5.3.3",
> - "cksum": "2442952958 9945",
> + "version": "5.3.4",
> + "cksum": "1155817817 9975",
> "tables": {
> "NB_Global": {
> "columns": {
> @@ -97,6 +97,7 @@
> "isRoot": true},
> "Load_Balancer": {
> "columns": {
> + "name": {"type": "string"},
> "vips": {
> "type": {"key": "string", "value": "string",
> "min": 0, "max": "unlimited"}},
> diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
> index c45a444..b7690d0 100644
> --- a/ovn/ovn-nb.xml
> +++ b/ovn/ovn-nb.xml
> @@ -676,6 +676,12 @@
> Each row represents one load balancer.
>
>
> +
> + A name for the load balancer. This name has no special meaning or
> + purpose other than to provide convenience for human interaction with
> + the ovn-nb database.
> +
> +
>
>
> A map of virtual IPv4 addresses (and an optional port number with
> diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
> index 76cf97e..991ecea 100644
> --- a/ovn/utilities/ovn-nbctl.8.xml
> +++ b/ovn/utilities/ovn-nbctl.8.xml
> @@ -400,6 +400,115 @@
>
>
>
> +
Load Balancer Commands
> +
> + - [
--may-exist
| --add-duplicate
]
> lb-add
lb vip ips
> [protocol]
> + -
> +
> + Creates a new load balancer named lb with the provided
> + vip and ips or adds the vip to
> + an existing lb. vip should be a
> + virtual IPv4 address (or an IPv4 address and a port number with
> + :
as a separator). Examples for vip are
> + 192.168.1.4
and 192.168.1.5:8080
.
> + ips should be comma separated IPv4 endpoints (or comma
> + separated IPv4 addresses and port numbers with :
as
> a
> + separator). Examples for ips are
> 10.0.0.1,10.0.0.2
> +
or 20.0.0.10:8800,20.0.0.11:8800
.
> +
> +
> +
> + The optional argument protocol must be either
> + tcp
or udp
. This argument is useful
> when
> + a port number is provided as part of the vip. If the
> + protocol is unspecified and a port number is provided
> as
> + part of the vip, OVN assumes the protocol
> to
> + be tcp
.
> +
> +
> +
> + It is an error if the vip already exists in the load
> + balancer named lb, unless --may-exist
is
> + specified. With --add-duplicate
, the command really
> + creates a new load balancer with a duplicate name.
> +
> +
> +
> + The following example adds a load balancer.
> +
> +
> +
> + lb-add lb0 30.0.0.10:80
> + 192.168.10.10:80,192.168.10.20:80,192.168.10.30:80 udp
> +
> +
> +
> + - [
--if-exists
] lb-del
lb
> [vip]
> + -
> + Deletes lb or the vip from lb.
> + If vip is supplied, only the vip will be
> + deleted from the lb. If only the lb is
> supplied,
> + the lb will be deleted. It is an error if
> vip
> + does not already exist in lb, unless
> +
--if-exists
is specified.
> +
> +
> + lb-list
[lb]
> + -
> + Lists the LBs. If lb is also specified, then only the
> + specified lb will be listed.
> +
> +
> + - [
--may-exist
] ls-lb-add
> switch lb
> + -
> + Adds the specified lb to switch.
> + It is an error if a load balancer named lb already
> exists
> + in the switch, unless
--may-exist
is
> specified.
> +
> +
> + - [
--if-exists
] ls-lb-del
> switch [lb]
> + -
> + Removes lb from switch. If only
> + switch is supplied, all the LBs from the logical
> switch are
> + removed. If lb is also specified, then only the
> + lb will be removed from the logical switch.
> + It is an error if lb does not exist in the
> + switch, unless
--if-exists
is specified.
> +
> +
> + ls-lb-list
switch [lb]
> + -
> + Lists the LBs for the given switch. If lb
> is
> + also specified, only the lb will be listed from the
> logical
> + switch.
> +
> +
> + - [
--may-exist
] lr-lb-add
> router lb
> + -
> + Adds the specified lb to router.
> + It is an error if a load balancer named lb already
> exists
> + in the router, unless
--may-exist
is
> specified.
> +
> +
> + - [
--if-exists
] lr-lb-del
> router [lb]
> + -
> + Removes lb from router. If only
> + router is supplied, all the LBs from the logical
> router are
> + removed. If lb is also specified, then only the
> + lb will be removed from the logical router.
> + It is an error if lb does not exist in the
> + router, unless
--if-exists
is specified.
> +
> +
> + lr-lb-list
router [lb]
> + -
> + Lists the LBs for the given router. If lb
> is
> + also specified, then only the lb will be listed from
> the
> + logical router.
> +
> +
> +
> +
> DHCP Options commands
>
>
> diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
> index 2148665..a252902 100644
> --- a/ovn/utilities/ovn-nbctl.c
> +++ b/ovn/utilities/ovn-nbctl.c
> @@ -384,6 +384,19 @@ Route commands:\n\
> remove routes from ROUTER\n\
> lr-route-list ROUTER print routes for ROUTER\n\
> \n\
> +LB commands:\n\
> + lb-add LB VIP[:PORT] IP[:PORT]... [PROTOCOL]\n\
> + create a load-balancer or add a VIP to an\n\
> + existing load balancer\n\
> + lb-del LB [VIP] remove a load-balancer or just the VIP from\n\
> + the load balancer\n\
> + lb-list [LB] print load-balancers\n\
> + lr-lb-add ROUTER LB add a load-balancer to ROUTER\n\
> + lr-lb-del ROUTER [LB] remove load-balancers from ROUTER\n\
> + lr-lb-list ROUTER [LB] print load-balancers\n\
> + ls-lb-add SWITCH LB add a load-balancer to SWITCH\n\
> + ls-lb-del SWITCH [LB] remove load-balancers from SWITCH\n\
> + ls-lb-list SWITCH [LB] print load-balancers\n\
> \n\
> DHCP Options commands:\n\
> dhcp-options-create CIDR [EXTERNAL_IDS]\n\
> @@ -493,6 +506,40 @@ ls_by_name_or_uuid(struct ctl_context *ctx, const
> char *id, bool must_exist)
> return ls;
> }
>
> +static const struct nbrec_load_balancer *
> +lb_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool
> must_exist)
> +{
> + const struct nbrec_load_balancer *lb = NULL;
> +
> + struct uuid lb_uuid;
> + bool is_uuid = uuid_from_string(&lb_uuid, id);
> + if (is_uuid) {
> + lb = nbrec_load_balancer_get_for_uuid(ctx->idl, &lb_uuid);
> + }
> +
> + if (!lb) {
> + const struct nbrec_load_balancer *iter;
> +
> + NBREC_LOAD_BALANCER_FOR_EACH(iter, ctx->idl) {
> + if (strcmp(iter->name, id)) {
> + continue;
> + }
> + if (lb) {
> + ctl_fatal("Multiple load balancers named '%s'. "
> + "Use a UUID.", id);
> + }
> + lb = iter;
> + }
> + }
> +
> + if (!lb && must_exist) {
> + ctl_fatal("%s: load balancer %s not found", id,
> + is_uuid ? "UUID" : "name");
> + }
> +
> + return lb;
> +}
> +
> /* Given pointer to logical router, this routine prints the router
> * information. */
> static void
> @@ -1316,7 +1363,527 @@ nbctl_acl_del(struct ctl_context *ctx)
> }
> }
> }
> -
> +
> +static void
> +nbctl_lb_add(struct ctl_context *ctx)
> +{
> + const char *lb_name = ctx->argv[1];
> + const char *lb_vip = ctx->argv[2];
> + const char *lb_ips = ctx->argv[3];
> +
> + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
> + bool add_duplicate = shash_find(&ctx->options, "--add-duplicate") !=
> NULL;
> +
> + const char *lb_proto;
> + bool is_update_proto = false;
> + bool is_vip_with_port = true;
> +
> + if (ctx->argc == 4) {
> + /* Default protocol. */
> + lb_proto = "tcp";
> + } else {
> + /* Validate protocol. */
> + lb_proto = ctx->argv[4];
> + is_update_proto = true;
> + if (strcmp(lb_proto, "tcp") && strcmp(lb_proto, "udp")) {
> + ctl_fatal("%s: protocol must be one of \"tcp\", \"udp\".",
> + lb_proto);
> + }
> + }
> +
> + ovs_be32 ipv4 = 0;
> + ovs_be16 port = 0;
> + char *error = ip_parse_port(lb_vip, &ipv4, &port);
> + 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);
> + }
> +
> + if (is_update_proto) {
> + ctl_fatal("Protocol is unnecessary when no port of vip is
> given.");
> + }
> + is_vip_with_port = false;
> + }
> +
> + char *token = NULL, *save_ptr = NULL;
> + char *ips = xstrdup(lb_ips);
> + for (token = strtok_r(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);
> + ctl_fatal("%s: should be an IPv4 address and a port "
> + "number with : as a separator.", token);
> + }
> + } else {
> + if (!ip_parse(token, &ipv4)) {
> + ctl_fatal("%s: should be an IPv4 address.", token);
> + }
> + }
> + }
> +
> + free(ips);
> +
> + const struct nbrec_load_balancer *lb = NULL;
> + if (!add_duplicate) {
> + lb = lb_by_name_or_uuid(ctx, lb_name, false);
> + if (lb) {
> + if (smap_get(&lb->vips, lb_vip)) {
> + if (!may_exist) {
> + ctl_fatal("%s: a load balancer with this vip (%s)
> already "
> + "exists", lb_name, lb_vip);
> + }
> + /* Update the vips. */
> + smap_replace(CONST_CAST(struct smap *, &lb->vips), lb_vip,
> + lb_ips);
> + } else {
> + /* Add the new vips. */
> + smap_add(CONST_CAST(struct smap *, &lb->vips), lb_vip,
> lb_ips);
> + }
> +
> + /* Update the load balancer. */
> + if (is_update_proto) {
> + nbrec_load_balancer_verify_protocol(lb);
> + nbrec_load_balancer_set_protocol(lb, lb_proto);
> + }
> + nbrec_load_balancer_verify_vips(lb);
> + nbrec_load_balancer_set_vips(lb, &lb->vips);
> + return;
> + }
> + }
> +
> + /* Create the load balancer. */
> + lb = nbrec_load_balancer_insert(ctx->txn);
> + 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, lb_ips);
> + nbrec_load_balancer_set_vips(lb, &lb->vips);
> +}
> +
> +static void
> +nbctl_lb_del(struct ctl_context *ctx)
> +{
> + const char *id = ctx->argv[1];
> + const struct nbrec_load_balancer *lb = NULL;
> + bool must_exist = !shash_find(&ctx->options, "--if-exists");
> +
> + lb = lb_by_name_or_uuid(ctx, id, false);
> + if (!lb) {
> + return;
> + }
> +
> + if (ctx->argc == 3) {
> + const char *lb_vip = ctx->argv[2];
> + if (smap_get(&lb->vips, lb_vip)) {
> + smap_remove(CONST_CAST(struct smap * ,&lb->vips), lb_vip);
> + if (smap_is_empty(&lb->vips)) {
> + nbrec_load_balancer_delete(lb);
> + return;
> + }
> +
> + /* Delete the vip of the load balancer. */
> + nbrec_load_balancer_verify_vips(lb);
> + nbrec_load_balancer_set_vips(lb, &lb->vips);
> + return;
> + }
> + if (must_exist) {
> + ctl_fatal("vip %s is not part of the load balancer.",
> + lb_vip);
> + }
> + return;
> + }
> + nbrec_load_balancer_delete(lb);
> +}
> +
> +static const struct smap_node **
> +nbctl_lb_list_router(struct ctl_context *ctx, struct smap *lbs,
> + const char *lr_name, const char *lb_name, bool
> lb_check)
> +{
> + const struct nbrec_logical_router *lr;
> + ovs_be32 ipv4 = 0;
> + ovs_be16 port = 0;
> + char *error, *protocol;
> +
> + 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];
> + if (lb_check && strcmp(lb->name, lb_name)) {
> + continue;
> + }
> +
> + const struct smap_node **nodes = smap_sort(&lb->vips);
> + if (nodes) {
> + struct ds key = DS_EMPTY_INITIALIZER;
> + struct ds val = DS_EMPTY_INITIALIZER;
> + 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);
> + if (error) {
> + free(error);
> + protocol = "tcp/udp";
> + }
> +
> + i == 0 ? ds_put_format(&val, UUID_FMT "
> %-20.16s%-11.7s%-25.21s%s",
> + UUID_ARGS(&lb->header_.uuid),
> + lb->name, protocol,
> + node->key, node->value)
> + : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
> + "", protocol,
> + node->key, node->value);
> + }
> +
> + ds_put_format(&key, "%-20.16s", lb->name);
> + smap_add(lbs, ds_cstr(&key), ds_cstr(&val));
> +
> + ds_destroy(&key);
> + ds_destroy(&val);
> + free(nodes);
> + }
> + }
> +
> + return smap_sort(lbs);
> +}
> +
> +static const struct smap_node **
> +nbctl_lb_list_switch(struct ctl_context *ctx, struct smap *lbs,
> + const char *ls_name, const char *lb_name, bool
> lb_check)
> +{
> + const struct nbrec_logical_switch *ls;
> + ovs_be32 ipv4 = 0;
> + ovs_be16 port = 0;
> + char *error, *protocol;
> +
> + 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];
> + if (lb_check && strcmp(lb->name, lb_name)) {
> + continue;
> + }
> +
> + const struct smap_node **nodes = smap_sort(&lb->vips);
> + if (nodes) {
> + struct ds key = DS_EMPTY_INITIALIZER;
> + struct ds val = DS_EMPTY_INITIALIZER;
> + 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);
> + if (error) {
> + free(error);
> + protocol = "tcp/udp";
> + }
> +
> + i == 0 ? ds_put_format(&val, UUID_FMT "
> %-20.16s%-11.7s%-25.21s%s",
> + UUID_ARGS(&lb->header_.uuid),
> + lb->name, protocol,
> + node->key, node->value)
> + : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
> + "", protocol,
> + node->key, node->value);
> + }
> +
> + ds_put_format(&key, "%-20.16s", lb->name);
> + smap_add(lbs, ds_cstr(&key), ds_cstr(&val));
> +
> + ds_destroy(&key);
> + ds_destroy(&val);
> + free(nodes);
> + }
> + }
> +
> + return smap_sort(lbs);
> +}
> +
> +static const struct smap_node **
> +nbctl_lb_list_all(struct ctl_context *ctx, struct smap *lbs,
> + const char *lb_name, bool lb_check) {
> +
> + const struct nbrec_load_balancer *lb;
> + ovs_be32 ipv4 = 0;
> + ovs_be16 port = 0;
> + char *error, *protocol;
> +
> + NBREC_LOAD_BALANCER_FOR_EACH(lb, ctx->idl) {
> +
> + if (lb_check && strcmp(lb->name, lb_name)) {
> + continue;
> + }
> +
> + const struct smap_node **nodes = smap_sort(&lb->vips);
> + if (nodes) {
> + struct ds key = DS_EMPTY_INITIALIZER;
> + struct ds val = DS_EMPTY_INITIALIZER;
> + 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);
> + if (error) {
> + free(error);
> + protocol = "tcp/udp";
> + }
> +
> + i == 0 ? ds_put_format(&val, UUID_FMT "
> %-20.16s%-11.7s%-25.21s%s",
> + UUID_ARGS(&lb->header_.uuid),
> + lb->name, protocol,
> + node->key, node->value)
> + : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
> + "", protocol,
> + node->key, node->value);
> + }
> +
> + ds_put_format(&key, "%-20.16s", lb->name);
> + smap_add(lbs, ds_cstr(&key), ds_cstr(&val));
> +
> + ds_destroy(&key);
> + ds_destroy(&val);
> + free(nodes);
> + }
> + }
> +
> + return smap_sort(lbs);
> +}
> +
> +static void
> +nbctl_lb_list(struct ctl_context *ctx)
> +{
> + struct smap lbs = SMAP_INITIALIZER(&lbs);
> + const struct smap_node **nodes = NULL;
> +
> + if (ctx->argc == 1) {
> + nodes = nbctl_lb_list_all(ctx, &lbs, NULL, false);
> + } else if (ctx->argc == 2) {
> + nodes = nbctl_lb_list_all(ctx, &lbs, ctx->argv[1], true);
> + }
> +
> + if (nodes) {
> + ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-25.
> 21s%s\n",
> + "UUID", "LB", "PROTO", "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);
> + }
> +
> + smap_destroy(&lbs);
> + free(nodes);
> + }
> +}
> +
> +static void
> +nbctl_lr_lb_add(struct ctl_context *ctx)
> +{
> + const struct nbrec_logical_router *lr;
> + const struct nbrec_load_balancer *new_lb;
> +
> + lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
> + new_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true);
> +
> + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
> + for (int i = 0; i < lr->n_load_balancer; i++) {
> + const struct nbrec_load_balancer *lb
> + = lr->load_balancer[i];
> +
> + if (uuid_equals(&new_lb->header_.uuid, &lb->header_.uuid)) {
> + if (may_exist) {
> + return;
> + }
> + ctl_fatal(UUID_FMT " : a load balancer with this UUID already
> "
> + "exists", UUID_ARGS(&lb->header_.uuid));
> + }
> + }
> +
> + /* Insert the load balancer into the logical router. */
> + nbrec_logical_router_verify_load_balancer(lr);
> + struct nbrec_load_balancer **new_lbs
> + = xmalloc(sizeof *new_lbs * (lr->n_load_balancer + 1));
> +
> + memcpy(new_lbs, lr->load_balancer, sizeof *new_lbs *
> lr->n_load_balancer);
> + new_lbs[lr->n_load_balancer] = CONST_CAST(struct nbrec_load_balancer
> *,
> + new_lb);
> + nbrec_logical_router_set_load_balancer(lr, new_lbs,
> + lr->n_load_balancer + 1);
> + free(new_lbs);
> +}
> +
> +static void
> +nbctl_lr_lb_del(struct ctl_context *ctx)
> +{
> + const struct nbrec_logical_router *lr;
> + const struct nbrec_load_balancer *del_lb;
> + lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true);
> +
> + if (ctx->argc == 2) {
> + /* If load-balancer is not specified, remove
> + * all load-balancers from the logical router. */
> + nbrec_logical_router_verify_load_balancer(lr);
> + nbrec_logical_router_set_load_balancer(lr, NULL, 0);
> + return;
> + }
> +
> + del_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true);
> + for (size_t i = 0; i < lr->n_load_balancer; i++) {
> + const struct nbrec_load_balancer *lb
> + = lr->load_balancer[i];
> +
> + if (uuid_equals(&del_lb->header_.uuid, &lb->header_.uuid)) {
> + /* Remove the matching rule. */
> + nbrec_logical_router_verify_load_balancer(lr);
> +
> + struct nbrec_load_balancer **new_lbs
> + = xmemdup(lr->load_balancer,
> + sizeof *new_lbs * lr->n_load_balancer);
> + new_lbs[i] = lr->load_balancer[lr->n_load_balancer - 1];
> + nbrec_logical_router_set_load_balancer(lr, new_lbs,
> + lr->n_load_balancer - 1);
> + free(new_lbs);
> + return;
> + }
> + }
> +
> + bool must_exist = !shash_find(&ctx->options, "--if-exists");
> + if (must_exist) {
> + ctl_fatal("load balancer %s is not part of any logical router.",
> + del_lb->name);
> + }
> +}
> +
> +static void
> +nbctl_lr_lb_list(struct ctl_context *ctx)
> +{
> + struct smap lbs = SMAP_INITIALIZER(&lbs);
> + const struct smap_node **nodes = NULL;
> +
> + if (ctx->argc == 2) {
> + nodes = nbctl_lb_list_router(ctx, &lbs, ctx->argv[1], NULL,
> false);
> + } else {
> + nodes = nbctl_lb_list_router(ctx, &lbs, ctx->argv[1],
> ctx->argv[2],
> + true);
> + }
> +
> + if (nodes) {
> + ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-25.
> 21s%s\n",
> + "UUID", "LB", "PROTO", "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);
> + }
> +
> + smap_destroy(&lbs);
> + free(nodes);
> + }
> +}
> +
> +static void
> +nbctl_ls_lb_add(struct ctl_context *ctx)
> +{
> + const struct nbrec_logical_switch *ls;
> + const struct nbrec_load_balancer *new_lb;
> +
> + ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
> + new_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true);
> +
> + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
> + for (int i = 0; i < ls->n_load_balancer; i++) {
> + const struct nbrec_load_balancer *lb
> + = ls->load_balancer[i];
> +
> + if (uuid_equals(&new_lb->header_.uuid, &lb->header_.uuid)) {
> + if (may_exist) {
> + return;
> + }
> + ctl_fatal(UUID_FMT " : a load balancer with this UUID already
> "
> + "exists", UUID_ARGS(&lb->header_.uuid));
> + }
> + }
> +
> + /* Insert the load balancer into the logical switch. */
> + nbrec_logical_switch_verify_load_balancer(ls);
> + struct nbrec_load_balancer **new_lbs
> + = xmalloc(sizeof *new_lbs * (ls->n_load_balancer + 1));
> +
> + memcpy(new_lbs, ls->load_balancer, sizeof *new_lbs *
> ls->n_load_balancer);
> + new_lbs[ls->n_load_balancer] = CONST_CAST(struct nbrec_load_balancer
> *,
> + new_lb);
> + nbrec_logical_switch_set_load_balancer(ls, new_lbs,
> + ls->n_load_balancer + 1);
> + free(new_lbs);
> +}
> +
> +static void
> +nbctl_ls_lb_del(struct ctl_context *ctx)
> +{
> + const struct nbrec_logical_switch *ls;
> + const struct nbrec_load_balancer *del_lb;
> + ls = ls_by_name_or_uuid(ctx, ctx->argv[1], true);
> +
> + if (ctx->argc == 2) {
> + /* If load-balancer is not specified, remove
> + * all load-balancers from the logical switch. */
> + nbrec_logical_switch_verify_load_balancer(ls);
> + nbrec_logical_switch_set_load_balancer(ls, NULL, 0);
> + return;
> + }
> +
> + del_lb = lb_by_name_or_uuid(ctx, ctx->argv[2], true);
> + for (size_t i = 0; i < ls->n_load_balancer; i++) {
> + const struct nbrec_load_balancer *lb
> + = ls->load_balancer[i];
> +
> + if (uuid_equals(&del_lb->header_.uuid, &lb->header_.uuid)) {
> + /* Remove the matching rule. */
> + nbrec_logical_switch_verify_load_balancer(ls);
> +
> + struct nbrec_load_balancer **new_lbs
> + = xmemdup(ls->load_balancer,
> + sizeof *new_lbs * ls->n_load_balancer);
> + new_lbs[i] = ls->load_balancer[ls->n_load_balancer - 1];
> + nbrec_logical_switch_set_load_balancer(ls, new_lbs,
> + ls->n_load_balancer - 1);
> + free(new_lbs);
> + return;
> + }
> + }
> +
> + bool must_exist = !shash_find(&ctx->options, "--if-exists");
> + if (must_exist) {
> + ctl_fatal("load balancer %s is not part of any logical switch.",
> + del_lb->name);
> + }
> +}
> +
> +static void
> +nbctl_ls_lb_list(struct ctl_context *ctx)
> +{
> + struct smap lbs = SMAP_INITIALIZER(&lbs);
> + const struct smap_node **nodes = NULL;
> +
> + if (ctx->argc == 2) {
> + nodes = nbctl_lb_list_switch(ctx, &lbs, ctx->argv[1], NULL,
> false);
> + } else {
> + nodes = nbctl_lb_list_switch(ctx, &lbs, ctx->argv[1],
> ctx->argv[2],
> + true);
> + }
> +
> + if (nodes) {
> + ds_put_format(&ctx->output, "%-40.36s%-20.16s%-11.7s%-25.
> 21s%s\n",
> + "UUID", "LB", "PROTO", "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);
> + }
> +
> + smap_destroy(&lbs);
> + free(nodes);
> + }
> +}
> +
> static void
> nbctl_lr_add(struct ctl_context *ctx)
> {
> @@ -2480,6 +3047,25 @@ static const struct ctl_command_syntax
> nbctl_commands[] = {
> { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL,
> "", RO },
>
> + /* load balancer commands. */
> + { "lb-add", 3, 4, "LB VIP[:PORT] IP[:PORT]... [PROTOCOL]", NULL,
> + nbctl_lb_add, NULL, "--may-exist,--add-duplicate", RW },
> + { "lb-del", 1, 2, "LB [VIP]", NULL, nbctl_lb_del, NULL,
> + "--if-exists", RW },
> + { "lb-list", 0, 1, "[LB]", NULL, nbctl_lb_list, NULL, "", RO },
> + { "lr-lb-add", 2, 2, "ROUTER LB", NULL, nbctl_lr_lb_add, NULL,
> + "--may-exist", RW },
> + { "lr-lb-del", 1, 2, "ROUTER [LB]", NULL, nbctl_lr_lb_del, NULL,
> + "--if-exists", RW },
> + { "lr-lb-list", 1, 2, "ROUTER [LB]", NULL, nbctl_lr_lb_list, NULL,
> + "", RO },
> + { "ls-lb-add", 2, 2, "SWITCH LB", NULL, nbctl_ls_lb_add, NULL,
> + "--may-exist", RW },
> + { "ls-lb-del", 1, 2, "SWITCH [LB]", NULL, nbctl_ls_lb_del, NULL,
> + "--if-exists", RW },
> + { "ls-lb-list", 1, 2, "SWITCH [LB]", NULL, nbctl_ls_lb_list, NULL,
> + "", RO },
> +
> /* DHCP_Options commands */
> {"dhcp-options-create", 1, INT_MAX, "CIDR [EXTERNAL:IDS]", NULL,
> nbctl_dhcp_options_create, NULL, "", RW },
> diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at
> index 241e6d3..6bd4aaf 100644
> --- a/tests/ovn-nbctl.at
> +++ b/tests/ovn-nbctl.at
> @@ -239,6 +239,234 @@ AT_CLEANUP
>
> dnl ---------------------------------------------------------------------
>
> +AT_SETUP([ovn-nbctl - LBs])
> +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).
> +])
> +
> +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).
> +])
> +
> +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).
> +])
> +
> +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.
> +])
> +
> +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).
> +])
> +
> +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).
> +])
> +
> +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.
> +])
> +
> +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.
> +])
> +
> +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.
> +])
> +
> +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.
> +])
> +
> +AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10 tcp], [1], [],
> +[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
> +])
> +
> +AT_CHECK([ovn-nbctl lb-add lb0 30.0.0.10 192.168.10.10:900 tcp], [1], [],
> +[ovn-nbctl: Protocol is unnecessary when no port of vip is given.
> +])
> +
> +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
> +])
> +
> +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
> +])
> +
> +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
> +])
> +
> +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
> +])
> +
> +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
> +])
> +
> +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
> +])
> +AT_CHECK([ovn-nbctl lb-del lb2 30.0.0.30])
> +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
> +])
> +
> +dnl If there are multiple load balancers with the same name, use a UUID
> to update/delete.
> +AT_CHECK([ovn-nbctl lb-add lb2 30.0.0.10:8080 192.168.10.10:80,
> 192.168.10.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 30.0.0.10:80
> 192.168.10.10:8080,192.168.10.20:8080 udp])
> +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:8080
> 192.168.10.10:8080,192.168.10.20:8080 udp])
> +AT_CHECK([ovn-nbctl --may-exist lb-add lb1 30.0.0.10:9090
> 192.168.10.10:8080,192.168.10.20:8080 udp])
> +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
> +])
> +
> +dnl Add load balancer to logical switch.
> +AT_CHECK([ovn-nbctl ls-add ls0])
> +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 udp])
> +AT_CHECK([ovn-nbctl lb-add lb3 30.0.0.10 192.168.10.10,192.168.10.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 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-list ls0 lb0 | ${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
> +])
> +
> +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
> +])
> +
> +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 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-list lr0 lb0 | ${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
> +])
> +
> +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
> +])
> +
> +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])
> OVN_NBCTL_TEST_START
>
> --
> 1.8.3.1
>
>
>
>
>
diff --git a/lib/packets.c b/lib/packets.c
index a8b1654..11bb587 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -427,7 +427,8 @@ ip_parse(const char *s, ovs_be32 *ip)
return inet_pton(AF_INET, s, ip) == 1;
}
-/* Parses string 's', which must be an IP address with a port number.
+/* Parses string 's', which must be an IP address with a port number
+ * with ":" as a separator (e.g.: 192.168.1.2:80).
* Stores the IP address into '*ip' and port number to '*port'. */
char * OVS_WARN_UNUSED_RESULT
ip_parse_port(const char *s, ovs_be32 *ip, ovs_be16 *port)
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index a252902..26c32c5 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -1478,7 +1478,7 @@ nbctl_lb_del(struct ctl_context *ctx)
if (ctx->argc == 3) {
const char *lb_vip = ctx->argv[2];
if (smap_get(&lb->vips, lb_vip)) {
- smap_remove(CONST_CAST(struct smap * ,&lb->vips), lb_vip);
+ smap_remove(CONST_CAST(struct smap *, &lb->vips), lb_vip);
if (smap_is_empty(&lb->vips)) {
nbrec_load_balancer_delete(lb);
return;
@@ -1529,11 +1529,12 @@ nbctl_lb_list_router(struct ctl_context *ctx,
struct smap *lbs,
protocol = "tcp/udp";
}
- i == 0 ? ds_put_format(&val, UUID_FMT "
%-20.16s%-11.7s%-25.21s%s",
+ i == 0 ? ds_put_format(&val,
+ UUID_FMT " %-20.16s%-11.7s%-25.21s%s",
UUID_ARGS(&lb->header_.uuid),
lb->name, protocol,
node->key, node->value)
- : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
+ : ds_put_format(&val, "\n%60s%-11.7s%-25.21s%s",
"", protocol,