From patchwork Tue Jan 12 21:09:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1425493 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=fraxinus.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=Jh7af/bC; dkim-atps=neutral Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4DFmPS3z47z9sWj for ; Wed, 13 Jan 2021 10:05:00 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 86D2885F4E; Tue, 12 Jan 2021 21:10:19 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ClKBWsraMqhx; Tue, 12 Jan 2021 21:10:11 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by fraxinus.osuosl.org (Postfix) with ESMTP id A047685D9B; Tue, 12 Jan 2021 21:10:11 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7A7AFC1E6F; Tue, 12 Jan 2021 21:10:11 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from whitealder.osuosl.org (smtp1.osuosl.org [140.211.166.138]) by lists.linuxfoundation.org (Postfix) with ESMTP id 36518C013A for ; Tue, 12 Jan 2021 21:10:10 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by whitealder.osuosl.org (Postfix) with ESMTP id 22F6685D26 for ; Tue, 12 Jan 2021 21:10:10 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from whitealder.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id GqbNaHoTtzxA for ; Tue, 12 Jan 2021 21:10:08 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by whitealder.osuosl.org (Postfix) with ESMTPS id 1D90C85CF0 for ; Tue, 12 Jan 2021 21:10:07 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610485806; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=yfXZli7SNp1YUfoxB6MqLZQKDXU9aVpNW7JCv3mdSk4=; b=Jh7af/bC9Jc5FXwdmZvK8OEJXPd3UXnYpszo+Id9AkSkW+rQxyJqEoP3TUrJMJeKvPcs2+ NLE7hs7AF9yz1SUlC/obWE/U4QIGEQRWbvQbh42s1FpJNDGn/9f9/CiWn9hR1C/TYkSQuu 5CaxpJXxbFUySdiVSCxn6MRUM7R0Cww= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-209-QziyYOgvNWW2_TLWrPTlwA-1; Tue, 12 Jan 2021 16:10:04 -0500 X-MC-Unique: QziyYOgvNWW2_TLWrPTlwA-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 84A621015942 for ; Tue, 12 Jan 2021 21:10:03 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-113-53.rdu2.redhat.com [10.10.113.53]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9A8816F96B for ; Tue, 12 Jan 2021 21:10:02 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Tue, 12 Jan 2021 16:09:57 -0500 Message-Id: <20210112210959.2151790-2-mmichels@redhat.com> In-Reply-To: <20210112210959.2151790-1-mmichels@redhat.com> References: <20210112210959.2151790-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v7 1/3] northd: refactor and split some IPAM functions X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" This refactor focuses on three efforts: * Break a large function into smaller functions * Pass more specific data types to functions * Isolate these functions in their own code unit Smaller functions have clearer purposes, have fewer chances of unwanted side effects, and are easier to test. The next commit in this series will add some unit tests that exercise the new functions created in this commit. Note that this is not a full refactor of IPAM. Dynamic address assignment is complicated and currently tightly coupled with datapath and port constructs. A refactor of that section of IPAM is difficult enough that it should have its own patch series separate from this one that focuses mostly on unit tests. Signed-off-by: Mark Michelson --- northd/automake.mk | 5 +- northd/ipam.c | 335 ++++++++++++++++++++++++++++++++++++++++++++ northd/ipam.h | 39 ++++++ northd/ovn-northd.c | 291 ++++---------------------------------- 4 files changed, 403 insertions(+), 267 deletions(-) create mode 100644 northd/ipam.c create mode 100644 northd/ipam.h diff --git a/northd/automake.mk b/northd/automake.mk index 69657e77e..3340178f5 100644 --- a/northd/automake.mk +++ b/northd/automake.mk @@ -1,6 +1,9 @@ # ovn-northd bin_PROGRAMS += northd/ovn-northd -northd_ovn_northd_SOURCES = northd/ovn-northd.c +northd_ovn_northd_SOURCES = \ + northd/ovn-northd.c \ + northd/ipam.c \ + northd/ipam.h northd_ovn_northd_LDADD = \ lib/libovn.la \ $(OVSDB_LIBDIR)/libovsdb.la \ diff --git a/northd/ipam.c b/northd/ipam.c new file mode 100644 index 000000000..c393ba53c --- /dev/null +++ b/northd/ipam.c @@ -0,0 +1,335 @@ +#include + +#include +#include +#include +#include + +#include "ipam.h" +#include "ovn/lex.h" + +#include "smap.h" +#include "packets.h" +#include "bitmap.h" +#include "openvswitch/vlog.h" + +VLOG_DEFINE_THIS_MODULE(ipam) + +static void +init_ipam_ipv6_prefix(const char *ipv6_prefix, struct ipam_info *info) +{ + info->ipv6_prefix_set = false; + info->ipv6_prefix = in6addr_any; + + if (!ipv6_prefix) { + return; + } + + /* XXX Since we only accept /64 addresses, why do we even bother + * with accepting and trying to analyze a user-provided mask? + */ + if (strchr(ipv6_prefix, '/')) { + /* If a prefix length was specified, it must be 64. */ + struct in6_addr mask; + char *error + = ipv6_parse_masked(ipv6_prefix, + &info->ipv6_prefix, &mask); + if (error) { + static struct vlog_rate_limit rl + = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "%s: bad 'ipv6_prefix' %s: %s", + info->id, ipv6_prefix, error); + free(error); + } else { + if (ipv6_count_cidr_bits(&mask) == 64) { + info->ipv6_prefix_set = true; + } else { + static struct vlog_rate_limit rl + = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "%s: bad 'ipv6_prefix' %s: must be /64", + info->id, ipv6_prefix); + } + } + } else { + info->ipv6_prefix_set = ipv6_parse( + ipv6_prefix, &info->ipv6_prefix); + if (!info->ipv6_prefix_set) { + static struct vlog_rate_limit rl + = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "%s: bad 'ipv6_prefix' %s", info->id, + ipv6_prefix); + } + } + + if (info->ipv6_prefix_set) { + /* Make sure nothing past first 64 bits are set */ + struct in6_addr mask = ipv6_create_mask(64); + info->ipv6_prefix = ipv6_addr_bitand(&info->ipv6_prefix, &mask); + } +} + +static void +init_ipam_ipv4(const char *subnet_str, const char *exclude_ip_list, + struct ipam_info *info) +{ + info->start_ipv4 = 0; + info->total_ipv4s = 0; + info->allocated_ipv4s = NULL; + + if (!subnet_str) { + return; + } + + ovs_be32 subnet, mask; + char *error = ip_parse_masked(subnet_str, &subnet, &mask); + if (error || mask == OVS_BE32_MAX || !ip_is_cidr(mask)) { + static struct vlog_rate_limit rl + = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "bad 'subnet' %s", subnet_str); + free(error); + return; + } + + info->start_ipv4 = ntohl(subnet & mask) + 1; + info->total_ipv4s = ~ntohl(mask); + info->allocated_ipv4s = + bitmap_allocate(info->total_ipv4s); + + /* Mark first IP as taken */ + bitmap_set1(info->allocated_ipv4s, 0); + + if (!exclude_ip_list) { + return; + } + + struct lexer lexer; + lexer_init(&lexer, exclude_ip_list); + /* exclude_ip_list could be in the format - + * "10.0.0.4 10.0.0.10 10.0.0.20..10.0.0.50 10.0.0.100..10.0.0.110". + */ + lexer_get(&lexer); + while (lexer.token.type != LEX_T_END) { + if (lexer.token.type != LEX_T_INTEGER) { + lexer_syntax_error(&lexer, "expecting address"); + break; + } + uint32_t start = ntohl(lexer.token.value.ipv4); + lexer_get(&lexer); + + uint32_t end = start + 1; + if (lexer_match(&lexer, LEX_T_ELLIPSIS)) { + if (lexer.token.type != LEX_T_INTEGER) { + lexer_syntax_error(&lexer, "expecting address range"); + break; + } + end = ntohl(lexer.token.value.ipv4) + 1; + lexer_get(&lexer); + } + + /* Clamp start...end to fit the subnet. */ + start = MAX(info->start_ipv4, start); + end = MIN(info->start_ipv4 + info->total_ipv4s, end); + if (end > start) { + bitmap_set_multiple(info->allocated_ipv4s, + start - info->start_ipv4, + end - start, 1); + } else { + lexer_error(&lexer, "excluded addresses not in subnet"); + } + } + if (lexer.error) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + /* + * VLOG_WARN_RL(&rl, "logical switch "UUID_FMT": bad exclude_ips (%s)", + * UUID_ARGS(&od->key), lexer.error); + */ + VLOG_WARN_RL(&rl, "logical switch: bad exclude_ips (%s)", lexer.error); + } + lexer_destroy(&lexer); +} + +void +init_ipam_info(struct ipam_info *info, const struct smap *config, const char *id) +{ + const char *subnet_str = smap_get(config, "subnet"); + const char *ipv6_prefix = smap_get(config, "ipv6_prefix"); + const char *exclude_ips = smap_get(config, "exclude_ips"); + + info->id = xstrdup(id ? id : ""); + + init_ipam_ipv4(subnet_str, exclude_ips, info); + init_ipam_ipv6_prefix(ipv6_prefix, info); + + if (!subnet_str && !ipv6_prefix) { + info->mac_only = smap_get_bool(config, "mac_only", false); + } +} + +void +destroy_ipam_info(struct ipam_info *info) +{ + bitmap_free(info->allocated_ipv4s); + free((char *) info->id); +} + +bool +ipam_insert_ip(struct ipam_info *info, uint32_t ip) +{ + if (!info->allocated_ipv4s) { + return true; + } + + if (ip >= info->start_ipv4 && + ip < (info->start_ipv4 + info->total_ipv4s)) { + if (bitmap_is_set(info->allocated_ipv4s, + ip - info->start_ipv4)) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "%s: Duplicate IP set: " IP_FMT, + info->id, IP_ARGS(htonl(ip))); + return false; + } + bitmap_set1(info->allocated_ipv4s, + ip - info->start_ipv4); + } + return true; +} + +uint32_t +ipam_get_unused_ip(struct ipam_info *info) +{ + if (!info->allocated_ipv4s) { + return 0; + } + + size_t new_ip_index = bitmap_scan(info->allocated_ipv4s, 0, 0, + info->total_ipv4s - 1); + if (new_ip_index == info->total_ipv4s - 1) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "%s: Subnet address space has been exhausted.", + info->id); + return 0; + } + + return info->start_ipv4 + new_ip_index; +} + +/* MAC address management (macam) table of "struct eth_addr"s, that holds the + * MAC addresses allocated by the OVN ipam module. */ +static struct hmap macam = HMAP_INITIALIZER(&macam); + +struct macam_node { + struct hmap_node hmap_node; + struct eth_addr mac_addr; /* Allocated MAC address. */ +}; + +#define MAC_ADDR_SPACE 0xffffff +static struct eth_addr mac_prefix; +static char mac_prefix_str[18]; + +void +cleanup_macam(void) +{ + struct macam_node *node; + HMAP_FOR_EACH_POP (node, hmap_node, &macam) { + free(node); + } +} + +const char * +set_mac_prefix(const char *prefix) +{ + mac_prefix = eth_addr_zero; + if (prefix) { + struct eth_addr addr; + + memset(&addr, 0, sizeof addr); + if (ovs_scan(prefix, "%"SCNx8":%"SCNx8":%"SCNx8, + &addr.ea[0], &addr.ea[1], &addr.ea[2])) { + mac_prefix = addr; + } + } + + if (eth_addr_equals(mac_prefix, eth_addr_zero)) { + eth_addr_random(&mac_prefix); + memset(&mac_prefix.ea[3], 0, 3); + } + + snprintf(mac_prefix_str, sizeof(mac_prefix_str), + "%02"PRIx8":%02"PRIx8":%02"PRIx8, + mac_prefix.ea[0], mac_prefix.ea[1], mac_prefix.ea[2]); + + return mac_prefix_str; +} + +struct eth_addr +get_mac_prefix(void) +{ + return mac_prefix; +} + +static bool +ipam_is_duplicate_mac(struct eth_addr *ea, uint64_t mac64, bool warn) +{ + struct macam_node *macam_node; + HMAP_FOR_EACH_WITH_HASH (macam_node, hmap_node, hash_uint64(mac64), + &macam) { + if (eth_addr_equals(*ea, macam_node->mac_addr)) { + if (warn) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); + VLOG_WARN_RL(&rl, "Duplicate MAC set: "ETH_ADDR_FMT, + ETH_ADDR_ARGS(macam_node->mac_addr)); + } + return true; + } + } + return false; +} + +void +ipam_insert_mac(struct eth_addr *ea, bool check) +{ + if (!ea) { + return; + } + + uint64_t mac64 = eth_addr_to_uint64(*ea); + uint64_t prefix = eth_addr_to_uint64(mac_prefix); + + /* If the new MAC was not assigned by this address management system or + * check is true and the new MAC is a duplicate, do not insert it into the + * macam hmap. */ + if (((mac64 ^ prefix) >> 24) + || (check && ipam_is_duplicate_mac(ea, mac64, true))) { + return; + } + + struct macam_node *new_macam_node = xmalloc(sizeof *new_macam_node); + new_macam_node->mac_addr = *ea; + hmap_insert(&macam, &new_macam_node->hmap_node, hash_uint64(mac64)); +} + +uint64_t +ipam_get_unused_mac(ovs_be32 ip) +{ + uint32_t mac_addr_suffix, i, base_addr = ntohl(ip) & MAC_ADDR_SPACE; + struct eth_addr mac; + uint64_t mac64; + + for (i = 0; i < MAC_ADDR_SPACE - 1; i++) { + /* The tentative MAC's suffix will be in the interval (1, 0xfffffe). */ + mac_addr_suffix = ((base_addr + i) % (MAC_ADDR_SPACE - 1)) + 1; + mac64 = eth_addr_to_uint64(mac_prefix) | mac_addr_suffix; + eth_addr_from_uint64(mac64, &mac); + if (!ipam_is_duplicate_mac(&mac, mac64, false)) { + break; + } + } + + if (i == MAC_ADDR_SPACE) { + static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); + VLOG_WARN_RL(&rl, "MAC address space exhausted."); + mac64 = 0; + } + + return mac64; +} diff --git a/northd/ipam.h b/northd/ipam.h new file mode 100644 index 000000000..447412769 --- /dev/null +++ b/northd/ipam.h @@ -0,0 +1,39 @@ +#ifndef NORTHD_IPAM_H +#define NORTHD_IPAM_H 1 + +#include +#include + +#include "openvswitch/types.h" + +struct ipam_info { + uint32_t start_ipv4; + size_t total_ipv4s; + unsigned long *allocated_ipv4s; /* A bitmap of allocated IPv4s */ + bool ipv6_prefix_set; + struct in6_addr ipv6_prefix; + bool mac_only; + const char *id; +}; + +struct smap; +void init_ipam_info(struct ipam_info *info, const struct smap *config, + const char *id); + +void destroy_ipam_info(struct ipam_info *info); + +bool ipam_insert_ip(struct ipam_info *info, uint32_t ip); + +uint32_t ipam_get_unused_ip(struct ipam_info *info); + +void ipam_insert_mac(struct eth_addr *ea, bool check); + +uint64_t ipam_get_unused_mac(ovs_be32 ip); + +void cleanup_macam(void); + +struct eth_addr get_mac_prefix(void); + +const char *set_mac_prefix(const char *hint); + +#endif /* NORTHD_IPAM_H */ diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c index 50507685b..edeee547f 100644 --- a/northd/ovn-northd.c +++ b/northd/ovn-northd.c @@ -22,6 +22,7 @@ #include "command-line.h" #include "daemon.h" #include "dirs.h" +#include "ipam.h" #include "openvswitch/dynamic-string.h" #include "fatal-signal.h" #include "hash.h" @@ -81,13 +82,6 @@ static const char *ovnnb_db; static const char *ovnsb_db; static const char *unixctl_path; -#define MAC_ADDR_SPACE 0xffffff - -/* MAC address management (macam) table of "struct eth_addr"s, that holds the - * MAC addresses allocated by the OVN ipam module. */ -static struct hmap macam = HMAP_INITIALIZER(&macam); -static struct eth_addr mac_prefix; - static bool controller_event_en; static bool check_lsp_is_up; @@ -482,15 +476,6 @@ port_has_qos_params(const struct smap *opts) } -struct ipam_info { - uint32_t start_ipv4; - size_t total_ipv4s; - unsigned long *allocated_ipv4s; /* A bitmap of allocated IPv4s */ - bool ipv6_prefix_set; - struct in6_addr ipv6_prefix; - bool mac_only; -}; - /* * Multicast snooping and querier per datapath configuration. */ @@ -796,20 +781,6 @@ struct lrouter_group { struct sset ha_chassis_groups; }; -struct macam_node { - struct hmap_node hmap_node; - struct eth_addr mac_addr; /* Allocated MAC address. */ -}; - -static void -cleanup_macam(struct hmap *macam_) -{ - struct macam_node *node; - HMAP_FOR_EACH_POP (node, hmap_node, macam_) { - free(node); - } -} - static struct ovn_datapath * ovn_datapath_create(struct hmap *datapaths, const struct uuid *key, const struct nbrec_logical_switch *nbs, @@ -841,7 +812,7 @@ ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od) * use it. */ hmap_remove(datapaths, &od->key_node); ovn_destroy_tnlids(&od->port_tnlids); - bitmap_free(od->ipam_info.allocated_ipv4s); + destroy_ipam_info(&od->ipam_info); free(od->router_ports); destroy_nat_entries(od); free(od->nat_entries); @@ -905,117 +876,9 @@ init_ipam_info_for_datapath(struct ovn_datapath *od) return; } - const char *subnet_str = smap_get(&od->nbs->other_config, "subnet"); - const char *ipv6_prefix = smap_get(&od->nbs->other_config, "ipv6_prefix"); - - if (ipv6_prefix) { - if (strstr(ipv6_prefix, "/")) { - /* If a prefix length was specified, it must be 64. */ - struct in6_addr mask; - char *error - = ipv6_parse_masked(ipv6_prefix, - &od->ipam_info.ipv6_prefix, &mask); - if (error) { - static struct vlog_rate_limit rl - = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s: %s", - ipv6_prefix, error); - free(error); - } else { - if (ipv6_count_cidr_bits(&mask) == 64) { - od->ipam_info.ipv6_prefix_set = true; - } else { - static struct vlog_rate_limit rl - = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s: must be /64", - ipv6_prefix); - } - } - } else { - od->ipam_info.ipv6_prefix_set = ipv6_parse( - ipv6_prefix, &od->ipam_info.ipv6_prefix); - if (!od->ipam_info.ipv6_prefix_set) { - static struct vlog_rate_limit rl - = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'ipv6_prefix' %s", ipv6_prefix); - } - } - } - - if (!subnet_str) { - if (!ipv6_prefix) { - od->ipam_info.mac_only = smap_get_bool(&od->nbs->other_config, - "mac_only", false); - } - return; - } - - ovs_be32 subnet, mask; - char *error = ip_parse_masked(subnet_str, &subnet, &mask); - if (error || mask == OVS_BE32_MAX || !ip_is_cidr(mask)) { - static struct vlog_rate_limit rl - = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "bad 'subnet' %s", subnet_str); - free(error); - return; - } - - od->ipam_info.start_ipv4 = ntohl(subnet & mask) + 1; - od->ipam_info.total_ipv4s = ~ntohl(mask); - od->ipam_info.allocated_ipv4s = - bitmap_allocate(od->ipam_info.total_ipv4s); - - /* Mark first IP as taken */ - bitmap_set1(od->ipam_info.allocated_ipv4s, 0); - - /* Check if there are any reserver IPs (list) to be excluded from IPAM */ - const char *exclude_ip_list = smap_get(&od->nbs->other_config, - "exclude_ips"); - if (!exclude_ip_list) { - return; - } - - struct lexer lexer; - lexer_init(&lexer, exclude_ip_list); - /* exclude_ip_list could be in the format - - * "10.0.0.4 10.0.0.10 10.0.0.20..10.0.0.50 10.0.0.100..10.0.0.110". - */ - lexer_get(&lexer); - while (lexer.token.type != LEX_T_END) { - if (lexer.token.type != LEX_T_INTEGER) { - lexer_syntax_error(&lexer, "expecting address"); - break; - } - uint32_t start = ntohl(lexer.token.value.ipv4); - lexer_get(&lexer); - - uint32_t end = start + 1; - if (lexer_match(&lexer, LEX_T_ELLIPSIS)) { - if (lexer.token.type != LEX_T_INTEGER) { - lexer_syntax_error(&lexer, "expecting address range"); - break; - } - end = ntohl(lexer.token.value.ipv4) + 1; - lexer_get(&lexer); - } - - /* Clamp start...end to fit the subnet. */ - start = MAX(od->ipam_info.start_ipv4, start); - end = MIN(od->ipam_info.start_ipv4 + od->ipam_info.total_ipv4s, end); - if (end > start) { - bitmap_set_multiple(od->ipam_info.allocated_ipv4s, - start - od->ipam_info.start_ipv4, - end - start, 1); - } else { - lexer_error(&lexer, "excluded addresses not in subnet"); - } - } - if (lexer.error) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "logical switch "UUID_FMT": bad exclude_ips (%s)", - UUID_ARGS(&od->key), lexer.error); - } - lexer_destroy(&lexer); + char uuid_s[UUID_LEN + 1]; + sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key)); + init_ipam_info(&od->ipam_info, &od->nbs->other_config, uuid_s); } static void @@ -1593,65 +1456,14 @@ lrport_is_enabled(const struct nbrec_logical_router_port *lrport) return !lrport->enabled || *lrport->enabled; } -static bool -ipam_is_duplicate_mac(struct eth_addr *ea, uint64_t mac64, bool warn) -{ - struct macam_node *macam_node; - HMAP_FOR_EACH_WITH_HASH (macam_node, hmap_node, hash_uint64(mac64), - &macam) { - if (eth_addr_equals(*ea, macam_node->mac_addr)) { - if (warn) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - VLOG_WARN_RL(&rl, "Duplicate MAC set: "ETH_ADDR_FMT, - ETH_ADDR_ARGS(macam_node->mac_addr)); - } - return true; - } - } - return false; -} - -static void -ipam_insert_mac(struct eth_addr *ea, bool check) -{ - if (!ea) { - return; - } - - uint64_t mac64 = eth_addr_to_uint64(*ea); - uint64_t prefix = eth_addr_to_uint64(mac_prefix); - - /* If the new MAC was not assigned by this address management system or - * check is true and the new MAC is a duplicate, do not insert it into the - * macam hmap. */ - if (((mac64 ^ prefix) >> 24) - || (check && ipam_is_duplicate_mac(ea, mac64, true))) { - return; - } - - struct macam_node *new_macam_node = xmalloc(sizeof *new_macam_node); - new_macam_node->mac_addr = *ea; - hmap_insert(&macam, &new_macam_node->hmap_node, hash_uint64(mac64)); -} - static void -ipam_insert_ip(struct ovn_datapath *od, uint32_t ip) +ipam_insert_ip_for_datapath(struct ovn_datapath *od, uint32_t ip) { - if (!od || !od->ipam_info.allocated_ipv4s) { + if (!od) { return; } - if (ip >= od->ipam_info.start_ipv4 && - ip < (od->ipam_info.start_ipv4 + od->ipam_info.total_ipv4s)) { - if (bitmap_is_set(od->ipam_info.allocated_ipv4s, - ip - od->ipam_info.start_ipv4)) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); - VLOG_WARN_RL(&rl, "Duplicate IP set on switch %s: "IP_FMT, - od->nbs->name, IP_ARGS(htonl(ip))); - } - bitmap_set1(od->ipam_info.allocated_ipv4s, - ip - od->ipam_info.start_ipv4); - } + ipam_insert_ip(&od->ipam_info, ip); } static void @@ -1680,7 +1492,7 @@ ipam_insert_lsp_addresses(struct ovn_datapath *od, struct ovn_port *op, for (size_t j = 0; j < laddrs.n_ipv4_addrs; j++) { uint32_t ip = ntohl(laddrs.ipv4_addrs[j].addr); - ipam_insert_ip(od, ip); + ipam_insert_ip_for_datapath(od, ip); } destroy_lport_addresses(&laddrs); @@ -1722,7 +1534,7 @@ ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op) * about a duplicate IP address. */ if (ip != op->peer->od->ipam_info.start_ipv4) { - ipam_insert_ip(op->peer->od, ip); + ipam_insert_ip_for_datapath(op->peer->od, ip); } } @@ -1730,50 +1542,6 @@ ipam_add_port_addresses(struct ovn_datapath *od, struct ovn_port *op) } } -static uint64_t -ipam_get_unused_mac(ovs_be32 ip) -{ - uint32_t mac_addr_suffix, i, base_addr = ntohl(ip) & MAC_ADDR_SPACE; - struct eth_addr mac; - uint64_t mac64; - - for (i = 0; i < MAC_ADDR_SPACE - 1; i++) { - /* The tentative MAC's suffix will be in the interval (1, 0xfffffe). */ - mac_addr_suffix = ((base_addr + i) % (MAC_ADDR_SPACE - 1)) + 1; - mac64 = eth_addr_to_uint64(mac_prefix) | mac_addr_suffix; - eth_addr_from_uint64(mac64, &mac); - if (!ipam_is_duplicate_mac(&mac, mac64, false)) { - break; - } - } - - if (i == MAC_ADDR_SPACE) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL(&rl, "MAC address space exhausted."); - mac64 = 0; - } - - return mac64; -} - -static uint32_t -ipam_get_unused_ip(struct ovn_datapath *od) -{ - if (!od || !od->ipam_info.allocated_ipv4s) { - return 0; - } - - size_t new_ip_index = bitmap_scan(od->ipam_info.allocated_ipv4s, 0, 0, - od->ipam_info.total_ipv4s - 1); - if (new_ip_index == od->ipam_info.total_ipv4s - 1) { - static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1); - VLOG_WARN_RL( &rl, "Subnet address space has been exhausted."); - return 0; - } - - return od->ipam_info.start_ipv4 + new_ip_index; -} - enum dynamic_update_type { NONE, /* No change to the address */ REMOVE, /* Address is no longer dynamic */ @@ -1813,7 +1581,7 @@ dynamic_mac_changed(const char *lsp_addresses, } uint64_t mac64 = eth_addr_to_uint64(update->current_addresses.ea); - uint64_t prefix = eth_addr_to_uint64(mac_prefix); + uint64_t prefix = eth_addr_to_uint64(get_mac_prefix()); if ((mac64 ^ prefix) >> 24) { return DYNAMIC; @@ -1975,7 +1743,7 @@ update_unchanged_dynamic_addresses(struct dynamic_address_update *update) ipam_insert_mac(&update->current_addresses.ea, false); } if (update->ipv4 == NONE && update->current_addresses.n_ipv4_addrs) { - ipam_insert_ip(update->op->od, + ipam_insert_ip_for_datapath(update->op->od, ntohl(update->current_addresses.ipv4_addrs[0].addr)); } } @@ -2055,7 +1823,7 @@ update_dynamic_addresses(struct dynamic_address_update *update) ip4 = update->static_ip; break; case DYNAMIC: - ip4 = htonl(ipam_get_unused_ip(update->od)); + ip4 = htonl(ipam_get_unused_ip(&update->od->ipam_info)); VLOG_INFO("Assigned dynamic IPv4 address '"IP_FMT"' to port '%s'", IP_ARGS(ip4), update->op->nbsp->name); } @@ -2104,7 +1872,7 @@ update_dynamic_addresses(struct dynamic_address_update *update) ipam_insert_mac(&mac, true); if (ip4) { - ipam_insert_ip(update->od, ntohl(ip4)); + ipam_insert_ip_for_datapath(update->od, ntohl(ip4)); ds_put_format(&new_addr, " "IP_FMT, IP_ARGS(ip4)); } if (!IN6_ARE_ADDR_EQUAL(&ip6, &in6addr_any)) { @@ -12746,16 +12514,8 @@ ovnnb_db_run(struct northd_context *ctx, sbrec_sb_global_set_options(sb, &nb->options); sb_loop->next_cfg = nb->nb_cfg; - const char *mac_addr_prefix = smap_get(&nb->options, "mac_prefix"); - if (mac_addr_prefix) { - struct eth_addr addr; - - memset(&addr, 0, sizeof addr); - if (ovs_scan(mac_addr_prefix, "%"SCNx8":%"SCNx8":%"SCNx8, - &addr.ea[0], &addr.ea[1], &addr.ea[2])) { - mac_prefix = addr; - } - } + const char *mac_addr_prefix = set_mac_prefix(smap_get(&nb->options, + "mac_prefix")); const char *monitor_mac = smap_get(&nb->options, "svc_monitor_mac"); if (monitor_mac) { @@ -12770,15 +12530,7 @@ ovnnb_db_run(struct northd_context *ctx, struct smap options; smap_clone(&options, &nb->options); - if (!mac_addr_prefix) { - eth_addr_random(&mac_prefix); - memset(&mac_prefix.ea[3], 0, 3); - - smap_add_format(&options, "mac_prefix", - "%02"PRIx8":%02"PRIx8":%02"PRIx8, - mac_prefix.ea[0], mac_prefix.ea[1], - mac_prefix.ea[2]); - } + smap_add(&options, "mac_prefix", mac_addr_prefix); if (!monitor_mac) { eth_addr_random(&svc_monitor_mac_ea); @@ -12858,7 +12610,14 @@ ovnnb_db_run(struct northd_context *ctx, } shash_destroy(&meter_groups); - cleanup_macam(&macam); + /* XXX Having to explicitly clean up macam here + * is a bit strange. We don't explicitly initialize + * macam in this module, but this is the logical place + * to clean it up. Ideally, more IPAM logic can be factored + * out of ovn-northd and this can be taken care of there + * as well. + */ + cleanup_macam(); } /* Stores the list of chassis which references an ha_chassis_group. From patchwork Tue Jan 12 21:09:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1425485 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.136; helo=silver.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=J9QxGuRH; dkim-atps=neutral Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4DFmBd6cDrz9sWg for ; Wed, 13 Jan 2021 09:55:34 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id 85ED320033; Tue, 12 Jan 2021 21:10:37 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 1o4duColUXvb; Tue, 12 Jan 2021 21:10:23 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by silver.osuosl.org (Postfix) with ESMTP id CF0F620493; Tue, 12 Jan 2021 21:10:22 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 8193AC1E70; Tue, 12 Jan 2021 21:10:22 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) by lists.linuxfoundation.org (Postfix) with ESMTP id 8DB4FC0893 for ; Tue, 12 Jan 2021 21:10:21 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id 27F8B20495 for ; Tue, 12 Jan 2021 21:10:21 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Ie0Q7NQbaAzZ for ; Tue, 12 Jan 2021 21:10:09 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [216.205.24.124]) by silver.osuosl.org (Postfix) with ESMTPS id 1EE4C20487 for ; Tue, 12 Jan 2021 21:10:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610485807; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=8wU6fH/dshBTjtk61Baa7/0KO2Tt9cfEB+IKMjikYqE=; b=J9QxGuRHVclOqKKl6XvRvvOQMpSrClpdc1vmSeEjXSqNyp0uBcsMPeQM095TZxDm1+BDiK OmaQ53qxYbxD5BNcUKhiYnbI2XMg/EU60Y89qbVGxNVmPGrSjzvHnAFAHorbo8Qtc81pTb JDUWsp5wqJKeErMRD97NkFnJoSaNqOA= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-435-PgoMTebnP6SqXMwlzd7nJQ-1; Tue, 12 Jan 2021 16:10:05 -0500 X-MC-Unique: PgoMTebnP6SqXMwlzd7nJQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 7453B80ED8A for ; Tue, 12 Jan 2021 21:10:04 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-113-53.rdu2.redhat.com [10.10.113.53]) by smtp.corp.redhat.com (Postfix) with ESMTP id E41386F96B for ; Tue, 12 Jan 2021 21:10:03 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Tue, 12 Jan 2021 16:09:58 -0500 Message-Id: <20210112210959.2151790-3-mmichels@redhat.com> In-Reply-To: <20210112210959.2151790-1-mmichels@redhat.com> References: <20210112210959.2151790-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v7 2/3] Add ipam unit tests X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" This adds unit tests for IPAM IPv6 initialization, IPv4 initialization, and IPv4 address retrieval. Signed-off-by: Mark Michelson --- northd/test-ipam.c | 146 ++++++++++++++++++++++++++++ tests/automake.mk | 8 +- tests/ovn-ipam.at | 234 +++++++++++++++++++++++++++++++++++++++++++++ tests/testsuite.at | 1 + 4 files changed, 387 insertions(+), 2 deletions(-) create mode 100644 northd/test-ipam.c create mode 100644 tests/ovn-ipam.at diff --git a/northd/test-ipam.c b/northd/test-ipam.c new file mode 100644 index 000000000..7872585a3 --- /dev/null +++ b/northd/test-ipam.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2020 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "tests/ovstest.h" + +#include "openvswitch/dynamic-string.h" +#include "smap.h" +#include "packets.h" +#include "bitmap.h" + +#include "ipam.h" + +static void +test_ipam_get_unused_ip(struct ovs_cmdl_context *ctx) +{ + struct ipam_info info; + + struct smap config = SMAP_INITIALIZER(&config); + smap_add(&config, "subnet", ctx->argv[1]); + int num_ips; + str_to_int(ctx->argv[2], 0, &num_ips); + if (ctx->argc > 3) { + smap_add(&config, "exclude_ips", ctx->argv[3]); + } + init_ipam_info(&info, &config, "Unused IP test"); + + bool fail = false; + struct ds output = DS_EMPTY_INITIALIZER; + struct ds err = DS_EMPTY_INITIALIZER; + for (size_t i = 0; i < num_ips; i++) { + uint32_t next_ip = ipam_get_unused_ip(&info); + ds_put_format(&output, IP_FMT "\n", IP_ARGS(htonl(next_ip))); + if (next_ip) { + ovs_assert(ipam_insert_ip(&info, next_ip)); + } + } + + printf("%s", ds_cstr(&output)); + if (fail) { + fprintf(stderr, "%s", ds_cstr(&err)); + } + + smap_destroy(&config); + destroy_ipam_info(&info); + ds_destroy(&output); + ds_destroy(&err); +} + +static void +test_ipam_init_ipv4(struct ovs_cmdl_context *ctx) +{ + const char *subnet = ctx->argv[1]; + const char *exclude_ips = ctx->argc > 2 ? ctx->argv[2] : NULL; + struct smap config = SMAP_INITIALIZER(&config); + smap_add(&config, "subnet", subnet); + if (exclude_ips) { + smap_add(&config, "exclude_ips", exclude_ips); + } + struct ipam_info ipam; + init_ipam_info(&ipam, &config, "IPv4 test"); + + struct ds output = DS_EMPTY_INITIALIZER; + ds_put_format(&output, "start_ipv4: " IP_FMT "\n", + IP_ARGS(htonl(ipam.start_ipv4))); + ds_put_format(&output, "total_ipv4s: %" PRIuSIZE "\n", ipam.total_ipv4s); + + ds_put_cstr(&output, "allocated_ipv4s: "); + if (ipam.allocated_ipv4s) { + int start = 0; + int end = ipam.total_ipv4s; + for (size_t bit = bitmap_scan(ipam.allocated_ipv4s, true, start, end); + bit != end; + bit = bitmap_scan(ipam.allocated_ipv4s, true, bit + 1, end)) { + ds_put_format(&output, IP_FMT " ", + IP_ARGS((htonl(ipam.start_ipv4 + bit)))); + } + } + ds_chomp(&output, ' '); + ds_put_char(&output, '\n'); + + printf("%s", ds_cstr(&output)); + + destroy_ipam_info(&ipam); + ds_destroy(&output); +} + +static void +test_ipam_init_ipv6_prefix(struct ovs_cmdl_context *ctx) +{ + const char *prefix = ctx->argc > 1 ? ctx->argv[1] : NULL; + struct smap config = SMAP_INITIALIZER(&config); + if (prefix) { + smap_add(&config, "ipv6_prefix", prefix); + }; + struct ipam_info ipam; + init_ipam_info(&ipam, &config, "IPv6 test"); + + struct ds output = DS_EMPTY_INITIALIZER; + ds_put_format(&output, "ipv6_prefix_set: %s\n", + ipam.ipv6_prefix_set ? "true" : "false"); + if (ipam.ipv6_prefix_set) { + char ipv6[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &ipam.ipv6_prefix, + ipv6, sizeof ipv6); + ds_put_format(&output, "ipv6_prefix: %s\n", ipv6); + } + + printf("%s", ds_cstr(&output)); + + destroy_ipam_info(&ipam); + ds_destroy(&output); +} + +static void +test_ipam_main(int argc, char *argv[]) +{ + set_program_name(argv[0]); + static const struct ovs_cmdl_command commands[] = { + {"ipam_get_unused_ip", NULL, 2, 3, test_ipam_get_unused_ip, OVS_RO}, + {"ipam_init_ipv6_prefix", NULL, 0, 1, test_ipam_init_ipv6_prefix, + OVS_RO}, + {"ipam_init_ipv4", NULL, 1, 2, test_ipam_init_ipv4, + OVS_RO}, + {NULL, NULL, 0, 0, NULL, OVS_RO}, + }; + struct ovs_cmdl_context ctx; + ctx.argc = argc - 1; + ctx.argv = argv + 1; + ovs_cmdl_run_command(&ctx, commands); +} + +OVSTEST_REGISTER("test-ipam", test_ipam_main); diff --git a/tests/automake.mk b/tests/automake.mk index db934cb95..16190cc8e 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -31,7 +31,8 @@ TESTSUITE_AT = \ tests/ovn-controller-vtep.at \ tests/ovn-ic.at \ tests/ovn-macros.at \ - tests/ovn-performance.at + tests/ovn-performance.at \ + tests/ovn-ipam.at SYSTEM_KMOD_TESTSUITE_AT = \ tests/system-common-macros.at \ @@ -205,7 +206,10 @@ noinst_PROGRAMS += tests/ovstest tests_ovstest_SOURCES = \ tests/ovstest.c \ tests/ovstest.h \ - tests/test-ovn.c + tests/test-ovn.c \ + northd/test-ipam.c \ + northd/ipam.c \ + northd/ipam.h tests_ovstest_LDADD = $(OVS_LIBDIR)/daemon.lo \ $(OVS_LIBDIR)/libopenvswitch.la lib/libovn.la diff --git a/tests/ovn-ipam.at b/tests/ovn-ipam.at new file mode 100644 index 000000000..ee00e62a0 --- /dev/null +++ b/tests/ovn-ipam.at @@ -0,0 +1,234 @@ +AT_BANNER([OVN unit tests]) + +AT_SETUP([ovn -- unit test -- init_ipam_ipv4]) +ovn_start + +# Valid subnet, no exclude IPs +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29], [0], [dnl +start_ipv4: 192.168.0.1 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.1 +]) + +# Valid subnet, single exclude IP +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 192.168.0.3], [0], [dnl +start_ipv4: 192.168.0.1 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.1 192.168.0.3 +]) + +# Valid subnet, two exclude IPs +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 "192.168.0.3 192.168.0.5"], [0], [dnl +start_ipv4: 192.168.0.1 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.1 192.168.0.3 192.168.0.5 +]) + +# Valid subnet, range of exclude IPs +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 "192.168.0.3..192.168.0.5"], [0], [dnl +start_ipv4: 192.168.0.1 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.1 192.168.0.3 192.168.0.4 192.168.0.5 +]) + +# Valid subnet, exclude IP outside of subnet +# Excluded IP should be ignored. +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 192.168.0.9], [0], [dnl +start_ipv4: 192.168.0.1 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.1 +],[ignore]) + +# Valid subnet, range of exclude IPs starts in subnet but ends outside +# Excluded IPs inside the subnet should be allocated +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 "192.168.0.5..192.168.0.11"], [0], [dnl +start_ipv4: 192.168.0.1 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.1 192.168.0.5 192.168.0.6 192.168.0.7 +],[ignore]) + +# Valid subnet, range of exclude IPs starts outside subnet but ends inside +# Excluded IPs inside the subnet should be allocated +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.8/29 "192.168.0.5..192.168.0.11"], [0], [dnl +start_ipv4: 192.168.0.9 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.9 192.168.0.10 192.168.0.11 +],[ignore]) + +# Valid subnet, range of exclude IPs starts before and ends after the subnet +# Entire subnet should be allocated +# XXX Should excluding every address in a subnet be an invalid configuration? +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.8/29 "192.168.0.5..192.168.0.18"], [0], [dnl +start_ipv4: 192.168.0.9 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.9 192.168.0.10 192.168.0.11 192.168.0.12 192.168.0.13 192.168.0.14 192.168.0.15 +],[ignore]) + +# Valid subnet, inverted exclude range +# Exclude range should be ignored +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 "192.168.0.5..192.168.0.2"], [0], [dnl +start_ipv4: 192.168.0.1 +total_ipv4s: 7 +allocated_ipv4s: 192.168.0.1 +],[ignore]) + +# XXX At this point, I wanted to insert some tests where I put in invalid +# IP addresses like 400.500.600.700 to ensure that the start_ipv4 was set +# to "0.0.0.0". However, ovs_scan_ip_masked() does no validation of the +# IP address. So long as the given IP address follows the format of +# xxx.xxx.xxx.xxx, it's seen as valid. In the specific case of +# "400.500.600.700", it ends up setting the start_ipv4 to +# "144.244.88.185". This result is probably system-dependent. + +# Invalid subnet: Bad mask +AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/-69], [0], [dnl +start_ipv4: 0.0.0.0 +total_ipv4s: 0 +allocated_ipv4s: +],[ignore]) + +AT_CLEANUP + +AT_SETUP([ovn -- unit test -- init_ipam_ipv6_prefix]) +ovn_start + +# No prefix set +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix], [0], [dnl +ipv6_prefix_set: false +]) + +# Good prefix with no mask +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::], [0], [dnl +ipv6_prefix_set: true +ipv6_prefix: aef0:: +]) + +# Good prefix with good mask +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::/64], [0], [dnl +ipv6_prefix_set: true +ipv6_prefix: aef0:: +]) + +# Bad prefix with no mask +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef20::], [0], [dnl +ipv6_prefix_set: false +],[ignore]) + +# Good prefix with nonsense mask. +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::/900], [0], [dnl +ipv6_prefix_set: false +],[ignore]) + +# Good prefix with a non-/64 mask. +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::/32], [0], [dnl +ipv6_prefix_set: false +],[ignore]) + +# Bad prefix and a non-/64 mask. +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef20::/32], [0], [dnl +ipv6_prefix_set: false +],[ignore]) + +# Overspecify the IPv6 address. +# We should "round down" to the /64 network address. +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::2323], [0], [dnl +ipv6_prefix_set: true +ipv6_prefix: aef0:: +],[ignore]) + +# Overspecify the IPv6 address, and specify a mask. +# We should "round down" to the /64 network address. +AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::2323/64], [0], [dnl +ipv6_prefix_set: true +ipv6_prefix: aef0:: +],[ignore]) + +AT_CLEANUP + +AT_SETUP([ovn -- unit test -- ipam_get_unused_ip]) +ovn_start + +# Ensure first address returned by IPAM is .2, since .1 is reserved for the +# connected router +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 1], [0], [dnl +192.168.0.2 +]) + +# Ensure that we only grab IPs within the requested subnet +# Ignore stderr so that the warning about address space being +# exhausted does not cause the test to fail +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 6], [0], [dnl +192.168.0.2 +192.168.0.3 +192.168.0.4 +192.168.0.5 +192.168.0.6 +0.0.0.0 +],[ignore]) + +# Set up an exclude IP and ensure it does not get selected +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 4 192.168.0.3], [0], [dnl +192.168.0.2 +192.168.0.4 +192.168.0.5 +192.168.0.6 +]) + +# Set up an exclude IP range and ensure none gets selected +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 2 192.168.0.3..192.168.0.5], [0], [dnl +192.168.0.2 +192.168.0.6 +]) + +# Set up an exclude range from outside the subnet. Ensure it is ignored. +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 5 192.168.1.3..192.168.1.5], [0], [dnl +192.168.0.2 +192.168.0.3 +192.168.0.4 +192.168.0.5 +192.168.0.6 +],[ignore]) + +# Set up an exclude range from outside the subnet. Ensure we cannot assign +# addresses outside the subnet +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 6 192.168.1.3..192.168.1.5], [0], [dnl +192.168.0.2 +192.168.0.3 +192.168.0.4 +192.168.0.5 +192.168.0.6 +0.0.0.0 +],[ignore]) + +# Set up an exclude range that starts before the subnet but ends in the subnet. +# The overlapping part should be excluded +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.8/29 2 192.168.0.2..192.168.0.12], [0], [dnl +192.168.0.13 +192.168.0.14 +],[ignore]) + +# Set up an exclude range that starts in the subnet but ends after the subnet. +# The overlapping part should be excluded. +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 3 192.168.0.4..192.168.0.9], [0], [dnl +192.168.0.2 +192.168.0.3 +0.0.0.0 +],[ignore]) + +# Set up an exclude range that starts before the subnet and ends after the subnet. +# The entire range should be excluded. +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.8/29 1 192.168.0.2..192.168.0.18], [0], [dnl +0.0.0.0 +],[ignore]) + +# Configure the subnet using a starting IP that is not the network address of the +# subnet. Ensure that we "round it down" to the proper subnet starting point. +AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.4/29 5], [0], [dnl +192.168.0.2 +192.168.0.3 +192.168.0.4 +192.168.0.5 +192.168.0.6 +]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 960227dcc..2416d797d 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -22,6 +22,7 @@ m4_include([tests/ofproto-macros.at]) m4_include([tests/ovn-macros.at]) m4_include([tests/network-functions.at]) +m4_include([tests/ovn-ipam.at]) m4_include([tests/ovn.at]) m4_include([tests/ovn-performance.at]) m4_include([tests/ovn-northd.at]) From patchwork Tue Jan 12 21:09:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mark Michelson X-Patchwork-Id: 1425495 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org (client-ip=140.211.166.137; helo=fraxinus.osuosl.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=boY8+dcj; dkim-atps=neutral Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4DFmPT2wq0z9sWr for ; Wed, 13 Jan 2021 10:05:00 +1100 (AEDT) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 6966F85D8F; Tue, 12 Jan 2021 21:10:19 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xxoanGhXP701; Tue, 12 Jan 2021 21:10:12 +0000 (UTC) Received: from lists.linuxfoundation.org (lf-lists.osuosl.org [140.211.9.56]) by fraxinus.osuosl.org (Postfix) with ESMTP id 84A9986149; Tue, 12 Jan 2021 21:10:12 +0000 (UTC) Received: from lf-lists.osuosl.org (localhost [127.0.0.1]) by lists.linuxfoundation.org (Postfix) with ESMTP id 448B6C1DA6; Tue, 12 Jan 2021 21:10:12 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@lists.linuxfoundation.org Received: from fraxinus.osuosl.org (smtp4.osuosl.org [140.211.166.137]) by lists.linuxfoundation.org (Postfix) with ESMTP id 7AB67C013A for ; Tue, 12 Jan 2021 21:10:10 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by fraxinus.osuosl.org (Postfix) with ESMTP id 6AB3484947 for ; Tue, 12 Jan 2021 21:10:10 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from fraxinus.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id UgFtxKL8oDXL for ; Tue, 12 Jan 2021 21:10:09 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [63.128.21.124]) by fraxinus.osuosl.org (Postfix) with ESMTPS id 62D8B846F4 for ; Tue, 12 Jan 2021 21:10:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1610485808; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=mP6BB8X77rKlh5vshknBKD82DLvVJhKkl6Kq6sA/pfg=; b=boY8+dcjrGPT3Qqe/tAmKtQvxmQpmxpD0KvNa0nSqbFAmqYdfpPOiKp0Y0oh4uqGPBBdmz EaSiAD5HHPU7NHL8WUtBqMbDvbWM2aSFzpupnpUVQPW1tA75/wwxT8N8vtWK4BmhEkDBTk 4yOlOzWAtfersBQkqdB4+jffQgaoZWE= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-368-rwkIAGU6Oi207iIETmzjfA-1; Tue, 12 Jan 2021 16:10:06 -0500 X-MC-Unique: rwkIAGU6Oi207iIETmzjfA-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 606CB107ACF7 for ; Tue, 12 Jan 2021 21:10:05 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-113-53.rdu2.redhat.com [10.10.113.53]) by smtp.corp.redhat.com (Postfix) with ESMTP id B5A686F97A for ; Tue, 12 Jan 2021 21:10:04 +0000 (UTC) From: Mark Michelson To: dev@openvswitch.org Date: Tue, 12 Jan 2021 16:09:59 -0500 Message-Id: <20210112210959.2151790-4-mmichels@redhat.com> In-Reply-To: <20210112210959.2151790-1-mmichels@redhat.com> References: <20210112210959.2151790-1-mmichels@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 Authentication-Results: relay.mimecast.com; auth=pass smtp.auth=CUSA124A263 smtp.mailfrom=mmichels@redhat.com X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Subject: [ovs-dev] [PATCH ovn v7 3/3] Disable logging to the console from ovstest. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: ovs-dev-bounces@openvswitch.org Sender: "dev" By default, OVN and OVS errors and warnings are written to stderr. GNU Autotest automatically fails a test if unexpected data is written to stderr. This causes two problems: 1) Unit tests that attempt off-nominal code paths may fail because of a warning message in OVN or OVS. To get around this, it is common for tests to pass "[ignore]" to AT_CHECK's stderr parameter so that OVN/OVS log messages do not cause failures. But... 2) Passing "[ignore]" makes it so that unit tests cannot then print their own messages to stderr to help debug test failures. By disabling OVS/OVN log messages from going to the console, we allow for tests to write their own messages to stderr. Signed-off-by: Mark Michelson --- tests/ovn-ipam.at | 36 ++++++++++++++++++------------------ tests/ovstest.c | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/tests/ovn-ipam.at b/tests/ovn-ipam.at index ee00e62a0..ab2ddda55 100644 --- a/tests/ovn-ipam.at +++ b/tests/ovn-ipam.at @@ -37,7 +37,7 @@ AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 192.168.0.9], [0], [dn start_ipv4: 192.168.0.1 total_ipv4s: 7 allocated_ipv4s: 192.168.0.1 -],[ignore]) +]) # Valid subnet, range of exclude IPs starts in subnet but ends outside # Excluded IPs inside the subnet should be allocated @@ -45,7 +45,7 @@ AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 "192.168.0.5..192.168. start_ipv4: 192.168.0.1 total_ipv4s: 7 allocated_ipv4s: 192.168.0.1 192.168.0.5 192.168.0.6 192.168.0.7 -],[ignore]) +]) # Valid subnet, range of exclude IPs starts outside subnet but ends inside # Excluded IPs inside the subnet should be allocated @@ -53,7 +53,7 @@ AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.8/29 "192.168.0.5..192.168. start_ipv4: 192.168.0.9 total_ipv4s: 7 allocated_ipv4s: 192.168.0.9 192.168.0.10 192.168.0.11 -],[ignore]) +]) # Valid subnet, range of exclude IPs starts before and ends after the subnet # Entire subnet should be allocated @@ -62,7 +62,7 @@ AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.8/29 "192.168.0.5..192.168. start_ipv4: 192.168.0.9 total_ipv4s: 7 allocated_ipv4s: 192.168.0.9 192.168.0.10 192.168.0.11 192.168.0.12 192.168.0.13 192.168.0.14 192.168.0.15 -],[ignore]) +]) # Valid subnet, inverted exclude range # Exclude range should be ignored @@ -70,7 +70,7 @@ AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/29 "192.168.0.5..192.168. start_ipv4: 192.168.0.1 total_ipv4s: 7 allocated_ipv4s: 192.168.0.1 -],[ignore]) +]) # XXX At this point, I wanted to insert some tests where I put in invalid # IP addresses like 400.500.600.700 to ensure that the start_ipv4 was set @@ -85,7 +85,7 @@ AT_CHECK([ovstest test-ipam ipam_init_ipv4 192.168.0.0/-69], [0], [dnl start_ipv4: 0.0.0.0 total_ipv4s: 0 allocated_ipv4s: -],[ignore]) +]) AT_CLEANUP @@ -112,36 +112,36 @@ ipv6_prefix: aef0:: # Bad prefix with no mask AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef20::], [0], [dnl ipv6_prefix_set: false -],[ignore]) +]) # Good prefix with nonsense mask. AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::/900], [0], [dnl ipv6_prefix_set: false -],[ignore]) +]) # Good prefix with a non-/64 mask. AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::/32], [0], [dnl ipv6_prefix_set: false -],[ignore]) +]) # Bad prefix and a non-/64 mask. AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef20::/32], [0], [dnl ipv6_prefix_set: false -],[ignore]) +]) # Overspecify the IPv6 address. # We should "round down" to the /64 network address. AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::2323], [0], [dnl ipv6_prefix_set: true ipv6_prefix: aef0:: -],[ignore]) +]) # Overspecify the IPv6 address, and specify a mask. # We should "round down" to the /64 network address. AT_CHECK([ovstest test-ipam ipam_init_ipv6_prefix aef0::2323/64], [0], [dnl ipv6_prefix_set: true ipv6_prefix: aef0:: -],[ignore]) +]) AT_CLEANUP @@ -164,7 +164,7 @@ AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 6], [0], [dnl 192.168.0.5 192.168.0.6 0.0.0.0 -],[ignore]) +]) # Set up an exclude IP and ensure it does not get selected AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 4 192.168.0.3], [0], [dnl @@ -187,7 +187,7 @@ AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 5 192.168.1.3..192 192.168.0.4 192.168.0.5 192.168.0.6 -],[ignore]) +]) # Set up an exclude range from outside the subnet. Ensure we cannot assign # addresses outside the subnet @@ -198,14 +198,14 @@ AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 6 192.168.1.3..192 192.168.0.5 192.168.0.6 0.0.0.0 -],[ignore]) +]) # Set up an exclude range that starts before the subnet but ends in the subnet. # The overlapping part should be excluded AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.8/29 2 192.168.0.2..192.168.0.12], [0], [dnl 192.168.0.13 192.168.0.14 -],[ignore]) +]) # Set up an exclude range that starts in the subnet but ends after the subnet. # The overlapping part should be excluded. @@ -213,13 +213,13 @@ AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.0/29 3 192.168.0.4..192 192.168.0.2 192.168.0.3 0.0.0.0 -],[ignore]) +]) # Set up an exclude range that starts before the subnet and ends after the subnet. # The entire range should be excluded. AT_CHECK([ovstest test-ipam ipam_get_unused_ip 192.168.0.8/29 1 192.168.0.2..192.168.0.18], [0], [dnl 0.0.0.0 -],[ignore]) +]) # Configure the subnet using a starting IP that is not the network address of the # subnet. Ensure that we "round it down" to the proper subnet starting point. diff --git a/tests/ovstest.c b/tests/ovstest.c index 068dcbb9b..86a60db36 100644 --- a/tests/ovstest.c +++ b/tests/ovstest.c @@ -23,6 +23,7 @@ #include #include "command-line.h" #include "openvswitch/dynamic-string.h" +#include "openvswitch/vlog.h" #include "ovstest.h" #include "util.h" @@ -124,6 +125,22 @@ main(int argc, char *argv[]) "use --help for usage"); } + /* Disable logging to the console when running tests. + * + * By default, OVN and OVS errors and warnings are written to + * stderr. GNU Autotest automatically fails a test if unexpected + * data is written to stderr. This causes two problems: + * 1) Unit tests that attempt off-nominal code paths may + * fail because of a warning message in OVN or OVS. To get + * around this, it is common for tests to pass "[ignore]" + * to AT_CHECK's stderr parameter so that OVN/OVS log messages + * do not cause failures. But... + * 2) Passing "[ignore]" makes it so that unit tests cannot + * then print their own messages to stderr to help debug + * test failures. + */ + vlog_set_levels(NULL, VLF_CONSOLE, VLL_OFF); + add_top_level_commands(); if (argc > 1) { struct ovs_cmdl_context ctx = {