]> git.proxmox.com Git - ceph.git/blame - ceph/src/librbd/api/Mirror.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / librbd / api / Mirror.cc
CommitLineData
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 "librbd/api/Mirror.h"
5#include "include/rados/librados.hpp"
11fdf7f2
TL
6#include "include/stringify.h"
7#include "common/ceph_json.h"
7c673cae
FG
8#include "common/dout.h"
9#include "common/errno.h"
10#include "cls/rbd/cls_rbd_client.h"
f67539c2 11#include "librbd/AsioEngine.h"
7c673cae
FG
12#include "librbd/ImageCtx.h"
13#include "librbd/ImageState.h"
14#include "librbd/Journal.h"
9f95a23c
TL
15#include "librbd/MirroringWatcher.h"
16#include "librbd/Operations.h"
7c673cae
FG
17#include "librbd/Utils.h"
18#include "librbd/api/Image.h"
9f95a23c 19#include "librbd/api/Namespace.h"
7c673cae
FG
20#include "librbd/mirror/DemoteRequest.h"
21#include "librbd/mirror/DisableRequest.h"
22#include "librbd/mirror/EnableRequest.h"
23#include "librbd/mirror/GetInfoRequest.h"
24#include "librbd/mirror/GetStatusRequest.h"
9f95a23c 25#include "librbd/mirror/GetUuidRequest.h"
7c673cae
FG
26#include "librbd/mirror/PromoteRequest.h"
27#include "librbd/mirror/Types.h"
28#include "librbd/MirroringWatcher.h"
9f95a23c
TL
29#include "librbd/mirror/snapshot/CreatePrimaryRequest.h"
30#include "librbd/mirror/snapshot/ImageMeta.h"
31#include "librbd/mirror/snapshot/UnlinkPeerRequest.h"
32#include "librbd/mirror/snapshot/Utils.h"
eafe8130
TL
33#include <boost/algorithm/string/trim.hpp>
34#include <boost/algorithm/string/replace.hpp>
7c673cae 35#include <boost/scope_exit.hpp>
9f95a23c
TL
36#include "json_spirit/json_spirit.h"
37
38#include <algorithm>
7c673cae
FG
39
40#define dout_subsys ceph_subsys_rbd
41#undef dout_prefix
42#define dout_prefix *_dout << "librbd::api::Mirror: " << __func__ << ": "
43
44namespace librbd {
45namespace api {
46
47namespace {
48
eafe8130
TL
49int get_config_key(librados::Rados& rados, const std::string& key,
50 std::string* value) {
51 std::string cmd =
52 "{"
53 "\"prefix\": \"config-key get\", "
54 "\"key\": \"" + key + "\""
55 "}";
56
57 bufferlist in_bl;
58 bufferlist out_bl;
59
60 int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr);
61 if (r == -EINVAL) {
62 return -EOPNOTSUPP;
63 } else if (r < 0 && r != -ENOENT) {
64 return r;
65 }
66
67 *value = out_bl.to_str();
68 return 0;
69}
70
71int set_config_key(librados::Rados& rados, const std::string& key,
72 const std::string& value) {
73 std::string cmd;
74 if (value.empty()) {
75 cmd = "{"
76 "\"prefix\": \"config-key rm\", "
77 "\"key\": \"" + key + "\""
78 "}";
79 } else {
80 cmd = "{"
81 "\"prefix\": \"config-key set\", "
82 "\"key\": \"" + key + "\", "
83 "\"val\": \"" + value + "\""
84 "}";
85 }
86 bufferlist in_bl;
87 bufferlist out_bl;
88
89 int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr);
90 if (r == -EINVAL) {
91 return -EOPNOTSUPP;
92 } else if (r < 0) {
93 return r;
94 }
95
96 return 0;
97}
98
11fdf7f2
TL
99std::string get_peer_config_key_name(int64_t pool_id,
100 const std::string& peer_uuid) {
101 return RBD_MIRROR_PEER_CONFIG_KEY_PREFIX + stringify(pool_id) + "/" +
102 peer_uuid;
103}
104
105int remove_peer_config_key(librados::IoCtx& io_ctx,
106 const std::string& peer_uuid) {
107 int64_t pool_id = io_ctx.get_id();
eafe8130
TL
108 auto key = get_peer_config_key_name(pool_id, peer_uuid);
109
110 librados::Rados rados(io_ctx);
111 int r = set_config_key(rados, key, "");
112 if (r < 0 && r != -ENOENT && r != -EPERM) {
113 return r;
114 }
115 return 0;
116}
117
118int create_bootstrap_user(CephContext* cct, librados::Rados& rados,
119 std::string* peer_client_id, std::string* cephx_key) {
120 ldout(cct, 20) << dendl;
121
122 // retrieve peer CephX user from config-key
123 int r = get_config_key(rados, RBD_MIRROR_PEER_CLIENT_ID_CONFIG_KEY,
124 peer_client_id);
125 if (r == -EACCES) {
126 ldout(cct, 5) << "insufficient permissions to get peer-client-id "
127 << "config-key" << dendl;
128 return r;
129 } else if (r < 0 && r != -ENOENT) {
130 lderr(cct) << "failed to retrieve peer client id key: "
131 << cpp_strerror(r) << dendl;
132 return r;
133 } else if (r == -ENOENT || peer_client_id->empty()) {
134 ldout(cct, 20) << "creating new peer-client-id config-key" << dendl;
135
136 *peer_client_id = "rbd-mirror-peer";
137 r = set_config_key(rados, RBD_MIRROR_PEER_CLIENT_ID_CONFIG_KEY,
138 *peer_client_id);
139 if (r == -EACCES) {
140 ldout(cct, 5) << "insufficient permissions to update peer-client-id "
141 << "config-key" << dendl;
142 return r;
143 } else if (r < 0) {
144 lderr(cct) << "failed to update peer client id key: "
145 << cpp_strerror(r) << dendl;
146 return r;
147 }
148 }
149 ldout(cct, 20) << "peer_client_id=" << *peer_client_id << dendl;
150
151 // create peer client user
11fdf7f2 152 std::string cmd =
eafe8130
TL
153 R"({)" \
154 R"( "prefix": "auth get-or-create",)" \
155 R"( "entity": "client.)" + *peer_client_id + R"(",)" \
156 R"( "caps": [)" \
157 R"( "mon", "profile rbd-mirror-peer",)" \
158 R"( "osd", "profile rbd"],)" \
159 R"( "format": "json")" \
160 R"(})";
11fdf7f2
TL
161
162 bufferlist in_bl;
163 bufferlist out_bl;
eafe8130
TL
164
165 r = rados.mon_command(cmd, in_bl, &out_bl, nullptr);
166 if (r == -EINVAL) {
167 ldout(cct, 5) << "caps mismatch for existing user" << dendl;
168 return -EEXIST;
169 } else if (r == -EACCES) {
170 ldout(cct, 5) << "insufficient permissions to create user" << dendl;
171 return r;
172 } else if (r < 0) {
173 lderr(cct) << "failed to create or update RBD mirroring bootstrap user: "
174 << cpp_strerror(r) << dendl;
11fdf7f2
TL
175 return r;
176 }
eafe8130
TL
177
178 // extract key from response
179 bool json_valid = false;
180 json_spirit::mValue json_root;
181 if(json_spirit::read(out_bl.to_str(), json_root)) {
182 try {
183 auto& json_obj = json_root.get_array()[0].get_obj();
184 *cephx_key = json_obj["key"].get_str();
185 json_valid = true;
186 } catch (std::runtime_error&) {
187 }
188 }
189
190 if (!json_valid) {
191 lderr(cct) << "invalid auth keyring JSON received" << dendl;
192 return -EBADMSG;
193 }
194
195 return 0;
196}
197
198int create_bootstrap_peer(CephContext* cct, librados::IoCtx& io_ctx,
9f95a23c 199 mirror_peer_direction_t direction,
eafe8130
TL
200 const std::string& site_name, const std::string& fsid,
201 const std::string& client_id, const std::string& key,
202 const std::string& mon_host,
203 const std::string& cluster1,
204 const std::string& cluster2) {
205 ldout(cct, 20) << dendl;
206
207 std::string peer_uuid;
9f95a23c
TL
208 std::vector<mirror_peer_site_t> peers;
209 int r = Mirror<>::peer_site_list(io_ctx, &peers);
eafe8130
TL
210 if (r < 0 && r != -ENOENT) {
211 lderr(cct) << "failed to list mirror peers: " << cpp_strerror(r) << dendl;
212 return r;
213 }
214
215 if (peers.empty()) {
9f95a23c
TL
216 r = Mirror<>::peer_site_add(io_ctx, &peer_uuid, direction, site_name,
217 "client." + client_id);
eafe8130
TL
218 if (r < 0) {
219 lderr(cct) << "failed to add " << cluster1 << " peer to "
220 << cluster2 << " " << "cluster: " << cpp_strerror(r) << dendl;
221 return r;
222 }
9f95a23c
TL
223 } else if (peers[0].site_name != site_name &&
224 peers[0].site_name != fsid) {
eafe8130
TL
225 // only support a single peer
226 lderr(cct) << "multiple peers are not currently supported" << dendl;
227 return -EINVAL;
228 } else {
229 peer_uuid = peers[0].uuid;
230
9f95a23c
TL
231 if (peers[0].site_name != site_name) {
232 r = Mirror<>::peer_site_set_name(io_ctx, peer_uuid, site_name);
eafe8130
TL
233 if (r < 0) {
234 // non-fatal attempt to update site name
235 lderr(cct) << "failed to update peer site name" << dendl;
236 }
237 }
238 }
239
240 Mirror<>::Attributes attributes {
241 {"mon_host", mon_host},
242 {"key", key}};
9f95a23c 243 r = Mirror<>::peer_site_set_attributes(io_ctx, peer_uuid, attributes);
eafe8130
TL
244 if (r < 0) {
245 lderr(cct) << "failed to update " << cluster1 << " cluster connection "
246 << "attributes in " << cluster2 << " cluster: "
247 << cpp_strerror(r) << dendl;
248 return r;
249 }
250
11fdf7f2
TL
251 return 0;
252}
253
7c673cae
FG
254int list_mirror_images(librados::IoCtx& io_ctx,
255 std::set<std::string>& mirror_image_ids) {
256 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
257
258 std::string last_read = "";
259 int max_read = 1024;
260 int r;
261 do {
262 std::map<std::string, std::string> mirror_images;
263 r = cls_client::mirror_image_list(&io_ctx, last_read, max_read,
264 &mirror_images);
3efd9988 265 if (r < 0 && r != -ENOENT) {
7c673cae
FG
266 lderr(cct) << "error listing mirrored image directory: "
267 << cpp_strerror(r) << dendl;
268 return r;
269 }
270 for (auto it = mirror_images.begin(); it != mirror_images.end(); ++it) {
271 mirror_image_ids.insert(it->first);
272 }
273 if (!mirror_images.empty()) {
274 last_read = mirror_images.rbegin()->first;
275 }
276 r = mirror_images.size();
277 } while (r == max_read);
278
279 return 0;
280}
281
282struct C_ImageGetInfo : public Context {
283 mirror_image_info_t *mirror_image_info;
9f95a23c 284 mirror_image_mode_t *mirror_image_mode;
7c673cae
FG
285 Context *on_finish;
286
287 cls::rbd::MirrorImage mirror_image;
11fdf7f2 288 mirror::PromotionState promotion_state = mirror::PROMOTION_STATE_PRIMARY;
9f95a23c 289 std::string primary_mirror_uuid;
7c673cae 290
9f95a23c
TL
291 C_ImageGetInfo(mirror_image_info_t *mirror_image_info,
292 mirror_image_mode_t *mirror_image_mode, Context *on_finish)
293 : mirror_image_info(mirror_image_info),
294 mirror_image_mode(mirror_image_mode), on_finish(on_finish) {
7c673cae
FG
295 }
296
297 void finish(int r) override {
9f95a23c 298 if (r < 0 && r != -ENOENT) {
7c673cae
FG
299 on_finish->complete(r);
300 return;
301 }
302
9f95a23c
TL
303 if (mirror_image_info != nullptr) {
304 mirror_image_info->global_id = mirror_image.global_image_id;
305 mirror_image_info->state = static_cast<rbd_mirror_image_state_t>(
306 mirror_image.state);
307 mirror_image_info->primary = (
308 promotion_state == mirror::PROMOTION_STATE_PRIMARY);
309 }
310
311 if (mirror_image_mode != nullptr) {
312 *mirror_image_mode =
313 static_cast<rbd_mirror_image_mode_t>(mirror_image.mode);
314 }
315
7c673cae
FG
316 on_finish->complete(0);
317 }
318};
319
9f95a23c 320struct C_ImageGetGlobalStatus : public C_ImageGetInfo {
7c673cae 321 std::string image_name;
9f95a23c 322 mirror_image_global_status_t *mirror_image_global_status;
7c673cae
FG
323
324 cls::rbd::MirrorImageStatus mirror_image_status_internal;
325
9f95a23c
TL
326 C_ImageGetGlobalStatus(
327 const std::string &image_name,
328 mirror_image_global_status_t *mirror_image_global_status,
329 Context *on_finish)
330 : C_ImageGetInfo(&mirror_image_global_status->info, nullptr, on_finish),
331 image_name(image_name),
332 mirror_image_global_status(mirror_image_global_status) {
7c673cae
FG
333 }
334
335 void finish(int r) override {
9f95a23c 336 if (r < 0 && r != -ENOENT) {
7c673cae
FG
337 on_finish->complete(r);
338 return;
339 }
340
9f95a23c
TL
341 mirror_image_global_status->name = image_name;
342 mirror_image_global_status->site_statuses.clear();
343 mirror_image_global_status->site_statuses.reserve(
344 mirror_image_status_internal.mirror_image_site_statuses.size());
345 for (auto& site_status :
346 mirror_image_status_internal.mirror_image_site_statuses) {
347 mirror_image_global_status->site_statuses.push_back({
348 site_status.mirror_uuid,
349 static_cast<mirror_image_status_state_t>(site_status.state),
350 site_status.description, site_status.last_update.sec(),
351 site_status.up});
352 }
7c673cae
FG
353 C_ImageGetInfo::finish(0);
354 }
355};
356
cd265ab1
TL
357template <typename I>
358struct C_ImageSnapshotCreate : public Context {
359 I *ictx;
f67539c2 360 uint64_t snap_create_flags;
cd265ab1
TL
361 uint64_t *snap_id;
362 Context *on_finish;
363
364 cls::rbd::MirrorImage mirror_image;
365 mirror::PromotionState promotion_state;
366 std::string primary_mirror_uuid;
367
f67539c2
TL
368 C_ImageSnapshotCreate(I *ictx, uint64_t snap_create_flags, uint64_t *snap_id,
369 Context *on_finish)
370 : ictx(ictx), snap_create_flags(snap_create_flags), snap_id(snap_id),
371 on_finish(on_finish) {
cd265ab1
TL
372 }
373
374 void finish(int r) override {
375 if (r < 0 && r != -ENOENT) {
376 on_finish->complete(r);
377 return;
378 }
379
380 if (mirror_image.mode != cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT ||
381 mirror_image.state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
382 lderr(ictx->cct) << "snapshot based mirroring is not enabled" << dendl;
383 on_finish->complete(-EINVAL);
384 return;
385 }
386
387 auto req = mirror::snapshot::CreatePrimaryRequest<I>::create(
f67539c2
TL
388 ictx, mirror_image.global_image_id, CEPH_NOSNAP, snap_create_flags, 0U,
389 snap_id, on_finish);
cd265ab1
TL
390 req->send();
391 }
392};
393
7c673cae
FG
394} // anonymous namespace
395
396template <typename I>
9f95a23c
TL
397int Mirror<I>::image_enable(I *ictx, mirror_image_mode_t mode,
398 bool relax_same_pool_parent_check) {
7c673cae 399 CephContext *cct = ictx->cct;
9f95a23c
TL
400 ldout(cct, 20) << "ictx=" << ictx << " mode=" << mode
401 << " relax_same_pool_parent_check="
402 << relax_same_pool_parent_check << dendl;
11fdf7f2 403
7c673cae
FG
404 int r = ictx->state->refresh_if_required();
405 if (r < 0) {
406 return r;
407 }
408
409 cls::rbd::MirrorMode mirror_mode;
410 r = cls_client::mirror_mode_get(&ictx->md_ctx, &mirror_mode);
411 if (r < 0) {
412 lderr(cct) << "cannot enable mirroring: failed to retrieve mirror mode: "
413 << cpp_strerror(r) << dendl;
414 return r;
415 }
416
417 if (mirror_mode != cls::rbd::MIRROR_MODE_IMAGE) {
418 lderr(cct) << "cannot enable mirroring in the current pool mirroring mode"
419 << dendl;
420 return -EINVAL;
421 }
422
423 // is mirroring not enabled for the parent?
424 {
9f95a23c 425 std::shared_lock image_locker{ictx->image_lock};
7c673cae
FG
426 ImageCtx *parent = ictx->parent;
427 if (parent) {
9f95a23c
TL
428 if (parent->md_ctx.get_id() != ictx->md_ctx.get_id() ||
429 !relax_same_pool_parent_check) {
7c673cae
FG
430 cls::rbd::MirrorImage mirror_image_internal;
431 r = cls_client::mirror_image_get(&(parent->md_ctx), parent->id,
432 &mirror_image_internal);
433 if (r == -ENOENT) {
434 lderr(cct) << "mirroring is not enabled for the parent" << dendl;
435 return -EINVAL;
436 }
437 }
438 }
439 }
440
9f95a23c
TL
441 if (mode == RBD_MIRROR_IMAGE_MODE_JOURNAL &&
442 !ictx->test_features(RBD_FEATURE_JOURNALING)) {
443 uint64_t features = RBD_FEATURE_JOURNALING;
444 if (!ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
445 features |= RBD_FEATURE_EXCLUSIVE_LOCK;
446 }
447 r = ictx->operations->update_features(features, true);
448 if (r < 0) {
449 lderr(cct) << "cannot enable journaling: " << cpp_strerror(r) << dendl;
450 return r;
451 }
7c673cae
FG
452 }
453
454 C_SaferCond ctx;
9f95a23c 455 auto req = mirror::EnableRequest<ImageCtx>::create(
1911f103 456 ictx, static_cast<cls::rbd::MirrorImageMode>(mode), "", false, &ctx);
7c673cae
FG
457 req->send();
458
459 r = ctx.wait();
460 if (r < 0) {
461 lderr(cct) << "cannot enable mirroring: " << cpp_strerror(r) << dendl;
462 return r;
463 }
464
465 return 0;
466}
467
468template <typename I>
469int Mirror<I>::image_disable(I *ictx, bool force) {
470 CephContext *cct = ictx->cct;
471 ldout(cct, 20) << "ictx=" << ictx << dendl;
472
473 int r = ictx->state->refresh_if_required();
474 if (r < 0) {
475 return r;
476 }
477
478 cls::rbd::MirrorMode mirror_mode;
479 r = cls_client::mirror_mode_get(&ictx->md_ctx, &mirror_mode);
480 if (r < 0) {
481 lderr(cct) << "cannot disable mirroring: failed to retrieve pool "
482 "mirroring mode: " << cpp_strerror(r) << dendl;
483 return r;
484 }
485
486 if (mirror_mode != cls::rbd::MIRROR_MODE_IMAGE) {
487 lderr(cct) << "cannot disable mirroring in the current pool mirroring "
488 "mode" << dendl;
489 return -EINVAL;
490 }
491
9f95a23c 492 // is mirroring enabled for the image?
7c673cae
FG
493 cls::rbd::MirrorImage mirror_image_internal;
494 r = cls_client::mirror_image_get(&ictx->md_ctx, ictx->id,
495 &mirror_image_internal);
496 if (r == -ENOENT) {
497 // mirroring is not enabled for this image
498 ldout(cct, 20) << "ignoring disable command: mirroring is not enabled for "
499 << "this image" << dendl;
500 return 0;
501 } else if (r == -EOPNOTSUPP) {
502 ldout(cct, 5) << "mirroring not supported by OSD" << dendl;
503 return r;
504 } else if (r < 0) {
505 lderr(cct) << "failed to retrieve mirror image metadata: "
506 << cpp_strerror(r) << dendl;
507 return r;
508 }
509
510 mirror_image_internal.state = cls::rbd::MIRROR_IMAGE_STATE_DISABLING;
511 r = cls_client::mirror_image_set(&ictx->md_ctx, ictx->id,
512 mirror_image_internal);
513 if (r < 0) {
514 lderr(cct) << "cannot disable mirroring: " << cpp_strerror(r) << dendl;
515 return r;
9f95a23c
TL
516 }
517
518 bool rollback = false;
519 BOOST_SCOPE_EXIT_ALL(ictx, &mirror_image_internal, &rollback) {
520 if (rollback) {
521 // restore the mask bit for treating the non-primary feature as read-only
522 ictx->image_lock.lock();
523 ictx->read_only_mask |= IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
524 ictx->image_lock.unlock();
525
526 ictx->state->handle_update_notification();
527
528 // attempt to restore the image state
529 CephContext *cct = ictx->cct;
530 mirror_image_internal.state = cls::rbd::MIRROR_IMAGE_STATE_ENABLED;
531 int r = cls_client::mirror_image_set(&ictx->md_ctx, ictx->id,
532 mirror_image_internal);
533 if (r < 0) {
534 lderr(cct) << "failed to re-enable image mirroring: "
535 << cpp_strerror(r) << dendl;
7c673cae 536 }
9f95a23c
TL
537 }
538 };
539
540 std::unique_lock image_locker{ictx->image_lock};
541 map<librados::snap_t, SnapInfo> snap_info = ictx->snap_info;
542 for (auto &info : snap_info) {
543 cls::rbd::ParentImageSpec parent_spec{ictx->md_ctx.get_id(),
544 ictx->md_ctx.get_namespace(),
545 ictx->id, info.first};
546 std::vector<librbd::linked_image_spec_t> child_images;
547 r = Image<I>::list_children(ictx, parent_spec, &child_images);
548 if (r < 0) {
549 rollback = true;
550 return r;
551 }
552
553 if (child_images.empty()) {
554 continue;
555 }
556
557 librados::IoCtx child_io_ctx;
558 int64_t child_pool_id = -1;
559 for (auto &child_image : child_images){
560 std::string pool = child_image.pool_name;
561 if (child_pool_id == -1 ||
562 child_pool_id != child_image.pool_id ||
563 child_io_ctx.get_namespace() != child_image.pool_namespace) {
564 r = util::create_ioctx(ictx->md_ctx, "child image",
565 child_image.pool_id,
566 child_image.pool_namespace,
567 &child_io_ctx);
7c673cae
FG
568 if (r < 0) {
569 rollback = true;
570 return r;
571 }
11fdf7f2 572
9f95a23c
TL
573 child_pool_id = child_image.pool_id;
574 }
11fdf7f2 575
9f95a23c
TL
576 cls::rbd::MirrorImage child_mirror_image_internal;
577 r = cls_client::mirror_image_get(&child_io_ctx, child_image.image_id,
578 &child_mirror_image_internal);
579 if (r != -ENOENT) {
580 rollback = true;
581 lderr(cct) << "mirroring is enabled on one or more children "
582 << dendl;
583 return -EBUSY;
7c673cae
FG
584 }
585 }
9f95a23c
TL
586 }
587 image_locker.unlock();
588
589 if (mirror_image_internal.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
590 // don't let the non-primary feature bit prevent image updates
591 ictx->image_lock.lock();
592 ictx->read_only_mask &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
593 ictx->image_lock.unlock();
7c673cae 594
9f95a23c
TL
595 r = ictx->state->refresh();
596 if (r < 0) {
597 rollback = true;
598 return r;
599 }
7c673cae 600
9f95a23c
TL
601 // remove any snapshot-based mirroring image-meta from image
602 std::string mirror_uuid;
603 r = uuid_get(ictx->md_ctx, &mirror_uuid);
7c673cae 604 if (r < 0) {
7c673cae
FG
605 rollback = true;
606 return r;
607 }
9f95a23c
TL
608
609 r = ictx->operations->metadata_remove(
610 mirror::snapshot::util::get_image_meta_key(mirror_uuid));
611 if (r < 0 && r != -ENOENT) {
612 lderr(cct) << "cannot remove snapshot image-meta key: " << cpp_strerror(r)
613 << dendl;
614 rollback = true;
615 return r;
616 }
617 }
618
619 C_SaferCond ctx;
620 auto req = mirror::DisableRequest<ImageCtx>::create(ictx, force, true,
621 &ctx);
622 req->send();
623
624 r = ctx.wait();
625 if (r < 0) {
626 lderr(cct) << "cannot disable mirroring: " << cpp_strerror(r) << dendl;
627 rollback = true;
628 return r;
629 }
630
631 if (mirror_image_internal.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
632 r = ictx->operations->update_features(RBD_FEATURE_JOURNALING, false);
633 if (r < 0) {
634 lderr(cct) << "cannot disable journaling: " << cpp_strerror(r) << dendl;
635 // not fatal
636 }
7c673cae
FG
637 }
638
639 return 0;
640}
641
642template <typename I>
643int Mirror<I>::image_promote(I *ictx, bool force) {
644 CephContext *cct = ictx->cct;
645
646 C_SaferCond ctx;
647 Mirror<I>::image_promote(ictx, force, &ctx);
648 int r = ctx.wait();
649 if (r < 0) {
650 lderr(cct) << "failed to promote image" << dendl;
651 return r;
652 }
653
654 return 0;
655}
656
657template <typename I>
658void Mirror<I>::image_promote(I *ictx, bool force, Context *on_finish) {
659 CephContext *cct = ictx->cct;
660 ldout(cct, 20) << "ictx=" << ictx << ", "
661 << "force=" << force << dendl;
662
9f95a23c
TL
663 // don't let the non-primary feature bit prevent image updates
664 ictx->image_lock.lock();
665 ictx->read_only_mask &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
666 ictx->image_lock.unlock();
667
668 auto on_promote = new LambdaContext([ictx, on_finish](int r) {
669 ictx->image_lock.lock();
670 ictx->read_only_mask |= IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
671 ictx->image_lock.unlock();
672
673 ictx->state->handle_update_notification();
674 on_finish->complete(r);
675 });
676
677 auto on_refresh = new LambdaContext([ictx, force, on_promote](int r) {
678 if (r < 0) {
679 lderr(ictx->cct) << "refresh failed: " << cpp_strerror(r) << dendl;
680 on_promote->complete(r);
681 return;
682 }
683
684 auto req = mirror::PromoteRequest<>::create(*ictx, force, on_promote);
685 req->send();
686 });
687 ictx->state->refresh(on_refresh);
7c673cae
FG
688}
689
690template <typename I>
691int Mirror<I>::image_demote(I *ictx) {
692 CephContext *cct = ictx->cct;
693
694 C_SaferCond ctx;
695 Mirror<I>::image_demote(ictx, &ctx);
696 int r = ctx.wait();
697 if (r < 0) {
698 lderr(cct) << "failed to demote image" << dendl;
699 return r;
700 }
701
702 return 0;
703}
704
705template <typename I>
706void Mirror<I>::image_demote(I *ictx, Context *on_finish) {
707 CephContext *cct = ictx->cct;
708 ldout(cct, 20) << "ictx=" << ictx << dendl;
709
1911f103
TL
710 auto on_cleanup = new LambdaContext([ictx, on_finish](int r) {
711 ictx->image_lock.lock();
712 ictx->read_only_mask |= IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
713 ictx->image_lock.unlock();
714
715 ictx->state->handle_update_notification();
716
717 on_finish->complete(r);
718 });
719 auto on_refresh = new LambdaContext([ictx, on_cleanup](int r) {
9f95a23c
TL
720 if (r < 0) {
721 lderr(ictx->cct) << "refresh failed: " << cpp_strerror(r) << dendl;
1911f103 722 on_cleanup->complete(r);
9f95a23c
TL
723 return;
724 }
725
1911f103 726 auto req = mirror::DemoteRequest<>::create(*ictx, on_cleanup);
9f95a23c
TL
727 req->send();
728 });
1911f103
TL
729
730 // ensure we can create a snapshot after setting the non-primary
731 // feature bit
732 ictx->image_lock.lock();
733 ictx->read_only_mask &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
734 ictx->image_lock.unlock();
735
736 ictx->state->refresh(on_refresh);
7c673cae
FG
737}
738
739template <typename I>
740int Mirror<I>::image_resync(I *ictx) {
741 CephContext *cct = ictx->cct;
742 ldout(cct, 20) << "ictx=" << ictx << dendl;
743
744 int r = ictx->state->refresh_if_required();
745 if (r < 0) {
746 return r;
747 }
748
9f95a23c
TL
749 cls::rbd::MirrorImage mirror_image;
750 mirror::PromotionState promotion_state;
751 std::string primary_mirror_uuid;
752 C_SaferCond get_info_ctx;
753 auto req = mirror::GetInfoRequest<I>::create(*ictx, &mirror_image,
754 &promotion_state,
755 &primary_mirror_uuid,
756 &get_info_ctx);
757 req->send();
758
759 r = get_info_ctx.wait();
7c673cae 760 if (r < 0) {
7c673cae 761 return r;
9f95a23c
TL
762 }
763
764 if (promotion_state == mirror::PROMOTION_STATE_PRIMARY) {
7c673cae
FG
765 lderr(cct) << "image is primary, cannot resync to itself" << dendl;
766 return -EINVAL;
767 }
768
9f95a23c
TL
769 if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_JOURNAL) {
770 // flag the journal indicating that we want to rebuild the local image
771 r = Journal<I>::request_resync(ictx);
772 if (r < 0) {
773 lderr(cct) << "failed to request resync: " << cpp_strerror(r) << dendl;
774 return r;
775 }
776 } else if (mirror_image.mode == cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
777 std::string mirror_uuid;
778 r = uuid_get(ictx->md_ctx, &mirror_uuid);
779 if (r < 0) {
780 return r;
781 }
782
783 mirror::snapshot::ImageMeta image_meta(ictx, mirror_uuid);
784
785 C_SaferCond load_meta_ctx;
786 image_meta.load(&load_meta_ctx);
787 r = load_meta_ctx.wait();
788 if (r < 0 && r != -ENOENT) {
789 lderr(cct) << "failed to load mirror image-meta: " << cpp_strerror(r)
790 << dendl;
791 return r;
792 }
793
794 image_meta.resync_requested = true;
795
796 C_SaferCond save_meta_ctx;
797 image_meta.save(&save_meta_ctx);
798 r = save_meta_ctx.wait();
799 if (r < 0) {
800 lderr(cct) << "failed to request resync: " << cpp_strerror(r) << dendl;
801 return r;
802 }
803 } else {
804 lderr(cct) << "unknown mirror mode" << dendl;
805 return -EINVAL;
7c673cae
FG
806 }
807
808 return 0;
809}
810
811template <typename I>
812void Mirror<I>::image_get_info(I *ictx, mirror_image_info_t *mirror_image_info,
11fdf7f2 813 Context *on_finish) {
7c673cae
FG
814 CephContext *cct = ictx->cct;
815 ldout(cct, 20) << "ictx=" << ictx << dendl;
7c673cae 816
9f95a23c 817 auto on_refresh = new LambdaContext(
92f5a8d4
TL
818 [ictx, mirror_image_info, on_finish](int r) {
819 if (r < 0) {
820 lderr(ictx->cct) << "refresh failed: " << cpp_strerror(r) << dendl;
821 on_finish->complete(r);
822 return;
823 }
824
9f95a23c 825 auto ctx = new C_ImageGetInfo(mirror_image_info, nullptr, on_finish);
92f5a8d4
TL
826 auto req = mirror::GetInfoRequest<I>::create(*ictx, &ctx->mirror_image,
827 &ctx->promotion_state,
9f95a23c 828 &ctx->primary_mirror_uuid,
92f5a8d4
TL
829 ctx);
830 req->send();
831 });
832
833 if (ictx->state->is_refresh_required()) {
834 ictx->state->refresh(on_refresh);
835 } else {
836 on_refresh->complete(0);
837 }
7c673cae
FG
838}
839
840template <typename I>
11fdf7f2 841int Mirror<I>::image_get_info(I *ictx, mirror_image_info_t *mirror_image_info) {
7c673cae 842 C_SaferCond ctx;
11fdf7f2 843 image_get_info(ictx, mirror_image_info, &ctx);
7c673cae
FG
844
845 int r = ctx.wait();
846 if (r < 0) {
847 return r;
848 }
849 return 0;
850}
851
852template <typename I>
9f95a23c 853void Mirror<I>::image_get_info(librados::IoCtx& io_ctx,
f67539c2 854 asio::ContextWQ *op_work_queue,
9f95a23c
TL
855 const std::string &image_id,
856 mirror_image_info_t *mirror_image_info,
857 Context *on_finish) {
858 auto cct = reinterpret_cast<CephContext *>(io_ctx.cct());
859 ldout(cct, 20) << "pool_id=" << io_ctx.get_id() << ", image_id=" << image_id
860 << dendl;
861
862 auto ctx = new C_ImageGetInfo(mirror_image_info, nullptr, on_finish);
863 auto req = mirror::GetInfoRequest<I>::create(io_ctx, op_work_queue, image_id,
864 &ctx->mirror_image,
865 &ctx->promotion_state,
866 &ctx->primary_mirror_uuid, ctx);
867 req->send();
868}
869
870template <typename I>
871int Mirror<I>::image_get_info(librados::IoCtx& io_ctx,
f67539c2 872 asio::ContextWQ *op_work_queue,
9f95a23c
TL
873 const std::string &image_id,
874 mirror_image_info_t *mirror_image_info) {
875 C_SaferCond ctx;
876 image_get_info(io_ctx, op_work_queue, image_id, mirror_image_info, &ctx);
877
878 int r = ctx.wait();
879 if (r < 0) {
880 return r;
881 }
882 return 0;
883}
884
885template <typename I>
886void Mirror<I>::image_get_mode(I *ictx, mirror_image_mode_t *mode,
887 Context *on_finish) {
7c673cae
FG
888 CephContext *cct = ictx->cct;
889 ldout(cct, 20) << "ictx=" << ictx << dendl;
7c673cae 890
9f95a23c
TL
891 auto ctx = new C_ImageGetInfo(nullptr, mode, on_finish);
892 auto req = mirror::GetInfoRequest<I>::create(*ictx, &ctx->mirror_image,
893 &ctx->promotion_state,
894 &ctx->primary_mirror_uuid, ctx);
895 req->send();
896}
897
898template <typename I>
899int Mirror<I>::image_get_mode(I *ictx, mirror_image_mode_t *mode) {
900 C_SaferCond ctx;
901 image_get_mode(ictx, mode, &ctx);
902
903 int r = ctx.wait();
904 if (r < 0) {
905 return r;
906 }
907 return 0;
908}
909
910template <typename I>
911void Mirror<I>::image_get_global_status(I *ictx,
912 mirror_image_global_status_t *status,
913 Context *on_finish) {
914 CephContext *cct = ictx->cct;
915 ldout(cct, 20) << "ictx=" << ictx << dendl;
916
917 auto ctx = new C_ImageGetGlobalStatus(ictx->name, status, on_finish);
7c673cae
FG
918 auto req = mirror::GetStatusRequest<I>::create(
919 *ictx, &ctx->mirror_image_status_internal, &ctx->mirror_image,
920 &ctx->promotion_state, ctx);
921 req->send();
922}
923
924template <typename I>
9f95a23c
TL
925int Mirror<I>::image_get_global_status(I *ictx,
926 mirror_image_global_status_t *status) {
7c673cae 927 C_SaferCond ctx;
9f95a23c 928 image_get_global_status(ictx, status, &ctx);
7c673cae
FG
929
930 int r = ctx.wait();
931 if (r < 0) {
932 return r;
933 }
934 return 0;
935}
936
11fdf7f2
TL
937template <typename I>
938int Mirror<I>::image_get_instance_id(I *ictx, std::string *instance_id) {
939 CephContext *cct = ictx->cct;
940 ldout(cct, 20) << "ictx=" << ictx << dendl;
941
942 cls::rbd::MirrorImage mirror_image;
943 int r = cls_client::mirror_image_get(&ictx->md_ctx, ictx->id, &mirror_image);
944 if (r < 0 && r != -ENOENT) {
945 lderr(cct) << "failed to retrieve mirroring state: " << cpp_strerror(r)
946 << dendl;
947 return r;
948 } else if (mirror_image.state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
949 lderr(cct) << "mirroring is not currently enabled" << dendl;
950 return -EINVAL;
951 }
952
953 entity_inst_t instance;
954 r = cls_client::mirror_image_instance_get(&ictx->md_ctx,
955 mirror_image.global_image_id,
956 &instance);
957 if (r < 0) {
958 if (r != -ENOENT && r != -ESTALE) {
959 lderr(cct) << "failed to get mirror image instance: " << cpp_strerror(r)
960 << dendl;
961 }
962 return r;
963 }
964
965 *instance_id = stringify(instance.name.num());
966 return 0;
967}
968
eafe8130
TL
969template <typename I>
970int Mirror<I>::site_name_get(librados::Rados& rados, std::string* name) {
971 CephContext *cct = reinterpret_cast<CephContext *>(rados.cct());
972 ldout(cct, 20) << dendl;
973
974 int r = get_config_key(rados, RBD_MIRROR_SITE_NAME_CONFIG_KEY, name);
975 if (r == -EOPNOTSUPP) {
976 return r;
977 } else if (r == -ENOENT || name->empty()) {
978 // default to the cluster fsid
979 r = rados.cluster_fsid(name);
980 if (r < 0) {
981 lderr(cct) << "failed to retrieve cluster fsid: " << cpp_strerror(r)
982 << dendl;
983 }
984 return r;
985 } else if (r < 0) {
986 lderr(cct) << "failed to retrieve site name: " << cpp_strerror(r)
987 << dendl;
988 return r;
989 }
990
991 return 0;
992}
993
994template <typename I>
995int Mirror<I>::site_name_set(librados::Rados& rados, const std::string& name) {
996 CephContext *cct = reinterpret_cast<CephContext *>(rados.cct());
997
998 std::string site_name{name};
999 boost::algorithm::trim(site_name);
1000 ldout(cct, 20) << "site_name=" << site_name << dendl;
1001
1002 int r = set_config_key(rados, RBD_MIRROR_SITE_NAME_CONFIG_KEY, name);
1003 if (r == -EOPNOTSUPP) {
1004 return r;
1005 } else if (r < 0 && r != -ENOENT) {
1006 lderr(cct) << "failed to update site name: " << cpp_strerror(r)
1007 << dendl;
1008 return r;
1009 }
1010
1011 return 0;
1012}
1013
7c673cae
FG
1014template <typename I>
1015int Mirror<I>::mode_get(librados::IoCtx& io_ctx,
1016 rbd_mirror_mode_t *mirror_mode) {
1017 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1018 ldout(cct, 20) << dendl;
1019
1020 cls::rbd::MirrorMode mirror_mode_internal;
1021 int r = cls_client::mirror_mode_get(&io_ctx, &mirror_mode_internal);
1022 if (r < 0) {
1023 lderr(cct) << "failed to retrieve mirror mode: " << cpp_strerror(r)
1024 << dendl;
1025 return r;
1026 }
1027
1028 switch (mirror_mode_internal) {
1029 case cls::rbd::MIRROR_MODE_DISABLED:
1030 case cls::rbd::MIRROR_MODE_IMAGE:
1031 case cls::rbd::MIRROR_MODE_POOL:
1032 *mirror_mode = static_cast<rbd_mirror_mode_t>(mirror_mode_internal);
1033 break;
1034 default:
1035 lderr(cct) << "unknown mirror mode ("
1036 << static_cast<uint32_t>(mirror_mode_internal) << ")"
1037 << dendl;
1038 return -EINVAL;
1039 }
1040 return 0;
1041}
1042
1043template <typename I>
1044int Mirror<I>::mode_set(librados::IoCtx& io_ctx,
1045 rbd_mirror_mode_t mirror_mode) {
1046 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1047 ldout(cct, 20) << dendl;
1048
1049 cls::rbd::MirrorMode next_mirror_mode;
1050 switch (mirror_mode) {
1051 case RBD_MIRROR_MODE_DISABLED:
1052 case RBD_MIRROR_MODE_IMAGE:
1053 case RBD_MIRROR_MODE_POOL:
1054 next_mirror_mode = static_cast<cls::rbd::MirrorMode>(mirror_mode);
1055 break;
1056 default:
1057 lderr(cct) << "unknown mirror mode ("
1058 << static_cast<uint32_t>(mirror_mode) << ")" << dendl;
1059 return -EINVAL;
1060 }
1061
1062 int r;
1063 if (next_mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) {
1064 // fail early if pool still has peers registered and attempting to disable
1065 std::vector<cls::rbd::MirrorPeer> mirror_peers;
1066 r = cls_client::mirror_peer_list(&io_ctx, &mirror_peers);
1067 if (r < 0 && r != -ENOENT) {
1068 lderr(cct) << "failed to list peers: " << cpp_strerror(r) << dendl;
1069 return r;
1070 } else if (!mirror_peers.empty()) {
1071 lderr(cct) << "mirror peers still registered" << dendl;
1072 return -EBUSY;
1073 }
1074 }
1075
1076 cls::rbd::MirrorMode current_mirror_mode;
1077 r = cls_client::mirror_mode_get(&io_ctx, &current_mirror_mode);
1078 if (r < 0) {
1079 lderr(cct) << "failed to retrieve mirror mode: " << cpp_strerror(r)
1080 << dendl;
1081 return r;
1082 }
1083
1084 if (current_mirror_mode == next_mirror_mode) {
1085 return 0;
1086 } else if (current_mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) {
1087 uuid_d uuid_gen;
1088 uuid_gen.generate_random();
1089 r = cls_client::mirror_uuid_set(&io_ctx, uuid_gen.to_string());
1090 if (r < 0) {
1091 lderr(cct) << "failed to allocate mirroring uuid: " << cpp_strerror(r)
1092 << dendl;
1093 return r;
1094 }
1095 }
1096
1097 if (current_mirror_mode != cls::rbd::MIRROR_MODE_IMAGE) {
1098 r = cls_client::mirror_mode_set(&io_ctx, cls::rbd::MIRROR_MODE_IMAGE);
1099 if (r < 0) {
1100 lderr(cct) << "failed to set mirror mode to image: "
1101 << cpp_strerror(r) << dendl;
1102 return r;
1103 }
1104
1105 r = MirroringWatcher<>::notify_mode_updated(io_ctx,
1106 cls::rbd::MIRROR_MODE_IMAGE);
1107 if (r < 0) {
1108 lderr(cct) << "failed to send update notification: " << cpp_strerror(r)
1109 << dendl;
1110 }
1111 }
1112
1113 if (next_mirror_mode == cls::rbd::MIRROR_MODE_IMAGE) {
1114 return 0;
1115 }
1116
1117 if (next_mirror_mode == cls::rbd::MIRROR_MODE_POOL) {
1118 map<string, string> images;
11fdf7f2 1119 r = Image<I>::list_images_v2(io_ctx, &images);
7c673cae
FG
1120 if (r < 0) {
1121 lderr(cct) << "failed listing images: " << cpp_strerror(r) << dendl;
1122 return r;
1123 }
1124
1125 for (const auto& img_pair : images) {
1126 uint64_t features;
11fdf7f2
TL
1127 uint64_t incompatible_features;
1128 r = cls_client::get_features(&io_ctx, util::header_name(img_pair.second),
1129 true, &features, &incompatible_features);
7c673cae
FG
1130 if (r < 0) {
1131 lderr(cct) << "error getting features for image " << img_pair.first
1132 << ": " << cpp_strerror(r) << dendl;
1133 return r;
1134 }
1135
9f95a23c
TL
1136 // Enable only journal based mirroring
1137
7c673cae
FG
1138 if ((features & RBD_FEATURE_JOURNALING) != 0) {
1139 I *img_ctx = I::create("", img_pair.second, nullptr, io_ctx, false);
11fdf7f2 1140 r = img_ctx->state->open(0);
7c673cae
FG
1141 if (r < 0) {
1142 lderr(cct) << "error opening image "<< img_pair.first << ": "
1143 << cpp_strerror(r) << dendl;
1144 return r;
1145 }
1146
9f95a23c 1147 r = image_enable(img_ctx, RBD_MIRROR_IMAGE_MODE_JOURNAL, true);
7c673cae
FG
1148 int close_r = img_ctx->state->close();
1149 if (r < 0) {
1150 lderr(cct) << "error enabling mirroring for image "
1151 << img_pair.first << ": " << cpp_strerror(r) << dendl;
1152 return r;
1153 } else if (close_r < 0) {
1154 lderr(cct) << "failed to close image " << img_pair.first << ": "
1155 << cpp_strerror(close_r) << dendl;
1156 return close_r;
1157 }
1158 }
1159 }
1160 } else if (next_mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) {
11fdf7f2
TL
1161 while (true) {
1162 bool retry_busy = false;
1163 bool pending_busy = false;
7c673cae 1164
11fdf7f2
TL
1165 std::set<std::string> image_ids;
1166 r = list_mirror_images(io_ctx, image_ids);
1167 if (r < 0) {
1168 lderr(cct) << "failed listing images: " << cpp_strerror(r) << dendl;
1169 return r;
1170 }
7c673cae 1171
11fdf7f2
TL
1172 for (const auto& img_id : image_ids) {
1173 if (current_mirror_mode == cls::rbd::MIRROR_MODE_IMAGE) {
1174 cls::rbd::MirrorImage mirror_image;
1175 r = cls_client::mirror_image_get(&io_ctx, img_id, &mirror_image);
1176 if (r < 0 && r != -ENOENT) {
1177 lderr(cct) << "failed to retrieve mirroring state for image id "
1178 << img_id << ": " << cpp_strerror(r) << dendl;
1179 return r;
1180 }
1181 if (mirror_image.state == cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
1182 lderr(cct) << "failed to disable mirror mode: there are still "
1183 << "images with mirroring enabled" << dendl;
1184 return -EINVAL;
1185 }
1186 } else {
1187 I *img_ctx = I::create("", img_id, nullptr, io_ctx, false);
1188 r = img_ctx->state->open(0);
1189 if (r < 0) {
1190 lderr(cct) << "error opening image id "<< img_id << ": "
1191 << cpp_strerror(r) << dendl;
1192 return r;
1193 }
1194
1195 r = image_disable(img_ctx, false);
1196 int close_r = img_ctx->state->close();
1197 if (r == -EBUSY) {
1198 pending_busy = true;
1199 } else if (r < 0) {
1200 lderr(cct) << "error disabling mirroring for image id " << img_id
1201 << cpp_strerror(r) << dendl;
1202 return r;
1203 } else if (close_r < 0) {
1204 lderr(cct) << "failed to close image id " << img_id << ": "
1205 << cpp_strerror(close_r) << dendl;
1206 return close_r;
1207 } else if (pending_busy) {
1208 // at least one mirrored image was successfully disabled, so we can
1209 // retry any failures caused by busy parent/child relationships
1210 retry_busy = true;
1211 }
7c673cae
FG
1212 }
1213 }
11fdf7f2
TL
1214
1215 if (!retry_busy && pending_busy) {
1216 lderr(cct) << "error disabling mirroring for one or more images"
1217 << dendl;
1218 return -EBUSY;
1219 } else if (!retry_busy) {
1220 break;
1221 }
7c673cae
FG
1222 }
1223 }
1224
1225 r = cls_client::mirror_mode_set(&io_ctx, next_mirror_mode);
1226 if (r < 0) {
1227 lderr(cct) << "failed to set mirror mode: " << cpp_strerror(r) << dendl;
1228 return r;
1229 }
1230
1231 r = MirroringWatcher<>::notify_mode_updated(io_ctx, next_mirror_mode);
1232 if (r < 0) {
1233 lderr(cct) << "failed to send update notification: " << cpp_strerror(r)
1234 << dendl;
1235 }
1236 return 0;
1237}
1238
9f95a23c
TL
1239template <typename I>
1240int Mirror<I>::uuid_get(librados::IoCtx& io_ctx, std::string* mirror_uuid) {
1241 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1242 ldout(cct, 20) << dendl;
1243
1244 C_SaferCond ctx;
1245 uuid_get(io_ctx, mirror_uuid, &ctx);
1246 int r = ctx.wait();
1247 if (r < 0) {
1248 if (r != -ENOENT) {
1249 lderr(cct) << "failed to retrieve mirroring uuid: " << cpp_strerror(r)
1250 << dendl;
1251 }
1252 return r;
1253 }
1254
1255 return 0;
1256}
1257
1258template <typename I>
1259void Mirror<I>::uuid_get(librados::IoCtx& io_ctx, std::string* mirror_uuid,
1260 Context* on_finish) {
1261 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1262 ldout(cct, 20) << dendl;
1263
1264 auto req = mirror::GetUuidRequest<I>::create(io_ctx, mirror_uuid, on_finish);
1265 req->send();
1266}
1267
eafe8130
TL
1268template <typename I>
1269int Mirror<I>::peer_bootstrap_create(librados::IoCtx& io_ctx,
1270 std::string* token) {
1271 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1272 ldout(cct, 20) << dendl;
1273
1274 auto mirror_mode = cls::rbd::MIRROR_MODE_DISABLED;
1275 int r = cls_client::mirror_mode_get(&io_ctx, &mirror_mode);
1276 if (r < 0 && r != -ENOENT) {
1277 lderr(cct) << "failed to retrieve mirroring mode: " << cpp_strerror(r)
1278 << dendl;
1279 return r;
1280 } else if (mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) {
1281 return -EINVAL;
1282 }
1283
1284 // retrieve the cluster fsid
1285 std::string fsid;
1286 librados::Rados rados(io_ctx);
1287 r = rados.cluster_fsid(&fsid);
1288 if (r < 0) {
1289 lderr(cct) << "failed to retrieve cluster fsid: " << cpp_strerror(r)
1290 << dendl;
1291 return r;
1292 }
1293
1294 std::string peer_client_id;
1295 std::string cephx_key;
1296 r = create_bootstrap_user(cct, rados, &peer_client_id, &cephx_key);
1297 if (r < 0) {
1298 return r;
1299 }
1300
1301 std::string mon_host = cct->_conf.get_val<std::string>("mon_host");
1302 ldout(cct, 20) << "mon_host=" << mon_host << dendl;
1303
1304 // format the token response
1305 bufferlist token_bl;
1306 token_bl.append(
1307 R"({)" \
1308 R"("fsid":")" + fsid + R"(",)" + \
1309 R"("client_id":")" + peer_client_id + R"(",)" + \
1310 R"("key":")" + cephx_key + R"(",)" + \
1311 R"("mon_host":")" + \
1312 boost::replace_all_copy(mon_host, "\"", "\\\"") + R"(")" + \
1313 R"(})");
1314 ldout(cct, 20) << "token=" << token_bl.to_str() << dendl;
1315
1316 bufferlist base64_bl;
1317 token_bl.encode_base64(base64_bl);
1318 *token = base64_bl.to_str();
1319
1320 return 0;
1321}
1322
1323template <typename I>
1324int Mirror<I>::peer_bootstrap_import(librados::IoCtx& io_ctx,
1325 rbd_mirror_peer_direction_t direction,
1326 const std::string& token) {
1327 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1328 ldout(cct, 20) << dendl;
1329
1330 if (direction != RBD_MIRROR_PEER_DIRECTION_RX &&
1331 direction != RBD_MIRROR_PEER_DIRECTION_RX_TX) {
1332 lderr(cct) << "invalid mirror peer direction" << dendl;
1333 return -EINVAL;
1334 }
1335
1336 bufferlist token_bl;
1337 try {
1338 bufferlist base64_bl;
1339 base64_bl.append(token);
1340 token_bl.decode_base64(base64_bl);
1341 } catch (buffer::error& err) {
1342 lderr(cct) << "failed to decode base64" << dendl;
1343 return -EINVAL;
1344 }
1345
1346 ldout(cct, 20) << "token=" << token_bl.to_str() << dendl;
1347
1348 bool json_valid = false;
1349 std::string expected_remote_fsid;
1350 std::string remote_client_id;
1351 std::string remote_key;
1352 std::string remote_mon_host;
1353
1354 json_spirit::mValue json_root;
1355 if(json_spirit::read(token_bl.to_str(), json_root)) {
1356 try {
1357 auto& json_obj = json_root.get_obj();
1358 expected_remote_fsid = json_obj["fsid"].get_str();
1359 remote_client_id = json_obj["client_id"].get_str();
1360 remote_key = json_obj["key"].get_str();
1361 remote_mon_host = json_obj["mon_host"].get_str();
1362 json_valid = true;
1363 } catch (std::runtime_error&) {
1364 }
1365 }
1366
1367 if (!json_valid) {
1368 lderr(cct) << "invalid bootstrap token JSON received" << dendl;
1369 return -EINVAL;
1370 }
1371
1372 // sanity check import process
1373 std::string local_fsid;
1374 librados::Rados rados(io_ctx);
1375 int r = rados.cluster_fsid(&local_fsid);
1376 if (r < 0) {
1377 lderr(cct) << "failed to retrieve cluster fsid: " << cpp_strerror(r)
1378 << dendl;
1379 return r;
1380 }
1381
1382 std::string local_site_name;
1383 r = site_name_get(rados, &local_site_name);
1384 if (r < 0) {
1385 lderr(cct) << "failed to retrieve cluster site name: " << cpp_strerror(r)
1386 << dendl;
1387 return r;
1388 }
1389
1390 // attempt to connect to remote cluster
1391 librados::Rados remote_rados;
1392 remote_rados.init(remote_client_id.c_str());
1393
1394 auto remote_cct = reinterpret_cast<CephContext*>(remote_rados.cct());
1395 remote_cct->_conf.set_val("mon_host", remote_mon_host);
1396 remote_cct->_conf.set_val("key", remote_key);
1397
1398 r = remote_rados.connect();
1399 if (r < 0) {
1400 lderr(cct) << "failed to connect to peer cluster: " << cpp_strerror(r)
1401 << dendl;
1402 return r;
1403 }
1404
1405 std::string remote_fsid;
1406 r = remote_rados.cluster_fsid(&remote_fsid);
1407 if (r < 0) {
1408 lderr(cct) << "failed to retrieve remote cluster fsid: "
1409 << cpp_strerror(r) << dendl;
1410 return r;
1411 } else if (local_fsid == remote_fsid) {
1412 lderr(cct) << "cannot import token for local cluster" << dendl;
1413 return -EINVAL;
1414 } else if (expected_remote_fsid != remote_fsid) {
1415 lderr(cct) << "unexpected remote cluster fsid" << dendl;
1416 return -EINVAL;
1417 }
1418
1419 std::string remote_site_name;
1420 r = site_name_get(remote_rados, &remote_site_name);
1421 if (r < 0) {
1422 lderr(cct) << "failed to retrieve remote cluster site name: "
1423 << cpp_strerror(r) << dendl;
1424 return r;
1425 } else if (local_site_name == remote_site_name) {
1426 lderr(cct) << "cannot import token for duplicate site name" << dendl;
1427 return -EINVAL;
1428 }
1429
1430 librados::IoCtx remote_io_ctx;
1431 r = remote_rados.ioctx_create(io_ctx.get_pool_name().c_str(), remote_io_ctx);
1432 if (r == -ENOENT) {
1433 ldout(cct, 10) << "remote pool does not exist" << dendl;
1434 return r;
1435 } else if (r < 0) {
1436 lderr(cct) << "failed to open remote pool '" << io_ctx.get_pool_name()
1437 << "': " << cpp_strerror(r) << dendl;
1438 return r;
1439 }
1440
1441 auto remote_mirror_mode = cls::rbd::MIRROR_MODE_DISABLED;
1442 r = cls_client::mirror_mode_get(&remote_io_ctx, &remote_mirror_mode);
1443 if (r < 0 && r != -ENOENT) {
1444 lderr(cct) << "failed to retrieve remote mirroring mode: "
1445 << cpp_strerror(r) << dendl;
1446 return r;
1447 } else if (remote_mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) {
1448 return -ENOSYS;
1449 }
1450
1451 auto local_mirror_mode = cls::rbd::MIRROR_MODE_DISABLED;
1452 r = cls_client::mirror_mode_get(&io_ctx, &local_mirror_mode);
1453 if (r < 0 && r != -ENOENT) {
1454 lderr(cct) << "failed to retrieve local mirroring mode: " << cpp_strerror(r)
1455 << dendl;
1456 return r;
1457 } else if (local_mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) {
1458 // copy mirror mode from remote peer
1459 r = mode_set(io_ctx, static_cast<rbd_mirror_mode_t>(remote_mirror_mode));
1460 if (r < 0) {
1461 return r;
1462 }
1463 }
1464
1465 if (direction == RBD_MIRROR_PEER_DIRECTION_RX_TX) {
1466 // create a local mirror peer user and export it to the remote cluster
1467 std::string local_client_id;
1468 std::string local_key;
1469 r = create_bootstrap_user(cct, rados, &local_client_id, &local_key);
1470 if (r < 0) {
1471 return r;
1472 }
1473
1474 std::string local_mon_host = cct->_conf.get_val<std::string>("mon_host");
1475
1476 // create local cluster peer in remote cluster
9f95a23c
TL
1477 r = create_bootstrap_peer(cct, remote_io_ctx,
1478 RBD_MIRROR_PEER_DIRECTION_RX_TX, local_site_name,
1479 local_fsid, local_client_id, local_key,
1480 local_mon_host, "local", "remote");
eafe8130
TL
1481 if (r < 0) {
1482 return r;
1483 }
1484 }
1485
1486 // create remote cluster peer in local cluster
9f95a23c
TL
1487 r = create_bootstrap_peer(cct, io_ctx, direction, remote_site_name,
1488 remote_fsid, remote_client_id, remote_key,
1489 remote_mon_host, "remote", "local");
eafe8130
TL
1490 if (r < 0) {
1491 return r;
1492 }
1493
1494 return 0;
1495}
1496
7c673cae 1497template <typename I>
9f95a23c
TL
1498int Mirror<I>::peer_site_add(librados::IoCtx& io_ctx, std::string *uuid,
1499 mirror_peer_direction_t direction,
1500 const std::string &site_name,
1501 const std::string &client_name) {
7c673cae 1502 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
9f95a23c 1503 ldout(cct, 20) << "name=" << site_name << ", "
7c673cae
FG
1504 << "client=" << client_name << dendl;
1505
9f95a23c
TL
1506 if (cct->_conf->cluster == site_name) {
1507 lderr(cct) << "cannot add self as remote peer" << dendl;
11fdf7f2
TL
1508 return -EINVAL;
1509 }
1510
9f95a23c 1511 if (direction == RBD_MIRROR_PEER_DIRECTION_TX) {
7c673cae
FG
1512 return -EINVAL;
1513 }
1514
1515 int r;
1516 do {
1517 uuid_d uuid_gen;
1518 uuid_gen.generate_random();
1519
1520 *uuid = uuid_gen.to_string();
9f95a23c
TL
1521 r = cls_client::mirror_peer_add(
1522 &io_ctx, {*uuid, static_cast<cls::rbd::MirrorPeerDirection>(direction),
1523 site_name, client_name, ""});
7c673cae
FG
1524 if (r == -ESTALE) {
1525 ldout(cct, 5) << "duplicate UUID detected, retrying" << dendl;
1526 } else if (r < 0) {
1911f103 1527 lderr(cct) << "failed to add mirror peer '" << site_name << "': "
7c673cae
FG
1528 << cpp_strerror(r) << dendl;
1529 return r;
1530 }
1531 } while (r == -ESTALE);
1532 return 0;
1533}
1534
1535template <typename I>
9f95a23c
TL
1536int Mirror<I>::peer_site_remove(librados::IoCtx& io_ctx,
1537 const std::string &uuid) {
7c673cae
FG
1538 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1539 ldout(cct, 20) << "uuid=" << uuid << dendl;
1540
11fdf7f2
TL
1541 int r = remove_peer_config_key(io_ctx, uuid);
1542 if (r < 0) {
1543 lderr(cct) << "failed to remove peer attributes '" << uuid << "': "
1544 << cpp_strerror(r) << dendl;
1545 return r;
1546 }
1547
1548 r = cls_client::mirror_peer_remove(&io_ctx, uuid);
7c673cae
FG
1549 if (r < 0 && r != -ENOENT) {
1550 lderr(cct) << "failed to remove peer '" << uuid << "': "
1551 << cpp_strerror(r) << dendl;
1552 return r;
1553 }
9f95a23c
TL
1554
1555 vector<string> names;
1556 r = Namespace<I>::list(io_ctx, &names);
1557 if (r < 0) {
1558 return r;
1559 }
1560
1561 names.push_back("");
1562
1563 librados::IoCtx ns_io_ctx;
1564 ns_io_ctx.dup(io_ctx);
1565
1566 for (auto &name : names) {
1567 ns_io_ctx.set_namespace(name);
1568
1569 std::set<std::string> image_ids;
1570 r = list_mirror_images(ns_io_ctx, image_ids);
1571 if (r < 0) {
1572 lderr(cct) << "failed listing images in "
1573 << (name.empty() ? "default" : name) << " namespace : "
1574 << cpp_strerror(r) << dendl;
1575 return r;
1576 }
1577
1578 for (const auto& image_id : image_ids) {
1579 cls::rbd::MirrorImage mirror_image;
1580 r = cls_client::mirror_image_get(&ns_io_ctx, image_id, &mirror_image);
1581 if (r == -ENOENT) {
1582 continue;
1583 }
1584 if (r < 0) {
1585 lderr(cct) << "error getting mirror info for image " << image_id
1586 << ": " << cpp_strerror(r) << dendl;
1587 return r;
1588 }
1589 if (mirror_image.mode != cls::rbd::MIRROR_IMAGE_MODE_SNAPSHOT) {
1590 continue;
1591 }
1592
1593 // Snapshot based mirroring. Unlink the peer from mirroring snapshots.
1594 // TODO: optimize.
1595
1596 I *img_ctx = I::create("", image_id, nullptr, ns_io_ctx, false);
1597 img_ctx->read_only_mask &= ~IMAGE_READ_ONLY_FLAG_NON_PRIMARY;
1598
1599 r = img_ctx->state->open(0);
1600 if (r == -ENOENT) {
1601 continue;
1602 }
1603 if (r < 0) {
1604 lderr(cct) << "error opening image " << image_id << ": "
1605 << cpp_strerror(r) << dendl;
1606 return r;
1607 }
1608
1609 std::list<uint64_t> snap_ids;
1610 {
1611 std::shared_lock image_locker{img_ctx->image_lock};
1612 for (auto &it : img_ctx->snap_info) {
1613 auto info = boost::get<cls::rbd::MirrorSnapshotNamespace>(
1614 &it.second.snap_namespace);
1615 if (info && info->mirror_peer_uuids.count(uuid)) {
1616 snap_ids.push_back(it.first);
1617 }
1618 }
1619 }
1620 for (auto snap_id : snap_ids) {
1621 C_SaferCond cond;
1622 auto req = mirror::snapshot::UnlinkPeerRequest<I>::create(
1623 img_ctx, snap_id, uuid, &cond);
1624 req->send();
1625 r = cond.wait();
1626 if (r == -ENOENT) {
1627 r = 0;
1628 }
1629 if (r < 0) {
1630 break;
1631 }
1632 }
1633
1634 int close_r = img_ctx->state->close();
1635 if (r < 0) {
1636 lderr(cct) << "error unlinking peer for image " << image_id << ": "
1637 << cpp_strerror(r) << dendl;
1638 return r;
1639 } else if (close_r < 0) {
1640 lderr(cct) << "failed to close image " << image_id << ": "
1641 << cpp_strerror(close_r) << dendl;
1642 return close_r;
1643 }
1644 }
1645 }
1646
7c673cae
FG
1647 return 0;
1648}
1649
1650template <typename I>
9f95a23c
TL
1651int Mirror<I>::peer_site_list(librados::IoCtx& io_ctx,
1652 std::vector<mirror_peer_site_t> *peers) {
7c673cae
FG
1653 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1654 ldout(cct, 20) << dendl;
1655
1656 std::vector<cls::rbd::MirrorPeer> mirror_peers;
1657 int r = cls_client::mirror_peer_list(&io_ctx, &mirror_peers);
1658 if (r < 0 && r != -ENOENT) {
1659 lderr(cct) << "failed to list peers: " << cpp_strerror(r) << dendl;
1660 return r;
1661 }
1662
1663 peers->clear();
1664 peers->reserve(mirror_peers.size());
1665 for (auto &mirror_peer : mirror_peers) {
9f95a23c 1666 mirror_peer_site_t peer;
7c673cae 1667 peer.uuid = mirror_peer.uuid;
9f95a23c
TL
1668 peer.direction = static_cast<mirror_peer_direction_t>(
1669 mirror_peer.mirror_peer_direction);
1670 peer.site_name = mirror_peer.site_name;
1671 peer.mirror_uuid = mirror_peer.mirror_uuid;
7c673cae 1672 peer.client_name = mirror_peer.client_name;
9f95a23c 1673 peer.last_seen = mirror_peer.last_seen.sec();
7c673cae
FG
1674 peers->push_back(peer);
1675 }
1676 return 0;
1677}
1678
1679template <typename I>
9f95a23c
TL
1680int Mirror<I>::peer_site_set_client(librados::IoCtx& io_ctx,
1681 const std::string &uuid,
1682 const std::string &client_name) {
7c673cae
FG
1683 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1684 ldout(cct, 20) << "uuid=" << uuid << ", "
1685 << "client=" << client_name << dendl;
1686
1687 int r = cls_client::mirror_peer_set_client(&io_ctx, uuid, client_name);
1688 if (r < 0) {
1689 lderr(cct) << "failed to update client '" << uuid << "': "
1690 << cpp_strerror(r) << dendl;
1691 return r;
1692 }
1693 return 0;
1694}
1695
1696template <typename I>
9f95a23c
TL
1697int Mirror<I>::peer_site_set_name(librados::IoCtx& io_ctx,
1698 const std::string &uuid,
1699 const std::string &site_name) {
7c673cae
FG
1700 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1701 ldout(cct, 20) << "uuid=" << uuid << ", "
9f95a23c 1702 << "name=" << site_name << dendl;
7c673cae 1703
9f95a23c 1704 if (cct->_conf->cluster == site_name) {
11fdf7f2
TL
1705 lderr(cct) << "cannot set self as remote peer" << dendl;
1706 return -EINVAL;
1707 }
1708
9f95a23c
TL
1709 int r = cls_client::mirror_peer_set_cluster(&io_ctx, uuid, site_name);
1710 if (r < 0) {
1711 lderr(cct) << "failed to update site '" << uuid << "': "
1712 << cpp_strerror(r) << dendl;
1713 return r;
1714 }
1715 return 0;
1716}
1717
1718template <typename I>
1719int Mirror<I>::peer_site_set_direction(librados::IoCtx& io_ctx,
1720 const std::string &uuid,
1721 mirror_peer_direction_t direction) {
1722 cls::rbd::MirrorPeerDirection mirror_peer_direction = static_cast<
1723 cls::rbd::MirrorPeerDirection>(direction);
1724
1725 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1726 ldout(cct, 20) << "uuid=" << uuid << ", "
1727 << "direction=" << mirror_peer_direction << dendl;
1728
1729 int r = cls_client::mirror_peer_set_direction(&io_ctx, uuid,
1730 mirror_peer_direction);
7c673cae 1731 if (r < 0) {
9f95a23c 1732 lderr(cct) << "failed to update direction '" << uuid << "': "
7c673cae
FG
1733 << cpp_strerror(r) << dendl;
1734 return r;
1735 }
1736 return 0;
1737}
1738
11fdf7f2 1739template <typename I>
9f95a23c
TL
1740int Mirror<I>::peer_site_get_attributes(librados::IoCtx& io_ctx,
1741 const std::string &uuid,
1742 Attributes* attributes) {
11fdf7f2
TL
1743 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1744 ldout(cct, 20) << "uuid=" << uuid << dendl;
1745
1746 attributes->clear();
11fdf7f2
TL
1747
1748 librados::Rados rados(io_ctx);
eafe8130
TL
1749 std::string value;
1750 int r = get_config_key(rados, get_peer_config_key_name(io_ctx.get_id(), uuid),
1751 &value);
1752 if (r == -ENOENT || value.empty()) {
11fdf7f2
TL
1753 return -ENOENT;
1754 } else if (r < 0) {
1755 lderr(cct) << "failed to retrieve peer attributes: " << cpp_strerror(r)
1756 << dendl;
1757 return r;
1758 }
1759
1760 bool json_valid = false;
1761 json_spirit::mValue json_root;
eafe8130 1762 if(json_spirit::read(value, json_root)) {
11fdf7f2
TL
1763 try {
1764 auto& json_obj = json_root.get_obj();
1765 for (auto& pairs : json_obj) {
1766 (*attributes)[pairs.first] = pairs.second.get_str();
1767 }
1768 json_valid = true;
1769 } catch (std::runtime_error&) {
1770 }
1771 }
1772
1773 if (!json_valid) {
1774 lderr(cct) << "invalid peer attributes JSON received" << dendl;
1775 return -EINVAL;
1776 }
1777 return 0;
1778}
1779
1780template <typename I>
9f95a23c
TL
1781int Mirror<I>::peer_site_set_attributes(librados::IoCtx& io_ctx,
1782 const std::string &uuid,
1783 const Attributes& attributes) {
11fdf7f2
TL
1784 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1785 ldout(cct, 20) << "uuid=" << uuid << ", "
1786 << "attributes=" << attributes << dendl;
1787
9f95a23c
TL
1788 std::vector<mirror_peer_site_t> mirror_peers;
1789 int r = peer_site_list(io_ctx, &mirror_peers);
11fdf7f2
TL
1790 if (r < 0) {
1791 return r;
1792 }
1793
1794 if (std::find_if(mirror_peers.begin(), mirror_peers.end(),
9f95a23c 1795 [&uuid](const librbd::mirror_peer_site_t& peer) {
11fdf7f2
TL
1796 return uuid == peer.uuid;
1797 }) == mirror_peers.end()) {
1798 ldout(cct, 5) << "mirror peer uuid " << uuid << " does not exist" << dendl;
1799 return -ENOENT;
1800 }
1801
1802 std::stringstream ss;
1803 ss << "{";
1804 for (auto& pair : attributes) {
1805 ss << "\\\"" << pair.first << "\\\": "
1806 << "\\\"" << pair.second << "\\\"";
1807 if (&pair != &(*attributes.rbegin())) {
1808 ss << ", ";
1809 }
1810 }
1811 ss << "}";
1812
11fdf7f2 1813 librados::Rados rados(io_ctx);
eafe8130
TL
1814 r = set_config_key(rados, get_peer_config_key_name(io_ctx.get_id(), uuid),
1815 ss.str());
1816 if (r < 0 && r != -ENOENT) {
11fdf7f2
TL
1817 lderr(cct) << "failed to update peer attributes: " << cpp_strerror(r)
1818 << dendl;
1819 return r;
1820 }
1821
1822 return 0;
1823}
1824
7c673cae 1825template <typename I>
9f95a23c
TL
1826int Mirror<I>::image_global_status_list(
1827 librados::IoCtx& io_ctx, const std::string &start_id, size_t max,
1828 IdToMirrorImageGlobalStatus *images) {
7c673cae
FG
1829 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1830 int r;
1831
1832 map<string, string> id_to_name;
1833 {
1834 map<string, string> name_to_id;
11fdf7f2 1835 r = Image<I>::list_images_v2(io_ctx, &name_to_id);
7c673cae
FG
1836 if (r < 0) {
1837 return r;
1838 }
1839 for (auto it : name_to_id) {
1840 id_to_name[it.second] = it.first;
1841 }
1842 }
1843
1844 map<std::string, cls::rbd::MirrorImage> images_;
1845 map<std::string, cls::rbd::MirrorImageStatus> statuses_;
1846
1847 r = librbd::cls_client::mirror_image_status_list(&io_ctx, start_id, max,
1848 &images_, &statuses_);
c07f9fc5 1849 if (r < 0 && r != -ENOENT) {
7c673cae
FG
1850 lderr(cct) << "failed to list mirror image statuses: "
1851 << cpp_strerror(r) << dendl;
1852 return r;
1853 }
1854
9f95a23c 1855 const std::string STATUS_NOT_FOUND("status not found");
7c673cae
FG
1856 for (auto it = images_.begin(); it != images_.end(); ++it) {
1857 auto &image_id = it->first;
1858 auto &info = it->second;
3efd9988
FG
1859 if (info.state == cls::rbd::MIRROR_IMAGE_STATE_DISABLED) {
1860 continue;
1861 }
1862
7c673cae
FG
1863 auto &image_name = id_to_name[image_id];
1864 if (image_name.empty()) {
1865 lderr(cct) << "failed to find image name for image " << image_id << ", "
1866 << "using image id as name" << dendl;
1867 image_name = image_id;
1868 }
9f95a23c
TL
1869
1870 mirror_image_global_status_t& global_status = (*images)[image_id];
1871 global_status.name = image_name;
1872 global_status.info = mirror_image_info_t{
7c673cae
FG
1873 info.global_image_id,
1874 static_cast<mirror_image_state_t>(info.state),
9f95a23c
TL
1875 false}; // XXX: To set "primary" right would require an additional call.
1876
f67539c2 1877 bool found_local_site_status = false;
9f95a23c
TL
1878 auto s_it = statuses_.find(image_id);
1879 if (s_it != statuses_.end()) {
1880 auto& status = s_it->second;
1881
1882 global_status.site_statuses.reserve(
1883 status.mirror_image_site_statuses.size());
1884 for (auto& site_status : status.mirror_image_site_statuses) {
f67539c2
TL
1885 if (site_status.mirror_uuid ==
1886 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID) {
1887 found_local_site_status = true;
1888 }
1889
9f95a23c
TL
1890 global_status.site_statuses.push_back(mirror_image_site_status_t{
1891 site_status.mirror_uuid,
1892 static_cast<mirror_image_status_state_t>(site_status.state),
1893 site_status.state == cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN ?
1894 STATUS_NOT_FOUND : site_status.description,
1895 site_status.last_update.sec(), site_status.up});
1896 }
f67539c2
TL
1897 }
1898
1899 if (!found_local_site_status) {
9f95a23c
TL
1900 global_status.site_statuses.push_back(mirror_image_site_status_t{
1901 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID,
1902 MIRROR_IMAGE_STATUS_STATE_UNKNOWN, STATUS_NOT_FOUND, 0, false});
1903 }
7c673cae
FG
1904 }
1905
1906 return 0;
1907}
1908
1909template <typename I>
1910int Mirror<I>::image_status_summary(librados::IoCtx& io_ctx,
1911 MirrorImageStatusStates *states) {
1912 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1913
9f95a23c
TL
1914 std::vector<cls::rbd::MirrorPeer> mirror_peers;
1915 int r = cls_client::mirror_peer_list(&io_ctx, &mirror_peers);
1916 if (r < 0 && r != -ENOENT) {
1917 lderr(cct) << "failed to list mirror peers: " << cpp_strerror(r) << dendl;
1918 return r;
1919 }
1920
f67539c2 1921 std::map<cls::rbd::MirrorImageStatusState, int32_t> states_;
9f95a23c
TL
1922 r = cls_client::mirror_image_status_get_summary(&io_ctx, mirror_peers,
1923 &states_);
3efd9988 1924 if (r < 0 && r != -ENOENT) {
7c673cae
FG
1925 lderr(cct) << "failed to get mirror status summary: "
1926 << cpp_strerror(r) << dendl;
1927 return r;
1928 }
1929 for (auto &s : states_) {
1930 (*states)[static_cast<mirror_image_status_state_t>(s.first)] = s.second;
1931 }
1932 return 0;
1933}
1934
11fdf7f2
TL
1935template <typename I>
1936int Mirror<I>::image_instance_id_list(
1937 librados::IoCtx& io_ctx, const std::string &start_image_id, size_t max,
1938 std::map<std::string, std::string> *instance_ids) {
1939 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1940 std::map<std::string, entity_inst_t> instances;
1941
1942 int r = librbd::cls_client::mirror_image_instance_list(
1943 &io_ctx, start_image_id, max, &instances);
1944 if (r < 0 && r != -ENOENT) {
1945 lderr(cct) << "failed to list mirror image instances: " << cpp_strerror(r)
1946 << dendl;
1947 return r;
1948 }
1949
1950 for (auto it : instances) {
1951 (*instance_ids)[it.first] = stringify(it.second.name.num());
1952 }
1953
1954 return 0;
1955}
1956
9f95a23c
TL
1957template <typename I>
1958int Mirror<I>::image_info_list(
1959 librados::IoCtx& io_ctx, mirror_image_mode_t *mode_filter,
1960 const std::string &start_id, size_t max,
1961 std::map<std::string, std::pair<mirror_image_mode_t,
1962 mirror_image_info_t>> *entries) {
1963 CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
1964 ldout(cct, 20) << "pool=" << io_ctx.get_pool_name() << ", mode_filter="
1965 << (mode_filter ? stringify(*mode_filter) : "null")
1966 << ", start_id=" << start_id << ", max=" << max << dendl;
1967
1968 std::string last_read = start_id;
1969 entries->clear();
1970
1971 while (entries->size() < max) {
1972 map<std::string, cls::rbd::MirrorImage> images;
1973 map<std::string, cls::rbd::MirrorImageStatus> statuses;
1974
1975 int r = librbd::cls_client::mirror_image_status_list(&io_ctx, last_read,
1976 max, &images,
1977 &statuses);
1978 if (r < 0 && r != -ENOENT) {
1979 lderr(cct) << "failed to list mirror image statuses: "
1980 << cpp_strerror(r) << dendl;
1981 return r;
1982 }
1983
1984 if (images.empty()) {
1985 break;
1986 }
1987
f67539c2 1988 AsioEngine asio_engine(io_ctx);
9f95a23c
TL
1989
1990 for (auto &it : images) {
1991 auto &image_id = it.first;
1992 auto &image = it.second;
1993 auto mode = static_cast<mirror_image_mode_t>(image.mode);
1994
1995 if ((mode_filter && mode != *mode_filter) ||
1996 image.state != cls::rbd::MIRROR_IMAGE_STATE_ENABLED) {
1997 continue;
1998 }
1999
2000 // need to call get_info for every image to retrieve promotion state
2001
2002 mirror_image_info_t info;
f67539c2
TL
2003 r = image_get_info(io_ctx, asio_engine.get_work_queue(), image_id, &info);
2004 if (r < 0) {
2005 continue;
2006 }
2007
2008 (*entries)[image_id] = std::make_pair(mode, info);
2009 if (entries->size() == max) {
2010 break;
9f95a23c
TL
2011 }
2012 }
2013
2014 last_read = images.rbegin()->first;
2015 }
2016
2017 return 0;
2018}
2019
2020template <typename I>
cd265ab1
TL
2021int Mirror<I>::image_snapshot_create(I *ictx, uint32_t flags,
2022 uint64_t *snap_id) {
2023 C_SaferCond ctx;
2024 Mirror<I>::image_snapshot_create(ictx, flags, snap_id, &ctx);
2025
2026 return ctx.wait();
2027}
2028
2029template <typename I>
2030void Mirror<I>::image_snapshot_create(I *ictx, uint32_t flags,
2031 uint64_t *snap_id, Context *on_finish) {
9f95a23c
TL
2032 CephContext *cct = ictx->cct;
2033 ldout(cct, 20) << "ictx=" << ictx << dendl;
2034
f67539c2
TL
2035 uint64_t snap_create_flags = 0;
2036 int r = util::snap_create_flags_api_to_internal(cct, flags,
2037 &snap_create_flags);
2038 if (r < 0) {
2039 on_finish->complete(r);
cd265ab1 2040 return;
9f95a23c
TL
2041 }
2042
cd265ab1 2043 auto on_refresh = new LambdaContext(
f67539c2 2044 [ictx, snap_create_flags, snap_id, on_finish](int r) {
cd265ab1
TL
2045 if (r < 0) {
2046 lderr(ictx->cct) << "refresh failed: " << cpp_strerror(r) << dendl;
2047 on_finish->complete(r);
2048 return;
2049 }
9f95a23c 2050
f67539c2
TL
2051 auto ctx = new C_ImageSnapshotCreate<I>(ictx, snap_create_flags, snap_id,
2052 on_finish);
cd265ab1
TL
2053 auto req = mirror::GetInfoRequest<I>::create(*ictx, &ctx->mirror_image,
2054 &ctx->promotion_state,
2055 &ctx->primary_mirror_uuid,
2056 ctx);
2057 req->send();
2058 });
9f95a23c 2059
cd265ab1
TL
2060 if (ictx->state->is_refresh_required()) {
2061 ictx->state->refresh(on_refresh);
2062 } else {
2063 on_refresh->complete(0);
2064 }
9f95a23c
TL
2065}
2066
7c673cae
FG
2067} // namespace api
2068} // namespace librbd
2069
2070template class librbd::api::Mirror<librbd::ImageCtx>;