From: Kamil Wiatrowski Date: Mon, 18 May 2020 13:14:01 +0000 (+0100) Subject: netlink plugin: add unit tests for VF part X-Git-Tag: collectd-5.12.0~15^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=70f8e6cb435b1424cbe956ddbac390a7ab0b8e15;p=thirdparty%2Fcollectd.git netlink plugin: add unit tests for VF part Add unit tests for VF part of netlink plugin. Signed-off-by: Kamil Wiatrowski --- diff --git a/Makefile.am b/Makefile.am index cb8205fcc..fbf5f8d61 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 64e20b04b..b45695254 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -6018,6 +6018,21 @@ to get an idea of what awaits you: If I is B, 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, all interfaces +in range I and I, and for verbose metrics all +interfaces with names starting with I followed by at least one digit. + =item B I [I] =item B I [I] @@ -6073,21 +6088,6 @@ available for interfaces specified in B or B. All available stats are collected no matter if parent interface is set by B or B. -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, all interfaces -in range I and I, and for verbose metrics all -interfaces with names starting with I followed by at least one digit. - =back =head2 Plugin C diff --git a/src/netlink.c b/src/netlink.c index 62a46cef7..abd31eb1a 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -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 index 000000000..447ddb818 --- /dev/null +++ b/src/netlink_test.c @@ -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 + **/ + +#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; +}