1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "test/librbd/test_mock_fixture.h"
5 #include "test/librbd/test_support.h"
6 #include "test/librbd/mock/MockImageCtx.h"
7 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
8 #include "test/librados_test_stub/MockTestMemRadosClient.h"
9 #include "common/Cond.h"
10 #include "common/Mutex.h"
11 #include "librados/AioCompletionImpl.h"
12 #include "librbd/Watcher.h"
13 #include "librbd/watcher/RewatchRequest.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
22 struct MockWatcher
: public Watcher
{
25 MockWatcher(librados::IoCtx
& ioctx
, ContextWQ
*work_queue
,
26 const std::string
& oid
)
27 : Watcher(ioctx
, work_queue
, oid
) {
30 virtual void handle_notify(uint64_t notify_id
, uint64_t handle
,
31 uint64_t notifier_id
, bufferlist
&bl
) {
35 } // anonymous namespace
41 using ::testing::DoDefault
;
42 using ::testing::Invoke
;
43 using ::testing::InSequence
;
44 using ::testing::Return
;
45 using ::testing::SaveArg
;
46 using ::testing::WithArg
;
47 using ::testing::WithArgs
;
49 class TestMockWatcher
: public TestMockFixture
{
51 TestMockWatcher() : m_lock("TestMockWatcher::m_lock") {
54 virtual void SetUp() {
55 TestMockFixture::SetUp();
57 m_oid
= get_temp_image_name();
60 ASSERT_EQ(0, m_ioctx
.write_full(m_oid
, bl
));
63 void expect_aio_watch(MockImageCtx
&mock_image_ctx
, int r
,
64 const std::function
<void()> &action
= std::function
<void()>()) {
65 librados::MockTestMemIoCtxImpl
&mock_io_ctx(get_mock_io_ctx(m_ioctx
));
66 librados::MockTestMemRadosClient
*mock_rados_client(
67 mock_io_ctx
.get_mock_rados_client());
69 EXPECT_CALL(mock_io_ctx
, aio_watch(m_oid
, _
, _
, _
))
70 .WillOnce(DoAll(WithArgs
<1, 2, 3>(Invoke([this, &mock_image_ctx
, mock_rados_client
, r
, action
](
71 librados::AioCompletionImpl
*c
, uint64_t *cookie
,
72 librados::WatchCtx2
*watch_ctx
) {
75 m_watch_ctx
= watch_ctx
;
79 mock_image_ctx
.image_ctx
->op_work_queue
->queue(new FunctionContext([mock_rados_client
, action
, c
](int r
) {
84 mock_rados_client
->finish_aio_completion(c
, r
);
90 void expect_aio_unwatch(MockImageCtx
&mock_image_ctx
, int r
,
91 const std::function
<void()> &action
= std::function
<void()>()) {
92 librados::MockTestMemIoCtxImpl
&mock_io_ctx(get_mock_io_ctx(m_ioctx
));
93 librados::MockTestMemRadosClient
*mock_rados_client(
94 mock_io_ctx
.get_mock_rados_client());
96 EXPECT_CALL(mock_io_ctx
, aio_unwatch(_
, _
))
97 .WillOnce(DoAll(Invoke([this, &mock_image_ctx
, mock_rados_client
, r
, action
](
98 uint64_t handle
, librados::AioCompletionImpl
*c
) {
100 mock_image_ctx
.image_ctx
->op_work_queue
->queue(new FunctionContext([mock_rados_client
, action
, c
](int r
) {
105 mock_rados_client
->finish_aio_completion(c
, r
);
112 librados::WatchCtx2
*m_watch_ctx
= nullptr;
114 void notify_watch() {
115 Mutex::Locker
locker(m_lock
);
120 bool wait_for_watch(MockImageCtx
&mock_image_ctx
, size_t count
) {
121 Mutex::Locker
locker(m_lock
);
122 while (m_watch_count
< count
) {
123 if (m_cond
.WaitInterval(m_lock
, utime_t(10, 0)) != 0) {
127 m_watch_count
-= count
;
133 size_t m_watch_count
= 0;
136 TEST_F(TestMockWatcher
, Success
) {
137 librbd::ImageCtx
*ictx
;
138 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
140 MockImageCtx
mock_image_ctx(*ictx
);
141 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
144 expect_aio_watch(mock_image_ctx
, 0);
145 expect_aio_unwatch(mock_image_ctx
, 0);
147 C_SaferCond register_ctx
;
148 mock_image_watcher
.register_watch(®ister_ctx
);
149 ASSERT_EQ(0, register_ctx
.wait());
151 C_SaferCond unregister_ctx
;
152 mock_image_watcher
.unregister_watch(&unregister_ctx
);
153 ASSERT_EQ(0, unregister_ctx
.wait());
156 TEST_F(TestMockWatcher
, RegisterError
) {
157 librbd::ImageCtx
*ictx
;
158 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
160 MockImageCtx
mock_image_ctx(*ictx
);
161 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
164 expect_aio_watch(mock_image_ctx
, -EINVAL
);
166 C_SaferCond register_ctx
;
167 mock_image_watcher
.register_watch(®ister_ctx
);
168 ASSERT_EQ(-EINVAL
, register_ctx
.wait());
171 TEST_F(TestMockWatcher
, UnregisterError
) {
172 librbd::ImageCtx
*ictx
;
173 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
175 MockImageCtx
mock_image_ctx(*ictx
);
176 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
179 expect_aio_watch(mock_image_ctx
, 0);
180 expect_aio_unwatch(mock_image_ctx
, -EINVAL
);
182 C_SaferCond register_ctx
;
183 mock_image_watcher
.register_watch(®ister_ctx
);
184 ASSERT_EQ(0, register_ctx
.wait());
186 C_SaferCond unregister_ctx
;
187 mock_image_watcher
.unregister_watch(&unregister_ctx
);
188 ASSERT_EQ(-EINVAL
, unregister_ctx
.wait());
191 TEST_F(TestMockWatcher
, Reregister
) {
192 librbd::ImageCtx
*ictx
;
193 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
195 MockImageCtx
mock_image_ctx(*ictx
);
196 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
198 expect_op_work_queue(mock_image_ctx
);
201 expect_aio_watch(mock_image_ctx
, 0);
202 expect_aio_unwatch(mock_image_ctx
, 0);
203 expect_aio_watch(mock_image_ctx
, 0);
204 expect_aio_unwatch(mock_image_ctx
, 0);
206 C_SaferCond register_ctx
;
207 mock_image_watcher
.register_watch(®ister_ctx
);
208 ASSERT_EQ(0, register_ctx
.wait());
210 ceph_assert(m_watch_ctx
!= nullptr);
211 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
213 // wait for recovery unwatch/watch
214 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 3));
216 C_SaferCond unregister_ctx
;
217 mock_image_watcher
.unregister_watch(&unregister_ctx
);
218 ASSERT_EQ(0, unregister_ctx
.wait());
221 TEST_F(TestMockWatcher
, ReregisterUnwatchError
) {
222 librbd::ImageCtx
*ictx
;
223 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
225 MockImageCtx
mock_image_ctx(*ictx
);
226 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
228 expect_op_work_queue(mock_image_ctx
);
231 expect_aio_watch(mock_image_ctx
, 0);
232 expect_aio_unwatch(mock_image_ctx
, -EINVAL
);
233 expect_aio_watch(mock_image_ctx
, 0);
234 expect_aio_unwatch(mock_image_ctx
, 0);
236 C_SaferCond register_ctx
;
237 mock_image_watcher
.register_watch(®ister_ctx
);
238 ASSERT_EQ(0, register_ctx
.wait());
240 ceph_assert(m_watch_ctx
!= nullptr);
241 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
243 // wait for recovery unwatch/watch
244 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 3));
246 C_SaferCond unregister_ctx
;
247 mock_image_watcher
.unregister_watch(&unregister_ctx
);
248 ASSERT_EQ(0, unregister_ctx
.wait());
251 TEST_F(TestMockWatcher
, ReregisterWatchError
) {
252 librbd::ImageCtx
*ictx
;
253 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
255 MockImageCtx
mock_image_ctx(*ictx
);
256 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
258 expect_op_work_queue(mock_image_ctx
);
261 expect_aio_watch(mock_image_ctx
, 0);
262 expect_aio_unwatch(mock_image_ctx
, 0);
263 expect_aio_watch(mock_image_ctx
, -EPERM
);
264 expect_aio_watch(mock_image_ctx
, 0);
265 expect_aio_unwatch(mock_image_ctx
, 0);
267 C_SaferCond register_ctx
;
268 mock_image_watcher
.register_watch(®ister_ctx
);
269 ASSERT_EQ(0, register_ctx
.wait());
271 ceph_assert(m_watch_ctx
!= nullptr);
272 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
274 // wait for recovery unwatch/watch
275 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 4));
277 C_SaferCond unregister_ctx
;
278 mock_image_watcher
.unregister_watch(&unregister_ctx
);
279 ASSERT_EQ(0, unregister_ctx
.wait());
282 TEST_F(TestMockWatcher
, ReregisterWatchBlacklist
) {
283 librbd::ImageCtx
*ictx
;
284 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
286 MockImageCtx
mock_image_ctx(*ictx
);
287 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
289 expect_op_work_queue(mock_image_ctx
);
292 expect_aio_watch(mock_image_ctx
, 0);
293 expect_aio_unwatch(mock_image_ctx
, 0);
294 expect_aio_watch(mock_image_ctx
, -EBLACKLISTED
);
296 C_SaferCond blacklist_ctx
;
297 expect_aio_watch(mock_image_ctx
, 0, [&blacklist_ctx
]() {
298 blacklist_ctx
.wait();
300 expect_aio_unwatch(mock_image_ctx
, 0);
302 C_SaferCond register_ctx
;
303 mock_image_watcher
.register_watch(®ister_ctx
);
304 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 1));
305 ASSERT_EQ(0, register_ctx
.wait());
307 ceph_assert(m_watch_ctx
!= nullptr);
308 m_watch_ctx
->handle_error(0, -EBLACKLISTED
);
310 // wait for recovery unwatch/watch
311 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 2));
313 ASSERT_TRUE(mock_image_watcher
.is_blacklisted());
314 blacklist_ctx
.complete(0);
316 // wait for post-blacklist recovery watch
317 ASSERT_TRUE(wait_for_watch(mock_image_ctx
, 1));
319 C_SaferCond unregister_ctx
;
320 mock_image_watcher
.unregister_watch(&unregister_ctx
);
321 ASSERT_EQ(0, unregister_ctx
.wait());
322 ASSERT_FALSE(mock_image_watcher
.is_blacklisted());
325 TEST_F(TestMockWatcher
, ReregisterUnwatchPendingUnregister
) {
326 librbd::ImageCtx
*ictx
;
327 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
329 MockImageCtx
mock_image_ctx(*ictx
);
330 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
332 expect_op_work_queue(mock_image_ctx
);
335 expect_aio_watch(mock_image_ctx
, 0);
337 // inject an unregister
338 C_SaferCond unregister_ctx
;
339 expect_aio_unwatch(mock_image_ctx
, -EBLACKLISTED
,
340 [&mock_image_watcher
, &unregister_ctx
]() {
341 mock_image_watcher
.unregister_watch(&unregister_ctx
);
344 C_SaferCond register_ctx
;
345 mock_image_watcher
.register_watch(®ister_ctx
);
346 ASSERT_EQ(0, register_ctx
.wait());
348 ceph_assert(m_watch_ctx
!= nullptr);
349 m_watch_ctx
->handle_error(0, -EBLACKLISTED
);
351 ASSERT_EQ(0, unregister_ctx
.wait());
354 TEST_F(TestMockWatcher
, ReregisterWatchPendingUnregister
) {
355 librbd::ImageCtx
*ictx
;
356 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
358 MockImageCtx
mock_image_ctx(*ictx
);
359 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
361 expect_op_work_queue(mock_image_ctx
);
364 expect_aio_watch(mock_image_ctx
, 0);
365 expect_aio_unwatch(mock_image_ctx
, 0);
367 // inject an unregister
368 C_SaferCond unregister_ctx
;
369 expect_aio_watch(mock_image_ctx
, -ESHUTDOWN
,
370 [&mock_image_watcher
, &unregister_ctx
]() {
371 mock_image_watcher
.unregister_watch(&unregister_ctx
);
374 C_SaferCond register_ctx
;
375 mock_image_watcher
.register_watch(®ister_ctx
);
376 ASSERT_EQ(0, register_ctx
.wait());
378 ceph_assert(m_watch_ctx
!= nullptr);
379 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
381 ASSERT_EQ(0, unregister_ctx
.wait());
384 TEST_F(TestMockWatcher
, ReregisterPendingUnregister
) {
385 librbd::ImageCtx
*ictx
;
386 ASSERT_EQ(0, open_image(m_image_name
, &ictx
));
388 MockImageCtx
mock_image_ctx(*ictx
);
389 MockWatcher
mock_image_watcher(m_ioctx
, ictx
->op_work_queue
, m_oid
);
391 expect_op_work_queue(mock_image_ctx
);
394 expect_aio_watch(mock_image_ctx
, 0);
395 expect_aio_unwatch(mock_image_ctx
, 0);
397 // inject an unregister
398 C_SaferCond unregister_ctx
;
399 expect_aio_watch(mock_image_ctx
, 0,
400 [&mock_image_watcher
, &unregister_ctx
]() {
401 mock_image_watcher
.unregister_watch(&unregister_ctx
);
404 expect_aio_unwatch(mock_image_ctx
, 0);
406 C_SaferCond register_ctx
;
407 mock_image_watcher
.register_watch(®ister_ctx
);
408 ASSERT_EQ(0, register_ctx
.wait());
410 ceph_assert(m_watch_ctx
!= nullptr);
411 m_watch_ctx
->handle_error(0, -ESHUTDOWN
);
413 ASSERT_EQ(0, unregister_ctx
.wait());
416 } // namespace librbd