]> git.proxmox.com Git - ceph.git/blob - ceph/src/os/bluestore/bluestore_tool.cc
update sources to 12.2.10
[ceph.git] / 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|O_CLOEXEC, 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|O_CLOEXEC, 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 }