diff options
-rw-r--r-- | ceph.spec.in | 1 | ||||
-rw-r--r-- | debian/ceph-mds.install | 1 | ||||
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/tools/Makefile.am | 8 | ||||
-rw-r--r-- | src/tools/cephfs/TableTool.cc | 306 | ||||
-rw-r--r-- | src/tools/cephfs/TableTool.h | 50 | ||||
-rw-r--r-- | src/tools/cephfs/cephfs-table-tool.cc | 46 |
7 files changed, 413 insertions, 0 deletions
diff --git a/ceph.spec.in b/ceph.spec.in index 126433eef0d..0886cbf9156 100644 --- a/ceph.spec.in +++ b/ceph.spec.in @@ -459,6 +459,7 @@ fi %{_bindir}/librados-config %{_bindir}/ceph-client-debug %{_bindir}/cephfs-journal-tool +%{_bindir}/cephfs-table-tool %{_bindir}/ceph-debugpack %{_bindir}/ceph-coverage %{_bindir}/ceph_mon_store_converter diff --git a/debian/ceph-mds.install b/debian/ceph-mds.install index df3cbe4e637..e76a3a1916b 100644 --- a/debian/ceph-mds.install +++ b/debian/ceph-mds.install @@ -1,3 +1,4 @@ usr/bin/ceph-mds usr/bin/cephfs-journal-tool +usr/bin/cephfs-table-tool usr/share/man/man8/ceph-mds.8 diff --git a/src/.gitignore b/src/.gitignore index 6e76998b46e..90d0cbc903e 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -11,6 +11,7 @@ Makefile /ceph-authtool /ceph-client-debug /cephfs-journal-tool +/cephfs-table-tool /ceph-conf /ceph-coverage /ceph-crush-location diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index d92ed558536..72ea57ac50b 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -71,6 +71,13 @@ cephfs_journal_tool_SOURCES = \ cephfs_journal_tool_LDADD = $(LIBMDS) $(LIBRADOS) $(CEPH_GLOBAL) bin_PROGRAMS += cephfs-journal-tool +cephfs_table_tool_SOURCES = \ + tools/cephfs/cephfs-table-tool.cc \ + tools/cephfs/TableTool.cc \ + tools/cephfs/MDSUtility.cc +cephfs_table_tool_LDADD = $(LIBMDS) $(LIBRADOS) $(CEPH_GLOBAL) +bin_PROGRAMS += cephfs-table-tool + if WITH_REST_BENCH rest_bench_SOURCES = tools/rest_bench.cc rest_bench_SOURCES += common/obj_bencher.cc # needs cleanup so it can go in libcommon.la @@ -105,6 +112,7 @@ noinst_HEADERS += \ tools/cephfs/EventOutput.h \ tools/cephfs/Resetter.h \ tools/cephfs/Dumper.h \ + tools/cephfs/TableTool.h \ tools/cephfs/MDSUtility.h \ tools/rados/rados_sync.h diff --git a/src/tools/cephfs/TableTool.cc b/src/tools/cephfs/TableTool.cc new file mode 100644 index 00000000000..4b22de04f72 --- /dev/null +++ b/src/tools/cephfs/TableTool.cc @@ -0,0 +1,306 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2015 John Spray <john.spray@redhat.com> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + */ + + +#include "common/ceph_argparse.h" +#include "common/errno.h" + +#include "mds/SessionMap.h" +#include "mds/InoTable.h" +#include "mds/SnapServer.h" + +#include "TableTool.h" + + +#define dout_subsys ceph_subsys_mds +#undef dout_prefix +#define dout_prefix *_dout << __func__ << ": " + +void TableTool::usage() +{ + std::cout << "Usage: \n" + << " cephfs-table-tool <all|[mds rank]> <reset|show> <session|snap|inode>" + << std::endl; + + generic_client_usage(); +} + + +int TableTool::main(std::vector<const char*> &argv) +{ + int r; + + dout(10) << __func__ << dendl; + + // RADOS init + // ========== + r = rados.init_with_context(g_ceph_context); + if (r < 0) { + derr << "RADOS unavailable, cannot scan filesystem journal" << dendl; + return r; + } + + dout(4) << "connecting to RADOS..." << dendl; + rados.connect(); + + int const pool_id = mdsmap->get_metadata_pool(); + dout(4) << "resolving pool " << pool_id << dendl; + std::string pool_name; + r = rados.pool_reverse_lookup(pool_id, &pool_name); + if (r < 0) { + derr << "Pool " << pool_id << " identified in MDS map not found in RADOS!" << dendl; + return r; + } + + dout(4) << "creating IoCtx.." << dendl; + r = rados.ioctx_create(pool_name.c_str(), io); + assert(r == 0); + + // Require at least 3 args <action> <table> <rank> + if (argv.size() < 3) { + usage(); + return -EINVAL; + } + + const std::string rank_str = std::string(argv[0]); + const std::string mode = std::string(argv[1]); + const std::string table = std::string(argv[2]); + + if (rank_str == "all") { + rank = MDS_RANK_NONE; + } else { + std::string rank_err; + rank = strict_strtol(rank_str.c_str(), 10, &rank_err); + if (!rank_err.empty()) { + derr << "Bad rank '" << rank_str << "'" << dendl; + usage(); + } + } + + JSONFormatter jf(true); + if (mode == "reset") { + if (table == "session") { + r = apply_rank_fn(&TableTool::_reset_session_table, &jf); + } else if (table == "inode") { + r = apply_rank_fn(&TableTool::_reset_ino_table, &jf); + } else if (table == "snap") { + r = _reset_snap_table(&jf); + } else { + derr << "Invalid table '" << table << "'" << dendl; + usage(); + return -EINVAL; + } + } else if (mode == "show") { + if (table == "session") { + r = apply_rank_fn(&TableTool::_show_session_table, &jf); + } else if (table == "inode") { + r = apply_rank_fn(&TableTool::_show_ino_table, &jf); + } else if (table == "snap") { + r = _show_snap_table(&jf); + } else { + derr << "Invalid table '" << table << "'" << dendl; + usage(); + return -EINVAL; + } + } else { + derr << "Invalid mode '" << mode << "'" << dendl; + usage(); + return -EINVAL; + } + + // Subcommand should have written to formatter, flush it + jf.flush(std::cout); + std::cout << std::endl; + return r; +} + + + + + + +/** + * For a function that takes an MDS rank as an argument and + * returns an error code, execute it either on all ranks (if + * this->rank is MDS_RANK_NONE), or on the rank specified + * by this->rank. + */ +int TableTool::apply_rank_fn(int (TableTool::*fptr) (mds_rank_t, Formatter*), Formatter *f) +{ + assert(f != NULL); + + int r = 0; + std::set<mds_rank_t> apply_to_ranks; + if (rank == MDS_RANK_NONE) { + mdsmap->get_mds_set(apply_to_ranks); + } else { + apply_to_ranks.insert(rank); + } + + f->open_object_section("ranks"); + + for (std::set<mds_rank_t>::iterator rank_i = apply_to_ranks.begin(); + rank_i != apply_to_ranks.end(); ++rank_i) { + std::ostringstream rank_str; + rank_str << *rank_i; + f->open_object_section(rank_str.str().c_str()); + + f->open_object_section("data"); + int rank_r = (this->*fptr)(*rank_i, f); + f->close_section(); + r = r ? r : rank_r; + + f->dump_int("result", rank_r); + f->close_section(); + } + + f->close_section(); + + return r; +} + + +/** + * This class wraps an MDS table class (SessionMap, SnapServer, InoTable) + * with offline load/store code such that we can do offline dumps and resets + * on those tables. + */ +template <typename A> +class TableHandler +{ +private: + // The RADOS object ID for the table + std::string object_name; + + // The rank in question (may be NONE) + mds_rank_t rank; + + // Whether this is an MDSTable subclass (i.e. has leading version field to decode) + bool mds_table; + +public: + TableHandler(mds_rank_t r, std::string const &name, bool mds_table_) + : rank(r), mds_table(mds_table_) + { + // Compose object name of the table we will dump + std::ostringstream oss; + oss << "mds"; + if (rank != MDS_RANK_NONE) { + oss << rank; + } + oss << "_" << name; + object_name = oss.str(); + } + + int load_and_dump(librados::IoCtx *io, Formatter *f) + { + assert(io != NULL); + assert(f != NULL); + + // Attempt read + bufferlist table_bl; + int read_r = io->read(object_name, table_bl, 0, 0); + if (read_r >= 0) { + bufferlist::iterator q = table_bl.begin(); + try { + if (mds_table) { + version_t version; + ::decode(version, q); + f->dump_int("version", version); + } + A table_inst; + table_inst.set_rank(rank); + table_inst.decode(q); + table_inst.dump(f); + + return 0; + } catch (buffer::error &e) { + derr << "table " << object_name << " is corrupt" << dendl; + return -EIO; + } + } else { + derr << "error reading table object " << object_name + << ": " << cpp_strerror(read_r) << dendl; + return read_r; + } + } + + int reset(librados::IoCtx *io) + { + A table_inst; + table_inst.set_rank(rank); + table_inst.reset_state(); + + // Compose new (blank) table + bufferlist new_bl; + if (mds_table) { + version_t version = 1; + ::encode(version, new_bl); + } + table_inst.encode(new_bl); + + // Write out new table + int r = io->write_full(object_name, new_bl); + if (r != 0) { + derr << "error writing table object " << object_name + << ": " << cpp_strerror(r) << dendl; + return r; + } + + return r; + } +}; + +int TableTool::_show_session_table(mds_rank_t rank, Formatter *f) +{ + return TableHandler<SessionMapStore>(rank, "sessionmap", false).load_and_dump(&io, f); +} + +int TableTool::_reset_session_table(mds_rank_t rank, Formatter *f) +{ + return TableHandler<SessionMapStore>(rank, "sessionmap", false).reset(&io); +} + +int TableTool::_show_ino_table(mds_rank_t rank, Formatter *f) +{ + return TableHandler<InoTable>(rank, "inotable", true).load_and_dump(&io, f);; +} + +int TableTool::_reset_ino_table(mds_rank_t rank, Formatter *f) +{ + return TableHandler<InoTable>(rank, "inotable", true).reset(&io); +} + +int TableTool::_show_snap_table(Formatter *f) +{ + int r; + + f->open_object_section("show_snap_table"); + { + r = TableHandler<SnapServer>(MDS_RANK_NONE, "snaptable", true).load_and_dump(&io, f); + f->dump_int("result", r); + } + f->close_section(); + + return r; +} + +int TableTool::_reset_snap_table(Formatter *f) +{ + int r = TableHandler<SnapServer>(MDS_RANK_NONE, "snaptable", true).reset(&io); + f->open_object_section("reset_snap_status"); + f->dump_int("result", r); + f->close_section(); + return r; +} + diff --git a/src/tools/cephfs/TableTool.h b/src/tools/cephfs/TableTool.h new file mode 100644 index 00000000000..0f43a7345f4 --- /dev/null +++ b/src/tools/cephfs/TableTool.h @@ -0,0 +1,50 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2015 John Spray <john.spray@redhat.com> + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + */ + + +#include "MDSUtility.h" + +#include "include/rados/librados.hpp" + + +/** + * Command line tool for debugging the backing store of + * MDSTable instances. + */ +class TableTool : public MDSUtility +{ + private: + mds_rank_t rank; + + // I/O handles + librados::Rados rados; + librados::IoCtx io; + + int apply_rank_fn(int (TableTool::*fptr) (mds_rank_t, Formatter *), Formatter *f); + + int _reset_session_table(mds_rank_t rank, Formatter *f); + int _show_session_table(mds_rank_t rank, Formatter *f); + + int _show_ino_table(mds_rank_t rank, Formatter *f); + int _reset_ino_table(mds_rank_t rank, Formatter *f); + + int _show_snap_table(Formatter *f); + int _reset_snap_table(Formatter *f); + + public: + void usage(); + TableTool() : + rank(MDS_RANK_NONE) {} + int main(std::vector<const char*> &argv); + +}; diff --git a/src/tools/cephfs/cephfs-table-tool.cc b/src/tools/cephfs/cephfs-table-tool.cc new file mode 100644 index 00000000000..749de6f84d6 --- /dev/null +++ b/src/tools/cephfs/cephfs-table-tool.cc @@ -0,0 +1,46 @@ + +#include "include/types.h" +#include "common/config.h" +#include "common/ceph_argparse.h" +#include "common/errno.h" +#include "global/global_init.h" + +#include "TableTool.h" + + +int main(int argc, const char **argv) +{ + vector<const char*> args; + argv_to_vec(argc, argv, args); + env_to_vec(args); + + global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0); + common_init_finish(g_ceph_context); + + TableTool tt; + + // Handle --help before calling init() so we don't depend on network. + if (args.empty() || (args.size() == 1 && (std::string(args[0]) == "--help" || std::string(args[0]) == "-h"))) { + tt.usage(); + return 0; + } + + // Connect to mon cluster, download MDS map etc + int rc = tt.init(); + if (rc != 0) { + std::cerr << "Error in initialization: " << cpp_strerror(rc) << std::endl; + return rc; + } + + // Finally, execute the user's commands + rc = tt.main(args); + if (rc != 0) { + std::cerr << "Error (" << cpp_strerror(rc) << ")" << std::endl; + } + + tt.shutdown(); + + return rc; +} + + |