]>
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 | ||
4 | #include "test/librbd/mock/MockImageCtx.h" | |
5 | #include "test/rbd_mirror/test_mock_fixture.h" | |
d2e6a577 FG |
6 | #include "test/rbd_mirror/mock/MockContextWQ.h" |
7 | #include "test/rbd_mirror/mock/MockSafeTimer.h" | |
c07f9fc5 | 8 | #include "tools/rbd_mirror/ImageDeleter.h" |
7c673cae | 9 | #include "tools/rbd_mirror/ImageReplayer.h" |
31f18b77 | 10 | #include "tools/rbd_mirror/InstanceWatcher.h" |
7c673cae | 11 | #include "tools/rbd_mirror/InstanceReplayer.h" |
c07f9fc5 | 12 | #include "tools/rbd_mirror/ServiceDaemon.h" |
7c673cae | 13 | #include "tools/rbd_mirror/Threads.h" |
c07f9fc5 | 14 | #include "tools/rbd_mirror/image_replayer/Types.h" |
7c673cae FG |
15 | |
16 | namespace librbd { | |
17 | ||
18 | namespace { | |
19 | ||
20 | struct MockTestImageCtx : public MockImageCtx { | |
21 | MockTestImageCtx(librbd::ImageCtx &image_ctx) | |
22 | : librbd::MockImageCtx(image_ctx) { | |
23 | } | |
24 | }; | |
25 | ||
26 | } // anonymous namespace | |
27 | ||
28 | } // namespace librbd | |
29 | ||
30 | namespace rbd { | |
31 | namespace mirror { | |
32 | ||
33 | template <> | |
34 | struct Threads<librbd::MockTestImageCtx> { | |
d2e6a577 | 35 | MockSafeTimer *timer; |
7c673cae | 36 | Mutex &timer_lock; |
d2e6a577 FG |
37 | Cond timer_cond; |
38 | ||
39 | MockContextWQ *work_queue; | |
7c673cae FG |
40 | |
41 | Threads(Threads<librbd::ImageCtx> *threads) | |
d2e6a577 FG |
42 | : timer(new MockSafeTimer()), |
43 | timer_lock(threads->timer_lock), | |
44 | work_queue(new MockContextWQ()) { | |
45 | } | |
46 | ~Threads() { | |
47 | delete timer; | |
48 | delete work_queue; | |
7c673cae FG |
49 | } |
50 | }; | |
51 | ||
c07f9fc5 FG |
52 | template <> |
53 | struct ImageDeleter<librbd::MockTestImageCtx> { | |
c07f9fc5 FG |
54 | }; |
55 | ||
56 | template<> | |
57 | struct ServiceDaemon<librbd::MockTestImageCtx> { | |
58 | MOCK_METHOD3(add_or_update_attribute, | |
59 | void(int64_t, const std::string&, | |
60 | const service_daemon::AttributeValue&)); | |
61 | }; | |
62 | ||
31f18b77 FG |
63 | template<> |
64 | struct InstanceWatcher<librbd::MockTestImageCtx> { | |
65 | }; | |
66 | ||
7c673cae FG |
67 | template<> |
68 | struct ImageReplayer<librbd::MockTestImageCtx> { | |
69 | static ImageReplayer* s_instance; | |
70 | std::string global_image_id; | |
71 | ||
72 | static ImageReplayer *create( | |
73 | Threads<librbd::MockTestImageCtx> *threads, | |
c07f9fc5 | 74 | ImageDeleter<librbd::MockTestImageCtx>* image_deleter, |
31f18b77 | 75 | InstanceWatcher<librbd::MockTestImageCtx> *instance_watcher, |
7c673cae FG |
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; | |
80 | return s_instance; | |
81 | } | |
82 | ||
83 | ImageReplayer() { | |
84 | assert(s_instance == nullptr); | |
85 | s_instance = this; | |
86 | } | |
87 | ||
88 | virtual ~ImageReplayer() { | |
89 | assert(s_instance == this); | |
90 | s_instance = nullptr; | |
91 | } | |
92 | ||
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 *)); | |
d2e6a577 | 99 | MOCK_METHOD2(add_peer, void(const std::string &, librados::IoCtx &)); |
7c673cae FG |
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()); | |
c07f9fc5 | 105 | |
d2e6a577 FG |
106 | MOCK_CONST_METHOD0(is_finished, bool()); |
107 | MOCK_METHOD1(set_finished, void(bool)); | |
108 | ||
c07f9fc5 | 109 | MOCK_CONST_METHOD0(get_health_state, image_replayer::HealthState()); |
7c673cae FG |
110 | }; |
111 | ||
7c673cae FG |
112 | ImageReplayer<librbd::MockTestImageCtx>* ImageReplayer<librbd::MockTestImageCtx>::s_instance = nullptr; |
113 | ||
114 | } // namespace mirror | |
115 | } // namespace rbd | |
116 | ||
117 | // template definitions | |
118 | #include "tools/rbd_mirror/InstanceReplayer.cc" | |
119 | ||
120 | namespace rbd { | |
121 | namespace mirror { | |
122 | ||
123 | using ::testing::_; | |
124 | using ::testing::InSequence; | |
125 | using ::testing::Invoke; | |
126 | using ::testing::Return; | |
127 | using ::testing::ReturnRef; | |
c07f9fc5 | 128 | using ::testing::WithArg; |
7c673cae FG |
129 | |
130 | class TestMockInstanceReplayer : public TestMockFixture { | |
131 | public: | |
d2e6a577 | 132 | typedef Threads<librbd::MockTestImageCtx> MockThreads; |
c07f9fc5 | 133 | typedef ImageDeleter<librbd::MockTestImageCtx> MockImageDeleter; |
7c673cae FG |
134 | typedef ImageReplayer<librbd::MockTestImageCtx> MockImageReplayer; |
135 | typedef InstanceReplayer<librbd::MockTestImageCtx> MockInstanceReplayer; | |
31f18b77 | 136 | typedef InstanceWatcher<librbd::MockTestImageCtx> MockInstanceWatcher; |
c07f9fc5 | 137 | typedef ServiceDaemon<librbd::MockTestImageCtx> MockServiceDaemon; |
7c673cae | 138 | |
d2e6a577 FG |
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); | |
143 | })); | |
7c673cae FG |
144 | } |
145 | ||
d2e6a577 FG |
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) { | |
153 | *timer_ctx = ctx; | |
154 | mock_threads.timer_cond.SignalOne(); | |
155 | } else { | |
156 | m_threads->work_queue->queue( | |
157 | new FunctionContext([&mock_threads, ctx](int) { | |
158 | Mutex::Locker timer_lock(mock_threads.timer_lock); | |
159 | ctx->complete(0); | |
160 | }), 0); | |
161 | } | |
162 | }))); | |
7c673cae FG |
163 | } |
164 | ||
d2e6a577 FG |
165 | void expect_cancel_event(MockThreads &mock_threads, bool canceled) { |
166 | EXPECT_CALL(*mock_threads.timer, cancel_event(_)) | |
167 | .WillOnce(Return(canceled)); | |
c07f9fc5 | 168 | } |
7c673cae FG |
169 | }; |
170 | ||
171 | TEST_F(TestMockInstanceReplayer, AcquireReleaseImage) { | |
d2e6a577 | 172 | MockThreads mock_threads(m_threads); |
c07f9fc5 FG |
173 | MockServiceDaemon mock_service_daemon; |
174 | MockImageDeleter mock_image_deleter; | |
31f18b77 | 175 | MockInstanceWatcher mock_instance_watcher; |
7c673cae FG |
176 | MockImageReplayer mock_image_replayer; |
177 | MockInstanceReplayer instance_replayer( | |
d2e6a577 | 178 | &mock_threads, &mock_service_daemon, &mock_image_deleter, |
7c673cae FG |
179 | rbd::mirror::RadosRef(new librados::Rados(m_local_io_ctx)), |
180 | "local_mirror_uuid", m_local_io_ctx.get_id()); | |
7c673cae FG |
181 | std::string global_image_id("global_image_id"); |
182 | ||
183 | EXPECT_CALL(mock_image_replayer, get_global_image_id()) | |
184 | .WillRepeatedly(ReturnRef(global_image_id)); | |
c07f9fc5 | 185 | |
7c673cae | 186 | InSequence seq; |
d2e6a577 FG |
187 | expect_work_queue(mock_threads); |
188 | Context *timer_ctx = nullptr; | |
189 | expect_add_event_after(mock_threads, &timer_ctx); | |
7c673cae | 190 | instance_replayer.init(); |
d2e6a577 | 191 | instance_replayer.add_peer("peer_uuid", m_remote_io_ctx); |
7c673cae FG |
192 | |
193 | // Acquire | |
194 | ||
195 | C_SaferCond on_acquire; | |
d2e6a577 FG |
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)); | |
7c673cae | 201 | EXPECT_CALL(mock_image_replayer, start(nullptr, false)); |
d2e6a577 | 202 | expect_work_queue(mock_threads); |
7c673cae | 203 | |
31f18b77 | 204 | instance_replayer.acquire_image(&mock_instance_watcher, global_image_id, |
31f18b77 | 205 | &on_acquire); |
7c673cae FG |
206 | ASSERT_EQ(0, on_acquire.wait()); |
207 | ||
208 | // Release | |
209 | ||
210 | C_SaferCond on_release; | |
211 | ||
7c673cae FG |
212 | EXPECT_CALL(mock_image_replayer, is_stopped()) |
213 | .WillOnce(Return(false)); | |
214 | EXPECT_CALL(mock_image_replayer, is_running()) | |
215 | .WillOnce(Return(false)); | |
d2e6a577 FG |
216 | expect_work_queue(mock_threads); |
217 | expect_add_event_after(mock_threads); | |
218 | expect_work_queue(mock_threads); | |
7c673cae FG |
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)); | |
d2e6a577 | 225 | expect_work_queue(mock_threads); |
7c673cae FG |
226 | EXPECT_CALL(mock_image_replayer, is_stopped()) |
227 | .WillOnce(Return(true)); | |
d2e6a577 | 228 | expect_work_queue(mock_threads); |
7c673cae FG |
229 | EXPECT_CALL(mock_image_replayer, destroy()); |
230 | ||
d2e6a577 | 231 | instance_replayer.release_image("global_image_id", &on_release); |
7c673cae FG |
232 | ASSERT_EQ(0, on_release.wait()); |
233 | ||
d2e6a577 FG |
234 | expect_work_queue(mock_threads); |
235 | expect_cancel_event(mock_threads, true); | |
236 | expect_work_queue(mock_threads); | |
7c673cae | 237 | instance_replayer.shut_down(); |
d2e6a577 FG |
238 | ASSERT_TRUE(timer_ctx != nullptr); |
239 | delete timer_ctx; | |
7c673cae FG |
240 | } |
241 | ||
d2e6a577 FG |
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"); | |
253 | ||
254 | EXPECT_CALL(mock_image_replayer, get_global_image_id()) | |
255 | .WillRepeatedly(ReturnRef(global_image_id)); | |
256 | ||
257 | InSequence seq; | |
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); | |
263 | ||
264 | // Acquire | |
265 | ||
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); | |
274 | ||
275 | instance_replayer.acquire_image(&mock_instance_watcher, global_image_id, | |
276 | &on_acquire); | |
277 | ASSERT_EQ(0, on_acquire.wait()); | |
278 | ||
279 | // periodic start timer | |
280 | Context *timer_ctx2 = nullptr; | |
281 | expect_add_event_after(mock_threads, &timer_ctx2); | |
282 | ||
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; | |
287 | })); | |
288 | ||
289 | ASSERT_TRUE(timer_ctx1 != nullptr); | |
290 | { | |
291 | Mutex::Locker timer_locker(mock_threads.timer_lock); | |
292 | timer_ctx1->complete(0); | |
293 | } | |
294 | ||
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); | |
303 | ||
304 | ASSERT_TRUE(start_image_replayers_ctx != nullptr); | |
305 | start_image_replayers_ctx->complete(0); | |
306 | ||
307 | // shut down | |
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); | |
313 | delete timer_ctx2; | |
314 | } | |
7c673cae FG |
315 | } // namespace mirror |
316 | } // namespace rbd |