From 391c72f38cdbbe5864b83f1525d57b087c643954 Mon Sep 17 00:00:00 2001 From: John Spray Date: Mon, 23 Mar 2015 13:20:35 +0000 Subject: [PATCH] tools: implement rados import/export New PoolDump (export) class to dump pool contents in format compatible with ceph-objectstore-tool, and wire up RadosImport class (import). Signed-off-by: John Spray --- src/common/hobject.h | 4 + src/tools/Makefile-client.am | 5 +- src/tools/Makefile.am | 3 +- src/tools/rados/PoolDump.cc | 157 +++++++++++++++++++++++++++++++++++ src/tools/rados/PoolDump.h | 32 +++++++ src/tools/rados/rados.cc | 86 ++++++++++++++----- 6 files changed, 263 insertions(+), 24 deletions(-) create mode 100644 src/tools/rados/PoolDump.cc create mode 100644 src/tools/rados/PoolDump.h diff --git a/src/common/hobject.h b/src/common/hobject.h index c6123d4ff7f4f..d2d3153bc92ae 100644 --- a/src/common/hobject.h +++ b/src/common/hobject.h @@ -49,6 +49,10 @@ public: return key; } + void set_key(const std::string &key_) { + key = key_; + } + string to_str() const; uint32_t get_hash() const { diff --git a/src/tools/Makefile-client.am b/src/tools/Makefile-client.am index b1b43dec9ea84..7d09a9825bf35 100644 --- a/src/tools/Makefile-client.am +++ b/src/tools/Makefile-client.am @@ -16,7 +16,10 @@ rados_SOURCES = \ tools/rados/rados.cc \ tools/rados/rados_import.cc \ tools/rados/rados_export.cc \ - tools/rados/rados_sync.cc + tools/rados/rados_sync.cc \ + tools/RadosDump.cc \ + tools/RadosImport.cc \ + tools/rados/PoolDump.cc rados_SOURCES += common/obj_bencher.cc # needs cleanup so it can go in libcommon.la rados_LDADD = libcls_lock_client.la $(LIBRADOS) $(CEPH_GLOBAL) bin_PROGRAMS += rados diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index df3ed892aac3a..508bcc7c0bd0b 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -57,4 +57,5 @@ noinst_HEADERS += \ tools/rados/rados_sync.h \ tools/RadosDump.h \ tools/RadosImport.h\ - tools/ceph_objectstore_tool.h + tools/ceph_objectstore_tool.h \ + tools/rados/PoolDump.h diff --git a/src/tools/rados/PoolDump.cc b/src/tools/rados/PoolDump.cc new file mode 100644 index 0000000000000..4fac45cdfb008 --- /dev/null +++ b/src/tools/rados/PoolDump.cc @@ -0,0 +1,157 @@ +// -*- 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 Red Hat + * + * 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/rados/librados.hpp" +#include "common/errno.h" + +#include "PoolDump.h" + +using namespace librados; + +#define dout_subsys ceph_subsys_rados + +/** + * Export RADOS objects from a live cluster + * to a serialized format via a file descriptor. + * + * @returns 0 on success, else error code + */ +int PoolDump::dump(IoCtx *io_ctx) +{ + assert(io_ctx != NULL); + + int r = 0; + write_super(); + + r = write_simple(TYPE_POOL_BEGIN, file_fd); + if (r != 0) { + return r; + } + + librados::NObjectIterator i = io_ctx->nobjects_begin(); + + librados::NObjectIterator i_end = io_ctx->nobjects_end(); + for (; i != i_end; ++i) { + const std::string oid = i->get_oid(); + dout(10) << "OID '" << oid << "'" << dendl; + + // Compose OBJECT_BEGIN + // ==================== + object_begin obj_begin; + obj_begin.hoid.hobj.oid = i->get_oid(); + obj_begin.hoid.hobj.nspace = i->get_nspace(); + obj_begin.hoid.hobj.set_key(i->get_locator()); + + // Only output head, RadosImport only wants that + obj_begin.hoid.hobj.snap = CEPH_NOSNAP; + + // Skip setting object_begin.oi, RadosImport doesn't care + + r = write_section(TYPE_OBJECT_BEGIN, obj_begin, file_fd); + if (r != 0) { + return r; + } + + // Compose TYPE_DATA chunks + // ======================== + const uint32_t op_size = 4096 * 1024; + uint64_t offset = 0; + while (true) { + bufferlist outdata; + r = io_ctx->read(oid, outdata, op_size, offset); + if (r <= 0) { + // Error or no data + break; + } + + r = write_section(TYPE_DATA, + data_section(offset, outdata.length(), outdata), file_fd); + if (r != 0) { + // Output stream error + return r; + } + + if (outdata.length() < op_size) { + // No more data + r = 0; + break; + } + offset += outdata.length(); + } + + // Compose TYPE_ATTRS chunk + // ======================== + std::map xattrs; + r = io_ctx->getxattrs(oid, xattrs); + if (r < 0) { + cerr << "error getting xattr set " << oid << ": " << cpp_strerror(r) + << std::endl; + return r; + } + r = write_section(TYPE_ATTRS, attr_section(xattrs), file_fd); + if (r != 0) { + return r; + } + + // Compose TYPE_OMAP_HDR section + // ============================= + bufferlist omap_header; + r = io_ctx->omap_get_header(oid, &omap_header); + if (r < 0) { + cerr << "error getting omap header " << oid + << ": " << cpp_strerror(r) << std::endl; + return r; + } + r = write_section(TYPE_OMAP_HDR, omap_hdr_section(omap_header), file_fd); + if (r != 0) { + return r; + } + + // Compose TYPE_OMAP + int MAX_READ = 512; + string last_read = ""; + do { + map values; + r = io_ctx->omap_get_vals(oid, last_read, MAX_READ, &values); + if (r < 0) { + cerr << "error getting omap keys " << oid << ": " + << cpp_strerror(r) << std::endl; + return r; + } + if (values.size()) { + last_read = values.rbegin()->first; + } else { + break; + } + + r = write_section(TYPE_OMAP, omap_section(values), file_fd); + if (r != 0) { + return r; + } + r = values.size(); + } while (r == MAX_READ); + r = 0; + + // Close object + // ============= + r = write_simple(TYPE_OBJECT_END, file_fd); + if (r != 0) { + return r; + } + } + + r = write_simple(TYPE_POOL_END, file_fd); + + return r; +} diff --git a/src/tools/rados/PoolDump.h b/src/tools/rados/PoolDump.h new file mode 100644 index 0000000000000..6b4eae50d9b5f --- /dev/null +++ b/src/tools/rados/PoolDump.h @@ -0,0 +1,32 @@ +// -*- 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 Red Hat + * + * 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. + * + */ + + +#ifndef POOL_DUMP_H_ +#define POOL_DUMP_H_ + +#include "tools/RadosDump.h" + +namespace librados { + class IoCtx; +} + +class PoolDump : public RadosDump +{ + public: + PoolDump(int file_fd_) : RadosDump(file_fd_, false) {} + int dump(librados::IoCtx *io_ctx); +}; + +#endif // POOL_DUMP_H_ diff --git a/src/tools/rados/rados.cc b/src/tools/rados/rados.cc index 9125d9a4841fd..2071c9703c6fd 100644 --- a/src/tools/rados/rados.cc +++ b/src/tools/rados/rados.cc @@ -17,7 +17,6 @@ #include "include/rados/librados.hpp" #include "include/rados/rados_types.hpp" #include "rados_sync.h" -using namespace librados; #include "common/config.h" #include "common/ceph_argparse.h" @@ -45,9 +44,14 @@ using namespace librados; #include "include/compat.h" #include "common/hobject.h" +#include "PoolDump.h" +#include "tools/RadosImport.h" + int rados_tool_sync(const std::map < std::string, std::string > &opts, std::vector &args); +using namespace librados; + // two steps seem to be necessary to do this right #define STR(x) _STR(x) #define _STR(x) #x @@ -113,17 +117,10 @@ void usage(ostream& out) " set allocation hint for an object\n" "\n" "IMPORT AND EXPORT\n" -" import [options] \n" -" Upload to \n" -" export [options] \n" -" Download to \n" -" options:\n" -" -f / --force Copy everything, even if it hasn't changed.\n" -" -d / --delete-after After synchronizing, delete unreferenced\n" -" files or objects from the target bucket\n" -" or directory.\n" -" --workers Number of worker threads to spawn \n" -" (default " STR(DEFAULT_NUM_RADOS_WORKER_THREADS) ")\n" +" export [filename]\n" +" Serialize pool contents to a file or standard out.\n" +" import [filename]\n" +" Load pool contents from a file or standard in\n" "\n" "ADVISORY LOCKS\n" " lock list \n" @@ -1434,7 +1431,7 @@ static int rados_tool_common(const std::map < std::string, std::string > &opts, vec.push_back(pool_name); } - map stats; + map stats; ret = rados.get_pool_stats(vec, stats); if (ret < 0) { cerr << "error fetching pool stats: " << cpp_strerror(ret) << std::endl; @@ -1452,11 +1449,11 @@ static int rados_tool_common(const std::map < std::string, std::string > &opts, formatter->open_object_section("stats"); formatter->open_array_section("pools"); } - for (map::iterator i = stats.begin(); + for (map::iterator i = stats.begin(); i != stats.end(); ++i) { const char *pool_name = i->first.c_str(); - pool_stat_t& s = i->second; + librados::pool_stat_t& s = i->second; if (!formatter) { printf("%-15s " "%12lld %12lld %12lld %12lld" @@ -2627,6 +2624,56 @@ static int rados_tool_common(const std::map < std::string, std::string > &opts, << cpp_strerror(ret) << std::endl; goto out; } + } else if (strcmp(nargs[0], "export") == 0) { + // export [filename] + if (!pool_name || nargs.size() > 2) { + usage_exit(); + } + + int file_fd; + if (nargs.size() < 2 || std::string(nargs[1]) == "-") { + file_fd = STDOUT_FILENO; + } else { + file_fd = open(nargs[1], O_WRONLY|O_CREAT|O_TRUNC, 0666); + if (file_fd < 0) { + cerr << "Error opening '" << nargs[1] << "': " + << cpp_strerror(file_fd) << std::endl; + ret = file_fd; + goto out; + } + } + + ret = PoolDump(file_fd).dump(&io_ctx); + if (ret < 0) { + cerr << "error from export: " + << cpp_strerror(ret) << std::endl; + goto out; + } + } else if (strcmp(nargs[0], "import") == 0) { + // import [filename] + if (!pool_name || nargs.size() > 2) { + usage_exit(); + } + + int file_fd; + if (nargs.size() < 2 || std::string(nargs[1]) == "-") { + file_fd = STDIN_FILENO; + } else { + file_fd = open(nargs[1], O_RDONLY); + if (file_fd < 0) { + cerr << "Error opening '" << nargs[1] << "': " + << cpp_strerror(file_fd) << std::endl; + ret = file_fd; + goto out; + } + } + + ret = RadosImport(file_fd, 0, false).import(io_ctx, false); + if (ret < 0) { + cerr << "error from import: " + << cpp_strerror(ret) << std::endl; + goto out; + } } else { cerr << "unrecognized command " << nargs[0] << "; -h or --help for usage" << std::endl; ret = -EINVAL; @@ -2749,11 +2796,6 @@ int main(int argc, const char **argv) cerr << "rados: you must give an action. Try --help" << std::endl; return 1; } - if ((strcmp(args[0], "import") == 0) || (strcmp(args[0], "export") == 0)) { - cout << "The import and export operations are not available" << std::endl; - exit(1); - //return rados_tool_sync(opts, args); - } else { - return rados_tool_common(opts, args); - } + + return rados_tool_common(opts, args); } -- 2.39.5