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/mock/MockImageCtx.h"
5 #include "test/rbd_mirror/test_mock_fixture.h"
6 #include "test/rbd_mirror/mock/MockContextWQ.h"
7 #include "test/rbd_mirror/mock/MockSafeTimer.h"
8 #include "tools/rbd_mirror/ImageDeleter.h"
9 #include "tools/rbd_mirror/ImageReplayer.h"
10 #include "tools/rbd_mirror/InstanceWatcher.h"
11 #include "tools/rbd_mirror/InstanceReplayer.h"
12 #include "tools/rbd_mirror/ServiceDaemon.h"
13 #include "tools/rbd_mirror/Threads.h"
14 #include "tools/rbd_mirror/image_replayer/Types.h"
20 struct MockTestImageCtx
: public MockImageCtx
{
21 MockTestImageCtx(librbd::ImageCtx
&image_ctx
)
22 : librbd::MockImageCtx(image_ctx
) {
26 } // anonymous namespace
34 struct Threads
<librbd::MockTestImageCtx
> {
39 MockContextWQ
*work_queue
;
41 Threads(Threads
<librbd::ImageCtx
> *threads
)
42 : timer(new MockSafeTimer()),
43 timer_lock(threads
->timer_lock
),
44 work_queue(new MockContextWQ()) {
53 struct ImageDeleter
<librbd::MockTestImageCtx
> {
57 struct ServiceDaemon
<librbd::MockTestImageCtx
> {
58 MOCK_METHOD3(add_or_update_attribute
,
59 void(int64_t, const std::string
&,
60 const service_daemon::AttributeValue
&));
64 struct InstanceWatcher
<librbd::MockTestImageCtx
> {
68 struct ImageReplayer
<librbd::MockTestImageCtx
> {
69 static ImageReplayer
* s_instance
;
70 std::string global_image_id
;
72 static ImageReplayer
*create(
73 Threads
<librbd::MockTestImageCtx
> *threads
,
74 ImageDeleter
<librbd::MockTestImageCtx
>* image_deleter
,
75 InstanceWatcher
<librbd::MockTestImageCtx
> *instance_watcher
,
76 RadosRef local
, const std::string
&local_mirror_uuid
, int64_t local_pool_id
,
77 const std::string
&global_image_id
) {
78 assert(s_instance
!= nullptr);
79 s_instance
->global_image_id
= global_image_id
;
84 assert(s_instance
== nullptr);
88 virtual ~ImageReplayer() {
89 assert(s_instance
== this);
93 MOCK_METHOD0(destroy
, void());
94 MOCK_METHOD2(start
, void(Context
*, bool));
95 MOCK_METHOD2(stop
, void(Context
*, bool));
96 MOCK_METHOD0(restart
, void());
97 MOCK_METHOD0(flush
, void());
98 MOCK_METHOD2(print_status
, void(Formatter
*, stringstream
*));
99 MOCK_METHOD2(add_peer
, void(const std::string
&, librados::IoCtx
&));
100 MOCK_METHOD0(get_global_image_id
, const std::string
&());
101 MOCK_METHOD0(get_local_image_id
, const std::string
&());
102 MOCK_METHOD0(is_running
, bool());
103 MOCK_METHOD0(is_stopped
, bool());
104 MOCK_METHOD0(is_blacklisted
, bool());
106 MOCK_CONST_METHOD0(is_finished
, bool());
107 MOCK_METHOD1(set_finished
, void(bool));
109 MOCK_CONST_METHOD0(get_health_state
, image_replayer::HealthState());
112 ImageReplayer
<librbd::MockTestImageCtx
>* ImageReplayer
<librbd::MockTestImageCtx
>::s_instance
= nullptr;
114 } // namespace mirror
117 // template definitions
118 #include "tools/rbd_mirror/InstanceReplayer.cc"
124 using ::testing::InSequence
;
125 using ::testing::Invoke
;
126 using ::testing::Return
;
127 using ::testing::ReturnRef
;
128 using ::testing::WithArg
;
130 class TestMockInstanceReplayer
: public TestMockFixture
{
132 typedef Threads
<librbd::MockTestImageCtx
> MockThreads
;
133 typedef ImageDeleter
<librbd::MockTestImageCtx
> MockImageDeleter
;
134 typedef ImageReplayer
<librbd::MockTestImageCtx
> MockImageReplayer
;
135 typedef InstanceReplayer
<librbd::MockTestImageCtx
> MockInstanceReplayer
;
136 typedef InstanceWatcher
<librbd::MockTestImageCtx
> MockInstanceWatcher
;
137 typedef ServiceDaemon
<librbd::MockTestImageCtx
> MockServiceDaemon
;
139 void expect_work_queue(MockThreads
&mock_threads
) {
140 EXPECT_CALL(*mock_threads
.work_queue
, queue(_
, _
))
141 .WillOnce(Invoke([this](Context
*ctx
, int r
) {
142 m_threads
->work_queue
->queue(ctx
, r
);
146 void expect_add_event_after(MockThreads
&mock_threads
,
147 Context
** timer_ctx
= nullptr) {
148 EXPECT_CALL(*mock_threads
.timer
, add_event_after(_
, _
))
149 .WillOnce(WithArg
<1>(
150 Invoke([this, &mock_threads
, timer_ctx
](Context
*ctx
) {
151 assert(mock_threads
.timer_lock
.is_locked());
152 if (timer_ctx
!= nullptr) {
154 mock_threads
.timer_cond
.SignalOne();
156 m_threads
->work_queue
->queue(
157 new FunctionContext([&mock_threads
, ctx
](int) {
158 Mutex::Locker
timer_lock(mock_threads
.timer_lock
);
165 void expect_cancel_event(MockThreads
&mock_threads
, bool canceled
) {
166 EXPECT_CALL(*mock_threads
.timer
, cancel_event(_
))
167 .WillOnce(Return(canceled
));
171 TEST_F(TestMockInstanceReplayer
, AcquireReleaseImage
) {
172 MockThreads
mock_threads(m_threads
);
173 MockServiceDaemon mock_service_daemon
;
174 MockImageDeleter mock_image_deleter
;
175 MockInstanceWatcher mock_instance_watcher
;
176 MockImageReplayer mock_image_replayer
;
177 MockInstanceReplayer
instance_replayer(
178 &mock_threads
, &mock_service_daemon
, &mock_image_deleter
,
179 rbd::mirror::RadosRef(new librados::Rados(m_local_io_ctx
)),
180 "local_mirror_uuid", m_local_io_ctx
.get_id());
181 std::string
global_image_id("global_image_id");
183 EXPECT_CALL(mock_image_replayer
, get_global_image_id())
184 .WillRepeatedly(ReturnRef(global_image_id
));
187 expect_work_queue(mock_threads
);
188 Context
*timer_ctx
= nullptr;
189 expect_add_event_after(mock_threads
, &timer_ctx
);
190 instance_replayer
.init();
191 instance_replayer
.add_peer("peer_uuid", m_remote_io_ctx
);
195 C_SaferCond on_acquire
;
196 EXPECT_CALL(mock_image_replayer
, add_peer("peer_uuid", _
));
197 EXPECT_CALL(mock_image_replayer
, set_finished(false));
198 EXPECT_CALL(mock_image_replayer
, is_stopped()).WillOnce(Return(true));
199 EXPECT_CALL(mock_image_replayer
, is_blacklisted()).WillOnce(Return(false));
200 EXPECT_CALL(mock_image_replayer
, is_finished()).WillOnce(Return(false));
201 EXPECT_CALL(mock_image_replayer
, start(nullptr, false));
202 expect_work_queue(mock_threads
);
204 instance_replayer
.acquire_image(&mock_instance_watcher
, global_image_id
,
206 ASSERT_EQ(0, on_acquire
.wait());
210 C_SaferCond on_release
;
212 EXPECT_CALL(mock_image_replayer
, is_stopped())
213 .WillOnce(Return(false));
214 EXPECT_CALL(mock_image_replayer
, is_running())
215 .WillOnce(Return(false));
216 expect_work_queue(mock_threads
);
217 expect_add_event_after(mock_threads
);
218 expect_work_queue(mock_threads
);
219 EXPECT_CALL(mock_image_replayer
, is_stopped())
220 .WillOnce(Return(false));
221 EXPECT_CALL(mock_image_replayer
, is_running())
222 .WillOnce(Return(true));
223 EXPECT_CALL(mock_image_replayer
, stop(_
, false))
224 .WillOnce(CompleteContext(0));
225 expect_work_queue(mock_threads
);
226 EXPECT_CALL(mock_image_replayer
, is_stopped())
227 .WillOnce(Return(true));
228 expect_work_queue(mock_threads
);
229 EXPECT_CALL(mock_image_replayer
, destroy());
231 instance_replayer
.release_image("global_image_id", &on_release
);
232 ASSERT_EQ(0, on_release
.wait());
234 expect_work_queue(mock_threads
);
235 expect_cancel_event(mock_threads
, true);
236 expect_work_queue(mock_threads
);
237 instance_replayer
.shut_down();
238 ASSERT_TRUE(timer_ctx
!= nullptr);
242 TEST_F(TestMockInstanceReplayer
, RemoveFinishedImage
) {
243 MockThreads
mock_threads(m_threads
);
244 MockServiceDaemon mock_service_daemon
;
245 MockImageDeleter mock_image_deleter
;
246 MockInstanceWatcher mock_instance_watcher
;
247 MockImageReplayer mock_image_replayer
;
248 MockInstanceReplayer
instance_replayer(
249 &mock_threads
, &mock_service_daemon
, &mock_image_deleter
,
250 rbd::mirror::RadosRef(new librados::Rados(m_local_io_ctx
)),
251 "local_mirror_uuid", m_local_io_ctx
.get_id());
252 std::string
global_image_id("global_image_id");
254 EXPECT_CALL(mock_image_replayer
, get_global_image_id())
255 .WillRepeatedly(ReturnRef(global_image_id
));
258 expect_work_queue(mock_threads
);
259 Context
*timer_ctx1
= nullptr;
260 expect_add_event_after(mock_threads
, &timer_ctx1
);
261 instance_replayer
.init();
262 instance_replayer
.add_peer("peer_uuid", m_remote_io_ctx
);
266 C_SaferCond on_acquire
;
267 EXPECT_CALL(mock_image_replayer
, add_peer("peer_uuid", _
));
268 EXPECT_CALL(mock_image_replayer
, set_finished(false));
269 EXPECT_CALL(mock_image_replayer
, is_stopped()).WillOnce(Return(true));
270 EXPECT_CALL(mock_image_replayer
, is_blacklisted()).WillOnce(Return(false));
271 EXPECT_CALL(mock_image_replayer
, is_finished()).WillOnce(Return(false));
272 EXPECT_CALL(mock_image_replayer
, start(nullptr, false));
273 expect_work_queue(mock_threads
);
275 instance_replayer
.acquire_image(&mock_instance_watcher
, global_image_id
,
277 ASSERT_EQ(0, on_acquire
.wait());
279 // periodic start timer
280 Context
*timer_ctx2
= nullptr;
281 expect_add_event_after(mock_threads
, &timer_ctx2
);
283 Context
*start_image_replayers_ctx
= nullptr;
284 EXPECT_CALL(*mock_threads
.work_queue
, queue(_
, 0))
285 .WillOnce(Invoke([&start_image_replayers_ctx
](Context
*ctx
, int r
) {
286 start_image_replayers_ctx
= ctx
;
289 ASSERT_TRUE(timer_ctx1
!= nullptr);
291 Mutex::Locker
timer_locker(mock_threads
.timer_lock
);
292 timer_ctx1
->complete(0);
295 // remove finished image replayer
296 EXPECT_CALL(mock_image_replayer
, get_health_state()).WillOnce(
297 Return(image_replayer::HEALTH_STATE_OK
));
298 EXPECT_CALL(mock_image_replayer
, is_stopped()).WillOnce(Return(true));
299 EXPECT_CALL(mock_image_replayer
, is_blacklisted()).WillOnce(Return(false));
300 EXPECT_CALL(mock_image_replayer
, is_finished()).WillOnce(Return(true));
301 EXPECT_CALL(mock_image_replayer
, destroy());
302 EXPECT_CALL(mock_service_daemon
,add_or_update_attribute(_
, _
, _
)).Times(3);
304 ASSERT_TRUE(start_image_replayers_ctx
!= nullptr);
305 start_image_replayers_ctx
->complete(0);
308 expect_work_queue(mock_threads
);
309 expect_cancel_event(mock_threads
, true);
310 expect_work_queue(mock_threads
);
311 instance_replayer
.shut_down();
312 ASSERT_TRUE(timer_ctx2
!= nullptr);
315 } // namespace mirror