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"
10 #include "common/errno.h"
11 #include "common/Mutex.h"
12 #include "common/RWLock.h"
13 #include "cls/lock/cls_lock_client.h"
14 #include "cls/lock/cls_lock_types.h"
15 #include "librbd/internal.h"
16 #include "librbd/ImageCtx.h"
17 #include "librbd/ImageWatcher.h"
18 #include "librbd/WatchNotifyTypes.h"
19 #include "librbd/io/AioCompletion.h"
20 #include "librbd/io/ImageRequestWQ.h"
21 #include "test/librados/test.h"
22 #include "gtest/gtest.h"
23 #include <boost/assign/std/set.hpp>
24 #include <boost/assign/std/map.hpp>
25 #include <boost/bind.hpp>
26 #include <boost/scope_exit.hpp>
27 #include <boost/thread/thread.hpp>
35 using namespace boost::assign;
36 using namespace librbd::watch_notify;
38 void register_test_image_watcher() {
41 class TestImageWatcher : public TestFixture {
44 TestImageWatcher() : m_watch_ctx(NULL), m_callback_lock("m_callback_lock")
48 class WatchCtx : public librados::WatchCtx2 {
50 explicit WatchCtx(TestImageWatcher &parent) : m_parent(parent), m_handle(0) {}
52 int watch(const librbd::ImageCtx &ictx) {
53 m_header_oid = ictx.header_oid;
54 return m_parent.m_ioctx.watch2(m_header_oid, &m_handle, this);
58 return m_parent.m_ioctx.unwatch2(m_handle);
61 void handle_notify(uint64_t notify_id,
64 bufferlist& bl) override {
68 bufferlist::iterator iter = bl.begin();
69 DECODE_START(1, iter);
71 iter.copy_all(payload);
74 NotifyOp notify_op = static_cast<NotifyOp>(op);
76 std::cout << "NOTIFY: " << notify_op << ", " << notify_id
77 << ", " << cookie << ", " << notifier_id << std::endl;
80 Mutex::Locker l(m_parent.m_callback_lock);
81 m_parent.m_notify_payloads[notify_op] = payload;
84 if (m_parent.m_notify_acks.count(notify_op) > 0) {
85 reply = m_parent.m_notify_acks[notify_op];
86 m_parent.m_notifies += notify_op;
87 m_parent.m_callback_cond.Signal();
90 m_parent.m_ioctx.notify_ack(m_header_oid, notify_id, cookie, reply);
96 void handle_error(uint64_t cookie, int err) override {
97 std::cerr << "ERROR: " << cookie << ", " << cpp_strerror(err)
101 uint64_t get_handle() const {
106 TestImageWatcher &m_parent;
107 std::string m_header_oid;
111 void TearDown() override {
112 deregister_image_watch();
113 TestFixture::TearDown();
116 int deregister_image_watch() {
117 if (m_watch_ctx != NULL) {
118 int r = m_watch_ctx->unwatch();
120 librados::Rados rados(m_ioctx);
130 int register_image_watch(librbd::ImageCtx &ictx) {
131 m_watch_ctx = new WatchCtx(*this);
132 return m_watch_ctx->watch(ictx);
135 bool wait_for_notifies(librbd::ImageCtx &ictx) {
136 Mutex::Locker l(m_callback_lock);
137 while (m_notifies.size() < m_notify_acks.size()) {
138 int r = m_callback_cond.WaitInterval(m_callback_lock,
144 return (m_notifies.size() == m_notify_acks.size());
147 bufferlist create_response_message(int r) {
149 ::encode(ResponseMessage(r), bl);
153 bool extract_async_request_id(NotifyOp op, AsyncRequestId *id) {
154 if (m_notify_payloads.count(op) == 0) {
158 bufferlist payload = m_notify_payloads[op];
159 bufferlist::iterator iter = payload.begin();
162 case NOTIFY_OP_FLATTEN:
164 FlattenPayload payload;
165 payload.decode(2, iter);
166 *id = payload.async_request_id;
169 case NOTIFY_OP_RESIZE:
171 ResizePayload payload;
172 payload.decode(2, iter);
173 *id = payload.async_request_id;
176 case NOTIFY_OP_REBUILD_OBJECT_MAP:
178 RebuildObjectMapPayload payload;
179 payload.decode(2, iter);
180 *id = payload.async_request_id;
189 int notify_async_progress(librbd::ImageCtx *ictx, const AsyncRequestId &id,
190 uint64_t offset, uint64_t total) {
192 ::encode(NotifyMessage(AsyncProgressPayload(id, offset, total)), bl);
193 return m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL);
196 int notify_async_complete(librbd::ImageCtx *ictx, const AsyncRequestId &id,
199 ::encode(NotifyMessage(AsyncCompletePayload(id, r)), bl);
200 return m_ioctx.notify2(ictx->header_oid, bl, 5000, NULL);
203 typedef std::map<NotifyOp, bufferlist> NotifyOpPayloads;
204 typedef std::set<NotifyOp> NotifyOps;
206 WatchCtx *m_watch_ctx;
208 NotifyOps m_notifies;
209 NotifyOpPayloads m_notify_payloads;
210 NotifyOpPayloads m_notify_acks;
212 AsyncRequestId m_async_request_id;
214 Mutex m_callback_lock;
215 Cond m_callback_cond;
219 struct ProgressContext : public librbd::ProgressContext {
226 ProgressContext() : mutex("ProgressContext::mutex"), received(false),
227 offset(0), total(0) {}
229 int update_progress(uint64_t offset_, uint64_t total_) override {
230 Mutex::Locker l(mutex);
238 bool wait(librbd::ImageCtx *ictx, uint64_t offset_, uint64_t total_) {
239 Mutex::Locker l(mutex);
241 int r = cond.WaitInterval(mutex, utime_t(10, 0));
246 return (received && offset == offset_ && total == total_);
251 librbd::ImageCtx *ictx;
252 ProgressContext *progress_context;
255 FlattenTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
256 : ictx(ictx_), progress_context(ctx), result(0) {}
259 RWLock::RLocker l(ictx->owner_lock);
261 ictx->image_watcher->notify_flatten(0, *progress_context, &ctx);
267 librbd::ImageCtx *ictx;
268 ProgressContext *progress_context;
271 ResizeTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
272 : ictx(ictx_), progress_context(ctx), result(0) {}
275 RWLock::RLocker l(ictx->owner_lock);
277 ictx->image_watcher->notify_resize(0, 0, true, *progress_context, &ctx);
282 struct RebuildObjectMapTask {
283 librbd::ImageCtx *ictx;
284 ProgressContext *progress_context;
287 RebuildObjectMapTask(librbd::ImageCtx *ictx_, ProgressContext *ctx)
288 : ictx(ictx_), progress_context(ctx), result(0) {}
291 RWLock::RLocker l(ictx->owner_lock);
293 ictx->image_watcher->notify_rebuild_object_map(0, *progress_context, &ctx);
298 TEST_F(TestImageWatcher, NotifyHeaderUpdate) {
299 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
301 librbd::ImageCtx *ictx;
302 ASSERT_EQ(0, open_image(m_image_name, &ictx));
304 ASSERT_EQ(0, register_image_watch(*ictx));
306 m_notify_acks = {{NOTIFY_OP_HEADER_UPDATE, {}}};
307 ictx->notify_update();
309 ASSERT_TRUE(wait_for_notifies(*ictx));
311 NotifyOps expected_notify_ops;
312 expected_notify_ops += NOTIFY_OP_HEADER_UPDATE;
313 ASSERT_EQ(expected_notify_ops, m_notifies);
316 TEST_F(TestImageWatcher, NotifyFlatten) {
317 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
319 librbd::ImageCtx *ictx;
320 ASSERT_EQ(0, open_image(m_image_name, &ictx));
322 ASSERT_EQ(0, register_image_watch(*ictx));
323 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
324 "auto " + stringify(m_watch_ctx->get_handle())));
326 m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}};
328 ProgressContext progress_context;
329 FlattenTask flatten_task(ictx, &progress_context);
330 boost::thread thread(boost::ref(flatten_task));
332 ASSERT_TRUE(wait_for_notifies(*ictx));
334 NotifyOps expected_notify_ops;
335 expected_notify_ops += NOTIFY_OP_FLATTEN;
336 ASSERT_EQ(expected_notify_ops, m_notifies);
338 AsyncRequestId async_request_id;
339 ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_FLATTEN, &async_request_id));
341 ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20));
342 ASSERT_TRUE(progress_context.wait(ictx, 10, 20));
344 ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
346 ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
347 ASSERT_EQ(0, flatten_task.result);
350 TEST_F(TestImageWatcher, NotifyResize) {
351 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
353 librbd::ImageCtx *ictx;
354 ASSERT_EQ(0, open_image(m_image_name, &ictx));
356 ASSERT_EQ(0, register_image_watch(*ictx));
357 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
358 "auto " + stringify(m_watch_ctx->get_handle())));
360 m_notify_acks = {{NOTIFY_OP_RESIZE, create_response_message(0)}};
362 ProgressContext progress_context;
363 ResizeTask resize_task(ictx, &progress_context);
364 boost::thread thread(boost::ref(resize_task));
366 ASSERT_TRUE(wait_for_notifies(*ictx));
368 NotifyOps expected_notify_ops;
369 expected_notify_ops += NOTIFY_OP_RESIZE;
370 ASSERT_EQ(expected_notify_ops, m_notifies);
372 AsyncRequestId async_request_id;
373 ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_RESIZE, &async_request_id));
375 ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20));
376 ASSERT_TRUE(progress_context.wait(ictx, 10, 20));
378 ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
380 ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
381 ASSERT_EQ(0, resize_task.result);
384 TEST_F(TestImageWatcher, NotifyRebuildObjectMap) {
385 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
387 librbd::ImageCtx *ictx;
388 ASSERT_EQ(0, open_image(m_image_name, &ictx));
390 ASSERT_EQ(0, register_image_watch(*ictx));
391 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
392 "auto " + stringify(m_watch_ctx->get_handle())));
394 m_notify_acks = {{NOTIFY_OP_REBUILD_OBJECT_MAP, create_response_message(0)}};
396 ProgressContext progress_context;
397 RebuildObjectMapTask rebuild_task(ictx, &progress_context);
398 boost::thread thread(boost::ref(rebuild_task));
400 ASSERT_TRUE(wait_for_notifies(*ictx));
402 NotifyOps expected_notify_ops;
403 expected_notify_ops += NOTIFY_OP_REBUILD_OBJECT_MAP;
404 ASSERT_EQ(expected_notify_ops, m_notifies);
406 AsyncRequestId async_request_id;
407 ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_REBUILD_OBJECT_MAP,
410 ASSERT_EQ(0, notify_async_progress(ictx, async_request_id, 10, 20));
411 ASSERT_TRUE(progress_context.wait(ictx, 10, 20));
413 ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, 0));
415 ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
416 ASSERT_EQ(0, rebuild_task.result);
419 TEST_F(TestImageWatcher, NotifySnapCreate) {
420 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
422 librbd::ImageCtx *ictx;
423 ASSERT_EQ(0, open_image(m_image_name, &ictx));
425 ASSERT_EQ(0, register_image_watch(*ictx));
426 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
427 "auto " + stringify(m_watch_ctx->get_handle())));
429 m_notify_acks = {{NOTIFY_OP_SNAP_CREATE, create_response_message(0)}};
431 RWLock::RLocker l(ictx->owner_lock);
432 C_SaferCond notify_ctx;
433 ictx->image_watcher->notify_snap_create(cls::rbd::UserSnapshotNamespace(),
434 "snap", ¬ify_ctx);
435 ASSERT_EQ(0, notify_ctx.wait());
437 NotifyOps expected_notify_ops;
438 expected_notify_ops += NOTIFY_OP_SNAP_CREATE;
439 ASSERT_EQ(expected_notify_ops, m_notifies);
442 TEST_F(TestImageWatcher, NotifySnapCreateError) {
443 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
445 librbd::ImageCtx *ictx;
446 ASSERT_EQ(0, open_image(m_image_name, &ictx));
448 ASSERT_EQ(0, register_image_watch(*ictx));
449 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
450 "auto " + stringify(m_watch_ctx->get_handle())));
452 m_notify_acks = {{NOTIFY_OP_SNAP_CREATE, create_response_message(-EEXIST)}};
454 RWLock::RLocker l(ictx->owner_lock);
455 C_SaferCond notify_ctx;
456 ictx->image_watcher->notify_snap_create(cls::rbd::UserSnapshotNamespace(),
457 "snap", ¬ify_ctx);
458 ASSERT_EQ(-EEXIST, notify_ctx.wait());
460 NotifyOps expected_notify_ops;
461 expected_notify_ops += NOTIFY_OP_SNAP_CREATE;
462 ASSERT_EQ(expected_notify_ops, m_notifies);
465 TEST_F(TestImageWatcher, NotifySnapRename) {
466 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
468 librbd::ImageCtx *ictx;
469 ASSERT_EQ(0, open_image(m_image_name, &ictx));
471 ASSERT_EQ(0, register_image_watch(*ictx));
472 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
473 "auto " + stringify(m_watch_ctx->get_handle())));
475 m_notify_acks = {{NOTIFY_OP_SNAP_RENAME, create_response_message(0)}};
477 RWLock::RLocker l(ictx->owner_lock);
478 C_SaferCond notify_ctx;
479 ictx->image_watcher->notify_snap_rename(1, "snap-rename", ¬ify_ctx);
480 ASSERT_EQ(0, notify_ctx.wait());
482 NotifyOps expected_notify_ops;
483 expected_notify_ops += NOTIFY_OP_SNAP_RENAME;
484 ASSERT_EQ(expected_notify_ops, m_notifies);
487 TEST_F(TestImageWatcher, NotifySnapRenameError) {
488 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
490 librbd::ImageCtx *ictx;
491 ASSERT_EQ(0, open_image(m_image_name, &ictx));
493 ASSERT_EQ(0, register_image_watch(*ictx));
494 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
495 "auto " + stringify(m_watch_ctx->get_handle())));
497 m_notify_acks = {{NOTIFY_OP_SNAP_RENAME, create_response_message(-EEXIST)}};
499 RWLock::RLocker l(ictx->owner_lock);
500 C_SaferCond notify_ctx;
501 ictx->image_watcher->notify_snap_rename(1, "snap-rename", ¬ify_ctx);
502 ASSERT_EQ(-EEXIST, notify_ctx.wait());
504 NotifyOps expected_notify_ops;
505 expected_notify_ops += NOTIFY_OP_SNAP_RENAME;
506 ASSERT_EQ(expected_notify_ops, m_notifies);
509 TEST_F(TestImageWatcher, NotifySnapRemove) {
510 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
512 librbd::ImageCtx *ictx;
513 ASSERT_EQ(0, open_image(m_image_name, &ictx));
515 ASSERT_EQ(0, register_image_watch(*ictx));
516 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
517 "auto " + stringify(m_watch_ctx->get_handle())));
519 m_notify_acks = {{NOTIFY_OP_SNAP_REMOVE, create_response_message(0)}};
521 RWLock::RLocker l(ictx->owner_lock);
522 C_SaferCond notify_ctx;
523 ictx->image_watcher->notify_snap_remove(cls::rbd::UserSnapshotNamespace(),
526 ASSERT_EQ(0, notify_ctx.wait());
528 NotifyOps expected_notify_ops;
529 expected_notify_ops += NOTIFY_OP_SNAP_REMOVE;
530 ASSERT_EQ(expected_notify_ops, m_notifies);
533 TEST_F(TestImageWatcher, NotifySnapProtect) {
534 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
536 librbd::ImageCtx *ictx;
537 ASSERT_EQ(0, open_image(m_image_name, &ictx));
539 ASSERT_EQ(0, register_image_watch(*ictx));
540 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
541 "auto " + stringify(m_watch_ctx->get_handle())));
543 m_notify_acks = {{NOTIFY_OP_SNAP_PROTECT, create_response_message(0)}};
545 RWLock::RLocker l(ictx->owner_lock);
546 C_SaferCond notify_ctx;
547 ictx->image_watcher->notify_snap_protect(cls::rbd::UserSnapshotNamespace(),
550 ASSERT_EQ(0, notify_ctx.wait());
552 NotifyOps expected_notify_ops;
553 expected_notify_ops += NOTIFY_OP_SNAP_PROTECT;
554 ASSERT_EQ(expected_notify_ops, m_notifies);
557 TEST_F(TestImageWatcher, NotifySnapUnprotect) {
558 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
560 librbd::ImageCtx *ictx;
561 ASSERT_EQ(0, open_image(m_image_name, &ictx));
563 ASSERT_EQ(0, register_image_watch(*ictx));
564 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
565 "auto " + stringify(m_watch_ctx->get_handle())));
567 m_notify_acks = {{NOTIFY_OP_SNAP_UNPROTECT, create_response_message(0)}};
569 RWLock::RLocker l(ictx->owner_lock);
570 C_SaferCond notify_ctx;
571 ictx->image_watcher->notify_snap_unprotect(cls::rbd::UserSnapshotNamespace(),
574 ASSERT_EQ(0, notify_ctx.wait());
576 NotifyOps expected_notify_ops;
577 expected_notify_ops += NOTIFY_OP_SNAP_UNPROTECT;
578 ASSERT_EQ(expected_notify_ops, m_notifies);
581 TEST_F(TestImageWatcher, NotifyRename) {
582 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
584 librbd::ImageCtx *ictx;
585 ASSERT_EQ(0, open_image(m_image_name, &ictx));
587 ASSERT_EQ(0, register_image_watch(*ictx));
588 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
589 "auto " + stringify(m_watch_ctx->get_handle())));
591 m_notify_acks = {{NOTIFY_OP_RENAME, create_response_message(0)}};
593 RWLock::RLocker l(ictx->owner_lock);
594 C_SaferCond notify_ctx;
595 ictx->image_watcher->notify_rename("new_name", ¬ify_ctx);
596 ASSERT_EQ(0, notify_ctx.wait());
598 NotifyOps expected_notify_ops;
599 expected_notify_ops += NOTIFY_OP_RENAME;
600 ASSERT_EQ(expected_notify_ops, m_notifies);
603 TEST_F(TestImageWatcher, NotifyAsyncTimedOut) {
604 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
606 librbd::ImageCtx *ictx;
607 ASSERT_EQ(0, open_image(m_image_name, &ictx));
609 ASSERT_EQ(0, register_image_watch(*ictx));
610 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
611 "auto " + stringify(m_watch_ctx->get_handle())));
613 m_notify_acks = {{NOTIFY_OP_FLATTEN, {}}};
615 ProgressContext progress_context;
616 FlattenTask flatten_task(ictx, &progress_context);
617 boost::thread thread(boost::ref(flatten_task));
619 ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
620 ASSERT_EQ(-ETIMEDOUT, flatten_task.result);
623 TEST_F(TestImageWatcher, NotifyAsyncError) {
624 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
626 librbd::ImageCtx *ictx;
627 ASSERT_EQ(0, open_image(m_image_name, &ictx));
629 ASSERT_EQ(0, register_image_watch(*ictx));
630 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
631 "auto " + stringify(m_watch_ctx->get_handle())));
633 m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(-EIO)}};
635 ProgressContext progress_context;
636 FlattenTask flatten_task(ictx, &progress_context);
637 boost::thread thread(boost::ref(flatten_task));
639 ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
640 ASSERT_EQ(-EIO, flatten_task.result);
643 TEST_F(TestImageWatcher, NotifyAsyncCompleteError) {
644 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
646 librbd::ImageCtx *ictx;
647 ASSERT_EQ(0, open_image(m_image_name, &ictx));
649 ASSERT_EQ(0, register_image_watch(*ictx));
650 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
651 "auto " + stringify(m_watch_ctx->get_handle())));
653 m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}};
655 ProgressContext progress_context;
656 FlattenTask flatten_task(ictx, &progress_context);
657 boost::thread thread(boost::ref(flatten_task));
659 ASSERT_TRUE(wait_for_notifies(*ictx));
661 NotifyOps expected_notify_ops;
662 expected_notify_ops += NOTIFY_OP_FLATTEN;
663 ASSERT_EQ(expected_notify_ops, m_notifies);
665 AsyncRequestId async_request_id;
666 ASSERT_TRUE(extract_async_request_id(NOTIFY_OP_FLATTEN, &async_request_id));
668 ASSERT_EQ(0, notify_async_complete(ictx, async_request_id, -ESHUTDOWN));
670 ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
671 ASSERT_EQ(-ESHUTDOWN, flatten_task.result);
674 TEST_F(TestImageWatcher, NotifyAsyncRequestTimedOut) {
675 REQUIRE_FEATURE(RBD_FEATURE_EXCLUSIVE_LOCK);
677 librbd::ImageCtx *ictx;
678 ASSERT_EQ(0, open_image(m_image_name, &ictx));
680 ictx->request_timed_out_seconds = 0;
682 ASSERT_EQ(0, register_image_watch(*ictx));
683 ASSERT_EQ(0, lock_image(*ictx, LOCK_EXCLUSIVE,
684 "auto " + stringify(m_watch_ctx->get_handle())));
686 m_notify_acks = {{NOTIFY_OP_FLATTEN, create_response_message(0)}};
688 ProgressContext progress_context;
689 FlattenTask flatten_task(ictx, &progress_context);
690 boost::thread thread(boost::ref(flatten_task));
692 ASSERT_TRUE(wait_for_notifies(*ictx));
694 ASSERT_TRUE(thread.timed_join(boost::posix_time::seconds(10)));
695 ASSERT_EQ(-ETIMEDOUT, flatten_task.result);