1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2012 Inktank, Inc.
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.
13 #include <boost/program_options/variables_map.hpp>
14 #include <boost/program_options/parsers.hpp>
15 #include <boost/scope_exit.hpp>
20 #include "common/Formatter.h"
21 #include "common/errno.h"
23 #include "auth/KeyRing.h"
24 #include "auth/cephx/CephxKeyServer.h"
25 #include "global/global_init.h"
26 #include "include/stringify.h"
27 #include "mgr/mgr_commands.h"
28 #include "mon/AuthMonitor.h"
29 #include "mon/MonitorDBStore.h"
30 #include "mon/Paxos.h"
31 #include "mon/MonMap.h"
32 #include "mds/FSMap.h"
33 #include "mon/MgrMap.h"
34 #include "osd/OSDMap.h"
35 #include "crush/CrushCompiler.h"
36 #include "mon/CreatingPGs.h"
38 namespace po
= boost::program_options
;
44 MonitorDBStore::TransactionRef t
;
46 explicit TraceIter(string fname
) : fd(-1), idx(-1) {
47 fd
= ::open(fname
.c_str(), O_RDONLY
);
48 t
.reset(new MonitorDBStore::Transaction
);
53 MonitorDBStore::TransactionRef
cur() {
57 unsigned num() { return idx
; }
61 int r
= bl
.read_fd(fd
, 6);
63 std::cerr
<< "Got error: " << cpp_strerror(r
) << " on read_fd"
68 } else if ((unsigned)r
< 6) {
69 std::cerr
<< "short read" << std::endl
;
74 bufferlist::iterator bliter
= bl
.begin();
76 ::decode(ver
, bliter
);
77 ::decode(ver2
, bliter
);
79 ::decode(len
, bliter
);
80 r
= bl
.read_fd(fd
, len
);
82 std::cerr
<< "Got error: " << cpp_strerror(r
) << " on read_fd"
87 } else if ((unsigned)r
< len
) {
88 std::cerr
<< "short read" << std::endl
;
94 t
.reset(new MonitorDBStore::Transaction
);
110 po::options_description
*desc
, /// < visible options description
111 po::options_description
*hidden_desc
, /// < hidden options description
112 po::positional_options_description
*positional
, /// < positional args
113 vector
<string
> &cmd_args
, /// < arguments to be parsed
114 po::variables_map
*vm
/// > post-parsing variable map
117 // desc_all will aggregate all visible and hidden options for parsing.
119 // From boost's program_options point of view, there is absolutely no
120 // distinction between 'desc' and 'hidden_desc'. This is a distinction
121 // that is only useful to us: 'desc' is whatever we are willing to show
122 // on 'usage()', whereas 'hidden_desc' refers to parameters we wish to
123 // take advantage of but do not wish to show on 'usage()'.
125 // For example, consider that program_options matches positional arguments
126 // (specified via 'positional') against the paramenters defined on a
127 // given 'po::options_description' class. This is performed below,
128 // supplying both the description and the positional arguments to the
129 // parser. However, we do not want the parameters that are mapped to
130 // positional arguments to be shown on usage, as that makes for ugly and
131 // confusing usage messages. Therefore we dissociate the options'
132 // description that is to be used as an aid to the user from those options
133 // that are nothing but useful for internal purposes (i.e., mapping options
134 // to positional arguments). We still need to aggregate them before parsing
135 // and that's what 'desc_all' is all about.
138 assert(desc
!= NULL
);
140 po::options_description desc_all
;
142 if (hidden_desc
!= NULL
)
143 desc_all
.add(*hidden_desc
);
146 po::command_line_parser parser
= po::command_line_parser(cmd_args
).
150 parser
= parser
.positional(*positional
);
153 po::parsed_options parsed
= parser
.run();
154 po::store(parsed
, *vm
);
156 } catch (po::error
&e
) {
157 std::cerr
<< "error: " << e
.what() << std::endl
;
165 * usage: ceph-monstore-tool <store-path> <command> [options]
169 * store-copy < --out arg >
172 * getmonmap < --out arg [ --version arg ] >
173 * getosdmap < --out arg [ --version arg ] >
174 * dump-paxos <--dump-start VER> <--dump-end VER>
175 * dump-trace < --trace-file arg >
182 * ceph-monstore-tool PATH CMD [options]
184 * ceph-monstore-tool PATH store-copy <PATH2 | -o PATH2>
185 * ceph-monstore-tool PATH dump-keys
186 * ceph-monstore-tool PATH compact
187 * ceph-monstore-tool PATH get monmap [VER]
188 * ceph-monstore-tool PATH get osdmap [VER]
189 * ceph-monstore-tool PATH dump-paxos STARTVER ENDVER
193 void usage(const char *n
, po::options_description
&d
)
196 "usage: " << n
<< " <store-path> <cmd> [args|options]\n"
199 << " store-copy PATH copies store to PATH\n"
200 << " compact compacts the store\n"
201 << " get monmap [-- options] get monmap (version VER if specified)\n"
202 << " (default: last committed)\n"
203 << " get osdmap [-- options] get osdmap (version VER if specified)\n"
204 << " (default: last committed)\n"
205 << " get mdsmap [-- options] get mdsmap (version VER if specified)\n"
206 << " (default: last committed)\n"
207 << " get mgr [-- options] get mgr map (version VER if specified)\n"
208 << " (default: last committed)\n"
209 << " get crushmap [-- options] get crushmap (version VER if specified)\n"
210 << " (default: last committed)\n"
211 << " show-versions [-- options] show the first&last committed version of map\n"
212 << " (show-versions -- --help for more info)\n"
213 << " dump-keys dumps store keys to FILE\n"
214 << " (default: stdout)\n"
215 << " dump-paxos [-- options] dump paxos transactions\n"
216 << " (dump-paxos -- --help for more info)\n"
217 << " dump-trace FILE [-- options] dump contents of trace file FILE\n"
218 << " (dump-trace -- --help for more info)\n"
219 << " replay-trace FILE [-- options] replay trace from FILE\n"
220 << " (replay-trace -- --help for more info)\n"
221 << " random-gen [-- options] add randomly generated ops to the store\n"
222 << " (random-gen -- --help for more info)\n"
223 << " rewrite-crush [-- options] add a rewrite commit to the store\n"
224 << " (rewrite-crush -- --help for more info)\n"
225 << " rebuild rebuild store\n"
226 << " (rebuild -- --help for more info)\n"
228 std::cerr
<< d
<< std::endl
;
230 << "\nPlease Note:\n"
231 << "* Ceph-specific options should be in the format --option-name=VAL\n"
232 << " (specifically, do not forget the '='!!)\n"
233 << "* Command-specific options need to be passed after a '--'\n"
234 << " e.g., 'get monmap -- --version 10 --out /tmp/foo'"
238 int update_osdmap(MonitorDBStore
& store
, version_t ver
, bool copy
,
239 ceph::shared_ptr
<CrushWrapper
> crush
,
240 MonitorDBStore::Transaction
* t
) {
241 const string
prefix("osdmap");
246 r
= store
.get(prefix
, store
.combine_strings("full", ver
), bl
);
248 std::cerr
<< "Error getting full map: " << cpp_strerror(r
) << std::endl
;
253 osdmap
.crush
= crush
;
258 // be consistent with OSDMonitor::update_from_paxos()
259 osdmap
.encode(bl
, CEPH_FEATURES_ALL
|CEPH_FEATURE_RESERVED
);
260 t
->put(prefix
, store
.combine_strings("full", osdmap
.get_epoch()), bl
);
263 OSDMap::Incremental inc
;
265 inc
.epoch
= osdmap
.get_epoch();
266 inc
.fsid
= osdmap
.get_fsid();
269 r
= store
.get(prefix
, ver
, bl
);
271 std::cerr
<< "Error getting inc map: " << cpp_strerror(r
) << std::endl
;
274 OSDMap::Incremental
inc(bl
);
275 if (inc
.crush
.length()) {
277 crush
->encode(inc
.crush
, CEPH_FEATURES_SUPPORTED_DEFAULT
);
279 if (inc
.fullmap
.length()) {
281 fullmap
.decode(inc
.fullmap
);
282 fullmap
.crush
= crush
;
284 fullmap
.encode(inc
.fullmap
);
287 assert(osdmap
.have_crc());
288 inc
.full_crc
= osdmap
.get_crc();
290 // be consistent with OSDMonitor::update_from_paxos()
291 inc
.encode(bl
, CEPH_FEATURES_ALL
|CEPH_FEATURE_RESERVED
);
292 t
->put(prefix
, inc
.epoch
, bl
);
296 int rewrite_transaction(MonitorDBStore
& store
, int version
,
297 const string
& crush_file
,
298 MonitorDBStore::Transaction
* t
) {
299 const string
prefix("osdmap");
301 // calc the known-good epoch
302 version_t last_committed
= store
.get(prefix
, "last_committed");
303 version_t good_version
= 0;
305 if (last_committed
>= (unsigned)-version
) {
306 good_version
= last_committed
+ version
;
308 std::cerr
<< "osdmap-version is less than: -" << last_committed
<< std::endl
;
312 good_version
= version
;
314 if (good_version
>= last_committed
) {
315 std::cout
<< "good epoch is greater or equal to the last committed one: "
316 << good_version
<< " >= " << last_committed
<< std::endl
;
320 // load/extract the crush map
322 ceph::shared_ptr
<CrushWrapper
> crush(new CrushWrapper
);
323 if (crush_file
.empty()) {
325 r
= store
.get(prefix
, store
.combine_strings("full", good_version
), bl
);
327 std::cerr
<< "Error getting map: " << cpp_strerror(r
) << std::endl
;
332 crush
= osdmap
.crush
;
336 r
= bl
.read_file(crush_file
.c_str(), &err
);
338 std::cerr
<< err
<< ": " << cpp_strerror(r
) << std::endl
;
341 bufferlist::iterator p
= bl
.begin();
345 // prepare a transaction to rewrite the epochs
346 // (good_version, last_committed]
347 // with the good crush map.
348 // XXX: may need to break this into several paxos versions?
349 assert(good_version
< last_committed
);
350 for (version_t v
= good_version
+ 1; v
<= last_committed
; v
++) {
351 cout
<< "rewriting epoch #" << v
<< "/" << last_committed
<< std::endl
;
352 r
= update_osdmap(store
, v
, false, crush
, t
);
357 // add a new osdmap epoch to store, so monitors will update their current osdmap
358 // in addition to the ones stored in epochs.
360 // This is needed due to the way the monitor updates from paxos and the
361 // facilities we are leveraging to push this update to the rest of the
364 // In a nutshell, we are generating a good version of the osdmap, with a
365 // proper crush, and building a transaction that will replace the bad
366 // osdmaps with good osdmaps. But this transaction needs to be applied on
367 // all nodes, so that the monitors will have good osdmaps to share with
368 // clients. We thus leverage Paxos, specifically the recovery mechanism, by
369 // creating a pending value that will be committed once the monitors form an
370 // initial quorum after being brought back to life.
372 // However, the way the monitor works has the paxos services, including the
373 // OSDMonitor, updating their state from disk *prior* to the recovery phase
374 // begins (so they have an up to date state in memory). This means the
375 // OSDMonitor will see the old, broken map, before the new paxos version is
376 // applied to disk, and the old version is cached. Even though we have the
377 // good map now, and we share the good map with clients, we will still be
378 // working on the old broken map. Instead of mucking around the monitor to
379 // make this work, we instead opt for adding the same osdmap but with a
380 // newer version, so that the OSDMonitor picks up on it when it updates from
381 // paxos after the proposal has been committed. This is not elegant, but
382 // avoids further unpleasantness that would arise from kludging around the
383 // current behavior. Also, has the added benefit of making sure the clients
384 // get an updated version of the map (because last_committed+1 >
385 // last_committed) :)
387 cout
<< "adding a new epoch #" << last_committed
+1 << std::endl
;
388 r
= update_osdmap(store
, last_committed
++, true, crush
, t
);
391 t
->put(prefix
, store
.combine_strings("full", "latest"), last_committed
);
392 t
->put(prefix
, "last_committed", last_committed
);
397 * create a new paxos version which carries a proposal to rewrite all epochs
398 * of incremental and full map of "osdmap" after a faulty crush map is injected.
399 * so the leader will trigger a recovery and propagate this fix to its peons,
400 * after the proposal is accepted, and the transaction in it is applied. all
401 * monitors will rewrite the bad crush map with the good one, and have a new
402 * osdmap epoch with the good crush map in it.
404 int rewrite_crush(const char* progname
,
405 vector
<string
>& subcmds
,
406 MonitorDBStore
& store
) {
407 po::options_description
op_desc("Allowed 'rewrite-crush' options");
410 op_desc
.add_options()
411 ("help,h", "produce this help message")
412 ("crush", po::value
<string
>(&crush_file
),
413 ("path to the crush map file "
414 "(default: will instead extract it from the known-good osdmap)"))
415 ("good-epoch", po::value
<int>(&version
),
416 "known-good epoch of osdmap, if a negative number '-N' is given, the "
417 "$last_committed-N is used instead (default: -1). "
418 "Please note, -1 is not necessarily a good epoch, because there are "
419 "good chance that we have more epochs slipped into the monstore after "
420 "the one where the crushmap is firstly injected.")
422 po::variables_map op_vm
;
423 int r
= parse_cmd_args(&op_desc
, NULL
, NULL
, subcmds
, &op_vm
);
427 if (op_vm
.count("help")) {
428 usage(progname
, op_desc
);
432 MonitorDBStore::Transaction rewrite_txn
;
433 r
= rewrite_transaction(store
, version
, crush_file
, &rewrite_txn
);
438 // store the transaction into store as a proposal
439 const string
prefix("paxos");
440 version_t pending_v
= store
.get(prefix
, "last_committed") + 1;
441 auto t(std::make_shared
<MonitorDBStore::Transaction
>());
443 rewrite_txn
.encode(bl
);
444 cout
<< "adding pending commit " << pending_v
445 << " " << bl
.length() << " bytes" << std::endl
;
446 t
->put(prefix
, pending_v
, bl
);
447 t
->put(prefix
, "pending_v", pending_v
);
448 // a large enough yet unique proposal number will probably do the trick
449 version_t pending_pn
= (store
.get(prefix
, "accepted_pn") / 100 + 4) * 100 + 1;
450 t
->put(prefix
, "pending_pn", pending_pn
);
451 store
.apply_transaction(t
);
455 static int update_auth(MonitorDBStore
& st
, const string
& keyring_path
)
457 // import all keyrings stored in the keyring file
459 int r
= keyring
.load(g_ceph_context
, keyring_path
);
461 cerr
<< "unable to load admin keyring: " << keyring_path
<< std::endl
;
469 for (const auto& k
: keyring
.get_keys()) {
470 KeyServerData::Incremental auth_inc
;
471 auth_inc
.name
= k
.first
;
472 auth_inc
.auth
= k
.second
;
473 if (auth_inc
.auth
.caps
.empty()) {
474 cerr
<< "no caps granted to: " << auth_inc
.name
<< std::endl
;
477 auth_inc
.op
= KeyServerData::AUTH_INC_ADD
;
479 AuthMonitor::Incremental inc
;
480 inc
.inc_type
= AuthMonitor::AUTH_DATA
;
481 ::encode(auth_inc
, inc
.auth_data
);
482 inc
.auth_type
= CEPH_AUTH_CEPHX
;
484 inc
.encode(bl
, CEPH_FEATURES_ALL
);
487 const string
prefix("auth");
488 auto last_committed
= st
.get(prefix
, "last_committed") + 1;
489 auto t
= make_shared
<MonitorDBStore::Transaction
>();
490 t
->put(prefix
, last_committed
, bl
);
491 t
->put(prefix
, "last_committed", last_committed
);
492 auto first_committed
= st
.get(prefix
, "first_committed");
493 if (!first_committed
) {
494 t
->put(prefix
, "first_committed", last_committed
);
496 st
.apply_transaction(t
);
500 static int update_mkfs(MonitorDBStore
& st
)
503 int r
= monmap
.build_initial(g_ceph_context
, cerr
);
505 cerr
<< "no initial monitors" << std::endl
;
509 monmap
.encode(bl
, CEPH_FEATURES_ALL
);
511 auto t
= make_shared
<MonitorDBStore::Transaction
>();
512 t
->put("mkfs", "monmap", bl
);
513 st
.apply_transaction(t
);
517 static int update_monitor(MonitorDBStore
& st
)
519 const string
prefix("monitor");
520 // a stripped-down Monitor::mkfs()
522 bl
.append(CEPH_MON_ONDISK_MAGIC
"\n");
523 auto t
= make_shared
<MonitorDBStore::Transaction
>();
524 t
->put(prefix
, "magic", bl
);
525 st
.apply_transaction(t
);
531 static int update_creating_pgs(MonitorDBStore
& st
)
534 auto last_osdmap_epoch
= st
.get("osdmap", "last_committed");
535 int r
= st
.get("osdmap", st
.combine_strings("full", last_osdmap_epoch
), bl
);
537 cerr
<< "unable to losd osdmap e" << last_osdmap_epoch
<< std::endl
;
543 creating_pgs_t creating
;
544 for (auto& i
: osdmap
.get_pools()) {
545 creating
.created_pools
.insert(i
.first
);
547 creating
.last_scan_epoch
= last_osdmap_epoch
;
550 ::encode(creating
, newbl
);
552 auto t
= make_shared
<MonitorDBStore::Transaction
>();
553 t
->put("osd_pg_creating", "creating", newbl
);
554 st
.apply_transaction(t
);
560 // - mgr_command_desc
561 static int update_mgrmap(MonitorDBStore
& st
)
563 auto t
= make_shared
<MonitorDBStore::Transaction
>();
567 // mgr expects epoch > 1
569 auto initial_modules
=
570 get_str_vec(g_ceph_context
->_conf
->get_val
<string
>("mgr_initial_modules"));
571 copy(begin(initial_modules
),
572 end(initial_modules
),
573 inserter(map
.modules
, end(map
.modules
)));
575 map
.encode(bl
, CEPH_FEATURES_ALL
);
576 t
->put("mgr", map
.epoch
, bl
);
577 t
->put("mgr", "last_committed", map
.epoch
);
580 auto mgr_command_descs
= mgr_commands
;
581 for (auto& c
: mgr_command_descs
) {
582 c
.set_flag(MonCommand::FLAG_MGR
);
585 ::encode(mgr_command_descs
, bl
);
586 t
->put("mgr_command_desc", "", bl
);
588 return st
.apply_transaction(t
);
591 static int update_paxos(MonitorDBStore
& st
)
593 // build a pending paxos proposal from all non-permanent k/v pairs. once the
594 // proposal is committed, it will gets applied. on the sync provider side, it
595 // will be a no-op, but on its peers, the paxos commit will help to build up
596 // the necessary epochs.
597 bufferlist pending_proposal
;
599 MonitorDBStore::Transaction t
;
600 vector
<string
> prefixes
= {"auth", "osdmap",
601 "mgr", "mgr_command_desc"};
602 for (const auto& prefix
: prefixes
) {
603 for (auto i
= st
.get_iterator(prefix
); i
->valid(); i
->next()) {
604 auto key
= i
->raw_key();
605 auto val
= i
->value();
606 t
.put(key
.first
, key
.second
, val
);
609 t
.encode(pending_proposal
);
611 const string
prefix("paxos");
612 auto t
= make_shared
<MonitorDBStore::Transaction
>();
613 t
->put(prefix
, "first_committed", 0);
614 t
->put(prefix
, "last_committed", 0);
616 t
->put(prefix
, pending_v
, pending_proposal
);
617 t
->put(prefix
, "pending_v", pending_v
);
618 t
->put(prefix
, "pending_pn", 400);
619 st
.apply_transaction(t
);
623 int rebuild_monstore(const char* progname
,
624 vector
<string
>& subcmds
,
627 po::options_description
op_desc("Allowed 'rebuild' options");
629 op_desc
.add_options()
630 ("keyring", po::value
<string
>(&keyring_path
),
631 "path to the client.admin key");
632 po::variables_map op_vm
;
633 int r
= parse_cmd_args(&op_desc
, nullptr, nullptr, subcmds
, &op_vm
);
637 if (op_vm
.count("help")) {
638 usage(progname
, op_desc
);
641 if (!keyring_path
.empty())
642 update_auth(st
, keyring_path
);
643 if ((r
= update_creating_pgs(st
))) {
646 if ((r
= update_mgrmap(st
))) {
649 if ((r
= update_paxos(st
))) {
652 if ((r
= update_mkfs(st
))) {
655 if ((r
= update_monitor(st
))) {
661 int main(int argc
, char **argv
) {
663 po::options_description
desc("Allowed options");
664 string store_path
, cmd
;
665 vector
<string
> subcmds
;
667 ("help,h", "produce help message")
670 /* Dear Future Developer:
672 * for further improvement, should you need to pass specific options to
673 * a command (e.g., get osdmap VER --hex), you can expand the current
674 * format by creating additional 'po::option_description' and passing
675 * 'subcmds' to 'po::command_line_parser', much like what is currently
676 * done by default. However, beware: in order to differentiate a
677 * command-specific option from the generic/global options, you will need
678 * to pass '--' in the command line (so that the first parser, the one
679 * below, assumes it has reached the end of all options); e.g.,
680 * 'get osdmap VER -- --hex'. Not pretty; far from intuitive; it was as
681 * far as I got with this library. Improvements on this format will be
682 * left as an excercise for the reader. -Joao
684 po::options_description
positional_desc("Positional argument options");
685 positional_desc
.add_options()
686 ("store-path", po::value
<string
>(&store_path
),
687 "path to monitor's store")
688 ("command", po::value
<string
>(&cmd
),
690 ("subcmd", po::value
<vector
<string
> >(&subcmds
),
691 "Command arguments/Sub-Commands")
693 po::positional_options_description positional
;
694 positional
.add("store-path", 1);
695 positional
.add("command", 1);
696 positional
.add("subcmd", -1);
698 po::options_description
all_desc("All options");
699 all_desc
.add(desc
).add(positional_desc
);
701 vector
<string
> ceph_option_strings
;
702 po::variables_map vm
;
704 po::parsed_options parsed
=
705 po::command_line_parser(argc
, argv
).
707 positional(positional
).
708 allow_unregistered().run();
715 // Specifying po::include_positional would have our positional arguments
716 // being collected (thus being part of ceph_option_strings and eventually
717 // passed on to global_init() below).
718 // Instead we specify po::exclude_positional, which has the upside of
719 // completely avoid this, but the downside of having to specify ceph
720 // options as --VAR=VAL (note the '='); otherwise we will capture the
721 // positional 'VAL' as belonging to us, never being collected.
722 ceph_option_strings
= po::collect_unrecognized(parsed
.options
,
723 po::exclude_positional
);
725 } catch(po::error
&e
) {
726 std::cerr
<< "error: " << e
.what() << std::endl
;
730 // parse command structure before calling global_init() and friends.
732 if (vm
.empty() || vm
.count("help") ||
733 store_path
.empty() || cmd
.empty() ||
734 *cmd
.begin() == '-') {
735 usage(argv
[0], desc
);
739 vector
<const char *> ceph_options
, def_args
;
740 ceph_options
.reserve(ceph_option_strings
.size());
741 for (vector
<string
>::iterator i
= ceph_option_strings
.begin();
742 i
!= ceph_option_strings
.end();
744 ceph_options
.push_back(i
->c_str());
747 auto cct
= global_init(
748 &def_args
, ceph_options
, CEPH_ENTITY_TYPE_MON
,
749 CODE_ENVIRONMENT_UTILITY
, 0);
750 common_init_finish(g_ceph_context
);
751 g_ceph_context
->_conf
->apply_changes(NULL
);
752 g_conf
= g_ceph_context
->_conf
;
754 // this is where we'll write *whatever*, on a per-command basis.
755 // not all commands require some place to write their things.
756 MonitorDBStore
st(store_path
);
757 if (store_path
.size()) {
761 std::cerr
<< ss
.str() << std::endl
;
766 if (cmd
== "dump-keys") {
767 KeyValueDB::WholeSpaceIterator iter
= st
.get_iterator();
768 while (iter
->valid()) {
769 pair
<string
,string
> key(iter
->raw_key());
770 cout
<< key
.first
<< " / " << key
.second
<< std::endl
;
773 } else if (cmd
== "compact") {
775 } else if (cmd
== "get") {
778 bool readable
= false;
780 // visible options for this command
781 po::options_description
op_desc("Allowed 'get' options");
782 op_desc
.add_options()
783 ("help,h", "produce this help message")
784 ("out,o", po::value
<string
>(&outpath
),
785 "output file (default: stdout)")
786 ("version,v", po::value
<unsigned>(&v
),
787 "map version to obtain")
788 ("readable,r", po::value
<bool>(&readable
)->default_value(false),
789 "print the map infomation in human readable format")
791 // this is going to be a positional argument; we don't want to show
792 // it as an option during --help, but we do want to have it captured
794 po::options_description
hidden_op_desc("Hidden 'get' options");
795 hidden_op_desc
.add_options()
796 ("map-type", po::value
<string
>(&map_type
),
799 po::positional_options_description op_positional
;
800 op_positional
.add("map-type", 1);
802 po::variables_map op_vm
;
803 int r
= parse_cmd_args(&op_desc
, &hidden_op_desc
, &op_positional
,
810 if (op_vm
.count("help") || map_type
.empty()) {
811 usage(argv
[0], op_desc
);
817 if (map_type
== "crushmap") {
818 v
= st
.get("osdmap", "last_committed");
820 v
= st
.get(map_type
, "last_committed");
824 int fd
= STDOUT_FILENO
;
825 if (!outpath
.empty()){
826 fd
= ::open(outpath
.c_str(), O_WRONLY
|O_CREAT
|O_TRUNC
, 0666);
828 std::cerr
<< "error opening output file: "
829 << cpp_strerror(errno
) << std::endl
;
835 BOOST_SCOPE_EXIT((&r
) (&fd
) (&outpath
)) {
837 if (r
< 0 && fd
!= STDOUT_FILENO
) {
838 ::remove(outpath
.c_str());
840 } BOOST_SCOPE_EXIT_END
844 if (map_type
== "osdmap") {
845 r
= st
.get(map_type
, st
.combine_strings("full", v
), bl
);
846 } else if (map_type
== "crushmap") {
848 r
= st
.get("osdmap", st
.combine_strings("full", v
), tmp
);
852 osdmap
.crush
->encode(bl
, CEPH_FEATURES_SUPPORTED_DEFAULT
);
855 r
= st
.get(map_type
, v
, bl
);
858 std::cerr
<< "Error getting map: " << cpp_strerror(r
) << std::endl
;
867 if (map_type
== "monmap") {
871 } else if (map_type
== "osdmap") {
875 } else if (map_type
== "mdsmap") {
879 } else if (map_type
== "mgr") {
884 f
.dump_object("mgrmap", mgr_map
);
886 } else if (map_type
== "crushmap") {
888 bufferlist::iterator it
= bl
.begin();
890 CrushCompiler
cc(cw
, std::cerr
, 0);
893 std::cerr
<< "This type of readable map does not exist: " << map_type
894 << std::endl
<< "You can only specify[osdmap|monmap|mdsmap"
895 "|crushmap|mgr]" << std::endl
;
897 } catch (const buffer::error
&err
) {
898 std::cerr
<< "Could not decode for human readable output (you may still"
899 " use non-readable mode). Detail: " << err
<< std::endl
;
908 if (!outpath
.empty()) {
909 std::cout
<< "wrote " << map_type
910 << " version " << v
<< " to " << outpath
913 } else if (cmd
== "show-versions") {
914 string map_type
; //map type:osdmap,monmap...
915 // visible options for this command
916 po::options_description
op_desc("Allowed 'show-versions' options");
917 op_desc
.add_options()
918 ("help,h", "produce this help message")
919 ("map-type", po::value
<string
>(&map_type
), "map_type");
921 po::positional_options_description op_positional
;
922 op_positional
.add("map-type", 1);
924 po::variables_map op_vm
;
925 int r
= parse_cmd_args(&op_desc
, NULL
, &op_positional
,
932 if (op_vm
.count("help") || map_type
.empty()) {
933 usage(argv
[0], op_desc
);
938 unsigned int v_first
= 0;
939 unsigned int v_last
= 0;
940 v_first
= st
.get(map_type
, "first_committed");
941 v_last
= st
.get(map_type
, "last_committed");
943 std::cout
<< "first committed:\t" << v_first
<< "\n"
944 << "last committed:\t" << v_last
<< std::endl
;
945 } else if (cmd
== "dump-paxos") {
948 po::options_description
op_desc("Allowed 'dump-paxos' options");
949 op_desc
.add_options()
950 ("help,h", "produce this help message")
951 ("start,s", po::value
<unsigned>(&dstart
),
952 "starting version (default: 0)")
953 ("end,e", po::value
<unsigned>(&dstop
),
954 "finish version (default: ~0)")
957 po::variables_map op_vm
;
958 int r
= parse_cmd_args(&op_desc
, NULL
, NULL
,
965 if (op_vm
.count("help")) {
966 usage(argv
[0], op_desc
);
971 if (dstart
> dstop
) {
972 std::cerr
<< "error: 'start' version (value: " << dstart
<< ") "
973 << " is greater than 'end' version (value: " << dstop
<< ")"
979 version_t v
= dstart
;
980 for (; v
<= dstop
; ++v
) {
982 st
.get("paxos", v
, bl
);
983 if (bl
.length() == 0)
985 cout
<< "\n--- " << v
<< " ---" << std::endl
;
986 auto tx(std::make_shared
<MonitorDBStore::Transaction
>());
987 Paxos::decode_append_transaction(tx
, bl
);
988 JSONFormatter
f(true);
993 std::cout
<< "dumped " << v
<< " paxos versions" << std::endl
;
995 } else if (cmd
== "dump-trace") {
1000 // visible options for this command
1001 po::options_description
op_desc("Allowed 'dump-trace' options");
1002 op_desc
.add_options()
1003 ("help,h", "produce this help message")
1004 ("start,s", po::value
<unsigned>(&dstart
),
1005 "starting version (default: 0)")
1006 ("end,e", po::value
<unsigned>(&dstop
),
1007 "finish version (default: ~0)")
1009 // this is going to be a positional argument; we don't want to show
1010 // it as an option during --help, but we do want to have it captured
1012 po::options_description
hidden_op_desc("Hidden 'dump-trace' options");
1013 hidden_op_desc
.add_options()
1014 ("out,o", po::value
<string
>(&outpath
),
1015 "file to write the dump to")
1017 po::positional_options_description op_positional
;
1018 op_positional
.add("out", 1);
1020 po::variables_map op_vm
;
1021 int r
= parse_cmd_args(&op_desc
, &hidden_op_desc
, &op_positional
,
1028 if (op_vm
.count("help")) {
1029 usage(argv
[0], op_desc
);
1034 if (outpath
.empty()) {
1035 usage(argv
[0], op_desc
);
1040 if (dstart
> dstop
) {
1041 std::cerr
<< "error: 'start' version (value: " << dstart
<< ") "
1042 << " is greater than 'stop' version (value: " << dstop
<< ")"
1048 TraceIter
iter(outpath
.c_str());
1053 if (iter
.num() >= dstop
) {
1056 if (iter
.num() >= dstart
) {
1057 JSONFormatter
f(true);
1058 iter
.cur()->dump(&f
, false);
1060 std::cout
<< std::endl
;
1064 std::cerr
<< "Read up to transaction " << iter
.num() << std::endl
;
1065 } else if (cmd
== "replay-trace") {
1067 unsigned num_replays
= 1;
1068 // visible options for this command
1069 po::options_description
op_desc("Allowed 'replay-trace' options");
1070 op_desc
.add_options()
1071 ("help,h", "produce this help message")
1072 ("num-replays,n", po::value
<unsigned>(&num_replays
),
1073 "finish version (default: 1)")
1075 // this is going to be a positional argument; we don't want to show
1076 // it as an option during --help, but we do want to have it captured
1078 po::options_description
hidden_op_desc("Hidden 'replay-trace' options");
1079 hidden_op_desc
.add_options()
1080 ("in,i", po::value
<string
>(&inpath
),
1081 "file to write the dump to")
1083 po::positional_options_description op_positional
;
1084 op_positional
.add("in", 1);
1086 // op_desc_all will aggregate all visible and hidden options for parsing.
1087 // when we call 'usage()' we just pass 'op_desc', as that's the description
1088 // holding the visible options.
1089 po::options_description op_desc_all
;
1090 op_desc_all
.add(op_desc
).add(hidden_op_desc
);
1092 po::variables_map op_vm
;
1094 po::parsed_options op_parsed
= po::command_line_parser(subcmds
).
1095 options(op_desc_all
).positional(op_positional
).run();
1096 po::store(op_parsed
, op_vm
);
1098 } catch (po::error
&e
) {
1099 std::cerr
<< "error: " << e
.what() << std::endl
;
1104 if (op_vm
.count("help")) {
1105 usage(argv
[0], op_desc
);
1110 if (inpath
.empty()) {
1111 usage(argv
[0], op_desc
);
1117 for (unsigned i
= 0; i
< num_replays
; ++i
) {
1118 TraceIter
iter(inpath
.c_str());
1123 std::cerr
<< "Replaying trans num " << num
<< std::endl
;
1124 st
.apply_transaction(iter
.cur());
1128 std::cerr
<< "Read up to transaction " << iter
.num() << std::endl
;
1130 } else if (cmd
== "random-gen") {
1131 unsigned tsize
= 200;
1132 unsigned tvalsize
= 1024;
1133 unsigned ntrans
= 100;
1134 po::options_description
op_desc("Allowed 'random-gen' options");
1135 op_desc
.add_options()
1136 ("help,h", "produce this help message")
1137 ("num-keys,k", po::value
<unsigned>(&tsize
),
1138 "keys to write in each transaction (default: 200)")
1139 ("size,s", po::value
<unsigned>(&tvalsize
),
1140 "size (in bytes) of the value to write in each key (default: 1024)")
1141 ("ntrans,n", po::value
<unsigned>(&ntrans
),
1142 "number of transactions to run (default: 100)")
1145 po::variables_map op_vm
;
1147 po::parsed_options op_parsed
= po::command_line_parser(subcmds
).
1148 options(op_desc
).run();
1149 po::store(op_parsed
, op_vm
);
1151 } catch (po::error
&e
) {
1152 std::cerr
<< "error: " << e
.what() << std::endl
;
1157 if (op_vm
.count("help")) {
1158 usage(argv
[0], op_desc
);
1164 for (unsigned i
= 0; i
< ntrans
; ++i
) {
1165 std::cerr
<< "Applying trans " << i
<< std::endl
;
1166 auto t(std::make_shared
<MonitorDBStore::Transaction
>());
1168 prefix
.push_back((i
%26)+'a');
1169 for (unsigned j
= 0; j
< tsize
; ++j
) {
1173 for (unsigned k
= 0; k
< tvalsize
; ++k
) bl
.append(rand());
1174 t
->put(prefix
, os
.str(), bl
);
1177 t
->compact_prefix(prefix
);
1178 st
.apply_transaction(t
);
1180 } else if (cmd
== "store-copy") {
1181 if (subcmds
.size() < 1 || subcmds
[0].empty()) {
1182 usage(argv
[0], desc
);
1187 string out_path
= subcmds
[0];
1189 MonitorDBStore
out_store(out_path
);
1192 int r
= out_store
.create_and_open(ss
);
1194 std::cerr
<< ss
.str() << std::endl
;
1200 KeyValueDB::WholeSpaceIterator it
= st
.get_iterator();
1201 uint64_t total_keys
= 0;
1202 uint64_t total_size
= 0;
1203 uint64_t total_tx
= 0;
1206 uint64_t num_keys
= 0;
1208 auto tx(std::make_shared
<MonitorDBStore::Transaction
>());
1210 while (it
->valid() && num_keys
< 128) {
1211 pair
<string
,string
> k
= it
->raw_key();
1212 bufferlist v
= it
->value();
1213 tx
->put(k
.first
, k
.second
, v
);
1217 total_size
+= v
.length();
1222 total_keys
+= num_keys
;
1225 out_store
.apply_transaction(tx
);
1227 std::cout
<< "copied " << total_keys
<< " keys so far ("
1228 << stringify(byte_u_t(total_size
)) << ")" << std::endl
;
1230 } while (it
->valid());
1232 std::cout
<< "summary: copied " << total_keys
<< " keys, using "
1233 << total_tx
<< " transactions, totalling "
1234 << stringify(byte_u_t(total_size
)) << std::endl
;
1235 std::cout
<< "from '" << store_path
<< "' to '" << out_path
<< "'"
1237 } else if (cmd
== "rewrite-crush") {
1238 err
= rewrite_crush(argv
[0], subcmds
, st
);
1239 } else if (cmd
== "rebuild") {
1240 err
= rebuild_monstore(argv
[0], subcmds
, st
);
1242 std::cerr
<< "Unrecognized command: " << cmd
<< std::endl
;
1243 usage(argv
[0], desc
);