]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/ceph_kvstore_tool.cc
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / tools / ceph_kvstore_tool.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) 2012 Inktank, Inc.
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#include <map>
14#include <set>
15#include <string>
16
17#include <boost/scoped_ptr.hpp>
18
19#include "common/ceph_argparse.h"
20#include "common/config.h"
21#include "common/errno.h"
22#include "common/strtol.h"
23#include "global/global_context.h"
24#include "global/global_init.h"
25#include "include/stringify.h"
26#include "include/utime.h"
27#include "common/Clock.h"
28#include "kv/KeyValueDB.h"
29#include "common/url_escape.h"
30
31#ifdef HAVE_LIBAIO
32#include "os/bluestore/BlueStore.h"
33#endif
34
35using namespace std;
36
37class StoreTool
38{
39 boost::scoped_ptr<KeyValueDB> db;
40 string store_path;
41
42 public:
43 StoreTool(string type, const string &path) : store_path(path) {
44 KeyValueDB *db_ptr;
45 if (type == "bluestore-kv") {
46#ifdef HAVE_LIBAIO
47 // note: we'll leak this! the only user is ceph-kvstore-tool and
48 // we don't care.
49 BlueStore *bluestore = new BlueStore(g_ceph_context, path);
50 int r = bluestore->start_kv_only(&db_ptr);
51 if (r < 0) {
52 exit(1);
53 }
54#else
55 cerr << "bluestore not compiled in" << std::endl;
56 exit(1);
57#endif
58 } else {
59 db_ptr = KeyValueDB::create(g_ceph_context, type, path);
60 int r = db_ptr->open(std::cerr);
61 if (r < 0) {
62 cerr << "failed to open type " << type << " path " << path << ": "
63 << cpp_strerror(r) << std::endl;
64 exit(1);
65 }
66 }
67 db.reset(db_ptr);
68 }
69
70 uint32_t traverse(const string &prefix,
71 const bool do_crc,
72 ostream *out) {
73 KeyValueDB::WholeSpaceIterator iter = db->get_iterator();
74
75 if (prefix.empty())
76 iter->seek_to_first();
77 else
78 iter->seek_to_first(prefix);
79
80 uint32_t crc = -1;
81
82 while (iter->valid()) {
83 pair<string,string> rk = iter->raw_key();
84 if (!prefix.empty() && (rk.first != prefix))
85 break;
86
87 if (out)
88 *out << url_escape(rk.first) << "\t" << url_escape(rk.second);
89 if (do_crc) {
90 bufferlist bl;
91 bl.append(rk.first);
92 bl.append(rk.second);
93 bl.append(iter->value());
94
95 crc = bl.crc32c(crc);
96 if (out) {
97 *out << "\t" << bl.crc32c(0);
98 }
99 }
100 if (out)
101 *out << std::endl;
102 iter->next();
103 }
104
105 return crc;
106 }
107
108 void list(const string &prefix, const bool do_crc) {
109 traverse(prefix, do_crc, &std::cout);
110 }
111
112 bool exists(const string &prefix) {
113 assert(!prefix.empty());
114 KeyValueDB::WholeSpaceIterator iter = db->get_iterator();
115 iter->seek_to_first(prefix);
116 return (iter->valid() && (iter->raw_key().first == prefix));
117 }
118
119 bool exists(const string &prefix, const string &key) {
120 assert(!prefix.empty());
121
122 if (key.empty()) {
123 return exists(prefix);
124 }
125
126 bool exists = false;
127 get(prefix, key, exists);
128 return exists;
129 }
130
131 bufferlist get(const string &prefix, const string &key, bool &exists) {
132 assert(!prefix.empty() && !key.empty());
133
134 map<string,bufferlist> result;
135 std::set<std::string> keys;
136 keys.insert(key);
137 db->get(prefix, keys, &result);
138
139 if (result.count(key) > 0) {
140 exists = true;
141 return result[key];
142 }
143 exists = false;
144 return bufferlist();
145 }
146
147 uint64_t get_size() {
148 map<string,uint64_t> extras;
149 uint64_t s = db->get_estimated_size(extras);
150 for (map<string,uint64_t>::iterator p = extras.begin();
151 p != extras.end(); ++p) {
152 std::cout << p->first << " - " << p->second << std::endl;
153 }
154 std::cout << "total: " << s << std::endl;
155 return s;
156 }
157
158 bool set(const string &prefix, const string &key, bufferlist &val) {
159 assert(!prefix.empty());
160 assert(!key.empty());
161 assert(val.length() > 0);
162
163 KeyValueDB::Transaction tx = db->get_transaction();
164 tx->set(prefix, key, val);
165 int ret = db->submit_transaction_sync(tx);
166
167 return (ret == 0);
168 }
169
170 int copy_store_to(string type, const string &other_path,
171 const int num_keys_per_tx) {
172
173 if (num_keys_per_tx <= 0) {
174 std::cerr << "must specify a number of keys/tx > 0" << std::endl;
175 return -EINVAL;
176 }
177
178 // open or create a leveldb store at @p other_path
179 KeyValueDB *other = KeyValueDB::create(g_ceph_context, type, other_path);
180 int err = other->create_and_open(std::cerr);
181 if (err < 0)
182 return err;
183
184 KeyValueDB::WholeSpaceIterator it = db->get_iterator();
185 it->seek_to_first();
186 uint64_t total_keys = 0;
187 uint64_t total_size = 0;
188 uint64_t total_txs = 0;
189
190 utime_t started_at = ceph_clock_now();
191
192 do {
193 int num_keys = 0;
194
195 KeyValueDB::Transaction tx = other->get_transaction();
196
197
198 while (it->valid() && num_keys < num_keys_per_tx) {
199 pair<string,string> k = it->raw_key();
200 bufferlist v = it->value();
201 tx->set(k.first, k.second, v);
202
203 num_keys ++;
204 total_size += v.length();
205
206 it->next();
207 }
208
209 total_txs ++;
210 total_keys += num_keys;
211
212 if (num_keys > 0)
213 other->submit_transaction_sync(tx);
214
215 utime_t cur_duration = ceph_clock_now() - started_at;
216 std::cout << "ts = " << cur_duration << "s, copied " << total_keys
217 << " keys so far (" << stringify(si_t(total_size)) << ")"
218 << std::endl;
219
220 } while (it->valid());
221
222 utime_t time_taken = ceph_clock_now() - started_at;
223
224 std::cout << "summary:" << std::endl;
225 std::cout << " copied " << total_keys << " keys" << std::endl;
226 std::cout << " used " << total_txs << " transactions" << std::endl;
227 std::cout << " total size " << stringify(si_t(total_size)) << std::endl;
228 std::cout << " from '" << store_path << "' to '" << other_path << "'"
229 << std::endl;
230 std::cout << " duration " << time_taken << " seconds" << std::endl;
231
232 return 0;
233 }
234
235 void compact() {
236 db->compact();
237 }
238 void compact_prefix(string prefix) {
239 db->compact_prefix(prefix);
240 }
241 void compact_range(string prefix, string start, string end) {
242 db->compact_range(prefix, start, end);
243 }
244};
245
246void usage(const char *pname)
247{
248 std::cerr << "Usage: " << pname << " <leveldb|rocksdb|...> <store path> command [args...]\n"
249 << "\n"
250 << "Commands:\n"
251 << " list [prefix]\n"
252 << " list-crc [prefix]\n"
253 << " exists <prefix> [key]\n"
254 << " get <prefix> <key> [out <file>]\n"
255 << " crc <prefix> <key>\n"
256 << " get-size [<prefix> <key>]\n"
257 << " set <prefix> <key> [ver <N>|in <file>]\n"
258 << " store-copy <path> [num-keys-per-tx]\n"
259 << " store-crc <path>\n"
260 << " compact\n"
261 << " compact-prefix <prefix>\n"
262 << " compact-range <prefix> <start> <end>\n"
263 << std::endl;
264}
265
266int main(int argc, const char *argv[])
267{
268 vector<const char*> args;
269 argv_to_vec(argc, argv, args);
270 env_to_vec(args);
271
272 auto cct = global_init(
273 NULL, args,
274 CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
275 common_init_finish(g_ceph_context);
276
277
278 if (args.size() < 3) {
279 usage(argv[0]);
280 return 1;
281 }
282
283 string type(args[0]);
284 string path(args[1]);
285 string cmd(args[2]);
286
287 StoreTool st(type, path);
288
289 if (cmd == "list" || cmd == "list-crc") {
290 string prefix;
291 if (argc > 4)
292 prefix = url_unescape(argv[4]);
293
294 bool do_crc = (cmd == "list-crc");
295
296 st.list(prefix, do_crc);
297
298 } else if (cmd == "exists") {
299 string key;
300 if (argc < 5) {
301 usage(argv[0]);
302 return 1;
303 }
304 string prefix(url_unescape(argv[4]));
305 if (argc > 5)
306 key = url_unescape(argv[5]);
307
308 bool ret = st.exists(prefix, key);
309 std::cout << "(" << url_escape(prefix) << ", " << url_escape(key) << ") "
310 << (ret ? "exists" : "does not exist")
311 << std::endl;
312 return (ret ? 0 : 1);
313
314 } else if (cmd == "get") {
315 if (argc < 6) {
316 usage(argv[0]);
317 return 1;
318 }
319 string prefix(url_unescape(argv[4]));
320 string key(url_unescape(argv[5]));
321
322 bool exists = false;
323 bufferlist bl = st.get(prefix, key, exists);
324 std::cout << "(" << url_escape(prefix) << ", " << url_escape(key) << ")";
325 if (!exists) {
326 std::cout << " does not exist" << std::endl;
327 return 1;
328 }
329 std::cout << std::endl;
330
331 if (argc >= 7) {
332 string subcmd(argv[6]);
333 if (subcmd != "out") {
334 std::cerr << "unrecognized subcmd '" << subcmd << "'"
335 << std::endl;
336 return 1;
337 }
338 if (argc < 8) {
339 std::cerr << "output path not specified" << std::endl;
340 return 1;
341 }
342 string out(argv[7]);
343
344 if (out.empty()) {
345 std::cerr << "unspecified out file" << std::endl;
346 return 1;
347 }
348
349 int err = bl.write_file(argv[7], 0644);
350 if (err < 0) {
351 std::cerr << "error writing value to '" << out << "': "
352 << cpp_strerror(err) << std::endl;
353 return 1;
354 }
355 } else {
356 ostringstream os;
357 bl.hexdump(os);
358 std::cout << os.str() << std::endl;
359 }
360
361 } else if (cmd == "crc") {
362 if (argc < 6) {
363 usage(argv[0]);
364 return 1;
365 }
366 string prefix(url_unescape(argv[4]));
367 string key(url_unescape(argv[5]));
368
369 bool exists = false;
370 bufferlist bl = st.get(prefix, key, exists);
371 std::cout << "(" << url_escape(prefix) << ", " << url_escape(key) << ") ";
372 if (!exists) {
373 std::cout << " does not exist" << std::endl;
374 return 1;
375 }
376 std::cout << " crc " << bl.crc32c(0) << std::endl;
377
378 } else if (cmd == "get-size") {
379 std::cout << "estimated store size: " << st.get_size() << std::endl;
380
381 if (argc < 5)
382 return 0;
383
384 if (argc < 6) {
385 usage(argv[0]);
386 return 1;
387 }
388 string prefix(url_unescape(argv[4]));
389 string key(url_unescape(argv[5]));
390
391 bool exists = false;
392 bufferlist bl = st.get(prefix, key, exists);
393 if (!exists) {
394 std::cerr << "(" << url_escape(prefix) << "," << url_escape(key)
395 << ") does not exist" << std::endl;
396 return 1;
397 }
398 std::cout << "(" << url_escape(prefix) << "," << url_escape(key)
399 << ") size " << si_t(bl.length()) << std::endl;
400
401 } else if (cmd == "set") {
402 if (argc < 8) {
403 usage(argv[0]);
404 return 1;
405 }
406 string prefix(url_unescape(argv[4]));
407 string key(url_unescape(argv[5]));
408 string subcmd(argv[6]);
409
410 bufferlist val;
411 string errstr;
412 if (subcmd == "ver") {
413 version_t v = (version_t) strict_strtoll(argv[7], 10, &errstr);
414 if (!errstr.empty()) {
415 std::cerr << "error reading version: " << errstr << std::endl;
416 return 1;
417 }
418 ::encode(v, val);
419 } else if (subcmd == "in") {
420 int ret = val.read_file(argv[7], &errstr);
421 if (ret < 0 || !errstr.empty()) {
422 std::cerr << "error reading file: " << errstr << std::endl;
423 return 1;
424 }
425 } else {
426 std::cerr << "unrecognized subcommand '" << subcmd << "'" << std::endl;
427 usage(argv[0]);
428 return 1;
429 }
430
431 bool ret = st.set(prefix, key, val);
432 if (!ret) {
433 std::cerr << "error setting ("
434 << url_escape(prefix) << "," << url_escape(key) << ")" << std::endl;
435 return 1;
436 }
437 } else if (cmd == "store-copy") {
438 int num_keys_per_tx = 128; // magic number that just feels right.
439 if (argc < 5) {
440 usage(argv[0]);
441 return 1;
442 } else if (argc > 5) {
443 string err;
444 num_keys_per_tx = strict_strtol(argv[5], 10, &err);
445 if (!err.empty()) {
446 std::cerr << "invalid num_keys_per_tx: " << err << std::endl;
447 return 1;
448 }
449 }
450
451 int ret = st.copy_store_to(argv[1], argv[4], num_keys_per_tx);
452 if (ret < 0) {
453 std::cerr << "error copying store to path '" << argv[4]
454 << "': " << cpp_strerror(ret) << std::endl;
455 return 1;
456 }
457
458 } else if (cmd == "store-crc") {
459 uint32_t crc = st.traverse(string(), true, NULL);
460 std::cout << "store at '" << path << "' crc " << crc << std::endl;
461
462 } else if (cmd == "compact") {
463 st.compact();
464 } else if (cmd == "compact-prefix") {
465 if (argc < 5) {
466 usage(argv[0]);
467 return 1;
468 }
469 string prefix(url_unescape(argv[4]));
470 st.compact_prefix(prefix);
471 } else if (cmd == "compact-range") {
472 if (argc < 7) {
473 usage(argv[0]);
474 return 1;
475 }
476 string prefix(url_unescape(argv[4]));
477 string start(url_unescape(argv[5]));
478 string end(url_unescape(argv[6]));
479 st.compact_range(prefix, start, end);
480 } else {
481 std::cerr << "Unrecognized command: " << cmd << std::endl;
482 return 1;
483 }
484
485 return 0;
486}