Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / tools / cephfs / TableTool.cc
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
30 void TableTool::usage()
31 {
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>"
35     << std::endl;
36
37   generic_client_usage();
38 }
39
40
41 /**
42  * For a function that takes an MDS role as an argument and
43  * returns an error code, execute it on the roles specified
44  * by `role_selector`.
45  */
46 int TableTool::apply_role_fn(std::function<int(mds_role_t, Formatter *)> fptr, Formatter *f)
47 {
48   assert(f != NULL);
49
50   int r = 0;
51
52   f->open_object_section("ranks");
53
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());
58
59     f->open_object_section("data");
60     int rank_r = fptr(role, f);
61     f->close_section();
62     r = r ? r : rank_r;
63
64     f->dump_int("result", rank_r);
65     f->close_section();
66
67     
68   }
69
70   f->close_section();
71
72   return r;
73 }
74
75
76 /**
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
79  * on those tables.
80  */
81 template <typename A>
82 class TableHandler
83 {
84 protected:
85   // The RADOS object ID for the table
86   std::string object_name;
87
88   // The role in question (may be NONE)
89   mds_role_t role;
90
91   // Whether this is an MDSTable subclass (i.e. has leading version field to decode)
92   bool mds_table;
93
94 public:
95   TableHandler(mds_role_t r, std::string const &name, bool mds_table_)
96     : role(r), mds_table(mds_table_)
97   {
98     // Compose object name of the table we will dump
99     std::ostringstream oss;
100     oss << "mds";
101     if (!role.is_none()) {
102       oss << role.rank;
103     }
104     oss << "_" << name;
105     object_name = oss.str();
106   }
107
108   int load_and_dump(librados::IoCtx *io, Formatter *f)
109   {
110     assert(io != NULL);
111     assert(f != NULL);
112
113     // Attempt read
114     bufferlist table_bl;
115     int read_r = io->read(object_name, table_bl, 0, 0);
116     if (read_r >= 0) {
117       bufferlist::iterator q = table_bl.begin();
118       try {
119         if (mds_table) {
120           version_t version;
121           ::decode(version, q);
122           f->dump_int("version", version);
123         }
124         A table_inst;
125         table_inst.set_rank(role.rank);
126         table_inst.decode(q);
127         table_inst.dump(f);
128
129         return 0;
130       } catch (buffer::error &e) {
131         derr << "table " << object_name << " is corrupt" << dendl;
132         return -EIO;
133       }
134     } else {
135       derr << "error reading table object " << object_name
136         << ": " << cpp_strerror(read_r) << dendl;
137       return read_r;
138     }
139   }
140
141   int reset(librados::IoCtx *io)
142   {
143     A table_inst;
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);
149   }
150
151 protected:
152
153   int write(const A &table_inst, librados::IoCtx *io)
154   {
155     bufferlist new_bl;
156     if (mds_table) {
157       version_t version = 1;
158       ::encode(version, new_bl);
159     }
160     table_inst.encode_state(new_bl);
161
162     // Write out new table
163     int r = io->write_full(object_name, new_bl);
164     if (r != 0) {
165       derr << "error writing table object " << object_name
166         << ": " << cpp_strerror(r) << dendl;
167       return r;
168     }
169
170     return r;
171   }
172 };
173
174 template <typename A>
175 class TableHandlerOmap
176 {
177 private:
178   // The RADOS object ID for the table
179   std::string object_name;
180
181   // The role (rank may be NONE)
182   mds_role_t role;
183
184   // Whether this is an MDSTable subclass (i.e. has leading version field to decode)
185   bool mds_table;
186
187 public:
188   TableHandlerOmap(mds_role_t r, std::string const &name, bool mds_table_)
189     : role(r), mds_table(mds_table_)
190   {
191     // Compose object name of the table we will dump
192     std::ostringstream oss;
193     oss << "mds";
194     if (!role.is_none()) {
195       oss << role.rank;
196     }
197     oss << "_" << name;
198     object_name = oss.str();
199   }
200
201   int load_and_dump(librados::IoCtx *io, Formatter *f)
202   {
203     assert(io != NULL);
204     assert(f != NULL);
205
206     // Read in the header
207     bufferlist header_bl;
208     int r = io->omap_get_header(object_name, &header_bl);
209     if (r != 0) {
210       derr << "error reading header on '" << object_name << "': "
211            << cpp_strerror(r) << dendl;
212       return r;
213     }
214
215     // Decode the header
216     A table_inst;
217     table_inst.set_rank(role.rank);
218     try {
219       table_inst.decode_header(header_bl);
220     } catch (buffer::error &e) {
221       derr << "table " << object_name << " is corrupt" << dendl;
222       return -EIO;
223     }
224
225     // Read and decode OMAP values in chunks
226     std::string last_key = "";
227     while(true) {
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);
231
232       if (r != 0) {
233         derr << "error reading values: " << cpp_strerror(r) << dendl;
234         return r;
235       }
236
237       if (values.empty()) {
238         break;
239       }
240
241       try {
242         table_inst.decode_values(values);
243       } catch (buffer::error &e) {
244         derr << "table " << object_name << " is corrupt" << dendl;
245         return -EIO;
246       }
247       last_key = values.rbegin()->first;
248     }
249
250     table_inst.dump(f);
251
252     return 0;
253   }
254
255   int reset(librados::IoCtx *io)
256   {
257     A table_inst;
258     table_inst.set_rank(role.rank);
259     table_inst.reset_state();
260     bufferlist header_bl;
261     table_inst.encode_header(&header_bl);
262
263     // Compose a transaction to clear and write header
264     librados::ObjectWriteOperation op;
265     op.omap_clear();
266     op.set_op_flags2(LIBRADOS_OP_FLAG_FAILOK);
267     op.omap_set_header(header_bl);
268     
269     return io->operate(object_name, &op);
270   }
271 };
272
273 class InoTableHandler : public TableHandler<InoTable>
274 {
275   public:
276   explicit InoTableHandler(mds_role_t r)
277     : TableHandler(r, "inotable", true)
278   {}
279
280   int take_inos(librados::IoCtx *io, inodeno_t max, Formatter *f)
281   {
282     InoTable inst;
283     inst.set_rank(role.rank);
284     inst.reset_state();
285
286     int r = 0;
287     if (inst.force_consume_to(max)) {
288       r = write(inst, io);
289     }
290
291     f->dump_int("version", inst.get_version());
292     inst.dump(f);
293
294     return r;
295   }
296 };
297
298
299 int TableTool::main(std::vector<const char*> &argv)
300 {
301   int r;
302
303   dout(10) << __func__ << dendl;
304
305   // RADOS init
306   // ==========
307   r = rados.init_with_context(g_ceph_context);
308   if (r < 0) {
309     derr << "RADOS unavailable, cannot scan filesystem journal" << dendl;
310     return r;
311   }
312
313   dout(4) << "connecting to RADOS..." << dendl;
314   r = rados.connect();
315   if (r < 0) {
316     derr << "couldn't connect to cluster: " << cpp_strerror(r) << dendl;
317     return r;
318   }
319
320   // Require at least 3 args <rank> <mode> <arg> [args...]
321   if (argv.size() < 3) {
322     usage();
323     return -EINVAL;
324   }
325
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]);
329
330   r = role_selector.parse(*fsmap, role_str);
331   if (r < 0) {
332     derr << "Bad rank selection: " << role_str << "'" << dendl;
333     return r;
334   }
335
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);
342   if (r < 0) {
343     derr << "Pool " << pool_id << " identified in MDS map not found in RADOS!"
344          << dendl;
345     return r;
346   }
347
348   dout(4) << "creating IoCtx.." << dendl;
349   r = rados.ioctx_create(pool_name.c_str(), io);
350   if (r != 0) {
351     return r;
352   }
353
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);
360       }, &jf);
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);
364       }, &jf);
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);
369       jf.close_section();
370     } else {
371       derr << "Invalid table '" << table << "'" << dendl;
372       usage();
373       return -EINVAL;
374     }
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);
380       }, &jf);
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);;
384       }, &jf);
385     } else if (table == "snap") {
386       jf.open_object_section("show_snap_table");
387       {
388         r = TableHandler<SnapServer>(
389             mds_role_t(), "snaptable", true).load_and_dump(&io, &jf);
390         jf.dump_int("result", r);
391       }
392       jf.close_section();
393     } else {
394       derr << "Invalid table '" << table << "'" << dendl;
395       usage();
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 {
410     derr << "Invalid mode '" << mode << "'" << dendl;
411     usage();
412     return -EINVAL;
413   }
414
415   // Subcommand should have written to formatter, flush it
416   jf.flush(std::cout);
417   std::cout << std::endl;
418   return r;
419 }
420