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