summaryrefslogtreecommitdiffstats
path: root/src/tools/cephfs/TableTool.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cephfs/TableTool.cc')
-rw-r--r--src/tools/cephfs/TableTool.cc419
1 files changed, 419 insertions, 0 deletions
diff --git a/src/tools/cephfs/TableTool.cc b/src/tools/cephfs/TableTool.cc
new file mode 100644
index 000000000..dcd35a624
--- /dev/null
+++ b/src/tools/cephfs/TableTool.cc
@@ -0,0 +1,419 @@
+// -*- 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_context g_ceph_context
+#define dout_subsys ceph_subsys_mds
+#undef dout_prefix
+#define dout_prefix *_dout << __func__ << ": "
+
+using namespace std;
+
+void TableTool::usage()
+{
+ std::cout << "Usage: \n"
+ << " cephfs-table-tool <all|[mds rank]> <reset|show> <session|snap|inode>"
+ << " cephfs-table-tool <all|[mds rank]> <take_inos> <max_ino>"
+ << std::endl;
+
+ generic_client_usage();
+}
+
+
+/**
+ * For a function that takes an MDS role as an argument and
+ * returns an error code, execute it on the roles specified
+ * by `role_selector`.
+ */
+int TableTool::apply_role_fn(std::function<int(mds_role_t, Formatter *)> fptr, Formatter *f)
+{
+ ceph_assert(f != NULL);
+
+ int r = 0;
+
+ f->open_object_section("ranks");
+
+ for (auto role : role_selector.get_roles()) {
+ std::ostringstream rank_str;
+ rank_str << role.rank;
+ f->open_object_section(rank_str.str().c_str());
+
+ f->open_object_section("data");
+ int rank_r = fptr(role, 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
+{
+protected:
+ // The RADOS object ID for the table
+ std::string object_name;
+
+ // The role in question (may be NONE)
+ mds_role_t role;
+
+ // Whether this is an MDSTable subclass (i.e. has leading version field to decode)
+ bool mds_table;
+
+public:
+ TableHandler(mds_role_t r, std::string const &name, bool mds_table_)
+ : role(r), mds_table(mds_table_)
+ {
+ // Compose object name of the table we will dump
+ std::ostringstream oss;
+ oss << "mds";
+ if (!role.is_none()) {
+ oss << role.rank;
+ }
+ oss << "_" << name;
+ object_name = oss.str();
+ }
+
+ int load_and_dump(librados::IoCtx *io, Formatter *f)
+ {
+ ceph_assert(io != NULL);
+ ceph_assert(f != NULL);
+
+ // Attempt read
+ bufferlist table_bl;
+ int read_r = io->read(object_name, table_bl, 0, 0);
+ if (read_r >= 0) {
+ auto q = table_bl.cbegin();
+ try {
+ if (mds_table) {
+ version_t version;
+ decode(version, q);
+ f->dump_int("version", version);
+ }
+ A table_inst;
+ table_inst.set_rank(role.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;
+ // Compose new (blank) table
+ table_inst.set_rank(role.rank);
+ table_inst.reset_state();
+ // Write the table out
+ return write(table_inst, io);
+ }
+
+protected:
+
+ int write(const A &table_inst, librados::IoCtx *io)
+ {
+ bufferlist new_bl;
+ if (mds_table) {
+ version_t version = 1;
+ encode(version, new_bl);
+ }
+ table_inst.encode_state(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;
+ }
+};
+
+template <typename A>
+class TableHandlerOmap
+{
+private:
+ // The RADOS object ID for the table
+ std::string object_name;
+
+ // The role (rank may be NONE)
+ mds_role_t role;
+
+ // Whether this is an MDSTable subclass (i.e. has leading version field to decode)
+ bool mds_table;
+
+public:
+ TableHandlerOmap(mds_role_t r, std::string const &name, bool mds_table_)
+ : role(r), mds_table(mds_table_)
+ {
+ // Compose object name of the table we will dump
+ std::ostringstream oss;
+ oss << "mds";
+ if (!role.is_none()) {
+ oss << role.rank;
+ }
+ oss << "_" << name;
+ object_name = oss.str();
+ }
+
+ int load_and_dump(librados::IoCtx *io, Formatter *f)
+ {
+ ceph_assert(io != NULL);
+ ceph_assert(f != NULL);
+
+ // Read in the header
+ bufferlist header_bl;
+ int r = io->omap_get_header(object_name, &header_bl);
+ if (r != 0) {
+ derr << "error reading header on '" << object_name << "': "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ // Decode the header
+ A table_inst;
+ table_inst.set_rank(role.rank);
+ try {
+ table_inst.decode_header(header_bl);
+ } catch (buffer::error &e) {
+ derr << "table " << object_name << " is corrupt" << dendl;
+ return -EIO;
+ }
+
+ // Read and decode OMAP values in chunks
+ std::string last_key = "";
+ while(true) {
+ std::map<std::string, bufferlist> values;
+ int r = io->omap_get_vals(object_name, last_key,
+ g_conf()->mds_sessionmap_keys_per_op, &values);
+
+ if (r != 0) {
+ derr << "error reading values: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ if (values.empty()) {
+ break;
+ }
+
+ try {
+ table_inst.decode_values(values);
+ } catch (buffer::error &e) {
+ derr << "table " << object_name << " is corrupt" << dendl;
+ return -EIO;
+ }
+ last_key = values.rbegin()->first;
+ }
+
+ table_inst.dump(f);
+
+ return 0;
+ }
+
+ int reset(librados::IoCtx *io)
+ {
+ A table_inst;
+ table_inst.set_rank(role.rank);
+ table_inst.reset_state();
+ bufferlist header_bl;
+ table_inst.encode_header(&header_bl);
+
+ // Compose a transaction to clear and write header
+ librados::ObjectWriteOperation op;
+ op.omap_clear();
+ op.set_op_flags2(LIBRADOS_OP_FLAG_FAILOK);
+ op.omap_set_header(header_bl);
+
+ return io->operate(object_name, &op);
+ }
+};
+
+class InoTableHandler : public TableHandler<InoTable>
+{
+ public:
+ explicit InoTableHandler(mds_role_t r)
+ : TableHandler(r, "inotable", true)
+ {}
+
+ int take_inos(librados::IoCtx *io, inodeno_t max, Formatter *f)
+ {
+ InoTable inst;
+ inst.set_rank(role.rank);
+ inst.reset_state();
+
+ int r = 0;
+ if (inst.force_consume_to(max)) {
+ r = write(inst, io);
+ }
+
+ f->dump_int("version", inst.get_version());
+ inst.dump(f);
+
+ return r;
+ }
+};
+
+
+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;
+ r = rados.connect();
+ if (r < 0) {
+ derr << "couldn't connect to cluster: " << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ // Require at least 3 args <rank> <mode> <arg> [args...]
+ if (argv.size() < 3) {
+ cerr << "missing required 3 arguments" << std::endl;
+ return -EINVAL;
+ }
+
+ const std::string role_str = std::string(argv[0]);
+ const std::string mode = std::string(argv[1]);
+ const std::string table = std::string(argv[2]);
+
+ r = role_selector.parse(*fsmap, role_str);
+ if (r < 0) {
+ derr << "Bad rank selection: " << role_str << "'" << dendl;
+ return r;
+ }
+
+ auto fs = fsmap->get_filesystem(role_selector.get_ns());
+ ceph_assert(fs != nullptr);
+ int64_t const pool_id = fs->mds_map.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);
+ if (r != 0) {
+ return r;
+ }
+
+ JSONFormatter jf(true);
+ if (mode == "reset") {
+ const std::string table = std::string(argv[2]);
+ if (table == "session") {
+ r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
+ return TableHandlerOmap<SessionMapStore>(rank, "sessionmap", false).reset(&io);
+ }, &jf);
+ } else if (table == "inode") {
+ r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
+ return TableHandler<InoTable>(rank, "inotable", true).reset(&io);
+ }, &jf);
+ } else if (table == "snap") {
+ r = TableHandler<SnapServer>(mds_role_t(), "snaptable", true).reset(&io);
+ jf.open_object_section("reset_snap_status");
+ jf.dump_int("result", r);
+ jf.close_section();
+ } else {
+ cerr << "Invalid table '" << table << "'" << std::endl;
+ return -EINVAL;
+ }
+ } else if (mode == "show") {
+ const std::string table = std::string(argv[2]);
+ if (table == "session") {
+ r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
+ return TableHandlerOmap<SessionMapStore>(rank, "sessionmap", false).load_and_dump(&io, f);
+ }, &jf);
+ } else if (table == "inode") {
+ r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
+ return TableHandler<InoTable>(rank, "inotable", true).load_and_dump(&io, f);;
+ }, &jf);
+ } else if (table == "snap") {
+ jf.open_object_section("show_snap_table");
+ {
+ r = TableHandler<SnapServer>(
+ mds_role_t(), "snaptable", true).load_and_dump(&io, &jf);
+ jf.dump_int("result", r);
+ }
+ jf.close_section();
+ } else {
+ cerr << "Invalid table '" << table << "'" << std::endl;
+ return -EINVAL;
+ }
+ } else if (mode == "take_inos") {
+ const std::string ino_str = std::string(argv[2]);
+ std::string ino_err;
+ inodeno_t ino = strict_strtoll(ino_str.c_str(), 10, &ino_err);
+ if (!ino_err.empty()) {
+ derr << "Bad ino '" << ino_str << "'" << dendl;
+ return -EINVAL;
+ }
+ r = apply_role_fn([this, ino](mds_role_t rank, Formatter *f) -> int {
+ return InoTableHandler(rank).take_inos(&io, ino, f);
+ }, &jf);
+ } else {
+ cerr << "Invalid mode '" << mode << "'" << std::endl;
+ return -EINVAL;
+ }
+
+ // Subcommand should have written to formatter, flush it
+ jf.flush(std::cout);
+ std::cout << std::endl;
+ return r;
+}
+