From 0d525d9179c0bba4ffac4f1f320c7a092def3e43 Mon Sep 17 00:00:00 2001 From: Jan Kryl Date: Wed, 16 May 2018 10:42:38 +0200 Subject: [PATCH] US1533 replacing disk in a pool Signed-off-by: Jan Kryl --- module/zfs/zfs_ioctl.c | 78 +++++++++-------- tests/cbtest/gtest/test_zrepl_prot.cc | 120 ++++++++++++++++++++++---- 2 files changed, 145 insertions(+), 53 deletions(-) diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index e76f01b37e31..6093f55d2b60 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -2021,7 +2021,41 @@ zfs_ioc_vdev_remove(zfs_cmd_t *zc) return (error); } -#if defined(_KERNEL) +static int +zfs_ioc_vdev_attach(zfs_cmd_t *zc) +{ + spa_t *spa; + int replacing = zc->zc_cookie; + nvlist_t *config; + int error; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, + zc->zc_iflags, &config)) == 0) { + error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); + nvlist_free(config); + } + + spa_close(spa, FTAG); + return (error); +} + +static int +zfs_ioc_vdev_detach(zfs_cmd_t *zc) +{ + spa_t *spa; + int error; + + if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) + return (error); + + error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); + + spa_close(spa, FTAG); + return (error); +} static int zfs_ioc_vdev_set_state(zfs_cmd_t *zc) @@ -2066,41 +2100,7 @@ zfs_ioc_vdev_set_state(zfs_cmd_t *zc) return (error); } -static int -zfs_ioc_vdev_attach(zfs_cmd_t *zc) -{ - spa_t *spa; - int replacing = zc->zc_cookie; - nvlist_t *config; - int error; - - if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) - return (error); - - if ((error = get_nvlist(zc->zc_nvlist_conf, zc->zc_nvlist_conf_size, - zc->zc_iflags, &config)) == 0) { - error = spa_vdev_attach(spa, zc->zc_guid, config, replacing); - nvlist_free(config); - } - - spa_close(spa, FTAG); - return (error); -} - -static int -zfs_ioc_vdev_detach(zfs_cmd_t *zc) -{ - spa_t *spa; - int error; - - if ((error = spa_open(zc->zc_name, &spa, FTAG)) != 0) - return (error); - - error = spa_vdev_detach(spa, zc->zc_guid, 0, B_FALSE); - - spa_close(spa, FTAG); - return (error); -} +#if defined(_KERNEL) static int zfs_ioc_vdev_split(zfs_cmd_t *zc) @@ -7279,6 +7279,12 @@ uzfs_handle_ioctl(const char *pool, zfs_cmd_t *zc, uzfs_info_t *ucmd_info) return (zfs_ioc_vdev_add(zc)); case ZFS_IOC_VDEV_REMOVE: return (zfs_ioc_vdev_remove(zc)); + case ZFS_IOC_VDEV_ATTACH: + return (zfs_ioc_vdev_attach(zc)); + case ZFS_IOC_VDEV_DETACH: + return (zfs_ioc_vdev_detach(zc)); + case ZFS_IOC_VDEV_SET_STATE: + return (zfs_ioc_vdev_set_state(zc)); } return (err); } diff --git a/tests/cbtest/gtest/test_zrepl_prot.cc b/tests/cbtest/gtest/test_zrepl_prot.cc index 121c833c47f1..3300e0e7a9e6 100644 --- a/tests/cbtest/gtest/test_zrepl_prot.cc +++ b/tests/cbtest/gtest/test_zrepl_prot.cc @@ -42,6 +42,7 @@ #include #include #include +#include #include #include "gtest_utils.h" @@ -420,6 +421,17 @@ static void graceful_close(int sockfd) close(sockfd); } +static std::string getPoolState(std::string pname) +{ + std::string s; + + s = execCmd("zpool", std::string("list -Ho health ") + pname); + // Trim white space at the end of string + s.erase(std::find_if(s.rbegin(), s.rend(), + std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + return (s); +} + /* * zrepl program wrapper. * @@ -566,23 +578,13 @@ class Target { int m_listenfd; }; -/* - * Class simplifying test zfs pool creation and creation of zvols on it. - * Automatic pool destruction takes place when object goes out of scope. - */ -class TestPool { +class Vdev { public: - TestPool(std::string poolname) { - m_name = poolname; - m_path = std::string("/tmp/") + m_name; + Vdev(std::string name) { + m_path = std::string("/tmp/") + name; } - ~TestPool() { - //try { - execCmd("zpool", std::string("destroy -f ") + m_name); - //} catch (std::runtime_error re) { - //; - //} + ~Vdev() { unlink(m_path.c_str()); } @@ -599,8 +601,35 @@ class TestPool { if (rc != 0) throw std::system_error(errno, std::system_category(), "Cannot truncate vdev file"); + } + + std::string m_path; +}; + +/* + * Class simplifying test zfs pool creation and creation of zvols on it. + * Automatic pool destruction takes place when object goes out of scope. + */ +class TestPool { +public: + TestPool(std::string poolname) { + m_name = poolname; + m_vdev = new Vdev(std::string("disk-for-") + poolname); + } + + ~TestPool() { + //try { + execCmd("zpool", std::string("destroy -f ") + m_name); + //} catch (std::runtime_error re) { + //; + //} + delete m_vdev; + } + + void create() { + m_vdev->create(); execCmd("zpool", std::string("create ") + m_name + " " + - m_path); + m_vdev->m_path); } void import() { @@ -621,8 +650,8 @@ class TestPool { return (m_name + "/" + name); } + Vdev *m_vdev; std::string m_name; - std::string m_path; }; class ZreplHandshakeTest : public testing::Test { @@ -1338,7 +1367,6 @@ class ZreplBlockSizeTest : public testing::Test { protected: /* * Shared setup hook for all zrepl block size tests - called just once. - * Creates a zvol with 4k block size. */ static void SetUpTestCase() { zvol_io_hdr_t hdr_out, hdr_in; @@ -1434,3 +1462,61 @@ TEST_F(ZreplBlockSizeTest, SetDifferentMetaBlockSizes) { do_data_connection(m_data_fd, m_host, m_port, m_zvol_name, 512, 120, ZVOL_OP_STATUS_FAILED); } + +/* + * Test disk replacement + */ +TEST(DiskReplaceTest, SpareReplacement) { + Zrepl zrepl; + Target target; + int rc, data_fd, control_fd; + std::string host; + uint16_t port; + int ioseq; + Vdev vdev2("vdev2"); + Vdev spare("spare"); + TestPool pool("rplcpool"); + + zrepl.start(); + vdev2.create(); + spare.create(); + pool.create(); + pool.createZvol("vol", "-o io.openebs:targetip=127.0.0.1"); + + rc = target.listen(); + ASSERT_GE(rc, 0); + control_fd = target.accept(-1); + ASSERT_GE(control_fd, 0); + do_handshake(pool.getZvolName("vol"), host, port, NULL, control_fd, + ZVOL_OP_STATUS_OK); + do_data_connection(data_fd, host, port, pool.getZvolName("vol")); + write_data_and_verify_resp(data_fd, ioseq, 0, 10); + + // construct mirrored pool with a spare + execCmd("zpool", std::string("attach ") + pool.m_name + " " + + pool.m_vdev->m_path + " " + vdev2.m_path); + write_data_and_verify_resp(data_fd, ioseq, 0, 10); + execCmd("zpool", std::string("add ") + pool.m_name + " spare " + + spare.m_path); + write_data_and_verify_resp(data_fd, ioseq, 0, 10); + ASSERT_STREQ(getPoolState(pool.m_name).c_str(), "ONLINE"); + + // fail one of the disks in the mirror + execCmd("zpool", std::string("offline ") + pool.m_name + " " + + vdev2.m_path); + write_data_and_verify_resp(data_fd, ioseq, 0, 10); + ASSERT_STREQ(getPoolState(pool.m_name).c_str(), "DEGRADED"); + + // replace failed disk by the spare and remove it from mirror + execCmd("zpool", std::string("replace ") + pool.m_name + " " + + vdev2.m_path + " " + spare.m_path); + write_data_and_verify_resp(data_fd, ioseq, 0, 10); + execCmd("zpool", std::string("detach ") + pool.m_name + " " + + vdev2.m_path); + ASSERT_STREQ(getPoolState(pool.m_name).c_str(), "ONLINE"); + + //std::cout << execCmd("zpool", std::string("status ") + pool.m_name); + + graceful_close(data_fd); + graceful_close(control_fd); +}