From: Joao Eduardo Luis Date: Tue, 18 Sep 2012 15:10:39 +0000 (+0100) Subject: mon: Add an offline monitor store converter X-Git-Tag: v0.59~150^2~1^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=19e5098afe87fc9ac89fcf965f9349851da8ff69;p=ceph.git mon: Add an offline monitor store converter This tool will convert an old monitor store format (bobtail) to the new key/value store-backed, single-paxos format. Signed-off-by: Joao Eduardo Luis --- diff --git a/src/.gitignore b/src/.gitignore index f05c939cbc70..82c302ddecf6 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -55,6 +55,7 @@ /xattr_bench /rest-bench /rbd-fuse +/mon_store_converter dev mondata mnt diff --git a/src/Makefile.am b/src/Makefile.am index cd17a3a9923d..9b9d7ed00459 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -79,6 +79,14 @@ ceph_mon_LDADD = libmon.a $(LIBOS_LDA) $(LIBGLOBAL_LDA) ceph_mon_CXXFLAGS = ${CRYPTO_CXXFLAGS} ${AM_CXXFLAGS} $(LEVELDB_INCLUDE) bin_PROGRAMS += ceph-mon +mon_store_converter_SOURCES = mon_store_converter.cc \ + mon/MonitorStore.cc +mon_store_converter_LDFLAGS = ${AM_LDFLAGS} +mon_store_converter_LDADD = $(LIBOS_LDA) $(LIBGLOBAL_LDA) +mon_store_converter_CXXFLAGS = ${AM_CXXFLAGS} $(LEVELDB_INCLUDE) +bin_PROGRAMS += mon_store_converter + + # osd ceph_osd_SOURCES = ceph_osd.cc objclass/class_debug.cc \ objclass/class_api.cc @@ -159,7 +167,7 @@ base: ceph-mon ceph-osd ceph-mds \ ceph-syn \ rados radosgw librados-config \ ceph-conf monmaptool osdmaptool crushtool ceph-authtool \ - init-ceph mkcephfs + init-ceph mkcephfs mon_store_converter # fuse targets? diff --git a/src/mon_store_converter.cc b/src/mon_store_converter.cc new file mode 100644 index 000000000000..3d8e53067e8f --- /dev/null +++ b/src/mon_store_converter.cc @@ -0,0 +1,336 @@ +// -*- 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) 2012 Inktank, Inc. +* +* 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/types.h" +#include "include/buffer.h" +#include "common/ceph_argparse.h" +#include "global/global_init.h" +#include "common/debug.h" +#include "common/config.h" + +#include "mon/MonitorDBStore.h" +#include "mon/MonitorStore.h" + +using namespace std; + +class MonitorStoreConverter { + + boost::scoped_ptr db; + boost::scoped_ptr store; + + set gvs; + version_t highest_last_pn; + version_t highest_accepted_pn; + + static const int PAXOS_MAX_VERSIONS = 50; + string MONITOR_NAME; + + public: + MonitorStoreConverter(string &store_path, string &db_store_path) + : db(0), store(0), + highest_last_pn(0), highest_accepted_pn(0), + MONITOR_NAME("monitor") + { + MonitorStore *store_ptr = new MonitorStore(store_path); + assert(!store_ptr->mount()); + store.reset(store_ptr); + + MonitorDBStore *db_ptr = new MonitorDBStore(db_store_path); + db.reset(db_ptr); + } + + int convert() { + if (db->open(std::cerr) >= 0) { + std::cerr << "store already exists" << std::endl; + return EEXIST; + } + assert(!db->create_and_open(std::cerr)); + if (db->exists("mon_convert", "on_going")) { + std::cout << __func__ << " found a mon store in mid-convertion; abort!" + << std::endl; + return EEXIST; + } + _mark_convert_start(); + _convert_monitor(); + _convert_machines(); + _mark_convert_finish(); + + std::cout << __func__ << " finished conversion" << std::endl; + + return 0; + } + + bool match() { + return true; + } + + private: + + set _get_machines_names() { + set names; + names.insert("auth"); + names.insert("logm"); + names.insert("mdsmap"); + names.insert("monmap"); + names.insert("osdmap"); + names.insert("pgmap"); + + return names; + } + + void _mark_convert_start() { + MonitorDBStore::Transaction tx; + tx.put("mon_convert", "on_going", 1); + db->apply_transaction(tx); + } + + void _mark_convert_finish() { + MonitorDBStore::Transaction tx; + tx.erase("mon_convert", "on_going"); + db->apply_transaction(tx); + } + + void _convert_monitor() { + + assert(store->exists_bl_ss("magic")); + assert(store->exists_bl_ss("keyring")); + assert(store->exists_bl_ss("feature_set")); + assert(store->exists_bl_ss("election_epoch")); + + MonitorDBStore::Transaction tx; + + if (store->exists_bl_ss("joined")) { + version_t joined = store->get_int("joined"); + tx.put(MONITOR_NAME, "joined", joined); + } + + vector keys; + keys.push_back("magic"); + keys.push_back("feature_set"); + keys.push_back("election_epoch"); + keys.push_back("cluster_uuid"); + + vector::iterator it; + for (it = keys.begin(); it != keys.end(); ++it) { + if (!store->exists_bl_ss((*it).c_str())) + continue; + + bufferlist bl; + int r = store->get_bl_ss(bl, (*it).c_str(), 0); + assert(r > 0); + tx.put(MONITOR_NAME, *it, bl); + } + + assert(!tx.empty()); + db->apply_transaction(tx); + } + + void _convert_machines(string machine) { + std::cout << __func__ << " " << machine << std::endl; + + version_t first_committed = + store->get_int(machine.c_str(), "first_committed"); + version_t last_committed = + store->get_int(machine.c_str(), "last_committed"); + + version_t accepted_pn = store->get_int(machine.c_str(), "accepted_pn"); + version_t last_pn = store->get_int(machine.c_str(), "last_pn"); + + if (accepted_pn > highest_accepted_pn) + highest_accepted_pn = accepted_pn; + if (last_pn > highest_last_pn) + highest_last_pn = last_pn; + + string machine_gv(machine); + machine_gv.append("_gv"); + bool has_gv = true; + + if (!store->exists_bl_ss(machine_gv.c_str())) { + std::cerr << __func__ << " " << machine + << " no gv dir '" << machine_gv << "'" << std::endl; + has_gv = false; + } + + for (version_t ver = first_committed; ver <= last_committed; ver++) { + if (!store->exists_bl_sn(machine.c_str(), ver)) { + std::cerr << __func__ << " " << machine + << " ver " << ver << " dne" << std::endl; + continue; + } + + bufferlist bl; + int r = store->get_bl_sn(bl, machine.c_str(), ver); + assert(r >= 0); + std::cout << __func__ << " " << machine + << " ver " << ver << " bl " << bl.length() << std::endl; + + MonitorDBStore::Transaction tx; + tx.put(machine, ver, bl); + tx.put(machine, "last_committed", ver); + + if (has_gv && store->exists_bl_sn(machine_gv.c_str(), ver)) { + stringstream s; + s << ver; + string ver_str = s.str(); + + version_t gv = store->get_int(machine_gv.c_str(), ver_str.c_str()); + std::cerr << __func__ << " " << machine + << " ver " << ver << " -> " << gv << std::endl; + + if (gvs.count(gv) == 0) { + gvs.insert(gv); + } else { + std::cerr << __func__ << " " << machine + << " gv " << gv << " already exists" + << std::endl; + } + + bufferlist tx_bl; + tx.encode(tx_bl); + tx.put("paxos", gv, tx_bl); + } + db->apply_transaction(tx); + } + + version_t lc = db->get(machine, "last_committed"); + assert(lc == last_committed); + + MonitorDBStore::Transaction tx; + tx.put(machine, "first_committed", first_committed); + tx.put(machine, "last_committed", last_committed); + + if (store->exists_bl_ss(machine.c_str(), "latest")) { + bufferlist latest_bl_raw; + int r = store->get_bl_ss(latest_bl_raw, machine.c_str(), "latest"); + assert(r >= 0); + if (!latest_bl_raw.length()) { + std::cerr << __func__ << " machine " << machine + << " skip latest with size 0" << std::endl; + goto out; + } + + tx.put(machine, "latest", latest_bl_raw); + + bufferlist::iterator lbl_it = latest_bl_raw.begin(); + bufferlist latest_bl; + version_t latest_ver; + ::decode(latest_ver, lbl_it); + ::decode(latest_bl, lbl_it); + + std::cout << __func__ << " machine " << machine + << " latest ver " << latest_ver << std::endl; + + tx.put(machine, "full_latest", latest_ver); + stringstream os; + os << "full_" << latest_ver; + tx.put(machine, os.str(), latest_bl); + } + out: + db->apply_transaction(tx); + } + + void _convert_paxos() { + assert(gvs.size() > 0); + + set::reverse_iterator rit = gvs.rbegin(); + version_t highest_gv = *rit; + version_t last_gv = highest_gv; + + int n = 0; + for (; (rit != gvs.rend()) && (n < PAXOS_MAX_VERSIONS); ++rit, ++n) { + + version_t gv = *rit; + + if (last_gv == gv) + continue; + + if ((last_gv - gv) > 1) { + // we are done; we found a gap and we are only interested in keeping + // contiguous paxos versions. + break; + } + last_gv = gv; + } + + // erase all paxos versions between [first, last_gv[, with first being the + // first gv in the map. + MonitorDBStore::Transaction tx; + set::iterator it = gvs.begin(); + std::cout << __func__ << " first gv " << (*it) + << " last gv " << last_gv << std::endl; + for (; it != gvs.end() && (*it < last_gv); ++it) { + tx.erase("paxos", *it); + } + tx.put("paxos", "first_committed", last_gv); + tx.put("paxos", "last_committed", highest_gv); + tx.put("paxos", "accepted_pn", highest_accepted_pn); + tx.put("paxos", "last_pn", highest_last_pn); + db->apply_transaction(tx); + } + + void _convert_machines() { + + set machine_names = _get_machines_names(); + set::iterator it = machine_names.begin(); + + std::cout << __func__ << std::endl; + + for (; it != machine_names.end(); ++it) { + _convert_machines(*it); + } + + _convert_paxos(); + } +}; + + +void usage(const char *pname) +{ + std::cerr << "Usage: " << pname << " \n" + << std::endl; +} + +int main(int argc, const char *argv[]) +{ + vector def_args; + vector args; + const char *our_name = argv[0]; + argv_to_vec(argc, argv, args); + + global_init(&def_args, args, + CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, + CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); + common_init_finish(g_ceph_context); + g_ceph_context->_conf->apply_changes(NULL); + + if (args.size() < 1) { + usage(our_name); + return 1; + } + string store(args[0]); + string new_store(store); + MonitorStoreConverter converter(store, new_store); + assert(!converter.convert()); + assert(converter.match()); + + std::cout << "store successfully converted to new format" << std::endl; + + return 0; +}