]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/api/Group.cc
buildsys: switch source download to quincy
[ceph.git] / ceph / src / librbd / api / Group.cc
CommitLineData
11fdf7f2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
9f95a23c 4#include "common/Cond.h"
11fdf7f2
TL
5#include "common/errno.h"
6
7#include "librbd/ExclusiveLock.h"
8#include "librbd/api/Group.h"
9#include "librbd/ImageCtx.h"
10#include "librbd/ImageState.h"
11#include "librbd/ImageWatcher.h"
12#include "librbd/Operations.h"
13#include "librbd/Utils.h"
f67539c2 14#include "librbd/internal.h"
11fdf7f2
TL
15#include "librbd/io/AioCompletion.h"
16
17#define dout_subsys ceph_subsys_rbd
18#undef dout_prefix
19#define dout_prefix *_dout << "librbd::api::Group: " << __func__ << ": "
20
21using std::map;
22using std::pair;
23using std::set;
24using std::string;
25using std::vector;
26// list binds to list() here, so std::list is explicitly used below
27
28using ceph::bufferlist;
29using librados::snap_t;
30using librados::IoCtx;
31using librados::Rados;
32
33
34namespace librbd {
35namespace api {
36
37namespace {
38
39template <typename I>
40snap_t get_group_snap_id(I* ictx,
41 const cls::rbd::SnapshotNamespace& in_snap_namespace) {
9f95a23c
TL
42 ceph_assert(ceph_mutex_is_locked(ictx->image_lock));
43 auto it = ictx->snap_ids.lower_bound({cls::rbd::GroupSnapshotNamespace{},
44 ""});
45 for (; it != ictx->snap_ids.end(); ++it) {
46 if (it->first.first == in_snap_namespace) {
47 return it->second;
48 } else if (boost::get<cls::rbd::GroupSnapshotNamespace>(&it->first.first) ==
49 nullptr) {
50 break;
51 }
11fdf7f2
TL
52 }
53 return CEPH_NOSNAP;
54}
55
56string generate_uuid(librados::IoCtx& io_ctx)
57{
58 Rados rados(io_ctx);
59 uint64_t bid = rados.get_instance_id();
60
61 uint32_t extra = rand() % 0xFFFFFFFF;
62 ostringstream bid_ss;
63 bid_ss << std::hex << bid << std::hex << extra;
64 return bid_ss.str();
65}
66
67int group_snap_list(librados::IoCtx& group_ioctx, const char *group_name,
68 std::vector<cls::rbd::GroupSnapshot> *cls_snaps)
69{
70 CephContext *cct = (CephContext *)group_ioctx.cct();
71
72 string group_id;
73 vector<string> ind_snap_names;
74
75 int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
76 group_name, &group_id);
77 if (r < 0) {
78 lderr(cct) << "error reading group id object: "
79 << cpp_strerror(r)
80 << dendl;
81 return r;
82 }
83 string group_header_oid = util::group_header_name(group_id);
84
85 const int max_read = 1024;
86 cls::rbd::GroupSnapshot snap_last;
87
88 for (;;) {
89 vector<cls::rbd::GroupSnapshot> snaps_page;
90
91 r = cls_client::group_snap_list(&group_ioctx, group_header_oid,
92 snap_last, max_read, &snaps_page);
93
94 if (r < 0) {
95 lderr(cct) << "error reading snap list from group: "
96 << cpp_strerror(-r) << dendl;
97 return r;
98 }
99 cls_snaps->insert(cls_snaps->end(), snaps_page.begin(), snaps_page.end());
100 if (snaps_page.size() < max_read) {
101 break;
102 }
103 snap_last = *snaps_page.rbegin();
104 }
105
106 return 0;
107}
108
109std::string calc_ind_image_snap_name(uint64_t pool_id,
110 const std::string &group_id,
111 const std::string &snap_id)
112{
113 std::stringstream ind_snap_name_stream;
114 ind_snap_name_stream << ".group." << std::hex << pool_id << "_"
115 << group_id << "_" << snap_id;
116 return ind_snap_name_stream.str();
117}
118
119int group_image_list(librados::IoCtx& group_ioctx, const char *group_name,
120 std::vector<cls::rbd::GroupImageStatus> *image_ids)
121{
122 CephContext *cct = (CephContext *)group_ioctx.cct();
123
124 string group_id;
125
126 int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
127 group_name, &group_id);
128 if (r < 0) {
129 lderr(cct) << "error reading group id object: "
130 << cpp_strerror(r)
131 << dendl;
132 return r;
133 }
134 string group_header_oid = util::group_header_name(group_id);
135
136 ldout(cct, 20) << "listing images in group name "
137 << group_name << " group id " << group_header_oid << dendl;
138 image_ids->clear();
139
140 const int max_read = 1024;
141 cls::rbd::GroupImageSpec start_last;
142 do {
143 std::vector<cls::rbd::GroupImageStatus> image_ids_page;
144
145 r = cls_client::group_image_list(&group_ioctx, group_header_oid,
146 start_last, max_read, &image_ids_page);
147
148 if (r < 0) {
149 lderr(cct) << "error reading image list from group: "
150 << cpp_strerror(-r) << dendl;
151 return r;
152 }
153 image_ids->insert(image_ids->end(),
154 image_ids_page.begin(), image_ids_page.end());
155
156 if (image_ids_page.size() > 0)
157 start_last = image_ids_page.rbegin()->spec;
158
159 r = image_ids_page.size();
160 } while (r == max_read);
161
162 return 0;
163}
164
165int group_image_remove(librados::IoCtx& group_ioctx, string group_id,
166 librados::IoCtx& image_ioctx, string image_id)
167{
168 CephContext *cct = (CephContext *)group_ioctx.cct();
169
170 string group_header_oid = util::group_header_name(group_id);
171
172 string image_header_oid = util::header_name(image_id);
173
174 ldout(cct, 20) << "removing image " << image_id
175 << " image id " << image_header_oid << dendl;
176
177 cls::rbd::GroupSpec group_spec(group_id, group_ioctx.get_id());
178
179 cls::rbd::GroupImageStatus incomplete_st(image_id, image_ioctx.get_id(),
180 cls::rbd::GROUP_IMAGE_LINK_STATE_INCOMPLETE);
181
182 cls::rbd::GroupImageSpec spec(image_id, image_ioctx.get_id());
183
184 int r = cls_client::group_image_set(&group_ioctx, group_header_oid,
185 incomplete_st);
186
187 if (r < 0) {
188 lderr(cct) << "couldn't put image into removing state: "
189 << cpp_strerror(-r) << dendl;
190 return r;
191 }
192
193 r = cls_client::image_group_remove(&image_ioctx, image_header_oid,
194 group_spec);
195 if ((r < 0) && (r != -ENOENT)) {
196 lderr(cct) << "couldn't remove group reference from image"
197 << cpp_strerror(-r) << dendl;
198 return r;
199 } else if (r >= 0) {
200 ImageWatcher<>::notify_header_update(image_ioctx, image_header_oid);
201 }
202
203 r = cls_client::group_image_remove(&group_ioctx, group_header_oid, spec);
204 if (r < 0) {
205 lderr(cct) << "couldn't remove image from group"
206 << cpp_strerror(-r) << dendl;
207 return r;
208 }
209
210 return 0;
211}
212
213int group_snap_remove_by_record(librados::IoCtx& group_ioctx,
214 const cls::rbd::GroupSnapshot& group_snap,
215 const std::string& group_id,
216 const std::string& group_header_oid) {
217
218 CephContext *cct = (CephContext *)group_ioctx.cct();
219 std::vector<C_SaferCond*> on_finishes;
220 int r, ret_code;
221
222 std::vector<librbd::ImageCtx*> ictxs;
223
224 cls::rbd::GroupSnapshotNamespace ne{group_ioctx.get_id(), group_id,
225 group_snap.id};
226
227 ldout(cct, 20) << "Removing snapshots" << dendl;
228 int snap_count = group_snap.snaps.size();
229
230 for (int i = 0; i < snap_count; ++i) {
231 librbd::IoCtx image_io_ctx;
232 r = util::create_ioctx(group_ioctx, "image", group_snap.snaps[i].pool, {},
233 &image_io_ctx);
234 if (r < 0) {
235 return r;
236 }
237
238 librbd::ImageCtx* image_ctx = new ImageCtx("", group_snap.snaps[i].image_id,
239 nullptr, image_io_ctx, false);
240
241 C_SaferCond* on_finish = new C_SaferCond;
242
243 image_ctx->state->open(0, on_finish);
244
245 ictxs.push_back(image_ctx);
246 on_finishes.push_back(on_finish);
247 }
248
249 ret_code = 0;
250 for (int i = 0; i < snap_count; ++i) {
251 r = on_finishes[i]->wait();
252 delete on_finishes[i];
253 if (r < 0) {
11fdf7f2
TL
254 ictxs[i] = nullptr;
255 ret_code = r;
256 }
257 }
258 if (ret_code != 0) {
259 goto finish;
260 }
261
262 ldout(cct, 20) << "Opened participating images. " <<
263 "Deleting snapshots themselves." << dendl;
264
265 for (int i = 0; i < snap_count; ++i) {
266 ImageCtx *ictx = ictxs[i];
267 on_finishes[i] = new C_SaferCond;
268
269 std::string snap_name;
9f95a23c 270 ictx->image_lock.lock_shared();
11fdf7f2
TL
271 snap_t snap_id = get_group_snap_id(ictx, ne);
272 r = ictx->get_snap_name(snap_id, &snap_name);
9f95a23c 273 ictx->image_lock.unlock_shared();
11fdf7f2
TL
274
275 if (r >= 0) {
276 ldout(cct, 20) << "removing individual snapshot from image " << ictx->name
277 << dendl;
278 ictx->operations->snap_remove(ne, snap_name, on_finishes[i]);
279 } else {
280 // We are ok to ignore missing image snapshots. The snapshot could have
281 // been inconsistent in the first place.
282 on_finishes[i]->complete(0);
283 }
284 }
285
286 for (int i = 0; i < snap_count; ++i) {
287 r = on_finishes[i]->wait();
288 delete on_finishes[i];
289 if (r < 0 && r != -ENOENT) {
290 // if previous attempts to remove this snapshot failed then the image's
291 // snapshot may not exist
292 lderr(cct) << "Failed deleting image snapshot. Ret code: " << r << dendl;
293 ret_code = r;
294 }
295 }
296
297 if (ret_code != 0) {
298 goto finish;
299 }
300
301 ldout(cct, 20) << "Removed images snapshots removing snapshot record."
302 << dendl;
303
304 r = cls_client::group_snap_remove(&group_ioctx, group_header_oid,
305 group_snap.id);
306 if (r < 0) {
307 ret_code = r;
308 goto finish;
309 }
310
311finish:
312 for (int i = 0; i < snap_count; ++i) {
313 if (ictxs[i] != nullptr) {
314 ictxs[i]->state->close();
315 }
316 }
317 return ret_code;
318}
319
320int group_snap_rollback_by_record(librados::IoCtx& group_ioctx,
321 const cls::rbd::GroupSnapshot& group_snap,
322 const std::string& group_id,
323 const std::string& group_header_oid,
324 ProgressContext& pctx) {
325 CephContext *cct = (CephContext *)group_ioctx.cct();
326 std::vector<C_SaferCond*> on_finishes;
327 int r, ret_code;
328
329 std::vector<librbd::ImageCtx*> ictxs;
330
331 cls::rbd::GroupSnapshotNamespace ne{group_ioctx.get_id(), group_id,
332 group_snap.id};
333
334 ldout(cct, 20) << "Rolling back snapshots" << dendl;
335 int snap_count = group_snap.snaps.size();
336
337 for (int i = 0; i < snap_count; ++i) {
338 librados::IoCtx image_io_ctx;
339 r = util::create_ioctx(group_ioctx, "image", group_snap.snaps[i].pool, {},
340 &image_io_ctx);
341 if (r < 0) {
342 return r;
343 }
344
345 librbd::ImageCtx* image_ctx = new ImageCtx("", group_snap.snaps[i].image_id,
346 nullptr, image_io_ctx, false);
347
348 C_SaferCond* on_finish = new C_SaferCond;
349
350 image_ctx->state->open(0, on_finish);
351
352 ictxs.push_back(image_ctx);
353 on_finishes.push_back(on_finish);
354 }
355
356 ret_code = 0;
357 for (int i = 0; i < snap_count; ++i) {
358 r = on_finishes[i]->wait();
359 delete on_finishes[i];
360 if (r < 0) {
11fdf7f2
TL
361 ictxs[i] = nullptr;
362 ret_code = r;
363 }
364 }
365 if (ret_code != 0) {
366 goto finish;
367 }
368
369 ldout(cct, 20) << "Requesting exclusive locks for images" << dendl;
370 for (auto ictx: ictxs) {
9f95a23c 371 std::shared_lock owner_lock{ictx->owner_lock};
11fdf7f2
TL
372 if (ictx->exclusive_lock != nullptr) {
373 ictx->exclusive_lock->block_requests(-EBUSY);
374 }
375 }
376 for (int i = 0; i < snap_count; ++i) {
377 ImageCtx *ictx = ictxs[i];
9f95a23c 378 std::shared_lock owner_lock{ictx->owner_lock};
11fdf7f2
TL
379
380 on_finishes[i] = new C_SaferCond;
381 if (ictx->exclusive_lock != nullptr) {
382 ictx->exclusive_lock->acquire_lock(on_finishes[i]);
383 }
384 }
385
386 ret_code = 0;
387 for (int i = 0; i < snap_count; ++i) {
388 r = 0;
389 ImageCtx *ictx = ictxs[i];
390 if (ictx->exclusive_lock != nullptr) {
391 r = on_finishes[i]->wait();
392 }
393 delete on_finishes[i];
394 if (r < 0) {
395 ret_code = r;
396 }
397 }
398 if (ret_code != 0) {
399 goto finish;
400 }
401
402 for (int i = 0; i < snap_count; ++i) {
403 ImageCtx *ictx = ictxs[i];
404 on_finishes[i] = new C_SaferCond;
405
9f95a23c 406 std::shared_lock owner_locker{ictx->owner_lock};
11fdf7f2 407 std::string snap_name;
9f95a23c 408 ictx->image_lock.lock_shared();
11fdf7f2
TL
409 snap_t snap_id = get_group_snap_id(ictx, ne);
410 r = ictx->get_snap_name(snap_id, &snap_name);
9f95a23c 411 ictx->image_lock.unlock_shared();
11fdf7f2
TL
412
413 if (r >= 0) {
414 ldout(cct, 20) << "rolling back to individual snapshot for image " << ictx->name
415 << dendl;
416 ictx->operations->execute_snap_rollback(ne, snap_name, pctx, on_finishes[i]);
417 } else {
418 on_finishes[i]->complete(r);
419 }
420 }
421
422 for (int i = 0; i < snap_count; ++i) {
423 r = on_finishes[i]->wait();
424 delete on_finishes[i];
425 if (r < 0 && r != -ENOENT) {
426 lderr(cct) << "Failed rolling back group to snapshot. Ret code: " << r << dendl;
427 ret_code = r;
428 }
429 }
430
431finish:
432 for (int i = 0; i < snap_count; ++i) {
433 if (ictxs[i] != nullptr) {
434 ictxs[i]->state->close();
435 }
436 }
437 return ret_code;
438}
439
f67539c2
TL
440template <typename I>
441void notify_unquiesce(std::vector<I*> &ictxs,
442 const std::vector<uint64_t> &requests) {
443 if (requests.empty()) {
444 return;
445 }
446
447 ceph_assert(requests.size() == ictxs.size());
448 int image_count = ictxs.size();
449 std::vector<C_SaferCond> on_finishes(image_count);
450
451 for (int i = 0; i < image_count; ++i) {
452 ImageCtx *ictx = ictxs[i];
453
454 ictx->image_watcher->notify_unquiesce(requests[i], &on_finishes[i]);
455 }
456
457 for (int i = 0; i < image_count; ++i) {
458 on_finishes[i].wait();
459 }
460}
461
462template <typename I>
463int notify_quiesce(std::vector<I*> &ictxs, ProgressContext &prog_ctx,
464 std::vector<uint64_t> *requests) {
465 int image_count = ictxs.size();
466 std::vector<C_SaferCond> on_finishes(image_count);
467
468 requests->resize(image_count);
469 for (int i = 0; i < image_count; ++i) {
470 auto ictx = ictxs[i];
471
472 ictx->image_watcher->notify_quiesce(&(*requests)[i], prog_ctx,
473 &on_finishes[i]);
474 }
475
476 int ret_code = 0;
477 for (int i = 0; i < image_count; ++i) {
478 int r = on_finishes[i].wait();
479 if (r < 0) {
480 ret_code = r;
481 }
482 }
483
484 if (ret_code != 0) {
485 notify_unquiesce(ictxs, *requests);
486 }
487
488 return ret_code;
489}
490
11fdf7f2
TL
491} // anonymous namespace
492
493template <typename I>
494int Group<I>::image_remove_by_id(librados::IoCtx& group_ioctx,
495 const char *group_name,
496 librados::IoCtx& image_ioctx,
497 const char *image_id)
498{
499 CephContext *cct = (CephContext *)group_ioctx.cct();
500 ldout(cct, 20) << "io_ctx=" << &group_ioctx
501 << " group name " << group_name << " image "
502 << &image_ioctx << " id " << image_id << dendl;
503
504 string group_id;
505
506 int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY, group_name,
507 &group_id);
508 if (r < 0) {
509 lderr(cct) << "error reading group id object: "
510 << cpp_strerror(r)
511 << dendl;
512 return r;
513 }
514
515 ldout(cct, 20) << "removing image from group name " << group_name
516 << " group id " << group_id << dendl;
517
518 return group_image_remove(group_ioctx, group_id, image_ioctx, string(image_id));
519}
520
521template <typename I>
522int Group<I>::create(librados::IoCtx& io_ctx, const char *group_name)
523{
524 CephContext *cct = (CephContext *)io_ctx.cct();
525
526 string id = generate_uuid(io_ctx);
527
528 ldout(cct, 2) << "adding group to directory..." << dendl;
529
530 int r = cls_client::group_dir_add(&io_ctx, RBD_GROUP_DIRECTORY, group_name,
531 id);
532 if (r < 0) {
533 lderr(cct) << "error adding group to directory: "
534 << cpp_strerror(r)
535 << dendl;
536 return r;
537 }
538 string header_oid = util::group_header_name(id);
539
540 r = io_ctx.create(header_oid, true);
541 if (r < 0) {
542 lderr(cct) << "error creating group header: " << cpp_strerror(r) << dendl;
543 goto err_remove_from_dir;
544 }
545
546 return 0;
547
548err_remove_from_dir:
549 int remove_r = cls_client::group_dir_remove(&io_ctx, RBD_GROUP_DIRECTORY,
550 group_name, id);
551 if (remove_r < 0) {
552 lderr(cct) << "error cleaning up group from rbd_directory "
553 << "object after creation failed: " << cpp_strerror(remove_r)
554 << dendl;
555 }
556
557 return r;
558}
559
560template <typename I>
561int Group<I>::remove(librados::IoCtx& io_ctx, const char *group_name)
562{
563 CephContext *cct((CephContext *)io_ctx.cct());
564 ldout(cct, 20) << "group_remove " << &io_ctx << " " << group_name << dendl;
565
566 std::string group_id;
567 int r = cls_client::dir_get_id(&io_ctx, RBD_GROUP_DIRECTORY,
568 std::string(group_name), &group_id);
569 if (r < 0 && r != -ENOENT) {
570 lderr(cct) << "error getting id of group" << dendl;
571 return r;
572 }
573 string group_header_oid = util::group_header_name(group_id);
574
575 std::vector<cls::rbd::GroupSnapshot> snaps;
576 r = group_snap_list(io_ctx, group_name, &snaps);
577 if (r < 0 && r != -ENOENT) {
578 lderr(cct) << "error listing group snapshots" << dendl;
579 return r;
580 }
581
582 for (auto &snap : snaps) {
583 r = group_snap_remove_by_record(io_ctx, snap, group_id, group_header_oid);
584 if (r < 0) {
585 return r;
586 }
587 }
588
589 std::vector<cls::rbd::GroupImageStatus> images;
590 r = group_image_list(io_ctx, group_name, &images);
591 if (r < 0 && r != -ENOENT) {
592 lderr(cct) << "error listing group images" << dendl;
593 return r;
594 }
595
596 for (auto image : images) {
597 IoCtx image_ioctx;
598 r = util::create_ioctx(io_ctx, "image", image.spec.pool_id, {},
599 &image_ioctx);
600 if (r < 0) {
601 return r;
602 }
603
604 r = group_image_remove(io_ctx, group_id, image_ioctx, image.spec.image_id);
605 if (r < 0 && r != -ENOENT) {
606 lderr(cct) << "error removing image from a group" << dendl;
607 return r;
608 }
609 }
610
611 string header_oid = util::group_header_name(group_id);
612
613 r = io_ctx.remove(header_oid);
614 if (r < 0 && r != -ENOENT) {
615 lderr(cct) << "error removing header: " << cpp_strerror(-r) << dendl;
616 return r;
617 }
618
619 r = cls_client::group_dir_remove(&io_ctx, RBD_GROUP_DIRECTORY,
620 group_name, group_id);
621 if (r < 0 && r != -ENOENT) {
622 lderr(cct) << "error removing group from directory" << dendl;
623 return r;
624 }
625
626 return 0;
627}
628
629template <typename I>
630int Group<I>::list(IoCtx& io_ctx, vector<string> *names)
631{
632 CephContext *cct = (CephContext *)io_ctx.cct();
633 ldout(cct, 20) << "io_ctx=" << &io_ctx << dendl;
634
635 int max_read = 1024;
636 string last_read = "";
637 int r;
638 do {
639 map<string, string> groups;
640 r = cls_client::group_dir_list(&io_ctx, RBD_GROUP_DIRECTORY, last_read,
641 max_read, &groups);
642 if (r == -ENOENT) {
643 return 0; // Ignore missing rbd group directory. It means we don't have any groups yet.
644 }
645 if (r < 0) {
646 if (r != -ENOENT) {
647 lderr(cct) << "error listing group in directory: "
648 << cpp_strerror(r) << dendl;
649 } else {
650 r = 0;
651 }
652 return r;
653 }
654 for (pair<string, string> group : groups) {
655 names->push_back(group.first);
656 }
657 if (!groups.empty()) {
658 last_read = groups.rbegin()->first;
659 }
660 r = groups.size();
661 } while (r == max_read);
662
663 return 0;
664}
665
666template <typename I>
667int Group<I>::image_add(librados::IoCtx& group_ioctx, const char *group_name,
668 librados::IoCtx& image_ioctx, const char *image_name)
669{
670 CephContext *cct = (CephContext *)group_ioctx.cct();
671 ldout(cct, 20) << "io_ctx=" << &group_ioctx
672 << " group name " << group_name << " image "
673 << &image_ioctx << " name " << image_name << dendl;
674
675 if (group_ioctx.get_namespace() != image_ioctx.get_namespace()) {
676 lderr(cct) << "group and image cannot be in different namespaces" << dendl;
677 return -EINVAL;
678 }
679
680 string group_id;
681
682 int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY, group_name,
683 &group_id);
684 if (r < 0) {
685 lderr(cct) << "error reading group id object: "
686 << cpp_strerror(r)
687 << dendl;
688 return r;
689 }
690 string group_header_oid = util::group_header_name(group_id);
691
692
693 ldout(cct, 20) << "adding image to group name " << group_name
694 << " group id " << group_header_oid << dendl;
695
696 string image_id;
697
698 r = cls_client::dir_get_id(&image_ioctx, RBD_DIRECTORY, image_name,
699 &image_id);
700 if (r < 0) {
701 lderr(cct) << "error reading image id object: "
702 << cpp_strerror(-r) << dendl;
703 return r;
704 }
705
706 string image_header_oid = util::header_name(image_id);
707
708 ldout(cct, 20) << "adding image " << image_name
709 << " image id " << image_header_oid << dendl;
710
711 cls::rbd::GroupImageStatus incomplete_st(
712 image_id, image_ioctx.get_id(),
713 cls::rbd::GROUP_IMAGE_LINK_STATE_INCOMPLETE);
714 cls::rbd::GroupImageStatus attached_st(
715 image_id, image_ioctx.get_id(), cls::rbd::GROUP_IMAGE_LINK_STATE_ATTACHED);
716
717 r = cls_client::group_image_set(&group_ioctx, group_header_oid,
718 incomplete_st);
719
720 cls::rbd::GroupSpec group_spec(group_id, group_ioctx.get_id());
721
722 if (r < 0) {
723 lderr(cct) << "error adding image reference to group: "
724 << cpp_strerror(-r) << dendl;
725 return r;
726 }
727
728 r = cls_client::image_group_add(&image_ioctx, image_header_oid, group_spec);
729 if (r < 0) {
730 lderr(cct) << "error adding group reference to image: "
731 << cpp_strerror(-r) << dendl;
732 cls::rbd::GroupImageSpec spec(image_id, image_ioctx.get_id());
733 cls_client::group_image_remove(&group_ioctx, group_header_oid, spec);
734 // Ignore errors in the clean up procedure.
735 return r;
736 }
737 ImageWatcher<>::notify_header_update(image_ioctx, image_header_oid);
738
739 r = cls_client::group_image_set(&group_ioctx, group_header_oid,
740 attached_st);
741
742 return r;
743}
744
745template <typename I>
746int Group<I>::image_remove(librados::IoCtx& group_ioctx, const char *group_name,
747 librados::IoCtx& image_ioctx, const char *image_name)
748{
749 CephContext *cct = (CephContext *)group_ioctx.cct();
750 ldout(cct, 20) << "io_ctx=" << &group_ioctx
751 << " group name " << group_name << " image "
752 << &image_ioctx << " name " << image_name << dendl;
753
754 if (group_ioctx.get_namespace() != image_ioctx.get_namespace()) {
755 lderr(cct) << "group and image cannot be in different namespaces" << dendl;
756 return -EINVAL;
757 }
758
759 string group_id;
760
761 int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY, group_name,
762 &group_id);
763 if (r < 0) {
764 lderr(cct) << "error reading group id object: "
765 << cpp_strerror(r)
766 << dendl;
767 return r;
768 }
769
770 ldout(cct, 20) << "removing image from group name " << group_name
771 << " group id " << group_id << dendl;
772
773 string image_id;
774 r = cls_client::dir_get_id(&image_ioctx, RBD_DIRECTORY, image_name,
775 &image_id);
776 if (r < 0) {
777 lderr(cct) << "error reading image id object: "
778 << cpp_strerror(-r) << dendl;
779 return r;
780 }
781
782 r = group_image_remove(group_ioctx, group_id, image_ioctx, image_id);
783
784 return r;
785}
786
787template <typename I>
788int Group<I>::image_list(librados::IoCtx& group_ioctx,
789 const char *group_name,
790 std::vector<group_image_info_t>* images)
791{
792 CephContext *cct = (CephContext *)group_ioctx.cct();
793 ldout(cct, 20) << "io_ctx=" << &group_ioctx
794 << " group name " << group_name << dendl;
795
796 std::vector<cls::rbd::GroupImageStatus> image_ids;
797
798 group_image_list(group_ioctx, group_name, &image_ids);
799
800 for (auto image_id : image_ids) {
801 IoCtx ioctx;
802 int r = util::create_ioctx(group_ioctx, "image", image_id.spec.pool_id, {},
803 &ioctx);
804 if (r < 0) {
805 return r;
806 }
807
808 std::string image_name;
809 r = cls_client::dir_get_name(&ioctx, RBD_DIRECTORY,
810 image_id.spec.image_id, &image_name);
811 if (r < 0) {
812 return r;
813 }
814
815 images->push_back(
816 group_image_info_t {
817 image_name,
818 ioctx.get_id(),
819 static_cast<group_image_state_t>(image_id.state)});
820 }
821
822 return 0;
823}
824
825template <typename I>
826int Group<I>::rename(librados::IoCtx& io_ctx, const char *src_name,
827 const char *dest_name)
828{
829 CephContext *cct((CephContext *)io_ctx.cct());
830 ldout(cct, 20) << "group_rename " << &io_ctx << " " << src_name
831 << " -> " << dest_name << dendl;
832
833 std::string group_id;
834 int r = cls_client::dir_get_id(&io_ctx, RBD_GROUP_DIRECTORY,
835 std::string(src_name), &group_id);
836 if (r < 0) {
837 if (r != -ENOENT)
838 lderr(cct) << "error getting id of group" << dendl;
839 return r;
840 }
841
842 r = cls_client::group_dir_rename(&io_ctx, RBD_GROUP_DIRECTORY,
843 src_name, dest_name, group_id);
844 if (r < 0 && r != -ENOENT) {
845 lderr(cct) << "error renaming group from directory" << dendl;
846 return r;
847 }
848
849 return 0;
850}
851
852
853template <typename I>
854int Group<I>::image_get_group(I *ictx, group_info_t *group_info)
855{
856 int r = ictx->state->refresh_if_required();
857 if (r < 0)
858 return r;
859
860 if (RBD_GROUP_INVALID_POOL != ictx->group_spec.pool_id) {
861 IoCtx ioctx;
862 r = util::create_ioctx(ictx->md_ctx, "group", ictx->group_spec.pool_id, {},
863 &ioctx);
864 if (r < 0) {
865 return r;
866 }
867
868 std::string group_name;
869 r = cls_client::dir_get_name(&ioctx, RBD_GROUP_DIRECTORY,
870 ictx->group_spec.group_id, &group_name);
871 if (r < 0)
872 return r;
873 group_info->pool = ioctx.get_id();
874 group_info->name = group_name;
875 } else {
876 group_info->pool = RBD_GROUP_INVALID_POOL;
877 group_info->name = "";
878 }
879
880 return 0;
881}
882
883template <typename I>
884int Group<I>::snap_create(librados::IoCtx& group_ioctx,
f67539c2
TL
885 const char *group_name, const char *snap_name,
886 uint32_t flags) {
11fdf7f2
TL
887 CephContext *cct = (CephContext *)group_ioctx.cct();
888
889 string group_id;
890 cls::rbd::GroupSnapshot group_snap;
891 vector<cls::rbd::ImageSnapshotSpec> image_snaps;
892 std::string ind_snap_name;
893
894 std::vector<librbd::ImageCtx*> ictxs;
895 std::vector<C_SaferCond*> on_finishes;
f67539c2
TL
896 std::vector<uint64_t> quiesce_requests;
897 NoOpProgressContext prog_ctx;
898 uint64_t internal_flags = 0;
11fdf7f2 899
f67539c2
TL
900 int r = util::snap_create_flags_api_to_internal(cct, flags, &internal_flags);
901 if (r < 0) {
902 return r;
903 }
904 internal_flags &= ~(SNAP_CREATE_FLAG_SKIP_NOTIFY_QUIESCE |
905 SNAP_CREATE_FLAG_IGNORE_NOTIFY_QUIESCE_ERROR);
906
907 r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY, group_name,
908 &group_id);
11fdf7f2
TL
909 if (r < 0) {
910 lderr(cct) << "error reading group id object: "
911 << cpp_strerror(r)
912 << dendl;
913 return r;
914 }
915
916 std::vector<cls::rbd::GroupImageStatus> images;
917 r = group_image_list(group_ioctx, group_name, &images);
918 if (r < 0) {
919 return r;
920 }
921 int image_count = images.size();
922
923 ldout(cct, 20) << "Found " << image_count << " images in group" << dendl;
924
925 image_snaps = vector<cls::rbd::ImageSnapshotSpec>(image_count,
926 cls::rbd::ImageSnapshotSpec());
927
928 for (int i = 0; i < image_count; ++i) {
929 image_snaps[i].pool = images[i].spec.pool_id;
930 image_snaps[i].image_id = images[i].spec.image_id;
931 }
932
933 string group_header_oid = util::group_header_name(group_id);
934
935 group_snap.id = generate_uuid(group_ioctx);
936 group_snap.name = string(snap_name);
937 group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE;
938 group_snap.snaps = image_snaps;
939
940 cls::rbd::GroupSnapshotNamespace ne{group_ioctx.get_id(), group_id,
941 group_snap.id};
942
943 r = cls_client::group_snap_set(&group_ioctx, group_header_oid, group_snap);
944 if (r == -EEXIST) {
945 lderr(cct) << "snapshot with this name already exists: "
946 << cpp_strerror(r)
947 << dendl;
948 }
949 int ret_code = 0;
950 if (r < 0) {
951 ret_code = r;
952 goto finish;
953 }
954
955 for (auto image: images) {
956 librbd::IoCtx image_io_ctx;
957 r = util::create_ioctx(group_ioctx, "image", image.spec.pool_id, {},
958 &image_io_ctx);
959 if (r < 0) {
960 ret_code = r;
961 goto finish;
962 }
963
964 ldout(cct, 20) << "Opening image with id " << image.spec.image_id << dendl;
965
966 librbd::ImageCtx* image_ctx = new ImageCtx("", image.spec.image_id.c_str(),
967 nullptr, image_io_ctx, false);
968
969 C_SaferCond* on_finish = new C_SaferCond;
970
971 image_ctx->state->open(0, on_finish);
972
973 ictxs.push_back(image_ctx);
974 on_finishes.push_back(on_finish);
975 }
976 ldout(cct, 20) << "Issued open request waiting for the completion" << dendl;
977 ret_code = 0;
978 for (int i = 0; i < image_count; ++i) {
979
980 ldout(cct, 20) << "Waiting for completion on on_finish: " <<
981 on_finishes[i] << dendl;
982
983 r = on_finishes[i]->wait();
984 delete on_finishes[i];
985 if (r < 0) {
11fdf7f2
TL
986 ictxs[i] = nullptr;
987 ret_code = r;
988 }
989 }
990 if (ret_code != 0) {
991 goto remove_record;
992 }
f67539c2
TL
993
994 if ((flags & RBD_SNAP_CREATE_SKIP_QUIESCE) == 0) {
995 ldout(cct, 20) << "Sending quiesce notification" << dendl;
996 ret_code = notify_quiesce(ictxs, prog_ctx, &quiesce_requests);
997 if (ret_code != 0 && (flags & RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR) == 0) {
998 goto remove_record;
999 }
1000 }
1001
11fdf7f2
TL
1002 ldout(cct, 20) << "Requesting exclusive locks for images" << dendl;
1003
1004 for (auto ictx: ictxs) {
9f95a23c 1005 std::shared_lock owner_lock{ictx->owner_lock};
11fdf7f2
TL
1006 if (ictx->exclusive_lock != nullptr) {
1007 ictx->exclusive_lock->block_requests(-EBUSY);
1008 }
1009 }
1010 for (int i = 0; i < image_count; ++i) {
1011 ImageCtx *ictx = ictxs[i];
9f95a23c 1012 std::shared_lock owner_lock{ictx->owner_lock};
11fdf7f2
TL
1013
1014 on_finishes[i] = new C_SaferCond;
1015 if (ictx->exclusive_lock != nullptr) {
1016 ictx->exclusive_lock->acquire_lock(on_finishes[i]);
1017 }
1018 }
1019
1020 ret_code = 0;
1021 for (int i = 0; i < image_count; ++i) {
1022 r = 0;
1023 ImageCtx *ictx = ictxs[i];
1024 if (ictx->exclusive_lock != nullptr) {
1025 r = on_finishes[i]->wait();
1026 }
1027 delete on_finishes[i];
1028 if (r < 0) {
1029 ret_code = r;
1030 }
1031 }
1032 if (ret_code != 0) {
f67539c2 1033 notify_unquiesce(ictxs, quiesce_requests);
11fdf7f2
TL
1034 goto remove_record;
1035 }
1036
1037 ind_snap_name = calc_ind_image_snap_name(group_ioctx.get_id(), group_id,
1038 group_snap.id);
1039
1040 for (int i = 0; i < image_count; ++i) {
1041 ImageCtx *ictx = ictxs[i];
1042
1043 C_SaferCond* on_finish = new C_SaferCond;
1044
f67539c2
TL
1045 std::shared_lock owner_locker{ictx->owner_lock};
1046 ictx->operations->execute_snap_create(
1047 ne, ind_snap_name.c_str(), on_finish, 0,
1048 SNAP_CREATE_FLAG_SKIP_NOTIFY_QUIESCE, prog_ctx);
11fdf7f2
TL
1049
1050 on_finishes[i] = on_finish;
1051 }
1052
1053 ret_code = 0;
1054 for (int i = 0; i < image_count; ++i) {
1055 r = on_finishes[i]->wait();
1056 delete on_finishes[i];
1057 if (r < 0) {
1058 ret_code = r;
1059 } else {
1060 ImageCtx *ictx = ictxs[i];
9f95a23c 1061 ictx->image_lock.lock_shared();
11fdf7f2 1062 snap_t snap_id = get_group_snap_id(ictx, ne);
9f95a23c 1063 ictx->image_lock.unlock_shared();
11fdf7f2
TL
1064 if (snap_id == CEPH_NOSNAP) {
1065 ldout(cct, 20) << "Couldn't find created snapshot with namespace: "
1066 << ne << dendl;
1067 ret_code = -ENOENT;
1068 } else {
1069 image_snaps[i].snap_id = snapid_t(snap_id);
1070 image_snaps[i].pool = ictx->md_ctx.get_id();
1071 image_snaps[i].image_id = ictx->id;
1072 }
1073 }
1074 }
1075 if (ret_code != 0) {
1076 goto remove_image_snaps;
1077 }
1078
1079 group_snap.snaps = image_snaps;
1080 group_snap.state = cls::rbd::GROUP_SNAPSHOT_STATE_COMPLETE;
1081
1082 r = cls_client::group_snap_set(&group_ioctx, group_header_oid, group_snap);
1083 if (r < 0) {
1084 ret_code = r;
1085 goto remove_image_snaps;
1086 }
1087
f67539c2
TL
1088 ldout(cct, 20) << "Sending unquiesce notification" << dendl;
1089 notify_unquiesce(ictxs, quiesce_requests);
1090
11fdf7f2
TL
1091 goto finish;
1092
1093remove_image_snaps:
f67539c2 1094 notify_unquiesce(ictxs, quiesce_requests);
11fdf7f2
TL
1095
1096 for (int i = 0; i < image_count; ++i) {
1097 ImageCtx *ictx = ictxs[i];
1098 ldout(cct, 20) << "Removing individual snapshot with name: " <<
1099 ind_snap_name << dendl;
1100
1101 on_finishes[i] = new C_SaferCond;
1102 std::string snap_name;
9f95a23c 1103 ictx->image_lock.lock_shared();
11fdf7f2
TL
1104 snap_t snap_id = get_group_snap_id(ictx, ne);
1105 r = ictx->get_snap_name(snap_id, &snap_name);
9f95a23c 1106 ictx->image_lock.unlock_shared();
11fdf7f2
TL
1107 if (r >= 0) {
1108 ictx->operations->snap_remove(ne, snap_name.c_str(), on_finishes[i]);
1109 } else {
1110 // Ignore missing image snapshots. The whole snapshot could have been
1111 // inconsistent.
1112 on_finishes[i]->complete(0);
1113 }
1114 }
1115
1116 for (int i = 0, n = on_finishes.size(); i < n; ++i) {
1117 r = on_finishes[i]->wait();
1118 delete on_finishes[i];
1119 if (r < 0 && r != -ENOENT) { // if previous attempts to remove this snapshot failed then the image's snapshot may not exist
1120 lderr(cct) << "Failed cleaning up image snapshot. Ret code: " << r << dendl;
1121 // just report error, but don't abort the process
1122 }
1123 }
1124
1125remove_record:
1126 r = cls_client::group_snap_remove(&group_ioctx, group_header_oid,
1127 group_snap.id);
1128 if (r < 0) {
1129 lderr(cct) << "error while cleaning up group snapshot" << dendl;
1130 // we ignore return value in clean up
1131 }
1132
1133finish:
1134 for (int i = 0, n = ictxs.size(); i < n; ++i) {
1135 if (ictxs[i] != nullptr) {
1136 ictxs[i]->state->close();
1137 }
1138 }
1139 return ret_code;
1140}
1141
1142template <typename I>
1143int Group<I>::snap_remove(librados::IoCtx& group_ioctx, const char *group_name,
1144 const char *snap_name)
1145{
1146 CephContext *cct = (CephContext *)group_ioctx.cct();
1147
1148 string group_id;
1149 int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
1150 group_name, &group_id);
1151 if (r < 0) {
1152 lderr(cct) << "error reading group id object: "
1153 << cpp_strerror(r)
1154 << dendl;
1155 return r;
1156 }
1157
1158 std::vector<cls::rbd::GroupSnapshot> snaps;
1159 r = group_snap_list(group_ioctx, group_name, &snaps);
1160 if (r < 0) {
1161 return r;
1162 }
1163
1164 cls::rbd::GroupSnapshot *group_snap = nullptr;
1165 for (auto &snap : snaps) {
1166 if (snap.name == string(snap_name)) {
1167 group_snap = &snap;
1168 break;
1169 }
1170 }
1171 if (group_snap == nullptr) {
1172 return -ENOENT;
1173 }
1174
1175 string group_header_oid = util::group_header_name(group_id);
1176 r = group_snap_remove_by_record(group_ioctx, *group_snap, group_id,
1177 group_header_oid);
1178 return r;
1179}
1180
1181template <typename I>
1182int Group<I>::snap_rename(librados::IoCtx& group_ioctx, const char *group_name,
1183 const char *old_snap_name,
1184 const char *new_snap_name) {
1185 CephContext *cct = (CephContext *)group_ioctx.cct();
1186 if (0 == strcmp(old_snap_name, new_snap_name))
1187 return -EEXIST;
1188
1189 std::string group_id;
1190 int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
1191 group_name, &group_id);
1192 if (r == -ENOENT) {
1193 return r;
1194 } else if (r < 0) {
1195 lderr(cct) << "error reading group id object: " << cpp_strerror(r) << dendl;
1196 return r;
1197 }
1198
1199 std::vector<cls::rbd::GroupSnapshot> group_snaps;
1200 r = group_snap_list(group_ioctx, group_name, &group_snaps);
1201 if (r < 0) {
1202 return r;
1203 }
1204
1205 cls::rbd::GroupSnapshot group_snap;
1206 for (auto &snap : group_snaps) {
1207 if (snap.name == old_snap_name) {
1208 group_snap = snap;
1209 break;
1210 }
1211 }
1212
1213 if (group_snap.id.empty()) {
1214 return -ENOENT;
1215 }
1216
1217 std::string group_header_oid = util::group_header_name(group_id);
1218 group_snap.name = new_snap_name;
1219 r = cls_client::group_snap_set(&group_ioctx, group_header_oid, group_snap);
1220 if (r < 0) {
1221 return r;
1222 }
1223
1224 return 0;
1225}
1226
1227template <typename I>
1228int Group<I>::snap_list(librados::IoCtx& group_ioctx, const char *group_name,
1229 std::vector<group_snap_info_t> *snaps)
1230{
1231 std::vector<cls::rbd::GroupSnapshot> cls_snaps;
1232
1233 int r = group_snap_list(group_ioctx, group_name, &cls_snaps);
1234 if (r < 0) {
1235 return r;
1236 }
1237
1238 for (auto snap : cls_snaps) {
1239 snaps->push_back(
1240 group_snap_info_t {
1241 snap.name,
1242 static_cast<group_snap_state_t>(snap.state)});
1243
1244 }
1245 return 0;
1246}
1247
1248template <typename I>
1249int Group<I>::snap_rollback(librados::IoCtx& group_ioctx,
1250 const char *group_name, const char *snap_name,
1251 ProgressContext& pctx)
1252{
1253 CephContext *cct = (CephContext *)group_ioctx.cct();
1254
1255 string group_id;
1256 int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
1257 group_name, &group_id);
1258 if (r < 0) {
1259 lderr(cct) << "error reading group id object: "
1260 << cpp_strerror(r) << dendl;
1261 return r;
1262 }
1263
1264 std::vector<cls::rbd::GroupSnapshot> snaps;
1265 r = group_snap_list(group_ioctx, group_name, &snaps);
1266 if (r < 0) {
1267 return r;
1268 }
1269
1270 cls::rbd::GroupSnapshot *group_snap = nullptr;
1271 for (auto &snap : snaps) {
1272 if (snap.name == string(snap_name)) {
1273 group_snap = &snap;
1274 break;
1275 }
1276 }
1277 if (group_snap == nullptr) {
1278 return -ENOENT;
1279 }
1280
1281 string group_header_oid = util::group_header_name(group_id);
1282 r = group_snap_rollback_by_record(group_ioctx, *group_snap, group_id,
1283 group_header_oid, pctx);
1284 return r;
1285}
1286
1287} // namespace api
1288} // namespace librbd
1289
1290template class librbd::api::Group<librbd::ImageCtx>;