]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
netlink plugin: add unit tests for VF part
authorKamil Wiatrowski <kamilx.wiatrowski@intel.com>
Mon, 18 May 2020 13:14:01 +0000 (14:14 +0100)
committerKamil Wiatrowski <kamilx.wiatrowski@intel.com>
Thu, 21 May 2020 14:30:31 +0000 (15:30 +0100)
Add unit tests for VF part of netlink plugin.

Signed-off-by: Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
Makefile.am
src/collectd.conf.pod
src/netlink.c
src/netlink_test.c [new file with mode: 0644]

index cb8205fccb001184da9ce71b179ba1e144450810..fbf5f8d6113d035a7d5ea82bba1c292643e032dc 100644 (file)
@@ -1491,6 +1491,19 @@ netlink_la_SOURCES = src/netlink.c
 netlink_la_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
 netlink_la_LDFLAGS = $(PLUGIN_LDFLAGS)
 netlink_la_LIBADD = $(BUILD_WITH_LIBMNL_LIBS)
+
+test_plugin_netlink_SOURCES = \
+       src/netlink_test.c \
+       src/daemon/configfile.c \
+       src/daemon/types_list.c
+test_plugin_netlink_CFLAGS = $(AM_CFLAGS) $(BUILD_WITH_LIBMNL_CFLAGS)
+test_plugin_netlink_LDFLAGS = $(PLUGIN_LDFLAGS)
+test_plugin_netlink_LDADD = \
+       liboconfig.la \
+       libplugin_mock.la \
+       $(BUILD_WITH_LIBMNL_LIBS)
+check_PROGRAMS += test_plugin_netlink
+TESTS += test_plugin_netlink
 endif
 
 if BUILD_PLUGIN_NETWORK
index 64e20b04bc58ea71fd6a790e6b26ce688fa1dfd1..b45695254bcd30354810bb5cd03edbc456d21fda 100644 (file)
@@ -6018,6 +6018,21 @@ to get an idea of what awaits you:
 
 If I<Interface> is B<All>, all interfaces will be selected.
 
+It is possible to use regular expressions to match interface names, if the
+name is surrounded by I</.../> and collectd was compiled with support for
+regexps. This is useful if there's a need to collect (or ignore) data
+for a group of interfaces that are similarly named, without the need to
+explicitly list all of them (especially useful if the list is dynamic).
+Examples:
+
+ Interface "/^eth/"
+ Interface "/^ens[1-4]$|^enp[0-3]$/"
+ VerboseInterface "/^eno[0-9]+/"
+
+This will match all interfaces with names starting with I<eth>, all interfaces
+in range I<ens1 - ens4> and I<enp0 - enp3>, and for verbose metrics all
+interfaces with names starting with I<eno> followed by at least one digit.
+
 =item B<QDisc> I<Interface> [I<QDisc>]
 
 =item B<Class> I<Interface> [I<Class>]
@@ -6073,21 +6088,6 @@ available for interfaces specified in B<Interface> or B<VerboseInterface>.
 All available stats are collected no matter if parent interface is set
 by B<Interface> or B<VerboseInterface>.
 
-It is possible to use regular expressions to match interface names, if the
-name is surrounded by I</.../> and collectd was compiled with support for
-regexps. This is useful if there's a need to collect (or ignore) data
-for a group of interfaces that are similarly named, without the need to
-explicitly list all of them (especially useful if the list is dynamic).
-Examples:
-
- Interface "/^eth/"
- Interface "/^ens[1-4]$|^enp[0-3]$/"
- VerboseInterface "/^eno[0-9]+/"
-
-This will match all interfaces with names starting with I<eth>, all interfaces
-in range I<ens1 - ens4> and I<enp0 - enp3>, and for verbose metrics all
-interfaces with names starting with I<eno> followed by at least one digit.
-
 =back
 
 =head2 Plugin C<network>
index 62a46cef7127d45083d5e6a0eb7e240d07576bd7..abd31eb1a55c71da5f885b9513e9db6249bf59e2 100644 (file)
@@ -249,17 +249,20 @@ static int check_ignorelist(const char *dev, const char *type,
         (strcasecmp(i->inst, type_instance) != 0))
       continue;
 
