]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/librbd/deep_copy/test_mock_ImageCopyRequest.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / test / librbd / deep_copy / test_mock_ImageCopyRequest.cc
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/test_mock_fixture.h"
5 #include "include/rbd/librbd.hpp"
6 #include "librbd/ImageCtx.h"
7 #include "librbd/ImageState.h"
8 #include "librbd/Operations.h"
9 #include "librbd/deep_copy/ImageCopyRequest.h"
10 #include "librbd/deep_copy/ObjectCopyRequest.h"
11 #include "librbd/image/CloseRequest.h"
12 #include "librbd/image/OpenRequest.h"
13 #include "librbd/internal.h"
14 #include "test/librados_test_stub/MockTestMemIoCtxImpl.h"
15 #include "test/librbd/mock/MockImageCtx.h"
16 #include "test/librbd/test_support.h"
17 #include <boost/scope_exit.hpp>
18
19 namespace librbd {
20
21 namespace {
22
23 struct MockTestImageCtx : public librbd::MockImageCtx {
24 static MockTestImageCtx* s_instance;
25 static MockTestImageCtx* create(const std::string &image_name,
26 const std::string &image_id,
27 librados::snap_t snap_id, librados::IoCtx& p,
28 bool read_only) {
29 ceph_assert(s_instance != nullptr);
30 return s_instance;
31 }
32
33 explicit MockTestImageCtx(librbd::ImageCtx &image_ctx)
34 : librbd::MockImageCtx(image_ctx) {
35 s_instance = this;
36 }
37
38 MOCK_METHOD0(destroy, void());
39 };
40
41 MockTestImageCtx* MockTestImageCtx::s_instance = nullptr;
42
43 } // anonymous namespace
44
45 namespace deep_copy {
46
47 template <>
48 struct ObjectCopyRequest<librbd::MockTestImageCtx> {
49 static ObjectCopyRequest* s_instance;
50 static ObjectCopyRequest* create(
51 librbd::MockTestImageCtx *src_image_ctx,
52 librbd::MockTestImageCtx *dst_image_ctx, const SnapMap &snap_map,
53 uint64_t object_number, bool flatten, Context *on_finish) {
54 ceph_assert(s_instance != nullptr);
55 Mutex::Locker locker(s_instance->lock);
56 s_instance->snap_map = &snap_map;
57 s_instance->object_contexts[object_number] = on_finish;
58 s_instance->cond.Signal();
59 return s_instance;
60 }
61
62 MOCK_METHOD0(send, void());
63
64 Mutex lock;
65 Cond cond;
66
67 const SnapMap *snap_map = nullptr;
68 std::map<uint64_t, Context *> object_contexts;
69
70 ObjectCopyRequest() : lock("lock") {
71 s_instance = this;
72 }
73 };
74
75 ObjectCopyRequest<librbd::MockTestImageCtx>* ObjectCopyRequest<librbd::MockTestImageCtx>::s_instance = nullptr;
76
77 } // namespace deep_copy
78
79 namespace image {
80
81 template <>
82 struct CloseRequest<MockTestImageCtx> {
83 Context* on_finish = nullptr;
84 static CloseRequest* s_instance;
85 static CloseRequest* create(MockTestImageCtx *image_ctx, Context *on_finish) {
86 ceph_assert(s_instance != nullptr);
87 s_instance->on_finish = on_finish;
88 return s_instance;
89 }
90
91 MOCK_METHOD0(send, void());
92
93 CloseRequest() {
94 s_instance = this;
95 }
96 };
97
98 CloseRequest<MockTestImageCtx>* CloseRequest<MockTestImageCtx>::s_instance = nullptr;
99
100 template <>
101 struct OpenRequest<MockTestImageCtx> {
102 Context* on_finish = nullptr;
103 static OpenRequest* s_instance;
104 static OpenRequest* create(MockTestImageCtx *image_ctx,
105 bool skip_open_parent, Context *on_finish) {
106 ceph_assert(s_instance != nullptr);
107 s_instance->on_finish = on_finish;
108 return s_instance;
109 }
110
111 MOCK_METHOD0(send, void());
112
113 OpenRequest() {
114 s_instance = this;
115 }
116 };
117
118 OpenRequest<MockTestImageCtx>* OpenRequest<MockTestImageCtx>::s_instance = nullptr;
119
120 } // namespace image
121
122 } // namespace librbd
123
124 // template definitions
125 #include "librbd/deep_copy/ImageCopyRequest.cc"
126 template class librbd::deep_copy::ImageCopyRequest<librbd::MockTestImageCtx>;
127
128 namespace librbd {
129 namespace deep_copy {
130
131 using ::testing::_;
132 using ::testing::InSequence;
133 using ::testing::Return;
134
135 class TestMockDeepCopyImageCopyRequest : public TestMockFixture {
136 public:
137 typedef ImageCopyRequest<librbd::MockTestImageCtx> MockImageCopyRequest;
138 typedef ObjectCopyRequest<librbd::MockTestImageCtx> MockObjectCopyRequest;
139
140 librbd::ImageCtx *m_src_image_ctx;
141 librbd::ImageCtx *m_dst_image_ctx;
142 ThreadPool *m_thread_pool;
143 ContextWQ *m_work_queue;
144 librbd::SnapSeqs m_snap_seqs;
145 SnapMap m_snap_map;
146
147 void SetUp() override {
148 TestMockFixture::SetUp();
149
150 ASSERT_EQ(0, open_image(m_image_name, &m_src_image_ctx));
151
152 librbd::RBD rbd;
153 std::string dst_image_name = get_temp_image_name();
154 ASSERT_EQ(0, create_image_pp(rbd, m_ioctx, dst_image_name, m_image_size));
155 ASSERT_EQ(0, open_image(dst_image_name, &m_dst_image_ctx));
156
157 librbd::ImageCtx::get_thread_pool_instance(m_src_image_ctx->cct,
158 &m_thread_pool, &m_work_queue);
159 }
160
161 void expect_get_image_size(librbd::MockTestImageCtx &mock_image_ctx,
162 uint64_t size) {
163 EXPECT_CALL(mock_image_ctx, get_image_size(_))
164 .WillOnce(Return(size)).RetiresOnSaturation();
165 }
166
167 void expect_object_copy_send(MockObjectCopyRequest &mock_object_copy_request) {
168 EXPECT_CALL(mock_object_copy_request, send());
169 }
170
171 bool complete_object_copy(MockObjectCopyRequest &mock_object_copy_request,
172 uint64_t object_num, Context **object_ctx, int r) {
173 Mutex::Locker locker(mock_object_copy_request.lock);
174 while (mock_object_copy_request.object_contexts.count(object_num) == 0) {
175 if (mock_object_copy_request.cond.WaitInterval(mock_object_copy_request.lock,
176 utime_t(10, 0)) != 0) {
177 return false;
178 }
179 }
180
181 if (object_ctx != nullptr) {
182 *object_ctx = mock_object_copy_request.object_contexts[object_num];
183 } else {
184 m_work_queue->queue(mock_object_copy_request.object_contexts[object_num],
185 r);
186 }
187 return true;
188 }
189
190 SnapMap wait_for_snap_map(MockObjectCopyRequest &mock_object_copy_request) {
191 Mutex::Locker locker(mock_object_copy_request.lock);
192 while (mock_object_copy_request.snap_map == nullptr) {
193 if (mock_object_copy_request.cond.WaitInterval(mock_object_copy_request.lock,
194 utime_t(10, 0)) != 0) {
195 return SnapMap();
196 }
197 }
198 return *mock_object_copy_request.snap_map;
199 }
200
201 int create_snap(librbd::ImageCtx *image_ctx, const char* snap_name,
202 librados::snap_t *snap_id) {
203 int r = image_ctx->operations->snap_create(
204 cls::rbd::UserSnapshotNamespace(), snap_name);
205 if (r < 0) {
206 return r;
207 }
208
209 r = image_ctx->state->refresh();
210 if (r < 0) {
211 return r;
212 }
213
214 if (image_ctx->snap_ids.count({cls::rbd::UserSnapshotNamespace(),
215 snap_name}) == 0) {
216 return -ENOENT;
217 }
218
219 if (snap_id != nullptr) {
220 *snap_id = image_ctx->snap_ids[{cls::rbd::UserSnapshotNamespace(),
221 snap_name}];
222 }
223 return 0;
224 }
225
226 int create_snap(const char* snap_name,
227 librados::snap_t *src_snap_id_ = nullptr) {
228 librados::snap_t src_snap_id;
229 int r = create_snap(m_src_image_ctx, snap_name, &src_snap_id);
230 if (r < 0) {
231 return r;
232 }
233
234 if (src_snap_id_ != nullptr) {
235 *src_snap_id_ = src_snap_id;
236 }
237
238 librados::snap_t dst_snap_id;
239 r = create_snap(m_dst_image_ctx, snap_name, &dst_snap_id);
240 if (r < 0) {
241 return r;
242 }
243
244 // collection of all existing snaps in dst image
245 SnapIds dst_snap_ids({dst_snap_id});
246 if (!m_snap_map.empty()) {
247 dst_snap_ids.insert(dst_snap_ids.end(),
248 m_snap_map.rbegin()->second.begin(),
249 m_snap_map.rbegin()->second.end());
250 }
251 m_snap_map[src_snap_id] = dst_snap_ids;
252 m_snap_seqs[src_snap_id] = dst_snap_id;
253 return 0;
254 }
255 };
256
257 TEST_F(TestMockDeepCopyImageCopyRequest, SimpleImage) {
258 librados::snap_t snap_id_end;
259 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
260
261 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
262 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
263 MockObjectCopyRequest mock_object_copy_request;
264
265 InSequence seq;
266 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
267 expect_get_image_size(mock_src_image_ctx, 0);
268 expect_object_copy_send(mock_object_copy_request);
269
270 librbd::NoOpProgressContext no_op;
271 C_SaferCond ctx;
272 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
273 &mock_dst_image_ctx,
274 0, snap_id_end, false, boost::none,
275 m_snap_seqs, &no_op, &ctx);
276 request->send();
277
278 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
279 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
280 ASSERT_EQ(0, ctx.wait());
281 }
282
283 TEST_F(TestMockDeepCopyImageCopyRequest, OutOfOrder) {
284 std::string max_ops_str;
285 ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
286 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "10"));
287 BOOST_SCOPE_EXIT( (max_ops_str) ) {
288 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
289 max_ops_str.c_str()));
290 } BOOST_SCOPE_EXIT_END;
291
292 librados::snap_t snap_id_end;
293 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
294
295 uint64_t object_count = 55;
296
297 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
298 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
299 MockObjectCopyRequest mock_object_copy_request;
300
301 expect_get_image_size(mock_src_image_ctx,
302 object_count * (1 << m_src_image_ctx->order));
303 expect_get_image_size(mock_src_image_ctx, 0);
304
305 EXPECT_CALL(mock_object_copy_request, send()).Times(object_count);
306
307 class ProgressContext : public librbd::ProgressContext {
308 public:
309 uint64_t object_count;
310 librbd::deep_copy::ObjectNumber expected_object_number;
311
312 ProgressContext(uint64_t object_count)
313 : object_count(object_count) {
314 }
315
316 int update_progress(uint64_t object_no, uint64_t end_object_no) override {
317 EXPECT_LE(object_no, object_count);
318 EXPECT_EQ(end_object_no, object_count);
319 if (!expected_object_number) {
320 expected_object_number = 0;
321 } else {
322 expected_object_number = *expected_object_number + 1;
323 }
324 EXPECT_EQ(*expected_object_number, object_no - 1);
325
326 return 0;
327 }
328 } prog_ctx(object_count);
329
330 C_SaferCond ctx;
331 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
332 &mock_dst_image_ctx,
333 0, snap_id_end, false, boost::none,
334 m_snap_seqs, &prog_ctx, &ctx);
335 request->send();
336
337 std::map<uint64_t, Context*> copy_contexts;
338 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
339 for (uint64_t i = 0; i < object_count; ++i) {
340 if (i % 10 == 0) {
341 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, i,
342 &copy_contexts[i], 0));
343 } else {
344 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, i, nullptr,
345 0));
346 }
347 }
348
349 for (auto& pair : copy_contexts) {
350 pair.second->complete(0);
351 }
352
353 ASSERT_EQ(0, ctx.wait());
354 }
355
356 TEST_F(TestMockDeepCopyImageCopyRequest, SnapshotSubset) {
357 librados::snap_t snap_id_start;
358 librados::snap_t snap_id_end;
359 ASSERT_EQ(0, create_snap("snap1"));
360 ASSERT_EQ(0, create_snap("snap2", &snap_id_start));
361 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
362
363 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
364 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
365 MockObjectCopyRequest mock_object_copy_request;
366
367 InSequence seq;
368 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
369 expect_get_image_size(mock_src_image_ctx, 0);
370 expect_get_image_size(mock_src_image_ctx, 0);
371 expect_get_image_size(mock_src_image_ctx, 0);
372 expect_object_copy_send(mock_object_copy_request);
373
374 librbd::NoOpProgressContext no_op;
375 C_SaferCond ctx;
376 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
377 &mock_dst_image_ctx,
378 snap_id_start, snap_id_end, false,
379 boost::none, m_snap_seqs, &no_op,
380 &ctx);
381 request->send();
382
383 SnapMap snap_map(m_snap_map);
384 snap_map.erase(snap_map.begin());
385 ASSERT_EQ(snap_map, wait_for_snap_map(mock_object_copy_request));
386
387 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
388 ASSERT_EQ(0, ctx.wait());
389 }
390
391 TEST_F(TestMockDeepCopyImageCopyRequest, RestartPartialSync) {
392 librados::snap_t snap_id_end;
393 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
394
395 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
396 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
397 MockObjectCopyRequest mock_object_copy_request;
398
399 InSequence seq;
400 expect_get_image_size(mock_src_image_ctx, 2 * (1 << m_src_image_ctx->order));
401 expect_get_image_size(mock_src_image_ctx, 0);
402 expect_object_copy_send(mock_object_copy_request);
403
404 librbd::NoOpProgressContext no_op;
405 C_SaferCond ctx;
406 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
407 &mock_dst_image_ctx,
408 0, snap_id_end, false,
409 librbd::deep_copy::ObjectNumber{0U},
410 m_snap_seqs, &no_op, &ctx);
411 request->send();
412
413 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0));
414 ASSERT_EQ(0, ctx.wait());
415 }
416
417 TEST_F(TestMockDeepCopyImageCopyRequest, Cancel) {
418 std::string max_ops_str;
419 ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
420 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "1"));
421 BOOST_SCOPE_EXIT( (max_ops_str) ) {
422 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
423 max_ops_str.c_str()));
424 } BOOST_SCOPE_EXIT_END;
425
426 librados::snap_t snap_id_end;
427 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
428
429 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
430 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
431 MockObjectCopyRequest mock_object_copy_request;
432
433 InSequence seq;
434 expect_get_image_size(mock_src_image_ctx, 1 << m_src_image_ctx->order);
435 expect_get_image_size(mock_src_image_ctx, 0);
436 expect_object_copy_send(mock_object_copy_request);
437
438 librbd::NoOpProgressContext no_op;
439 C_SaferCond ctx;
440 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
441 &mock_dst_image_ctx,
442 0, snap_id_end, false, boost::none,
443 m_snap_seqs, &no_op, &ctx);
444 request->send();
445
446 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
447 request->cancel();
448
449 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
450 ASSERT_EQ(-ECANCELED, ctx.wait());
451 }
452
453 TEST_F(TestMockDeepCopyImageCopyRequest, Cancel_Inflight_Sync) {
454 std::string max_ops_str;
455 ASSERT_EQ(0, _rados.conf_get("rbd_concurrent_management_ops", max_ops_str));
456 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops", "3"));
457 BOOST_SCOPE_EXIT( (max_ops_str) ) {
458 ASSERT_EQ(0, _rados.conf_set("rbd_concurrent_management_ops",
459 max_ops_str.c_str()));
460 } BOOST_SCOPE_EXIT_END;
461
462 librados::snap_t snap_id_end;
463 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
464
465 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
466 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
467 MockObjectCopyRequest mock_object_copy_request;
468
469 InSequence seq;
470 expect_get_image_size(mock_src_image_ctx, 6 * (1 << m_src_image_ctx->order));
471 expect_get_image_size(mock_src_image_ctx, m_image_size);
472
473 EXPECT_CALL(mock_object_copy_request, send()).Times(6);
474
475 struct ProgressContext : public librbd::ProgressContext {
476 librbd::deep_copy::ObjectNumber object_number;
477
478 int update_progress(uint64_t object_no, uint64_t end_object_no) override {
479 object_number = object_number ? *object_number + 1 : 0;
480 return 0;
481 }
482 } prog_ctx;
483
484 C_SaferCond ctx;
485 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
486 &mock_dst_image_ctx,
487 0, snap_id_end, false, boost::none,
488 m_snap_seqs, &prog_ctx, &ctx);
489 request->send();
490
491 ASSERT_EQ(m_snap_map, wait_for_snap_map(mock_object_copy_request));
492
493 Context *cancel_ctx = nullptr;
494 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 0, nullptr, 0));
495 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 1, nullptr, 0));
496 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 2, nullptr, 0));
497 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 3, &cancel_ctx,
498 0));
499 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 4, nullptr, 0));
500 ASSERT_TRUE(complete_object_copy(mock_object_copy_request, 5, nullptr, 0));
501
502 request->cancel();
503 cancel_ctx->complete(0);
504
505 ASSERT_EQ(-ECANCELED, ctx.wait());
506 ASSERT_EQ(5u, prog_ctx.object_number.get());
507 }
508
509 TEST_F(TestMockDeepCopyImageCopyRequest, MissingSnap) {
510 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
511 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
512
513 librbd::NoOpProgressContext no_op;
514 C_SaferCond ctx;
515 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
516 &mock_dst_image_ctx,
517 0, 123, false, boost::none,
518 m_snap_seqs, &no_op, &ctx);
519 request->send();
520 ASSERT_EQ(-EINVAL, ctx.wait());
521 }
522
523 TEST_F(TestMockDeepCopyImageCopyRequest, MissingFromSnap) {
524 librados::snap_t snap_id_end;
525 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
526
527 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
528 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
529
530 librbd::NoOpProgressContext no_op;
531 C_SaferCond ctx;
532 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
533 &mock_dst_image_ctx,
534 123, snap_id_end, false, boost::none,
535 m_snap_seqs, &no_op, &ctx);
536 request->send();
537 ASSERT_EQ(-EINVAL, ctx.wait());
538 }
539
540 TEST_F(TestMockDeepCopyImageCopyRequest, EmptySnapMap) {
541 librados::snap_t snap_id_start;
542 librados::snap_t snap_id_end;
543 ASSERT_EQ(0, create_snap("snap1", &snap_id_start));
544 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
545
546 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
547 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
548
549 librbd::NoOpProgressContext no_op;
550 C_SaferCond ctx;
551 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
552 &mock_dst_image_ctx,
553 snap_id_start, snap_id_end, false,
554 boost::none, {{0, 0}}, &no_op, &ctx);
555 request->send();
556 ASSERT_EQ(-EINVAL, ctx.wait());
557 }
558
559 TEST_F(TestMockDeepCopyImageCopyRequest, EmptySnapSeqs) {
560 librados::snap_t snap_id_start;
561 librados::snap_t snap_id_end;
562 ASSERT_EQ(0, create_snap("snap1", &snap_id_start));
563 ASSERT_EQ(0, create_snap("copy", &snap_id_end));
564
565 librbd::MockTestImageCtx mock_src_image_ctx(*m_src_image_ctx);
566 librbd::MockTestImageCtx mock_dst_image_ctx(*m_dst_image_ctx);
567
568 librbd::NoOpProgressContext no_op;
569 C_SaferCond ctx;
570 auto request = new MockImageCopyRequest(&mock_src_image_ctx,
571 &mock_dst_image_ctx,
572 snap_id_start, snap_id_end, false,
573 boost::none, {}, &no_op, &ctx);
574 request->send();
575 ASSERT_EQ(-EINVAL, ctx.wait());
576 }
577
578 } // namespace deep_copy
579 } // namespace librbd