Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / os / bluestore / bluestore_tool.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <boost/program_options/variables_map.hpp>
5 #include <boost/program_options/parsers.hpp>
6
7 #include <stdio.h>
8 #include <string.h>
9 #include <iostream>
10 #include <time.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include "global/global_init.h"
14 #include "common/ceph_argparse.h"
15 #include "include/stringify.h"
16 #include "common/errno.h"
17 #include "common/safe_io.h"
18
19 #include "os/bluestore/BlueFS.h"
20 #include "os/bluestore/BlueStore.h"
21
22 namespace po = boost::program_options;
23
24 void usage(po::options_description &desc)
25 {
26   cout << desc << std::endl;
27 }
28
29 void validate_path(CephContext *cct, const string& path, bool bluefs)
30 {
31   BlueStore bluestore(cct, path);
32   string type;
33   int r = bluestore.read_meta("type", &type);
34   if (r < 0) {
35     cerr << "failed to load os-type: " << cpp_strerror(r) << std::endl;
36     exit(EXIT_FAILURE);
37   }
38   if (type != "bluestore") {
39     cerr << "expected bluestore, but type is " << type << std::endl;
40     exit(EXIT_FAILURE);
41   }
42   if (!bluefs) {
43     return;
44   }
45
46   string kv_backend;
47   r = bluestore.read_meta("kv_backend", &kv_backend);
48   if (r < 0) {
49     cerr << "failed to load kv_backend: " << cpp_strerror(r) << std::endl;
50     exit(EXIT_FAILURE);
51   }
52   if (kv_backend != "rocksdb") {
53     cerr << "expect kv_backend to be rocksdb, but is " << kv_backend
54          << std::endl;
55     exit(EXIT_FAILURE);
56   }
57   string bluefs_enabled;
58   r = bluestore.read_meta("bluefs", &bluefs_enabled);
59   if (r < 0) {
60     cerr << "failed to load do_bluefs: " << cpp_strerror(r) << std::endl;
61     exit(EXIT_FAILURE);
62   }
63   if (bluefs_enabled != "1") {
64     cerr << "bluefs not enabled for rocksdb" << std::endl;
65     exit(EXIT_FAILURE);
66   }
67 }
68
69 BlueFS *open_bluefs(
70   CephContext *cct,
71   const string& path,
72   const vector<string>& devs)
73 {
74   validate_path(cct, path, true);
75   BlueFS *fs = new BlueFS(cct);
76
77   string main;
78   set<int> got;
79   for (auto& i : devs) {
80     bluestore_bdev_label_t label;
81     int r = BlueStore::_read_bdev_label(cct, i, &label);
82     if (r < 0) {
83       cerr << "unable to read label for " << i << ": "
84            << cpp_strerror(r) << std::endl;
85       exit(EXIT_FAILURE);
86     }
87     int id = -1;
88     if (label.description == "main")
89       main = i;
90     else if (label.description == "bluefs db")
91       id = BlueFS::BDEV_DB;
92     else if (label.description == "bluefs wal")
93       id = BlueFS::BDEV_WAL;
94     if (id >= 0) {
95       got.insert(id);
96       cout << " slot " << id << " " << i << std::endl;
97       int r = fs->add_block_device(id, i);
98       if (r < 0) {
99         cerr << "unable to open " << i << ": " << cpp_strerror(r) << std::endl;
100         exit(EXIT_FAILURE);
101       }
102     }
103   }
104   if (main.length()) {
105     int id = BlueFS::BDEV_DB;
106     if (got.count(BlueFS::BDEV_DB))
107       id = BlueFS::BDEV_SLOW;
108     cout << " slot " << id << " " << main << std::endl;
109     int r = fs->add_block_device(id, main);
110     if (r < 0) {
111       cerr << "unable to open " << main << ": " << cpp_strerror(r)
112            << std::endl;
113       exit(EXIT_FAILURE);
114     }
115   }
116
117   int r = fs->mount();
118   if (r < 0) {
119     cerr << "unable to mount bluefs: " << cpp_strerror(r)
120          << std::endl;
121     exit(EXIT_FAILURE);
122   }
123   return fs;
124 }
125
126 int main(int argc, char **argv)
127 {
128   string out_dir;
129   vector<string> devs;
130   string path;
131   string action;
132   string log_file;
133   string key, value;
134   int log_level = 30;
135   bool fsck_deep = false;
136   po::options_description po_options("Options");
137   po_options.add_options()
138     ("help,h", "produce help message")
139     ("path", po::value<string>(&path), "bluestore path")
140     ("out-dir", po::value<string>(&out_dir), "output directory")
141     ("log-file,l", po::value<string>(&log_file), "log file")
142     ("log-level", po::value<int>(&log_level), "log level (30=most, 20=lots, 10=some, 1=little)")
143     ("dev", po::value<vector<string>>(&devs), "device(s)")
144     ("deep", po::value<bool>(&fsck_deep), "deep fsck (read all data)")
145     ("key,k", po::value<string>(&key), "label metadata key name")
146     ("value,v", po::value<string>(&value), "label metadata value")
147     ;
148   po::options_description po_positional("Positional options");
149   po_positional.add_options()
150     ("command", po::value<string>(&action), "fsck, repair, bluefs-export, bluefs-bdev-sizes, bluefs-bdev-expand, show-label, set-label-key, rm-label-key, prime-osd-dir")
151     ;
152   po::options_description po_all("All options");
153   po_all.add(po_options).add(po_positional);
154   po::positional_options_description pd;
155   pd.add("command", 1);
156
157   vector<string> ceph_option_strings;
158   po::variables_map vm;
159   try {
160     po::parsed_options parsed =
161       po::command_line_parser(argc, argv).options(po_all).allow_unregistered().positional(pd).run();
162     po::store( parsed, vm);
163     po::notify(vm);
164     ceph_option_strings = po::collect_unrecognized(parsed.options,
165                                                    po::include_positional);
166   } catch(po::error &e) {
167     std::cerr << e.what() << std::endl;
168     exit(EXIT_FAILURE);
169   }
170
171   if (vm.count("help")) {
172     usage(po_all);
173     exit(EXIT_SUCCESS);
174   }
175   if (action.empty()) {
176     cerr << "must specify an action; --help for help" << std::endl;
177     exit(EXIT_FAILURE);
178   }
179
180   if (action == "fsck" || action == "repair") {
181     if (path.empty()) {
182       cerr << "must specify bluestore path" << std::endl;
183       exit(EXIT_FAILURE);
184     }
185   }
186   if (action == "prime-osd-dir") {
187     if (devs.size() != 1) {
188       cerr << "must specify the main bluestore device" << std::endl;
189       exit(EXIT_FAILURE);
190     }
191     if (path.empty()) {
192       cerr << "must specify osd dir to prime" << std::endl;
193       exit(EXIT_FAILURE);
194     }
195   }
196   if (action == "set-label-key" ||
197       action == "rm-label-key") {
198     if (devs.size() != 1) {
199       cerr << "must specify the main bluestore device" << std::endl;
200       exit(EXIT_FAILURE);
201     }
202     if (key.size() == 0) {
203       cerr << "must specify a key name with -k" << std::endl;
204       exit(EXIT_FAILURE);
205     }
206     if (action == "set-label-key" && value.size() == 0) {
207       cerr << "must specify a value with -v" << std::endl;
208       exit(EXIT_FAILURE);
209     }
210   }
211   if (action == "show-label") {
212     if (devs.empty() && path.empty()) {
213       cerr << "must specify bluestore path *or* raw device(s)" << std::endl;
214       exit(EXIT_FAILURE);
215     }
216     if (devs.empty()) {
217       cout << "infering bluefs devices from bluestore path" << std::endl;
218       for (auto fn : {"block", "block.wal", "block.db"}) {
219         string p = path + "/" + fn;
220         struct stat st;
221         if (::stat(p.c_str(), &st) == 0) {
222           devs.push_back(p);
223         }
224       }
225     }
226   }
227   if (action == "bluefs-export") {
228     if (path.empty()) {
229       cerr << "must specify bluestore path" << std::endl;
230       exit(EXIT_FAILURE);
231     }
232     if (out_dir.empty()) {
233       cerr << "must specify out-dir to export bluefs" << std::endl;
234       exit(EXIT_FAILURE);
235     }
236     cout << "infering bluefs devices from bluestore path" << std::endl;
237     for (auto fn : {"block", "block.wal", "block.db"}) {
238       string p = path + "/" + fn;
239       struct stat st;
240       if (::stat(p.c_str(), &st) == 0) {
241         devs.push_back(p);
242       }
243     }
244   }
245   if (action == "bluefs-bdev-sizes" || action == "bluefs-bdev-expand") {
246     if (path.empty()) {
247       cerr << "must specify bluestore path" << std::endl;
248       exit(EXIT_FAILURE);
249     }
250     cout << "infering bluefs devices from bluestore path" << std::endl;
251     for (auto fn : {"block", "block.wal", "block.db"}) {
252       string p = path + "/" + fn;
253       struct stat st;
254       if (::stat(p.c_str(), &st) == 0) {
255         devs.push_back(p);
256       }
257     }
258   }
259
260   vector<const char*> args;
261   if (log_file.size()) {
262     args.push_back("--log-file");
263     args.push_back(log_file.c_str());
264     static char ll[10];
265     snprintf(ll, sizeof(ll), "%d", log_level);
266     args.push_back("--debug-bluestore");
267     args.push_back(ll);
268     args.push_back("--debug-bluefs");
269     args.push_back(ll);
270   }
271   args.push_back("--no-log-to-stderr");
272   args.push_back("--err-to-stderr");
273
274   for (auto& i : ceph_option_strings) {
275     args.push_back(i.c_str());
276   }
277   env_to_vec(args);
278
279   auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
280                          CODE_ENVIRONMENT_UTILITY, 0);
281   common_init_finish(cct.get());
282
283   if (action == "fsck" ||
284       action == "repair") {
285     validate_path(cct.get(), path, false);
286     BlueStore bluestore(cct.get(), path);
287     int r;
288     if (action == "fsck") {
289       r = bluestore.fsck(fsck_deep);
290     } else {
291       r = bluestore.repair(fsck_deep);
292     }
293     if (r < 0) {
294       cerr << "error from fsck: " << cpp_strerror(r) << std::endl;
295       exit(EXIT_FAILURE);
296     }
297     cout << action << " success" << std::endl;
298   }
299   else if (action == "prime-osd-dir") {
300     bluestore_bdev_label_t label;
301     int r = BlueStore::_read_bdev_label(cct.get(), devs.front(), &label);
302     if (r < 0) {
303       cerr << "failed to read label for " << devs.front() << ": "
304            << cpp_strerror(r) << std::endl;
305       exit(EXIT_FAILURE);
306     }
307
308     // kludge some things into the map that we want to populate into
309     // target dir
310     label.meta["path_block"] = devs.front();
311     label.meta["type"] = "bluestore";
312     label.meta["fsid"] = stringify(label.osd_uuid);
313     
314     for (auto kk : {
315         "whoami",
316           "osd_key",
317           "path_block", "path_block.db", "path_block.wal",
318           "ceph_fsid",
319           "fsid",
320           "type",
321           "ready" }) {
322       string k = kk;
323       auto i = label.meta.find(k);
324       if (i == label.meta.end()) {
325         continue;
326       }
327       string p = path + "/" + k;
328       string v = i->second;
329       if (k == "osd_key") {
330         p = path + "/keyring";
331         v = "[osd.";
332         v += label.meta["whoami"];
333         v += "]\nkey = " + i->second;
334       }
335       if (k.find("path_") == 0) {
336         p = path + "/" + k.substr(5);
337         int r = ::symlink(v.c_str(), p.c_str());
338         if (r < 0 && errno == EEXIST) {
339           struct stat st;
340           r = ::stat(p.c_str(), &st);
341           if (r == 0 && S_ISLNK(st.st_mode)) {
342             char target[PATH_MAX];
343             r = ::readlink(p.c_str(), target, sizeof(target));
344             if (r > 0) {
345               if (v == target) {
346                 r = 0;  // already matches our target
347               } else {
348                 ::unlink(p.c_str());
349                 r = ::symlink(v.c_str(), p.c_str());
350               }
351             } else {
352               cerr << "error reading existing link at " << p << ": " << cpp_strerror(errno)
353                    << std::endl;
354             }
355           }
356         }
357         if (r < 0) {
358           cerr << "error symlinking " << p << ": " << cpp_strerror(errno)
359                << std::endl;
360           exit(EXIT_FAILURE);
361         }
362       } else {
363         v += "\n";
364         int fd = ::open(p.c_str(), O_CREAT|O_TRUNC|O_WRONLY, 0600);
365         if (fd < 0) {
366           cerr << "error writing " << p << ": " << cpp_strerror(errno)
367                << std::endl;
368           exit(EXIT_FAILURE);
369         }
370         int r = safe_write(fd, v.c_str(), v.size());
371         if (r < 0) {
372           cerr << "error writing to " << p << ": " << cpp_strerror(errno)
373                << std::endl;
374           exit(EXIT_FAILURE);
375         }
376         ::close(fd);
377       }
378     }
379   }
380   else if (action == "show-label") {
381     JSONFormatter jf(true);
382     jf.open_object_section("devices");
383     for (auto& i : devs) {
384       bluestore_bdev_label_t label;
385       int r = BlueStore::_read_bdev_label(cct.get(), i, &label);
386       if (r < 0) {
387         cerr << "unable to read label for " << i << ": "
388              << cpp_strerror(r) << std::endl;
389         exit(EXIT_FAILURE);
390       }
391       jf.open_object_section(i.c_str());
392       label.dump(&jf);
393       jf.close_section();
394     }
395     jf.close_section();
396     jf.flush(cout);
397   }
398   else if (action == "set-label-key") {
399     bluestore_bdev_label_t label;
400     int r = BlueStore::_read_bdev_label(cct.get(), devs.front(), &label);
401     if (r < 0) {
402       cerr << "unable to read label for " << devs.front() << ": "
403            << cpp_strerror(r) << std::endl;
404       exit(EXIT_FAILURE);
405     }
406     label.meta[key] = value;
407     r = BlueStore::_write_bdev_label(cct.get(), devs.front(), label);
408     if (r < 0) {
409       cerr << "unable to write label for " << devs.front() << ": "
410            << cpp_strerror(r) << std::endl;
411       exit(EXIT_FAILURE);
412     }
413   }
414   else if (action == "rm-label-key") {
415     bluestore_bdev_label_t label;
416     int r = BlueStore::_read_bdev_label(cct.get(), devs.front(), &label);
417     if (r < 0) {
418       cerr << "unable to read label for " << devs.front() << ": "
419            << cpp_strerror(r) << std::endl;
420       exit(EXIT_FAILURE);
421     }
422     if (!label.meta.count(key)) {
423       cerr << "key '" << key << "' not present" << std::endl;
424       exit(EXIT_FAILURE);
425     }
426     label.meta.erase(key);
427     r = BlueStore::_write_bdev_label(cct.get(), devs.front(), label);
428     if (r < 0) {
429       cerr << "unable to write label for " << devs.front() << ": "
430            << cpp_strerror(r) << std::endl;
431       exit(EXIT_FAILURE);
432     }
433   }
434   else if (action == "bluefs-bdev-sizes") {
435     BlueFS *fs = open_bluefs(cct.get(), path, devs);
436     fs->dump_block_extents(cout);
437     delete fs;
438   }
439   else if (action == "bluefs-bdev-expand") {
440     BlueFS *fs = open_bluefs(cct.get(), path, devs);
441     cout << "start:" << std::endl;
442     fs->dump_block_extents(cout);
443     for (int devid : { BlueFS::BDEV_WAL, BlueFS::BDEV_DB }) {
444       interval_set<uint64_t> before;
445       fs->get_block_extents(devid, &before);
446       uint64_t end = before.range_end();
447       uint64_t size = fs->get_block_device_size(devid);
448       if (end < size) {
449         cout << "expanding dev " << devid << " from 0x" << std::hex
450              << end << " to 0x" << size << std::dec << std::endl;
451         fs->add_block_extent(devid, end, size-end);
452       }
453     }
454     delete fs;
455   }
456   else if (action == "bluefs-export") {
457     BlueFS *fs = open_bluefs(cct.get(), path, devs);
458
459     vector<string> dirs;
460     int r = fs->readdir("", &dirs);
461     if (r < 0) {
462       cerr << "readdir in root failed: " << cpp_strerror(r) << std::endl;
463       exit(EXIT_FAILURE);
464     }
465     for (auto& dir : dirs) {
466       if (dir[0] == '.')
467         continue;
468       cout << dir << "/" << std::endl;
469       vector<string> ls;
470       r = fs->readdir(dir, &ls);
471       if (r < 0) {
472         cerr << "readdir " << dir << " failed: " << cpp_strerror(r) << std::endl;
473         exit(EXIT_FAILURE);
474       }
475       string full = out_dir + "/" + dir;
476       r = ::mkdir(full.c_str(), 0755);
477       if (r < 0) {
478         r = -errno;
479         cerr << "mkdir " << full << " failed: " << cpp_strerror(r) << std::endl;
480         exit(EXIT_FAILURE);
481       }
482       for (auto& file : ls) {
483         if (file[0] == '.')
484           continue;
485         cout << dir << "/" << file << std::endl;
486         uint64_t size;
487         utime_t mtime;
488         r = fs->stat(dir, file, &size, &mtime);
489         if (r < 0) {
490           cerr << "stat " << file << " failed: " << cpp_strerror(r) << std::endl;
491           exit(EXIT_FAILURE);
492         }
493         string path = out_dir + "/" + dir + "/" + file;
494         int fd = ::open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, 0644);
495         if (fd < 0) {
496           r = -errno;
497           cerr << "open " << path << " failed: " << cpp_strerror(r) << std::endl;
498           exit(EXIT_FAILURE);
499         }
500         assert(fd >= 0);
501         if (size > 0) {
502           BlueFS::FileReader *h;
503           r = fs->open_for_read(dir, file, &h, false);
504           if (r < 0) {
505             cerr << "open_for_read " << dir << "/" << file << " failed: "
506                  << cpp_strerror(r) << std::endl;
507             exit(EXIT_FAILURE);
508           }
509           int pos = 0;
510           int left = size;
511           while (left) {
512             bufferlist bl;
513             r = fs->read(h, &h->buf, pos, left, &bl, NULL);
514             if (r <= 0) {
515               cerr << "read " << dir << "/" << file << " from " << pos
516                    << " failed: " << cpp_strerror(r) << std::endl;
517               exit(EXIT_FAILURE);
518             }
519             int rc = bl.write_fd(fd);
520             if (rc < 0) {
521               cerr << "write to " << path << " failed: "
522                    << cpp_strerror(r) << std::endl;
523               exit(EXIT_FAILURE);
524             }
525             pos += r;
526             left -= r;
527           }
528           delete h;
529         }
530         ::close(fd);
531       }
532     }
533     fs->umount();
534     delete fs;
535   } else {
536     cerr << "unrecognized action " << action << std::endl;
537     return 1;
538   }
539
540   return 0;
541 }