}
};
+
+/**
+ * usage: ceph-monstore-tool <store-path> <command> [options]
+ *
+ * commands:
+ *
+ * store-copy < --out arg >
+ * dump-keys
+ * compact
+ * getmonmap < --out arg [ --version arg ] >
+ * getosdmap < --out arg [ --version arg ] >
+ * dump-paxos <--dump-start VER> <--dump-end VER>
+ * dump-trace < --trace-file arg >
+ * replay-trace
+ * random-gen
+ *
+ * wanted syntax:
+ *
+ * ceph-monstore-tool PATH CMD [options]
+ *
+ * ceph-monstore-tool PATH store-copy <PATH2 | -o PATH2>
+ * ceph-monstore-tool PATH dump-keys
+ * ceph-monstore-tool PATH compact
+ * ceph-monstore-tool PATH get monmap [VER]
+ * ceph-monstore-tool PATH get osdmap [VER]
+ * ceph-monstore-tool PATH dump-paxos STARTVER ENDVER
+ *
+ *
+ */
+void usage(const char *n, po::options_description &d)
+{
+ std::cerr <<
+ "usage: " << n << " <store-path> <cmd> [args|options]\n"
+ << "\n"
+ << "Commands:\n"
+ << " store-copy PATH copies store to PATH\n"
+ << " compact compacts the store\n"
+ << " get monmap [-- options] get monmap (version VER if specified)\n"
+ << " (default: last committed)\n"
+ << " get osdmap [-- options] get osdmap (version VER if specified)\n"
+ << " (default: last committed)\n"
+ << " get mdsmap [-- options] get mdsmap (version VER if specified)\n"
+ << " (default: last committed)\n"
+ << " dump-keys dumps store keys to FILE\n"
+ << " (default: stdout)\n"
+ << " dump-paxos [-- options] dump paxos transactions\n"
+ << " (dump-paxos -- --help for more info)\n"
+ << " dump-trace FILE [-- options] dump contents of trace file FILE\n"
+ << " (dump-trace -- --help for more info)\n"
+ << " replay-trace FILE [-- options] replay trace from FILE\n"
+ << " (replay-trace -- --help for more info)\n"
+ << " random-gen [-- options] add randomly generated ops to the store\n"
+ << " (random-gen -- --help for more info)\n"
+ << std::endl;
+ std::cerr << d << std::endl;
+ std::cerr
+ << "\nPlease Note:\n"
+ << "* Ceph-specific options should be in the format --option-name=VAL\n"
+ << " (specifically, do not forget the '='!!)\n"
+ << "* Command-specific options need to be passed after a '--'\n"
+ << " e.g., 'get monmap -- --version 10 --out /tmp/foo'"
+ << std::endl;
+}
+
int main(int argc, char **argv) {
+ int err = 0;
po::options_description desc("Allowed options");
- int version = -1;
- string store_path, cmd, out_path, tfile;
- unsigned dstart = 0;
- unsigned dstop = ~0;
- unsigned num_replays = 1;
- unsigned tsize = 200;
- unsigned tvalsize = 1024;
- unsigned ntrans = 100;
+ string store_path, cmd;
+ vector<string> subcmds;
desc.add_options()
- ("help", "produce help message")
- ("mon-store-path", po::value<string>(&store_path),
- "path to mon directory, mandatory")
- ("out", po::value<string>(&out_path),
- "out path")
- ("version", po::value<int>(&version),
- "version requested")
- ("trace-file", po::value<string>(&tfile),
- "trace file")
- ("dump-start", po::value<unsigned>(&dstart),
- "transaction num to start dumping at")
- ("dump-end", po::value<unsigned>(&dstop),
- "transaction num to stop dumping at")
- ("num-replays", po::value<unsigned>(&num_replays),
- "number of times to replay")
- ("trans-size", po::value<unsigned>(&tsize),
- "keys to write in each transaction")
- ("trans-val-size", po::value<unsigned>(&tvalsize),
- "val to write in each key")
- ("num-trans", po::value<unsigned>(&ntrans),
- "number of transactions to run")
+ ("help,h", "produce help message")
+ ;
+
+ /* Dear Future Developer:
+ *
+ * for further improvement, should you need to pass specific options to
+ * a command (e.g., get osdmap VER --hex), you can expand the current
+ * format by creating additional 'po::option_description' and passing
+ * 'subcmds' to 'po::command_line_parser', much like what is currently
+ * done by default. However, beware: in order to differentiate a
+ * command-specific option from the generic/global options, you will need
+ * to pass '--' in the command line (so that the first parser, the one
+ * below, assumes it has reached the end of all options); e.g.,
+ * 'get osdmap VER -- --hex'. Not pretty; far from intuitive; it was as
+ * far as I got with this library. Improvements on this format will be
+ * left as an excercise for the reader. -Joao
+ */
+ po::options_description positional_desc("Positional argument options");
+ positional_desc.add_options()
+ ("store-path", po::value<string>(&store_path),
+ "path to monitor's store")
("command", po::value<string>(&cmd),
- "command")
+ "Command")
+ ("subcmd", po::value<vector<string> >(&subcmds),
+ "Command arguments/Sub-Commands")
;
- po::positional_options_description p;
- p.add("command", 1);
- p.add("version", 1);
+ po::positional_options_description positional;
+ positional.add("store-path", 1);
+ positional.add("command", 1);
+ positional.add("subcmd", -1);
+
+ po::options_description all_desc("All options");
+ all_desc.add(desc).add(positional_desc);
vector<string> ceph_option_strings;
po::variables_map vm;
try {
po::parsed_options parsed =
- po::command_line_parser(argc, argv).options(desc).positional(p).allow_unregistered().run();
+ po::command_line_parser(argc, argv).
+ options(all_desc).
+ positional(positional).
+ allow_unregistered().run();
+
po::store(
parsed,
vm);
po::notify(vm);
+ // Specifying po::include_positional would have our positional arguments
+ // being collected (thus being part of ceph_option_strings and eventually
+ // passed on to global_init() below).
+ // Instead we specify po::exclude_positional, which has the upside of
+ // completely avoid this, but the downside of having to specify ceph
+ // options as --VAR=VAL (note the '='); otherwise we will capture the
+ // positional 'VAL' as belonging to us, never being collected.
ceph_option_strings = po::collect_unrecognized(parsed.options,
- po::include_positional);
+ po::exclude_positional);
+
} catch(po::error &e) {
- std::cerr << e.what() << std::endl;
+ std::cerr << "error: " << e.what() << std::endl;
+ return 1;
+ }
+
+ // parse command structure before calling global_init() and friends.
+
+ if (vm.empty() || vm.count("help") ||
+ store_path.empty() || cmd.empty() ||
+ *cmd.begin() == '-') {
+ usage(argv[0], desc);
return 1;
}
g_ceph_context->_conf->apply_changes(NULL);
g_conf = g_ceph_context->_conf;
- if (vm.count("help")) {
- std::cerr << desc << std::endl;
- return 1;
- }
-
- int fd;
- if (vm.count("out")) {
- if ((fd = open(out_path.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) {
- int _err = errno;
- if (_err != EISDIR) {
- std::cerr << "Couldn't open " << out_path << ": " << cpp_strerror(_err) << std::endl;
- return 1;
- }
- }
- } else {
- fd = STDOUT_FILENO;
- }
-
- if (fd < 0 && cmd != "store-copy") {
- std::cerr << "error: '" << out_path << "' is a directory!" << std::endl;
- return 1;
- }
-
+ // this is where we'll write *whatever*, on a per-command basis.
+ // not all commands require some place to write their things.
MonitorDBStore st(store_path);
if (store_path.size()) {
stringstream ss;
int r = st.open(ss);
if (r < 0) {
std::cerr << ss.str() << std::endl;
- goto done;
+ return EINVAL;
}
}
+
if (cmd == "dump-keys") {
KeyValueDB::WholeSpaceIterator iter = st.get_iterator();
while (iter->valid()) {
}
} else if (cmd == "compact") {
st.compact();
- } else if (cmd == "getmonmap") {
- assert(fd >= 0);
- if (!store_path.size()) {
- std::cerr << "need mon store path" << std::endl;
- std::cerr << desc << std::endl;
+ } else if (cmd == "get") {
+ unsigned v = 0;
+ string outpath;
+ string map_type;
+ // visible options for this command
+ po::options_description op_desc("Allowed 'get' options");
+ op_desc.add_options()
+ ("help,h", "produce this help message")
+ ("out,o", po::value<string>(&outpath),
+ "output file (default: stdout)")
+ ("version,v", po::value<unsigned>(&v),
+ "map version to obtain")
+ ;
+ // this is going to be a positional argument; we don't want to show
+ // it as an option during --help, but we do want to have it captured
+ // when parsing.
+ po::options_description hidden_op_desc("Hidden 'get' options");
+ hidden_op_desc.add_options()
+ ("map-type", po::value<string>(&map_type),
+ "map-type")
+ ;
+ po::positional_options_description op_positional;
+ op_positional.add("map-type", 1);
+
+ // op_desc_all will aggregate all visible and hidden options for parsing.
+ // when we call 'usage()' we just pass 'op_desc', as that's the description
+ // holding the visible options.
+ po::options_description op_desc_all;
+ op_desc_all.add(op_desc).add(hidden_op_desc);
+
+ po::variables_map op_vm;
+ try {
+ po::parsed_options op_parsed = po::command_line_parser(subcmds).
+ options(op_desc_all).positional(op_positional).run();
+ po::store(op_parsed, op_vm);
+ po::notify(op_vm);
+ } catch (po::error &e) {
+ std::cerr << "error: " << e.what() << std::endl;
+ err = EINVAL;
goto done;
}
- version_t v;
- if (version <= 0) {
- v = st.get("monmap", "last_committed");
- } else {
- v = version;
- }
- bufferlist bl;
- /// XXX: this is not ok, osdmap and full should be abstracted somewhere
- int r = st.get("monmap", v, bl);
- if (r < 0) {
- std::cerr << "Error getting map: " << cpp_strerror(r) << std::endl;
+ if (op_vm.count("help") || map_type.empty()) {
+ usage(argv[0], op_desc);
+ err = 0;
goto done;
}
- bl.write_fd(fd);
- } else if (cmd == "getosdmap") {
- if (!store_path.size()) {
- std::cerr << "need mon store path" << std::endl;
- std::cerr << desc << std::endl;
- goto done;
+
+ if (v <= 0) {
+ v = st.get(map_type, "last_committed");
}
- version_t v;
- if (version == -1) {
- v = st.get("osdmap", "last_committed");
- } else {
- v = version;
+
+ int fd = ::open(outpath.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (fd < 0) {
+ std::cerr << "error opening output file: "
+ << cpp_strerror(errno) << std::endl;
+ err = EINVAL;
+ goto done;
}
bufferlist bl;
- /// XXX: this is not ok, osdmap and full should be abstracted somewhere
- int r = st.get("osdmap", st.combine_strings("full", v), bl);
+ int r = 0;
+ if (map_type == "osdmap") {
+ r = st.get(map_type, st.combine_strings("full", v), bl);
+ } else {
+ r = st.get(map_type, v, bl);
+ }
if (r < 0) {
std::cerr << "Error getting map: " << cpp_strerror(r) << std::endl;
+ err = EINVAL;
+ ::close(fd);
goto done;
}
bl.write_fd(fd);
} else if (cmd == "dump-paxos") {
+ unsigned dstart = 0;
+ unsigned dstop = ~0;
+ po::options_description op_desc("Allowed 'dump-paxos' options");
+ op_desc.add_options()
+ ("help,h", "produce this help message")
+ ("start,s", po::value<unsigned>(&dstart),
+ "starting version (default: 0)")
+ ("end,e", po::value<unsigned>(&dstop),
+ "finish version (default: ~0)")
+ ;
+
+ po::variables_map op_vm;
+ try {
+ po::parsed_options op_parsed = po::command_line_parser(subcmds).
+ options(op_desc).run();
+ po::store(op_parsed, op_vm);
+ po::notify(op_vm);
+ } catch (po::error &e) {
+ std::cerr << "error: " << e.what() << std::endl;
+ err = EINVAL;
+ goto done;
+ }
+
+ if (op_vm.count("help")) {
+ usage(argv[0], op_desc);
+ err = 0;
+ goto done;
+ }
+
for (version_t v = dstart; v <= dstop; ++v) {
bufferlist bl;
st.get("paxos", v, bl);
f.flush(cout);
}
} else if (cmd == "dump-trace") {
- if (tfile.empty()) {
- std::cerr << "Need trace_file" << std::endl;
- std::cerr << desc << std::endl;
+ unsigned dstart = 0;
+ unsigned dstop = ~0;
+ string outpath;
+
+ // visible options for this command
+ po::options_description op_desc("Allowed 'dump-trace' options");
+ op_desc.add_options()
+ ("help,h", "produce this help message")
+ ("start,s", po::value<unsigned>(&dstart),
+ "starting version (default: 0)")
+ ("end,e", po::value<unsigned>(&dstop),
+ "finish version (default: ~0)")
+ ;
+ // this is going to be a positional argument; we don't want to show
+ // it as an option during --help, but we do want to have it captured
+ // when parsing.
+ po::options_description hidden_op_desc("Hidden 'dump-trace' options");
+ hidden_op_desc.add_options()
+ ("out,o", po::value<string>(&outpath),
+ "file to write the dump to")
+ ;
+ po::positional_options_description op_positional;
+ op_positional.add("out", 1);
+
+ // op_desc_all will aggregate all visible and hidden options for parsing.
+ // when we call 'usage()' we just pass 'op_desc', as that's the description
+ // holding the visible options.
+ po::options_description op_desc_all;
+ op_desc_all.add(op_desc).add(hidden_op_desc);
+
+ po::variables_map op_vm;
+ try {
+ po::parsed_options op_parsed = po::command_line_parser(subcmds).
+ options(op_desc_all).positional(op_positional).run();
+ po::store(op_parsed, op_vm);
+ po::notify(op_vm);
+ } catch (po::error &e) {
+ std::cerr << "error: " << e.what() << std::endl;
+ err = EINVAL;
+ goto done;
+ }
+
+ if (op_vm.count("help")) {
+ usage(argv[0], op_desc);
+ err = 0;
+ goto done;
+ }
+
+ if (outpath.empty()) {
+ usage(argv[0], op_desc);
+ err = EINVAL;
goto done;
}
- TraceIter iter(tfile.c_str());
+
+ TraceIter iter(outpath.c_str());
iter.init();
while (true) {
if (!iter.valid())
}
std::cerr << "Read up to transaction " << iter.num() << std::endl;
} else if (cmd == "replay-trace") {
- if (!store_path.size()) {
- std::cerr << "need mon store path" << std::endl;
- std::cerr << desc << std::endl;
+ string inpath;
+ unsigned num_replays = 1;
+ // visible options for this command
+ po::options_description op_desc("Allowed 'replay-trace' options");
+ op_desc.add_options()
+ ("help,h", "produce this help message")
+ ("num-replays,n", po::value<unsigned>(&num_replays),
+ "finish version (default: 1)")
+ ;
+ // this is going to be a positional argument; we don't want to show
+ // it as an option during --help, but we do want to have it captured
+ // when parsing.
+ po::options_description hidden_op_desc("Hidden 'replay-trace' options");
+ hidden_op_desc.add_options()
+ ("in,i", po::value<string>(&inpath),
+ "file to write the dump to")
+ ;
+ po::positional_options_description op_positional;
+ op_positional.add("in", 1);
+
+ // op_desc_all will aggregate all visible and hidden options for parsing.
+ // when we call 'usage()' we just pass 'op_desc', as that's the description
+ // holding the visible options.
+ po::options_description op_desc_all;
+ op_desc_all.add(op_desc).add(hidden_op_desc);
+
+ po::variables_map op_vm;
+ try {
+ po::parsed_options op_parsed = po::command_line_parser(subcmds).
+ options(op_desc_all).positional(op_positional).run();
+ po::store(op_parsed, op_vm);
+ po::notify(op_vm);
+ } catch (po::error &e) {
+ std::cerr << "error: " << e.what() << std::endl;
+ err = EINVAL;
+ goto done;
+ }
+
+ if (op_vm.count("help")) {
+ usage(argv[0], op_desc);
+ err = 0;
goto done;
}
- if (tfile.empty()) {
- std::cerr << "Need trace_file" << std::endl;
- std::cerr << desc << std::endl;
+
+ if (inpath.empty()) {
+ usage(argv[0], op_desc);
+ err = EINVAL;
goto done;
}
+
unsigned num = 0;
for (unsigned i = 0; i < num_replays; ++i) {
- TraceIter iter(tfile.c_str());
+ TraceIter iter(inpath.c_str());
iter.init();
while (true) {
if (!iter.valid())
std::cerr << "Read up to transaction " << iter.num() << std::endl;
}
} else if (cmd == "random-gen") {
- if (!store_path.size()) {
- std::cerr << "need mon store path" << std::endl;
- std::cerr << desc << std::endl;
+ unsigned tsize = 200;
+ unsigned tvalsize = 1024;
+ unsigned ntrans = 100;
+ po::options_description op_desc("Allowed 'random-gen' options");
+ op_desc.add_options()
+ ("help,h", "produce this help message")
+ ("num-keys,k", po::value<unsigned>(&tsize),
+ "keys to write in each transaction (default: 200)")
+ ("size,s", po::value<unsigned>(&tvalsize),
+ "size (in bytes) of the value to write in each key (default: 1024)")
+ ("ntrans,n", po::value<unsigned>(&ntrans),
+ "number of transactions to run (default: 100)")
+ ;
+
+ po::variables_map op_vm;
+ try {
+ po::parsed_options op_parsed = po::command_line_parser(subcmds).
+ options(op_desc).run();
+ po::store(op_parsed, op_vm);
+ po::notify(op_vm);
+ } catch (po::error &e) {
+ std::cerr << "error: " << e.what() << std::endl;
+ err = EINVAL;
goto done;
}
+
+ if (op_vm.count("help")) {
+ usage(argv[0], op_desc);
+ err = 0;
+ goto done;
+ }
+
unsigned num = 0;
for (unsigned i = 0; i < ntrans; ++i) {
std::cerr << "Applying trans " << i << std::endl;
st.apply_transaction(t);
}
} else if (cmd == "store-copy") {
- if (!store_path.size()) {
- std::cerr << "need mon store path to copy from" << std::endl;
- std::cerr << desc << std::endl;
- goto done;
- }
- if (!out_path.size()) {
- std::cerr << "need mon store path to copy to (--out <mon_data_dir>)"
- << std::endl;
- std::cerr << desc << std::endl;
- goto done;
- }
- if (fd > 0) {
- std::cerr << "supplied out path '" << out_path << "' is not a directory"
- << std::endl;
+ if (subcmds.size() < 1 || subcmds[0].empty()) {
+ usage(argv[0], desc);
+ err = EINVAL;
goto done;
}
+ string out_path = subcmds[0];
+
MonitorDBStore out_store(out_path);
{
stringstream ss;
<< std::endl;
} else {
std::cerr << "Unrecognized command: " << cmd << std::endl;
+ usage(argv[0], desc);
goto done;
}
done:
st.close();
- if (vm.count("out") && fd > 0) {
- ::close(fd);
- }
- return 0;
+ return err;
}