]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/action/MirrorImage.cc
02fdbf3a7e0b72f92ee30294b6ab24657100337f
[ceph.git] / ceph / src / tools / rbd / action / MirrorImage.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2016 SUSE LINUX GmbH
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14 #include "tools/rbd/ArgumentTypes.h"
15 #include "tools/rbd/MirrorDaemonServiceInfo.h"
16 #include "tools/rbd/Shell.h"
17 #include "tools/rbd/Utils.h"
18 #include "include/stringify.h"
19 #include "common/config.h"
20 #include "common/errno.h"
21 #include "common/Formatter.h"
22 #include "common/TextTable.h"
23 #include "global/global_context.h"
24 #include <iostream>
25 #include <boost/program_options.hpp>
26
27 namespace rbd {
28 namespace action {
29 namespace mirror_image {
30
31 namespace at = argument_types;
32 namespace po = boost::program_options;
33
34 namespace {
35
36 int validate_mirroring_enabled(librbd::Image &image, bool snapshot = false) {
37 librbd::mirror_image_info_t mirror_image;
38 int r = image.mirror_image_get_info(&mirror_image, sizeof(mirror_image));
39 if (r < 0) {
40 std::cerr << "rbd: failed to retrieve mirror info: "
41 << cpp_strerror(r) << std::endl;
42 return r;
43 }
44
45 if (mirror_image.state != RBD_MIRROR_IMAGE_ENABLED) {
46 std::cerr << "rbd: mirroring not enabled on the image" << std::endl;
47 return -EINVAL;
48 }
49
50 if (snapshot) {
51 librbd::mirror_image_mode_t mode;
52 r = image.mirror_image_get_mode(&mode);
53 if (r < 0) {
54 std::cerr << "rbd: failed to retrieve mirror mode: "
55 << cpp_strerror(r) << std::endl;
56 return r;
57 }
58
59 if (mode != RBD_MIRROR_IMAGE_MODE_SNAPSHOT) {
60 std::cerr << "rbd: snapshot based mirroring not enabled on the image"
61 << std::endl;
62 return -EINVAL;
63 }
64 }
65
66 return 0;
67 }
68
69 } // anonymous namespace
70
71 void get_arguments(po::options_description *positional,
72 po::options_description *options) {
73 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
74 }
75
76 void get_arguments_enable(po::options_description *positional,
77 po::options_description *options) {
78 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
79 positional->add_options()
80 ("mode", "mirror image mode (journal or snapshot) [default: journal]");
81 }
82
83 void get_arguments_disable(po::options_description *positional,
84 po::options_description *options) {
85 options->add_options()
86 ("force", po::bool_switch(), "disable even if not primary");
87 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
88 }
89
90 int execute_enable_disable(const po::variables_map &vm, bool enable,
91 bool force) {
92 size_t arg_index = 0;
93 std::string pool_name;
94 std::string namespace_name;
95 std::string image_name;
96 std::string snap_name;
97 int r = utils::get_pool_image_snapshot_names(
98 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
99 &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
100 utils::SPEC_VALIDATION_NONE);
101 if (r < 0) {
102 return r;
103 }
104
105 librados::Rados rados;
106 librados::IoCtx io_ctx;
107 librbd::Image image;
108 r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
109 false, &rados, &io_ctx, &image);
110 if (r < 0) {
111 return r;
112 }
113
114 if (enable) {
115 librbd::mirror_image_mode_t mode = RBD_MIRROR_IMAGE_MODE_JOURNAL;
116 std::string mode_arg = utils::get_positional_argument(vm, arg_index++);
117 if (mode_arg == "journal") {
118 mode = RBD_MIRROR_IMAGE_MODE_JOURNAL;
119 } else if (mode_arg == "snapshot") {
120 mode = RBD_MIRROR_IMAGE_MODE_SNAPSHOT;
121 } else if (!mode_arg.empty()) {
122 std::cerr << "rbd: invalid mode name: " << mode_arg << std::endl;
123 return -EINVAL;
124 }
125 r = image.mirror_image_enable2(mode);
126 } else {
127 r = image.mirror_image_disable(force);
128 }
129 if (r < 0) {
130 return r;
131 }
132
133 std::cout << (enable ? "Mirroring enabled" : "Mirroring disabled")
134 << std::endl;
135
136 return 0;
137 }
138
139 int execute_disable(const po::variables_map &vm,
140 const std::vector<std::string> &ceph_global_init_args) {
141 return execute_enable_disable(vm, false, vm["force"].as<bool>());
142 }
143
144 int execute_enable(const po::variables_map &vm,
145 const std::vector<std::string> &ceph_global_init_args) {
146 return execute_enable_disable(vm, true, false);
147 }
148
149 void get_arguments_promote(po::options_description *positional,
150 po::options_description *options) {
151 options->add_options()
152 ("force", po::bool_switch(), "promote even if not cleanly demoted by remote cluster");
153 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
154 }
155
156 int execute_promote(const po::variables_map &vm,
157 const std::vector<std::string> &ceph_global_init_args) {
158 size_t arg_index = 0;
159 std::string pool_name;
160 std::string namespace_name;
161 std::string image_name;
162 std::string snap_name;
163 int r = utils::get_pool_image_snapshot_names(
164 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
165 &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
166 utils::SPEC_VALIDATION_NONE);
167 if (r < 0) {
168 return r;
169 }
170
171 bool force = vm["force"].as<bool>();
172
173 librados::Rados rados;
174 librados::IoCtx io_ctx;
175 librbd::Image image;
176 r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
177 false, &rados, &io_ctx, &image);
178 if (r < 0) {
179 return r;
180 }
181
182 r = validate_mirroring_enabled(image);
183 if (r < 0) {
184 return r;
185 }
186
187 r = image.mirror_image_promote(force);
188 if (r < 0) {
189 std::cerr << "rbd: error promoting image to primary" << std::endl;
190 return r;
191 }
192
193 std::cout << "Image promoted to primary" << std::endl;
194 return 0;
195 }
196
197 int execute_demote(const po::variables_map &vm,
198 const std::vector<std::string> &ceph_global_init_args) {
199 size_t arg_index = 0;
200 std::string pool_name;
201 std::string namespace_name;
202 std::string image_name;
203 std::string snap_name;
204 int r = utils::get_pool_image_snapshot_names(
205 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
206 &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
207 utils::SPEC_VALIDATION_NONE);
208 if (r < 0) {
209 return r;
210 }
211
212 librados::Rados rados;
213 librados::IoCtx io_ctx;
214 librbd::Image image;
215 r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
216 false, &rados, &io_ctx, &image);
217 if (r < 0) {
218 return r;
219 }
220
221 r = validate_mirroring_enabled(image);
222 if (r < 0) {
223 return r;
224 }
225
226 r = image.mirror_image_demote();
227 if (r < 0) {
228 std::cerr << "rbd: error demoting image to non-primary" << std::endl;
229 return r;
230 }
231
232 std::cout << "Image demoted to non-primary" << std::endl;
233 return 0;
234 }
235
236 int execute_resync(const po::variables_map &vm,
237 const std::vector<std::string> &ceph_global_init_args) {
238 size_t arg_index = 0;
239 std::string pool_name;
240 std::string namespace_name;
241 std::string image_name;
242 std::string snap_name;
243 int r = utils::get_pool_image_snapshot_names(
244 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
245 &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
246 utils::SPEC_VALIDATION_NONE);
247 if (r < 0) {
248 return r;
249 }
250
251 librados::Rados rados;
252 librados::IoCtx io_ctx;
253 librbd::Image image;
254 r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
255 false, &rados, &io_ctx, &image);
256 if (r < 0) {
257 return r;
258 }
259
260 r = validate_mirroring_enabled(image);
261 if (r < 0) {
262 return r;
263 }
264
265 r = image.mirror_image_resync();
266 if (r < 0) {
267 std::cerr << "rbd: error flagging image resync" << std::endl;
268 return r;
269 }
270
271 std::cout << "Flagged image for resync from primary" << std::endl;
272 return 0;
273 }
274
275 void get_status_arguments(po::options_description *positional,
276 po::options_description *options) {
277 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
278 at::add_format_options(options);
279 }
280
281 int execute_status(const po::variables_map &vm,
282 const std::vector<std::string> &ceph_global_init_args) {
283 at::Format::Formatter formatter;
284 int r = utils::get_formatter(vm, &formatter);
285 if (r < 0) {
286 return r;
287 }
288
289 size_t arg_index = 0;
290 std::string pool_name;
291 std::string namespace_name;
292 std::string image_name;
293 std::string snap_name;
294 r = utils::get_pool_image_snapshot_names(
295 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
296 &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
297 utils::SPEC_VALIDATION_NONE);
298 if (r < 0) {
299 return r;
300 }
301
302 librados::Rados rados;
303 librados::IoCtx io_ctx;
304 librbd::Image image;
305 r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
306 false, &rados, &io_ctx, &image);
307 if (r < 0) {
308 return r;
309 }
310
311 r = validate_mirroring_enabled(image);
312 if (r < 0) {
313 return r;
314 }
315
316 librados::IoCtx default_ns_io_ctx;
317 default_ns_io_ctx.dup(io_ctx);
318 default_ns_io_ctx.set_namespace("");
319
320 std::vector<librbd::mirror_peer_site_t> mirror_peers;
321 utils::get_mirror_peer_sites(default_ns_io_ctx, &mirror_peers);
322
323 std::map<std::string, std::string> peer_mirror_uuids_to_name;
324 utils::get_mirror_peer_mirror_uuids_to_names(mirror_peers,
325 &peer_mirror_uuids_to_name);
326
327 librbd::mirror_image_global_status_t status;
328 r = image.mirror_image_get_global_status(&status, sizeof(status));
329 if (r < 0) {
330 std::cerr << "rbd: failed to get status for image " << image_name << ": "
331 << cpp_strerror(r) << std::endl;
332 return r;
333 }
334
335 utils::populate_unknown_mirror_image_site_statuses(mirror_peers, &status);
336
337 std::string instance_id;
338 MirrorDaemonServiceInfo daemon_service_info(io_ctx);
339
340 librbd::mirror_image_site_status_t local_status;
341 int local_site_r = utils::get_local_mirror_image_status(
342 status, &local_status);
343 status.site_statuses.erase(
344 std::remove_if(status.site_statuses.begin(),
345 status.site_statuses.end(),
346 [](auto& status) {
347 return (status.mirror_uuid ==
348 RBD_MIRROR_IMAGE_STATUS_LOCAL_MIRROR_UUID);
349 }),
350 status.site_statuses.end());
351
352 if (local_site_r >= 0 && local_status.up) {
353 r = image.mirror_image_get_instance_id(&instance_id);
354 if (r == -EOPNOTSUPP) {
355 std::cerr << "rbd: newer release of Ceph OSDs required to map image "
356 << "to rbd-mirror daemon instance" << std::endl;
357 // not fatal
358 } else if (r < 0 && r != -ENOENT) {
359 std::cerr << "rbd: failed to get service id for image "
360 << image_name << ": " << cpp_strerror(r) << std::endl;
361 // not fatal
362 } else if (!instance_id.empty()) {
363 daemon_service_info.init();
364 }
365 }
366
367 std::vector<librbd::snap_info_t> snaps;
368 if (status.info.primary && status.info.state == RBD_MIRROR_IMAGE_ENABLED) {
369 librbd::mirror_image_mode_t mode = RBD_MIRROR_IMAGE_MODE_JOURNAL;
370 r = image.mirror_image_get_mode(&mode);
371 if (r < 0) {
372 std::cerr << "rbd: failed to retrieve mirror mode: "
373 << cpp_strerror(r) << std::endl;
374 // not fatal
375 }
376
377 if (mode == RBD_MIRROR_IMAGE_MODE_SNAPSHOT) {
378 image.snap_list(snaps);
379 snaps.erase(
380 remove_if(snaps.begin(),
381 snaps.end(),
382 [&image](const librbd::snap_info_t &snap) {
383 librbd::snap_namespace_type_t type;
384 int r = image.snap_get_namespace_type(snap.id, &type);
385 if (r < 0) {
386 return false;
387 }
388 return type != RBD_SNAP_NAMESPACE_TYPE_MIRROR;
389 }),
390 snaps.end());
391 }
392 }
393
394 auto mirror_service = daemon_service_info.get_by_instance_id(instance_id);
395
396 if (formatter != nullptr) {
397 formatter->open_object_section("image");
398 formatter->dump_string("name", image_name);
399 formatter->dump_string("global_id", status.info.global_id);
400 if (local_site_r >= 0) {
401 formatter->dump_string("state", utils::mirror_image_site_status_state(
402 local_status));
403 formatter->dump_string("description", local_status.description);
404 if (mirror_service != nullptr) {
405 mirror_service->dump_image(formatter);
406 }
407 formatter->dump_string("last_update", utils::timestr(
408 local_status.last_update));
409 }
410 if (!status.site_statuses.empty()) {
411 formatter->open_array_section("peer_sites");
412 for (auto& status : status.site_statuses) {
413 formatter->open_object_section("peer_site");
414
415 auto name_it = peer_mirror_uuids_to_name.find(status.mirror_uuid);
416 formatter->dump_string("site_name",
417 (name_it != peer_mirror_uuids_to_name.end() ? name_it->second : ""));
418 formatter->dump_string("mirror_uuids", status.mirror_uuid);
419
420 formatter->dump_string("state", utils::mirror_image_site_status_state(
421 status));
422 formatter->dump_string("description", status.description);
423 formatter->dump_string("last_update", utils::timestr(
424 status.last_update));
425 formatter->close_section(); // peer_site
426 }
427 formatter->close_section(); // peer_sites
428 }
429 if (!snaps.empty()) {
430 formatter->open_array_section("snapshots");
431 for (auto &snap : snaps) {
432 librbd::snap_mirror_namespace_t info;
433 r = image.snap_get_mirror_namespace(snap.id, &info, sizeof(info));
434 if (r < 0 ||
435 (info.state != RBD_SNAP_MIRROR_STATE_PRIMARY &&
436 info.state != RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED)) {
437 continue;
438 }
439 formatter->open_object_section("snapshot");
440 formatter->dump_unsigned("id", snap.id);
441 formatter->dump_string("name", snap.name);
442 formatter->dump_bool("demoted",
443 info.state == RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED);
444 formatter->open_array_section("mirror_peer_uuids");
445 for (auto &peer : info.mirror_peer_uuids) {
446 formatter->dump_string("peer_uuid", peer);
447 }
448 formatter->close_section(); // mirror_peer_uuids
449 formatter->close_section(); // snapshot
450 }
451 formatter->close_section(); // snapshots
452 }
453 formatter->close_section(); // image
454 formatter->flush(std::cout);
455 } else {
456 std::cout << image_name << ":\n"
457 << " global_id: " << status.info.global_id << "\n";
458 if (local_site_r >= 0) {
459 std::cout << " state: " << utils::mirror_image_site_status_state(
460 local_status) << "\n"
461 << " description: " << local_status.description << "\n";
462 if (mirror_service != nullptr) {
463 std::cout << " service: " <<
464 mirror_service->get_image_description() << "\n";
465 }
466 std::cout << " last_update: " << utils::timestr(
467 local_status.last_update) << std::endl;
468 }
469 if (!status.site_statuses.empty()) {
470 std::cout << " peer_sites:" << std::endl;
471
472 bool first_site = true;
473 for (auto& site : status.site_statuses) {
474 if (!first_site) {
475 std::cout << std::endl;
476 }
477 first_site = false;
478
479 auto name_it = peer_mirror_uuids_to_name.find(site.mirror_uuid);
480 std::cout << " name: "
481 << (name_it != peer_mirror_uuids_to_name.end() ?
482 name_it->second : site.mirror_uuid)
483 << std::endl
484 << " state: " << utils::mirror_image_site_status_state(
485 site) << std::endl
486 << " description: " << site.description << std::endl
487 << " last_update: " << utils::timestr(
488 site.last_update) << std::endl;
489 }
490 }
491 if (!snaps.empty()) {
492 std::cout << " snapshots:" << std::endl;
493
494 bool first_site = true;
495 for (auto &snap : snaps) {
496 librbd::snap_mirror_namespace_t info;
497 r = image.snap_get_mirror_namespace(snap.id, &info, sizeof(info));
498 if (r < 0 ||
499 (info.state != RBD_SNAP_MIRROR_STATE_PRIMARY &&
500 info.state != RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED)) {
501 continue;
502 }
503
504 if (!first_site) {
505 std::cout << std::endl;
506 }
507
508 first_site = false;
509 std::cout << " " << snap.id << " " << snap.name << " ("
510 << (info.state == RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED ?
511 "demoted " : "")
512 << "peer_uuids:[" << info.mirror_peer_uuids << "])";
513 }
514 std::cout << std::endl;
515 }
516 }
517
518 return 0;
519 }
520
521 int execute_snapshot(const po::variables_map &vm,
522 const std::vector<std::string> &ceph_global_init_args) {
523 size_t arg_index = 0;
524 std::string pool_name;
525 std::string namespace_name;
526 std::string image_name;
527 int r = utils::get_pool_image_snapshot_names(
528 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
529 &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE,
530 utils::SPEC_VALIDATION_NONE);
531 if (r < 0) {
532 return r;
533 }
534
535 librados::Rados rados;
536 librados::IoCtx io_ctx;
537 librbd::Image image;
538 r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
539 false, &rados, &io_ctx, &image);
540 if (r < 0) {
541 return r;
542 }
543
544 r = validate_mirroring_enabled(image, true);
545 if (r < 0) {
546 return r;
547 }
548
549 uint64_t snap_id;
550 r = image.mirror_image_create_snapshot(&snap_id);
551 if (r < 0) {
552 std::cerr << "rbd: error creating snapshot: " << cpp_strerror(r)
553 << std::endl;
554 return r;
555 }
556
557 std::cout << "Snapshot ID: " << snap_id << std::endl;
558 return 0;
559 }
560
561 Shell::Action action_enable(
562 {"mirror", "image", "enable"}, {},
563 "Enable RBD mirroring for an image.", "",
564 &get_arguments_enable, &execute_enable);
565 Shell::Action action_disable(
566 {"mirror", "image", "disable"}, {},
567 "Disable RBD mirroring for an image.", "",
568 &get_arguments_disable, &execute_disable);
569 Shell::Action action_promote(
570 {"mirror", "image", "promote"}, {},
571 "Promote an image to primary for RBD mirroring.", "",
572 &get_arguments_promote, &execute_promote);
573 Shell::Action action_demote(
574 {"mirror", "image", "demote"}, {},
575 "Demote an image to non-primary for RBD mirroring.", "",
576 &get_arguments, &execute_demote);
577 Shell::Action action_resync(
578 {"mirror", "image", "resync"}, {},
579 "Force resync to primary image for RBD mirroring.", "",
580 &get_arguments, &execute_resync);
581 Shell::Action action_status(
582 {"mirror", "image", "status"}, {},
583 "Show RBD mirroring status for an image.", "",
584 &get_status_arguments, &execute_status);
585 Shell::Action action_snapshot(
586 {"mirror", "image", "snapshot"}, {},
587 "Create RBD mirroring image snapshot.", "",
588 &get_arguments, &execute_snapshot);
589
590 } // namespace mirror_image
591 } // namespace action
592 } // namespace rbd