From patchwork Tue Oct 11 14:36:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: nickcooper-zhangtonghao X-Patchwork-Id: 680778 X-Patchwork-Delegate: guru@ovn.org 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 3stfhh3Ln3z9s9Y for ; Wed, 12 Oct 2016 01:37:08 +1100 (AEDT) Received: from archives.nicira.com (localhost [127.0.0.1]) by archives.nicira.com (Postfix) with ESMTP id A3442102BE; Tue, 11 Oct 2016 07:37:00 -0700 (PDT) X-Original-To: dev@openvswitch.org Delivered-To: dev@openvswitch.org Received: from mx3v3.cudamail.com (mx3.cudamail.com [64.34.241.5]) by archives.nicira.com (Postfix) with ESMTPS id DFEA0102AD for ; Tue, 11 Oct 2016 07:36:58 -0700 (PDT) Received: from bar6.cudamail.com (localhost [127.0.0.1]) by mx3v3.cudamail.com (Postfix) with ESMTPS id 753D216835B for ; Tue, 11 Oct 2016 08:36:58 -0600 (MDT) X-ASG-Debug-ID: 1476196614-0b3237218928750001-byXFYA Received: from mx1-pf1.cudamail.com ([192.168.24.1]) by bar6.cudamail.com with ESMTP id WgJd0Can31nfmhIj (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NO) for ; Tue, 11 Oct 2016 08:36:54 -0600 (MDT) X-Barracuda-Envelope-From: nickcooper-zhangtonghao@opencloud.tech X-Barracuda-RBL-Trusted-Forwarder: 192.168.24.1 Received: from unknown (HELO smtpbgsg2.qq.com) (54.254.200.128) by mx1-pf1.cudamail.com with ESMTPS (DHE-RSA-AES256-SHA encrypted); 11 Oct 2016 14:36:53 -0000 Received-SPF: none (mx1-pf1.cudamail.com: domain at opencloud.tech does not designate permitted sender hosts) X-Barracuda-Apparent-Source-IP: 54.254.200.128 X-Barracuda-RBL-IP: 54.254.200.128 X-QQ-mid: bizesmtp9t1476196602tei0uxlgn Received: from local.opencloud.tech.localdomai (unknown [117.100.104.154]) by esmtp4.qq.com (ESMTP) with id ; Tue, 11 Oct 2016 22:36:36 +0800 (CST) X-QQ-SSF: 01300000002000F0FGF0B00A0000000 X-QQ-FEAT: Tp2hW+Mew+f89S+lmYmic4zmX2Q0zSWNakFDfRJk0vr2RGEdQPn4woKxTTkUK ag9e5fCS5f418+vbvHVlQIRNdSFgVW/uPU3aOpKwJh+Y3qalcSgbSfrTRoU01OuAcSFKH4y ZSeUwYaaCEfW2wGQlE/P2nl+YKHSbfklYQgCpXYojvp6G6bGKveClqZ3UAkobWxJIlM3N7L itp+j8RwaZSFlvwLRHXXgNVAAob0Jj3HUxLZdWiS9s3dxJKEUk5XZK3k/+Ee7p0umlURU9j vnWA== X-QQ-GoodBg: 0 X-CudaMail-Envelope-Sender: nickcooper-zhangtonghao@opencloud.tech From: nickcooper-zhangtonghao To: dev@openvswitch.org X-CudaMail-MID: CM-E1-1010033268 X-CudaMail-DTE: 101116 X-CudaMail-Originating-IP: 54.254.200.128 Date: Tue, 11 Oct 2016 07:36:24 -0700 X-ASG-Orig-Subj: [##CM-E1-1010033268##][PATCH 1/2] ovn-nbctl: Add NAT commands. Message-Id: <1476196585-80069-1-git-send-email-nickcooper-zhangtonghao@opencloud.tech> X-Mailer: git-send-email 1.8.3.1 X-QQ-SENDSIZE: 520 X-QQ-Bgrelay: 1 X-Barracuda-Connect: UNKNOWN[192.168.24.1] X-Barracuda-Start-Time: 1476196614 X-Barracuda-Encrypted: ECDHE-RSA-AES256-GCM-SHA384 X-Barracuda-URL: https://web.cudamail.com:443/cgi-mod/mark.cgi X-Virus-Scanned: by bsmtpd at cudamail.com X-Barracuda-BRTS-Status: 1 X-Barracuda-Spam-Score: 1.10 X-Barracuda-Spam-Status: No, SCORE=1.10 using global scores of TAG_LEVEL=3.5 QUARANTINE_LEVEL=1000.0 KILL_LEVEL=4.0 tests=BSF_SC0_MV0713, BSF_SC5_MJ1963, RDNS_NONE X-Barracuda-Spam-Report: Code version 3.2, rules version 3.2.3.33611 Rule breakdown below pts rule name description ---- ---------------------- -------------------------------------------------- 0.10 RDNS_NONE Delivered to trusted network by a host with no rDNS 0.50 BSF_SC0_MV0713 Custom rule MV0713 0.50 BSF_SC5_MJ1963 Custom Rule MJ1963 Cc: nickcooper-zhangtonghao Subject: [ovs-dev] [PATCH 1/2] ovn-nbctl: Add NAT 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: , MIME-Version: 1.0 Errors-To: dev-bounces@openvswitch.org Sender: "dev" This patch provides the command line to create NAT rules on logical router. Signed-off-by: nickcooper-zhangtonghao --- ovn/utilities/ovn-nbctl.8.xml | 66 +++++++++++++++ ovn/utilities/ovn-nbctl.c | 185 ++++++++++++++++++++++++++++++++++++++++++ tests/ovn-nbctl.at | 114 ++++++++++++++++++++++++++ 3 files changed, 365 insertions(+) diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml index 2cbd6e0..be802da 100644 --- a/ovn/utilities/ovn-nbctl.8.xml +++ b/ovn/utilities/ovn-nbctl.8.xml @@ -424,6 +424,72 @@ +

NAT Commands

+ +
+
[--may-exist] lr-nat-add router type external_ip logical_ip
+
+

+ Adds the specified NAT to router. + The type must be one of snat, + dnat, or dnat_and_snat. + The external_ip is an IPv4 address. + The logical_ip is an IPv4 network (e.g 192.168.1.0/24) + or an IPv4 address. +

+

+ When type is dnat, the externally + visible IP address external_ip is DNATted to the + IP address logical_ip in the logical space. +

+

+ When type is snat, IP packets with their + source IP address that either matches the IP address in + logical_ip or is in the network provided by + logical_ip is SNATed into the IP address in + external_ip. +

+

+ When type is dnat_and_snat, + the externally visible IP address external_ip + is DNATted to the IP address logical_ip in + the logical space. In addition, IP packets with the source + IP address that matches logical_ip is SNATed into + the IP address in external_ip. +

+

+ It is an error if a NAT already exists, + unless --may-exist is specified. +

+
+ +
[--if-exists] lr-nat-del router [type [ip]]
+
+

+ Deletes NATs from router. If only router + is supplied, all the NATs from the logical router are + deleted. If type is also specified, then all the + NATs that match the type will be deleted from the logical + router. If all the fields are given, then a single NAT rule + that matches all the fields will be deleted. When type + is snat, the ip should be logical_ip. + When type is dnat or + dnat_and_snat, the ip shoud be external_ip. +

+ +

+ It is an error if ip is specified and there + is no matching NAT entry, unless --if-exists is + specified. +

+
+ +
lr-nat-list router
+
+ Lists the NATs on router. +
+
+

Load Balancer Commands

[--may-exist | --add-duplicate] lb-add lb vip ips [protocol]
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c index ad2d2f8..916dedb 100644 --- a/ovn/utilities/ovn-nbctl.c +++ b/ovn/utilities/ovn-nbctl.c @@ -384,6 +384,13 @@ Route commands:\n\ remove routes from ROUTER\n\ lr-route-list ROUTER print routes for ROUTER\n\ \n\ +NAT commands:\n\ + lr-nat-add ROUTER TYPE EXTERNAL_IP LOGICAL_IP\n\ + add a NAT to ROUTER\n\ + lr-nat-del ROUTER [TYPE [IP]]\n\ + remove NATs from ROUTER\n\ + lr-nat-list ROUTER print NATs 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\ @@ -2165,6 +2172,177 @@ nbctl_lr_route_del(struct ctl_context *ctx) } free(prefix); } + +static void +nbctl_lr_nat_add(struct ctl_context *ctx) +{ + const struct nbrec_logical_router *lr; + const char *nat_type = ctx->argv[2]; + const char *external_ip = ctx->argv[3]; + const char *logical_ip = ctx->argv[4]; + char *new_logical_ip = NULL; + + lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); + + if (strcmp(nat_type, "dnat") && strcmp(nat_type, "snat") + && strcmp(nat_type, "dnat_and_snat")) { + ctl_fatal("%s: type must be one of \"dnat\", \"snat\" and " + "\"dnat_and_snat\".", nat_type); + } + + ovs_be32 ipv4 = 0; + unsigned int plen; + if (!ip_parse(external_ip, &ipv4)) { + ctl_fatal("%s: should be an IPv4 address.", external_ip); + } + + if (strcmp("snat", nat_type)) { + if (!ip_parse(logical_ip, &ipv4)) { + ctl_fatal("%s: should be an IPv4 address.", logical_ip); + } + new_logical_ip = xstrdup(logical_ip); + } else { + char *error = ip_parse_cidr(logical_ip, &ipv4, &plen); + if (error) { + free(error); + ctl_fatal("%s: should be an IPv4 address or network.", + logical_ip); + } + new_logical_ip = normalize_ipv4_prefix(ipv4, plen); + } + + bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL; + int is_snat = !strcmp("snat", nat_type); + for (size_t i = 0; i < lr->n_nat; i++) { + const struct nbrec_nat *nat = lr->nat[i]; + if (!strcmp(nat_type, nat->type)) { + if (!strcmp(is_snat ? new_logical_ip : external_ip, + is_snat ? nat->logical_ip : nat->external_ip)) { + if (!strcmp(is_snat ? external_ip : new_logical_ip, + is_snat ? nat->external_ip : nat->logical_ip)) { + if (may_exist) { + free(new_logical_ip); + return; + } + ctl_fatal("%s, %s: a NAT with this external_ip and " + "logical_ip already exists", + external_ip, new_logical_ip); + } else { + ctl_fatal("a NAT with this type (%s) and %s (%s) " + "already exists", + nat_type, + is_snat ? "logical_ip" : "external_ip", + is_snat ? new_logical_ip : external_ip); + } + } + } + } + + /* Create the NAT. */ + struct nbrec_nat *nat = nbrec_nat_insert(ctx->txn); + nbrec_nat_set_type(nat, nat_type); + nbrec_nat_set_external_ip(nat, external_ip); + nbrec_nat_set_logical_ip(nat, new_logical_ip); + free(new_logical_ip); + + /* Insert the NAT into the logical router. */ + nbrec_logical_router_verify_nat(lr); + struct nbrec_nat **new_nats = xmalloc(sizeof *new_nats * (lr->n_nat + 1)); + memcpy(new_nats, lr->nat, sizeof *new_nats * lr->n_nat); + new_nats[lr->n_nat] = nat; + nbrec_logical_router_set_nat(lr, new_nats, lr->n_nat + 1); + free(new_nats); +} + +static void +nbctl_lr_nat_del(struct ctl_context *ctx) +{ + const struct nbrec_logical_router *lr; + bool must_exist = !shash_find(&ctx->options, "--if-exists"); + lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); + + if (ctx->argc == 2) { + /* If type, external_ip and logical_ip are not specified, delete + * all NATs. */ + nbrec_logical_router_verify_nat(lr); + nbrec_logical_router_set_nat(lr, NULL, 0); + return; + } + + const char *nat_type = ctx->argv[2]; + if (strcmp(nat_type, "dnat") && strcmp(nat_type, "snat") + && strcmp(nat_type, "dnat_and_snat")) { + ctl_fatal("%s: type must be one of \"dnat\", \"snat\" and " + "\"dnat_and_snat\".", nat_type); + } + + if (ctx->argc == 3) { + /*Deletes all NATs with the specified type. */ + struct nbrec_nat **new_nats = xmalloc(sizeof *new_nats * lr->n_nat); + int n_nat = 0; + for (size_t i = 0; i < lr->n_nat; i++) { + if (strcmp(nat_type, lr->nat[i]->type)) { + new_nats[n_nat++] = lr->nat[i]; + } + } + + nbrec_logical_router_verify_nat(lr); + nbrec_logical_router_set_nat(lr, new_nats, n_nat); + free(new_nats); + return; + } + + const char *nat_ip = ctx->argv[3]; + int is_snat = !strcmp("snat", nat_type); + /* Remove the matching NAT. */ + for (size_t i = 0; i < lr->n_nat; i++) { + struct nbrec_nat *nat = lr->nat[i]; + if (!strcmp(nat_type, nat->type) && + !strcmp(nat_ip, is_snat ? nat->logical_ip : nat->external_ip)) { + struct nbrec_nat **new_nats + = xmemdup(lr->nat, sizeof *new_nats * lr->n_nat); + new_nats[i] = lr->nat[lr->n_nat - 1]; + nbrec_logical_router_verify_nat(lr); + nbrec_logical_router_set_nat(lr, new_nats, + lr->n_nat - 1); + free(new_nats); + return; + } + } + + if (must_exist) { + ctl_fatal("no matching NAT with the type (%s) and %s (%s)", + nat_type, is_snat ? "logical_ip" : "external_ip", nat_ip); + } +} + +static void +nbctl_lr_nat_list(struct ctl_context *ctx) +{ + const struct nbrec_logical_router *lr; + lr = lr_by_name_or_uuid(ctx, ctx->argv[1], true); + + struct smap lr_nats = SMAP_INITIALIZER(&lr_nats); + for (size_t i = 0; i < lr->n_nat; i++) { + const struct nbrec_nat *nat = lr->nat[i]; + smap_add_format(&lr_nats, nat->type, "%-19.15s%s", + nat->external_ip, nat->logical_ip); + } + + const struct smap_node **nodes = smap_sort(&lr_nats); + if (nodes) { + ds_put_format(&ctx->output, "%-17.13s%-19.15s%s\n", + "TYPE", "EXTERNAL_IP", "LOGICAL_IP"); + for (size_t i = 0; i < smap_count(&lr_nats); i++) { + const struct smap_node *node = nodes[i]; + ds_put_format(&ctx->output, "%-17.13s%s\n", + node->key, node->value); + } + free(nodes); + } + smap_destroy(&lr_nats); +} + static const struct nbrec_logical_router_port * lrp_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist) @@ -2954,6 +3132,13 @@ static const struct ctl_command_syntax nbctl_commands[] = { { "lr-route-list", 1, 1, "ROUTER", NULL, nbctl_lr_route_list, NULL, "", RO }, + /* NAT commands. */ + { "lr-nat-add", 4, 4, "ROUTER TYPE EXTERNAL_IP LOGICAL_IP", NULL, + nbctl_lr_nat_add, NULL, "--may-exist", RW }, + { "lr-nat-del", 1, 3, "ROUTER [TYPE [IP]]", NULL, + nbctl_lr_nat_del, NULL, "--if-exists", RW }, + { "lr-nat-list", 1, 1, "ROUTER", NULL, nbctl_lr_nat_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 }, diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at index af00dad..96269f8 100644 --- a/tests/ovn-nbctl.at +++ b/tests/ovn-nbctl.at @@ -238,6 +238,120 @@ OVN_NBCTL_TEST_STOP AT_CLEANUP dnl --------------------------------------------------------------------- +AT_SETUP([ovn-nbctl - NATs]) +OVN_NBCTL_TEST_START +AT_CHECK([ovn-nbctl lr-add lr0]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], [1], [], +[ovn-nbctl: snatt: type must be one of "dnat", "snat" and "dnat_and_snat". +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2a 192.168.1.2], [1], [], +[ovn-nbctl: 30.0.0.2a: should be an IPv4 address. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0 192.168.1.2], [1], [], +[ovn-nbctl: 30.0.0: should be an IPv4 address. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2/24 192.168.1.2], [1], [], +[ovn-nbctl: 30.0.0.2/24: should be an IPv4 address. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2:80 192.168.1.2], [1], [], +[ovn-nbctl: 30.0.0.2:80: should be an IPv4 address. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2a], [1], [], +[ovn-nbctl: 192.168.1.2a: should be an IPv4 address or network. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1], [1], [], +[ovn-nbctl: 192.168.1: should be an IPv4 address or network. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2:80], [1], [], +[ovn-nbctl: 192.168.1.2:80: should be an IPv4 address or network. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.2/a], [1], [], +[ovn-nbctl: 192.168.1.2/a: should be an IPv4 address or network. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2a], [1], [], +[ovn-nbctl: 192.168.1.2a: should be an IPv4 address. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1], [1], [], +[ovn-nbctl: 192.168.1: should be an IPv4 address. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2:80], [1], [], +[ovn-nbctl: 192.168.1.2:80: should be an IPv4 address. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.2 192.168.1.2/24], [1], [], +[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address. +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.2 192.168.1.2/24], [1], [], +[ovn-nbctl: 192.168.1.2/24: should be an IPv4 address. +]) + +dnl Add snat and dnat +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2]) +AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl +TYPE EXTERNAL_IP LOGICAL_IP +dnat 30.0.0.1 192.168.1.2 +dnat_and_snat 30.0.0.1 192.168.1.2 +snat 30.0.0.1 192.168.1.0/24 +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24], [1], [], +[ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and logical_ip already exists +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.1 192.168.1.10/24], [1], [], +[ovn-nbctl: 30.0.0.1, 192.168.1.0/24: a NAT with this external_ip and logical_ip already exists +]) +AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 snat 30.0.0.1 192.168.1.0/24]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 snat 30.0.0.2 192.168.1.0/24], [1], [], +[ovn-nbctl: a NAT with this type (snat) and logical_ip (192.168.1.0/24) already exists +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2], [1], [], +[ovn-nbctl: 30.0.0.1, 192.168.1.2: a NAT with this external_ip and logical_ip already exists +]) +AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat 30.0.0.1 192.168.1.2]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat 30.0.0.1 192.168.1.3], [1], [], +[ovn-nbctl: a NAT with this type (dnat) and external_ip (30.0.0.1) already exists +]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2], [1], [], +[ovn-nbctl: 30.0.0.1, 192.168.1.2: a NAT with this external_ip and logical_ip already exists +]) +AT_CHECK([ovn-nbctl --may-exist lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.2]) +AT_CHECK([ovn-nbctl lr-nat-add lr0 dnat_and_snat 30.0.0.1 192.168.1.3], [1], [], +[ovn-nbctl: a NAT with this type (dnat_and_snat) and external_ip (30.0.0.1) already exists +]) + +dnl Deletes the NATs +AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.2], [1], [], +[ovn-nbctl: no matching NAT with the type (dnat_and_snat) and external_ip (30.0.0.2) +]) +AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat 30.0.0.2], [1], [], +[ovn-nbctl: no matching NAT with the type (dnat) and external_ip (30.0.0.2) +]) +AT_CHECK([ovn-nbctl lr-nat-del lr0 snat 192.168.10.0/24], [1], [], +[ovn-nbctl: no matching NAT with the type (snat) and logical_ip (192.168.10.0/24) +]) +AT_CHECK([ovn-nbctl --if-exists lr-nat-del lr0 snat 192.168.10.0/24]) + +AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat_and_snat 30.0.0.1]) +AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl +TYPE EXTERNAL_IP LOGICAL_IP +dnat 30.0.0.1 192.168.1.2 +snat 30.0.0.1 192.168.1.0/24 +]) + +AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat]) +AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], [dnl +TYPE EXTERNAL_IP LOGICAL_IP +snat 30.0.0.1 192.168.1.0/24 +]) + +AT_CHECK([ovn-nbctl lr-nat-del lr0]) +AT_CHECK([ovn-nbctl lr-nat-list lr0], [0], []) +AT_CHECK([ovn-nbctl lr-nat-del lr0]) +AT_CHECK([ovn-nbctl lr-nat-del lr0 dnat]) +OVN_NBCTL_TEST_STOP +AT_CLEANUP + +dnl --------------------------------------------------------------------- AT_SETUP([ovn-nbctl - LBs]) OVN_NBCTL_TEST_START