@@ -13,7 +13,7 @@ CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
LIBDEPS += -L$(multipathdir) -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
- alias directio
+ alias directio valid
.SILENT: $(TESTS:%=%.o)
.PRECIOUS: $(TESTS:%=%-test)
@@ -50,6 +50,8 @@ vpd-test_OBJDEPS := ../libmultipath/discovery.o
vpd-test_LIBDEPS := -ludev -lpthread -ldl
alias-test_TESTDEPS := test-log.o
alias-test_LIBDEPS := -lpthread -ldl
+valid-test_OBJDEPS := ../libmultipath/valid.o
+valid-test_LIBDEPS := -ludev -lpthread -ldl
ifneq ($(DIO_TEST_DEV),)
directio-test_LIBDEPS := -laio
endif
new file mode 100644
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2020 Benjamin Marzinski, Redhat
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <cmocka.h>
+#include "globals.c"
+#include "util.h"
+#include "discovery.h"
+#include "wwids.h"
+#include "blacklist.h"
+#include "valid.h"
+
+int test_fd;
+struct udev_device {
+ int unused;
+} test_udev;
+
+bool __wrap_sysfs_is_multipathed(struct path *pp, bool set_wwid)
+{
+ bool is_multipathed = mock_type(bool);
+ assert_non_null(pp);
+ assert_int_not_equal(strlen(pp->dev), 0);
+ if (is_multipathed && set_wwid)
+ strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+ return is_multipathed;
+}
+
+int __wrap___mpath_connect(int nonblocking)
+{
+ bool connected = mock_type(bool);
+ assert_int_equal(nonblocking, 1);
+ if (connected)
+ return test_fd;
+ errno = mock_type(int);
+ return -1;
+}
+
+int __wrap_systemd_service_enabled(const char *dev)
+{
+ return (int)mock_type(bool);
+}
+
+/* There's no point in checking the return value here */
+int __wrap_mpath_disconnect(int fd)
+{
+ assert_int_equal(fd, test_fd);
+ return 0;
+}
+
+struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
+{
+ bool passed = mock_type(bool);
+ assert_string_equal(sysname, mock_ptr_type(char *));
+ if (passed)
+ return &test_udev;
+ return NULL;
+}
+
+int __wrap_pathinfo(struct path *pp, struct config *conf, int mask)
+{
+ int ret = mock_type(int);
+ assert_string_equal(pp->dev, mock_ptr_type(char *));
+ assert_int_equal(mask, DI_SYSFS | DI_WWID | DI_BLACKLIST);
+ if (ret == PATHINFO_OK) {
+ pp->uid_attribute = "ID_TEST";
+ strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+ } else
+ memset(pp->wwid, 0, WWID_SIZE);
+ return ret;
+}
+
+int __wrap_filter_property(struct config *conf, struct udev_device *udev,
+ int lvl, const char *uid_attribute)
+{
+ int ret = mock_type(int);
+ assert_string_equal(uid_attribute, "ID_TEST");
+ return ret;
+}
+
+int __wrap_is_failed_wwid(const char *wwid)
+{
+ int ret = mock_type(int);
+ assert_string_equal(wwid, mock_ptr_type(char *));
+ return ret;
+}
+
+int __wrap_check_wwids_file(char *wwid, int write_wwid)
+{
+ bool passed = mock_type(bool);
+ assert_int_equal(write_wwid, 0);
+ assert_string_equal(wwid, mock_ptr_type(char *));
+ if (passed)
+ return 0;
+ else
+ return -1;
+}
+
+int __wrap_dm_map_present_by_uuid(const char *uuid)
+{
+ int ret = mock_type(int);
+ assert_string_equal(uuid, mock_ptr_type(char *));
+ return ret;
+}
+
+enum {
+ STAGE_IS_MULTIPATHED,
+ STAGE_CHECK_MULTIPATHD,
+ STAGE_GET_UDEV_DEVICE,
+ STAGE_PATHINFO,
+ STAGE_FILTER_PROPERTY,
+ STAGE_IS_FAILED,
+ STAGE_CHECK_WWIDS,
+ STAGE_UUID_PRESENT,
+};
+
+enum {
+ CHECK_MPATHD_RUNNING,
+ CHECK_MPATHD_EAGAIN,
+ CHECK_MPATHD_ENABLED,
+ CHECK_MPATHD_SKIP,
+};
+
+/* setup the test to continue past the given stage in is_path_valid() */
+static void setup_passing(char *name, char *wwid, unsigned int check_multipathd,
+ unsigned int stage)
+{
+ will_return(__wrap_sysfs_is_multipathed, false);
+ if (stage == STAGE_IS_MULTIPATHED)
+ return;
+ if (check_multipathd == CHECK_MPATHD_RUNNING)
+ will_return(__wrap___mpath_connect, true);
+ else if (check_multipathd == CHECK_MPATHD_EAGAIN) {
+ will_return(__wrap___mpath_connect, false);
+ will_return(__wrap___mpath_connect, EAGAIN);
+ } else if (check_multipathd == CHECK_MPATHD_ENABLED) {
+ will_return(__wrap___mpath_connect, false);
+ will_return(__wrap___mpath_connect, ECONNREFUSED);
+ will_return(__wrap_systemd_service_enabled, true);
+ }
+ /* nothing for CHECK_MPATHD_SKIP */
+ if (stage == STAGE_CHECK_MULTIPATHD)
+ return;
+ will_return(__wrap_udev_device_new_from_subsystem_sysname, true);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname,
+ name);
+ if (stage == STAGE_GET_UDEV_DEVICE)
+ return;
+ will_return(__wrap_pathinfo, PATHINFO_OK);
+ will_return(__wrap_pathinfo, name);
+ will_return(__wrap_pathinfo, wwid);
+ if (stage == STAGE_PATHINFO)
+ return;
+ will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_EXCEPT);
+ if (stage == STAGE_FILTER_PROPERTY)
+ return;
+ will_return(__wrap_is_failed_wwid, WWID_IS_NOT_FAILED);
+ will_return(__wrap_is_failed_wwid, wwid);
+ if (stage == STAGE_IS_FAILED)
+ return;
+ will_return(__wrap_check_wwids_file, false);
+ will_return(__wrap_check_wwids_file, wwid);
+ if (stage == STAGE_CHECK_WWIDS)
+ return;
+ will_return(__wrap_dm_map_present_by_uuid, 0);
+ will_return(__wrap_dm_map_present_by_uuid, wwid);
+}
+
+static void test_bad_arguments(void **state)
+{
+ struct path pp;
+ char too_long[FILE_NAME_SIZE + 1];
+
+ memset(&pp, 0, sizeof(pp));
+ /* test NULL pointers */
+ assert_int_equal(is_path_valid("test", &conf, NULL, true),
+ PATH_IS_ERROR);
+ assert_int_equal(is_path_valid("test", NULL, &pp, true),
+ PATH_IS_ERROR);
+ assert_int_equal(is_path_valid(NULL, &conf, &pp, true),
+ PATH_IS_ERROR);
+ /* test undefined find_multipaths */
+ conf.find_multipaths = FIND_MULTIPATHS_UNDEF;
+ assert_int_equal(is_path_valid("test", &conf, &pp, true),
+ PATH_IS_ERROR);
+ /* test name too long */
+ memset(too_long, 'x', sizeof(too_long));
+ too_long[sizeof(too_long) - 1] = '\0';
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ assert_int_equal(is_path_valid(too_long, &conf, &pp, true),
+ PATH_IS_ERROR);
+}
+
+static void test_sysfs_is_multipathed(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test_wwid";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ /* test for already existing multiapthed device */
+ will_return(__wrap_sysfs_is_multipathed, true);
+ will_return(__wrap_sysfs_is_multipathed, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_VALID_NO_CHECK);
+ assert_string_equal(pp.dev, name);
+ assert_string_equal(pp.wwid, wwid);
+ /* test for wwid device with empty wwid */
+ will_return(__wrap_sysfs_is_multipathed, true);
+ will_return(__wrap_sysfs_is_multipathed, "");
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_ERROR);
+}
+
+static void test_check_multipathd(void **state)
+{
+ struct path pp;
+ char *name = "test";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ /* test failed check to see if multipathd is active */
+ will_return(__wrap_sysfs_is_multipathed, false);
+ will_return(__wrap___mpath_connect, false);
+ will_return(__wrap___mpath_connect, ECONNREFUSED);
+ will_return(__wrap_systemd_service_enabled, false);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_NOT_VALID);
+ assert_string_equal(pp.dev, name);
+ /* test pass because service is enabled. fail getting udev */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, NULL, CHECK_MPATHD_ENABLED, STAGE_CHECK_MULTIPATHD);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname,
+ name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_ERROR);
+ assert_string_equal(pp.dev, name);
+ /* test pass because connect returned EAGAIN. fail getting udev */
+ setup_passing(name, NULL, CHECK_MPATHD_EAGAIN, STAGE_CHECK_MULTIPATHD);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname,
+ name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_ERROR);
+ /* test pass because connect succeeded. fail getting udev */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, NULL, CHECK_MPATHD_RUNNING, STAGE_CHECK_MULTIPATHD);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+ will_return(__wrap_udev_device_new_from_subsystem_sysname,
+ name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_ERROR);
+ assert_string_equal(pp.dev, name);
+}
+
+static void test_pathinfo(void **state)
+{
+ struct path pp;
+ char *name = "test";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ /* Test pathinfo blacklisting device */
+ setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+ will_return(__wrap_pathinfo, PATHINFO_SKIPPED);
+ will_return(__wrap_pathinfo, name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ /* Test pathinfo failing */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+ will_return(__wrap_pathinfo, PATHINFO_FAILED);
+ will_return(__wrap_pathinfo, name);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_ERROR);
+ /* Test blank wwid */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+ will_return(__wrap_pathinfo, PATHINFO_OK);
+ will_return(__wrap_pathinfo, name);
+ will_return(__wrap_pathinfo, "");
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+}
+
+static void test_filter_property(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ /* test blacklist property */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+ will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+ /* test missing property */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+ will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_MISSING);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ /* test MATCH_NOTHING fail on is_failed_wwid */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+ will_return(__wrap_filter_property, MATCH_NOTHING);
+ will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
+ will_return(__wrap_is_failed_wwid, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+}
+
+static void test_is_failed_wwid(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ /* Test wwid failed */
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY);
+ will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
+ will_return(__wrap_is_failed_wwid, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+ /* test is_failed_wwid error */
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY);
+ will_return(__wrap_is_failed_wwid, WWID_FAILED_ERROR);
+ will_return(__wrap_is_failed_wwid, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_ERROR);
+}
+
+static void test_greedy(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ /* test greedy success with checking multipathd */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_GREEDY;
+ setup_passing(name, wwid, CHECK_MPATHD_RUNNING, STAGE_IS_FAILED);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+ /* test greedy success without checking multiapthd */
+ memset(&pp, 0, sizeof(pp));
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_IS_FAILED);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_VALID);
+}
+
+static void test_check_wwids(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ setup_passing(name, wwid, CHECK_MPATHD_EAGAIN, STAGE_IS_FAILED);
+ will_return(__wrap_check_wwids_file, true);
+ will_return(__wrap_check_wwids_file, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_VALID_NO_CHECK);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+}
+
+static void test_check_uuid_present(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ setup_passing(name, wwid, CHECK_MPATHD_ENABLED, STAGE_CHECK_WWIDS);
+ will_return(__wrap_dm_map_present_by_uuid, 1);
+ will_return(__wrap_dm_map_present_by_uuid, wwid);
+ assert_int_equal(is_path_valid(name, &conf, &pp, true),
+ PATH_IS_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+}
+
+
+static void test_find_multipaths(void **state)
+{
+ struct path pp;
+ char *name = "test";
+ char *wwid = "test-wwid";
+
+ /* test find_multipaths = FIND_MULTIPATHS_STRICT */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+ /* test find_multipaths = FIND_MULTIPATHS_OFF */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_OFF;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ /* test find_multipaths = FIND_MULTIPATHS_ON */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_ON;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_NOT_VALID);
+ /* test find_multipaths = FIND_MULTIPATHS_SMART */
+ memset(&pp, 0, sizeof(pp));
+ conf.find_multipaths = FIND_MULTIPATHS_SMART;
+ setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+ assert_int_equal(is_path_valid(name, &conf, &pp, false),
+ PATH_IS_MAYBE_VALID);
+ assert_string_equal(pp.dev, name);
+ assert_ptr_equal(pp.udev, &test_udev);
+ assert_string_equal(pp.wwid, wwid);
+}
+
+int test_valid(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(test_bad_arguments),
+ cmocka_unit_test(test_sysfs_is_multipathed),
+ cmocka_unit_test(test_check_multipathd),
+ cmocka_unit_test(test_pathinfo),
+ cmocka_unit_test(test_filter_property),
+ cmocka_unit_test(test_is_failed_wwid),
+ cmocka_unit_test(test_greedy),
+ cmocka_unit_test(test_check_wwids),
+ cmocka_unit_test(test_check_uuid_present),
+ cmocka_unit_test(test_find_multipaths),
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+int main(void)
+{
+ int ret = 0;
+ ret += test_valid();
+ return ret;
+}