From 83fbc91e5c4e52cc1513f34908f99d2ac3b930df Mon Sep 17 00:00:00 2001 From: David Zafman Date: Wed, 30 Jul 2014 12:39:49 -0700 Subject: [PATCH] Complete replacement of ceph_filestore_tool and ceph_filestore_dump with unified ceph_objectstore_tool Move list-lost-objects and fix-lost-objects features from ceph_filestore_tool to ceph_objectstore_tool as list-lost, fix-lost Change --type to --op for info, log, export...operations Add --type for the ObjectStore type (defaults to filestore) Change --filestore-path to --data-path Update installation, Makefile.am, and .gitignore Fix and rename test case to match Add some additional invalid option checks Signed-off-by: David Zafman --- ceph.spec.in | 3 +- debian/ceph-test.install | 3 +- src/.gitignore | 3 +- src/test/ceph_objectstore_tool.py | 40 +++-- src/tools/Makefile.am | 16 +- src/tools/ceph_objectstore_tool.cc | 235 +++++++++++++++++++++-------- 6 files changed, 205 insertions(+), 95 deletions(-) diff --git a/ceph.spec.in b/ceph.spec.in index 694c439e5b454..66a54d27e9c4f 100644 --- a/ceph.spec.in +++ b/ceph.spec.in @@ -715,8 +715,7 @@ ln -sf %{_libdir}/librbd.so.1 /usr/lib64/qemu/librbd.so.1 %{_bindir}/ceph_smalliobenchdumb %{_bindir}/ceph_smalliobenchfs %{_bindir}/ceph_smalliobenchrbd -%{_bindir}/ceph_filestore_dump -%{_bindir}/ceph_filestore_tool +%{_bindir}/ceph_objectstore_tool %{_bindir}/ceph_streamtest %{_bindir}/ceph_test_* %{_bindir}/ceph_tpbench diff --git a/debian/ceph-test.install b/debian/ceph-test.install index 70107d98842e4..5f46b04c59224 100644 --- a/debian/ceph-test.install +++ b/debian/ceph-test.install @@ -1,8 +1,7 @@ usr/bin/ceph-coverage usr/bin/ceph_bench_log usr/bin/ceph_dupstore -usr/bin/ceph_filestore_dump -usr/bin/ceph_filestore_tool +usr/bin/ceph_objectstore_tool usr/bin/ceph_kvstorebench usr/bin/ceph_multi_stress_watch usr/bin/ceph_erasure_code diff --git a/src/.gitignore b/src/.gitignore index 2482e1a8a2804..d8d4976e55853 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -25,8 +25,7 @@ Makefile /ceph.conf /ceph_bench_log /ceph_dupstore -/ceph_filestore_dump -/ceph_filestore_tool +/ceph_objectstore_tool /ceph_mon_store_converter /ceph_multi_stress_watch /ceph_erasure_code diff --git a/src/test/ceph_objectstore_tool.py b/src/test/ceph_objectstore_tool.py index 3718a0ee0374e..8aa55a2f1fbfd 100755 --- a/src/test/ceph_objectstore_tool.py +++ b/src/test/ceph_objectstore_tool.py @@ -139,7 +139,7 @@ def main(): pid = os.getpid() TESTDIR = "/tmp/test.{pid}".format(pid=pid) DATADIR = "/tmp/data.{pid}".format(pid=pid) - CFSD_PREFIX = "./ceph_filestore_dump --filestore-path dev/{osd} --journal-path dev/{osd}.journal " + CFSD_PREFIX = "./ceph_objectstore_tool --data-path dev/{osd} --journal-path dev/{osd}.journal " DATALINECOUNT = 10000 PROFNAME = "testecprofile" @@ -307,7 +307,7 @@ def main(): print "Test invalid parameters" # On export can't use stdout to a terminal - cmd = (CFSD_PREFIX + "--type export --pgid {pg}").format(osd=ONEOSD, pg=ONEPG) + cmd = (CFSD_PREFIX + "--op export --pgid {pg}").format(osd=ONEOSD, pg=ONEPG) ERRORS += test_failure(cmd, "stdout is a tty and no --file option specified") OTHERFILE = "/tmp/foo.{pid}".format(pid=pid) @@ -315,30 +315,42 @@ def main(): foofd.close() # On import can't specify a PG - cmd = (CFSD_PREFIX + "--type import --pgid {pg} --file {FOO}").format(osd=ONEOSD, pg=ONEPG, FOO=OTHERFILE) + cmd = (CFSD_PREFIX + "--op import --pgid {pg} --file {FOO}").format(osd=ONEOSD, pg=ONEPG, FOO=OTHERFILE) ERRORS += test_failure(cmd, "--pgid option invalid with import") os.unlink(OTHERFILE) - cmd = (CFSD_PREFIX + "--type import --file {FOO}").format(osd=ONEOSD, FOO=OTHERFILE) + cmd = (CFSD_PREFIX + "--op import --file {FOO}").format(osd=ONEOSD, FOO=OTHERFILE) ERRORS += test_failure(cmd, "open: No such file or directory") # On import can't use stdin from a terminal - cmd = (CFSD_PREFIX + "--type import --pgid {pg}").format(osd=ONEOSD, pg=ONEPG) + cmd = (CFSD_PREFIX + "--op import --pgid {pg}").format(osd=ONEOSD, pg=ONEPG) ERRORS += test_failure(cmd, "stdin is a tty and no --file option specified") - # Test --type list and generate json for all objects - print "Test --type list by generating json for all objects" + # Specify a bad --type + cmd = (CFSD_PREFIX + "--type foobar --op list --pgid {pg}").format(osd=ONEOSD, pg=ONEPG) + ERRORS += test_failure(cmd, "Must provide --type (filestore, memstore, keyvaluestore-dev)") + + # Don't specify a data-path + cmd = "./ceph_objectstore_tool --journal-path dev/{osd}.journal --type memstore --op list --pgid {pg}".format(osd=ONEOSD, pg=ONEPG) + ERRORS += test_failure(cmd, "Must provide --data-path") + + # Don't specify a journal-path for filestore + cmd = "./ceph_objectstore_tool --type filestore --data-path dev/{osd} --op list --pgid {pg}".format(osd=ONEOSD, pg=ONEPG) + ERRORS += test_failure(cmd, "Must provide --journal-path") + + # Test --op list and generate json for all objects + print "Test --op list by generating json for all objects" TMPFILE = r"/tmp/tmp.{pid}".format(pid=pid) ALLPGS = OBJREPPGS + OBJECPGS for pg in ALLPGS: OSDS = get_osds(pg, OSDDIR) for osd in OSDS: - cmd = (CFSD_PREFIX + "--type list --pgid {pg}").format(osd=osd, pg=pg) + cmd = (CFSD_PREFIX + "--op list --pgid {pg}").format(osd=osd, pg=pg) tmpfd = open(TMPFILE, "a") logging.debug(cmd) ret = call(cmd, shell=True, stdout=tmpfd) if ret != 0: - logging.error("Bad exit status {ret} from --type list request".format(ret=ret)) + logging.error("Bad exit status {ret} from --op list request".format(ret=ret)) ERRORS += 1 tmpfd.close() @@ -487,7 +499,7 @@ def main(): print "Test pg info" for pg in ALLREPPGS + ALLECPGS: for osd in get_osds(pg, OSDDIR): - cmd = (CFSD_PREFIX + "--type info --pgid {pg} | grep '\"pgid\": \"{pg}\"'").format(osd=osd, pg=pg) + cmd = (CFSD_PREFIX + "--op info --pgid {pg} | grep '\"pgid\": \"{pg}\"'").format(osd=osd, pg=pg) logging.debug(cmd) ret = call(cmd, shell=True, stdout=nullfd) if ret != 0: @@ -498,7 +510,7 @@ def main(): for pg in ALLREPPGS + ALLECPGS: for osd in get_osds(pg, OSDDIR): tmpfd = open(TMPFILE, "w") - cmd = (CFSD_PREFIX + "--type log --pgid {pg}").format(osd=osd, pg=pg) + cmd = (CFSD_PREFIX + "--op log --pgid {pg}").format(osd=osd, pg=pg) logging.debug(cmd) ret = call(cmd, shell=True, stdout=tmpfd) if ret != 0: @@ -530,7 +542,7 @@ def main(): for osd in get_osds(pg, OSDDIR): mydir = os.path.join(TESTDIR, osd) fname = os.path.join(mydir, pg) - cmd = (CFSD_PREFIX + "--type export --pgid {pg} --file {file}").format(osd=osd, pg=pg, file=fname) + cmd = (CFSD_PREFIX + "--op export --pgid {pg} --file {file}").format(osd=osd, pg=pg, file=fname) logging.debug(cmd) ret = call(cmd, shell=True, stdout=nullfd, stderr=nullfd) if ret != 0: @@ -543,7 +555,7 @@ def main(): RM_ERRORS = 0 for pg in ALLREPPGS + ALLECPGS: for osd in get_osds(pg, OSDDIR): - cmd = (CFSD_PREFIX + "--type remove --pgid {pg}").format(pg=pg, osd=osd) + cmd = (CFSD_PREFIX + "--op remove --pgid {pg}").format(pg=pg, osd=osd) logging.debug(cmd) ret = call(cmd, shell=True, stdout=nullfd) if ret != 0: @@ -559,7 +571,7 @@ def main(): dir = os.path.join(TESTDIR, osd) for pg in [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]: file = os.path.join(dir, pg) - cmd = (CFSD_PREFIX + "--type import --file {file}").format(osd=osd, file=file) + cmd = (CFSD_PREFIX + "--op import --file {file}").format(osd=osd, file=file) logging.debug(cmd) ret = call(cmd, shell=True, stdout=nullfd) if ret != 0: diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 003c82918db2d..a99d08da43684 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -11,20 +11,12 @@ ceph_kvstore_tool_LDADD = $(LIBOS) $(CEPH_GLOBAL) ceph_kvstore_tool_CXXFLAGS = $(UNITTEST_CXXFLAGS) bin_DEBUGPROGRAMS += ceph-kvstore-tool - -ceph_filestore_tool_SOURCES = tools/ceph_filestore_tool.cc -ceph_filestore_tool_LDADD = $(LIBOSD) $(LIBOS) $(CEPH_GLOBAL) -lboost_program_options -if LINUX -ceph_filestore_tool_LDADD += -ldl -endif # LINUX -bin_PROGRAMS += ceph_filestore_tool - -ceph_filestore_dump_SOURCES = tools/ceph_filestore_dump.cc -ceph_filestore_dump_LDADD = $(LIBOSD) $(LIBOS) $(CEPH_GLOBAL) $(BOOST_PROGRAM_OPTIONS_LIBS) +ceph_objectstore_tool_SOURCES = tools/ceph_objectstore_tool.cc +ceph_objectstore_tool_LDADD = $(LIBOSD) $(LIBOS) $(CEPH_GLOBAL) $(BOOST_PROGRAM_OPTIONS_LIBS) if LINUX -ceph_filestore_dump_LDADD += -ldl +ceph_objectstore_tool_LDADD += -ldl endif # LINUX -bin_PROGRAMS += ceph_filestore_dump +bin_PROGRAMS += ceph_objectstore_tool monmaptool_SOURCES = tools/monmaptool.cc monmaptool_LDADD = $(CEPH_GLOBAL) $(LIBCOMMON) diff --git a/src/tools/ceph_objectstore_tool.cc b/src/tools/ceph_objectstore_tool.cc index 6655497d229a5..539c1eeb24287 100644 --- a/src/tools/ceph_objectstore_tool.cc +++ b/src/tools/ceph_objectstore_tool.cc @@ -411,9 +411,9 @@ static int get_fd_data(int fd, bufferlist &bl) return 0; } -static void invalid_path(string &path) +static void invalid_filestore_path(string &path) { - cerr << "Invalid path to osd store specified: " << path << "\n"; + cerr << "Invalid filestore path specified: " << path << "\n"; exit(1); } @@ -1481,36 +1481,38 @@ void usage(po::options_description &desc) cerr << std::endl; cerr << "Positional syntax:" << std::endl; cerr << std::endl; - cerr << "(requires --filestore-path, --journal-path and --pgid to be specified)" << std::endl; + cerr << "(requires --data, --journal (for filestore type) and --pgid to be specified)" << std::endl; cerr << "(optional [file] argument will read stdin or write stdout if not specified or if '-' specified)" << std::endl; - cerr << "ceph-filestore-dump ... (get|set)-bytes [file]" << std::endl; - cerr << "ceph-filestore-dump ... (set-(attr|omap) [file]" << std::endl; - cerr << "ceph-filestore-dump ... (set-omaphdr) [file]" << std::endl; - cerr << "ceph-filestore-dump ... (get|rm)-(attr|omap) " << std::endl; - cerr << "ceph-filestore-dump ... (get-omaphdr)" << std::endl; - cerr << "ceph-filestore-dump ... list-attrs" << std::endl; - cerr << "ceph-filestore-dump ... list-omap" << std::endl; - cerr << "ceph-filestore-dump ... remove" << std::endl; + cerr << "ceph_objectstore_tool ... (get|set)-bytes [file]" << std::endl; + cerr << "ceph_objectstore_tool ... (set-(attr|omap) [file]" << std::endl; + cerr << "ceph_objectstore_tool ... (set-omaphdr) [file]" << std::endl; + cerr << "ceph_objectstore_tool ... (get|rm)-(attr|omap) " << std::endl; + cerr << "ceph_objectstore_tool ... (get-omaphdr)" << std::endl; + cerr << "ceph_objectstore_tool ... list-attrs" << std::endl; + cerr << "ceph_objectstore_tool ... list-omap" << std::endl; + cerr << "ceph_objectstore_tool ... remove" << std::endl; cerr << std::endl; exit(1); } int main(int argc, char **argv) { - string fspath, jpath, pgidstr, type, file, object, objcmd, arg1, arg2; + string dpath, jpath, pgidstr, op, file, object, objcmd, arg1, arg2, type; ghobject_t ghobj; po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") - ("filestore-path", po::value(&fspath), - "path to filestore directory, mandatory") + ("type", po::value(&type), + "Arg is one of [filestore (default), memstore, keyvaluestore-dev]") + ("data-path", po::value(&dpath), + "path to object store, mandatory") ("journal-path", po::value(&jpath), - "path to journal, mandatory") + "path to journal, mandatory for filestore type") ("pgid", po::value(&pgidstr), - "PG id, mandatory except for import") - ("type", po::value(&type), - "Arg is one of [info, log, remove, export, import, list]") + "PG id, mandatory except for import, list-lost, fix-lost") + ("op", po::value(&op), + "Arg is one of [info, log, remove, export, import, list, list-lost, fix-lost]") ("file", po::value(&file), "path of file to export or import") ("debug", "Enable diagnostic output to stderr") @@ -1547,11 +1549,14 @@ int main(int argc, char **argv) usage(desc); } - if (!vm.count("filestore-path")) { - cerr << "Must provide --filestore-path" << std::endl; + if (!vm.count("data-path")) { + cerr << "Must provide --data-path" << std::endl; usage(desc); } - if (!vm.count("journal-path")) { + if (!vm.count("type")) { + type = "filestore"; + } + if (type == "filestore" && !vm.count("journal-path")) { cerr << "Must provide --journal-path" << std::endl; usage(desc); } @@ -1559,16 +1564,16 @@ int main(int argc, char **argv) cerr << "Invalid syntax, missing command" << std::endl; usage(desc); } - if (!vm.count("type") && !(vm.count("object") && vm.count("objcmd"))) { - cerr << "Must provide --type or object command..." - << std::endl; + if (!vm.count("op") && !(vm.count("object") && vm.count("objcmd"))) { + cerr << "Must provide --op or object command..." << std::endl; usage(desc); } - if (vm.count("type") && vm.count("object")) { - cerr << "Can't specify both --type and object command syntax" << std::endl; + if (vm.count("op") && vm.count("object")) { + cerr << "Can't specify both --op and object command syntax" << std::endl; usage(desc); } - if (type != "import" && !vm.count("pgid")) { + if (op != "import" && op != "list-lost" && op != "fix-lost" + && !vm.count("pgid")) { cerr << "Must provide pgid" << std::endl; usage(desc); } @@ -1588,7 +1593,7 @@ int main(int argc, char **argv) outistty = isatty(STDOUT_FILENO); file_fd = fd_none; - if (type == "export") { + if (op == "export") { if (!vm.count("file")) { if (outistty) { cerr << "stdout is a tty and no --file option specified" << std::endl; @@ -1598,7 +1603,7 @@ int main(int argc, char **argv) } else { file_fd = open(file.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666); } - } else if (type == "import") { + } else if (op == "import") { if (!vm.count("file")) { if (isatty(STDIN_FILENO)) { cerr << "stdin is a tty and no --file option specified" << std::endl; @@ -1620,13 +1625,12 @@ int main(int argc, char **argv) return 1; } - if ((fspath.length() == 0 || jpath.length() == 0) || - (type != "import" && pgidstr.length() == 0)) { + if (dpath.length() == 0) { cerr << "Invalid params" << std::endl; return 1; } - if (type == "import" && pgidstr.length()) { + if (op == "import" && pgidstr.length()) { cerr << "--pgid option invalid with import" << std::endl; return 1; } @@ -1665,30 +1669,33 @@ int main(int argc, char **argv) } g_conf->apply_changes(NULL); - //Verify that fspath really is an osd store + //Verify that data-path really exists struct stat st; - if (::stat(fspath.c_str(), &st) == -1) { - perror("fspath"); - invalid_path(fspath); - } - if (!S_ISDIR(st.st_mode)) { - invalid_path(fspath); - } - string check = fspath + "/whoami"; - if (::stat(check.c_str(), &st) == -1) { - perror("whoami"); - invalid_path(fspath); - } - if (!S_ISREG(st.st_mode)) { - invalid_path(fspath); - } - check = fspath + "/current"; - if (::stat(check.c_str(), &st) == -1) { - perror("current"); - invalid_path(fspath); - } - if (!S_ISDIR(st.st_mode)) { - invalid_path(fspath); + if (::stat(dpath.c_str(), &st) == -1) { + perror("data-path"); + exit(1); + } + //Verify data data-path really is a filestore + if (type == "filestore") { + if (!S_ISDIR(st.st_mode)) { + invalid_filestore_path(dpath); + } + string check = dpath + "/whoami"; + if (::stat(check.c_str(), &st) == -1) { + perror("whoami"); + invalid_filestore_path(dpath); + } + if (!S_ISREG(st.st_mode)) { + invalid_filestore_path(dpath); + } + check = dpath + "/current"; + if (::stat(check.c_str(), &st) == -1) { + perror("current"); + invalid_filestore_path(dpath); + } + if (!S_ISDIR(st.st_mode)) { + invalid_filestore_path(dpath); + } } spg_t pgid; @@ -1697,7 +1704,11 @@ int main(int argc, char **argv) return 1; } - ObjectStore *fs = ObjectStore::create(NULL, "filestore", fspath, jpath, flags); + ObjectStore *fs = ObjectStore::create(NULL, type, dpath, jpath, flags); + if (fs == NULL) { + cerr << "Must provide --type (filestore, memstore, keyvaluestore-dev)" << std::endl; + exit(1); + } int r = fs->mount(); if (r < 0) { @@ -1766,7 +1777,7 @@ int main(int argc, char **argv) goto out; } - if (type == "import") { + if (op == "import") { try { ret = do_import(fs, superblock); @@ -1786,7 +1797,7 @@ int main(int argc, char **argv) log_oid = OSD::make_pg_log_oid(pgid); biginfo_oid = OSD::make_pg_biginfo_oid(pgid); - if (type == "remove") { + if (op == "remove") { uint64_t next_removal_seq = 0; //My local seq finish_remove_pgs(fs, &next_removal_seq); int r = initiate_new_remove_pg(fs, pgid, &next_removal_seq); @@ -1800,6 +1811,104 @@ int main(int argc, char **argv) goto out; } + if (op == "list-lost" || op == "fix-lost") { + unsigned LIST_AT_A_TIME = 100; + unsigned scanned = 0; + int r; + vector colls_to_check; + if (pgidstr.length()) { + colls_to_check.push_back(coll_t(pgid)); + } else { + vector candidates; + r = fs->list_collections(candidates); + if (r < 0) { + cerr << "Error listing collections: " << cpp_strerror(r) << std::endl; + goto UMOUNT; + } + for (vector::iterator i = candidates.begin(); + i != candidates.end(); + ++i) { + spg_t pgid; + snapid_t snap; + if (i->is_pg(pgid, snap)) { + colls_to_check.push_back(*i); + } + } + } + + cerr << colls_to_check.size() << " pgs to scan" << std::endl; + for (vector::iterator i = colls_to_check.begin(); + i != colls_to_check.end(); + ++i, ++scanned) { + cerr << "Scanning " << *i << ", " << scanned << "/" + << colls_to_check.size() << " completed" << std::endl; + ghobject_t next; + while (!next.is_max()) { + vector list; + r = fs->collection_list_partial( + *i, + next, + LIST_AT_A_TIME, + LIST_AT_A_TIME, + CEPH_NOSNAP, + &list, + &next); + if (r < 0) { + cerr << "Error listing collection: " << *i << ", " + << cpp_strerror(r) << std::endl; + goto UMOUNT; + } + for (vector::iterator obj = list.begin(); + obj != list.end(); + ++obj) { + bufferlist attr; + r = fs->getattr(*i, *obj, OI_ATTR, attr); + if (r < 0) { + cerr << "Error getting attr on : " << make_pair(*i, *obj) << ", " + << cpp_strerror(r) << std::endl; + goto UMOUNT; + } + object_info_t oi; + bufferlist::iterator bp = attr.begin(); + try { + ::decode(oi, bp); + } catch (...) { + r = -EINVAL; + cerr << "Error getting attr on : " << make_pair(*i, *obj) << ", " + << cpp_strerror(r) << std::endl; + goto UMOUNT; + } + if (oi.is_lost()) { + if (op == "list-lost") { + cout << *i << "/" << *obj << " is lost" << std::endl; + } + if (op == "fix-lost") { + cerr << *i << "/" << *obj << " is lost, fixing" << std::endl; + oi.clear_flag(object_info_t::FLAG_LOST); + bufferlist bl2; + ::encode(oi, bl2); + ObjectStore::Transaction t; + t.setattr(*i, *obj, OI_ATTR, bl2); + r = fs->apply_transaction(t); + if (r < 0) { + cerr << "Error getting fixing attr on : " << make_pair(*i, *obj) + << ", " + << cpp_strerror(r) << std::endl; + goto UMOUNT; + } + } + } + } + } + } + cerr << "Completed" << std::endl; + + UMOUNT: + fs->sync_and_flush(); + ret = r; + goto out; + } + r = fs->list_collections(ls); if (r < 0) { cerr << "failed to list pgs: " << cpp_strerror(-r) << std::endl; @@ -1997,7 +2106,7 @@ int main(int argc, char **argv) usage(desc); } - if (type == "list") { + if (op == "list") { Formatter *formatter = new JSONFormatter(false); r = do_list(fs, coll, formatter); if (r) { @@ -2029,17 +2138,17 @@ int main(int argc, char **argv) if (debug) cerr << "struct_v " << (int)struct_ver << std::endl; - if (type == "export") { + if (op == "export") { ret = do_export(fs, coll, pgid, info, map_epoch, struct_ver, superblock); if (ret == 0 && file_fd != STDOUT_FILENO) cout << "Export successful" << std::endl; - } else if (type == "info") { + } else if (op == "info") { formatter->open_object_section("info"); info.dump(formatter); formatter->close_section(); formatter->flush(cout); cout << std::endl; - } else if (type == "log") { + } else if (op == "log") { PGLog::IndexedLog log; pg_missing_t missing; ret = get_log(fs, coll, pgid, info, log, missing); @@ -2057,7 +2166,7 @@ int main(int argc, char **argv) formatter->flush(cout); cout << std::endl; } else { - cerr << "Must provide --type (info, log, remove, export, import, list)" + cerr << "Must provide --op (info, log, remove, export, import, list, list-lost, fix-lost)" << std::endl; usage(desc); } -- 2.39.5