]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/test_mock_Watcher.cc
import 15.2.4
[ceph.git] / ceph / src / test / librbd / test_mock_Watcher.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
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/ceph_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"
16 #include <list>
17
18 namespace librbd {
19
20 namespace {
21
22 struct MockWatcher : public Watcher {
23 std::string oid;
24
25 MockWatcher(librados::IoCtx& ioctx, ContextWQ *work_queue,
26 const std::string& oid)
27 : Watcher(ioctx, work_queue, oid) {
28 }
29
30 virtual void handle_notify(uint64_t notify_id, uint64_t handle,
31 uint64_t notifier_id, bufferlist &bl) {
32 }
33 };
34
35 } // anonymous namespace
36 } // namespace librbd
37
38 namespace librbd {
39
40 using ::testing::_;
41 using ::testing::DoAll;
42 using ::testing::DoDefault;
43 using ::testing::Invoke;
44 using ::testing::InSequence;
45 using ::testing::Return;
46 using ::testing::SaveArg;
47 using ::testing::WithArg;
48 using ::testing::WithArgs;
49
50 class TestMockWatcher : public TestMockFixture {
51 public:
52 TestMockWatcher() = default;
53
54 virtual void SetUp() {
55 TestMockFixture::SetUp();
56
57 m_oid = get_temp_image_name();
58
59 bufferlist bl;
60 ASSERT_EQ(0, m_ioctx.write_full(m_oid, bl));
61 }
62
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());
68
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) {
73 if (r == 0) {
74 *cookie = 234U;
75 m_watch_ctx = watch_ctx;
76 }
77
78 c->get();
79 mock_image_ctx.image_ctx->op_work_queue->queue(new LambdaContext([mock_rados_client, action, c](int r) {
80 if (action) {
81 action();
82 }
83
84 mock_rados_client->finish_aio_completion(c, r);
85 }), r);
86 notify_watch();
87 })), Return(0)));
88 }
89
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());
95
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) {
99 c->get();
100 mock_image_ctx.image_ctx->op_work_queue->queue(new LambdaContext([mock_rados_client, action, c](int r) {
101 if (action) {
102 action();
103 }
104
105 mock_rados_client->finish_aio_completion(c, r);
106 }), r);
107 notify_watch();
108 }), Return(0)));
109 }
110
111 std::string m_oid;
112 librados::WatchCtx2 *m_watch_ctx = nullptr;
113
114 void notify_watch() {
115 std::lock_guard locker{m_lock};
116 ++m_watch_count;
117 m_cond.notify_all();
118 }
119
120 bool wait_for_watch(MockImageCtx &mock_image_ctx, size_t count) {
121 std::unique_lock locker{m_lock};
122 while (m_watch_count < count) {
123 if (m_cond.wait_for(locker, 10s) == std::cv_status::timeout) {
124 return false;
125 }
126 }
127 m_watch_count -= count;
128 return true;
129 }
130
131 ceph::mutex m_lock = ceph::make_mutex("TestMockWatcher::m_lock");
132 ceph::condition_variable m_cond;
133 size_t m_watch_count = 0;
134 };
135
136 TEST_F(TestMockWatcher, Success) {
137 librbd::ImageCtx *ictx;
138 ASSERT_EQ(0, open_image(m_image_name, &ictx));
139
140 MockImageCtx mock_image_ctx(*ictx);
141 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
142
143 InSequence seq;
144 expect_aio_watch(mock_image_ctx, 0);
145 expect_aio_unwatch(mock_image_ctx, 0);
146
147 C_SaferCond register_ctx;
148 mock_image_watcher.register_watch(&register_ctx);
149 ASSERT_EQ(0, register_ctx.wait());
150
151 C_SaferCond unregister_ctx;
152 mock_image_watcher.unregister_watch(&unregister_ctx);
153 ASSERT_EQ(0, unregister_ctx.wait());
154 }
155
156 TEST_F(TestMockWatcher, RegisterError) {
157 librbd::ImageCtx *ictx;
158 ASSERT_EQ(0, open_image(m_image_name, &ictx));
159
160 MockImageCtx mock_image_ctx(*ictx);
161 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
162
163 InSequence seq;
164 expect_aio_watch(mock_image_ctx, -EINVAL);
165
166 C_SaferCond register_ctx;
167 mock_image_watcher.register_watch(&register_ctx);
168 ASSERT_EQ(-EINVAL, register_ctx.wait());
169 }
170
171 TEST_F(TestMockWatcher, UnregisterError) {
172 librbd::ImageCtx *ictx;
173 ASSERT_EQ(0, open_image(m_image_name, &ictx));
174
175 MockImageCtx mock_image_ctx(*ictx);
176 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
177
178 InSequence seq;
179 expect_aio_watch(mock_image_ctx, 0);
180 expect_aio_unwatch(mock_image_ctx, -EINVAL);
181
182 C_SaferCond register_ctx;
183 mock_image_watcher.register_watch(&register_ctx);
184 ASSERT_EQ(0, register_ctx.wait());
185
186 C_SaferCond unregister_ctx;
187 mock_image_watcher.unregister_watch(&unregister_ctx);
188 ASSERT_EQ(-EINVAL, unregister_ctx.wait());
189 }
190
191 TEST_F(TestMockWatcher, Reregister) {
192 librbd::ImageCtx *ictx;
193 ASSERT_EQ(0, open_image(m_image_name, &ictx));
194
195 MockImageCtx mock_image_ctx(*ictx);
196 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
197
198 expect_op_work_queue(mock_image_ctx);
199
200 InSequence seq;
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);
205
206 C_SaferCond register_ctx;
207 mock_image_watcher.register_watch(&register_ctx);
208 ASSERT_EQ(0, register_ctx.wait());
209
210 ceph_assert(m_watch_ctx != nullptr);
211 m_watch_ctx->handle_error(0, -ESHUTDOWN);
212
213 // wait for recovery unwatch/watch
214 ASSERT_TRUE(wait_for_watch(mock_image_ctx, 3));
215
216 C_SaferCond unregister_ctx;
217 mock_image_watcher.unregister_watch(&unregister_ctx);
218 ASSERT_EQ(0, unregister_ctx.wait());
219 }
220
221 TEST_F(TestMockWatcher, ReregisterUnwatchError) {
222 librbd::ImageCtx *ictx;
223 ASSERT_EQ(0, open_image(m_image_name, &ictx));
224
225 MockImageCtx mock_image_ctx(*ictx);
226 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
227
228 expect_op_work_queue(mock_image_ctx);
229
230 InSequence seq;
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);
235
236 C_SaferCond register_ctx;
237 mock_image_watcher.register_watch(&register_ctx);
238 ASSERT_EQ(0, register_ctx.wait());
239
240 ceph_assert(m_watch_ctx != nullptr);
241 m_watch_ctx->handle_error(0, -ESHUTDOWN);
242
243 // wait for recovery unwatch/watch
244 ASSERT_TRUE(wait_for_watch(mock_image_ctx, 3));
245
246 C_SaferCond unregister_ctx;
247 mock_image_watcher.unregister_watch(&unregister_ctx);
248 ASSERT_EQ(0, unregister_ctx.wait());
249 }
250
251 TEST_F(TestMockWatcher, ReregisterWatchError) {
252 librbd::ImageCtx *ictx;
253 ASSERT_EQ(0, open_image(m_image_name, &ictx));
254
255 MockImageCtx mock_image_ctx(*ictx);
256 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
257
258 expect_op_work_queue(mock_image_ctx);
259
260 InSequence seq;
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);
266
267 C_SaferCond register_ctx;
268 mock_image_watcher.register_watch(&register_ctx);
269 ASSERT_EQ(0, register_ctx.wait());
270
271 ceph_assert(m_watch_ctx != nullptr);
272 m_watch_ctx->handle_error(0, -ESHUTDOWN);
273
274 // wait for recovery unwatch/watch
275 ASSERT_TRUE(wait_for_watch(mock_image_ctx, 4));
276
277 C_SaferCond unregister_ctx;
278 mock_image_watcher.unregister_watch(&unregister_ctx);
279 ASSERT_EQ(0, unregister_ctx.wait());
280 }
281
282 TEST_F(TestMockWatcher, ReregisterWatchBlacklist) {
283 librbd::ImageCtx *ictx;
284 ASSERT_EQ(0, open_image(m_image_name, &ictx));
285
286 MockImageCtx mock_image_ctx(*ictx);
287 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
288
289 expect_op_work_queue(mock_image_ctx);
290
291 InSequence seq;
292 expect_aio_watch(mock_image_ctx, 0);
293 expect_aio_unwatch(mock_image_ctx, 0);
294 expect_aio_watch(mock_image_ctx, -EBLACKLISTED);
295
296 C_SaferCond register_ctx;
297 mock_image_watcher.register_watch(&register_ctx);
298 ASSERT_TRUE(wait_for_watch(mock_image_ctx, 1));
299 ASSERT_EQ(0, register_ctx.wait());
300
301 ceph_assert(m_watch_ctx != nullptr);
302 m_watch_ctx->handle_error(0, -EBLACKLISTED);
303
304 // wait for recovery unwatch/watch
305 ASSERT_TRUE(wait_for_watch(mock_image_ctx, 2));
306 ASSERT_TRUE(mock_image_watcher.is_blacklisted());
307
308 C_SaferCond unregister_ctx;
309 mock_image_watcher.unregister_watch(&unregister_ctx);
310 ASSERT_EQ(0, unregister_ctx.wait());
311 }
312
313 TEST_F(TestMockWatcher, ReregisterUnwatchPendingUnregister) {
314 librbd::ImageCtx *ictx;
315 ASSERT_EQ(0, open_image(m_image_name, &ictx));
316
317 MockImageCtx mock_image_ctx(*ictx);
318 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
319
320 expect_op_work_queue(mock_image_ctx);
321
322 InSequence seq;
323 expect_aio_watch(mock_image_ctx, 0);
324
325 // inject an unregister
326 C_SaferCond unregister_ctx;
327 expect_aio_unwatch(mock_image_ctx, -EBLACKLISTED,
328 [&mock_image_watcher, &unregister_ctx]() {
329 mock_image_watcher.unregister_watch(&unregister_ctx);
330 });
331
332 C_SaferCond register_ctx;
333 mock_image_watcher.register_watch(&register_ctx);
334 ASSERT_EQ(0, register_ctx.wait());
335
336 ceph_assert(m_watch_ctx != nullptr);
337 m_watch_ctx->handle_error(0, -EBLACKLISTED);
338
339 ASSERT_EQ(0, unregister_ctx.wait());
340 }
341
342 TEST_F(TestMockWatcher, ReregisterWatchPendingUnregister) {
343 librbd::ImageCtx *ictx;
344 ASSERT_EQ(0, open_image(m_image_name, &ictx));
345
346 MockImageCtx mock_image_ctx(*ictx);
347 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
348
349 expect_op_work_queue(mock_image_ctx);
350
351 InSequence seq;
352 expect_aio_watch(mock_image_ctx, 0);
353 expect_aio_unwatch(mock_image_ctx, 0);
354
355 // inject an unregister
356 C_SaferCond unregister_ctx;
357 expect_aio_watch(mock_image_ctx, -ESHUTDOWN,
358 [&mock_image_watcher, &unregister_ctx]() {
359 mock_image_watcher.unregister_watch(&unregister_ctx);
360 });
361
362 C_SaferCond register_ctx;
363 mock_image_watcher.register_watch(&register_ctx);
364 ASSERT_EQ(0, register_ctx.wait());
365
366 ceph_assert(m_watch_ctx != nullptr);
367 m_watch_ctx->handle_error(0, -ESHUTDOWN);
368
369 ASSERT_EQ(0, unregister_ctx.wait());
370 }
371
372 TEST_F(TestMockWatcher, ReregisterPendingUnregister) {
373 librbd::ImageCtx *ictx;
374 ASSERT_EQ(0, open_image(m_image_name, &ictx));
375
376 MockImageCtx mock_image_ctx(*ictx);
377 MockWatcher mock_image_watcher(m_ioctx, ictx->op_work_queue, m_oid);
378
379 expect_op_work_queue(mock_image_ctx);
380
381 InSequence seq;
382 expect_aio_watch(mock_image_ctx, 0);
383 expect_aio_unwatch(mock_image_ctx, 0);
384
385 // inject an unregister
386 C_SaferCond unregister_ctx;
387 expect_aio_watch(mock_image_ctx, 0,
388 [&mock_image_watcher, &unregister_ctx]() {
389 mock_image_watcher.unregister_watch(&unregister_ctx);
390 });
391
392 expect_aio_unwatch(mock_image_ctx, 0);
393
394 C_SaferCond register_ctx;
395 mock_image_watcher.register_watch(&register_ctx);
396 ASSERT_EQ(0, register_ctx.wait());
397
398 ceph_assert(m_watch_ctx != nullptr);
399 m_watch_ctx->handle_error(0, -ESHUTDOWN);
400
401 ASSERT_EQ(0, unregister_ctx.wait());
402 }
403
404 } // namespace librbd