illumos/illumos-gate@9adfa60d48 https://github.com/illumos/illumos-gate/commit/9adfa60d484ce2435f5af77cc99dcd4e6 92b6660 https://www.illumos.org/issues/6314 Callers of dsl_dataset_name pass a buffer of size ZFS_MAXNAMELEN, but dsl_dataset_name copies the datasets' name PLUS the snapshot name to it, resulting in a max of 2 * ZFS_MAXNAMELEN + '@'. Reviewed by: George Wilson <george.wilson@delphix.com> Reviewed by: Prakash Surya <prakash.surya@delphix.com> Reviewed by: Igor Kozhukhov <ikozhukhov@gmail.com> Approved by: Dan McDonald <danmcd@omniti.com> Author: Matthew Ahrens <mahrens@delphix.com>
771 lines
20 KiB
C++
771 lines
20 KiB
C++
/*-
|
|
* Copyright (c) 2012, 2013, 2014 Spectra Logic Corporation
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions, and the following disclaimer,
|
|
* without modification.
|
|
* 2. Redistributions in binary form must reproduce at minimum a disclaimer
|
|
* substantially similar to the "NO WARRANTY" disclaimer below
|
|
* ("Disclaimer") and any redistribution must be conditioned upon
|
|
* including a substantially similar Disclaimer requirement for further
|
|
* binary redistribution.
|
|
*
|
|
* NO WARRANTY
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGES.
|
|
*
|
|
* Authors: Alan Somers (Spectra Logic Corporation)
|
|
*/
|
|
#include <sys/cdefs.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <syslog.h>
|
|
|
|
#include <libnvpair.h>
|
|
#include <libzfs.h>
|
|
|
|
#include <list>
|
|
#include <map>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <devdctl/guid.h>
|
|
#include <devdctl/event.h>
|
|
#include <devdctl/event_factory.h>
|
|
#include <devdctl/exception.h>
|
|
#include <devdctl/consumer.h>
|
|
|
|
#include <zfsd/callout.h>
|
|
#include <zfsd/vdev_iterator.h>
|
|
#include <zfsd/zfsd_event.h>
|
|
#include <zfsd/case_file.h>
|
|
#include <zfsd/vdev.h>
|
|
#include <zfsd/zfsd.h>
|
|
#include <zfsd/zfsd_exception.h>
|
|
#include <zfsd/zpool_list.h>
|
|
|
|
#include "libmocks.h"
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/*================================== Macros ==================================*/
|
|
#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x))
|
|
|
|
/*============================ Namespace Control =============================*/
|
|
using std::string;
|
|
using std::stringstream;
|
|
|
|
using DevdCtl::Event;
|
|
using DevdCtl::EventFactory;
|
|
using DevdCtl::EventList;
|
|
using DevdCtl::Guid;
|
|
using DevdCtl::NVPairMap;
|
|
|
|
/* redefine zpool_handle here because libzfs_impl.h is not includable */
|
|
struct zpool_handle
|
|
{
|
|
libzfs_handle_t *zpool_hdl;
|
|
zpool_handle_t *zpool_next;
|
|
char zpool_name[ZFS_MAX_DATASET_NAME_LEN];
|
|
int zpool_state;
|
|
size_t zpool_config_size;
|
|
nvlist_t *zpool_config;
|
|
nvlist_t *zpool_old_config;
|
|
nvlist_t *zpool_props;
|
|
diskaddr_t zpool_start_block;
|
|
};
|
|
|
|
class MockZfsEvent : public ZfsEvent
|
|
{
|
|
public:
|
|
MockZfsEvent(Event::Type, NVPairMap&, const string&);
|
|
virtual ~MockZfsEvent() {}
|
|
|
|
static BuildMethod MockZfsEventBuilder;
|
|
|
|
MOCK_CONST_METHOD0(ProcessPoolEvent, void());
|
|
|
|
static EventFactory::Record s_buildRecords[];
|
|
};
|
|
|
|
EventFactory::Record MockZfsEvent::s_buildRecords[] =
|
|
{
|
|
{ Event::NOTIFY, "ZFS", &MockZfsEvent::MockZfsEventBuilder }
|
|
};
|
|
|
|
MockZfsEvent::MockZfsEvent(Event::Type type, NVPairMap& map,
|
|
const string& str)
|
|
: ZfsEvent(type, map, str)
|
|
{
|
|
}
|
|
|
|
Event *
|
|
MockZfsEvent::MockZfsEventBuilder(Event::Type type,
|
|
NVPairMap &nvpairs,
|
|
const string &eventString)
|
|
{
|
|
return (new MockZfsEvent(type, nvpairs, eventString));
|
|
}
|
|
|
|
/*
|
|
* A dummy Vdev class used for testing other classes
|
|
*/
|
|
class MockVdev : public Vdev
|
|
{
|
|
public:
|
|
MockVdev(nvlist_t *vdevConfig);
|
|
virtual ~MockVdev() {}
|
|
|
|
MOCK_CONST_METHOD0(GUID, Guid());
|
|
MOCK_CONST_METHOD0(PoolGUID, Guid());
|
|
MOCK_CONST_METHOD0(State, vdev_state());
|
|
MOCK_CONST_METHOD0(PhysicalPath, string());
|
|
};
|
|
|
|
MockVdev::MockVdev(nvlist_t *vdevConfig)
|
|
: Vdev(vdevConfig)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* A CaseFile class with side effects removed, for testing
|
|
*/
|
|
class TestableCaseFile : public CaseFile
|
|
{
|
|
public:
|
|
static TestableCaseFile &Create(Vdev &vdev);
|
|
TestableCaseFile(Vdev &vdev);
|
|
virtual ~TestableCaseFile() {}
|
|
|
|
MOCK_METHOD0(Close, void());
|
|
MOCK_METHOD1(RegisterCallout, void(const Event &event));
|
|
MOCK_METHOD0(RefreshVdevState, bool());
|
|
MOCK_METHOD1(ReEvaluate, bool(const ZfsEvent &event));
|
|
|
|
bool RealReEvaluate(const ZfsEvent &event)
|
|
{
|
|
return (CaseFile::ReEvaluate(event));
|
|
}
|
|
|
|
/*
|
|
* This splices the event lists, a procedure that would normally be done
|
|
* by OnGracePeriodEnded, but we don't necessarily call that in the
|
|
* unit tests
|
|
*/
|
|
void SpliceEvents();
|
|
|
|
/*
|
|
* Used by some of our expectations. CaseFile does not publicize this
|
|
*/
|
|
static int getActiveCases()
|
|
{
|
|
return (s_activeCases.size());
|
|
}
|
|
};
|
|
|
|
TestableCaseFile::TestableCaseFile(Vdev &vdev)
|
|
: CaseFile(vdev)
|
|
{
|
|
}
|
|
|
|
TestableCaseFile &
|
|
TestableCaseFile::Create(Vdev &vdev)
|
|
{
|
|
TestableCaseFile *newCase;
|
|
newCase = new TestableCaseFile(vdev);
|
|
return (*newCase);
|
|
}
|
|
|
|
void
|
|
TestableCaseFile::SpliceEvents()
|
|
{
|
|
m_events.splice(m_events.begin(), m_tentativeEvents);
|
|
}
|
|
|
|
|
|
/*
|
|
* Test class ZfsdException
|
|
*/
|
|
class ZfsdExceptionTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
virtual void SetUp()
|
|
{
|
|
ASSERT_EQ(0, nvlist_alloc(&poolConfig, NV_UNIQUE_NAME, 0));
|
|
ASSERT_EQ(0, nvlist_add_string(poolConfig,
|
|
ZPOOL_CONFIG_POOL_NAME, "unit_test_pool"));
|
|
ASSERT_EQ(0, nvlist_add_uint64(poolConfig,
|
|
ZPOOL_CONFIG_POOL_GUID, 0x1234));
|
|
|
|
ASSERT_EQ(0, nvlist_alloc(&vdevConfig, NV_UNIQUE_NAME, 0));
|
|
ASSERT_EQ(0, nvlist_add_uint64(vdevConfig,
|
|
ZPOOL_CONFIG_GUID, 0x5678));
|
|
bzero(&poolHandle, sizeof(poolHandle));
|
|
poolHandle.zpool_config = poolConfig;
|
|
}
|
|
|
|
virtual void TearDown()
|
|
{
|
|
nvlist_free(poolConfig);
|
|
nvlist_free(vdevConfig);
|
|
}
|
|
|
|
nvlist_t *poolConfig;
|
|
nvlist_t *vdevConfig;
|
|
zpool_handle_t poolHandle;
|
|
};
|
|
|
|
TEST_F(ZfsdExceptionTest, StringConstructorNull)
|
|
{
|
|
ZfsdException ze("");
|
|
EXPECT_STREQ("", ze.GetString().c_str());
|
|
}
|
|
|
|
TEST_F(ZfsdExceptionTest, StringConstructorFormatted)
|
|
{
|
|
ZfsdException ze(" %d %s", 55, "hello world");
|
|
EXPECT_STREQ(" 55 hello world", ze.GetString().c_str());
|
|
}
|
|
|
|
TEST_F(ZfsdExceptionTest, LogSimple)
|
|
{
|
|
ZfsdException ze("unit test w/o vdev or pool");
|
|
ze.Log();
|
|
EXPECT_EQ(LOG_ERR, syslog_last_priority);
|
|
EXPECT_STREQ("unit test w/o vdev or pool\n", syslog_last_message);
|
|
}
|
|
|
|
TEST_F(ZfsdExceptionTest, Pool)
|
|
{
|
|
const char msg[] = "Exception with pool name";
|
|
char expected[4096];
|
|
sprintf(expected, "Pool unit_test_pool: %s\n", msg);
|
|
ZfsdException ze(poolConfig, msg);
|
|
ze.Log();
|
|
EXPECT_STREQ(expected, syslog_last_message);
|
|
}
|
|
|
|
TEST_F(ZfsdExceptionTest, PoolHandle)
|
|
{
|
|
const char msg[] = "Exception with pool handle";
|
|
char expected[4096];
|
|
sprintf(expected, "Pool unit_test_pool: %s\n", msg);
|
|
ZfsdException ze(&poolHandle, msg);
|
|
ze.Log();
|
|
EXPECT_STREQ(expected, syslog_last_message);
|
|
}
|
|
|
|
/*
|
|
* Test class Vdev
|
|
*/
|
|
class VdevTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
virtual void SetUp()
|
|
{
|
|
ASSERT_EQ(0, nvlist_alloc(&m_poolConfig, NV_UNIQUE_NAME, 0));
|
|
ASSERT_EQ(0, nvlist_add_uint64(m_poolConfig,
|
|
ZPOOL_CONFIG_POOL_GUID,
|
|
0x1234));
|
|
|
|
ASSERT_EQ(0, nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0));
|
|
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_GUID,
|
|
0x5678));
|
|
}
|
|
|
|
virtual void TearDown()
|
|
{
|
|
nvlist_free(m_poolConfig);
|
|
nvlist_free(m_vdevConfig);
|
|
}
|
|
|
|
nvlist_t *m_poolConfig;
|
|
nvlist_t *m_vdevConfig;
|
|
};
|
|
|
|
|
|
TEST_F(VdevTest, StateFromConfig)
|
|
{
|
|
vdev_stat_t vs;
|
|
|
|
vs.vs_state = VDEV_STATE_OFFLINE;
|
|
|
|
ASSERT_EQ(0, nvlist_add_uint64_array(m_vdevConfig,
|
|
ZPOOL_CONFIG_VDEV_STATS,
|
|
(uint64_t*)&vs,
|
|
sizeof(vs) / sizeof(uint64_t)));
|
|
|
|
Vdev vdev(m_poolConfig, m_vdevConfig);
|
|
|
|
EXPECT_EQ(VDEV_STATE_OFFLINE, vdev.State());
|
|
}
|
|
|
|
TEST_F(VdevTest, StateFaulted)
|
|
{
|
|
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_FAULTED, 1));
|
|
|
|
Vdev vdev(m_poolConfig, m_vdevConfig);
|
|
|
|
EXPECT_EQ(VDEV_STATE_FAULTED, vdev.State());
|
|
}
|
|
|
|
/*
|
|
* Test that we can construct a Vdev from the label information that is stored
|
|
* on an available spare drive
|
|
*/
|
|
TEST_F(VdevTest, ConstructAvailSpare)
|
|
{
|
|
nvlist_t *labelConfig;
|
|
|
|
ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0));
|
|
ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID,
|
|
1948339428197961030));
|
|
ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE,
|
|
POOL_STATE_SPARE));
|
|
|
|
EXPECT_NO_THROW(Vdev vdev(labelConfig));
|
|
|
|
nvlist_free(labelConfig);
|
|
}
|
|
|
|
/* Available spares will always show the HEALTHY state */
|
|
TEST_F(VdevTest, AvailSpareState) {
|
|
nvlist_t *labelConfig;
|
|
|
|
ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0));
|
|
ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID,
|
|
1948339428197961030));
|
|
ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE,
|
|
POOL_STATE_SPARE));
|
|
|
|
Vdev vdev(labelConfig);
|
|
EXPECT_EQ(VDEV_STATE_HEALTHY, vdev.State());
|
|
|
|
nvlist_free(labelConfig);
|
|
}
|
|
|
|
/* Test the Vdev::IsSpare method */
|
|
TEST_F(VdevTest, IsSpare) {
|
|
Vdev notSpare(m_poolConfig, m_vdevConfig);
|
|
EXPECT_EQ(false, notSpare.IsSpare());
|
|
|
|
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_IS_SPARE, 1));
|
|
Vdev isSpare(m_poolConfig, m_vdevConfig);
|
|
EXPECT_EQ(true, isSpare.IsSpare());
|
|
}
|
|
|
|
/*
|
|
* Test class ZFSEvent
|
|
*/
|
|
class ZfsEventTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
virtual void SetUp()
|
|
{
|
|
m_eventFactory = new EventFactory();
|
|
m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords,
|
|
NUM_ELEMENTS(MockZfsEvent::s_buildRecords));
|
|
|
|
m_event = NULL;
|
|
}
|
|
|
|
virtual void TearDown()
|
|
{
|
|
delete m_eventFactory;
|
|
delete m_event;
|
|
}
|
|
|
|
EventFactory *m_eventFactory;
|
|
Event *m_event;
|
|
};
|
|
|
|
TEST_F(ZfsEventTest, ProcessPoolEventGetsCalled)
|
|
{
|
|
string evString("!system=ZFS "
|
|
"subsystem=ZFS "
|
|
"type=misc.fs.zfs.vdev_remove "
|
|
"pool_name=foo "
|
|
"pool_guid=9756779504028057996 "
|
|
"vdev_guid=1631193447431603339 "
|
|
"vdev_path=/dev/da1 "
|
|
"timestamp=1348871594");
|
|
m_event = Event::CreateEvent(*m_eventFactory, evString);
|
|
MockZfsEvent *mock_event = static_cast<MockZfsEvent*>(m_event);
|
|
|
|
EXPECT_CALL(*mock_event, ProcessPoolEvent()).Times(1);
|
|
mock_event->Process();
|
|
}
|
|
|
|
/*
|
|
* Test class CaseFile
|
|
*/
|
|
|
|
class CaseFileTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
virtual void SetUp()
|
|
{
|
|
m_eventFactory = new EventFactory();
|
|
m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords,
|
|
NUM_ELEMENTS(MockZfsEvent::s_buildRecords));
|
|
|
|
m_event = NULL;
|
|
|
|
nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0);
|
|
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig,
|
|
ZPOOL_CONFIG_GUID, 0xbeef));
|
|
m_vdev = new MockVdev(m_vdevConfig);
|
|
ON_CALL(*m_vdev, GUID())
|
|
.WillByDefault(::testing::Return(Guid(123)));
|
|
ON_CALL(*m_vdev, PoolGUID())
|
|
.WillByDefault(::testing::Return(Guid(456)));
|
|
ON_CALL(*m_vdev, State())
|
|
.WillByDefault(::testing::Return(VDEV_STATE_HEALTHY));
|
|
m_caseFile = &TestableCaseFile::Create(*m_vdev);
|
|
ON_CALL(*m_caseFile, ReEvaluate(::testing::_))
|
|
.WillByDefault(::testing::Invoke(m_caseFile, &TestableCaseFile::RealReEvaluate));
|
|
return;
|
|
}
|
|
|
|
virtual void TearDown()
|
|
{
|
|
delete m_caseFile;
|
|
nvlist_free(m_vdevConfig);
|
|
delete m_vdev;
|
|
delete m_event;
|
|
delete m_eventFactory;
|
|
}
|
|
|
|
nvlist_t *m_vdevConfig;
|
|
MockVdev *m_vdev;
|
|
TestableCaseFile *m_caseFile;
|
|
Event *m_event;
|
|
EventFactory *m_eventFactory;
|
|
};
|
|
|
|
/*
|
|
* A Vdev with no events should not be degraded or faulted
|
|
*/
|
|
TEST_F(CaseFileTest, HealthyVdev)
|
|
{
|
|
EXPECT_FALSE(m_caseFile->ShouldDegrade());
|
|
EXPECT_FALSE(m_caseFile->ShouldFault());
|
|
}
|
|
|
|
/*
|
|
* A Vdev with only one event should not be degraded or faulted
|
|
* For performance reasons, RefreshVdevState should not be called.
|
|
*/
|
|
TEST_F(CaseFileTest, HealthyishVdev)
|
|
{
|
|
string evString("!system=ZFS "
|
|
"class=ereport.fs.zfs.io "
|
|
"ena=12091638756982918145 "
|
|
"parent_guid=13237004955564865395 "
|
|
"parent_type=raidz "
|
|
"pool=testpool.4415 "
|
|
"pool_context=0 "
|
|
"pool_failmode=wait "
|
|
"pool_guid=456 "
|
|
"subsystem=ZFS "
|
|
"timestamp=1348867914 "
|
|
"type=ereport.fs.zfs.io "
|
|
"vdev_guid=123 "
|
|
"vdev_path=/dev/da400 "
|
|
"vdev_type=disk "
|
|
"zio_blkid=622 "
|
|
"zio_err=1 "
|
|
"zio_level=-2 "
|
|
"zio_object=0 "
|
|
"zio_objset=37 "
|
|
"zio_offset=25598976 "
|
|
"zio_size=1024");
|
|
m_event = Event::CreateEvent(*m_eventFactory, evString);
|
|
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
|
|
|
EXPECT_CALL(*m_caseFile, RefreshVdevState())
|
|
.Times(::testing::Exactly(0));
|
|
EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event));
|
|
EXPECT_FALSE(m_caseFile->ShouldDegrade());
|
|
EXPECT_FALSE(m_caseFile->ShouldFault());
|
|
}
|
|
|
|
/* The case file should be closed when its pool is destroyed */
|
|
TEST_F(CaseFileTest, PoolDestroy)
|
|
{
|
|
string evString("!system=ZFS "
|
|
"pool_name=testpool.4415 "
|
|
"pool_guid=456 "
|
|
"subsystem=ZFS "
|
|
"timestamp=1348867914 "
|
|
"type=misc.fs.zfs.pool_destroy ");
|
|
m_event = Event::CreateEvent(*m_eventFactory, evString);
|
|
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
|
EXPECT_CALL(*m_caseFile, Close());
|
|
EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event));
|
|
}
|
|
|
|
/*
|
|
* A Vdev with a very large number of IO errors should fault
|
|
* For performance reasons, RefreshVdevState should be called at most once
|
|
*/
|
|
TEST_F(CaseFileTest, VeryManyIOErrors)
|
|
{
|
|
EXPECT_CALL(*m_caseFile, RefreshVdevState())
|
|
.Times(::testing::AtMost(1))
|
|
.WillRepeatedly(::testing::Return(true));
|
|
|
|
for(int i=0; i<100; i++) {
|
|
stringstream evStringStream;
|
|
evStringStream <<
|
|
"!system=ZFS "
|
|
"class=ereport.fs.zfs.io "
|
|
"ena=12091638756982918145 "
|
|
"parent_guid=13237004955564865395 "
|
|
"parent_type=raidz "
|
|
"pool=testpool.4415 "
|
|
"pool_context=0 "
|
|
"pool_failmode=wait "
|
|
"pool_guid=456 "
|
|
"subsystem=ZFS "
|
|
"timestamp=";
|
|
evStringStream << i << " ";
|
|
evStringStream <<
|
|
"type=ereport.fs.zfs.io "
|
|
"vdev_guid=123 "
|
|
"vdev_path=/dev/da400 "
|
|
"vdev_type=disk "
|
|
"zio_blkid=622 "
|
|
"zio_err=1 "
|
|
"zio_level=-2 "
|
|
"zio_object=0 "
|
|
"zio_objset=37 "
|
|
"zio_offset=25598976 "
|
|
"zio_size=1024";
|
|
Event *event(Event::CreateEvent(*m_eventFactory,
|
|
evStringStream.str()));
|
|
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(event);
|
|
EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event));
|
|
delete event;
|
|
}
|
|
|
|
m_caseFile->SpliceEvents();
|
|
EXPECT_FALSE(m_caseFile->ShouldDegrade());
|
|
EXPECT_TRUE(m_caseFile->ShouldFault());
|
|
}
|
|
|
|
/*
|
|
* A Vdev with a very large number of checksum errors should degrade
|
|
* For performance reasons, RefreshVdevState should be called at most once
|
|
*/
|
|
TEST_F(CaseFileTest, VeryManyChecksumErrors)
|
|
{
|
|
EXPECT_CALL(*m_caseFile, RefreshVdevState())
|
|
.Times(::testing::AtMost(1))
|
|
.WillRepeatedly(::testing::Return(true));
|
|
|
|
for(int i=0; i<100; i++) {
|
|
stringstream evStringStream;
|
|
evStringStream <<
|
|
"!system=ZFS "
|
|
"bad_cleared_bits=03000000000000803f50b00000000000 "
|
|
"bad_range_clears=0000000e "
|
|
"bad_range_sets=00000000 "
|
|
"bad_ranges=0000000000000010 "
|
|
"bad_ranges_min_gap=8 "
|
|
"bad_set_bits=00000000000000000000000000000000 "
|
|
"class=ereport.fs.zfs.checksum "
|
|
"ena=12272856582652437505 "
|
|
"parent_guid=5838204195352909894 "
|
|
"parent_type=raidz pool=testpool.7640 "
|
|
"pool_context=0 "
|
|
"pool_failmode=wait "
|
|
"pool_guid=456 "
|
|
"subsystem=ZFS timestamp=";
|
|
evStringStream << i << " ";
|
|
evStringStream <<
|
|
"type=ereport.fs.zfs.checksum "
|
|
"vdev_guid=123 "
|
|
"vdev_path=/mnt/tmp/file1.7702 "
|
|
"vdev_type=file "
|
|
"zio_blkid=0 "
|
|
"zio_err=0 "
|
|
"zio_level=0 "
|
|
"zio_object=3 "
|
|
"zio_objset=0 "
|
|
"zio_offset=16896 "
|
|
"zio_size=512";
|
|
Event *event(Event::CreateEvent(*m_eventFactory,
|
|
evStringStream.str()));
|
|
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(event);
|
|
EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event));
|
|
delete event;
|
|
}
|
|
|
|
m_caseFile->SpliceEvents();
|
|
EXPECT_TRUE(m_caseFile->ShouldDegrade());
|
|
EXPECT_FALSE(m_caseFile->ShouldFault());
|
|
}
|
|
|
|
/*
|
|
* Test CaseFile::ReEvaluateByGuid
|
|
*/
|
|
class ReEvaluateByGuidTest : public ::testing::Test
|
|
{
|
|
protected:
|
|
virtual void SetUp()
|
|
{
|
|
m_eventFactory = new EventFactory();
|
|
m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords,
|
|
NUM_ELEMENTS(MockZfsEvent::s_buildRecords));
|
|
m_event = Event::CreateEvent(*m_eventFactory, s_evString);
|
|
nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0);
|
|
ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig,
|
|
ZPOOL_CONFIG_GUID, 0xbeef));
|
|
m_vdev456 = new ::testing::NiceMock<MockVdev>(m_vdevConfig);
|
|
m_vdev789 = new ::testing::NiceMock<MockVdev>(m_vdevConfig);
|
|
ON_CALL(*m_vdev456, GUID())
|
|
.WillByDefault(::testing::Return(Guid(123)));
|
|
ON_CALL(*m_vdev456, PoolGUID())
|
|
.WillByDefault(::testing::Return(Guid(456)));
|
|
ON_CALL(*m_vdev456, State())
|
|
.WillByDefault(::testing::Return(VDEV_STATE_HEALTHY));
|
|
ON_CALL(*m_vdev789, GUID())
|
|
.WillByDefault(::testing::Return(Guid(123)));
|
|
ON_CALL(*m_vdev789, PoolGUID())
|
|
.WillByDefault(::testing::Return(Guid(789)));
|
|
ON_CALL(*m_vdev789, State())
|
|
.WillByDefault(::testing::Return(VDEV_STATE_HEALTHY));
|
|
m_caseFile456 = NULL;
|
|
m_caseFile789 = NULL;
|
|
return;
|
|
}
|
|
|
|
virtual void TearDown()
|
|
{
|
|
delete m_caseFile456;
|
|
delete m_caseFile789;
|
|
nvlist_free(m_vdevConfig);
|
|
delete m_vdev456;
|
|
delete m_vdev789;
|
|
delete m_event;
|
|
delete m_eventFactory;
|
|
}
|
|
|
|
static string s_evString;
|
|
nvlist_t *m_vdevConfig;
|
|
::testing::NiceMock<MockVdev> *m_vdev456;
|
|
::testing::NiceMock<MockVdev> *m_vdev789;
|
|
TestableCaseFile *m_caseFile456;
|
|
TestableCaseFile *m_caseFile789;
|
|
Event *m_event;
|
|
EventFactory *m_eventFactory;
|
|
};
|
|
|
|
string ReEvaluateByGuidTest::s_evString(
|
|
"!system=ZFS "
|
|
"pool_guid=16271873792808333580 "
|
|
"pool_name=foo "
|
|
"subsystem=ZFS "
|
|
"timestamp=1360620391 "
|
|
"type=misc.fs.zfs.config_sync");
|
|
|
|
|
|
/*
|
|
* Test the ReEvaluateByGuid method on an empty list of casefiles.
|
|
* We must create one event, even though it never gets used, because it will
|
|
* be passed by reference to ReEvaluateByGuid
|
|
*/
|
|
TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_empty)
|
|
{
|
|
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
|
|
|
EXPECT_EQ(0, TestableCaseFile::getActiveCases());
|
|
CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event);
|
|
EXPECT_EQ(0, TestableCaseFile::getActiveCases());
|
|
}
|
|
|
|
/*
|
|
* Test the ReEvaluateByGuid method on a list of CaseFiles that contains only
|
|
* one CaseFile, which doesn't match the criteria
|
|
*/
|
|
TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneFalse)
|
|
{
|
|
m_caseFile456 = &TestableCaseFile::Create(*m_vdev456);
|
|
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
|
|
|
EXPECT_EQ(1, TestableCaseFile::getActiveCases());
|
|
EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_))
|
|
.Times(::testing::Exactly(0));
|
|
CaseFile::ReEvaluateByGuid(Guid(789), *zfs_event);
|
|
EXPECT_EQ(1, TestableCaseFile::getActiveCases());
|
|
}
|
|
|
|
/*
|
|
* Test the ReEvaluateByGuid method on a list of CaseFiles that contains only
|
|
* one CaseFile, which does match the criteria
|
|
*/
|
|
TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneTrue)
|
|
{
|
|
m_caseFile456 = &TestableCaseFile::Create(*m_vdev456);
|
|
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
|
|
|
EXPECT_EQ(1, TestableCaseFile::getActiveCases());
|
|
EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_))
|
|
.Times(::testing::Exactly(1))
|
|
.WillRepeatedly(::testing::Return(false));
|
|
CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event);
|
|
EXPECT_EQ(1, TestableCaseFile::getActiveCases());
|
|
}
|
|
|
|
/*
|
|
* Test the ReEvaluateByGuid method on a long list of CaseFiles that contains a
|
|
* few cases which meet the criteria
|
|
*/
|
|
TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_five)
|
|
{
|
|
TestableCaseFile *CaseFile1 = &TestableCaseFile::Create(*m_vdev456);
|
|
TestableCaseFile *CaseFile2 = &TestableCaseFile::Create(*m_vdev789);
|
|
TestableCaseFile *CaseFile3 = &TestableCaseFile::Create(*m_vdev456);
|
|
TestableCaseFile *CaseFile4 = &TestableCaseFile::Create(*m_vdev789);
|
|
TestableCaseFile *CaseFile5 = &TestableCaseFile::Create(*m_vdev789);
|
|
ZfsEvent *zfs_event = static_cast<ZfsEvent*>(m_event);
|
|
|
|
EXPECT_EQ(5, TestableCaseFile::getActiveCases());
|
|
EXPECT_CALL(*CaseFile1, ReEvaluate(::testing::_))
|
|
.Times(::testing::Exactly(1))
|
|
.WillRepeatedly(::testing::Return(false));
|
|
EXPECT_CALL(*CaseFile3, ReEvaluate(::testing::_))
|
|
.Times(::testing::Exactly(1))
|
|
.WillRepeatedly(::testing::Return(false));
|
|
EXPECT_CALL(*CaseFile2, ReEvaluate(::testing::_))
|
|
.Times(::testing::Exactly(0));
|
|
EXPECT_CALL(*CaseFile4, ReEvaluate(::testing::_))
|
|
.Times(::testing::Exactly(0));
|
|
EXPECT_CALL(*CaseFile5, ReEvaluate(::testing::_))
|
|
.Times(::testing::Exactly(0));
|
|
CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event);
|
|
EXPECT_EQ(5, TestableCaseFile::getActiveCases());
|
|
delete CaseFile1;
|
|
delete CaseFile2;
|
|
delete CaseFile3;
|
|
delete CaseFile4;
|
|
delete CaseFile5;
|
|
}
|