+#if COLLECT_DEBUG
+#if HAVE_REGEX_H
+    const char *device = i->device == NULL
+                             ? (i->rdevice != NULL ? "(regexp)" : "(nil)")
+                             : i->device;
+#else
+    const char *device = i->device == NULL ? "(nil)" : i->device;
+#endif
     DEBUG("netlink plugin: check_ignorelist: "
           "(dev = %s; type = %s; inst = %s) matched "
           "(dev = %s; type = %s; inst = %s)",
-          dev, type, type_instance == NULL ? "(nil)" : type_instance,
-          i->device == NULL ?
-#if HAVE_REGEX_H
-                            i->rdevice != NULL ? "(regexp)" :
-#endif
-                                               "(nil)"
-                            : i->device,
+          dev, type, type_instance == NULL ? "(nil)" : type_instance, device,
           i->type, i->inst == NULL ? "(nil)" : i->inst);
+#endif
 
     return ir_ignorelist_invert ? 0 : 1;
   } /* for i */
diff --git a/src/netlink_test.c b/src/netlink_test.c
new file mode 100644 (file)
index 0000000..447ddb8
--- /dev/null
@@ -0,0 +1,364 @@
+/**
+ * collectd - src/netlink_test.c
+ *
+ * Copyright(c) 2020 Intel Corporation. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors:
+ *   Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
+ **/
+
+#define plugin_dispatch_values plugin_dispatch_values_nl_test
+
+#include "netlink.c" /* sic */
+#include "testing.h"
+
+static vf_stats_t g_test_res;
+static char g_instance[512];
+static int g_type_valid;
+static void *g_test_payload;
+static uint16_t g_attr_type;
+
+/* mock functions */
+int plugin_dispatch_values_nl_test(value_list_t const *vl) {
+  if (g_instance[0] == '\0')
+    sstrncpy(g_instance, vl->plugin_instance, sizeof(g_instance));
+
+  if (strcmp("vf_link_info", vl->type) == 0 &&
+      strcmp("vlan", vl->type_instance) == 0)
+    g_test_res.vlan = vl->values[0].gauge;
+
+  if (strcmp("vf_link_info", vl->type) == 0 &&
+      strcmp("spoofcheck", vl->type_instance) == 0)
+    g_test_res.spoofcheck = vl->values[0].gauge;
+
+  if (strcmp("vf_link_info", vl->type) == 0 &&
+      strcmp("link_state", vl->type_instance) == 0)
+    g_test_res.link_state = vl->values[0].gauge;
+
+  if (strcmp("vf_broadcast", vl->type) == 0)
+    g_test_res.broadcast = vl->values[0].derive;
+
+  if (strcmp("vf_multicast", vl->type) == 0)
+    g_test_res.multicast = vl->values[0].derive;
+
+  if (strcmp("vf_packets", vl->type) == 0) {
+    g_test_res.rx_packets = vl->values[0].derive;
+    g_test_res.tx_packets = vl->values[1].derive;
+  }
+
+  if (strcmp("vf_bytes", vl->type) == 0) {
+    g_test_res.rx_bytes = vl->values[0].derive;
+    g_test_res.tx_bytes = vl->values[1].derive;
+  }
+
+  return 0;
+}
+
+int mnl_attr_type_valid(__attribute__((unused)) const struct nlattr *attr,
+                        __attribute__((unused)) uint16_t maxtype) {
+  return g_type_valid;
+}
+
+uint16_t mnl_attr_get_type(__attribute__((unused)) const struct nlattr *attr) {
+  return g_attr_type;
+}
+
+int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type) {
+  return attr->nla_type == type ? 0 : -1;
+}
+
+int mnl_attr_validate2(const struct nlattr *attr, enum mnl_attr_data_type type,
+                       __attribute__((unused)) size_t len) {
+  return attr->nla_type == type ? 0 : -1;
+}
+
+void *mnl_attr_get_payload(__attribute__((unused)) const struct nlattr *attr) {
+  return g_test_payload;
+}
+/* end mock functions */
+
+DEF_TEST(plugin_nl_config) {
+  EXPECT_EQ_INT(0, collect_vf_stats);
+  int ret = ir_config("CollectVFStats", "true");
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, collect_vf_stats);
+  ret = ir_config("CollectVFStats", "0");
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0, collect_vf_stats);
+  ret = ir_config("CollectVFStats", "true false");
+  EXPECT_EQ_INT(-1, ret);
+  EXPECT_EQ_INT(0, collect_vf_stats);
+  ret = ir_config("CollectVFStats", "false");
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(0, collect_vf_stats);
+  ret = ir_config("CollectVFStats", "yes");
+  EXPECT_EQ_INT(0, ret);
+  EXPECT_EQ_INT(1, collect_vf_stats);
+
+  return 0;
+}
+
+DEF_TEST(ignorelist_test) {
+  ir_ignorelist_invert = 1;
+  int ret = add_ignorelist("eno1", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = add_ignorelist("eno2", "if_detail", NULL);
+  EXPECT_EQ_INT(0, ret);
+
+  ret = check_ignorelist("eno1", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("eno2", "if_detail", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("eno1", "if_detail", NULL);
+  EXPECT_EQ_INT(1, ret);
+  ret = check_ignorelist("eno2", "interface", NULL);
+  EXPECT_EQ_INT(1, ret);
+
+#if HAVE_REGEX_H
+  ret = add_ignorelist("/^eno[1-3]|^eth[0-2|4]/", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = add_ignorelist("/^ens0[1|3]/", "if_detail", NULL);
+  EXPECT_EQ_INT(0, ret);
+
+  ret = check_ignorelist("eno1", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("eno3", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("eth0", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("eth1", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("eth2", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("eth3", "interface", NULL);
+  EXPECT_EQ_INT(1, ret);
+  ret = check_ignorelist("eth4", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+
+  ret = check_ignorelist("ens01", "if_detail", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("ens02", "if_detail", NULL);
+  EXPECT_EQ_INT(1, ret);
+  ret = check_ignorelist("ens03", "if_detail", NULL);
+  EXPECT_EQ_INT(0, ret);
+
+  ret = check_ignorelist("eth0", "if_detail", NULL);
+  EXPECT_EQ_INT(1, ret);
+  ret = check_ignorelist("ens01", "interface", NULL);
+  EXPECT_EQ_INT(1, ret);
+#endif
+
+  ir_ignorelist_invert = 0;
+  ret = check_ignorelist("eno1", "interface", NULL);
+  EXPECT_EQ_INT(1, ret);
+  ret = check_ignorelist("eno2", "if_detail", NULL);
+  EXPECT_EQ_INT(1, ret);
+  ret = check_ignorelist("abcdf", "if_detail", NULL);
+  EXPECT_EQ_INT(0, ret);
+  ret = check_ignorelist("abcfdf", "interface", NULL);
+  EXPECT_EQ_INT(0, ret);
+
+#if HAVE_REGEX_H
+  ret = check_ignorelist("ens03", "if_detail", NULL);
+  EXPECT_EQ_INT(1, ret);
+#endif
+  ir_ignorelist_invert = 1;
+
+  ir_ignorelist_t *next = NULL;
+  for (ir_ignorelist_t *i = ir_ignorelist_head; i != NULL; i = next) {
+    next = i->next;
+#if HAVE_REGEX_H
+    if (i->rdevice != NULL) {
+      regfree(i->rdevice);
+      sfree(i->rdevice);
+    }
+#endif
+    sfree(i->inst);
+    sfree(i->type);
+    sfree(i->device);
+    sfree(i);
+  }
+  ir_ignorelist_head = NULL;
+
+  return 0;
+}
+
+DEF_TEST(vf_submit_test) {
+  const char *test_dev = "eth0";
+  vf_stats_t test_stats;
+  struct ifla_vf_mac test_mac;
+  test_mac.mac[0] = 0x01;
+  test_mac.mac[1] = 0x1a;
+  test_mac.mac[2] = 0x2b;
+  test_mac.mac[3] = 0x3c;
+  test_mac.mac[4] = 0x4d;
+  test_mac.mac[5] = 0x5e;
+  test_mac.vf = 2;
+  test_stats.vf_mac = &test_mac;
+  test_stats.vlan = 100;
+  test_stats.spoofcheck = 1;
+  test_stats.link_state = 2;
+  test_stats.broadcast = 1234;
+  test_stats.multicast = 0;
+  test_stats.rx_packets = 21110;
+  test_stats.tx_packets = 31110;
+  test_stats.rx_bytes = 4294967295;
+  test_stats.tx_bytes = 8;
+
+  g_instance[0] = '\0';
+  vf_info_submit(test_dev, &test_stats);
+
+  EXPECT_EQ_STR("eth0_vf2_01:1a:2b:3c:4d:5e", g_instance);
+  EXPECT_EQ_UINT64(100, g_test_res.vlan);
+  EXPECT_EQ_UINT64(1, g_test_res.spoofcheck);
+  EXPECT_EQ_UINT64(2, g_test_res.link_state);
+  EXPECT_EQ_UINT64(1234, g_test_res.broadcast);
+  EXPECT_EQ_UINT64(0, g_test_res.multicast);
+  EXPECT_EQ_UINT64(21110, g_test_res.rx_packets);
+  EXPECT_EQ_UINT64(31110, g_test_res.tx_packets);
+  EXPECT_EQ_UINT64(4294967295, g_test_res.rx_bytes);
+  EXPECT_EQ_UINT64(8, g_test_res.tx_bytes);
+
+  return 0;
+}
+
+DEF_TEST(vf_info_attr_cb_test) {
+  struct nlattr attr;
+  vf_stats_t test_stats = {0};
+
+  attr.nla_type = -1;
+  g_type_valid = -1;
+  int ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+
+  struct ifla_vf_mac test_mac;
+  g_test_payload = &test_mac;
+  g_type_valid = 0;
+  g_attr_type = IFLA_VF_MAC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  attr.nla_type = MNL_TYPE_UNSPEC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+  EXPECT_EQ_PTR(&test_mac, test_stats.vf_mac);
+
+  struct ifla_vf_vlan test_vlan = {.vlan = 1024, .qos = 2};
+  g_test_payload = &test_vlan;
+  g_attr_type = IFLA_VF_VLAN;
+  attr.nla_type = -1;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  attr.nla_type = MNL_TYPE_UNSPEC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+  EXPECT_EQ_UINT64(1024, test_stats.vlan);
+  EXPECT_EQ_UINT64(2, test_stats.qos);
+
+  struct ifla_vf_tx_rate test_tx_rate = {.rate = 100};
+  g_test_payload = &test_tx_rate;
+  g_attr_type = IFLA_VF_TX_RATE;
+  attr.nla_type = -1;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  attr.nla_type = MNL_TYPE_UNSPEC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+  EXPECT_EQ_UINT64(100, test_stats.txrate);
+
+  struct ifla_vf_spoofchk test_spoofchk = {.setting = 1};
+  g_test_payload = &test_spoofchk;
+  g_attr_type = IFLA_VF_SPOOFCHK;
+  attr.nla_type = -1;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  attr.nla_type = MNL_TYPE_UNSPEC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+  EXPECT_EQ_UINT64(1, test_stats.spoofcheck);
+
+  struct ifla_vf_link_state test_link_state = {.link_state = 2};
+  g_test_payload = &test_link_state;
+  g_attr_type = IFLA_VF_LINK_STATE;
+  attr.nla_type = -1;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  attr.nla_type = MNL_TYPE_UNSPEC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+  EXPECT_EQ_UINT64(2, test_stats.link_state);
+
+  struct ifla_vf_rate test_rate = {.min_tx_rate = 1000, .max_tx_rate = 2001};
+  g_test_payload = &test_rate;
+  g_attr_type = IFLA_VF_RATE;
+  attr.nla_type = -1;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  attr.nla_type = MNL_TYPE_UNSPEC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+  EXPECT_EQ_UINT64(1000, test_stats.min_txrate);
+  EXPECT_EQ_UINT64(2001, test_stats.max_txrate);
+
+  struct ifla_vf_rss_query_en test_query_en = {.setting = 1};
+  g_test_payload = &test_query_en;
+  g_attr_type = IFLA_VF_RSS_QUERY_EN;
+  attr.nla_type = -1;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  attr.nla_type = MNL_TYPE_UNSPEC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+  EXPECT_EQ_UINT64(1, test_stats.rss_query_en);
+
+  struct ifla_vf_trust test_trust = {.setting = 1};
+  g_test_payload = &test_trust;
+  g_attr_type = IFLA_VF_TRUST;
+  attr.nla_type = -1;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  attr.nla_type = MNL_TYPE_UNSPEC;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_OK, ret);
+  EXPECT_EQ_UINT64(1, test_stats.trust);
+
+  g_attr_type = IFLA_VF_STATS;
+  ret = vf_info_attr_cb(&attr, &test_stats);
+  EXPECT_EQ_INT(MNL_CB_ERROR, ret);
+
+  return 0;
+}
+
+int main(void) {
+  RUN_TEST(plugin_nl_config);
+  RUN_TEST(ignorelist_test);
+
+  RUN_TEST(vf_submit_test);
+  RUN_TEST(vf_info_attr_cb_test);
+  END_TEST;
+}