From: David Zafman Date: Wed, 30 Jan 2013 02:21:51 +0000 (-0800) Subject: osd: create tool to extract pg info and pg log from filestore X-Git-Tag: v0.57~72 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=3c8d7d784778d3480bd2e6e50436235d709449ff;p=ceph.git osd: create tool to extract pg info and pg log from filestore 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 Reviewed-by: Samuel Just --- diff --git a/src/.gitignore b/src/.gitignore index d3cab1a4d1f1..e2038b453b2b 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -112,3 +112,4 @@ Makefile /test_librbd_fsx /scratchtool /scratchtoolpp +/ceph-filestore-dump diff --git a/src/Makefile.am b/src/Makefile.am index 996fd4e22078..47dbb97362c4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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) diff --git a/src/osd/OSD.h b/src/osd/OSD.h index fb6f487bcaa1..b411c177a366 100644 --- a/src/osd/OSD.h +++ b/src/osd/OSD.h @@ -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; diff --git a/src/osd/PG.cc b/src/osd/PG.cc index ff879ec46fc0..6526633b7d78 100644 --- a/src/osd/PG.cc +++ b/src/osd/PG.cc @@ -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 &past_intervals, + hobject_t &biginfo_oid, interval_set &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); /** diff --git a/src/osd/PG.h b/src/osd/PG.h index f6dd8817f727..ba80f8186e6c 100644 --- a/src/osd/PG.h +++ b/src/osd/PG.h @@ -1774,13 +1774,18 @@ public: void add_log_entry(pg_log_entry_t& e, bufferlist& log_bl); void append_log(vector& 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 &past_intervals, + hobject_t &biginfo_oid, interval_set &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 index 000000000000..d470468c7cde --- /dev/null +++ b/src/tools/ceph-filestore-dump.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(&fspath)->required(), + "path to filestore directory, mandatory") + ("journal-path", po::value(&jpath)->required(), + "path to journal, mandatory") + ("pgid", po::value(&pgid)->required(), + "PG id, mandatory") + ("type", po::value(&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 ceph_options, def_args; + vector ceph_option_strings = po::collect_unrecognized( + parsed.options, po::include_positional); + ceph_options.reserve(ceph_option_strings.size()); + for (vector::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 ls; + int r = fs->list_collections(ls); + if (r < 0) { + cerr << "failed to list pgs: " << cpp_strerror(-r) << std::endl; + exit(1); + } + + for (vector::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 past_intervals; + hobject_t biginfo_oid = OSD::make_pg_biginfo_oid(pgid); + interval_set 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; +} +