]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/cephfs/TableTool.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / tools / cephfs / TableTool.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2015 John Spray <john.spray@redhat.com>
7 *
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.
12 */
13
14
15#include "common/ceph_argparse.h"
16#include "common/errno.h"
17
18#include "mds/SessionMap.h"
19#include "mds/InoTable.h"
20#include "mds/SnapServer.h"
21
22#include "TableTool.h"
23
24
25#define dout_context g_ceph_context
26#define dout_subsys ceph_subsys_mds
27#undef dout_prefix
28#define dout_prefix *_dout << __func__ << ": "
29
20effc67
TL
30using namespace std;
31
7c673cae
FG
32void TableTool::usage()
33{
34 std::cout << "Usage: \n"
35 << " cephfs-table-tool <all|[mds rank]> <reset|show> <session|snap|inode>"
36 << " cephfs-table-tool <all|[mds rank]> <take_inos> <max_ino>"
37 << std::endl;
38
39 generic_client_usage();
40}
41
42
43/**
44 * For a function that takes an MDS role as an argument and
45 * returns an error code, execute it on the roles specified
46 * by `role_selector`.
47 */
48int TableTool::apply_role_fn(std::function<int(mds_role_t, Formatter *)> fptr, Formatter *f)
49{
11fdf7f2 50 ceph_assert(f != NULL);
7c673cae
FG
51
52 int r = 0;
53
54 f->open_object_section("ranks");
55
56 for (auto role : role_selector.get_roles()) {
57 std::ostringstream rank_str;
58 rank_str << role.rank;
59 f->open_object_section(rank_str.str().c_str());
60
61 f->open_object_section("data");
62 int rank_r = fptr(role, f);
63 f->close_section();
64 r = r ? r : rank_r;
65
66 f->dump_int("result", rank_r);
67 f->close_section();
68
69
70 }
71
72 f->close_section();
73
74 return r;
75}
76
77
78/**
79 * This class wraps an MDS table class (SessionMap, SnapServer, InoTable)
80 * with offline load/store code such that we can do offline dumps and resets
81 * on those tables.
82 */
83template <typename A>
84class TableHandler
85{
86protected:
87 // The RADOS object ID for the table
88 std::string object_name;
89
90 // The role in question (may be NONE)
91 mds_role_t role;
92
93 // Whether this is an MDSTable subclass (i.e. has leading version field to decode)
94 bool mds_table;
95
96public:
97 TableHandler(mds_role_t r, std::string const &name, bool mds_table_)
98 : role(r), mds_table(mds_table_)
99 {
100 // Compose object name of the table we will dump
101 std::ostringstream oss;
102 oss << "mds";
103 if (!role.is_none()) {
104 oss << role.rank;
105 }
106 oss << "_" << name;
107 object_name = oss.str();
108 }
109
110 int load_and_dump(librados::IoCtx *io, Formatter *f)
111 {
11fdf7f2
TL
112 ceph_assert(io != NULL);
113 ceph_assert(f != NULL);
7c673cae
FG
114
115 // Attempt read
116 bufferlist table_bl;
117 int read_r = io->read(object_name, table_bl, 0, 0);
118 if (read_r >= 0) {
11fdf7f2 119 auto q = table_bl.cbegin();
7c673cae
FG
120 try {
121 if (mds_table) {
122 version_t version;
11fdf7f2 123 decode(version, q);
7c673cae
FG
124 f->dump_int("version", version);
125 }
126 A table_inst;
127 table_inst.set_rank(role.rank);
128 table_inst.decode(q);
129 table_inst.dump(f);
130
131 return 0;
132 } catch (buffer::error &e) {
133 derr << "table " << object_name << " is corrupt" << dendl;
134 return -EIO;
135 }
136 } else {
137 derr << "error reading table object " << object_name
138 << ": " << cpp_strerror(read_r) << dendl;
139 return read_r;
140 }
141 }
142
143 int reset(librados::IoCtx *io)
144 {
145 A table_inst;
146 // Compose new (blank) table
147 table_inst.set_rank(role.rank);
148 table_inst.reset_state();
149 // Write the table out
150 return write(table_inst, io);
151 }
152
153protected:
154
155 int write(const A &table_inst, librados::IoCtx *io)
156 {
157 bufferlist new_bl;
158 if (mds_table) {
159 version_t version = 1;
11fdf7f2 160 encode(version, new_bl);
7c673cae
FG
161 }
162 table_inst.encode_state(new_bl);
163
164 // Write out new table
165 int r = io->write_full(object_name, new_bl);
166 if (r != 0) {
167 derr << "error writing table object " << object_name
168 << ": " << cpp_strerror(r) << dendl;
169 return r;
170 }
171
172 return r;
173 }
174};
175
176template <typename A>
177class TableHandlerOmap
178{
179private:
180 // The RADOS object ID for the table
181 std::string object_name;
182
183 // The role (rank may be NONE)
184 mds_role_t role;
185
186 // Whether this is an MDSTable subclass (i.e. has leading version field to decode)
187 bool mds_table;
188
189public:
190 TableHandlerOmap(mds_role_t r, std::string const &name, bool mds_table_)
191 : role(r), mds_table(mds_table_)
192 {
193 // Compose object name of the table we will dump
194 std::ostringstream oss;
195 oss << "mds";
196 if (!role.is_none()) {
197 oss << role.rank;
198 }
199 oss << "_" << name;
200 object_name = oss.str();
201 }
202
203 int load_and_dump(librados::IoCtx *io, Formatter *f)
204 {
11fdf7f2
TL
205 ceph_assert(io != NULL);
206 ceph_assert(f != NULL);
7c673cae
FG
207
208 // Read in the header
209 bufferlist header_bl;
210 int r = io->omap_get_header(object_name, &header_bl);
211 if (r != 0) {
212 derr << "error reading header on '" << object_name << "': "
213 << cpp_strerror(r) << dendl;
214 return r;
215 }
216
217 // Decode the header
218 A table_inst;
219 table_inst.set_rank(role.rank);
220 try {
221 table_inst.decode_header(header_bl);
222 } catch (buffer::error &e) {
223 derr << "table " << object_name << " is corrupt" << dendl;
224 return -EIO;
225 }
226
227 // Read and decode OMAP values in chunks
228 std::string last_key = "";
229 while(true) {
230 std::map<std::string, bufferlist> values;
231 int r = io->omap_get_vals(object_name, last_key,
11fdf7f2 232 g_conf()->mds_sessionmap_keys_per_op, &values);
7c673cae
FG
233
234 if (r != 0) {
235 derr << "error reading values: " << cpp_strerror(r) << dendl;
236 return r;
237 }
238
239 if (values.empty()) {
240 break;
241 }
242
243 try {
244 table_inst.decode_values(values);
245 } catch (buffer::error &e) {
246 derr << "table " << object_name << " is corrupt" << dendl;
247 return -EIO;
248 }
249 last_key = values.rbegin()->first;
250 }
251
252 table_inst.dump(f);
253
254 return 0;
255 }
256
257 int reset(librados::IoCtx *io)
258 {
259 A table_inst;
260 table_inst.set_rank(role.rank);
261 table_inst.reset_state();
262 bufferlist header_bl;
263 table_inst.encode_header(&header_bl);
264
265 // Compose a transaction to clear and write header
266 librados::ObjectWriteOperation op;
267 op.omap_clear();
268 op.set_op_flags2(LIBRADOS_OP_FLAG_FAILOK);
269 op.omap_set_header(header_bl);
270
271 return io->operate(object_name, &op);
272 }
273};
274
275class InoTableHandler : public TableHandler<InoTable>
276{
277 public:
278 explicit InoTableHandler(mds_role_t r)
279 : TableHandler(r, "inotable", true)
280 {}
281
282 int take_inos(librados::IoCtx *io, inodeno_t max, Formatter *f)
283 {
284 InoTable inst;
285 inst.set_rank(role.rank);
286 inst.reset_state();
287
288 int r = 0;
289 if (inst.force_consume_to(max)) {
290 r = write(inst, io);
291 }
292
293 f->dump_int("version", inst.get_version());
294 inst.dump(f);
295
296 return r;
297 }
298};
299
300
301int TableTool::main(std::vector<const char*> &argv)
302{
303 int r;
304
305 dout(10) << __func__ << dendl;
306
307 // RADOS init
308 // ==========
309 r = rados.init_with_context(g_ceph_context);
310 if (r < 0) {
311 derr << "RADOS unavailable, cannot scan filesystem journal" << dendl;
312 return r;
313 }
314
315 dout(4) << "connecting to RADOS..." << dendl;
316 r = rados.connect();
317 if (r < 0) {
318 derr << "couldn't connect to cluster: " << cpp_strerror(r) << dendl;
319 return r;
320 }
321
322 // Require at least 3 args <rank> <mode> <arg> [args...]
323 if (argv.size() < 3) {
11fdf7f2 324 cerr << "missing required 3 arguments" << std::endl;
7c673cae
FG
325 return -EINVAL;
326 }
327
328 const std::string role_str = std::string(argv[0]);
329 const std::string mode = std::string(argv[1]);
330 const std::string table = std::string(argv[2]);
331
332 r = role_selector.parse(*fsmap, role_str);
333 if (r < 0) {
334 derr << "Bad rank selection: " << role_str << "'" << dendl;
335 return r;
336 }
337
338 auto fs = fsmap->get_filesystem(role_selector.get_ns());
11fdf7f2 339 ceph_assert(fs != nullptr);
7c673cae
FG
340 int64_t const pool_id = fs->mds_map.get_metadata_pool();
341 dout(4) << "resolving pool " << pool_id << dendl;
342 std::string pool_name;
343 r = rados.pool_reverse_lookup(pool_id, &pool_name);
344 if (r < 0) {
345 derr << "Pool " << pool_id << " identified in MDS map not found in RADOS!"
346 << dendl;
347 return r;
348 }
349
350 dout(4) << "creating IoCtx.." << dendl;
351 r = rados.ioctx_create(pool_name.c_str(), io);
352 if (r != 0) {
353 return r;
354 }
355
356 JSONFormatter jf(true);
357 if (mode == "reset") {
358 const std::string table = std::string(argv[2]);
359 if (table == "session") {
360 r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
361 return TableHandlerOmap<SessionMapStore>(rank, "sessionmap", false).reset(&io);
362 }, &jf);
363 } else if (table == "inode") {
364 r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
365 return TableHandler<InoTable>(rank, "inotable", true).reset(&io);
366 }, &jf);
367 } else if (table == "snap") {
368 r = TableHandler<SnapServer>(mds_role_t(), "snaptable", true).reset(&io);
369 jf.open_object_section("reset_snap_status");
370 jf.dump_int("result", r);
371 jf.close_section();
372 } else {
11fdf7f2 373 cerr << "Invalid table '" << table << "'" << std::endl;
7c673cae
FG
374 return -EINVAL;
375 }
376 } else if (mode == "show") {
377 const std::string table = std::string(argv[2]);
378 if (table == "session") {
379 r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
380 return TableHandlerOmap<SessionMapStore>(rank, "sessionmap", false).load_and_dump(&io, f);
381 }, &jf);
382 } else if (table == "inode") {
383 r = apply_role_fn([this](mds_role_t rank, Formatter *f) -> int {
384 return TableHandler<InoTable>(rank, "inotable", true).load_and_dump(&io, f);;
385 }, &jf);
386 } else if (table == "snap") {
387 jf.open_object_section("show_snap_table");
388 {
389 r = TableHandler<SnapServer>(
390 mds_role_t(), "snaptable", true).load_and_dump(&io, &jf);
391 jf.dump_int("result", r);
392 }
393 jf.close_section();
394 } else {
11fdf7f2 395 cerr << "Invalid table '" << table << "'" << std::endl;
7c673cae
FG
396 return -EINVAL;
397 }
398 } else if (mode == "take_inos") {
399 const std::string ino_str = std::string(argv[2]);
400 std::string ino_err;
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;
404 return -EINVAL;
405 }
406 r = apply_role_fn([this, ino](mds_role_t rank, Formatter *f) -> int {
407 return InoTableHandler(rank).take_inos(&io, ino, f);
408 }, &jf);
409 } else {
11fdf7f2 410 cerr << "Invalid mode '" << mode << "'" << std::endl;
7c673cae
FG
411 return -EINVAL;
412 }
413
414 // Subcommand should have written to formatter, flush it
415 jf.flush(std::cout);
416 std::cout << std::endl;
417 return r;
418}
419