1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2015 John Spray <john.spray@redhat.com>
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
15 #include "common/ceph_argparse.h"
16 #include "common/errno.h"
18 #include "mds/SessionMap.h"
19 #include "mds/InoTable.h"
20 #include "mds/SnapServer.h"
22 #include "TableTool.h"
25 #define dout_context g_ceph_context
26 #define dout_subsys ceph_subsys_mds
28 #define dout_prefix *_dout << __func__ << ": "
30 void TableTool::usage()
32 std::cout << "Usage: \n"
33 << " cephfs-table-tool <all|[mds rank]> <reset|show> <session|snap|inode>"
34 << " cephfs-table-tool <all|[mds rank]> <take_inos> <max_ino>"
37 generic_client_usage();
42 * For a function that takes an MDS role as an argument and
43 * returns an error code, execute it on the roles specified
46 int TableTool::apply_role_fn(std::function<int(mds_role_t, Formatter *)> fptr, Formatter *f)
52 f->open_object_section("ranks");
54 for (auto role : role_selector.get_roles()) {
55 std::ostringstream rank_str;
56 rank_str << role.rank;
57 f->open_object_section(rank_str.str().c_str());
59 f->open_object_section("data");
60 int rank_r = fptr(role, f);
64 f->dump_int("result", rank_r);
77 * This class wraps an MDS table class (SessionMap, SnapServer, InoTable)
78 * with offline load/store code such that we can do offline dumps and resets
85 // The RADOS object ID for the table
86 std::string object_name;
88 // The role in question (may be NONE)
91 // Whether this is an MDSTable subclass (i.e. has leading version field to decode)
95 TableHandler(mds_role_t r, std::string const &name, bool mds_table_)
96 : role(r), mds_table(mds_table_)
98 // Compose object name of the table we will dump
99 std::ostringstream oss;
101 if (!role.is_none()) {
105 object_name = oss.str();
108 int load_and_dump(librados::IoCtx *io, Formatter *f)
115 int read_r = io->read(object_name, table_bl, 0, 0);
117 bufferlist::iterator q = table_bl.begin();
121 ::decode(version, q);
122 f->dump_int("version", version);
125 table_inst.set_rank(role.rank);
126 table_inst.decode(q);
130 } catch (buffer::error &e) {
131 derr << "table " << object_name << " is corrupt" << dendl;
135 derr << "error reading table object " << object_name
136 << ": " << cpp_strerror(read_r) << dendl;
141 int reset(librados::IoCtx *io)
144 // Compose new (blank) table
145 table_inst.set_rank(role.rank);
146 table_inst.reset_state();
147 // Write the table out
148 return write(table_inst, io);
153 int write(const A &table_inst, librados::IoCtx *io)
157 version_t version = 1;
158 ::encode(version, new_bl);
160 table_inst.encode_state(new_bl);
162 // Write out new table
163 int r = io->write_full(object_name, new_bl);
165 derr << "error writing table object " << object_name
166 << ": " << cpp_strerror(r) << dendl;
174 template <typename A>
175 class TableHandlerOmap
178 // The RADOS object ID for the table
179 std::string object_name;
181 // The role (rank may be NONE)
184 // Whether this is an MDSTable subclass (i.e. has leading version field to decode)
188 TableHandlerOmap(mds_role_t r, std::string const &name, bool mds_table_)
189 : role(r), mds_table(mds_table_)
191 // Compose object name of the table we will dump
192 std::ostringstream oss;
194 if (!role.is_none()) {
198 object_name = oss.str();
201 int load_and_dump(librados::IoCtx *io, Formatter *f)
206 // Read in the header
207 bufferlist header_bl;
208 int r = io->omap_get_header(object_name, &header_bl);
210 derr << "error reading header on '" << object_name << "': "
211 << cpp_strerror(r) << dendl;
217 table_inst.set_rank(role.rank);
219 table_inst.decode_header(header_bl);
220 } catch (buffer::error &e) {
221 derr << "table " << object_name << " is corrupt" << dendl;
225 // Read and decode OMAP values in chunks
226 std::string last_key = "";
228 std::map<std::string, bufferlist> values;
229 int r = io->omap_get_vals(object_name, last_key,
230 g_conf->mds_sessionmap_keys_per_op, &values);
233 derr << "error reading values: " << cpp_strerror(r) << dendl;
237 if (values.empty()) {
242 table_inst.decode_values(values);
243 } catch (buffer::error &e) {
244 derr << "table " << object_name << " is corrupt" << dendl;
247 last_key = values.rbegin()->first;
255 int reset(librados::IoCtx *io)
258 table_inst.set_rank(role.rank);
259 table_inst.reset_state();
260 bufferlist header_bl;
261 table_inst.encode_header(&header_bl);
263 // Compose a transaction to clear and write header
264 librados::ObjectWriteOperation op;
266 op.set_op_flags2(LIBRADOS_OP_FLAG_FAILOK);
267 op.omap_set_header(header_bl);
269 return io->operate(object_name, &op);
273 class InoTableHandler : public TableHandler<InoTable>
276 explicit InoTableHandler(mds_role_t r)
277 : TableHandler(r, "inotable", true)
280 int take_inos(librados::IoCtx *io, inodeno_t max, Formatter *f)
283 inst.set_rank(role.rank);
287 if (inst.force_consume_to(max)) {
291 f->dump_int("version", inst.get_version());
299 int TableTool::main(std::vector<const char*> &argv)
303 dout(10) << __func__ << dendl;
307 r = rados.init_with_context(g_ceph_context);
309 derr << "RADOS unavailable, cannot scan filesystem journal" << dendl;
313 dout(4) << "connecting to RADOS..." << dendl;
316 derr << "couldn't connect to cluster: " << cpp_strerror(r) << dendl;
320 // Require at least 3 args <rank> <mode> <arg> [args...]
321 if (argv.size() < 3) {
326 const std::string role_str = std::string(argv[0]);
327 const std::string mode = std::string(argv[1]);
328 const std::string table = std::string(argv[2]);
330 r = role_selector.parse(*fsmap, role_str);
332 derr << "Bad rank selection: " << role_str << "'" << dendl;
336 auto fs = fsmap->get_filesystem(role_selector.get_ns());
337 assert(fs != nullptr);
338 int64_t const pool_id = fs->mds_map.get_metadata_pool();
339 dout(4) << "resolving pool " << pool_id << dendl;
340 std::string pool_name;
341 r = rados.pool_reverse_lookup(pool_id, &pool_name);
343 derr << "Pool " << pool_id << " identified in MDS map not found in RADOS!"
348 dout(4) << "creating IoCtx.." << dendl;
349 r = rados.ioctx_create(pool_name.c_str(), io);
354 JSONFormatter jf(true);
355 if (mode == "reset") {
356 const std::string table = std::string(argv[2]);
357 if (table == "session") {
358 r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
359 return TableHandlerOmap<SessionMapStore>(rank, "sessionmap", false).reset(&io);
361 } else if (table == "inode") {
362 r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
363 return TableHandler<InoTable>(rank, "inotable", true).reset(&io);
365 } else if (table == "snap") {
366 r = TableHandler<SnapServer>(mds_role_t(), "snaptable", true).reset(&io);
367 jf.open_object_section("reset_snap_status");
368 jf.dump_int("result", r);
371 derr << "Invalid table '" << table << "'" << dendl;
375 } else if (mode == "show") {
376 const std::string table = std::string(argv[2]);
377 if (table == "session") {
378 r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
379 return TableHandlerOmap<SessionMapStore>(rank, "sessionmap", false).load_and_dump(&io, f);
381 } else if (table == "inode") {
382 r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
383 return TableHandler<InoTable>(rank, "inotable", true).load_and_dump(&io, f);;
385 } else if (table == "snap") {
386 jf.open_object_section("show_snap_table");
388 r = TableHandler<SnapServer>(
389 mds_role_t(), "snaptable", true).load_and_dump(&io, &jf);
390 jf.dump_int("result", r);
394 derr << "Invalid table '" << table << "'" << dendl;
398 } else if (mode == "take_inos") {
399 const std::string ino_str = std::string(argv[2]);
401 inodeno_t ino = strict_strtoll(ino_str.c_str(), 10, &ino_err);
402 if (!ino_err.empty()) {
403 derr << "Bad ino '" << ino_str << "'" << dendl;
406 r = apply_role_fn([this, ino](mds_role_t rank, Formatter *f) -> int {
407 return InoTableHandler(rank).take_inos(&io, ino, f);
410 derr << "Invalid mode '" << mode << "'" << dendl;
415 // Subcommand should have written to formatter, flush it
417 std::cout << std::endl;