]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
osd: create tool to extract pg info and pg log from filestore
authorDavid Zafman <david.zafman@inktank.com>
Wed, 30 Jan 2013 02:21:51 +0000 (18:21 -0800)
committerDavid Zafman <david.zafman@inktank.com>
Wed, 30 Jan 2013 22:21:47 +0000 (14:21 -0800)
New application ceph-filestore-dump created that mounts filstore
and can dump info or log in JSON when an OSD is not running.

Feature: #3890

Signed-off-by: David Zafman <david.zafman@inktank.com>
Reviewed-by: Samuel Just <sam.just@inktank.com>
src/.gitignore
src/Makefile.am
src/osd/OSD.h
src/osd/PG.cc
src/osd/PG.h
src/tools/ceph-filestore-dump.cc [new file with mode: 0644]

index d3cab1a4d1f132854a0dcc5f2687ca3f29d38bc7..e2038b453b2b13f46559afb58067523e56cf6722 100644 (file)
@@ -112,3 +112,4 @@ Makefile
 /test_librbd_fsx
 /scratchtool
 /scratchtoolpp
+/ceph-filestore-dump
index 996fd4e220788d7c0b0091fe62ff260b7b3432d0..47dbb97362c4150ab06aabc1f3a483a1db79783e 100644 (file)
@@ -110,7 +110,14 @@ ceph_conf_SOURCES = ceph_conf.cc
 ceph_conf_LDADD = $(LIBGLOBAL_LDA)
 ceph_authtool_SOURCES = ceph_authtool.cc
 ceph_authtool_LDADD = $(LIBGLOBAL_LDA)
-bin_PROGRAMS += ceph ceph-conf ceph-authtool
+ceph_filestore_dump_SOURCES = tools/ceph-filestore-dump.cc objclass/class_debug.cc \
+              objclass/class_api.cc
+ceph_filestore_dump_SOURCES += perfglue/disabled_heap_profiler.cc
+ceph_filestore_dump_LDADD = libosd.a $(LIBOS_LDA) $(LIBGLOBAL_LDA) -lboost_program_options
+if LINUX
+ceph_filestore_dump_LDADD += -ldl
+endif
+bin_PROGRAMS += ceph ceph-conf ceph-authtool ceph-filestore-dump
 
 monmaptool_SOURCES = monmaptool.cc
 monmaptool_LDADD = $(LIBGLOBAL_LDA)
index fb6f487bcaa1ef03c918ffd5446c1eb4fc922474..b411c177a3665c7cd0d0a3d9664d77ae27663a2a 100644 (file)
@@ -461,7 +461,7 @@ public:
     return hobject_t(sobject_t(object_t(foo), 0)); 
   }
 
