@@ -35,6 +35,7 @@
#include "lib/ovn-nb-idl.h"
#include "lib/ovn-sb-idl.h"
#include "lib/ovn-util.h"
+#include "lib/unit-test.h"
#include "ovn/actions.h"
#include "ovn/logical-fields.h"
#include "packets.h"
@@ -13050,6 +13051,8 @@ main(int argc, char *argv[])
cluster_state_reset_cmd,
&reset_ovnnb_idl_min_index);
+ register_unixctl_unit_test();
+
daemonize_complete();
/* We want to detect (almost) all changes to the ovn-nb db. */
@@ -13464,3 +13467,176 @@ cluster_state_reset_cmd(struct unixctl_conn *conn, int argc OVS_UNUSED,
poll_immediate_wake();
unixctl_command_reply(conn, NULL);
}
+
+#ifdef ENABLE_UNIT_TESTS
+
+static enum test_result
+test_init_ipam_ipv6_prefix(void *ignore OVS_UNUSED)
+{
+ struct ipam_info ipam;
+ struct {
+ const char *prefix;
+ bool expected_ipv6_prefix_set;
+ struct in6_addr expected_ipv6_prefix;
+ } test_cases[] = {
+ /* No prefix */
+ { NULL, false, in6addr_any },
+ /* Bad prefix */
+ { "aef02::", false, in6addr_any},
+ /* Bad subnet size */
+ { "aef0::/8", false, in6addr_any},
+ /* Good prefix, no subnet size */
+ { "aef0::", true, {{{0xae, 0xf0}}}},
+ /* Good prefix, good subnet size */
+ { "aef0::/64", true, {{{0xae, 0xf0}}}},
+ };
+
+ enum test_result result = OVN_TEST_PASS;
+
+ for (size_t i = 0; i < ARRAY_SIZE(test_cases); i++) {
+ memset(&ipam, 0, sizeof ipam);
+ init_ipam_ipv6_prefix(test_cases[i].prefix, &ipam);
+ if (ipam.ipv6_prefix_set != test_cases[i].expected_ipv6_prefix_set) {
+ VLOG_WARN("Expected ipv6_prefix_set to be %s but got %s",
+ test_cases[i].expected_ipv6_prefix_set ? "true" : "false",
+ ipam.ipv6_prefix_set ? "true" : "false");
+ result = OVN_TEST_FAIL;
+ continue;
+ }
+ if (ipam.ipv6_prefix_set
+ && !IN6_ARE_ADDR_EQUAL(&ipam.ipv6_prefix,
+ &test_cases[i].expected_ipv6_prefix)) {
+ char expected[INET6_ADDRSTRLEN];
+ char actual[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &ipam.ipv6_prefix,
+ actual, sizeof actual);
+ inet_ntop(AF_INET6, &test_cases[i].expected_ipv6_prefix,
+ expected, sizeof expected);
+ VLOG_WARN("Expected IPv6 prefix %s but got %s",
+ expected, actual);
+ result = OVN_TEST_FAIL;
+ continue;
+ }
+ }
+
+ return result;
+}
+
+UNIT_TEST_DEFINE(init_ipam_ipv6_prefix, test_init_ipam_ipv6_prefix, NULL);
+
+static enum test_result
+test_init_ipam_ipv4(void *ignore OVS_UNUSED)
+{
+ struct {
+ const char *subnet;
+ const char *exclude;
+ struct ipam_info info;
+ uint32_t expected_start_ip;
+ size_t expected_total_ips;
+ unsigned long *expected_allocated_ips;
+ } test_cases[] = {
+ { "192.168.0.0/24", NULL, {0,}, 0xc0a80001, 255,
+ bitmap_set1(bitmap_allocate(255), 0) },
+ { "192.168.0.0/24", "192.168.0.10", {0,}, 0xc0a80001, 255,
+ bitmap_set1(bitmap_set1(bitmap_allocate(255), 0), 9)},
+ { "192.168.0.0/24", "192.168.0.10..192.168.0.19", {0, }, 0xc0a80001, 255,
+ bitmap_set_multiple(bitmap_set1(bitmap_allocate(255), 0), 9, 10, true)},
+ };
+ enum test_result result = OVN_TEST_PASS;
+
+ for (size_t i = 0; i < ARRAY_SIZE(test_cases); i++) {
+ init_ipam_ipv4(test_cases[i].subnet,
+ test_cases[i].exclude,
+ &test_cases[i].info);
+
+ if (test_cases[i].info.start_ipv4 != test_cases[i].expected_start_ip) {
+ VLOG_WARN("Mismatched starting IP. Expected %" PRIu32
+ " but got %" PRIu32 "\n",
+ test_cases[i].expected_start_ip,
+ test_cases[i].info.start_ipv4);
+ result = OVN_TEST_FAIL;
+ continue;
+ }
+ if (test_cases[i].info.total_ipv4s != test_cases[i].expected_total_ips) {
+ VLOG_WARN("Mismatched total IPs. Expected %" PRIuSIZE
+ " but got %" PRIuSIZE "\n",
+ test_cases[i].expected_total_ips,
+ test_cases[i].info.total_ipv4s);
+ result = OVN_TEST_FAIL;
+ continue;
+ }
+ if (!bitmap_equal(test_cases[i].info.allocated_ipv4s,
+ test_cases[i].expected_allocated_ips,
+ test_cases[i].expected_total_ips)) {
+ VLOG_WARN("Mismatched allocated IPs\n");
+ result = OVN_TEST_FAIL;
+ continue;
+ }
+ }
+
+ for (size_t i = 0; i < ARRAY_SIZE(test_cases); i++) {
+ bitmap_free(test_cases[i].info.allocated_ipv4s);
+ bitmap_free(test_cases[i].expected_allocated_ips);
+ }
+
+ return result;
+}
+
+UNIT_TEST_DEFINE(init_ipam_ipv4, test_init_ipam_ipv4, NULL);
+
+static enum test_result
+test_ipam_get_unused_ip(void *ignore OVS_UNUSED)
+{
+ enum test_result result = OVN_TEST_PASS;
+ struct ipam_info info;
+
+ struct smap config = SMAP_INITIALIZER(&config);
+ smap_add(&config, "subnet", "192.168.0.0/29");
+ smap_add(&config, "exclude_ips", "192.168.0.5");
+ init_ipam_info(&info, &config);
+
+ uint32_t expected_ip [] = {
+ /* First IP we retrieve will be 192.168.0.2 since
+ * 192.168.0.1 is reserved for the connected router
+ */
+ 0xc0a80002,
+ 0xc0a80003,
+ 0xc0a80004,
+ /* We should skip 192.168.0.5 since it is excluded */
+ 0xc0a80006,
+ /* After 192.168.0.6, we should be out of space for the
+ * subnet.
+ */
+ 0,
+ /* Just to be safe, let's make sure we get the same result
+ * when we try again.
+ */
+ 0,
+ };
+
+ for (size_t i = 0; i < ARRAY_SIZE(expected_ip); i++) {
+ uint32_t next_ip;
+
+ next_ip = ipam_get_unused_ip(&info);
+ if (next_ip != expected_ip[i]) {
+ VLOG_WARN("Expected IP address "IP_FMT" but got "IP_FMT" instead",
+ IP_ARGS(htonl(expected_ip[i])), IP_ARGS(htonl(next_ip)));
+ result = OVN_TEST_FAIL;
+ break;
+ }
+ if (next_ip && !ipam_insert_ip(&info, next_ip)) {
+ VLOG_WARN("Unable to insert ip "IP_FMT" into IPAM",
+ IP_ARGS(htonl(next_ip)));
+ result = OVN_TEST_FAIL;
+ break;
+ }
+ }
+
+ smap_destroy(&config);
+ bitmap_free(info.allocated_ipv4s);
+ return result;
+}
+
+UNIT_TEST_DEFINE(ipam_get_unused_ip, test_ipam_get_unused_ip, NULL);
+
+#endif /* ENABLE_UNIT_TESTS */
@@ -30,7 +30,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-unit-tests.at
SYSTEM_KMOD_TESTSUITE_AT = \
tests/system-common-macros.at \
new file mode 100644
@@ -0,0 +1,26 @@
+AT_BANNER([OVN unit tests])
+
+AT_SETUP([ovn -- unit test -- init_ipam_ipv4])
+AT_SKIP_IF([test "$ENABLE_UNIT_TESTS" = no])
+ovn_start
+AT_CHECK([ovn-appctl -t northd/ovn-northd unit-test init_ipam_ipv4], [0], [dnl
+Unit test init_ipam_ipv4 passed
+])
+AT_CLEANUP
+
+AT_SETUP([ovn -- unit test -- init_ipam_ipv6_prefix])
+AT_SKIP_IF([test "$ENABLE_UNIT_TESTS" = no])
+ovn_start
+AT_CHECK([ovn-appctl -t northd/ovn-northd unit-test init_ipam_ipv6_prefix], [0], [dnl
+Unit test init_ipam_ipv6_prefix passed
+])
+AT_CLEANUP
+
+AT_SETUP([ovn -- unit test -- ipam_get_unused_ip])
+AT_SKIP_IF([test "$ENABLE_UNIT_TESTS" = no])
+ovn_start
+AT_CHECK([ovn-appctl -t northd/ovn-northd unit-test ipam_get_unused_ip], [0], [dnl
+Unit test ipam_get_unused_ip passed
+])
+AT_CLEANUP
+
@@ -21,6 +21,7 @@ m4_include([tests/ovsdb-macros.at])
m4_include([tests/ofproto-macros.at])
m4_include([tests/ovn-macros.at])
+m4_include([tests/ovn-unit-tests.at])
m4_include([tests/ovn.at])
m4_include([tests/ovn-performance.at])
m4_include([tests/ovn-northd.at])
This adds unit tests for IPAM IPv6 initialization, IPv4 initialization, and IPv4 address retrieval to ovn-northd. It also adds testsuite tests corresponding to these unit tests. Note that these unit tests are meant to serve as examples. They are not nearly as exhaustive as they should be in order to test their pertinent functions. Signed-off-by: Mark Michelson <mmichels@redhat.com> --- northd/ovn-northd.c | 176 ++++++++++++++++++++++++++++++++++++++++ tests/automake.mk | 3 +- tests/ovn-unit-tests.at | 26 ++++++ tests/testsuite.at | 1 + 4 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 tests/ovn-unit-tests.at