1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "tools/rbd/ArgumentTypes.h"
5 #include "tools/rbd/Shell.h"
6 #include "tools/rbd/Utils.h"
7 #include "common/Cond.h"
8 #include "common/Formatter.h"
9 #include "common/ceph_json.h"
10 #include "common/errno.h"
11 #include "common/safe_io.h"
12 #include "include/stringify.h"
15 #include <boost/program_options.hpp>
16 #include "cls/rbd/cls_rbd_client.h"
17 #include "cls/journal/cls_journal_types.h"
18 #include "cls/journal/cls_journal_client.h"
20 #include "journal/Journaler.h"
21 #include "journal/ReplayEntry.h"
22 #include "journal/ReplayHandler.h"
23 #include "journal/Settings.h"
24 #include "librbd/journal/Types.h"
30 namespace at
= argument_types
;
31 namespace po
= boost::program_options
;
33 static const std::string
JOURNAL_SPEC("journal-spec");
34 static const std::string
JOURNAL_NAME("journal");
35 static const std::string
DEST_JOURNAL_NAME("dest-journal");
37 void add_journal_option(po::options_description
*opt
,
38 at::ArgumentModifier modifier
) {
39 std::string name
= JOURNAL_NAME
;
40 std::string description
= at::get_description_prefix(modifier
) +
43 case at::ARGUMENT_MODIFIER_NONE
:
44 case at::ARGUMENT_MODIFIER_SOURCE
:
46 case at::ARGUMENT_MODIFIER_DEST
:
47 name
= DEST_JOURNAL_NAME
;
53 (name
.c_str(), po::value
<std::string
>(), description
.c_str());
56 void add_journal_spec_options(po::options_description
*pos
,
57 po::options_description
*opt
,
58 at::ArgumentModifier modifier
) {
61 ((get_name_prefix(modifier
) + JOURNAL_SPEC
).c_str(),
62 (get_description_prefix(modifier
) + "journal specification\n" +
63 "(example: [<pool-name>/[<namespace>/]]<journal-name>)").c_str());
64 add_pool_option(opt
, modifier
);
65 add_namespace_option(opt
, modifier
);
66 add_image_option(opt
, modifier
);
67 add_journal_option(opt
, modifier
);
70 int get_pool_journal_names(const po::variables_map
&vm
,
71 at::ArgumentModifier mod
,
72 size_t *spec_arg_index
,
73 std::string
*pool_name
,
74 std::string
*namespace_name
,
75 std::string
*journal_name
) {
76 std::string pool_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
77 at::DEST_POOL_NAME
: at::POOL_NAME
);
78 std::string namespace_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
79 at::DEST_NAMESPACE_NAME
: at::NAMESPACE_NAME
);
80 std::string image_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
81 at::DEST_IMAGE_NAME
: at::IMAGE_NAME
);
82 std::string journal_key
= (mod
== at::ARGUMENT_MODIFIER_DEST
?
83 DEST_JOURNAL_NAME
: JOURNAL_NAME
);
85 if (vm
.count(pool_key
) && pool_name
!= nullptr) {
86 *pool_name
= vm
[pool_key
].as
<std::string
>();
88 if (vm
.count(namespace_key
) && namespace_name
!= nullptr) {
89 *namespace_name
= vm
[namespace_key
].as
<std::string
>();
91 if (vm
.count(journal_key
) && journal_name
!= nullptr) {
92 *journal_name
= vm
[journal_key
].as
<std::string
>();
95 std::string image_name
;
96 if (vm
.count(image_key
)) {
97 image_name
= vm
[image_key
].as
<std::string
>();
101 if (journal_name
!= nullptr && !journal_name
->empty()) {
102 // despite the separate pool option,
103 // we can also specify them via the journal option
104 std::string
journal_name_copy(*journal_name
);
105 r
= extract_spec(journal_name_copy
, pool_name
, namespace_name
, journal_name
,
106 nullptr, utils::SPEC_VALIDATION_FULL
);
112 if (!image_name
.empty()) {
113 // despite the separate pool option,
114 // we can also specify them via the image option
115 std::string
image_name_copy(image_name
);
116 r
= extract_spec(image_name_copy
, pool_name
, namespace_name
, &image_name
,
117 nullptr, utils::SPEC_VALIDATION_NONE
);
123 if (journal_name
!= nullptr && spec_arg_index
!= nullptr &&
124 journal_name
->empty()) {
125 std::string spec
= utils::get_positional_argument(vm
, (*spec_arg_index
)++);
127 r
= extract_spec(spec
, pool_name
, namespace_name
, journal_name
, nullptr,
128 utils::SPEC_VALIDATION_FULL
);
135 if (pool_name
!= nullptr && pool_name
->empty()) {
136 *pool_name
= utils::get_default_pool_name();
139 if (pool_name
!= nullptr && namespace_name
!= nullptr &&
140 journal_name
!= nullptr && journal_name
->empty() && !image_name
.empty()) {
141 // Try to get journal name from image info.
142 librados::Rados rados
;
143 librados::IoCtx io_ctx
;
145 int r
= utils::init_and_open_image(*pool_name
, *namespace_name
, image_name
,
146 "", "", true, &rados
, &io_ctx
, &image
);
148 std::cerr
<< "rbd: failed to open image " << image_name
149 << " to get journal name: " << cpp_strerror(r
) << std::endl
;
154 r
= image
.features(&features
);
158 if ((features
& RBD_FEATURE_JOURNALING
) == 0) {
159 std::cerr
<< "rbd: journaling is not enabled for image " << image_name
163 *journal_name
= utils::image_id(image
);
166 if (journal_name
!= nullptr && journal_name
->empty()) {
167 std::string prefix
= at::get_description_prefix(mod
);
169 << (mod
== at::ARGUMENT_MODIFIER_DEST
? prefix
: std::string())
170 << "journal was not specified" << std::endl
;
177 static int do_show_journal_info(librados::Rados
& rados
, librados::IoCtx
& io_ctx
,
178 const std::string
& journal_id
, Formatter
*f
)
183 std::string header_oid
= ::journal::Journaler::header_oid(journal_id
);
184 std::string object_oid_prefix
= ::journal::Journaler::object_oid_prefix(
185 io_ctx
.get_id(), journal_id
);
190 cls::journal::client::get_immutable_metadata(io_ctx
, header_oid
, &order
,
191 &splay_width
, &pool_id
, &cond
);
194 std::cerr
<< "failed to get journal metadata: " << cpp_strerror(r
)
199 std::string object_pool_name
;
201 r
= rados
.pool_reverse_lookup(pool_id
, &object_pool_name
);
203 std::cerr
<< "error looking up pool name for pool_id=" << pool_id
<< ": "
204 << cpp_strerror(r
) << std::endl
;
209 f
->open_object_section("journal");
210 f
->dump_string("journal_id", journal_id
);
211 f
->dump_string("header_oid", header_oid
);
212 f
->dump_string("object_oid_prefix", object_oid_prefix
);
213 f
->dump_int("order", order
);
214 f
->dump_int("splay_width", splay_width
);
215 if (!object_pool_name
.empty()) {
216 f
->dump_string("object_pool", object_pool_name
);
221 std::cout
<< "rbd journal '" << journal_id
<< "':" << std::endl
;
222 std::cout
<< "\theader_oid: " << header_oid
<< std::endl
;
223 std::cout
<< "\tobject_oid_prefix: " << object_oid_prefix
<< std::endl
;
224 std::cout
<< "\torder: " << static_cast<int>(order
) << " ("
225 << byte_u_t(1ull << order
) << " objects)"<< std::endl
;
226 std::cout
<< "\tsplay_width: " << static_cast<int>(splay_width
) << std::endl
;
227 if (!object_pool_name
.empty()) {
228 std::cout
<< "\tobject_pool: " << object_pool_name
<< std::endl
;
234 static int do_show_journal_status(librados::IoCtx
& io_ctx
,
235 const std::string
& journal_id
, Formatter
*f
)
240 uint64_t minimum_set
;
242 std::set
<cls::journal::Client
> registered_clients
;
243 std::string oid
= ::journal::Journaler::header_oid(journal_id
);
245 cls::journal::client::get_mutable_metadata(io_ctx
, oid
, &minimum_set
,
246 &active_set
, ®istered_clients
,
250 std::cerr
<< "warning: failed to get journal metadata" << std::endl
;
255 f
->open_object_section("status");
256 f
->dump_unsigned("minimum_set", minimum_set
);
257 f
->dump_unsigned("active_set", active_set
);
258 f
->open_array_section("registered_clients");
259 for (std::set
<cls::journal::Client
>::iterator c
=
260 registered_clients
.begin(); c
!= registered_clients
.end(); ++c
) {
261 f
->open_object_section("client");
269 std::cout
<< "minimum_set: " << minimum_set
<< std::endl
;
270 std::cout
<< "active_set: " << active_set
<< std::endl
;
271 std::cout
<< "registered clients: " << std::endl
;
272 for (std::set
<cls::journal::Client
>::iterator c
=
273 registered_clients
.begin(); c
!= registered_clients
.end(); ++c
) {
274 std::cout
<< "\t" << *c
<< std::endl
;
280 static int do_reset_journal(librados::IoCtx
& io_ctx
,
281 const std::string
& journal_id
)
283 // disable/re-enable journaling to delete/re-create the journal
284 // to properly handle mirroring constraints
285 std::string image_name
;
286 int r
= librbd::cls_client::dir_get_name(&io_ctx
, RBD_DIRECTORY
, journal_id
,
289 std::cerr
<< "failed to locate journal's image: " << cpp_strerror(r
)
295 r
= utils::open_image(io_ctx
, image_name
, false, &image
);
297 std::cerr
<< "failed to open image: " << cpp_strerror(r
) << std::endl
;
301 r
= image
.update_features(RBD_FEATURE_JOURNALING
, false);
303 std::cerr
<< "failed to disable image journaling: " << cpp_strerror(r
)
308 r
= image
.update_features(RBD_FEATURE_JOURNALING
, true);
310 std::cerr
<< "failed to re-enable image journaling: " << cpp_strerror(r
)
317 static int do_disconnect_journal_client(librados::IoCtx
& io_ctx
,
318 const std::string
& journal_id
,
319 const std::string
& client_id
)
324 uint64_t minimum_set
;
326 std::set
<cls::journal::Client
> registered_clients
;
327 std::string oid
= ::journal::Journaler::header_oid(journal_id
);
329 cls::journal::client::get_mutable_metadata(io_ctx
, oid
, &minimum_set
,
330 &active_set
, ®istered_clients
,
334 std::cerr
<< "warning: failed to get journal metadata" << std::endl
;
338 static const std::string
IMAGE_CLIENT_ID("");
341 for (auto &c
: registered_clients
) {
342 if (c
.id
== IMAGE_CLIENT_ID
|| (!client_id
.empty() && client_id
!= c
.id
)) {
345 r
= cls::journal::client::client_update_state(io_ctx
, oid
, c
.id
,
346 cls::journal::CLIENT_STATE_DISCONNECTED
);
348 std::cerr
<< "warning: failed to disconnect client " << c
.id
<< ": "
349 << cpp_strerror(r
) << std::endl
;
352 std::cout
<< "client " << c
.id
<< " disconnected" << std::endl
;
357 if (!client_id
.empty()) {
358 std::cerr
<< "warning: client " << client_id
<< " is not registered"
361 std::cerr
<< "no registered clients to disconnect" << std::endl
;
367 r
= io_ctx
.notify2(oid
, bl
, 5000, NULL
);
369 std::cerr
<< "warning: failed to notify state change:" << ": "
370 << cpp_strerror(r
) << std::endl
;
377 class Journaler
: public ::journal::Journaler
{
379 Journaler(librados::IoCtx
& io_ctx
, const std::string
& journal_id
,
380 const std::string
&client_id
) :
381 ::journal::Journaler(io_ctx
, journal_id
, client_id
, {}) {
387 // TODO register with librbd payload
388 r
= register_client(bufferlist());
390 std::cerr
<< "failed to register client: " << cpp_strerror(r
)
397 ::journal::Journaler::init(&cond
);
400 std::cerr
<< "failed to initialize journal: " << cpp_strerror(r
)
402 (void) unregister_client();
410 int r
= unregister_client();
412 std::cerr
<< "rbd: failed to unregister journal client: "
413 << cpp_strerror(r
) << std::endl
;
415 ::journal::Journaler::shut_down();
421 class JournalPlayer
{
423 JournalPlayer(librados::IoCtx
& io_ctx
, const std::string
& journal_id
,
424 const std::string
&client_id
) :
425 m_journaler(io_ctx
, journal_id
, client_id
),
430 virtual ~JournalPlayer() {}
435 r
= m_journaler
.init();
440 ReplayHandler
replay_handler(this);
442 m_journaler
.start_replay(&replay_handler
);
446 std::cerr
<< "rbd: failed to process journal: " << cpp_strerror(r
)
456 return m_journaler
.shut_down();
460 struct ReplayHandler
: public ::journal::ReplayHandler
{
461 JournalPlayer
*journal
;
462 explicit ReplayHandler(JournalPlayer
*_journal
) : journal(_journal
) {}
464 void get() override
{}
465 void put() override
{}
467 void handle_entries_available() override
{
468 journal
->handle_replay_ready();
470 void handle_complete(int r
) override
{
471 journal
->handle_replay_complete(r
);
475 void handle_replay_ready() {
478 ::journal::ReplayEntry replay_entry
;
480 if (!m_journaler
.try_pop_front(&replay_entry
, &tag_id
)) {
484 r
= process_entry(replay_entry
, tag_id
);
491 virtual int process_entry(::journal::ReplayEntry replay_entry
,
492 uint64_t tag_id
) = 0;
494 void handle_replay_complete(int r
) {
495 if (m_r
== 0 && r
< 0) {
498 m_journaler
.stop_replay(&m_cond
);
501 Journaler m_journaler
;
506 static int inspect_entry(bufferlist
& data
,
507 librbd::journal::EventEntry
& event_entry
,
510 auto it
= data
.cbegin();
511 decode(event_entry
, it
);
512 } catch (const buffer::error
&err
) {
513 std::cerr
<< "failed to decode event entry: " << err
.what() << std::endl
;
517 JSONFormatter
f(true);
518 f
.open_object_section("event_entry");
519 event_entry
.dump(&f
);
526 class JournalInspector
: public JournalPlayer
{
528 JournalInspector(librados::IoCtx
& io_ctx
, const std::string
& journal_id
,
530 JournalPlayer(io_ctx
, journal_id
, "INSPECT"),
535 int exec() override
{
536 int r
= JournalPlayer::exec();
543 Stats() : total(0), error(0) {}
546 std::cout
<< "Summary:" << std::endl
547 << " " << total
<< " entries inspected, " << error
<< " errors"
555 int process_entry(::journal::ReplayEntry replay_entry
,
556 uint64_t tag_id
) override
{
559 std::cout
<< "Entry: tag_id=" << tag_id
<< ", commit_tid="
560 << replay_entry
.get_commit_tid() << std::endl
;
562 bufferlist data
= replay_entry
.get_data();
563 librbd::journal::EventEntry event_entry
;
564 int r
= inspect_entry(data
, event_entry
, m_verbose
);
576 static int do_inspect_journal(librados::IoCtx
& io_ctx
,
577 const std::string
& journal_id
,
579 JournalInspector
inspector(io_ctx
, journal_id
, verbose
);
580 int r
= inspector
.exec();
582 inspector
.shut_down();
586 r
= inspector
.shut_down();
599 ExportEntry() : tag_id(0), commit_tid(0), type(0), entry() {}
601 ExportEntry(uint64_t tag_id
, uint64_t commit_tid
, int type
,
602 const bufferlist
& entry
)
603 : tag_id(tag_id
), commit_tid(commit_tid
), type(type
), entry(entry
) {
606 void dump(Formatter
*f
) const {
607 ::encode_json("tag_id", tag_id
, f
);
608 ::encode_json("commit_tid", commit_tid
, f
);
609 ::encode_json("type", type
, f
);
610 ::encode_json("entry", entry
, f
);
613 void decode_json(JSONObj
*obj
) {
614 JSONDecoder::decode_json("tag_id", tag_id
, obj
);
615 JSONDecoder::decode_json("commit_tid", commit_tid
, obj
);
616 JSONDecoder::decode_json("type", type
, obj
);
617 JSONDecoder::decode_json("entry", entry
, obj
);
621 class JournalExporter
: public JournalPlayer
{
623 JournalExporter(librados::IoCtx
& io_ctx
, const std::string
& journal_id
,
624 int fd
, bool no_error
, bool verbose
) :
625 JournalPlayer(io_ctx
, journal_id
, "EXPORT"),
626 m_journal_id(journal_id
),
628 m_no_error(no_error
),
633 int exec() override
{
634 std::string
header("# journal_id: " + m_journal_id
+ "\n");
636 r
= safe_write(m_fd
, header
.c_str(), header
.size());
638 std::cerr
<< "rbd: failed to write to export file: " << cpp_strerror(r
)
642 r
= JournalPlayer::exec();
649 Stats() : total(0), error(0) {}
652 std::cout
<< total
<< " entries processed, " << error
<< " errors"
660 int process_entry(::journal::ReplayEntry replay_entry
,
661 uint64_t tag_id
) override
{
664 bufferlist entry
= replay_entry
.get_data();
665 librbd::journal::EventEntry event_entry
;
666 int r
= inspect_entry(entry
, event_entry
, m_verbose
);
670 return m_no_error
? 0 : r
;
672 type
= event_entry
.get_event_type();
674 ExportEntry
export_entry(tag_id
, replay_entry
.get_commit_tid(), type
,
677 ::encode_json("event_entry", export_entry
, &f
);
678 std::ostringstream oss
;
680 std::string objstr
= oss
.str();
681 std::string header
= stringify(objstr
.size()) + " ";
682 r
= safe_write(m_fd
, header
.c_str(), header
.size());
684 r
= safe_write(m_fd
, objstr
.c_str(), objstr
.size());
687 r
= safe_write(m_fd
, "\n", 1);
690 std::cerr
<< "rbd: failed to write to export file: " << cpp_strerror(r
)
698 std::string m_journal_id
;
705 static int do_export_journal(librados::IoCtx
& io_ctx
,
706 const std::string
& journal_id
,
707 const std::string
& path
,
708 bool no_error
, bool verbose
) {
711 bool to_stdout
= path
== "-";
715 fd
= open(path
.c_str(), O_WRONLY
| O_CREAT
| O_EXCL
, 0644);
718 std::cerr
<< "rbd: error creating " << path
<< std::endl
;
721 #ifdef HAVE_POSIX_FADVISE
722 posix_fadvise(fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
726 JournalExporter
exporter(io_ctx
, journal_id
, fd
, no_error
, verbose
);
733 int shut_down_r
= exporter
.shut_down();
734 if (r
== 0 && shut_down_r
< 0) {
741 class JournalImporter
{
743 JournalImporter(librados::IoCtx
& io_ctx
, const std::string
& journal_id
,
744 int fd
, bool no_error
, bool verbose
) :
745 m_journaler(io_ctx
, journal_id
, "IMPORT"),
747 m_no_error(no_error
),
751 bool read_entry(bufferlist
& bl
, int& r
) {
752 // Entries are stored in the file using the following format:
754 // # Optional comments
755 // NNN {json encoded entry}
758 // Where NNN is the encoded entry size.
761 // Skip line feed and comments (lines started with #).
762 while ((r
= safe_read_exact(m_fd
, buf
, 1)) == 0) {
763 if (buf
[0] == '\n') {
765 } else if (buf
[0] == '#') {
766 while ((r
= safe_read_exact(m_fd
, buf
, 1)) == 0) {
767 if (buf
[0] == '\n') {
781 // Read entry size to buf.
782 if (!isdigit(buf
[0])) {
784 std::cerr
<< "rbd: import data invalid format (digit expected)"
788 for (size_t i
= 1; i
< sizeof(buf
); i
++) {
789 r
= safe_read_exact(m_fd
, buf
+ i
, 1);
791 std::cerr
<< "rbd: error reading import data" << std::endl
;
794 if (!isdigit(buf
[i
])) {
797 std::cerr
<< "rbd: import data invalid format (space expected)"
805 int entry_size
= atoi(buf
);
806 if (entry_size
== 0) {
808 std::cerr
<< "rbd: import data invalid format (zero entry size)"
812 ceph_assert(entry_size
> 0);
814 r
= bl
.read_fd(m_fd
, entry_size
);
816 std::cerr
<< "rbd: error reading from stdin: " << cpp_strerror(r
)
820 if (r
!= entry_size
) {
821 std::cerr
<< "rbd: error reading from stdin: truncated"
831 int r
= m_journaler
.init();
835 m_journaler
.start_append(0);
841 while (read_entry(bl
, r
)) {
845 if (!p
.parse(bl
.c_str(), bl
.length())) {
846 std::cerr
<< "rbd: error parsing input (entry " << n
<< ")"
858 decode_json_obj(e
, &p
);
859 } catch (JSONDecoder::err
& err
) {
860 std::cerr
<< "rbd: error json decoding import data (entry " << n
<< "):"
861 << err
.message
<< std::endl
;
870 librbd::journal::EventEntry event_entry
;
871 r
= inspect_entry(e
.entry
, event_entry
, m_verbose
);
873 std::cerr
<< "rbd: corrupted entry " << n
<< ": tag_tid=" << e
.tag_id
874 << ", commit_tid=" << e
.commit_tid
<< std::endl
;
882 m_journaler
.append(e
.tag_id
, e
.entry
);
886 std::cout
<< n
<< " entries processed, " << error_count
<< " errors" << std::endl
;
888 std::cout
<< "Waiting for journal append to complete..." << std::endl
;
891 m_journaler
.stop_append(&cond
);
895 std::cerr
<< "failed to append journal: " << cpp_strerror(r
) << std::endl
;
898 if (r1
< 0 && r
== 0) {
905 return m_journaler
.shut_down();
909 Journaler m_journaler
;
915 static int do_import_journal(librados::IoCtx
& io_ctx
,
916 const std::string
& journal_id
,
917 const std::string
& path
,
918 bool no_error
, bool verbose
) {
922 bool from_stdin
= path
== "-";
926 if ((fd
= open(path
.c_str(), O_RDONLY
)) < 0) {
928 std::cerr
<< "rbd: error opening " << path
<< std::endl
;
931 #ifdef HAVE_POSIX_FADVISE
932 posix_fadvise(fd
, 0, 0, POSIX_FADV_SEQUENTIAL
);
936 JournalImporter
importer(io_ctx
, journal_id
, fd
, no_error
, verbose
);
943 int shut_down_r
= importer
.shut_down();
944 if (r
== 0 && shut_down_r
< 0) {
951 void get_info_arguments(po::options_description
*positional
,
952 po::options_description
*options
) {
953 add_journal_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
954 at::add_format_options(options
);
957 int execute_info(const po::variables_map
&vm
,
958 const std::vector
<std::string
> &ceph_global_init_args
) {
959 size_t arg_index
= 0;
960 std::string pool_name
;
961 std::string namespace_name
;
962 std::string journal_name
;
963 int r
= get_pool_journal_names(vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
,
964 &pool_name
, &namespace_name
, &journal_name
);
969 at::Format::Formatter formatter
;
970 r
= utils::get_formatter(vm
, &formatter
);
975 librados::Rados rados
;
976 librados::IoCtx io_ctx
;
977 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
982 r
= do_show_journal_info(rados
, io_ctx
, journal_name
, formatter
.get());
984 std::cerr
<< "rbd: journal info: " << cpp_strerror(r
) << std::endl
;
991 void get_status_arguments(po::options_description
*positional
,
992 po::options_description
*options
) {
993 add_journal_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
994 at::add_format_options(options
);
997 int execute_status(const po::variables_map
&vm
,
998 const std::vector
<std::string
> &ceph_global_init_args
) {
999 size_t arg_index
= 0;
1000 std::string pool_name
;
1001 std::string namespace_name
;
1002 std::string journal_name
;
1003 int r
= get_pool_journal_names(vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
,
1004 &pool_name
, &namespace_name
, &journal_name
);
1009 at::Format::Formatter formatter
;
1010 r
= utils::get_formatter(vm
, &formatter
);
1015 librados::Rados rados
;
1016 librados::IoCtx io_ctx
;
1017 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
1022 r
= do_show_journal_status(io_ctx
, journal_name
, formatter
.get());
1024 std::cerr
<< "rbd: journal status: " << cpp_strerror(r
) << std::endl
;
1030 void get_reset_arguments(po::options_description
*positional
,
1031 po::options_description
*options
) {
1032 add_journal_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
1035 int execute_reset(const po::variables_map
&vm
,
1036 const std::vector
<std::string
> &ceph_global_init_args
) {
1037 size_t arg_index
= 0;
1038 std::string pool_name
;
1039 std::string namespace_name
;
1040 std::string journal_name
;
1041 int r
= get_pool_journal_names(vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
,
1042 &pool_name
, &namespace_name
, &journal_name
);
1047 librados::Rados rados
;
1048 librados::IoCtx io_ctx
;
1049 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
1054 r
= do_reset_journal(io_ctx
, journal_name
);
1056 std::cerr
<< "rbd: journal reset: " << cpp_strerror(r
) << std::endl
;
1062 void get_client_disconnect_arguments(po::options_description
*positional
,
1063 po::options_description
*options
) {
1064 add_journal_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
1065 options
->add_options()
1066 ("client-id", po::value
<std::string
>(),
1067 "client ID (or leave unspecified to disconnect all)");
1070 int execute_client_disconnect(const po::variables_map
&vm
,
1071 const std::vector
<std::string
> &ceph_global_init_args
) {
1072 size_t arg_index
= 0;
1073 std::string pool_name
;
1074 std::string namespace_name
;
1075 std::string journal_name
;
1076 int r
= get_pool_journal_names(vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
,
1077 &pool_name
, &namespace_name
, &journal_name
);
1082 std::string client_id
;
1083 if (vm
.count("client-id")) {
1084 client_id
= vm
["client-id"].as
<std::string
>();
1087 librados::Rados rados
;
1088 librados::IoCtx io_ctx
;
1089 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
1094 r
= do_disconnect_journal_client(io_ctx
, journal_name
, client_id
);
1096 std::cerr
<< "rbd: journal client disconnect: " << cpp_strerror(r
)
1103 void get_inspect_arguments(po::options_description
*positional
,
1104 po::options_description
*options
) {
1105 add_journal_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_NONE
);
1106 at::add_verbose_option(options
);
1109 int execute_inspect(const po::variables_map
&vm
,
1110 const std::vector
<std::string
> &ceph_global_init_args
) {
1111 size_t arg_index
= 0;
1112 std::string pool_name
;
1113 std::string namespace_name
;
1114 std::string journal_name
;
1115 int r
= get_pool_journal_names(vm
, at::ARGUMENT_MODIFIER_NONE
, &arg_index
,
1116 &pool_name
, &namespace_name
, &journal_name
);
1121 librados::Rados rados
;
1122 librados::IoCtx io_ctx
;
1123 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
1128 r
= do_inspect_journal(io_ctx
, journal_name
, vm
[at::VERBOSE
].as
<bool>());
1130 std::cerr
<< "rbd: journal inspect: " << cpp_strerror(r
) << std::endl
;
1136 void get_export_arguments(po::options_description
*positional
,
1137 po::options_description
*options
) {
1138 add_journal_spec_options(positional
, options
,
1139 at::ARGUMENT_MODIFIER_SOURCE
);
1140 at::add_path_options(positional
, options
,
1141 "export file (or '-' for stdout)");
1142 at::add_verbose_option(options
);
1143 at::add_no_error_option(options
);
1146 int execute_export(const po::variables_map
&vm
,
1147 const std::vector
<std::string
> &ceph_global_init_args
) {
1148 size_t arg_index
= 0;
1149 std::string pool_name
;
1150 std::string namespace_name
;
1151 std::string journal_name
;
1152 int r
= get_pool_journal_names(vm
, at::ARGUMENT_MODIFIER_SOURCE
, &arg_index
,
1153 &pool_name
, &namespace_name
, &journal_name
);
1159 r
= utils::get_path(vm
, &arg_index
, &path
);
1164 librados::Rados rados
;
1165 librados::IoCtx io_ctx
;
1166 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
1171 r
= do_export_journal(io_ctx
, journal_name
, path
, vm
[at::NO_ERROR
].as
<bool>(),
1172 vm
[at::VERBOSE
].as
<bool>());
1174 std::cerr
<< "rbd: journal export: " << cpp_strerror(r
) << std::endl
;
1180 void get_import_arguments(po::options_description
*positional
,
1181 po::options_description
*options
) {
1182 at::add_path_options(positional
, options
,
1183 "import file (or '-' for stdin)");
1184 add_journal_spec_options(positional
, options
, at::ARGUMENT_MODIFIER_DEST
);
1185 at::add_verbose_option(options
);
1186 at::add_no_error_option(options
);
1189 int execute_import(const po::variables_map
&vm
,
1190 const std::vector
<std::string
> &ceph_global_init_args
) {
1192 size_t arg_index
= 0;
1193 int r
= utils::get_path(vm
, &arg_index
, &path
);
1198 std::string pool_name
;
1199 std::string namespace_name
;
1200 std::string journal_name
;
1201 r
= get_pool_journal_names(vm
, at::ARGUMENT_MODIFIER_DEST
, &arg_index
,
1202 &pool_name
, &namespace_name
, &journal_name
);
1207 librados::Rados rados
;
1208 librados::IoCtx io_ctx
;
1209 r
= utils::init(pool_name
, namespace_name
, &rados
, &io_ctx
);
1214 r
= do_import_journal(io_ctx
, journal_name
, path
, vm
[at::NO_ERROR
].as
<bool>(),
1215 vm
[at::VERBOSE
].as
<bool>());
1217 std::cerr
<< "rbd: journal import: " << cpp_strerror(r
) << std::endl
;
1223 Shell::Action
action_info(
1224 {"journal", "info"}, {}, "Show information about image journal.", "",
1225 &get_info_arguments
, &execute_info
);
1227 Shell::Action
action_status(
1228 {"journal", "status"}, {}, "Show status of image journal.", "",
1229 &get_status_arguments
, &execute_status
);
1231 Shell::Action
action_reset(
1232 {"journal", "reset"}, {}, "Reset image journal.", "",
1233 &get_reset_arguments
, &execute_reset
);
1235 Shell::Action
action_inspect(
1236 {"journal", "inspect"}, {}, "Inspect image journal for structural errors.", "",
1237 &get_inspect_arguments
, &execute_inspect
);
1239 Shell::Action
action_export(
1240 {"journal", "export"}, {}, "Export image journal.", "",
1241 &get_export_arguments
, &execute_export
);
1243 Shell::Action
action_import(
1244 {"journal", "import"}, {}, "Import image journal.", "",
1245 &get_import_arguments
, &execute_import
);
1247 Shell::Action
action_disconnect(
1248 {"journal", "client", "disconnect"}, {},
1249 "Flag image journal client as disconnected.", "",
1250 &get_client_disconnect_arguments
, &execute_client_disconnect
);
1252 } // namespace journal
1253 } // namespace action