From: David Zafman Date: Wed, 6 Dec 2017 04:57:47 +0000 (-0800) Subject: ceph-objectstore-tool: Add option "dump-import" to examine an export X-Git-Tag: v13.0.1~12^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=a8b8d541ddcc8085a9e7e301948665f3d6c17f0c;p=ceph.git ceph-objectstore-tool: Add option "dump-import" to examine an export Fixes: http://tracker.ceph.com/issues/22086 Signed-off-by: David Zafman --- diff --git a/qa/standalone/special/ceph_objectstore_tool.py b/qa/standalone/special/ceph_objectstore_tool.py index c34b850349b9..efc827ec4406 100755 --- a/qa/standalone/special/ceph_objectstore_tool.py +++ b/qa/standalone/special/ceph_objectstore_tool.py @@ -1027,7 +1027,7 @@ def main(argv): # Specify a bad --op command cmd = (CFSD_PREFIX + "--op oops").format(osd=ONEOSD) - ERRORS += test_failure(cmd, "Must provide --op (info, log, remove, mkfs, fsck, repair, export, export-remove, import, list, fix-lost, list-pgs, dump-journal, dump-super, meta-list, get-osdmap, set-osdmap, get-inc-osdmap, set-inc-osdmap, mark-complete)") + ERRORS += test_failure(cmd, "Must provide --op (info, log, remove, mkfs, fsck, repair, export, export-remove, import, list, fix-lost, list-pgs, dump-journal, dump-super, meta-list, get-osdmap, set-osdmap, get-inc-osdmap, set-inc-osdmap, mark-complete, dump-import)") # Provide just the object param not a command cmd = (CFSD_PREFIX + "object").format(osd=ONEOSD) diff --git a/src/tools/ceph_objectstore_tool.cc b/src/tools/ceph_objectstore_tool.cc index 0a224f03b4af..ee339cd82c79 100644 --- a/src/tools/ceph_objectstore_tool.cc +++ b/src/tools/ceph_objectstore_tool.cc @@ -799,6 +799,21 @@ int ObjectStoreTool::do_export(ObjectStore *fs, coll_t coll, spg_t pgid, return 0; } +int dump_data(Formatter *formatter, bufferlist &bl) +{ + bufferlist::iterator ebliter = bl.begin(); + data_section ds; + ds.decode(ebliter); + + formatter->open_object_section("data_block"); + formatter->dump_unsigned("offset", ds.offset); + formatter->dump_unsigned("len", ds.len); + // XXX: Add option to dump data like od -cx ? + formatter->close_section(); + formatter->flush(cout); + return 0; +} + int get_data(ObjectStore *store, coll_t coll, ghobject_t hoid, ObjectStore::Transaction *t, bufferlist &bl) { @@ -812,6 +827,63 @@ int get_data(ObjectStore *store, coll_t coll, ghobject_t hoid, return 0; } +int dump_attrs( + Formatter *formatter, ghobject_t hoid, + bufferlist &bl) +{ + bufferlist::iterator ebliter = bl.begin(); + attr_section as; + as.decode(ebliter); + + // This could have been handled in the caller if we didn't need to + // support exports that didn't include object_info_t in object_begin. + if (hoid.generation == ghobject_t::NO_GEN && + hoid.hobj.is_head()) { + map::iterator mi = as.data.find(SS_ATTR); + if (mi != as.data.end()) { + SnapSet snapset; + auto p = mi->second.begin(); + snapset.decode(p); + formatter->open_object_section("snapset"); + snapset.dump(formatter); + formatter->close_section(); + } else { + formatter->open_object_section("snapset"); + formatter->dump_string("error", "missing SS_ATTR"); + formatter->close_section(); + } + } + + formatter->open_object_section("attrs"); + formatter->open_array_section("user"); + for (auto kv : as.data) { + // Skip system attributes + if (('_' != kv.first.at(0)) || kv.first.size() == 1) + continue; + formatter->open_object_section("user_attr"); + formatter->dump_string("name", kv.first.substr(1)); + bool b64; + formatter->dump_string("value", cleanbin(kv.second, b64)); + formatter->dump_bool("Base64", b64); + formatter->close_section(); + } + formatter->close_section(); + formatter->open_array_section("system"); + for (auto kv : as.data) { + // Skip user attributes + if (('_' == kv.first.at(0)) && kv.first.size() != 1) + continue; + formatter->open_object_section("sys_attr"); + formatter->dump_string("name", kv.first); + formatter->close_section(); + } + formatter->close_section(); + formatter->close_section(); + formatter->flush(cout); + + return 0; +} + int get_attrs( ObjectStore *store, coll_t coll, ghobject_t hoid, ObjectStore::Transaction *t, bufferlist &bl, @@ -862,6 +934,19 @@ int get_attrs( return 0; } +int dump_omap_hdr(Formatter *formatter, bufferlist &bl) +{ + bufferlist::iterator ebliter = bl.begin(); + omap_hdr_section oh; + oh.decode(ebliter); + + formatter->open_object_section("omap_header"); + formatter->dump_string("value", string(oh.hdr.c_str(), oh.hdr.length())); + formatter->close_section(); + formatter->flush(cout); + return 0; +} + int get_omap_hdr(ObjectStore *store, coll_t coll, ghobject_t hoid, ObjectStore::Transaction *t, bufferlist &bl) { @@ -876,6 +961,29 @@ int get_omap_hdr(ObjectStore *store, coll_t coll, ghobject_t hoid, return 0; } +int dump_omap(Formatter *formatter, bufferlist &bl) +{ + bufferlist::iterator ebliter = bl.begin(); + omap_section os; + os.decode(ebliter); + + formatter->open_object_section("omaps"); + formatter->dump_unsigned("count", os.omap.size()); + formatter->open_array_section("data"); + for (auto o : os.omap) { + formatter->open_object_section("omap"); + formatter->dump_string("name", o.first); + bool b64; + formatter->dump_string("value", cleanbin(o.second, b64)); + formatter->dump_bool("Base64", b64); + formatter->close_section(); + } + formatter->close_section(); + formatter->close_section(); + formatter->flush(cout); + return 0; +} + int get_omap(ObjectStore *store, coll_t coll, ghobject_t hoid, ObjectStore::Transaction *t, bufferlist &bl) { @@ -889,6 +997,73 @@ int get_omap(ObjectStore *store, coll_t coll, ghobject_t hoid, return 0; } +int ObjectStoreTool::dump_object(Formatter *formatter, + bufferlist &bl) +{ + bufferlist::iterator ebliter = bl.begin(); + object_begin ob; + ob.decode(ebliter); + + if (ob.hoid.hobj.is_temp()) { + cerr << "ERROR: Export contains temporary object '" << ob.hoid << "'" << std::endl; + return -EFAULT; + } + + formatter->open_object_section("object"); + formatter->open_object_section("oid"); + ob.hoid.dump(formatter); + formatter->close_section(); + formatter->open_object_section("object_info"); + ob.oi.dump(formatter); + formatter->close_section(); + + bufferlist ebl; + bool done = false; + while(!done) { + sectiontype_t type; + int ret = read_section(&type, &ebl); + if (ret) + return ret; + + //cout << "\tdo_object: Section type " << hex << type << dec << std::endl; + //cout << "\t\tsection size " << ebl.length() << std::endl; + if (type >= END_OF_TYPES) { + cout << "Skipping unknown object section type" << std::endl; + continue; + } + switch(type) { + case TYPE_DATA: + if (dry_run) break; + ret = dump_data(formatter, ebl); + if (ret) return ret; + break; + case TYPE_ATTRS: + if (dry_run) break; + ret = dump_attrs(formatter, ob.hoid, ebl); + if (ret) return ret; + break; + case TYPE_OMAP_HDR: + if (dry_run) break; + ret = dump_omap_hdr(formatter, ebl); + if (ret) return ret; + break; + case TYPE_OMAP: + if (dry_run) break; + ret = dump_omap(formatter, ebl); + if (ret) return ret; + break; + case TYPE_OBJECT_END: + done = true; + break; + default: + cerr << "Unknown section type " << type << std::endl; + return -EFAULT; + } + } + formatter->close_section(); + return 0; +} + int ObjectStoreTool::get_object(ObjectStore *store, coll_t coll, bufferlist &bl, OSDMap &curmap, bool *skipped_objects, @@ -990,6 +1165,49 @@ int ObjectStoreTool::get_object(ObjectStore *store, coll_t coll, return 0; } +int dump_pg_metadata(Formatter *formatter, bufferlist &bl, metadata_section &ms) +{ + bufferlist::iterator ebliter = bl.begin(); + ms.decode(ebliter); + + formatter->open_object_section("metadata_section"); + + formatter->dump_unsigned("pg_disk_version", (int)ms.struct_ver); + formatter->dump_unsigned("map_epoch", ms.map_epoch); + + formatter->open_object_section("OSDMap"); + ms.osdmap.dump(formatter); + formatter->close_section(); + formatter->flush(cout); + cout << std::endl; + + formatter->open_object_section("info"); + ms.info.dump(formatter); + formatter->close_section(); + formatter->flush(cout); + + formatter->open_object_section("log"); + ms.log.dump(formatter); + formatter->close_section(); + formatter->flush(cout); + + formatter->open_object_section("pg_missing_t"); + ms.missing.dump(formatter); + formatter->close_section(); + + // XXX: ms.past_intervals? + + formatter->close_section(); + formatter->flush(cout); + + if (ms.osdmap.get_epoch() != 0 && ms.map_epoch != ms.osdmap.get_epoch()) { + cerr << "FATAL: Invalid OSDMap epoch in export data" << std::endl; + return -EFAULT; + } + + return 0; +} + int get_pg_metadata(ObjectStore *store, bufferlist &bl, metadata_section &ms, const OSDSuperblock& sb, OSDMap& curmap, spg_t pgid) { @@ -1252,6 +1470,106 @@ void filter_divergent_priors(spg_t import_pgid, const OSDMap &curmap, } } +int ObjectStoreTool::dump_import(Formatter *formatter) +{ + bufferlist ebl; + pg_info_t info; + PGLog::IndexedLog log; + //bool skipped_objects = false; + + int ret = read_super(); + if (ret) + return ret; + + if (sh.magic != super_header::super_magic) { + cerr << "Invalid magic number" << std::endl; + return -EFAULT; + } + + if (sh.version > super_header::super_ver) { + cerr << "Can't handle export format version=" << sh.version << std::endl; + return -EINVAL; + } + + formatter->open_object_section("Export"); + + //First section must be TYPE_PG_BEGIN + sectiontype_t type; + ret = read_section(&type, &ebl); + if (ret) + return ret; + if (type == TYPE_POOL_BEGIN) { + cerr << "Dump of pool exports not supported" << std::endl; + return -EINVAL; + } else if (type != TYPE_PG_BEGIN) { + cerr << "Invalid first section type " << std::to_string(type) << std::endl; + return -EFAULT; + } + + bufferlist::iterator ebliter = ebl.begin(); + pg_begin pgb; + pgb.decode(ebliter); + spg_t pgid = pgb.pgid; + + formatter->dump_string("pgid", stringify(pgid)); + formatter->dump_string("cluster_fsid", stringify(pgb.superblock.cluster_fsid)); + formatter->dump_string("features", stringify(pgb.superblock.compat_features)); + + bool done = false; + bool found_metadata = false; + metadata_section ms; + bool objects_started = false; + while(!done) { + ret = read_section(&type, &ebl); + if (ret) + return ret; + + if (debug) { + cerr << "dump_import: Section type " << std::to_string(type) << std::endl; + } + if (type >= END_OF_TYPES) { + cerr << "Skipping unknown section type" << std::endl; + continue; + } + switch(type) { + case TYPE_OBJECT_BEGIN: + if (!objects_started) { + formatter->open_array_section("objects"); + objects_started = true; + } + ret = dump_object(formatter, ebl); + if (ret) return ret; + break; + case TYPE_PG_METADATA: + if (objects_started) + cerr << "WARNING: metadata_section out of order" << std::endl; + ret = dump_pg_metadata(formatter, ebl, ms); + if (ret) return ret; + found_metadata = true; + break; + case TYPE_PG_END: + if (objects_started) { + formatter->close_section(); + } + done = true; + break; + default: + cerr << "Unknown section type " << std::to_string(type) << std::endl; + return -EFAULT; + } + } + + if (!found_metadata) { + cerr << "Missing metadata section" << std::endl; + return -EFAULT; + } + + formatter->close_section(); + formatter->flush(cout); + + return 0; +} + int ObjectStoreTool::do_import(ObjectStore *store, OSDSuperblock& sb, bool force, std::string pgidstr, ObjectStore::Sequencer &osr) @@ -2607,7 +2925,7 @@ int main(int argc, char **argv) "Pool name, mandatory for apply-layout-settings if --pgid is not specified") ("op", po::value(&op), "Arg is one of [info, log, remove, mkfs, fsck, repair, fuse, dup, export, export-remove, import, list, fix-lost, list-pgs, dump-journal, dump-super, meta-list, " - "get-osdmap, set-osdmap, get-inc-osdmap, set-inc-osdmap, mark-complete, apply-layout-settings, update-mon-db]") + "get-osdmap, set-osdmap, get-inc-osdmap, set-inc-osdmap, mark-complete, apply-layout-settings, update-mon-db, dump-import]") ("epoch", po::value(&epoch), "epoch# for get-osdmap and get-inc-osdmap, the current epoch in use if not specified") ("file", po::value(&file), @@ -2713,6 +3031,7 @@ int main(int argc, char **argv) type = "bluestore"; } if (!vm.count("data-path") && + op != "dump-import" && !(op == "dump-journal" && type == "filestore")) { cerr << "Must provide --data-path" << std::endl; usage(desc); @@ -2761,7 +3080,7 @@ int main(int argc, char **argv) } else { file_fd = open(file.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666); } - } else if (op == "import" || op == "set-osdmap" || op == "set-inc-osdmap") { + } else if (op == "import" || op == "dump-import" || op == "set-osdmap" || op == "set-inc-osdmap") { if (!vm.count("file") || file == "-") { if (isatty(STDIN_FILENO)) { cerr << "stdin is a tty and no --file filename specified" << std::endl; @@ -2776,7 +3095,7 @@ int main(int argc, char **argv) ObjectStoreTool tool = ObjectStoreTool(file_fd, dry_run); if (vm.count("file") && file_fd == fd_none && !dry_run) { - cerr << "--file option only applies to import, export, export-remove, " + cerr << "--file option only applies to import, dump-import, export, export-remove, " << "get-osdmap, set-osdmap, get-inc-osdmap or set-inc-osdmap" << std::endl; return 1; } @@ -2825,6 +3144,16 @@ int main(int argc, char **argv) return 0; } + if (op == "dump-import") { + int ret = tool.dump_import(formatter); + if (ret < 0) { + cerr << "dump-import: " + << cpp_strerror(ret) << std::endl; + return 1; + } + return 0; + } + //Verify that data-path really exists struct stat st; if (::stat(dpath.c_str(), &st) == -1) { @@ -3306,7 +3635,7 @@ int main(int argc, char **argv) // before complaining about a bad pgid if (!vm.count("objcmd") && op != "export" && op != "export-remove" && op != "info" && op != "log" && op != "mark-complete") { cerr << "Must provide --op (info, log, remove, mkfs, fsck, repair, export, export-remove, import, list, fix-lost, list-pgs, dump-journal, dump-super, meta-list, " - "get-osdmap, set-osdmap, get-inc-osdmap, set-inc-osdmap, mark-complete)" + "get-osdmap, set-osdmap, get-inc-osdmap, set-inc-osdmap, mark-complete, dump-import)" << std::endl; usage(desc); ret = 1; diff --git a/src/tools/ceph_objectstore_tool.h b/src/tools/ceph_objectstore_tool.h index 91aaeb590113..4072c35ed3b4 100644 --- a/src/tools/ceph_objectstore_tool.h +++ b/src/tools/ceph_objectstore_tool.h @@ -24,6 +24,7 @@ class ObjectStoreTool : public RadosDump : RadosDump(file_fd, dry_run) {} + int dump_import(Formatter *formatter); int do_import(ObjectStore *store, OSDSuperblock& sb, bool force, std::string pgidstr, ObjectStore::Sequencer &osr); @@ -31,6 +32,8 @@ class ObjectStoreTool : public RadosDump pg_info_t &info, epoch_t map_epoch, __u8 struct_ver, const OSDSuperblock& superblock, PastIntervals &past_intervals); + int dump_object(Formatter *formatter, + bufferlist &bl); int get_object( ObjectStore *store, coll_t coll, bufferlist &bl, OSDMap &curmap, bool *skipped_objects,