[ovs-dev] [PATCH v6] ovn-nbctl: Add LB commands.

Guru Shetty guru at ovn.org
Mon Sep 19 14:40:43 UTC 2016


On 19 September 2016 at 05:16, nickcooper-zhangtonghao <
nickcooper-zhangtonghao at 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 <nickcooper-zhangtonghao@
> opencloud.tech>
>

I get the following compilation error:

ovn/utilities/ovn-nbctl.c: In function ‘nbctl_lb_list_router’:
ovn/utilities/ovn-nbctl.c:1493:13: error: format not a string literal and
no format arguments [-Werror=format-security]
             smap_add_format(lbs, ds_cstr(&key), ds_cstr(&val));
             ^
ovn/utilities/ovn-nbctl.c: In function ‘nbctl_lb_list_switch’:
ovn/utilities/ovn-nbctl.c:1540:13: error: format not a string literal and
no format arguments [-Werror=format-security]
             smap_add_format(lbs, ds_cstr(&key), ds_cstr(&val));
             ^
ovn/utilities/ovn-nbctl.c: In function ‘nbctl_lb_list_all’:
ovn/utilities/ovn-nbctl.c:1584:13: error: format not a string literal and
no format arguments [-Werror=format-security]
             smap_add_format(lbs, ds_cstr(&key), ds_cstr(&val));
             ^
cc1: all warnings being treated as errors
make[2]: *** [ovn/utilities/ovn-nbctl.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[2]: Leaving directory `/root/git/openvswitch'
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory `/root/git/openvswitch'
make: *** [all] Error 2

Can you please add to the unit test the scenario where we have multiple
vips in a single load balancer and lb-list it.. Also add the case wherein
the vip is just an IP address. Also, please update the version number in
schema to 5.3.4 (the rationale is explained in 'man ovs-vswitchd.conf.db'.
Search for 'db_version'.)


---
>  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
>
>
>
> _______________________________________________
> dev mailing list
> dev at openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
>



More information about the dev mailing list