]>
Commit | Line | Data |
---|---|---|
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> | |
11fdf7f2 | 16 | #include <fstream> |
7c673cae FG |
17 | |
18 | #include "common/ceph_argparse.h" | |
19 | #include "common/config.h" | |
20 | #include "common/errno.h" | |
21 | #include "common/strtol.h" | |
7c673cae FG |
22 | #include "common/url_escape.h" |
23 | ||
11fdf7f2 TL |
24 | #include "global/global_context.h" |
25 | #include "global/global_init.h" | |
7c673cae | 26 | |
11fdf7f2 | 27 | #include "kvstore_tool.h" |
7c673cae FG |
28 | |
29 | void usage(const char *pname) | |
30 | { | |
11fdf7f2 | 31 | std::cout << "Usage: " << pname << " <leveldb|rocksdb|bluestore-kv> <store path> command [args...]\n" |
7c673cae FG |
32 | << "\n" |
33 | << "Commands:\n" | |
34 | << " list [prefix]\n" | |
35 | << " list-crc [prefix]\n" | |
11fdf7f2 | 36 | << " dump [prefix]\n" |
7c673cae FG |
37 | << " exists <prefix> [key]\n" |
38 | << " get <prefix> <key> [out <file>]\n" | |
39 | << " crc <prefix> <key>\n" | |
40 | << " get-size [<prefix> <key>]\n" | |
41 | << " set <prefix> <key> [ver <N>|in <file>]\n" | |
31f18b77 FG |
42 | << " rm <prefix> <key>\n" |
43 | << " rm-prefix <prefix>\n" | |
11fdf7f2 | 44 | << " store-copy <path> [num-keys-per-tx] [leveldb|rocksdb|...] \n" |
7c673cae FG |
45 | << " store-crc <path>\n" |
46 | << " compact\n" | |
47 | << " compact-prefix <prefix>\n" | |
48 | << " compact-range <prefix> <start> <end>\n" | |
11fdf7f2 | 49 | << " destructive-repair (use only as last resort! may corrupt healthy data)\n" |
494da23a | 50 | << " stats\n" |
7c673cae FG |
51 | << std::endl; |
52 | } | |
53 | ||
54 | int main(int argc, const char *argv[]) | |
55 | { | |
56 | vector<const char*> args; | |
57 | argv_to_vec(argc, argv, args); | |
11fdf7f2 TL |
58 | if (args.empty()) { |
59 | cerr << argv[0] << ": -h or --help for usage" << std::endl; | |
60 | exit(1); | |
61 | } | |
62 | if (ceph_argparse_need_usage(args)) { | |
63 | usage(argv[0]); | |
64 | exit(0); | |
65 | } | |
66 | ||
67 | map<string,string> defaults = { | |
68 | { "debug_rocksdb", "2" } | |
69 | }; | |
7c673cae FG |
70 | |
71 | auto cct = global_init( | |
11fdf7f2 TL |
72 | &defaults, args, |
73 | CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, | |
74 | CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); | |
7c673cae FG |
75 | common_init_finish(g_ceph_context); |
76 | ||
11fdf7f2 TL |
77 | ceph_assert((int)args.size() < argc); |
78 | for(size_t i=0; i<args.size(); i++) | |
79 | argv[i+1] = args[i]; | |
80 | argc = args.size() + 1; | |
7c673cae FG |
81 | |
82 | if (args.size() < 3) { | |
83 | usage(argv[0]); | |
84 | return 1; | |
85 | } | |
86 | ||
87 | string type(args[0]); | |
88 | string path(args[1]); | |
89 | string cmd(args[2]); | |
90 | ||
11fdf7f2 TL |
91 | if (type != "leveldb" && |
92 | type != "rocksdb" && | |
93 | type != "bluestore-kv") { | |
94 | ||
95 | std::cerr << "Unrecognized type: " << args[0] << std::endl; | |
96 | usage(argv[0]); | |
97 | return 1; | |
98 | } | |
99 | ||
100 | bool need_open_db = (cmd != "destructive-repair"); | |
494da23a TL |
101 | bool need_stats = (cmd == "stats"); |
102 | StoreTool st(type, path, need_open_db, need_stats); | |
7c673cae | 103 | |
11fdf7f2 TL |
104 | if (cmd == "destructive-repair") { |
105 | int ret = st.destructive_repair(); | |
106 | if (!ret) { | |
107 | std::cout << "destructive-repair completed without reporting an error" | |
108 | << std::endl; | |
109 | } else { | |
110 | std::cout << "destructive-repair failed with " << cpp_strerror(ret) | |
111 | << std::endl; | |
112 | } | |
113 | return ret; | |
114 | } else if (cmd == "list" || cmd == "list-crc") { | |
7c673cae FG |
115 | string prefix; |
116 | if (argc > 4) | |
117 | prefix = url_unescape(argv[4]); | |
118 | ||
119 | bool do_crc = (cmd == "list-crc"); | |
11fdf7f2 | 120 | st.list(prefix, do_crc, false); |
7c673cae | 121 | |
11fdf7f2 TL |
122 | } else if (cmd == "dump") { |
123 | string prefix; | |
124 | if (argc > 4) | |
125 | prefix = url_unescape(argv[4]); | |
126 | st.list(prefix, false, true); | |
7c673cae FG |
127 | |
128 | } else if (cmd == "exists") { | |
129 | string key; | |
130 | if (argc < 5) { | |
131 | usage(argv[0]); | |
132 | return 1; | |
133 | } | |
134 | string prefix(url_unescape(argv[4])); | |
135 | if (argc > 5) | |
136 | key = url_unescape(argv[5]); | |
137 | ||
138 | bool ret = st.exists(prefix, key); | |
139 | std::cout << "(" << url_escape(prefix) << ", " << url_escape(key) << ") " | |
140 | << (ret ? "exists" : "does not exist") | |
141 | << std::endl; | |
142 | return (ret ? 0 : 1); | |
143 | ||
144 | } else if (cmd == "get") { | |
145 | if (argc < 6) { | |
146 | usage(argv[0]); | |
147 | return 1; | |
148 | } | |
149 | string prefix(url_unescape(argv[4])); | |
150 | string key(url_unescape(argv[5])); | |
151 | ||
152 | bool exists = false; | |
153 | bufferlist bl = st.get(prefix, key, exists); | |
154 | std::cout << "(" << url_escape(prefix) << ", " << url_escape(key) << ")"; | |
155 | if (!exists) { | |
156 | std::cout << " does not exist" << std::endl; | |
157 | return 1; | |
158 | } | |
159 | std::cout << std::endl; | |
160 | ||
161 | if (argc >= 7) { | |
162 | string subcmd(argv[6]); | |
163 | if (subcmd != "out") { | |
164 | std::cerr << "unrecognized subcmd '" << subcmd << "'" | |
165 | << std::endl; | |
166 | return 1; | |
167 | } | |
168 | if (argc < 8) { | |
169 | std::cerr << "output path not specified" << std::endl; | |
170 | return 1; | |
171 | } | |
172 | string out(argv[7]); | |
173 | ||
174 | if (out.empty()) { | |
175 | std::cerr << "unspecified out file" << std::endl; | |
176 | return 1; | |
177 | } | |
178 | ||
179 | int err = bl.write_file(argv[7], 0644); | |
180 | if (err < 0) { | |
181 | std::cerr << "error writing value to '" << out << "': " | |
182 | << cpp_strerror(err) << std::endl; | |
183 | return 1; | |
184 | } | |
185 | } else { | |
186 | ostringstream os; | |
187 | bl.hexdump(os); | |
188 | std::cout << os.str() << std::endl; | |
189 | } | |
190 | ||
191 | } else if (cmd == "crc") { | |
192 | if (argc < 6) { | |
193 | usage(argv[0]); | |
194 | return 1; | |
195 | } | |
196 | string prefix(url_unescape(argv[4])); | |
197 | string key(url_unescape(argv[5])); | |
198 | ||
199 | bool exists = false; | |
200 | bufferlist bl = st.get(prefix, key, exists); | |
201 | std::cout << "(" << url_escape(prefix) << ", " << url_escape(key) << ") "; | |
202 | if (!exists) { | |
203 | std::cout << " does not exist" << std::endl; | |
204 | return 1; | |
205 | } | |
206 | std::cout << " crc " << bl.crc32c(0) << std::endl; | |
207 | ||
208 | } else if (cmd == "get-size") { | |
209 | std::cout << "estimated store size: " << st.get_size() << std::endl; | |
210 | ||
211 | if (argc < 5) | |
212 | return 0; | |
213 | ||
214 | if (argc < 6) { | |
215 | usage(argv[0]); | |
216 | return 1; | |
217 | } | |
218 | string prefix(url_unescape(argv[4])); | |
219 | string key(url_unescape(argv[5])); | |
220 | ||
221 | bool exists = false; | |
222 | bufferlist bl = st.get(prefix, key, exists); | |
223 | if (!exists) { | |
224 | std::cerr << "(" << url_escape(prefix) << "," << url_escape(key) | |
225 | << ") does not exist" << std::endl; | |
226 | return 1; | |
227 | } | |
228 | std::cout << "(" << url_escape(prefix) << "," << url_escape(key) | |
11fdf7f2 | 229 | << ") size " << byte_u_t(bl.length()) << std::endl; |
7c673cae FG |
230 | |
231 | } else if (cmd == "set") { | |
232 | if (argc < 8) { | |
233 | usage(argv[0]); | |
234 | return 1; | |
235 | } | |
236 | string prefix(url_unescape(argv[4])); | |
237 | string key(url_unescape(argv[5])); | |
238 | string subcmd(argv[6]); | |
239 | ||
240 | bufferlist val; | |
241 | string errstr; | |
242 | if (subcmd == "ver") { | |
243 | version_t v = (version_t) strict_strtoll(argv[7], 10, &errstr); | |
244 | if (!errstr.empty()) { | |
245 | std::cerr << "error reading version: " << errstr << std::endl; | |
246 | return 1; | |
247 | } | |
11fdf7f2 | 248 | encode(v, val); |
7c673cae FG |
249 | } else if (subcmd == "in") { |
250 | int ret = val.read_file(argv[7], &errstr); | |
251 | if (ret < 0 || !errstr.empty()) { | |
252 | std::cerr << "error reading file: " << errstr << std::endl; | |
253 | return 1; | |
254 | } | |
255 | } else { | |
256 | std::cerr << "unrecognized subcommand '" << subcmd << "'" << std::endl; | |
257 | usage(argv[0]); | |
258 | return 1; | |
259 | } | |
260 | ||
261 | bool ret = st.set(prefix, key, val); | |
262 | if (!ret) { | |
263 | std::cerr << "error setting (" | |
264 | << url_escape(prefix) << "," << url_escape(key) << ")" << std::endl; | |
265 | return 1; | |
266 | } | |
31f18b77 FG |
267 | } else if (cmd == "rm") { |
268 | if (argc < 6) { | |
269 | usage(argv[0]); | |
270 | return 1; | |
271 | } | |
272 | string prefix(url_unescape(argv[4])); | |
273 | string key(url_unescape(argv[5])); | |
274 | ||
275 | bool ret = st.rm(prefix, key); | |
276 | if (!ret) { | |
277 | std::cerr << "error removing (" | |
278 | << url_escape(prefix) << "," << url_escape(key) << ")" | |
279 | << std::endl; | |
280 | return 1; | |
281 | } | |
282 | } else if (cmd == "rm-prefix") { | |
283 | if (argc < 5) { | |
284 | usage(argv[0]); | |
285 | return 1; | |
286 | } | |
287 | string prefix(url_unescape(argv[4])); | |
288 | ||
289 | bool ret = st.rm_prefix(prefix); | |
290 | if (!ret) { | |
291 | std::cerr << "error removing prefix (" | |
292 | << url_escape(prefix) << ")" | |
293 | << std::endl; | |
294 | return 1; | |
295 | } | |
7c673cae FG |
296 | } else if (cmd == "store-copy") { |
297 | int num_keys_per_tx = 128; // magic number that just feels right. | |
298 | if (argc < 5) { | |
299 | usage(argv[0]); | |
300 | return 1; | |
301 | } else if (argc > 5) { | |
302 | string err; | |
303 | num_keys_per_tx = strict_strtol(argv[5], 10, &err); | |
304 | if (!err.empty()) { | |
305 | std::cerr << "invalid num_keys_per_tx: " << err << std::endl; | |
306 | return 1; | |
307 | } | |
308 | } | |
11fdf7f2 TL |
309 | string other_store_type = argv[1]; |
310 | if (argc > 6) { | |
311 | other_store_type = argv[6]; | |
312 | } | |
7c673cae | 313 | |
11fdf7f2 | 314 | int ret = st.copy_store_to(argv[1], argv[4], num_keys_per_tx, other_store_type); |
7c673cae FG |
315 | if (ret < 0) { |
316 | std::cerr << "error copying store to path '" << argv[4] | |
317 | << "': " << cpp_strerror(ret) << std::endl; | |
318 | return 1; | |
319 | } | |
320 | ||
321 | } else if (cmd == "store-crc") { | |
11fdf7f2 TL |
322 | if (argc < 4) { |
323 | usage(argv[0]); | |
324 | return 1; | |
325 | } | |
326 | std::ofstream fs(argv[4]); | |
327 | uint32_t crc = st.traverse(string(), true, false, &fs); | |
328 | std::cout << "store at '" << argv[4] << "' crc " << crc << std::endl; | |
7c673cae FG |
329 | |
330 | } else if (cmd == "compact") { | |
331 | st.compact(); | |
332 | } else if (cmd == "compact-prefix") { | |
333 | if (argc < 5) { | |
334 | usage(argv[0]); | |
335 | return 1; | |
336 | } | |
337 | string prefix(url_unescape(argv[4])); | |
338 | st.compact_prefix(prefix); | |
339 | } else if (cmd == "compact-range") { | |
340 | if (argc < 7) { | |
341 | usage(argv[0]); | |
342 | return 1; | |
343 | } | |
344 | string prefix(url_unescape(argv[4])); | |
345 | string start(url_unescape(argv[5])); | |
346 | string end(url_unescape(argv[6])); | |
347 | st.compact_range(prefix, start, end); | |
494da23a TL |
348 | } else if (cmd == "stats") { |
349 | st.print_stats(); | |
7c673cae FG |
350 | } else { |
351 | std::cerr << "Unrecognized command: " << cmd << std::endl; | |
352 | return 1; | |
353 | } | |
354 | ||
355 | return 0; | |
356 | } |