-  hobject_t make_pg_log_oid(pg_t pg) {
+  static hobject_t make_pg_log_oid(pg_t pg) {
     stringstream ss;
     ss << "pglog_" << pg;
     string s;
@@ -469,7 +469,7 @@ public:
     return hobject_t(sobject_t(object_t(s.c_str()), 0));
   }
   
-  hobject_t make_pg_biginfo_oid(pg_t pg) {
+  static hobject_t make_pg_biginfo_oid(pg_t pg) {
     stringstream ss;
     ss << "pginfo_" << pg;
     string s;
index ff879ec46fc05ee46ac5abb839f908c721bdbb58..6526633b7d78fac15b1c3191869f186c8b95df90 100644 (file)
@@ -2595,7 +2595,9 @@ std::string PG::get_corrupt_pg_log_name() const
   return buf;
 }
 
-void PG::read_state(ObjectStore *store, bufferlist &bl)
+int PG::read_info(ObjectStore *store, const coll_t coll, bufferlist &bl,
+  pg_info_t &info, map<epoch_t,pg_interval_t> &past_intervals,
+  hobject_t &biginfo_oid, interval_set<snapid_t>  &snap_collections)
 {
   bufferlist::iterator p = bl.begin();
   __u8 struct_v;
@@ -2614,7 +2616,9 @@ void PG::read_state(ObjectStore *store, bufferlist &bl)
     ::decode(struct_v, p);
   } else {
     bl.clear();
-    store->read(coll_t::META_COLL, biginfo_oid, 0, 0, bl);
+    int r = store->read(coll_t::META_COLL, biginfo_oid, 0, 0, bl);
+    if (r < 0)
+       return r;
     p = bl.begin();
     ::decode(past_intervals, p);
   }
@@ -2633,9 +2637,19 @@ void PG::read_state(ObjectStore *store, bufferlist &bl)
     if (struct_v >= 4)
       ::decode(info, p);
   }
+  return 0;
+}
+
+void PG::read_state(ObjectStore *store, bufferlist &bl)
+{
+  int r = read_info(store, coll, bl, info, past_intervals, biginfo_oid,
+    snap_collections);
+  assert(r >= 0);
 
   try {
-    read_log(store);
+    ostringstream oss;
+    read_log(store, coll, log_oid, info, ondisklog, log, missing, oss, this);
+    osd->clog.error() << oss;
   }
   catch (const buffer::error &e) {
     string cr_log_coll_name(get_corrupt_pg_log_name());
@@ -5024,7 +5038,14 @@ std::ostream& operator<<(std::ostream& oss,
   return oss;
 }
 
-void PG::read_log(ObjectStore *store)
+/*---------------------------------------------------*/
+// Handle staitc function so it can use dout()
+#undef dout_prefix
+#define dout_prefix if (passedpg) _prefix(_dout, passedpg)
+
+void PG::read_log(ObjectStore *store, coll_t coll, hobject_t log_oid,
+  const pg_info_t &info, OndiskLog &ondisklog, IndexedLog &log,
+  pg_missing_t &missing, ostringstream &oss, const PG *passedpg)
 {
   // load bounds
   ondisklog.tail = ondisklog.head = 0;
@@ -5085,7 +5106,7 @@ void PG::read_log(ObjectStore *store)
       // [repair] in order?
       if (e.version < last) {
        dout(0) << "read_log " << pos << " out of order entry " << e << " follows " << last << dendl;
-       osd->clog.error() << info.pgid << " log has out of order entry "
+       oss << info.pgid << " log has out of order entry "
              << e << " following " << last << "\n";
        reorder = true;
       }
@@ -5097,7 +5118,7 @@ void PG::read_log(ObjectStore *store)
       if (last.version == e.version.version) {
        dout(0) << "read_log  got dup " << e.version << " (last was " << last << ", dropping that one)" << dendl;
        log.log.pop_back();
-       osd->clog.error() << info.pgid << " read_log got dup "
+       oss << info.pgid << " read_log got dup "
              << e.version << " after " << last << "\n";
       }
 
@@ -5136,7 +5157,7 @@ void PG::read_log(ObjectStore *store)
 
       // [repair] at end of log?
       if (!p.end() && e.version == info.last_update) {
-       osd->clog.error() << info.pgid << " log has extra data at "
+       oss << info.pgid << " log has extra data at "
           << endpos << "~" << (ondisklog.head-endpos) << " after "
           << info.last_update << "\n";
 
@@ -5177,7 +5198,7 @@ void PG::read_log(ObjectStore *store)
       if (i->is_delete()) continue;
       
       bufferlist bv;
-      int r = osd->store->getattr(coll, i->soid, OI_ATTR, bv);
+      int r = store->getattr(coll, i->soid, OI_ATTR, bv);
       if (r >= 0) {
        object_info_t oi(bv);
        if (oi.version < i->version) {
@@ -5197,7 +5218,7 @@ void PG::read_log(ObjectStore *store)
       if (did.count(i->second)) continue;
       did.insert(i->second);
       bufferlist bv;
-      int r = osd->store->getattr(coll, i->second, OI_ATTR, bv);
+      int r = store->getattr(coll, i->second, OI_ATTR, bv);
       if (r >= 0) {
        object_info_t oi(bv);
        /**
index f6dd8817f7276a11d9576ca9652d8e2c366cda4c..ba80f8186e6c9927d151953c6504774541c644e7 100644 (file)
@@ -1774,13 +1774,18 @@ public:
   void add_log_entry(pg_log_entry_t& e, bufferlist& log_bl);
   void append_log(vector<pg_log_entry_t>& logv, eversion_t trim_to, ObjectStore::Transaction &t);
 
-  void read_log(ObjectStore *store);
+  static void read_log(ObjectStore *store, coll_t coll, hobject_t log_oid,
+    const pg_info_t &info, OndiskLog &ondisklog, IndexedLog &log,
+    pg_missing_t &missing, ostringstream &oss, const PG *passedpg = NULL);
   bool check_log_for_corruption(ObjectStore *store);
   void trim(ObjectStore::Transaction& t, eversion_t v);
   void trim_ondisklog(ObjectStore::Transaction& t);
   void trim_peers();
 
   std::string get_corrupt_pg_log_name() const;
+  static int read_info(ObjectStore *store, const coll_t coll,
+    bufferlist &bl, pg_info_t &info, map<epoch_t,pg_interval_t> &past_intervals,
+    hobject_t &biginfo_oid, interval_set<snapid_t>  &snap_collections);
   void read_state(ObjectStore *store, bufferlist &bl);
   static epoch_t peek_map_epoch(ObjectStore *store,
                                coll_t coll, bufferlist *bl);
diff --git a/src/tools/ceph-filestore-dump.cc b/src/tools/ceph-filestore-dump.cc
new file mode 100644 (file)
index 0000000..d470468
--- /dev/null
@@ -0,0 +1,263 @@
+// -*- 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) 2013 Inktank
+ *
+ * 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 <boost/scoped_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/program_options/option.hpp>
+#include <boost/program_options/options_description.hpp>
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/cmdline.hpp>
+#include <boost/program_options/parsers.hpp>
+#include <iostream>
+#include <set>
+#include <sstream>
+#include <stdlib.h>
+#include <fstream>
+#include <iostream>
+
+#include "common/Formatter.h"
+
+#include "global/global_init.h"
+#include "os/ObjectStore.h"
+#include "os/FileStore.h"
+#include "common/perf_counters.h"
+#include "common/errno.h"
+#include "osd/PG.h"
+#include "osd/OSD.h"
+
+namespace po = boost::program_options;
+using namespace std;
+
+static void invalid_path(string &path)
+{
+  cout << "Invalid path to osd store specified: " << path << "\n";
+  exit(1);
+}
+
+int main(int argc, char **argv)
+{
+  string fspath, jpath, pgid, type;
+  Formatter *formatter = new JSONFormatter(true);
+
+  po::options_description desc("Allowed options");
+  desc.add_options()
+    ("help", "produce help message")
+    ("filestore-path", po::value<string>(&fspath)->required(),
+     "path to filestore directory, mandatory")
+    ("journal-path", po::value<string>(&jpath)->required(),
+     "path to journal, mandatory")
+    ("pgid", po::value<string>(&pgid)->required(),
+     "PG id, mandatory")
+    ("type", po::value<string>(&type)->required(),
+     "Type which is 'info' or 'log'")
+    ("debug", "Enable diagnostic output to stderr")
+    ;
+
+  po::variables_map vm;
+  po::parsed_options parsed =
+    po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
+  po::store( parsed, vm);
+  try {
+    po::notify(vm);
+  }
+  catch(...) {
+    cout << desc << "\n";
+    exit(1);
+  }
+     
+  //Never get here with required() options
+  if (vm.count("help")) {
+    cout << desc << "\n";
+    exit(1);
+  }
+
+  vector<const char *> ceph_options, def_args;
+  vector<string> ceph_option_strings = po::collect_unrecognized(
+    parsed.options, po::include_positional);
+  ceph_options.reserve(ceph_option_strings.size());
+  for (vector<string>::iterator i = ceph_option_strings.begin();
+       i != ceph_option_strings.end();
+       ++i) {
+    ceph_options.push_back(i->c_str());
+  }
+
+  //Suppress derr() output to stderr by default
+  if (!vm.count("debug")) {
+    close(2);
+    (void)open("/dev/null", O_WRONLY);
+  }
+
+  global_init(
+    &def_args, ceph_options, CEPH_ENTITY_TYPE_OSD,
+    CODE_ENVIRONMENT_UTILITY, 0);
+    //CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+  common_init_finish(g_ceph_context);
+  g_ceph_context->_conf->apply_changes(NULL);
+  g_conf = g_ceph_context->_conf;
+
+  if (!vm.count("filestore-path") || !vm.count("journal-path")) {
+    cout << "Must provide filestore-path and journal-path" << std::endl
+        << desc << std::endl;
+    return 1;
+  }
+
+  if (vm.count("help")) {
+    cout << desc << std::endl;
+    return 1;
+  }
+
+  if (fspath.length() == 0 || jpath.length() == 0 || pgid.length() == 0 ||
+    (type != "info" && type != "log")) {
+    cerr << "Invalid params" << std::endl;
+    exit(1);
+  }
+
+  //Verify that fspath really is an osd store
+  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);
+  }
+
+  pg_t arg_pgid;
+  if (!arg_pgid.parse(pgid.c_str())) {
+    cerr << "Invalid pgid '" << pgid << "' specified" << std::endl;
+    exit(1);
+  }
+
+  int ret = 0;
+
+  ObjectStore *fs = new FileStore(fspath, jpath);
+  
+  if (fs->mount() < 0) {
+    cout << "mount failed" << std::endl;
+    return 1;
+  }
+
+  bool found = false;
+  vector<coll_t> ls;
+  int r = fs->list_collections(ls);
+  if (r < 0) {
+    cerr << "failed to list pgs: " << cpp_strerror(-r) << std::endl;
+    exit(1);
+  }
+
+  for (vector<coll_t>::iterator it = ls.begin();
+       it != ls.end();
+       it++) {
+    coll_t coll = *it;
+    pg_t pgid;
+    snapid_t snap;
+    if (!it->is_pg(pgid, snap)) {
+      continue;
+    }
+
+    if (pgid != arg_pgid) {
+      continue;
+    }
+    if (snap != CEPH_NOSNAP) {
+      cout << "load_pgs skipping snapped dir " << coll
+              << " (pg " << pgid << " snap " << snap << ")" << std::endl;
+      continue;
+    }
+
+    bufferlist bl;
+    epoch_t map_epoch = PG::peek_map_epoch(fs, coll, &bl);
+    (void)map_epoch;
+
+    found = true;
+
+    pg_info_t info;
+    map<epoch_t,pg_interval_t> past_intervals;
+    hobject_t biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
+    interval_set<snapid_t> snap_collections;
+
+    int r = PG::read_info(fs, coll, bl, info, past_intervals, biginfo_oid,
+      snap_collections);
+    if (r < 0) {
+      cerr << "read_info error " << cpp_strerror(-r) << std::endl;
+      ret = 1;
+      continue;
+    }
+
+    if (type == "info") {
+      formatter->open_object_section("info");
+      info.dump(formatter);
+      formatter->close_section();
+      formatter->flush(cout);
+      cout << std::endl;
+      break;
+    } else if (type == "log") {
+      PG::OndiskLog ondisklog;
+      PG::IndexedLog log;
+      pg_missing_t missing;
+      hobject_t logoid = OSD::make_pg_log_oid(pgid);
+      try {
+        ostringstream oss;
+        PG::read_log(fs, coll, logoid, info, ondisklog, log, missing, oss);
+        if (vm.count("debug"))
+          cerr << oss;
+      }
+      catch (const buffer::error &e) {
+        cerr << "read_log threw exception error", e.what();
+        ret = 1;
+        break;
+      }
+      
+      formatter->open_object_section("log");
+      log.dump(formatter);
+      formatter->close_section();
+      formatter->flush(cout);
+      cout << std::endl;
+      formatter->open_object_section("missing");
+      missing.dump(formatter);
+      formatter->close_section();
+      formatter->flush(cout);
+      cout << std::endl;
+
+    }
+  }
+
+  if (!found) {
+    cerr << "PG '" << arg_pgid << "' not found" << std::endl;
+    ret = 1;
+  }
+
+  if (fs->umount() < 0) {
+    cerr << "umount failed" << std::endl;
+    return 1;
+  }
+
+  return ret;
+}
+