diff mbox series

[ovs-dev,RFC,3/3] northd: Add init_ipam unit tests.

Message ID 20201009152928.1312612-4-mmichels@redhat.com
State New
Headers show
Series Unit Testing in OVN | expand

Commit Message

Mark Michelson Oct. 9, 2020, 3:29 p.m. UTC
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
diff mbox series

Patch

diff --git a/northd/ovn-northd.c b/northd/ovn-northd.c
index d989ddf53..10671edb7 100644
--- a/northd/ovn-northd.c
+++ b/northd/ovn-northd.c
@@ -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 */
diff --git a/tests/automake.mk b/tests/automake.mk
index 26b6d11b4..9ad577576 100644
--- a/tests/automake.mk
+++ b/tests/automake.mk
@@ -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 \
diff --git a/tests/ovn-unit-tests.at b/tests/ovn-unit-tests.at
new file mode 100644
index 000000000..b7d16667c
--- /dev/null
+++ b/tests/ovn-unit-tests.at
@@ -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
+
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 1985923d5..5e60bad73 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -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])