[ovs-dev] [PATCH v6] ovn-nbctl: Add LB commands.
nickcooper-zhangtonghao
nickcooper-zhangtonghao at opencloud.tech
Mon Sep 19 12:16:12 UTC 2016
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 <nickcooper-zhangtonghao at opencloud.tech>
---
ovn/ovn-nb.ovsschema | 3 +-
ovn/ovn-nb.xml | 6 +
ovn/utilities/ovn-nbctl.8.xml | 109 +++++++++
ovn/utilities/ovn-nbctl.c | 514 +++++++++++++++++++++++++++++++++++++++++-
tests/ovn-nbctl.at | 147 ++++++++++++
5 files changed, 777 insertions(+), 2 deletions(-)
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index b7e70aa..7772ad2 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",
+ "cksum": "1191768021 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 9a8bdbd..2ebae29 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -676,6 +676,12 @@
Each row represents one load balancer.
</p>
+ <column name="name">
+ 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.
+ </column>
+
<column name="vips">
<p>
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..bdccc23 100644
--- a/ovn/utilities/ovn-nbctl.8.xml
+++ b/ovn/utilities/ovn-nbctl.8.xml
@@ -400,6 +400,115 @@
</dd>
</dl>
+ <h1>Load Balancer Commands</h1>
+ <dl>
+ <dt>[<code>--may-exist</code> | <code>--add-duplicate</code>] <code>lb-add</code> <var>lb</var> <var>vip</var> <var>ips</var> [<var>protocol</var>]</dt>
+ <dd>
+ <p>
+ Creates a new load balancer named <var>lb</var> with the provided
+ <var>vip</var> and <var>ips</var> or adds the <var>vip</var> to
+ an existing <var>lb</var>. <var>vip</var> should be a
+ virtual IPv4 address (or an IPv4 address and a port number with
+ <code>:</code> as a separator). Examples for <var>vip</var> are
+ <code>192.168.1.4</code> and <code>192.168.1.5:8080</code>.
+ <var>ips</var> should be comma separated IPv4 endpoints (or comma
+ separated IPv4 addresses and port numbers with <code>:</code> as a
+ separator). Examples for <var>ips</var> are <code>10.0.0.1,10.0.0.2
+ </code>or <code>20.0.0.10:8800,20.0.0.11:8800</code>.
+ </p>
+
+ <p>
+ The optional argument <var>protocol</var> must be either
+ <code>tcp</code> or <code>udp</code>. This argument is useful when
+ a port number is provided as part of the <var>vip</var>. If the
+ <var>protocol</var> is unspecified and a port number is provided as
+ part of the <var>vip</var>, OVN assumes the <var>protocol</var> to
+ be <code>tcp</code>.
+ </p>
+
+ <p>
+ It is an error if the <var>vip</var> already exists in the load
+ balancer named <var>lb</var>, unless <code>--may-exist</code> is
+ specified. With <code>--add-duplicate</code>, the command really
+ creates a new load balancer with a duplicate name.
+ </p>
+
+ <p>
+ The following example adds a load balancer.
+ </p>
+
+ <p>
+ <code>lb-add lb0 30.0.0.10:80
+ 192.168.10.10:80,192.168.10.20:80,192.168.10.30:80 udp</code>
+ </p>
+ </dd>
+
+ <dt>[<code>--if-exists</code>] <code>lb-del</code> <var>lb</var> [<var>vip</var>]</dt>
+ <dd>
+ Deletes <var>lb</var> or the <var>vip</var> from <var>lb</var>.
+ If <var>vip</var> is supplied, only the <var>vip</var> will be
+ deleted from the <var>lb</var>. If only the <var>lb</var> is supplied,
+ the <var>lb</var> will be deleted. It is an error if <var>vip</var>
+ does not already exist in <var>lb</var>, unless
+ <code>--if-exists</code> is specified.
+ </dd>
+
+ <dt><code>lb-list</code> [<var>lb</var>]</dt>
+ <dd>
+ Lists the LBs. If <var>lb</var> is also specified, then only the
+ specified <var>lb</var> will be listed.
+ </dd>
+
+ <dt>[<code>--may-exist</code>] <code>ls-lb-add</code> <var>switch</var> <var>lb</var></dt>
+ <dd>
+ Adds the specified <var>lb</var> to <var>switch</var>.
+ It is an error if a load balancer named <var>lb</var> already exists
+ in the <var>switch</var>, unless <code>--may-exist</code> is specified.
+ </dd>
+
+ <dt>[<code>--if-exists</code>] <code>ls-lb-del</code> <var>switch</var> [<var>lb</var>]</dt>
+ <dd>
+ Removes <var>lb</var> from <var>switch</var>. If only
+ <var>switch</var> is supplied, all the LBs from the logical switch are
+ removed. If <var>lb</var> is also specified, then only the
+ <var>lb</var> will be removed from the logical switch.
+ It is an error if <var>lb</var> does not exist in the
+ <var>switch</var>, unless <code>--if-exists</code> is specified.
+ </dd>
+
+ <dt><code>ls-lb-list</code> <var>switch</var> [<var>lb</var>]</dt>
+ <dd>
+ Lists the LBs. If <var>switch</var> is supplied, all the LBs from
+ the logical switch are listed. If <var>lb</var> is also specified,
+ only the <var>lb</var> will be listed from the logical switch.
+ </dd>
+
+ <dt>[<code>--may-exist</code>] <code>lr-lb-add</code> <var>router</var> <var>lb</var></dt>
+ <dd>
+ Adds the specified <var>lb</var> to <var>router</var>.
+ It is an error if a load balancer named <var>lb</var> already exists
+ in the <var>router</var>, unless <code>--may-exist</code> is specified.
+ </dd>
+
+ <dt>[<code>--if-exists</code>] <code>lr-lb-del</code> <var>router</var> [<var>lb</var>]</dt>
+ <dd>
+ Removes <var>lb</var> from <var>router</var>. If only
+ <var>router</var> is supplied, all the LBs from the logical router are
+ removed. If <var>lb</var> is also specified, then only the
+ <var>lb</var> will be removed from the logical router.
+ It is an error if <var>lb</var> does not exist in the
+ <var>router</var>, unless <code>--if-exists</code> is specified.
+ </dd>
+
+ <dt><code>lr-lb-list</code> <var>router</var> [<var>lb</var>]</dt>
+ <dd>
+ Lists the LBs. If <var>router</var> is supplied, all the LBs from
+ the logical router are listed. If <var>lb</var> is also specified,
+ then only the <var>lb</var> will be listed from the logical router.
+ </dd>
+ </dl>
+
+
<h1>DHCP Options commands</h1>
<dl>
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index 2148665..f47070f 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -384,6 +384,17 @@ 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\
+ add a load-balancer or VIP to load balancer\n\
+ lb-del LB [VIP] remove a load-balancer or VIP from 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 +504,39 @@ 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 +1360,463 @@ 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;
+ 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);
+ }
+ }
+
+ 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;
+ 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];
+ if (i == 0) {
+ ds_put_format(&val, UUID_FMT " %-10.8s %-8s %-20s %s",
+ UUID_ARGS(&lb->header_.uuid),
+ lb->name, lb->protocol,
+ node->key,
+ node->value);
+ } else {
+ ds_put_format(&val,"\n%49s %-8s %-20s %s",
+ "",
+ lb->protocol,
+ node->key,
+ node->value);
+ }
+ }
+
+ ds_put_format(&key, "%-10.8s", lb->name);
+ smap_add_format(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;
+ 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];
+ if (i == 0) {
+ ds_put_format(&val, UUID_FMT " %-10.8s %-8s %-20s %s",
+ UUID_ARGS(&lb->header_.uuid),
+ lb->name, lb->protocol,
+ node->key,
+ node->value);
+ } else {
+ ds_put_format(&val,"\n%49s %-8s %-20s %s",
+ "",
+ lb->protocol,
+ node->key,
+ node->value);
+ }
+ }
+
+ ds_put_format(&key, "%-10.8s", lb->name);
+ smap_add_format(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;
+ 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];
+ if (i == 0) {
+ ds_put_format(&val, UUID_FMT " %-10.8s %-8s %-20s %s",
+ UUID_ARGS(&lb->header_.uuid),
+ lb->name, lb->protocol,
+ node->key,
+ node->value);
+ } else {
+ ds_put_format(&val,"\n%49s %-8s %-20s %s",
+ "",
+ lb->protocol,
+ node->key,
+ node->value);
+ }
+ }
+
+ ds_put_format(&key, "%-10.8s", lb->name);
+ smap_add_format(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, "%-36s %-10.8s %-8s %-20s %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, "%-36s %-10.8s %-8s %-20s %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, "%-36s %-10.8s %-8s %-20s %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 +2980,18 @@ 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..9508e70 100644
--- a/tests/ovn-nbctl.at
+++ b/tests/ovn-nbctl.at
@@ -239,6 +239,153 @@ 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: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:8080,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:8080,192.168.10.20:8080
+])
+
+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 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:8080,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,192.168.10.20:80 udp])
+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:8080,192.168.10.20:8080
+])
+
+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:8080,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 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-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
+])
+
+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
+])
+
+AT_CHECK([ovn-nbctl ls-lb-del ls0 lb1])
+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-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 lb-add lb00 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80])
+AT_CHECK([ovn-nbctl lb-add lb01 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80 udp])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb00])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb01])
+AT_CHECK([ovn-nbctl --may-exist lr-lb-add lr0 lb01])
+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-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID LB PROTO VIP IPs
+<0> lb00 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
+<1> lb01 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
+])
+
+AT_CHECK([ovn-nbctl lr-lb-list lr0 lb00 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID LB PROTO VIP IPs
+<0> lb00 tcp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
+])
+
+AT_CHECK([ovn-nbctl lr-lb-del lr0 lb00])
+AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [dnl
+UUID LB PROTO VIP IPs
+<0> lb01 udp 30.0.0.10:80 192.168.10.10:80,192.168.10.20:80
+])
+
+AT_CHECK([ovn-nbctl lr-lb-del lr0 lb01])
+AT_CHECK([ovn-nbctl lr-lb-list lr0 | ${PERL} $srcdir/uuidfilt.pl], [0], [])
+AT_CHECK([ovn-nbctl --if-exists lr-lb-del lr0 lb01])
+
+dnl Remove all load balancers from logical router.
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb00])
+AT_CHECK([ovn-nbctl lr-lb-add lr0 lb01])
+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
More information about the dev
mailing list