]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
tools: create cephfs-table-tool 3335/head
authorJohn Spray <john.spray@redhat.com>
Fri, 2 Jan 2015 17:48:25 +0000 (17:48 +0000)
committerJohn Spray <john.spray@redhat.com>
Fri, 16 Jan 2015 00:45:25 +0000 (00:45 +0000)
It was unnatural to shoehorn resetting tables
into the journaltool.  This new tool initially
can simply dump or reset the session/snap/ino
tables, and would also be a place for any
more complex operations in future.

Signed-off-by: John Spray <john.spray@redhat.com>
ceph.spec.in
debian/ceph-mds.install
src/.gitignore
src/tools/Makefile.am
src/tools/cephfs/TableTool.cc [new file with mode: 0644]
src/tools/cephfs/TableTool.h [new file with mode: 0644]
src/tools/cephfs/cephfs-table-tool.cc [new file with mode: 0644]

index 126433eef0d6bb3ed6eb9e3dd0c23ece99e36de6..0886cbf915651caffe637ed7bc9ffda3022c7740 100644 (file)
@@ -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
index df3cbe4e637f3445a2d0958ef383485a89259709..e76a3a1916be98276bb03b72d3258901d58255a6 100644 (file)
@@ -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
index 6e76998b46e66175bb6110fbd9f70b83c1e2e52d..90d0cbc903eb4964d65e57f364329f5ba76b8a90 100644 (file)
@@ -11,6 +11,7 @@ Makefile
 /ceph-authtool
 /ceph-client-debug
 /cephfs-journal-tool
+/cephfs-table-tool
 /ceph-conf
 /ceph-coverage
 /ceph-crush-location
index d92ed558536334f3f589cebb7b6dfb684943946b..72ea57ac50b10649b427e2fe486bd60a59243f2b 100644 (file)
@@ -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 (file)
index 0000000..4b22de0
--- /dev/null
@@ -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 (file)
index 0000000..0f43a73
--- /dev/null
@@ -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 (file)
index 0000000..749de6f
--- /dev/null
@@ -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;
+}
+
+