]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | #include "test/librbd/test_fixture.h" | |
4 | #include "test/librbd/test_support.h" | |
5 | #include "include/int_types.h" | |
6 | #include "include/stringify.h" | |
7 | #include "include/rados/librados.h" | |
8 | #include "include/rbd/librbd.hpp" | |
9 | #include "common/Cond.h" | |
9f95a23c | 10 | #include "common/ceph_mutex.h" |
7c673cae | 11 | #include "common/errno.h" |
7c673cae FG |
12 | #include "cls/lock/cls_lock_client.h" |
13 | #include "cls/lock/cls_lock_types.h" | |
14 | #include "librbd/internal.h" | |
15 | #include "librbd/ImageCtx.h" | |
16 | #include "librbd/ImageWatcher.h" | |
17 | #include "librbd/WatchNotifyTypes.h" | |
18 | #include "librbd/io/AioCompletion.h" | |
19 | #include "librbd/io/ImageRequestWQ.h" | |
20 | #include "test/librados/test.h" | |
21 | #include "gtest/gtest.h" | |
22 | #include <boost/assign/std/set.hpp> | |
23 | #include <boost/assign/std/map.hpp> | |
24 | #include <boost/bind.hpp> | |
25 | #include <boost/scope_exit.hpp> | |
26 | #include <boost/thread/thread.hpp> | |
27 | #include <iostream> | |
28 | #include <map> | |
29 | #include <set> | |
30 | #include <sstream> | |
31 | #include <vector> | |
32 | ||
33 | using namespace ceph; | |
34 | using namespace boost::assign; | |
35 | using namespace librbd::watch_notify; | |
36 | ||
37 | void register_test_image_watcher() { | |
38 | } | |
39 | ||
40 | class TestImageWatcher : public TestFixture { | |
41 | public: | |
42 | ||
9f95a23c | 43 | TestImageWatcher() : m_watch_ctx(NULL) |
7c673cae FG |
44 | { |
45 | } | |
46 | ||
47 | class WatchCtx : public librados::WatchCtx2 { | |
48 | public: | |
49 | explicit WatchCtx(TestImageWatcher &parent) : m_parent(parent), m_handle(0) {} | |
50 | ||
51 | int watch(const librbd::ImageCtx &ictx) { | |
52 | m_header_oid = ictx.header_oid; | |
53 | return m_parent.m_ioctx.watch2(m_header_oid, &m_handle, this); | |
54 | } | |
55 | ||
56 | int unwatch() { | |
57 | return m_parent.m_ioctx.unwatch2(m_handle); | |
58 | } | |
59 | ||
60 | void handle_notify(uint64_t notify_id, | |
61 | uint64_t cookie, | |
62 | uint64_t notifier_id, | |
63 | bufferlist& bl) override { | |
64 | try { | |
65 | int op; | |
66 | bufferlist payload; | |
11fdf7f2 | 67 | auto iter = bl.cbegin(); |
7c673cae | 68 | DECODE_START(1, iter); |
11fdf7f2 | 69 | decode(op, iter); |
7c673cae FG |
70 | iter.copy_all(payload); |
71 | DECODE_FINISH(iter); | |
72 | ||
73 | NotifyOp notify_op = static_cast<NotifyOp>(op); | |
74 | /* | |
75 | std::cout << "NOTIFY: " << notify_op << ", " << notify_id | |
76 | << ", " << cookie << ", " << notifier_id << std::endl; | |
77 | */ | |
78 | ||
9f95a23c | 79 | std::lock_guard l{m_parent.m_callback_lock}; |
7c673cae FG |
80 | m_parent.m_notify_payloads[notify_op] = payload; |
81 | ||
82 | bufferlist reply; | |
83 | if (m_parent.m_notify_acks.count(notify_op) > 0) { | |
84 | reply = m_parent.m_notify_acks[notify_op]; | |
85 | m_parent.m_notifies += notify_op; | |
9f95a23c | 86 | m_parent.m_callback_cond.notify_all(); |
7c673cae FG |
87 | } |
88 | ||
89 | m_parent.m_ioctx.notify_ack(m_header_oid, notify_id, cookie, reply); | |
90 | } catch (...) { | |
91 | FAIL(); | |
92 | } | |
93 | } | |
94 | ||
95 | void handle_error(uint64_t cookie, int err) override { | |
96 | std::cerr << "ERROR: " << cookie << ", " << cpp_strerror(err) | |
97 | << std::endl; | |
98 | } | |
99 | ||
100 | uint64_t get_handle() const { | |
101 | return m_handle; | |
102 | } | |
103 | ||
104 | private: | |
105 | TestImageWatcher &m_parent; | |
106 | std::string m_header_oid; | |
107 | uint64_t m_handle; | |
108 | }; | |
109 | ||
110 | void TearDown() override { | |
111 | deregister_image_watch(); | |
112 | TestFixture::TearDown(); | |
113 | } | |
114 | ||
115 | int deregister_image_watch() { | |
116 | if (m_watch_ctx != NULL) { | |
117 | int r = m_watch_ctx->unwatch(); | |
118 | ||
119 | librados::Rados rados(m_ioctx); | |
120 | rados.watch_flush(); | |
121 | ||
122 | delete m_watch_ctx; | |
123 | m_watch_ctx = NULL; | |
124 | return r; | |
125 | } | |
126 | return 0; | |
127 | } | |
128 | ||
129 | int register_image_watch(librbd::ImageCtx &ictx) { | |
130 | m_watch_ctx = new WatchCtx(*this); | |
131 | return m_watch_ctx->watch(ictx); | |
132 | } | |
133 | ||
134 | bool wait_for_notifies(librbd::ImageCtx &ictx) { | |
9f95a23c | 135 | std::unique_lock l{m_callback_lock}; |
7c673cae | 136 | while (m_notifies.size() < m_notify_acks.size()) { |
9f95a23c | 137 | if (m_callback_cond.wait_for(l, 10s) == std::cv_status::timeout) { |
7c673cae FG |
138 | break; |
139 | } | |
140 | } | |
141 | return (m_notifies.size() == m_notify_acks.size()); | |
142 | } | |
143 | ||
144 | bufferlist create_response_message(int r) { | |
145 | bufferlist bl; | |
11fdf7f2 | 146 | encode(ResponseMessage(r), bl); |
7c673cae FG |
147 | return bl; |
148 | } | |
149 | ||
150 | bool extract_async_request_id(NotifyOp op, AsyncRequestId *id) { | |
151 | if (m_notify_payloads.count(op) == 0) { | |
152 | return false; | |
153 | } | |
154 | ||
155 | bufferlist payload = m_notify_payloads[op]; | |
11fdf7f2 TL |
156 | auto iter = payload.cbegin(); |
157 | ||
7c673cae FG |
158 | switch (op) { |
159 | case NOTIFY_OP_FLATTEN: | |
160 | { | |
161 | FlattenPayload payload; | |
162 | payload.decode(2, iter); | |
163 | *id = payload.async_request_id; | |
164 | } | |
165 | return true; | |
166 | case NOTIFY_OP_RESIZE: | |
167 | { | |
168 | ResizePayload payload; | |
169 | payload.decode(2, iter); | |
170 | *id = payload.async_request_id; | |
171 | } | |
172 | return true; | |
173 | case NOTIFY_OP_REBUILD_OBJECT_MAP: | |
174 | { | |
175 | RebuildObjectMapPayload payload; | |
176 | payload.decode(2, iter); | |
177 | *id = payload.async_request_id; | |
178 | } | |
179 | return true; | |
180 | default: | |
181 | break; | |
182 | } | |
183 | return false; | |
184 | } | |
185 | ||
186 | int notify_async_progress(librbd::ImageCtx *ictx, const AsyncRequestId &id, | |
187 | uint64_t offset, uint64_t total) { | |
188 | bufferlist bl; | |
11fdf7f2 | 189 | encode(NotifyMessage(AsyncProgressPayload(id, offset, total)), bl); |
7c673cae FG |
190 | return m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL); |
191 | } | |
192 | ||
193 | int notify_async_complete(librbd::ImageCtx *ictx, const AsyncRequestId &id, | |
194 | int r) { | |
195 | bufferlist bl; | |
11fdf7f2 | 196 | encode(NotifyMessage(AsyncCompletePayload(id, r)), bl); |
7c673cae FG |
197 | return m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL); |
198 | } | |
199 | ||
200 | typedef std::map<NotifyOp, bufferlist> NotifyOpPayloads; | |
201 | typedef std::set<NotifyOp> NotifyOps; | |
202 | ||
203 | WatchCtx *m_watch_ctx; | |
204 | ||
205 | NotifyOps m_notifies; | |
206 | NotifyOpPayloads m_notify_payloads; | |
207 | NotifyOpPayloads m_notify_acks; | |
208 | ||
209 | AsyncRequestId m_async_request_id; | |
210 | ||
9f95a23c TL |
211 | ceph::mutex m_callback_lock = ceph::make_mutex("m_callback_lock"); |
212 | ceph::condition_variable m_callback_cond; | |
7c673cae FG |
213 | |
214 | }; | |
215 | ||
216 | struct ProgressContext : public librbd::ProgressContext { | |
9f95a23c TL |
217 | ceph::mutex mutex = ceph::make_mutex("ProgressContext::mutex"); |
218 | ceph::condition_variable cond; | |
7c673cae FG |
219 | bool received; |
220 | uint64_t offset; | |
221 | uint64_t total; | |
222 | ||
9f95a23c | 223 | ProgressContext() : received(false), |
7c673cae FG |
224 | offset(0), total(0) {} |
225 | ||
226 | int update_progress(uint64_t offset_, uint64_t total_) override { | |
9f95a23c | 227 | std::lock_guard l{mutex}; |
7c673cae FG |
228 | offset = offset_; |
229 | total = total_; | |
230 | received = true; | |
9f95a23c | 231 | cond.notify_all(); |
7c673cae FG |
232 | return 0; |
233 | } | |
234 | ||
235 | bool wait(librbd::ImageCtx *ictx, uint64_t offset_, uint64_t total_) { | |
9f95a23c | 236 | std::unique_lock l{mutex}; |
7c673cae | 237 | while (!received) { |
9f95a23c | 238 | if (cond.wait_for(l, 10s) == std::cv_status::timeout) { |
7c673cae FG |
239 | break; |
240 | } | |
241 | } | |
242 | return (received && offset == offset_ && total == total_); | |
243 | } | |
244 | }; | |
245 | ||
246 | struct FlattenTask { | |
247 | librbd::ImageCtx *ictx; | |
248 | ProgressContext *progress_context; | |
249 | int result; | |
250 | ||
251 | FlattenTask(librbd::ImageCtx *ictx_, ProgressContext *ctx) | |
252 | : ictx(ictx_), progress_context(ctx), result(0) {} | |
253 | ||
254 | void operator()() { | |
9f95a23c | 255 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
256 | C_SaferCond ctx; |
257 | ictx->image_watcher->notify_flatten(0, *progress_context, &ctx); | |
258 | result = ctx.wait(); | |
259 | } | |
260 | }; | |
261 | ||
262 | struct ResizeTask { | |
263 | librbd::ImageCtx *ictx; | |
264 | ProgressContext *progress_context; | |
265 | int result; | |
266 | ||
267 | ResizeTask(librbd::ImageCtx *ictx_, ProgressContext *ctx) | |
268 | : ictx(ictx_), progress_context(ctx), result(0) {} | |
269 | ||
270 | void operator()() { | |
9f95a23c | 271 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
272 | C_SaferCond ctx; |
273 | ictx->image_watcher->notify_resize(0, 0, true, *progress_context, &ctx); | |
274 | result = ctx.wait(); | |
275 | } | |
276 | }; | |
277 | ||
278 | struct RebuildObjectMapTask { | |
279 | librbd::ImageCtx *ictx; | |
280 | ProgressContext *progress_context; | |
281 | int result; | |
282 | ||
283 | RebuildObjectMapTask(librbd::ImageCtx *ictx_, ProgressContext *ctx) | |
284 | : ictx(ictx_), progress_context(ctx), result(0) {} | |
285 | ||
286 | void operator()() { | |
9f95a23c | 287 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
288 | C_SaferCond ctx; |
289 | ictx->image_watcher->notify_rebuild_object_map(0, *progress_context, &ctx); | |
290 | result = ctx.wait(); | |
291 | } | |
292 | }; | |
293 | ||
294 | TEST_F(TestImageWatcher, NotifyHeaderUpdate) { | |
295 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
296 | ||
297 | librbd::ImageCtx *ictx; | |
298 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
299 | ||
300 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
301 | ||
302 | m_notify_acks = {{NOTIFY_OP_HEADER_UPDATE, {}}}; | |
303 | ictx->notify_update(); | |
304 | ||
305 | ASSERT_TRUE(wait_for_notifies(*ictx)); | |
306 | ||
307 | NotifyOps expected_notify_ops; | |
308 | expected_notify_ops += NOTIFY_OP_HEADER_UPDATE; | |
309 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
310 | } | |
311 | ||
312 | TEST_F(TestImageWatcher, NotifyFlatten) { | |
313 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
314 | ||
315 | librbd::ImageCtx *ictx; | |
316 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
317 | ||
318 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
319 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
320 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
321 | ||
322 | m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}}; | |
323 | ||
324 | ProgressContext progress_context; | |
325 | FlattenTask flatten_task(ictx, &progress_context); | |
326 | boost::thread thread(boost::ref(flatten_task)); | |
327 | ||
328 | ASSERT_TRUE(wait_for_notifies(*ictx)); | |
329 | ||
330 | NotifyOps expected_notify_ops; | |
331 | expected_notify_ops += NOTIFY_OP_FLATTEN; | |
332 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
333 | ||
334 | AsyncRequestId async_request_id; | |
335 | ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_FLATTEN, &async_request_id)); | |
336 | ||
337 | ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20)); | |
338 | ASSERT_TRUE(progress_context.wait(ictx, 10, 20)); | |
339 | ||
340 | ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0)); | |
341 | ||
342 | ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10))); | |
343 | ASSERT_EQ(0, flatten_task.result); | |
344 | } | |
345 | ||
346 | TEST_F(TestImageWatcher, NotifyResize) { | |
347 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
348 | ||
349 | librbd::ImageCtx *ictx; | |
350 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
351 | ||
352 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
353 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
354 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
355 | ||
356 | m_notify_acks = {{NOTIFY_OP_RESIZE, create_response_message(0)}}; | |
357 | ||
358 | ProgressContext progress_context; | |
359 | ResizeTask resize_task(ictx, &progress_context); | |
360 | boost::thread thread(boost::ref(resize_task)); | |
361 | ||
362 | ASSERT_TRUE(wait_for_notifies(*ictx)); | |
363 | ||
364 | NotifyOps expected_notify_ops; | |
365 | expected_notify_ops += NOTIFY_OP_RESIZE; | |
366 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
367 | ||
368 | AsyncRequestId async_request_id; | |
369 | ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_RESIZE, &async_request_id)); | |
370 | ||
371 | ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20)); | |
372 | ASSERT_TRUE(progress_context.wait(ictx, 10, 20)); | |
373 | ||
374 | ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0)); | |
375 | ||
376 | ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10))); | |
377 | ASSERT_EQ(0, resize_task.result); | |
378 | } | |
379 | ||
380 | TEST_F(TestImageWatcher, NotifyRebuildObjectMap) { | |
381 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
382 | ||
383 | librbd::ImageCtx *ictx; | |
384 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
385 | ||
386 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
387 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
388 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
389 | ||
390 | m_notify_acks = {{NOTIFY_OP_REBUILD_OBJECT_MAP, create_response_message(0)}}; | |
391 | ||
392 | ProgressContext progress_context; | |
393 | RebuildObjectMapTask rebuild_task(ictx, &progress_context); | |
394 | boost::thread thread(boost::ref(rebuild_task)); | |
395 | ||
396 | ASSERT_TRUE(wait_for_notifies(*ictx)); | |
397 | ||
398 | NotifyOps expected_notify_ops; | |
399 | expected_notify_ops += NOTIFY_OP_REBUILD_OBJECT_MAP; | |
400 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
401 | ||
402 | AsyncRequestId async_request_id; | |
403 | ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_REBUILD_OBJECT_MAP, | |
404 | &async_request_id)); | |
405 | ||
406 | ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20)); | |
407 | ASSERT_TRUE(progress_context.wait(ictx, 10, 20)); | |
408 | ||
409 | ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0)); | |
410 | ||
411 | ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10))); | |
412 | ASSERT_EQ(0, rebuild_task.result); | |
413 | } | |
414 | ||
415 | TEST_F(TestImageWatcher, NotifySnapCreate) { | |
416 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
417 | ||
418 | librbd::ImageCtx *ictx; | |
419 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
420 | ||
421 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
422 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
423 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
424 | ||
425 | m_notify_acks = {{NOTIFY_OP_SNAP_CREATE, create_response_message(0)}}; | |
426 | ||
9f95a23c | 427 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
428 | C_SaferCond notify_ctx; |
429 | ictx->image_watcher->notify_snap_create(cls::rbd::UserSnapshotNamespace(), | |
430 | "snap", ¬ify_ctx); | |
431 | ASSERT_EQ(0, notify_ctx.wait()); | |
432 | ||
433 | NotifyOps expected_notify_ops; | |
434 | expected_notify_ops += NOTIFY_OP_SNAP_CREATE; | |
435 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
436 | } | |
437 | ||
438 | TEST_F(TestImageWatcher, NotifySnapCreateError) { | |
439 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
440 | ||
441 | librbd::ImageCtx *ictx; | |
442 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
443 | ||
444 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
445 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
446 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
447 | ||
448 | m_notify_acks = {{NOTIFY_OP_SNAP_CREATE, create_response_message(-EEXIST)}}; | |
449 | ||
9f95a23c | 450 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
451 | C_SaferCond notify_ctx; |
452 | ictx->image_watcher->notify_snap_create(cls::rbd::UserSnapshotNamespace(), | |
453 | "snap", ¬ify_ctx); | |
454 | ASSERT_EQ(-EEXIST, notify_ctx.wait()); | |
455 | ||
456 | NotifyOps expected_notify_ops; | |
457 | expected_notify_ops += NOTIFY_OP_SNAP_CREATE; | |
458 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
459 | } | |
460 | ||
461 | TEST_F(TestImageWatcher, NotifySnapRename) { | |
462 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
463 | ||
464 | librbd::ImageCtx *ictx; | |
465 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
466 | ||
467 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
468 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
469 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
470 | ||
471 | m_notify_acks = {{NOTIFY_OP_SNAP_RENAME, create_response_message(0)}}; | |
472 | ||
9f95a23c | 473 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
474 | C_SaferCond notify_ctx; |
475 | ictx->image_watcher->notify_snap_rename(1, "snap-rename", ¬ify_ctx); | |
476 | ASSERT_EQ(0, notify_ctx.wait()); | |
477 | ||
478 | NotifyOps expected_notify_ops; | |
479 | expected_notify_ops += NOTIFY_OP_SNAP_RENAME; | |
480 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
481 | } | |
482 | ||
483 | TEST_F(TestImageWatcher, NotifySnapRenameError) { | |
484 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
485 | ||
486 | librbd::ImageCtx *ictx; | |
487 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
488 | ||
489 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
490 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
491 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
492 | ||
493 | m_notify_acks = {{NOTIFY_OP_SNAP_RENAME, create_response_message(-EEXIST)}}; | |
494 | ||
9f95a23c | 495 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
496 | C_SaferCond notify_ctx; |
497 | ictx->image_watcher->notify_snap_rename(1, "snap-rename", ¬ify_ctx); | |
498 | ASSERT_EQ(-EEXIST, notify_ctx.wait()); | |
499 | ||
500 | NotifyOps expected_notify_ops; | |
501 | expected_notify_ops += NOTIFY_OP_SNAP_RENAME; | |
502 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
503 | } | |
504 | ||
505 | TEST_F(TestImageWatcher, NotifySnapRemove) { | |
506 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
507 | ||
508 | librbd::ImageCtx *ictx; | |
509 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
510 | ||
511 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
512 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
513 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
514 | ||
515 | m_notify_acks = {{NOTIFY_OP_SNAP_REMOVE, create_response_message(0)}}; | |
516 | ||
9f95a23c | 517 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
518 | C_SaferCond notify_ctx; |
519 | ictx->image_watcher->notify_snap_remove(cls::rbd::UserSnapshotNamespace(), | |
520 | "snap", | |
521 | ¬ify_ctx); | |
522 | ASSERT_EQ(0, notify_ctx.wait()); | |
523 | ||
524 | NotifyOps expected_notify_ops; | |
525 | expected_notify_ops += NOTIFY_OP_SNAP_REMOVE; | |
526 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
527 | } | |
528 | ||
529 | TEST_F(TestImageWatcher, NotifySnapProtect) { | |
530 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
531 | ||
532 | librbd::ImageCtx *ictx; | |
533 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
534 | ||
535 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
536 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
537 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
538 | ||
539 | m_notify_acks = {{NOTIFY_OP_SNAP_PROTECT, create_response_message(0)}}; | |
540 | ||
9f95a23c | 541 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
542 | C_SaferCond notify_ctx; |
543 | ictx->image_watcher->notify_snap_protect(cls::rbd::UserSnapshotNamespace(), | |
544 | "snap", | |
545 | ¬ify_ctx); | |
546 | ASSERT_EQ(0, notify_ctx.wait()); | |
547 | ||
548 | NotifyOps expected_notify_ops; | |
549 | expected_notify_ops += NOTIFY_OP_SNAP_PROTECT; | |
550 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
551 | } | |
552 | ||
553 | TEST_F(TestImageWatcher, NotifySnapUnprotect) { | |
554 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
555 | ||
556 | librbd::ImageCtx *ictx; | |
557 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
558 | ||
559 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
560 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
561 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
562 | ||
563 | m_notify_acks = {{NOTIFY_OP_SNAP_UNPROTECT, create_response_message(0)}}; | |
564 | ||
9f95a23c | 565 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
566 | C_SaferCond notify_ctx; |
567 | ictx->image_watcher->notify_snap_unprotect(cls::rbd::UserSnapshotNamespace(), | |
568 | "snap", | |
569 | ¬ify_ctx); | |
570 | ASSERT_EQ(0, notify_ctx.wait()); | |
571 | ||
572 | NotifyOps expected_notify_ops; | |
573 | expected_notify_ops += NOTIFY_OP_SNAP_UNPROTECT; | |
574 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
575 | } | |
576 | ||
577 | TEST_F(TestImageWatcher, NotifyRename) { | |
578 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
579 | ||
580 | librbd::ImageCtx *ictx; | |
581 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
582 | ||
583 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
584 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
585 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
586 | ||
587 | m_notify_acks = {{NOTIFY_OP_RENAME, create_response_message(0)}}; | |
588 | ||
9f95a23c | 589 | std::shared_lock l{ictx->owner_lock}; |
7c673cae FG |
590 | C_SaferCond notify_ctx; |
591 | ictx->image_watcher->notify_rename("new_name", ¬ify_ctx); | |
592 | ASSERT_EQ(0, notify_ctx.wait()); | |
593 | ||
594 | NotifyOps expected_notify_ops; | |
595 | expected_notify_ops += NOTIFY_OP_RENAME; | |
596 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
597 | } | |
598 | ||
599 | TEST_F(TestImageWatcher, NotifyAsyncTimedOut) { | |
600 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
601 | ||
602 | librbd::ImageCtx *ictx; | |
603 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
604 | ||
605 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
606 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
607 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
608 | ||
609 | m_notify_acks = {{NOTIFY_OP_FLATTEN, {}}}; | |
610 | ||
611 | ProgressContext progress_context; | |
612 | FlattenTask flatten_task(ictx, &progress_context); | |
613 | boost::thread thread(boost::ref(flatten_task)); | |
614 | ||
615 | ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10))); | |
616 | ASSERT_EQ(-ETIMEDOUT, flatten_task.result); | |
617 | } | |
618 | ||
619 | TEST_F(TestImageWatcher, NotifyAsyncError) { | |
620 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
621 | ||
622 | librbd::ImageCtx *ictx; | |
623 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
624 | ||
625 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
626 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
627 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
628 | ||
629 | m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(-EIO)}}; | |
630 | ||
631 | ProgressContext progress_context; | |
632 | FlattenTask flatten_task(ictx, &progress_context); | |
633 | boost::thread thread(boost::ref(flatten_task)); | |
634 | ||
635 | ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10))); | |
636 | ASSERT_EQ(-EIO, flatten_task.result); | |
637 | } | |
638 | ||
639 | TEST_F(TestImageWatcher, NotifyAsyncCompleteError) { | |
640 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
641 | ||
642 | librbd::ImageCtx *ictx; | |
643 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
644 | ||
645 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
646 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
647 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
648 | ||
649 | m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}}; | |
650 | ||
651 | ProgressContext progress_context; | |
652 | FlattenTask flatten_task(ictx, &progress_context); | |
653 | boost::thread thread(boost::ref(flatten_task)); | |
654 | ||
655 | ASSERT_TRUE(wait_for_notifies(*ictx)); | |
656 | ||
657 | NotifyOps expected_notify_ops; | |
658 | expected_notify_ops += NOTIFY_OP_FLATTEN; | |
659 | ASSERT_EQ(expected_notify_ops, m_notifies); | |
660 | ||
661 | AsyncRequestId async_request_id; | |
662 | ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_FLATTEN, &async_request_id)); | |
663 | ||
664 | ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, -ESHUTDOWN)); | |
665 | ||
666 | ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10))); | |
667 | ASSERT_EQ(-ESHUTDOWN, flatten_task.result); | |
668 | } | |
669 | ||
670 | TEST_F(TestImageWatcher, NotifyAsyncRequestTimedOut) { | |
671 | REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK); | |
672 | ||
673 | librbd::ImageCtx *ictx; | |
674 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
675 | ||
11fdf7f2 | 676 | ictx->config.set_val("rbd_request_timed_out_seconds", "0"); |
7c673cae FG |
677 | |
678 | ASSERT_EQ(0, register_image_watch(*ictx)); | |
679 | ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE, | |
680 | "auto " + stringify(m_watch_ctx->get_handle()))); | |
681 | ||
682 | m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}}; | |
683 | ||
684 | ProgressContext progress_context; | |
685 | FlattenTask flatten_task(ictx, &progress_context); | |
686 | boost::thread thread(boost::ref(flatten_task)); | |
687 | ||
688 | ASSERT_TRUE(wait_for_notifies(*ictx)); | |
689 | ||
690 | ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10))); | |
691 | ASSERT_EQ(-ETIMEDOUT, flatten_task.result); | |
692 | } | |
693 |