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/rbd_mirror/test_fixture.h"
5 #include "include/stringify.h"
6 #include "include/rbd/librbd.hpp"
7 #include "common/Cond.h"
8 #include "journal/Journaler.h"
9 #include "journal/Settings.h"
10 #include "librbd/ExclusiveLock.h"
11 #include "librbd/ImageCtx.h"
12 #include "librbd/ImageState.h"
13 #include "librbd/internal.h"
14 #include "librbd/Operations.h"
15 #include "librbd/io/AioCompletion.h"
16 #include "librbd/io/ImageDispatchSpec.h"
17 #include "librbd/io/ImageRequestWQ.h"
18 #include "librbd/io/ReadResult.h"
19 #include "librbd/journal/Types.h"
20 #include "tools/rbd_mirror/ImageSync.h"
21 #include "tools/rbd_mirror/InstanceWatcher.h"
22 #include "tools/rbd_mirror/Threads.h"
23 #include "tools/rbd_mirror/Throttler.h"
24
25 void register_test_image_sync() {
26 }
27
28 namespace rbd {
29 namespace mirror {
30
31 namespace {
32
33 int flush(librbd::ImageCtx *image_ctx) {
34 C_SaferCond ctx;
35 auto aio_comp = librbd::io::AioCompletion::create_and_start(
36 &ctx, image_ctx, librbd::io::AIO_TYPE_FLUSH);
37 auto req = librbd::io::ImageDispatchSpec<>::create_flush_request(
38 *image_ctx, aio_comp, librbd::io::FLUSH_SOURCE_INTERNAL, {});
39 req->send();
40 delete req;
41 return ctx.wait();
42 }
43
44 void scribble(librbd::ImageCtx *image_ctx, int num_ops, uint64_t max_size)
45 {
46 max_size = std::min<uint64_t>(image_ctx->size, max_size);
47 for (int i=0; i<num_ops; i++) {
48 uint64_t off = rand() % (image_ctx->size - max_size + 1);
(1) Event dont_call: |
"rand" should not be used for security-related applications, because linear congruential algorithms are too easy to break. |
(2) Event remediation: |
Use a compliant random number generator, such as "/dev/random" or "/dev/urandom" on Unix-like systems, and CNG (Cryptography API: Next Generation) on Windows. |
49 uint64_t len = 1 + rand() % max_size;
50
51 if (rand() % 4 == 0) {
52 ASSERT_EQ((int)len,
53 image_ctx->io_work_queue->discard(
54 off, len, image_ctx->discard_granularity_bytes));
55 } else {
56 bufferlist bl;
57 bl.append(std::string(len, '1'));
58 ASSERT_EQ((int)len, image_ctx->io_work_queue->write(off, len,
59 std::move(bl), 0));
60 }
61 }
62
63 std::shared_lock owner_locker{image_ctx->owner_lock};
64 ASSERT_EQ(0, flush(image_ctx));
65 }
66
67 } // anonymous namespace
68 class TestImageSync : public TestFixture {
69 public:
70
71 void SetUp() override {
72 TestFixture::SetUp();
73 create_and_open(m_local_io_ctx, &m_local_image_ctx);
74 create_and_open(m_remote_io_ctx, &m_remote_image_ctx);
75
76 auto cct = reinterpret_cast<CephContext*>(m_local_io_ctx.cct());
77 m_image_sync_throttler = rbd::mirror::Throttler<>::create(
78 cct, "rbd_mirror_concurrent_image_syncs");
79
80 m_instance_watcher = rbd::mirror::InstanceWatcher<>::create(
81 m_local_io_ctx, m_threads->work_queue, nullptr, m_image_sync_throttler);
82 m_instance_watcher->handle_acquire_leader();
83
84 m_remote_journaler = new ::journal::Journaler(
85 m_threads->work_queue, m_threads->timer, &m_threads->timer_lock,
86 m_remote_io_ctx, m_remote_image_ctx->id, "mirror-uuid", {}, nullptr);
87
88 m_client_meta = {"image-id"};
89
90 librbd::journal::ClientData client_data(m_client_meta);
91 bufferlist client_data_bl;
92 encode(client_data, client_data_bl);
93
94 ASSERT_EQ(0, m_remote_journaler->register_client(client_data_bl));
95 }
96
97 void TearDown() override {
98 m_instance_watcher->handle_release_leader();
99
100 delete m_remote_journaler;
101 delete m_instance_watcher;
102 delete m_image_sync_throttler;
103
104 TestFixture::TearDown();
105 }
106
107 void create_and_open(librados::IoCtx &io_ctx, librbd::ImageCtx **image_ctx) {
108 librbd::RBD rbd;
109 ASSERT_EQ(0, create_image(rbd, io_ctx, m_image_name, m_image_size));
110 ASSERT_EQ(0, open_image(io_ctx, m_image_name, image_ctx));
111
112 C_SaferCond ctx;
113 {
114 std::shared_lock owner_locker{(*image_ctx)->owner_lock};
115 (*image_ctx)->exclusive_lock->try_acquire_lock(&ctx);
116 }
117 ASSERT_EQ(0, ctx.wait());
118 ASSERT_TRUE((*image_ctx)->exclusive_lock->is_lock_owner());
119 }
120
121 ImageSync<> *create_request(Context *ctx) {
122 return new ImageSync<>(m_local_image_ctx, m_remote_image_ctx,
123 m_threads->timer, &m_threads->timer_lock,
124 "mirror-uuid", m_remote_journaler, &m_client_meta,
125 m_threads->work_queue, m_instance_watcher, ctx);
126 }
127
128 librbd::ImageCtx *m_remote_image_ctx;
129 librbd::ImageCtx *m_local_image_ctx;
130 rbd::mirror::Throttler<> *m_image_sync_throttler;
131 rbd::mirror::InstanceWatcher<> *m_instance_watcher;
132 ::journal::Journaler *m_remote_journaler;
133 librbd::journal::MirrorPeerClientMeta m_client_meta;
134 };
135
136 TEST_F(TestImageSync, Empty) {
137 C_SaferCond ctx;
138 ImageSync<> *request = create_request(&ctx);
139 request->send();
140 ASSERT_EQ(0, ctx.wait());
141
142 ASSERT_EQ(0U, m_client_meta.sync_points.size());
143 ASSERT_EQ(0, m_remote_image_ctx->state->refresh());
144 ASSERT_EQ(0U, m_remote_image_ctx->snap_ids.size());
145 ASSERT_EQ(0, m_local_image_ctx->state->refresh());
146 ASSERT_EQ(1U, m_local_image_ctx->snap_ids.size()); // deleted on journal replay
147 }
148
149 TEST_F(TestImageSync, Simple) {
150 scribble(m_remote_image_ctx, 10, 102400);
151
152 C_SaferCond ctx;
153 ImageSync<> *request = create_request(&ctx);
154 request->send();
155 ASSERT_EQ(0, ctx.wait());
156
157 int64_t object_size = std::min<int64_t>(
158 m_remote_image_ctx->size, 1 << m_remote_image_ctx->order);
159 bufferlist read_remote_bl;
160 read_remote_bl.append(std::string(object_size, '1'));
161 bufferlist read_local_bl;
162 read_local_bl.append(std::string(object_size, '1'));
163
164 for (uint64_t offset = 0; offset < m_remote_image_ctx->size;
165 offset += object_size) {
166 ASSERT_LE(0, m_remote_image_ctx->io_work_queue->read(
167 offset, object_size,
168 librbd::io::ReadResult{&read_remote_bl}, 0));
169 ASSERT_LE(0, m_local_image_ctx->io_work_queue->read(
170 offset, object_size,
171 librbd::io::ReadResult{&read_local_bl}, 0));
172 ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl));
173 }
174 }
175
176 TEST_F(TestImageSync, Resize) {
177 int64_t object_size = std::min<int64_t>(
178 m_remote_image_ctx->size, 1 << m_remote_image_ctx->order);
179
180 uint64_t off = 0;
181 uint64_t len = object_size / 10;
182
183 bufferlist bl;
184 bl.append(std::string(len, '1'));
185 ASSERT_EQ((int)len, m_remote_image_ctx->io_work_queue->write(off, len,
186 std::move(bl),
187 0));
188 {
189 std::shared_lock owner_locker{m_remote_image_ctx->owner_lock};
190 ASSERT_EQ(0, flush(m_remote_image_ctx));
191 }
192
193 ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap", nullptr));
194
195 uint64_t size = object_size - 1;
196 librbd::NoOpProgressContext no_op_progress_ctx;
197 ASSERT_EQ(0, m_remote_image_ctx->operations->resize(size, true,
198 no_op_progress_ctx));
199
200 C_SaferCond ctx;
201 ImageSync<> *request = create_request(&ctx);
202 request->send();
203 ASSERT_EQ(0, ctx.wait());
204
205 bufferlist read_remote_bl;
206 read_remote_bl.append(std::string(len, '\0'));
207 bufferlist read_local_bl;
208 read_local_bl.append(std::string(len, '\0'));
209
210 ASSERT_LE(0, m_remote_image_ctx->io_work_queue->read(
211 off, len, librbd::io::ReadResult{&read_remote_bl}, 0));
212 ASSERT_LE(0, m_local_image_ctx->io_work_queue->read(
213 off, len, librbd::io::ReadResult{&read_local_bl}, 0));
214
215 ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl));
216 }
217
218 TEST_F(TestImageSync, Discard) {
219 int64_t object_size = std::min<int64_t>(
220 m_remote_image_ctx->size, 1 << m_remote_image_ctx->order);
221
222 uint64_t off = 0;
223 uint64_t len = object_size / 10;
224
225 bufferlist bl;
226 bl.append(std::string(len, '1'));
227 ASSERT_EQ((int)len, m_remote_image_ctx->io_work_queue->write(off, len,
228 std::move(bl),
229 0));
230 {
231 std::shared_lock owner_locker{m_remote_image_ctx->owner_lock};
232 ASSERT_EQ(0, flush(m_remote_image_ctx));
233 }
234
235 ASSERT_EQ(0, create_snap(m_remote_image_ctx, "snap", nullptr));
236
237 ASSERT_EQ((int)len - 2,
238 m_remote_image_ctx->io_work_queue->discard(
239 off + 1, len - 2, m_remote_image_ctx->discard_granularity_bytes));
240 {
241 std::shared_lock owner_locker{m_remote_image_ctx->owner_lock};
242 ASSERT_EQ(0, flush(m_remote_image_ctx));
243 }
244
245 C_SaferCond ctx;
246 ImageSync<> *request = create_request(&ctx);
247 request->send();
248 ASSERT_EQ(0, ctx.wait());
249
250 bufferlist read_remote_bl;
251 read_remote_bl.append(std::string(object_size, '\0'));
252 bufferlist read_local_bl;
253 read_local_bl.append(std::string(object_size, '\0'));
254
255 ASSERT_LE(0, m_remote_image_ctx->io_work_queue->read(
256 off, len, librbd::io::ReadResult{&read_remote_bl}, 0));
257 ASSERT_LE(0, m_local_image_ctx->io_work_queue->read(
258 off, len, librbd::io::ReadResult{&read_local_bl}, 0));
259
260 ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl));
261 }
262
263 TEST_F(TestImageSync, SnapshotStress) {
264 std::list<std::string> snap_names;
265
266 const int num_snaps = 4;
267 for (int idx = 0; idx <= num_snaps; ++idx) {
268 scribble(m_remote_image_ctx, 10, 102400);
269
270 librbd::NoOpProgressContext no_op_progress_ctx;
271 uint64_t size = 1 + rand() % m_image_size;
272 ASSERT_EQ(0, m_remote_image_ctx->operations->resize(size, true,
273 no_op_progress_ctx));
274 ASSERT_EQ(0, m_remote_image_ctx->state->refresh());
275
276 if (idx < num_snaps) {
277 snap_names.push_back("snap" + stringify(idx + 1));
278 ASSERT_EQ(0, create_snap(m_remote_image_ctx, snap_names.back().c_str(),
279 nullptr));
280 } else {
281 snap_names.push_back("");
282 }
283 }
284
285 C_SaferCond ctx;
286 ImageSync<> *request = create_request(&ctx);
287 request->send();
288 ASSERT_EQ(0, ctx.wait());
289
290 int64_t object_size = std::min<int64_t>(
291 m_remote_image_ctx->size, 1 << m_remote_image_ctx->order);
292 bufferlist read_remote_bl;
293 read_remote_bl.append(std::string(object_size, '1'));
294 bufferlist read_local_bl;
295 read_local_bl.append(std::string(object_size, '1'));
296
297 for (auto &snap_name : snap_names) {
298 uint64_t remote_snap_id;
299 {
300 std::shared_lock remote_image_locker{m_remote_image_ctx->image_lock};
301 remote_snap_id = m_remote_image_ctx->get_snap_id(
302 cls::rbd::UserSnapshotNamespace{}, snap_name);
303 }
304
305 uint64_t remote_size;
306 {
307 C_SaferCond ctx;
308 m_remote_image_ctx->state->snap_set(remote_snap_id, &ctx);
309 ASSERT_EQ(0, ctx.wait());
310
311 std::shared_lock remote_image_locker{m_remote_image_ctx->image_lock};
312 remote_size = m_remote_image_ctx->get_image_size(
313 m_remote_image_ctx->snap_id);
314 }
315
316 uint64_t local_snap_id;
317 {
318 std::shared_lock image_locker{m_local_image_ctx->image_lock};
319 local_snap_id = m_local_image_ctx->get_snap_id(
320 cls::rbd::UserSnapshotNamespace{}, snap_name);
321 }
322
323 uint64_t local_size;
324 {
325 C_SaferCond ctx;
326 m_local_image_ctx->state->snap_set(local_snap_id, &ctx);
327 ASSERT_EQ(0, ctx.wait());
328
329 std::shared_lock image_locker{m_local_image_ctx->image_lock};
330 local_size = m_local_image_ctx->get_image_size(
331 m_local_image_ctx->snap_id);
332 bool flags_set;
333 ASSERT_EQ(0, m_local_image_ctx->test_flags(m_local_image_ctx->snap_id,
334 RBD_FLAG_OBJECT_MAP_INVALID,
335 m_local_image_ctx->image_lock,
336 &flags_set));
337 ASSERT_FALSE(flags_set);
338 }
339
340 ASSERT_EQ(remote_size, local_size);
341
342 for (uint64_t offset = 0; offset < remote_size; offset += object_size) {
343 ASSERT_LE(0, m_remote_image_ctx->io_work_queue->read(
344 offset, object_size,
345 librbd::io::ReadResult{&read_remote_bl}, 0));
346 ASSERT_LE(0, m_local_image_ctx->io_work_queue->read(
347 offset, object_size,
348 librbd::io::ReadResult{&read_local_bl}, 0));
349 ASSERT_TRUE(read_remote_bl.contents_equal(read_local_bl));
350 }
351 }
352 }
353
354 } // namespace mirror
355 } // namespace rbd
356