]>
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) 2004-2006 Sage Weil <sage@newdream.net> | |
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 | */ | |
14 | ||
15 | #include "include/types.h" | |
16 | ||
17 | #include "include/rados/librados.hpp" | |
18 | #include "include/rados/rados_types.hpp" | |
19 | #include "include/radosstriper/libradosstriper.hpp" | |
20 | using namespace libradosstriper; | |
21 | ||
22 | #include "common/config.h" | |
23 | #include "common/ceph_argparse.h" | |
24 | #include "global/global_init.h" | |
25 | #include "common/Cond.h" | |
26 | #include "common/debug.h" | |
27 | #include "common/errno.h" | |
28 | #include "common/Formatter.h" | |
29 | #include "common/obj_bencher.h" | |
30 | #include "common/TextTable.h" | |
31 | #include "include/stringify.h" | |
32 | #include "mds/inode_backtrace.h" | |
33 | #include "auth/Crypto.h" | |
34 | #include <iostream> | |
35 | #include <fstream> | |
36 | ||
37 | #include <stdlib.h> | |
38 | #include <time.h> | |
39 | #include <sstream> | |
40 | #include <errno.h> | |
41 | #include <dirent.h> | |
42 | #include <stdexcept> | |
43 | #include <climits> | |
44 | #include <locale> | |
45 | #include <memory> | |
46 | ||
47 | #include "cls/lock/cls_lock_client.h" | |
48 | #include "include/compat.h" | |
49 | #include "include/util.h" | |
50 | #include "common/hobject.h" | |
51 | ||
52 | #include "PoolDump.h" | |
53 | #include "RadosImport.h" | |
54 | ||
55 | using namespace librados; | |
56 | ||
57 | // two steps seem to be necessary to do this right | |
58 | #define STR(x) _STR(x) | |
59 | #define _STR(x) #x | |
60 | ||
61 | void usage(ostream& out) | |
62 | { | |
63 | out << \ | |
64 | "usage: rados [options] [commands]\n" | |
65 | "POOL COMMANDS\n" | |
66 | " lspools list pools\n" | |
67 | " mkpool <pool-name> [123[ 4]] create pool <pool-name>'\n" | |
68 | " [with auid 123[and using crush rule 4]]\n" | |
69 | " cppool <pool-name> <dest-pool> copy content of a pool\n" | |
70 | " rmpool <pool-name> [<pool-name> --yes-i-really-really-mean-it]\n" | |
71 | " remove pool <pool-name>'\n" | |
72 | " purge <pool-name> --yes-i-really-really-mean-it\n" | |
73 | " remove all objects from pool <pool-name> without removing it\n" | |
74 | " df show per-pool and total usage\n" | |
75 | " ls list objects in pool\n\n" | |
76 | " chown 123 change the pool owner to auid 123\n" | |
77 | "\n" | |
78 | "POOL SNAP COMMANDS\n" | |
79 | " lssnap list snaps\n" | |
80 | " mksnap <snap-name> create snap <snap-name>\n" | |
81 | " rmsnap <snap-name> remove snap <snap-name>\n" | |
82 | "\n" | |
83 | "OBJECT COMMANDS\n" | |
84 | " get <obj-name> [outfile] fetch object\n" | |
85 | " put <obj-name> [infile] [--offset offset]\n" | |
31f18b77 | 86 | " write object with start offset (default:0)\n" |
7c673cae FG |
87 | " append <obj-name> [infile] append object\n" |
88 | " truncate <obj-name> length truncate object\n" | |
89 | " create <obj-name> create object\n" | |
90 | " rm <obj-name> ...[--force-full] [force no matter full or not]remove object(s)\n" | |
91 | " cp <obj-name> [target-obj] copy object\n" | |
92 | " listxattr <obj-name>\n" | |
93 | " getxattr <obj-name> attr\n" | |
94 | " setxattr <obj-name> attr val\n" | |
95 | " rmxattr <obj-name> attr\n" | |
35e4c445 | 96 | " stat <obj-name> stat the named object\n" |
7c673cae FG |
97 | " mapext <obj-name>\n" |
98 | " rollback <obj-name> <snap-name> roll back object to snap <snap-name>\n" | |
99 | "\n" | |
100 | " listsnaps <obj-name> list the snapshots of this object\n" | |
101 | " bench <seconds> write|seq|rand [-t concurrent_operations] [--no-cleanup] [--run-name run_name] [--no-hints]\n" | |
102 | " default is 16 concurrent IOs and 4 MB ops\n" | |
103 | " default is to clean up after write benchmark\n" | |
104 | " default run-name is 'benchmark_last_metadata'\n" | |
105 | " cleanup [--run-name run_name] [--prefix prefix]\n" | |
106 | " clean up a previous benchmark operation\n" | |
107 | " default run-name is 'benchmark_last_metadata'\n" | |
108 | " load-gen [options] generate load on the cluster\n" | |
109 | " listomapkeys <obj-name> list the keys in the object map\n" | |
110 | " listomapvals <obj-name> list the keys and vals in the object map \n" | |
111 | " getomapval <obj-name> <key> [file] show the value for the specified key\n" | |
112 | " in the object's object map\n" | |
113 | " setomapval <obj-name> <key> <val>\n" | |
114 | " rmomapkey <obj-name> <key>\n" | |
115 | " getomapheader <obj-name> [file]\n" | |
116 | " setomapheader <obj-name> <val>\n" | |
117 | " tmap-to-omap <obj-name> convert tmap keys/values to omap\n" | |
118 | " watch <obj-name> add watcher on this object\n" | |
119 | " notify <obj-name> <message> notify watcher of this object with message\n" | |
120 | " listwatchers <obj-name> list the watchers of this object\n" | |
121 | " set-alloc-hint <obj-name> <expected-object-size> <expected-write-size>\n" | |
122 | " set allocation hint for an object\n" | |
123 | "\n" | |
124 | "IMPORT AND EXPORT\n" | |
125 | " export [filename]\n" | |
126 | " Serialize pool contents to a file or standard out.\n" | |
127 | " import [--dry-run] [--no-overwrite] < filename | - >\n" | |
128 | " Load pool contents from a file or standard in\n" | |
129 | "\n" | |
130 | "ADVISORY LOCKS\n" | |
131 | " lock list <obj-name>\n" | |
132 | " List all advisory locks on an object\n" | |
133 | " lock get <obj-name> <lock-name>\n" | |
134 | " Try to acquire a lock\n" | |
135 | " lock break <obj-name> <lock-name> <locker-name>\n" | |
136 | " Try to break a lock acquired by another client\n" | |
137 | " lock info <obj-name> <lock-name>\n" | |
138 | " Show lock information\n" | |
139 | " options:\n" | |
140 | " --lock-tag Lock tag, all locks operation should use\n" | |
141 | " the same tag\n" | |
142 | " --lock-cookie Locker cookie\n" | |
143 | " --lock-description Description of lock\n" | |
144 | " --lock-duration Lock duration (in seconds)\n" | |
145 | " --lock-type Lock type (shared, exclusive)\n" | |
146 | "\n" | |
147 | "SCRUB AND REPAIR:\n" | |
148 | " list-inconsistent-pg <pool> list inconsistent PGs in given pool\n" | |
149 | " list-inconsistent-obj <pgid> list inconsistent objects in given pg\n" | |
150 | " list-inconsistent-snapset <pgid> list inconsistent snapsets in the given pg\n" | |
151 | "\n" | |
152 | "CACHE POOLS: (for testing/development only)\n" | |
153 | " cache-flush <obj-name> flush cache pool object (blocking)\n" | |
154 | " cache-try-flush <obj-name> flush cache pool object (non-blocking)\n" | |
155 | " cache-evict <obj-name> evict cache pool object\n" | |
156 | " cache-flush-evict-all flush+evict all objects\n" | |
157 | " cache-try-flush-evict-all try-flush+evict all objects\n" | |
158 | "\n" | |
159 | "GLOBAL OPTIONS:\n" | |
160 | " --object_locator object_locator\n" | |
161 | " set object_locator for operation\n" | |
162 | " -p pool\n" | |
163 | " --pool=pool\n" | |
164 | " select given pool by name\n" | |
165 | " --target-pool=pool\n" | |
166 | " select target pool by name\n" | |
167 | " -b op_size\n" | |
168 | " set the block size for put/get ops and for write benchmarking\n" | |
169 | " -o object_size\n" | |
170 | " set the object size for put/get ops and for write benchmarking\n" | |
171 | " --max-objects\n" | |
172 | " set the max number of objects for write benchmarking\n" | |
173 | " -s name\n" | |
174 | " --snap name\n" | |
175 | " select given snap name for (read) IO\n" | |
176 | " -i infile\n" | |
177 | " --create\n" | |
178 | " create the pool or directory that was specified\n" | |
179 | " -N namespace\n" | |
180 | " --namespace=namespace\n" | |
181 | " specify the namespace to use for the object\n" | |
182 | " --all\n" | |
183 | " Use with ls to list objects in all namespaces\n" | |
184 | " Put in CEPH_ARGS environment variable to make this the default\n" | |
185 | " --default\n" | |
186 | " Use with ls to list objects in default namespace\n" | |
187 | " Takes precedence over --all in case --all is in environment\n" | |
188 | " --target-locator\n" | |
189 | " Use with cp to specify the locator of the new object\n" | |
190 | " --target-nspace\n" | |
191 | " Use with cp to specify the namespace of the new object\n" | |
192 | " --striper\n" | |
193 | " Use radostriper interface rather than pure rados\n" | |
194 | " Available for stat, get, put, truncate, rm, ls and \n" | |
195 | " all xattr related operations\n" | |
196 | "\n" | |
197 | "BENCH OPTIONS:\n" | |
198 | " -t N\n" | |
199 | " --concurrent-ios=N\n" | |
200 | " Set number of concurrent I/O operations\n" | |
201 | " --show-time\n" | |
202 | " prefix output with date/time\n" | |
203 | " --no-verify\n" | |
204 | " do not verify contents of read objects\n" | |
205 | " --write-object\n" | |
206 | " write contents to the objects\n" | |
207 | " --write-omap\n" | |
208 | " write contents to the omap\n" | |
209 | " --write-xattr\n" | |
210 | " write contents to the extended attributes\n" | |
211 | "\n" | |
212 | "LOAD GEN OPTIONS:\n" | |
213 | " --num-objects total number of objects\n" | |
214 | " --min-object-size min object size\n" | |
215 | " --max-object-size max object size\n" | |
216 | " --min-op-len min io size of operations\n" | |
217 | " --max-op-len max io size of operations\n" | |
218 | " --max-ops max number of operations\n" | |
219 | " --max-backlog max backlog size\n" | |
220 | " --read-percent percent of operations that are read\n" | |
221 | " --target-throughput target throughput (in bytes)\n" | |
222 | " --run-length total time (in seconds)\n" | |
223 | "CACHE POOLS OPTIONS:\n" | |
224 | " --with-clones include clones when doing flush or evict\n" | |
225 | "OMAP OPTIONS:\n" | |
226 | " --omap-key-file file read the omap key from a file\n"; | |
227 | } | |
228 | ||
229 | unsigned default_op_size = 1 << 22; | |
230 | ||
231 | static void usage_exit() | |
232 | { | |
233 | usage(cerr); | |
234 | exit(1); | |
235 | } | |
236 | ||
237 | ||
238 | template <typename I, typename T> | |
239 | static int rados_sistrtoll(I &i, T *val) { | |
240 | std::string err; | |
241 | *val = strict_sistrtoll(i->second.c_str(), &err); | |
242 | if (err != "") { | |
243 | cerr << "Invalid value for " << i->first << ": " << err << std::endl; | |
244 | return -EINVAL; | |
245 | } else { | |
246 | return 0; | |
247 | } | |
248 | } | |
249 | ||
250 | ||
251 | static int dump_data(std::string const &filename, bufferlist const &data) | |
252 | { | |
253 | int fd; | |
254 | if (filename == "-") { | |
255 | fd = STDOUT_FILENO; | |
256 | } else { | |
257 | fd = TEMP_FAILURE_RETRY(::open(filename.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0644)); | |
258 | if (fd < 0) { | |
259 | int err = errno; | |
260 | cerr << "failed to open file: " << cpp_strerror(err) << std::endl; | |
261 | return -err; | |
262 | } | |
263 | } | |
264 | ||
265 | int r = data.write_fd(fd); | |
266 | ||
267 | if (fd != 1) { | |
268 | VOID_TEMP_FAILURE_RETRY(::close(fd)); | |
269 | } | |
270 | ||
271 | return r; | |
272 | } | |
273 | ||
274 | ||
275 | static int do_get(IoCtx& io_ctx, RadosStriper& striper, | |
276 | const char *objname, const char *outfile, unsigned op_size, | |
277 | bool use_striper) | |
278 | { | |
279 | string oid(objname); | |
280 | ||
281 | int fd; | |
282 | if (strcmp(outfile, "-") == 0) { | |
283 | fd = STDOUT_FILENO; | |
284 | } else { | |
285 | fd = TEMP_FAILURE_RETRY(::open(outfile, O_WRONLY|O_CREAT|O_TRUNC, 0644)); | |
286 | if (fd < 0) { | |
287 | int err = errno; | |
288 | cerr << "failed to open file: " << cpp_strerror(err) << std::endl; | |
289 | return -err; | |
290 | } | |
291 | } | |
292 | ||
293 | uint64_t offset = 0; | |
294 | int ret; | |
295 | while (true) { | |
296 | bufferlist outdata; | |
297 | if (use_striper) { | |
298 | ret = striper.read(oid, &outdata, op_size, offset); | |
299 | } else { | |
300 | ret = io_ctx.read(oid, outdata, op_size, offset); | |
301 | } | |
302 | if (ret <= 0) { | |
303 | goto out; | |
304 | } | |
305 | ret = outdata.write_fd(fd); | |
306 | if (ret < 0) { | |
307 | cerr << "error writing to file: " << cpp_strerror(ret) << std::endl; | |
308 | goto out; | |
309 | } | |
310 | if (outdata.length() < op_size) | |
311 | break; | |
312 | offset += outdata.length(); | |
313 | } | |
314 | ret = 0; | |
315 | ||
316 | out: | |
317 | if (fd != 1) | |
318 | VOID_TEMP_FAILURE_RETRY(::close(fd)); | |
319 | return ret; | |
320 | } | |
321 | ||
322 | static int do_copy(IoCtx& io_ctx, const char *objname, | |
323 | IoCtx& target_ctx, const char *target_obj) | |
324 | { | |
325 | __le32 src_fadvise_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL | LIBRADOS_OP_FLAG_FADVISE_NOCACHE; | |
326 | __le32 dest_fadvise_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL | LIBRADOS_OP_FLAG_FADVISE_DONTNEED; | |
327 | ObjectWriteOperation op; | |
328 | op.copy_from2(objname, io_ctx, 0, src_fadvise_flags); | |
329 | op.set_op_flags2(dest_fadvise_flags); | |
330 | ||
331 | return target_ctx.operate(target_obj, &op); | |
332 | } | |
333 | ||
334 | static int do_copy_pool(Rados& rados, const char *src_pool, const char *target_pool) | |
335 | { | |
336 | IoCtx src_ctx, target_ctx; | |
337 | int ret = rados.ioctx_create(src_pool, src_ctx); | |
338 | if (ret < 0) { | |
339 | cerr << "cannot open source pool: " << src_pool << std::endl; | |
340 | return ret; | |
341 | } | |
342 | ret = rados.ioctx_create(target_pool, target_ctx); | |
343 | if (ret < 0) { | |
344 | cerr << "cannot open target pool: " << target_pool << std::endl; | |
345 | return ret; | |
346 | } | |
347 | src_ctx.set_namespace(all_nspaces); | |
348 | librados::NObjectIterator i = src_ctx.nobjects_begin(); | |
349 | librados::NObjectIterator i_end = src_ctx.nobjects_end(); | |
350 | for (; i != i_end; ++i) { | |
351 | string nspace = i->get_nspace(); | |
352 | string oid = i->get_oid(); | |
353 | string locator = i->get_locator(); | |
354 | ||
355 | string target_name = (nspace.size() ? nspace + "/" : "") + oid; | |
356 | string src_name = target_name; | |
357 | if (locator.size()) | |
358 | src_name += "(@" + locator + ")"; | |
359 | cout << src_pool << ":" << src_name << " => " | |
360 | << target_pool << ":" << target_name << std::endl; | |
361 | ||
362 | src_ctx.locator_set_key(locator); | |
363 | src_ctx.set_namespace(nspace); | |
364 | target_ctx.set_namespace(nspace); | |
365 | ret = do_copy(src_ctx, oid.c_str(), target_ctx, oid.c_str()); | |
366 | if (ret < 0) { | |
367 | cerr << "error copying object: " << cpp_strerror(errno) << std::endl; | |
368 | return ret; | |
369 | } | |
370 | } | |
371 | ||
372 | return 0; | |
373 | } | |
374 | ||
375 | static int do_put(IoCtx& io_ctx, RadosStriper& striper, | |
376 | const char *objname, const char *infile, int op_size, | |
377 | uint64_t obj_offset, bool use_striper) | |
378 | { | |
379 | string oid(objname); | |
380 | bool stdio = (strcmp(infile, "-") == 0); | |
381 | int ret = 0; | |
382 | int fd = STDIN_FILENO; | |
383 | if (!stdio) | |
384 | fd = open(infile, O_RDONLY); | |
385 | if (fd < 0) { | |
386 | cerr << "error reading input file " << infile << ": " << cpp_strerror(errno) << std::endl; | |
387 | return 1; | |
388 | } | |
389 | int count = op_size; | |
390 | uint64_t offset = obj_offset; | |
391 | while (count != 0) { | |
392 | bufferlist indata; | |
393 | count = indata.read_fd(fd, op_size); | |
394 | if (count < 0) { | |
395 | ret = -errno; | |
396 | cerr << "error reading input file " << infile << ": " << cpp_strerror(ret) << std::endl; | |
397 | goto out; | |
398 | } | |
399 | ||
400 | if (count == 0) { | |
401 | if (offset == obj_offset) { // in case we have to create an empty object & if obj_offset > 0 do a hole | |
402 | if (use_striper) { | |
403 | ret = striper.write_full(oid, indata); // indata is empty | |
404 | } else { | |
405 | ret = io_ctx.write_full(oid, indata); // indata is empty | |
406 | } | |
407 | if (ret < 0) { | |
408 | goto out; | |
409 | } | |
410 | if (offset) { | |
411 | if (use_striper) { | |
412 | ret = striper.trunc(oid, offset); // before truncate, object must be existed. | |
413 | } else { | |
414 | ret = io_ctx.trunc(oid, offset); // before truncate, object must be existed. | |
415 | } | |
416 | ||
417 | if (ret < 0) { | |
418 | goto out; | |
419 | } | |
420 | } | |
421 | } | |
422 | continue; | |
423 | } | |
424 | if (use_striper) { | |
425 | if (offset == 0) | |
426 | ret = striper.write_full(oid, indata); | |
427 | else | |
428 | ret = striper.write(oid, indata, count, offset); | |
429 | } else { | |
430 | if (offset == 0) | |
431 | ret = io_ctx.write_full(oid, indata); | |
432 | else | |
433 | ret = io_ctx.write(oid, indata, count, offset); | |
434 | } | |
435 | ||
436 | if (ret < 0) { | |
437 | goto out; | |
438 | } | |
439 | offset += count; | |
440 | } | |
441 | ret = 0; | |
442 | out: | |
443 | if (fd != STDOUT_FILENO) | |
444 | VOID_TEMP_FAILURE_RETRY(close(fd)); | |
445 | return ret; | |
446 | } | |
447 | ||
448 | static int do_append(IoCtx& io_ctx, RadosStriper& striper, | |
449 | const char *objname, const char *infile, int op_size, | |
450 | bool use_striper) | |
451 | { | |
452 | string oid(objname); | |
453 | bool stdio = (strcmp(infile, "-") == 0); | |
454 | int ret = 0; | |
455 | int fd = STDIN_FILENO; | |
456 | if (!stdio) | |
457 | fd = open(infile, O_RDONLY); | |
458 | if (fd < 0) { | |
459 | cerr << "error reading input file " << infile << ": " << cpp_strerror(errno) << std::endl; | |
460 | return 1; | |
461 | } | |
462 | int count = op_size; | |
463 | while (count != 0) { | |
464 | bufferlist indata; | |
465 | count = indata.read_fd(fd, op_size); | |
466 | if (count < 0) { | |
467 | ret = -errno; | |
468 | cerr << "error reading input file " << infile << ": " << cpp_strerror(ret) << std::endl; | |
469 | goto out; | |
470 | } | |
471 | if (use_striper) { | |
472 | ret = striper.append(oid, indata, count); | |
473 | } else { | |
474 | ret = io_ctx.append(oid, indata, count); | |
475 | } | |
476 | ||
477 | if (ret < 0) { | |
478 | goto out; | |
479 | } | |
480 | } | |
481 | ret = 0; | |
482 | out: | |
483 | if (fd != STDOUT_FILENO) | |
484 | VOID_TEMP_FAILURE_RETRY(close(fd)); | |
485 | return ret; | |
486 | } | |
487 | ||
488 | class RadosWatchCtx : public librados::WatchCtx2 { | |
489 | IoCtx& ioctx; | |
490 | string name; | |
491 | public: | |
492 | RadosWatchCtx(IoCtx& io, const char *imgname) : ioctx(io), name(imgname) {} | |
493 | ~RadosWatchCtx() override {} | |
494 | void handle_notify(uint64_t notify_id, | |
495 | uint64_t cookie, | |
496 | uint64_t notifier_id, | |
497 | bufferlist& bl) override { | |
498 | cout << "NOTIFY" | |
499 | << " cookie " << cookie | |
500 | << " notify_id " << notify_id | |
501 | << " from " << notifier_id | |
502 | << std::endl; | |
503 | bl.hexdump(cout); | |
504 | ioctx.notify_ack(name, notify_id, cookie, bl); | |
505 | } | |
506 | void handle_error(uint64_t cookie, int err) override { | |
507 | cout << "ERROR" | |
508 | << " cookie " << cookie | |
509 | << " err " << cpp_strerror(err) | |
510 | << std::endl; | |
511 | } | |
512 | }; | |
513 | ||
514 | static const char alphanum_table[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; | |
515 | ||
516 | int gen_rand_alphanumeric(char *dest, int size) /* size should be the required string size + 1 */ | |
517 | { | |
518 | int ret = get_random_bytes(dest, size); | |
519 | if (ret < 0) { | |
520 | cerr << "cannot get random bytes: " << cpp_strerror(ret) << std::endl; | |
521 | return -1; | |
522 | } | |
523 | ||
524 | int i; | |
525 | for (i=0; i<size - 1; i++) { | |
526 | int pos = (unsigned)dest[i]; | |
527 | dest[i] = alphanum_table[pos & 63]; | |
528 | } | |
529 | dest[i] = '\0'; | |
530 | ||
531 | return 0; | |
532 | } | |
533 | ||
534 | struct obj_info { | |
535 | string name; | |
536 | size_t len; | |
537 | }; | |
538 | ||
539 | class LoadGen { | |
540 | size_t total_sent; | |
541 | size_t total_completed; | |
542 | ||
543 | IoCtx io_ctx; | |
544 | Rados *rados; | |
545 | ||
546 | map<int, obj_info> objs; | |
547 | ||
548 | utime_t start_time; | |
549 | ||
550 | bool going_down; | |
551 | ||
552 | public: | |
553 | int read_percent; | |
554 | int num_objs; | |
555 | size_t min_obj_len; | |
556 | uint64_t max_obj_len; | |
557 | size_t min_op_len; | |
558 | size_t max_op_len; | |
559 | size_t max_ops; | |
560 | size_t max_backlog; | |
561 | size_t target_throughput; | |
562 | int run_length; | |
563 | ||
564 | enum { | |
565 | OP_READ, | |
566 | OP_WRITE, | |
567 | }; | |
568 | ||
569 | struct LoadGenOp { | |
570 | int id; | |
571 | int type; | |
572 | string oid; | |
573 | size_t off; | |
574 | size_t len; | |
575 | bufferlist bl; | |
576 | LoadGen *lg; | |
577 | librados::AioCompletion *completion; | |
578 | ||
579 | LoadGenOp() : id(0), type(0), off(0), len(0), lg(NULL), completion(NULL) {} | |
580 | explicit LoadGenOp(LoadGen *_lg) : id(0), type(0), off(0), len(0), lg(_lg), completion(NULL) {} | |
581 | }; | |
582 | ||
583 | int max_op; | |
584 | ||
585 | map<int, LoadGenOp *> pending_ops; | |
586 | ||
587 | void gen_op(LoadGenOp *op); | |
588 | uint64_t gen_next_op(); | |
589 | void run_op(LoadGenOp *op); | |
590 | ||
591 | uint64_t cur_sent_rate() { | |
592 | return total_sent / time_passed(); | |
593 | } | |
594 | ||
595 | uint64_t cur_completed_rate() { | |
596 | return total_completed / time_passed(); | |
597 | } | |
598 | ||
599 | uint64_t total_expected() { | |
600 | return target_throughput * time_passed(); | |
601 | } | |
602 | ||
603 | float time_passed() { | |
604 | utime_t now = ceph_clock_now(); | |
605 | now -= start_time; | |
606 | uint64_t ns = now.nsec(); | |
607 | float total = (float) ns / 1000000000.0; | |
608 | total += now.sec(); | |
609 | return total; | |
610 | } | |
611 | ||
612 | Mutex lock; | |
613 | Cond cond; | |
614 | ||
615 | explicit LoadGen(Rados *_rados) : rados(_rados), going_down(false), lock("LoadGen") { | |
616 | read_percent = 80; | |
617 | min_obj_len = 1024; | |
618 | max_obj_len = 5ull * 1024ull * 1024ull * 1024ull; | |
619 | min_op_len = 1024; | |
620 | target_throughput = 5 * 1024 * 1024; // B/sec | |
621 | max_op_len = 2 * 1024 * 1024; | |
622 | max_ops = 16; | |
623 | max_backlog = target_throughput * 2; | |
624 | run_length = 60; | |
625 | ||
626 | total_sent = 0; | |
627 | total_completed = 0; | |
628 | num_objs = 200; | |
629 | max_op = 0; | |
630 | } | |
631 | int bootstrap(const char *pool); | |
632 | int run(); | |
633 | void cleanup(); | |
634 | ||
635 | void io_cb(completion_t c, LoadGenOp *op) { | |
636 | Mutex::Locker l(lock); | |
637 | ||
638 | total_completed += op->len; | |
639 | ||
640 | double rate = (double)cur_completed_rate() / (1024 * 1024); | |
641 | std::streamsize original_precision = cout.precision(); | |
642 | cout.precision(3); | |
643 | cout << "op " << op->id << " completed, throughput=" << rate << "MB/sec" << std::endl; | |
644 | cout.precision(original_precision); | |
645 | ||
646 | map<int, LoadGenOp *>::iterator iter = pending_ops.find(op->id); | |
647 | if (iter != pending_ops.end()) | |
648 | pending_ops.erase(iter); | |
649 | ||
650 | if (!going_down) | |
651 | op->completion->release(); | |
652 | ||
653 | delete op; | |
654 | ||
655 | cond.Signal(); | |
656 | } | |
657 | }; | |
658 | ||
659 | static void _load_gen_cb(completion_t c, void *param) | |
660 | { | |
661 | LoadGen::LoadGenOp *op = (LoadGen::LoadGenOp *)param; | |
662 | op->lg->io_cb(c, op); | |
663 | } | |
664 | ||
665 | int LoadGen::bootstrap(const char *pool) | |
666 | { | |
667 | char buf[128]; | |
668 | int i; | |
669 | ||
670 | if (!pool) { | |
671 | cerr << "ERROR: pool name was not specified" << std::endl; | |
672 | return -EINVAL; | |
673 | } | |
674 | ||
675 | int ret = rados->ioctx_create(pool, io_ctx); | |
676 | if (ret < 0) { | |
677 | cerr << "error opening pool " << pool << ": " << cpp_strerror(ret) << std::endl; | |
678 | return ret; | |
679 | } | |
680 | ||
681 | int buf_len = 1; | |
682 | bufferptr p = buffer::create(buf_len); | |
683 | bufferlist bl; | |
684 | memset(p.c_str(), 0, buf_len); | |
685 | bl.push_back(p); | |
686 | ||
687 | list<librados::AioCompletion *> completions; | |
688 | for (i = 0; i < num_objs; i++) { | |
689 | obj_info info; | |
690 | gen_rand_alphanumeric(buf, 16); | |
691 | info.name = "obj-"; | |
692 | info.name.append(buf); | |
693 | info.len = get_random(min_obj_len, max_obj_len); | |
694 | ||
695 | // throttle... | |
696 | while (completions.size() > max_ops) { | |
697 | AioCompletion *c = completions.front(); | |
698 | c->wait_for_complete(); | |
699 | ret = c->get_return_value(); | |
700 | c->release(); | |
701 | completions.pop_front(); | |
702 | if (ret < 0) { | |
703 | cerr << "aio_write failed" << std::endl; | |
704 | return ret; | |
705 | } | |
706 | } | |
707 | ||
708 | librados::AioCompletion *c = rados->aio_create_completion(NULL, NULL, NULL); | |
709 | completions.push_back(c); | |
710 | // generate object | |
711 | ret = io_ctx.aio_write(info.name, c, bl, buf_len, info.len - buf_len); | |
712 | if (ret < 0) { | |
713 | cerr << "couldn't write obj: " << info.name << " ret=" << ret << std::endl; | |
714 | return ret; | |
715 | } | |
716 | objs[i] = info; | |
717 | } | |
718 | ||
719 | list<librados::AioCompletion *>::iterator iter; | |
720 | for (iter = completions.begin(); iter != completions.end(); ++iter) { | |
721 | AioCompletion *c = *iter; | |
722 | c->wait_for_complete(); | |
723 | ret = c->get_return_value(); | |
724 | c->release(); | |
725 | if (ret < 0) { // yes, we leak. | |
726 | cerr << "aio_write failed" << std::endl; | |
727 | return ret; | |
728 | } | |
729 | } | |
730 | return 0; | |
731 | } | |
732 | ||
733 | void LoadGen::run_op(LoadGenOp *op) | |
734 | { | |
735 | op->completion = rados->aio_create_completion(op, _load_gen_cb, NULL); | |
736 | ||
737 | switch (op->type) { | |
738 | case OP_READ: | |
739 | io_ctx.aio_read(op->oid, op->completion, &op->bl, op->len, op->off); | |
740 | break; | |
741 | case OP_WRITE: | |
742 | bufferptr p = buffer::create(op->len); | |
743 | memset(p.c_str(), 0, op->len); | |
744 | op->bl.push_back(p); | |
745 | ||
746 | io_ctx.aio_write(op->oid, op->completion, op->bl, op->len, op->off); | |
747 | break; | |
748 | } | |
749 | ||
750 | total_sent += op->len; | |
751 | } | |
752 | ||
753 | void LoadGen::gen_op(LoadGenOp *op) | |
754 | { | |
755 | int i = get_random(0, objs.size() - 1); | |
756 | obj_info& info = objs[i]; | |
757 | op->oid = info.name; | |
758 | ||
759 | size_t len = get_random(min_op_len, max_op_len); | |
760 | if (len > info.len) | |
761 | len = info.len; | |
762 | size_t off = get_random(0, info.len); | |
763 | ||
764 | if (off + len > info.len) | |
765 | off = info.len - len; | |
766 | ||
767 | op->off = off; | |
768 | op->len = len; | |
769 | ||
770 | i = get_random(1, 100); | |
771 | if (i > read_percent) | |
772 | op->type = OP_WRITE; | |
773 | else | |
774 | op->type = OP_READ; | |
775 | ||
776 | cout << (op->type == OP_READ ? "READ" : "WRITE") << " : oid=" << op->oid << " off=" << op->off << " len=" << op->len << std::endl; | |
777 | } | |
778 | ||
779 | uint64_t LoadGen::gen_next_op() | |
780 | { | |
781 | lock.Lock(); | |
782 | ||
783 | LoadGenOp *op = new LoadGenOp(this); | |
784 | gen_op(op); | |
785 | op->id = max_op++; | |
786 | pending_ops[op->id] = op; | |
787 | ||
788 | lock.Unlock(); | |
789 | ||
790 | run_op(op); | |
791 | ||
792 | return op->len; | |
793 | } | |
794 | ||
795 | int LoadGen::run() | |
796 | { | |
797 | start_time = ceph_clock_now(); | |
798 | utime_t end_time = start_time; | |
799 | end_time += run_length; | |
800 | utime_t stamp_time = start_time; | |
801 | uint32_t total_sec = 0; | |
802 | ||
803 | while (1) { | |
804 | lock.Lock(); | |
805 | utime_t one_second(1, 0); | |
806 | cond.WaitInterval(lock, one_second); | |
807 | lock.Unlock(); | |
808 | utime_t now = ceph_clock_now(); | |
809 | ||
810 | if (now > end_time) | |
811 | break; | |
812 | ||
813 | uint64_t expected = total_expected(); | |
814 | lock.Lock(); | |
815 | uint64_t sent = total_sent; | |
816 | uint64_t completed = total_completed; | |
817 | lock.Unlock(); | |
818 | ||
819 | if (now - stamp_time >= utime_t(1, 0)) { | |
820 | double rate = (double)cur_completed_rate() / (1024 * 1024); | |
821 | ++total_sec; | |
822 | std::streamsize original_precision = cout.precision(); | |
823 | cout.precision(3); | |
824 | cout << setw(5) << total_sec << ": throughput=" << rate << "MB/sec" << " pending data=" << sent - completed << std::endl; | |
825 | cout.precision(original_precision); | |
826 | stamp_time = now; | |
827 | } | |
828 | ||
829 | while (sent < expected && | |
830 | sent - completed < max_backlog && | |
831 | pending_ops.size() < max_ops) { | |
832 | sent += gen_next_op(); | |
833 | } | |
834 | } | |
835 | ||
836 | // get a reference to all pending requests | |
837 | vector<librados::AioCompletion *> completions; | |
838 | lock.Lock(); | |
839 | going_down = true; | |
840 | map<int, LoadGenOp *>::iterator iter; | |
841 | for (iter = pending_ops.begin(); iter != pending_ops.end(); ++iter) { | |
842 | LoadGenOp *op = iter->second; | |
843 | completions.push_back(op->completion); | |
844 | } | |
845 | lock.Unlock(); | |
846 | ||
847 | cout << "waiting for all operations to complete" << std::endl; | |
848 | ||
849 | // now wait on all the pending requests | |
850 | for (vector<librados::AioCompletion *>::iterator citer = completions.begin(); citer != completions.end(); ++citer) { | |
851 | librados::AioCompletion *c = *citer; | |
852 | c->wait_for_complete(); | |
853 | c->release(); | |
854 | } | |
855 | ||
856 | return 0; | |
857 | } | |
858 | ||
859 | void LoadGen::cleanup() | |
860 | { | |
861 | cout << "cleaning up objects" << std::endl; | |
862 | map<int, obj_info>::iterator iter; | |
863 | for (iter = objs.begin(); iter != objs.end(); ++iter) { | |
864 | obj_info& info = iter->second; | |
865 | int ret = io_ctx.remove(info.name); | |
866 | if (ret < 0) | |
867 | cerr << "couldn't remove obj: " << info.name << " ret=" << ret << std::endl; | |
868 | } | |
869 | } | |
870 | ||
871 | enum OpWriteDest { | |
872 | OP_WRITE_DEST_OBJ = 2 << 0, | |
873 | OP_WRITE_DEST_OMAP = 2 << 1, | |
874 | OP_WRITE_DEST_XATTR = 2 << 2, | |
875 | }; | |
876 | ||
877 | class RadosBencher : public ObjBencher { | |
878 | librados::AioCompletion **completions; | |
879 | librados::Rados& rados; | |
880 | librados::IoCtx& io_ctx; | |
881 | librados::NObjectIterator oi; | |
882 | bool iterator_valid; | |
883 | OpWriteDest write_destination; | |
884 | ||
885 | protected: | |
886 | int completions_init(int concurrentios) override { | |
887 | completions = new librados::AioCompletion *[concurrentios]; | |
888 | return 0; | |
889 | } | |
890 | void completions_done() override { | |
891 | delete[] completions; | |
892 | completions = NULL; | |
893 | } | |
894 | int create_completion(int slot, void (*cb)(void *, void*), void *arg) override { | |
895 | completions[slot] = rados.aio_create_completion((void *) arg, 0, cb); | |
896 | ||
897 | if (!completions[slot]) | |
898 | return -EINVAL; | |
899 | ||
900 | return 0; | |
901 | } | |
902 | void release_completion(int slot) override { | |
903 | completions[slot]->release(); | |
904 | completions[slot] = 0; | |
905 | } | |
906 | ||
907 | int aio_read(const std::string& oid, int slot, bufferlist *pbl, size_t len, | |
908 | size_t offset) override { | |
909 | return io_ctx.aio_read(oid, completions[slot], pbl, len, 0); | |
910 | } | |
911 | ||
912 | int aio_write(const std::string& oid, int slot, bufferlist& bl, size_t len, | |
913 | size_t offset) override { | |
914 | librados::ObjectWriteOperation op; | |
915 | ||
916 | if (write_destination & OP_WRITE_DEST_OBJ) { | |
917 | if (data.hints) | |
918 | op.set_alloc_hint2(data.object_size, data.op_size, | |
919 | ALLOC_HINT_FLAG_SEQUENTIAL_WRITE | | |
920 | ALLOC_HINT_FLAG_SEQUENTIAL_READ | | |
921 | ALLOC_HINT_FLAG_APPEND_ONLY | | |
922 | ALLOC_HINT_FLAG_IMMUTABLE); | |
923 | op.write(offset, bl); | |
924 | } | |
925 | ||
926 | if (write_destination & OP_WRITE_DEST_OMAP) { | |
927 | std::map<std::string, librados::bufferlist> omap; | |
928 | omap[string("bench-omap-key-") + stringify(offset)] = bl; | |
929 | op.omap_set(omap); | |
930 | } | |
931 | ||
932 | if (write_destination & OP_WRITE_DEST_XATTR) { | |
933 | char key[80]; | |
934 | snprintf(key, sizeof(key), "bench-xattr-key-%d", (int)offset); | |
935 | op.setxattr(key, bl); | |
936 | } | |
937 | ||
938 | return io_ctx.aio_operate(oid, completions[slot], &op); | |
939 | } | |
940 | ||
941 | int aio_remove(const std::string& oid, int slot) override { | |
942 | return io_ctx.aio_remove(oid, completions[slot]); | |
943 | } | |
944 | ||
945 | int sync_read(const std::string& oid, bufferlist& bl, size_t len) override { | |
946 | return io_ctx.read(oid, bl, len, 0); | |
947 | } | |
948 | int sync_write(const std::string& oid, bufferlist& bl, size_t len) override { | |
949 | return io_ctx.write_full(oid, bl); | |
950 | } | |
951 | ||
952 | int sync_remove(const std::string& oid) override { | |
953 | return io_ctx.remove(oid); | |
954 | } | |
955 | ||
956 | bool completion_is_done(int slot) override { | |
957 | return completions[slot]->is_safe(); | |
958 | } | |
959 | ||
960 | int completion_wait(int slot) override { | |
961 | return completions[slot]->wait_for_safe_and_cb(); | |
962 | } | |
963 | int completion_ret(int slot) override { | |
964 | return completions[slot]->get_return_value(); | |
965 | } | |
966 | ||
967 | bool get_objects(std::list<Object>* objects, int num) override { | |
968 | int count = 0; | |
969 | ||
970 | if (!iterator_valid) { | |
971 | oi = io_ctx.nobjects_begin(); | |
972 | iterator_valid = true; | |
973 | } | |
974 | ||
975 | librados::NObjectIterator ei = io_ctx.nobjects_end(); | |
976 | ||
977 | if (oi == ei) { | |
978 | iterator_valid = false; | |
979 | return false; | |
980 | } | |
981 | ||
982 | objects->clear(); | |
983 | for ( ; oi != ei && count < num; ++oi) { | |
984 | Object obj(oi->get_oid(), oi->get_nspace()); | |
985 | objects->push_back(obj); | |
986 | ++count; | |
987 | } | |
988 | ||
989 | return true; | |
990 | } | |
991 | ||
992 | void set_namespace( const std::string& ns) override { | |
993 | io_ctx.set_namespace(ns); | |
994 | } | |
995 | ||
996 | public: | |
997 | RadosBencher(CephContext *cct_, librados::Rados& _r, librados::IoCtx& _i) | |
998 | : ObjBencher(cct_), completions(NULL), rados(_r), io_ctx(_i), iterator_valid(false), write_destination(OP_WRITE_DEST_OBJ) {} | |
999 | ~RadosBencher() override { } | |
1000 | ||
1001 | void set_write_destination(OpWriteDest dest) { | |
1002 | write_destination = dest; | |
1003 | } | |
1004 | }; | |
1005 | ||
1006 | static int do_lock_cmd(std::vector<const char*> &nargs, | |
1007 | const std::map < std::string, std::string > &opts, | |
1008 | IoCtx *ioctx, | |
1009 | Formatter *formatter) | |
1010 | { | |
1011 | if (nargs.size() < 3) | |
1012 | usage_exit(); | |
1013 | ||
1014 | string cmd(nargs[1]); | |
1015 | string oid(nargs[2]); | |
1016 | ||
1017 | string lock_tag; | |
1018 | string lock_cookie; | |
1019 | string lock_description; | |
1020 | int lock_duration = 0; | |
1021 | ClsLockType lock_type = LOCK_EXCLUSIVE; | |
1022 | ||
1023 | map<string, string>::const_iterator i; | |
1024 | i = opts.find("lock-tag"); | |
1025 | if (i != opts.end()) { | |
1026 | lock_tag = i->second; | |
1027 | } | |
1028 | i = opts.find("lock-cookie"); | |
1029 | if (i != opts.end()) { | |
1030 | lock_cookie = i->second; | |
1031 | } | |
1032 | i = opts.find("lock-description"); | |
1033 | if (i != opts.end()) { | |
1034 | lock_description = i->second; | |
1035 | } | |
1036 | i = opts.find("lock-duration"); | |
1037 | if (i != opts.end()) { | |
1038 | if (rados_sistrtoll(i, &lock_duration)) { | |
1039 | return -EINVAL; | |
1040 | } | |
1041 | } | |
1042 | i = opts.find("lock-type"); | |
1043 | if (i != opts.end()) { | |
1044 | const string& type_str = i->second; | |
1045 | if (type_str.compare("exclusive") == 0) { | |
1046 | lock_type = LOCK_EXCLUSIVE; | |
1047 | } else if (type_str.compare("shared") == 0) { | |
1048 | lock_type = LOCK_SHARED; | |
1049 | } else { | |
1050 | cerr << "unknown lock type was specified, aborting" << std::endl; | |
1051 | return -EINVAL; | |
1052 | } | |
1053 | } | |
1054 | ||
1055 | if (cmd.compare("list") == 0) { | |
1056 | list<string> locks; | |
1057 | int ret = rados::cls::lock::list_locks(ioctx, oid, &locks); | |
1058 | if (ret < 0) { | |
1059 | cerr << "ERROR: rados_list_locks(): " << cpp_strerror(ret) << std::endl; | |
1060 | return ret; | |
1061 | } | |
1062 | ||
1063 | formatter->open_object_section("object"); | |
1064 | formatter->dump_string("objname", oid); | |
1065 | formatter->open_array_section("locks"); | |
1066 | list<string>::iterator iter; | |
1067 | for (iter = locks.begin(); iter != locks.end(); ++iter) { | |
1068 | formatter->open_object_section("lock"); | |
1069 | formatter->dump_string("name", *iter); | |
1070 | formatter->close_section(); | |
1071 | } | |
1072 | formatter->close_section(); | |
1073 | formatter->close_section(); | |
1074 | formatter->flush(cout); | |
1075 | return 0; | |
1076 | } | |
1077 | ||
1078 | if (nargs.size() < 4) | |
1079 | usage_exit(); | |
1080 | ||
1081 | string lock_name(nargs[3]); | |
1082 | ||
1083 | if (cmd.compare("info") == 0) { | |
1084 | map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t> lockers; | |
1085 | ClsLockType type = LOCK_NONE; | |
1086 | string tag; | |
1087 | int ret = rados::cls::lock::get_lock_info(ioctx, oid, lock_name, &lockers, &type, &tag); | |
1088 | if (ret < 0) { | |
1089 | cerr << "ERROR: rados_lock_get_lock_info(): " << cpp_strerror(ret) << std::endl; | |
1090 | return ret; | |
1091 | } | |
1092 | ||
1093 | formatter->open_object_section("lock"); | |
1094 | formatter->dump_string("name", lock_name); | |
1095 | formatter->dump_string("type", cls_lock_type_str(type)); | |
1096 | formatter->dump_string("tag", tag); | |
1097 | formatter->open_array_section("lockers"); | |
1098 | map<rados::cls::lock::locker_id_t, rados::cls::lock::locker_info_t>::iterator iter; | |
1099 | for (iter = lockers.begin(); iter != lockers.end(); ++iter) { | |
1100 | const rados::cls::lock::locker_id_t& id = iter->first; | |
1101 | const rados::cls::lock::locker_info_t& info = iter->second; | |
1102 | formatter->open_object_section("locker"); | |
1103 | formatter->dump_stream("name") << id.locker; | |
1104 | formatter->dump_string("cookie", id.cookie); | |
1105 | formatter->dump_string("description", info.description); | |
1106 | formatter->dump_stream("expiration") << info.expiration; | |
1107 | formatter->dump_stream("addr") << info.addr; | |
1108 | formatter->close_section(); | |
1109 | } | |
1110 | formatter->close_section(); | |
1111 | formatter->close_section(); | |
1112 | formatter->flush(cout); | |
1113 | ||
1114 | return ret; | |
1115 | } else if (cmd.compare("get") == 0) { | |
1116 | rados::cls::lock::Lock l(lock_name); | |
1117 | l.set_cookie(lock_cookie); | |
1118 | l.set_tag(lock_tag); | |
1119 | l.set_duration(utime_t(lock_duration, 0)); | |
1120 | l.set_description(lock_description); | |
1121 | int ret; | |
1122 | switch (lock_type) { | |
1123 | case LOCK_SHARED: | |
1124 | ret = l.lock_shared(ioctx, oid); | |
1125 | break; | |
1126 | default: | |
1127 | ret = l.lock_exclusive(ioctx, oid); | |
1128 | } | |
1129 | if (ret < 0) { | |
1130 | cerr << "ERROR: failed locking: " << cpp_strerror(ret) << std::endl; | |
1131 | return ret; | |
1132 | } | |
1133 | ||
1134 | return ret; | |
1135 | } | |
1136 | ||
1137 | if (nargs.size() < 5) | |
1138 | usage_exit(); | |
1139 | ||
1140 | if (cmd.compare("break") == 0) { | |
1141 | string locker(nargs[4]); | |
1142 | rados::cls::lock::Lock l(lock_name); | |
1143 | l.set_cookie(lock_cookie); | |
1144 | l.set_tag(lock_tag); | |
1145 | entity_name_t name; | |
1146 | if (!name.parse(locker)) { | |
1147 | cerr << "ERROR: failed to parse locker name (" << locker << ")" << std::endl; | |
1148 | return -EINVAL; | |
1149 | } | |
1150 | int ret = l.break_lock(ioctx, oid, name); | |
1151 | if (ret < 0) { | |
1152 | cerr << "ERROR: failed breaking lock: " << cpp_strerror(ret) << std::endl; | |
1153 | return ret; | |
1154 | } | |
1155 | } else { | |
1156 | usage_exit(); | |
1157 | } | |
1158 | ||
1159 | return 0; | |
1160 | } | |
1161 | ||
1162 | static int do_cache_flush(IoCtx& io_ctx, string oid) | |
1163 | { | |
1164 | ObjectReadOperation op; | |
1165 | op.cache_flush(); | |
1166 | librados::AioCompletion *completion = | |
1167 | librados::Rados::aio_create_completion(); | |
1168 | io_ctx.aio_operate(oid.c_str(), completion, &op, | |
1169 | librados::OPERATION_IGNORE_CACHE | | |
1170 | librados::OPERATION_IGNORE_OVERLAY, | |
1171 | NULL); | |
1172 | completion->wait_for_safe(); | |
1173 | int r = completion->get_return_value(); | |
1174 | completion->release(); | |
1175 | return r; | |
1176 | } | |
1177 | ||
1178 | static int do_cache_try_flush(IoCtx& io_ctx, string oid) | |
1179 | { | |
1180 | ObjectReadOperation op; | |
1181 | op.cache_try_flush(); | |
1182 | librados::AioCompletion *completion = | |
1183 | librados::Rados::aio_create_completion(); | |
1184 | io_ctx.aio_operate(oid.c_str(), completion, &op, | |
1185 | librados::OPERATION_IGNORE_CACHE | | |
1186 | librados::OPERATION_IGNORE_OVERLAY | | |
1187 | librados::OPERATION_SKIPRWLOCKS, | |
1188 | NULL); | |
1189 | completion->wait_for_safe(); | |
1190 | int r = completion->get_return_value(); | |
1191 | completion->release(); | |
1192 | return r; | |
1193 | } | |
1194 | ||
1195 | static int do_cache_evict(IoCtx& io_ctx, string oid) | |
1196 | { | |
1197 | ObjectReadOperation op; | |
1198 | op.cache_evict(); | |
1199 | librados::AioCompletion *completion = | |
1200 | librados::Rados::aio_create_completion(); | |
1201 | io_ctx.aio_operate(oid.c_str(), completion, &op, | |
1202 | librados::OPERATION_IGNORE_CACHE | | |
1203 | librados::OPERATION_IGNORE_OVERLAY | | |
1204 | librados::OPERATION_SKIPRWLOCKS, | |
1205 | NULL); | |
1206 | completion->wait_for_safe(); | |
1207 | int r = completion->get_return_value(); | |
1208 | completion->release(); | |
1209 | return r; | |
1210 | } | |
1211 | ||
1212 | static int do_cache_flush_evict_all(IoCtx& io_ctx, bool blocking) | |
1213 | { | |
1214 | int errors = 0; | |
1215 | io_ctx.set_namespace(all_nspaces); | |
1216 | try { | |
1217 | librados::NObjectIterator i = io_ctx.nobjects_begin(); | |
1218 | librados::NObjectIterator i_end = io_ctx.nobjects_end(); | |
1219 | for (; i != i_end; ++i) { | |
1220 | int r; | |
1221 | cout << i->get_nspace() << "\t" << i->get_oid() << "\t" << i->get_locator() << std::endl; | |
1222 | if (i->get_locator().size()) { | |
1223 | io_ctx.locator_set_key(i->get_locator()); | |
1224 | } else { | |
1225 | io_ctx.locator_set_key(string()); | |
1226 | } | |
1227 | io_ctx.set_namespace(i->get_nspace()); | |
1228 | snap_set_t ls; | |
1229 | io_ctx.snap_set_read(LIBRADOS_SNAP_DIR); | |
1230 | r = io_ctx.list_snaps(i->get_oid(), &ls); | |
1231 | if (r < 0) { | |
1232 | cerr << "error listing snap shots " << i->get_nspace() << "/" << i->get_oid() << ": " | |
1233 | << cpp_strerror(r) << std::endl; | |
1234 | ++errors; | |
1235 | continue; | |
1236 | } | |
1237 | std::vector<clone_info_t>::iterator ci = ls.clones.begin(); | |
1238 | // no snapshots | |
1239 | if (ci == ls.clones.end()) { | |
1240 | io_ctx.snap_set_read(CEPH_NOSNAP); | |
1241 | if (blocking) | |
1242 | r = do_cache_flush(io_ctx, i->get_oid()); | |
1243 | else | |
1244 | r = do_cache_try_flush(io_ctx, i->get_oid()); | |
1245 | if (r < 0) { | |
1246 | cerr << "failed to flush " << i->get_nspace() << "/" << i->get_oid() << ": " | |
1247 | << cpp_strerror(r) << std::endl; | |
1248 | ++errors; | |
1249 | continue; | |
1250 | } | |
1251 | r = do_cache_evict(io_ctx, i->get_oid()); | |
1252 | if (r < 0) { | |
1253 | cerr << "failed to evict " << i->get_nspace() << "/" << i->get_oid() << ": " | |
1254 | << cpp_strerror(r) << std::endl; | |
1255 | ++errors; | |
1256 | continue; | |
1257 | } | |
1258 | } else { | |
1259 | // has snapshots | |
1260 | for (std::vector<clone_info_t>::iterator ci = ls.clones.begin(); | |
1261 | ci != ls.clones.end(); ++ci) { | |
1262 | io_ctx.snap_set_read(ci->cloneid); | |
1263 | if (blocking) | |
1264 | r = do_cache_flush(io_ctx, i->get_oid()); | |
1265 | else | |
1266 | r = do_cache_try_flush(io_ctx, i->get_oid()); | |
1267 | if (r < 0) { | |
1268 | cerr << "failed to flush " << i->get_nspace() << "/" << i->get_oid() << ": " | |
1269 | << cpp_strerror(r) << std::endl; | |
1270 | ++errors; | |
1271 | break; | |
1272 | } | |
1273 | r = do_cache_evict(io_ctx, i->get_oid()); | |
1274 | if (r < 0) { | |
1275 | cerr << "failed to evict " << i->get_nspace() << "/" << i->get_oid() << ": " | |
1276 | << cpp_strerror(r) << std::endl; | |
1277 | ++errors; | |
1278 | break; | |
1279 | } | |
1280 | } | |
1281 | } | |
1282 | } | |
1283 | } | |
1284 | catch (const std::runtime_error& e) { | |
1285 | cerr << e.what() << std::endl; | |
1286 | return -1; | |
1287 | } | |
1288 | return errors ? -1 : 0; | |
1289 | } | |
1290 | ||
1291 | static int do_get_inconsistent_pg_cmd(const std::vector<const char*> &nargs, | |
1292 | Rados& rados, | |
1293 | Formatter& formatter) | |
1294 | { | |
1295 | if (nargs.size() < 2) { | |
1296 | usage_exit(); | |
1297 | } | |
1298 | int64_t pool_id = rados.pool_lookup(nargs[1]); | |
1299 | if (pool_id < 0) { | |
1300 | cerr << "pool \"" << nargs[1] << "\" not found" << std::endl; | |
1301 | return (int)pool_id; | |
1302 | } | |
1303 | std::vector<PlacementGroup> pgs; | |
1304 | int ret = rados.get_inconsistent_pgs(pool_id, &pgs); | |
1305 | if (ret) { | |
1306 | return ret; | |
1307 | } | |
1308 | formatter.open_array_section("pgs"); | |
1309 | for (auto& pg : pgs) { | |
1310 | formatter.dump_stream("pg") << pg; | |
1311 | } | |
1312 | formatter.close_section(); | |
1313 | formatter.flush(cout); | |
1314 | cout << std::endl; | |
1315 | return 0; | |
1316 | } | |
1317 | ||
1318 | static void dump_errors(const err_t &err, Formatter &f, const char *name) | |
1319 | { | |
1320 | f.open_array_section(name); | |
1321 | if (err.has_shard_missing()) | |
1322 | f.dump_string("error", "missing"); | |
1323 | if (err.has_stat_error()) | |
1324 | f.dump_string("error", "stat_error"); | |
1325 | if (err.has_read_error()) | |
1326 | f.dump_string("error", "read_error"); | |
1327 | if (err.has_data_digest_mismatch_oi()) | |
1328 | f.dump_string("error", "data_digest_mismatch_oi"); | |
1329 | if (err.has_omap_digest_mismatch_oi()) | |
1330 | f.dump_string("error", "omap_digest_mismatch_oi"); | |
1331 | if (err.has_size_mismatch_oi()) | |
1332 | f.dump_string("error", "size_mismatch_oi"); | |
1333 | if (err.has_ec_hash_error()) | |
1334 | f.dump_string("error", "ec_hash_error"); | |
1335 | if (err.has_ec_size_error()) | |
1336 | f.dump_string("error", "ec_size_error"); | |
1337 | if (err.has_oi_attr_missing()) | |
1338 | f.dump_string("error", "oi_attr_missing"); | |
1339 | if (err.has_oi_attr_corrupted()) | |
1340 | f.dump_string("error", "oi_attr_corrupted"); | |
1341 | f.close_section(); | |
1342 | } | |
1343 | ||
1344 | static void dump_shard(const shard_info_t& shard, | |
1345 | const inconsistent_obj_t& inc, | |
1346 | Formatter &f) | |
1347 | { | |
1348 | dump_errors(shard, f, "errors"); | |
1349 | ||
1350 | if (shard.has_shard_missing()) | |
1351 | return; | |
1352 | ||
1353 | if (!shard.has_stat_error()) | |
1354 | f.dump_unsigned("size", shard.size); | |
1355 | if (shard.omap_digest_present) { | |
1356 | f.dump_format("omap_digest", "0x%08x", shard.omap_digest); | |
1357 | } | |
1358 | if (shard.data_digest_present) { | |
1359 | f.dump_format("data_digest", "0x%08x", shard.data_digest); | |
1360 | } | |
1361 | ||
1362 | if (!shard.has_oi_attr_missing() && !shard.has_oi_attr_corrupted() && | |
1363 | inc.has_object_info_inconsistency()) { | |
1364 | object_info_t oi; | |
1365 | bufferlist bl; | |
1366 | map<std::string, ceph::bufferlist>::iterator k = (const_cast<shard_info_t&>(shard)).attrs.find(OI_ATTR); | |
1367 | assert(k != shard.attrs.end()); // Can't be missing | |
1368 | bufferlist::iterator bliter = k->second.begin(); | |
1369 | ::decode(oi, bliter); // Can't be corrupted | |
1370 | f.dump_stream("object_info") << oi; | |
1371 | } | |
1372 | if (inc.has_attr_name_mismatch() || inc.has_attr_value_mismatch()) { | |
1373 | f.open_array_section("attrs"); | |
1374 | for (auto kv : shard.attrs) { | |
1375 | f.open_object_section("attr"); | |
1376 | f.dump_string("name", kv.first); | |
1377 | bool b64; | |
1378 | f.dump_string("value", cleanbin(kv.second, b64)); | |
1379 | f.dump_bool("Base64", b64); | |
1380 | f.close_section(); | |
1381 | } | |
1382 | f.close_section(); | |
1383 | } | |
1384 | } | |
1385 | ||
1386 | static void dump_obj_errors(const obj_err_t &err, Formatter &f) | |
1387 | { | |
1388 | f.open_array_section("errors"); | |
1389 | if (err.has_object_info_inconsistency()) | |
1390 | f.dump_string("error", "object_info_inconsistency"); | |
1391 | if (err.has_data_digest_mismatch()) | |
1392 | f.dump_string("error", "data_digest_mismatch"); | |
1393 | if (err.has_omap_digest_mismatch()) | |
1394 | f.dump_string("error", "omap_digest_mismatch"); | |
1395 | if (err.has_size_mismatch()) | |
1396 | f.dump_string("error", "size_mismatch"); | |
1397 | if (err.has_attr_value_mismatch()) | |
1398 | f.dump_string("error", "attr_value_mismatch"); | |
1399 | if (err.has_attr_name_mismatch()) | |
1400 | f.dump_string("error", "attr_name_mismatch"); | |
1401 | f.close_section(); | |
1402 | } | |
1403 | ||
1404 | static void dump_object_id(const object_id_t& object, | |
1405 | Formatter &f) | |
1406 | { | |
1407 | f.dump_string("name", object.name); | |
1408 | f.dump_string("nspace", object.nspace); | |
1409 | f.dump_string("locator", object.locator); | |
1410 | switch (object.snap) { | |
1411 | case CEPH_NOSNAP: | |
1412 | f.dump_string("snap", "head"); | |
1413 | break; | |
1414 | case CEPH_SNAPDIR: | |
1415 | f.dump_string("snap", "snapdir"); | |
1416 | break; | |
1417 | default: | |
1418 | f.dump_unsigned("snap", object.snap); | |
1419 | break; | |
1420 | } | |
1421 | } | |
1422 | ||
1423 | static void dump_inconsistent(const inconsistent_obj_t& inc, | |
1424 | Formatter &f) | |
1425 | { | |
1426 | f.open_object_section("object"); | |
1427 | dump_object_id(inc.object, f); | |
1428 | f.dump_unsigned("version", inc.version); | |
1429 | f.close_section(); | |
1430 | ||
1431 | dump_obj_errors(inc, f); | |
1432 | dump_errors(inc.union_shards, f, "union_shard_errors"); | |
1433 | for (const auto& shard_info : inc.shards) { | |
1434 | shard_info_t shard = const_cast<shard_info_t&>(shard_info.second); | |
1435 | if (shard.selected_oi) { | |
1436 | object_info_t oi; | |
1437 | bufferlist bl; | |
1438 | auto k = shard.attrs.find(OI_ATTR); | |
1439 | assert(k != shard.attrs.end()); // Can't be missing | |
1440 | bufferlist::iterator bliter = k->second.begin(); | |
1441 | ::decode(oi, bliter); // Can't be corrupted | |
1442 | f.dump_stream("selected_object_info") << oi; | |
1443 | break; | |
1444 | } | |
1445 | } | |
1446 | f.open_array_section("shards"); | |
1447 | for (const auto& shard_info : inc.shards) { | |
1448 | f.open_object_section("shard"); | |
1449 | auto& osd_shard = shard_info.first; | |
1450 | f.dump_int("osd", osd_shard.osd); | |
1451 | auto shard = osd_shard.shard; | |
1452 | if (shard != shard_id_t::NO_SHARD) | |
1453 | f.dump_unsigned("shard", shard); | |
1454 | dump_shard(shard_info.second, inc, f); | |
1455 | f.close_section(); | |
1456 | } | |
1457 | f.close_section(); | |
1458 | } | |
1459 | ||
1460 | static void dump_inconsistent(const inconsistent_snapset_t& inc, | |
1461 | Formatter &f) | |
1462 | { | |
1463 | dump_object_id(inc.object, f); | |
1464 | ||
1465 | f.open_array_section("errors"); | |
1466 | if (inc.ss_attr_missing()) | |
1467 | f.dump_string("error", "ss_attr_missing"); | |
1468 | if (inc.ss_attr_corrupted()) | |
1469 | f.dump_string("error", "ss_attr_corrupted"); | |
1470 | if (inc.oi_attr_missing()) | |
1471 | f.dump_string("error", "oi_attr_missing"); | |
1472 | if (inc.oi_attr_corrupted()) | |
1473 | f.dump_string("error", "oi_attr_corrupted"); | |
1474 | if (inc.snapset_mismatch()) | |
1475 | f.dump_string("error", "snapset_mismatch"); | |
1476 | if (inc.head_mismatch()) | |
1477 | f.dump_string("error", "head_mismatch"); | |
1478 | if (inc.headless()) | |
1479 | f.dump_string("error", "headless"); | |
1480 | if (inc.size_mismatch()) | |
1481 | f.dump_string("error", "size_mismatch"); | |
1482 | if (inc.extra_clones()) | |
1483 | f.dump_string("error", "extra_clones"); | |
1484 | if (inc.clone_missing()) | |
1485 | f.dump_string("error", "clone_missing"); | |
1486 | f.close_section(); | |
1487 | ||
1488 | if (inc.extra_clones()) { | |
1489 | f.open_array_section("extra clones"); | |
1490 | for (auto snap : inc.clones) { | |
1491 | f.dump_unsigned("snap", snap); | |
1492 | } | |
1493 | f.close_section(); | |
1494 | } | |
1495 | ||
1496 | if (inc.clone_missing()) { | |
1497 | f.open_array_section("missing"); | |
1498 | for (auto snap : inc.missing) { | |
1499 | f.dump_unsigned("snap", snap); | |
1500 | } | |
1501 | f.close_section(); | |
1502 | } | |
1503 | } | |
1504 | ||
1505 | // dispatch the call by type | |
1506 | static int do_get_inconsistent(Rados& rados, | |
1507 | const PlacementGroup& pg, | |
1508 | const librados::object_id_t &start, | |
1509 | unsigned max_return, | |
1510 | AioCompletion *c, | |
1511 | std::vector<inconsistent_obj_t>* objs, | |
1512 | uint32_t* interval) | |
1513 | { | |
1514 | return rados.get_inconsistent_objects(pg, start, max_return, c, | |
1515 | objs, interval); | |
1516 | } | |
1517 | ||
1518 | static int do_get_inconsistent(Rados& rados, | |
1519 | const PlacementGroup& pg, | |
1520 | const librados::object_id_t &start, | |
1521 | unsigned max_return, | |
1522 | AioCompletion *c, | |
1523 | std::vector<inconsistent_snapset_t>* snapsets, | |
1524 | uint32_t* interval) | |
1525 | { | |
1526 | return rados.get_inconsistent_snapsets(pg, start, max_return, c, | |
1527 | snapsets, interval); | |
1528 | } | |
1529 | ||
1530 | template <typename T> | |
1531 | static int do_get_inconsistent_cmd(const std::vector<const char*> &nargs, | |
1532 | Rados& rados, | |
1533 | Formatter& formatter) | |
1534 | { | |
1535 | if (nargs.size() < 2) { | |
1536 | usage_exit(); | |
1537 | } | |
1538 | PlacementGroup pg; | |
1539 | int ret = 0; | |
1540 | ret = pg.parse(nargs[1]); | |
1541 | if (!ret) { | |
1542 | cerr << "bad pg: " << nargs[1] << std::endl; | |
1543 | return ret; | |
1544 | } | |
1545 | uint32_t interval = 0, first_interval = 0; | |
1546 | const unsigned max_item_num = 32; | |
1547 | bool opened = false; | |
1548 | for (librados::object_id_t start;;) { | |
1549 | std::vector<T> items; | |
1550 | auto completion = librados::Rados::aio_create_completion(); | |
1551 | ret = do_get_inconsistent(rados, pg, start, max_item_num, completion, | |
1552 | &items, &interval); | |
1553 | completion->wait_for_safe(); | |
1554 | ret = completion->get_return_value(); | |
1555 | completion->release(); | |
1556 | if (ret < 0) { | |
1557 | if (ret == -EAGAIN) | |
1558 | cerr << "interval#" << interval << " expired." << std::endl; | |
1559 | else if (ret == -ENOENT) | |
1560 | cerr << "No scrub information available for pg " << pg << std::endl; | |
1561 | else | |
1562 | cerr << "Unknown error " << cpp_strerror(ret) << std::endl; | |
1563 | break; | |
1564 | } | |
1565 | // It must be the same interval every time. EAGAIN would | |
1566 | // occur if interval changes. | |
1567 | assert(start.name.empty() || first_interval == interval); | |
1568 | if (start.name.empty()) { | |
1569 | first_interval = interval; | |
1570 | formatter.open_object_section("info"); | |
1571 | formatter.dump_int("epoch", interval); | |
1572 | formatter.open_array_section("inconsistents"); | |
1573 | opened = true; | |
1574 | } | |
1575 | for (auto& inc : items) { | |
1576 | formatter.open_object_section("inconsistent"); | |
1577 | dump_inconsistent(inc, formatter); | |
1578 | formatter.close_section(); | |
1579 | } | |
1580 | if (items.size() < max_item_num) { | |
1581 | formatter.close_section(); | |
1582 | break; | |
1583 | } | |
1584 | if (!items.empty()) { | |
1585 | start = items.back().object; | |
1586 | } | |
1587 | items.clear(); | |
1588 | } | |
1589 | if (opened) { | |
1590 | formatter.close_section(); | |
1591 | formatter.flush(cout); | |
1592 | } | |
1593 | return ret; | |
1594 | } | |
1595 | ||
1596 | /********************************************** | |
1597 | ||
1598 | **********************************************/ | |
1599 | static int rados_tool_common(const std::map < std::string, std::string > &opts, | |
1600 | std::vector<const char*> &nargs) | |
1601 | { | |
1602 | int ret; | |
1603 | bool create_pool = false; | |
1604 | const char *pool_name = NULL; | |
1605 | const char *target_pool_name = NULL; | |
1606 | string oloc, target_oloc, nspace, target_nspace; | |
1607 | int concurrent_ios = 16; | |
1608 | unsigned op_size = default_op_size; | |
1609 | unsigned object_size = 0; | |
1610 | unsigned max_objects = 0; | |
1611 | uint64_t obj_offset = 0; | |
1612 | bool block_size_specified = false; | |
1613 | int bench_write_dest = 0; | |
1614 | bool cleanup = true; | |
1615 | bool hints = true; // for rados bench | |
1616 | bool no_verify = false; | |
1617 | bool use_striper = false; | |
1618 | bool with_clones = false; | |
1619 | const char *snapname = NULL; | |
1620 | snap_t snapid = CEPH_NOSNAP; | |
1621 | std::map<std::string, std::string>::const_iterator i; | |
1622 | ||
1623 | uint64_t min_obj_len = 0; | |
1624 | uint64_t max_obj_len = 0; | |
1625 | uint64_t min_op_len = 0; | |
1626 | uint64_t max_op_len = 0; | |
1627 | uint64_t max_ops = 0; | |
1628 | uint64_t max_backlog = 0; | |
1629 | uint64_t target_throughput = 0; | |
1630 | int64_t read_percent = -1; | |
1631 | uint64_t num_objs = 0; | |
1632 | int run_length = 0; | |
1633 | ||
1634 | bool show_time = false; | |
1635 | bool wildcard = false; | |
1636 | ||
1637 | std::string run_name; | |
1638 | std::string prefix; | |
1639 | bool forcefull = false; | |
1640 | Formatter *formatter = NULL; | |
1641 | bool pretty_format = false; | |
1642 | const char *output = NULL; | |
1643 | bool omap_key_valid = false; | |
1644 | std::string omap_key; | |
1645 | std::string omap_key_pretty; | |
1646 | ||
1647 | Rados rados; | |
1648 | IoCtx io_ctx; | |
1649 | RadosStriper striper; | |
1650 | ||
1651 | i = opts.find("create"); | |
1652 | if (i != opts.end()) { | |
1653 | create_pool = true; | |
1654 | } | |
1655 | i = opts.find("pool"); | |
1656 | if (i != opts.end()) { | |
1657 | pool_name = i->second.c_str(); | |
1658 | } | |
1659 | i = opts.find("target_pool"); | |
1660 | if (i != opts.end()) { | |
1661 | target_pool_name = i->second.c_str(); | |
1662 | } | |
1663 | i = opts.find("object_locator"); | |
1664 | if (i != opts.end()) { | |
1665 | oloc = i->second; | |
1666 | } | |
1667 | i = opts.find("target_locator"); | |
1668 | if (i != opts.end()) { | |
1669 | target_oloc = i->second; | |
1670 | } | |
1671 | i = opts.find("target_nspace"); | |
1672 | if (i != opts.end()) { | |
1673 | target_nspace = i->second; | |
1674 | } | |
1675 | i = opts.find("concurrent-ios"); | |
1676 | if (i != opts.end()) { | |
1677 | if (rados_sistrtoll(i, &concurrent_ios)) { | |
1678 | return -EINVAL; | |
1679 | } | |
1680 | } | |
1681 | i = opts.find("run-name"); | |
1682 | if (i != opts.end()) { | |
1683 | run_name = i->second; | |
1684 | } | |
1685 | ||
1686 | i = opts.find("force-full"); | |
1687 | if (i != opts.end()) { | |
1688 | forcefull = true; | |
1689 | } | |
1690 | i = opts.find("prefix"); | |
1691 | if (i != opts.end()) { | |
1692 | prefix = i->second; | |
1693 | } | |
1694 | i = opts.find("block-size"); | |
1695 | if (i != opts.end()) { | |
1696 | if (rados_sistrtoll(i, &op_size)) { | |
1697 | return -EINVAL; | |
1698 | } | |
1699 | block_size_specified = true; | |
1700 | } | |
1701 | i = opts.find("object-size"); | |
1702 | if (i != opts.end()) { | |
1703 | if (rados_sistrtoll(i, &object_size)) { | |
1704 | return -EINVAL; | |
1705 | } | |
1706 | block_size_specified = true; | |
1707 | } | |
1708 | i = opts.find("max-objects"); | |
1709 | if (i != opts.end()) { | |
1710 | if (rados_sistrtoll(i, &max_objects)) { | |
1711 | return -EINVAL; | |
1712 | } | |
1713 | } | |
1714 | i = opts.find("offset"); | |
1715 | if (i != opts.end()) { | |
1716 | if (rados_sistrtoll(i, &obj_offset)) { | |
1717 | return -EINVAL; | |
1718 | } | |
1719 | } | |
1720 | i = opts.find("snap"); | |
1721 | if (i != opts.end()) { | |
1722 | snapname = i->second.c_str(); | |
1723 | } | |
1724 | i = opts.find("snapid"); | |
1725 | if (i != opts.end()) { | |
1726 | if (rados_sistrtoll(i, &snapid)) { | |
1727 | return -EINVAL; | |
1728 | } | |
1729 | } | |
1730 | i = opts.find("min-object-size"); | |
1731 | if (i != opts.end()) { | |
1732 | if (rados_sistrtoll(i, &min_obj_len)) { | |
1733 | return -EINVAL; | |
1734 | } | |
1735 | } | |
1736 | i = opts.find("max-object-size"); | |
1737 | if (i != opts.end()) { | |
1738 | if (rados_sistrtoll(i, &max_obj_len)) { | |
1739 | return -EINVAL; | |
1740 | } | |
1741 | } | |
1742 | i = opts.find("min-op-len"); | |
1743 | if (i != opts.end()) { | |
1744 | if (rados_sistrtoll(i, &min_op_len)) { | |
1745 | return -EINVAL; | |
1746 | } | |
1747 | } | |
1748 | i = opts.find("max-op-len"); | |
1749 | if (i != opts.end()) { | |
1750 | if (rados_sistrtoll(i, &max_op_len)) { | |
1751 | return -EINVAL; | |
1752 | } | |
1753 | } | |
1754 | i = opts.find("max-ops"); | |
1755 | if (i != opts.end()) { | |
1756 | if (rados_sistrtoll(i, &max_ops)) { | |
1757 | return -EINVAL; | |
1758 | } | |
1759 | } | |
1760 | i = opts.find("max-backlog"); | |
1761 | if (i != opts.end()) { | |
1762 | if (rados_sistrtoll(i, &max_backlog)) { | |
1763 | return -EINVAL; | |
1764 | } | |
1765 | } | |
1766 | i = opts.find("target-throughput"); | |
1767 | if (i != opts.end()) { | |
1768 | if (rados_sistrtoll(i, &target_throughput)) { | |
1769 | return -EINVAL; | |
1770 | } | |
1771 | } | |
1772 | i = opts.find("read-percent"); | |
1773 | if (i != opts.end()) { | |
1774 | if (rados_sistrtoll(i, &read_percent)) { | |
1775 | return -EINVAL; | |
1776 | } | |
1777 | } | |
1778 | i = opts.find("num-objects"); | |
1779 | if (i != opts.end()) { | |
1780 | if (rados_sistrtoll(i, &num_objs)) { | |
1781 | return -EINVAL; | |
1782 | } | |
1783 | } | |
1784 | i = opts.find("run-length"); | |
1785 | if (i != opts.end()) { | |
1786 | if (rados_sistrtoll(i, &run_length)) { | |
1787 | return -EINVAL; | |
1788 | } | |
1789 | } | |
1790 | i = opts.find("show-time"); | |
1791 | if (i != opts.end()) { | |
1792 | show_time = true; | |
1793 | } | |
1794 | i = opts.find("no-cleanup"); | |
1795 | if (i != opts.end()) { | |
1796 | cleanup = false; | |
1797 | } | |
1798 | i = opts.find("no-hints"); | |
1799 | if (i != opts.end()) { | |
1800 | hints = false; | |
1801 | } | |
1802 | i = opts.find("pretty-format"); | |
1803 | if (i != opts.end()) { | |
1804 | pretty_format = true; | |
1805 | } | |
1806 | i = opts.find("format"); | |
1807 | if (i != opts.end()) { | |
1808 | const char *format = i->second.c_str(); | |
1809 | formatter = Formatter::create(format); | |
1810 | if (!formatter) { | |
1811 | cerr << "unrecognized format: " << format << std::endl; | |
1812 | return -EINVAL; | |
1813 | } | |
1814 | } | |
1815 | i = opts.find("namespace"); | |
1816 | if (i != opts.end()) { | |
1817 | nspace = i->second; | |
1818 | } | |
1819 | i = opts.find("no-verify"); | |
1820 | if (i != opts.end()) { | |
1821 | no_verify = true; | |
1822 | } | |
1823 | i = opts.find("output"); | |
1824 | if (i != opts.end()) { | |
1825 | output = i->second.c_str(); | |
1826 | } | |
1827 | i = opts.find("write-dest-obj"); | |
1828 | if (i != opts.end()) { | |
1829 | bench_write_dest |= static_cast<int>(OP_WRITE_DEST_OBJ); | |
1830 | } | |
1831 | i = opts.find("write-dest-omap"); | |
1832 | if (i != opts.end()) { | |
1833 | bench_write_dest |= static_cast<int>(OP_WRITE_DEST_OMAP); | |
1834 | } | |
1835 | i = opts.find("write-dest-xattr"); | |
1836 | if (i != opts.end()) { | |
1837 | bench_write_dest |= static_cast<int>(OP_WRITE_DEST_XATTR); | |
1838 | } | |
1839 | i = opts.find("with-clones"); | |
1840 | if (i != opts.end()) { | |
1841 | with_clones = true; | |
1842 | } | |
1843 | i = opts.find("omap-key-file"); | |
1844 | if (i != opts.end()) { | |
1845 | string err; | |
1846 | bufferlist indata; | |
1847 | ret = indata.read_file(i->second.c_str(), &err); | |
1848 | if (ret < 0) { | |
1849 | cerr << err << std::endl; | |
1850 | return 1; | |
1851 | } | |
1852 | ||
1853 | omap_key_valid = true; | |
1854 | omap_key = std::string(indata.c_str(), indata.length()); | |
1855 | omap_key_pretty = omap_key; | |
1856 | if (std::find_if_not(omap_key.begin(), omap_key.end(), | |
1857 | (int (*)(int))isprint) != omap_key.end()) { | |
1858 | omap_key_pretty = "(binary key)"; | |
1859 | } | |
1860 | } | |
1861 | ||
1862 | // open rados | |
1863 | ret = rados.init_with_context(g_ceph_context); | |
1864 | if (ret < 0) { | |
1865 | cerr << "couldn't initialize rados: " << cpp_strerror(ret) << std::endl; | |
1866 | goto out; | |
1867 | } | |
1868 | ||
1869 | ret = rados.connect(); | |
1870 | if (ret) { | |
1871 | cerr << "couldn't connect to cluster: " << cpp_strerror(ret) << std::endl; | |
1872 | ret = -1; | |
1873 | goto out; | |
1874 | } | |
1875 | ||
1876 | if (create_pool && !pool_name) { | |
1877 | cerr << "--create-pool requested but pool_name was not specified!" << std::endl; | |
1878 | usage_exit(); | |
1879 | } | |
1880 | ||
1881 | if (create_pool) { | |
1882 | ret = rados.pool_create(pool_name, 0, 0); | |
1883 | if (ret < 0) { | |
1884 | cerr << "error creating pool " << pool_name << ": " | |
1885 | << cpp_strerror(ret) << std::endl; | |
1886 | goto out; | |
1887 | } | |
1888 | } | |
1889 | ||
1890 | // open io context. | |
1891 | if (pool_name) { | |
1892 | ret = rados.ioctx_create(pool_name, io_ctx); | |
1893 | if (ret < 0) { | |
1894 | cerr << "error opening pool " << pool_name << ": " | |
1895 | << cpp_strerror(ret) << std::endl; | |
1896 | goto out; | |
1897 | } | |
1898 | ||
1899 | // align op_size | |
1900 | { | |
1901 | bool requires; | |
1902 | ret = io_ctx.pool_requires_alignment2(&requires); | |
1903 | if (ret < 0) { | |
1904 | cerr << "error checking pool alignment requirement" | |
1905 | << cpp_strerror(ret) << std::endl; | |
1906 | goto out; | |
1907 | } | |
1908 | ||
1909 | if (requires) { | |
1910 | uint64_t align = 0; | |
1911 | ret = io_ctx.pool_required_alignment2(&align); | |
1912 | if (ret < 0) { | |
1913 | cerr << "error getting pool alignment" | |
1914 | << cpp_strerror(ret) << std::endl; | |
1915 | goto out; | |
1916 | } | |
1917 | ||
1918 | const uint64_t prev_op_size = op_size; | |
1919 | op_size = uint64_t((op_size + align - 1) / align) * align; | |
1920 | // Warn: if user specified and it was rounded | |
1921 | if (prev_op_size != default_op_size && prev_op_size != op_size) | |
1922 | cerr << "INFO: op_size has been rounded to " << op_size << std::endl; | |
1923 | } | |
1924 | } | |
1925 | ||
1926 | // create striper interface | |
1927 | if (opts.find("striper") != opts.end()) { | |
1928 | ret = RadosStriper::striper_create(io_ctx, &striper); | |
1929 | if (0 != ret) { | |
1930 | cerr << "error opening pool " << pool_name << " with striper interface: " | |
1931 | << cpp_strerror(ret) << std::endl; | |
1932 | goto out; | |
1933 | } | |
1934 | use_striper = true; | |
1935 | } | |
1936 | } | |
1937 | ||
1938 | // snapname? | |
1939 | if (snapname) { | |
1940 | if (!pool_name) { | |
1941 | cerr << "pool name must be specified with --snap" << std::endl; | |
1942 | ret = -1; | |
1943 | goto out; | |
1944 | } | |
1945 | ret = io_ctx.snap_lookup(snapname, &snapid); | |
1946 | if (ret < 0) { | |
1947 | cerr << "error looking up snap '" << snapname << "': " << cpp_strerror(ret) << std::endl; | |
1948 | goto out; | |
1949 | } | |
1950 | } | |
1951 | if (oloc.size()) { | |
1952 | if (!pool_name) { | |
1953 | cerr << "pool name must be specified with --object_locator" << std::endl; | |
1954 | ret = -1; | |
1955 | goto out; | |
1956 | } | |
1957 | io_ctx.locator_set_key(oloc); | |
1958 | } | |
1959 | // Use namespace from command line if specified | |
1960 | if (opts.find("namespace") != opts.end()) { | |
1961 | if (!pool_name) { | |
1962 | cerr << "pool name must be specified with --namespace" << std::endl; | |
1963 | ret = -1; | |
1964 | goto out; | |
1965 | } | |
1966 | io_ctx.set_namespace(nspace); | |
1967 | // Use wildcard if --all specified and --default NOT specified | |
1968 | } else if (opts.find("all") != opts.end() && opts.find("default") == opts.end()) { | |
1969 | // Only the ls should ever set namespace to special value | |
1970 | wildcard = true; | |
1971 | } | |
1972 | if (snapid != CEPH_NOSNAP) { | |
1973 | if (!pool_name) { | |
1974 | cerr << "pool name must be specified with --snapid" << std::endl; | |
1975 | ret = -1; | |
1976 | goto out; | |
1977 | } | |
1978 | string name; | |
1979 | ret = io_ctx.snap_get_name(snapid, &name); | |
1980 | if (ret < 0) { | |
1981 | cerr << "snapid " << snapid << " doesn't exist in pool " | |
1982 | << io_ctx.get_pool_name() << std::endl; | |
1983 | goto out; | |
1984 | } | |
1985 | io_ctx.snap_set_read(snapid); | |
1986 | cout << "selected snap " << snapid << " '" << name << "'" << std::endl; | |
1987 | } | |
1988 | ||
1989 | assert(!nargs.empty()); | |
1990 | ||
1991 | // list pools? | |
1992 | if (strcmp(nargs[0], "lspools") == 0) { | |
1993 | list<string> vec; | |
1994 | ret = rados.pool_list(vec); | |
1995 | if (ret < 0) { | |
1996 | cerr << "error listing pools: " << cpp_strerror(ret) << std::endl; | |
1997 | goto out; | |
1998 | } | |
1999 | for (list<string>::iterator i = vec.begin(); i != vec.end(); ++i) | |
2000 | cout << *i << std::endl; | |
2001 | } | |
2002 | else if (strcmp(nargs[0], "df") == 0) { | |
2003 | // pools | |
2004 | list<string> vec; | |
2005 | ||
2006 | if (!pool_name) { | |
2007 | ret = rados.pool_list(vec); | |
2008 | if (ret < 0) { | |
2009 | cerr << "error listing pools: " << cpp_strerror(ret) << std::endl; | |
2010 | goto out; | |
2011 | } | |
2012 | } else { | |
2013 | vec.push_back(pool_name); | |
2014 | } | |
2015 | ||
2016 | map<string,librados::pool_stat_t> stats; | |
2017 | ret = rados.get_pool_stats(vec, stats); | |
2018 | if (ret < 0) { | |
2019 | cerr << "error fetching pool stats: " << cpp_strerror(ret) << std::endl; | |
2020 | goto out; | |
2021 | } | |
2022 | ||
2023 | TextTable tab; | |
2024 | ||
2025 | if (!formatter) { | |
2026 | tab.define_column("POOL_NAME", TextTable::LEFT, TextTable::LEFT); | |
2027 | tab.define_column("USED", TextTable::LEFT, TextTable::RIGHT); | |
2028 | tab.define_column("OBJECTS", TextTable::LEFT, TextTable::RIGHT); | |
2029 | tab.define_column("CLONES", TextTable::LEFT, TextTable::RIGHT); | |
2030 | tab.define_column("COPIES", TextTable::LEFT, TextTable::RIGHT); | |
2031 | tab.define_column("MISSING_ON_PRIMARY", TextTable::LEFT, TextTable::RIGHT); | |
2032 | tab.define_column("UNFOUND", TextTable::LEFT, TextTable::RIGHT); | |
31f18b77 | 2033 | tab.define_column("DEGRADED", TextTable::LEFT, TextTable::RIGHT); |
7c673cae FG |
2034 | tab.define_column("RD_OPS", TextTable::LEFT, TextTable::RIGHT); |
2035 | tab.define_column("RD", TextTable::LEFT, TextTable::RIGHT); | |
2036 | tab.define_column("WR_OPS", TextTable::LEFT, TextTable::RIGHT); | |
2037 | tab.define_column("WR", TextTable::LEFT, TextTable::RIGHT); | |
2038 | } else { | |
2039 | formatter->open_object_section("stats"); | |
2040 | formatter->open_array_section("pools"); | |
2041 | } | |
2042 | for (map<string,librados::pool_stat_t>::iterator i = stats.begin(); | |
2043 | i != stats.end(); | |
2044 | ++i) { | |
2045 | const char *pool_name = i->first.c_str(); | |
2046 | librados::pool_stat_t& s = i->second; | |
2047 | if (!formatter) { | |
2048 | tab << pool_name | |
2049 | << si_t(s.num_bytes) | |
2050 | << s.num_objects | |
2051 | << s.num_object_clones | |
2052 | << s.num_object_copies | |
2053 | << s.num_objects_missing_on_primary | |
2054 | << s.num_objects_unfound | |
2055 | << s.num_objects_degraded | |
2056 | << s.num_rd | |
2057 | << si_t(s.num_rd_kb << 10) | |
2058 | << s.num_wr | |
2059 | << si_t(s.num_wr_kb << 10) | |
2060 | << TextTable::endrow; | |
2061 | } else { | |
2062 | formatter->open_object_section("pool"); | |
2063 | int64_t pool_id = rados.pool_lookup(pool_name); | |
2064 | formatter->dump_string("name", pool_name); | |
2065 | if (pool_id >= 0) | |
2066 | formatter->dump_int("id", pool_id); | |
2067 | else | |
2068 | cerr << "ERROR: lookup_pg_pool_name for name=" << pool_name | |
2069 | << " returned " << pool_id << std::endl; | |
2070 | formatter->dump_int("size_bytes",s.num_bytes); | |
2071 | formatter->dump_int("size_kb", s.num_kb); | |
2072 | formatter->dump_int("num_objects", s.num_objects); | |
2073 | formatter->dump_int("num_object_clones", s.num_object_clones); | |
2074 | formatter->dump_int("num_object_copies", s.num_object_copies); | |
2075 | formatter->dump_int("num_objects_missing_on_primary", s.num_objects_missing_on_primary); | |
2076 | formatter->dump_int("num_objects_unfound", s.num_objects_unfound); | |
2077 | formatter->dump_int("num_objects_degraded", s.num_objects_degraded); | |
2078 | formatter->dump_int("read_ops", s.num_rd); | |
2079 | formatter->dump_int("read_bytes", s.num_rd_kb * 1024ull); | |
2080 | formatter->dump_int("write_ops", s.num_wr); | |
2081 | formatter->dump_int("write_bytes", s.num_wr_kb * 1024ull); | |
2082 | formatter->close_section(); | |
2083 | } | |
2084 | } | |
2085 | ||
2086 | if (!formatter) { | |
2087 | cout << tab; | |
2088 | } | |
2089 | ||
2090 | // total | |
2091 | cluster_stat_t tstats; | |
2092 | ret = rados.cluster_stat(tstats); | |
2093 | if (ret < 0) { | |
2094 | cerr << "error getting total cluster usage: " << cpp_strerror(ret) << std::endl; | |
2095 | goto out; | |
2096 | } | |
2097 | if (!formatter) { | |
2098 | cout << std::endl; | |
2099 | cout << "total_objects " << tstats.num_objects | |
2100 | << std::endl; | |
2101 | cout << "total_used " << si_t(tstats.kb_used << 10) | |
2102 | << std::endl; | |
2103 | cout << "total_avail " << si_t(tstats.kb_avail << 10) | |
2104 | << std::endl; | |
2105 | cout << "total_space " << si_t(tstats.kb << 10) | |
2106 | << std::endl; | |
2107 | } else { | |
2108 | formatter->close_section(); | |
2109 | formatter->dump_int("total_objects", tstats.num_objects); | |
2110 | formatter->dump_int("total_used", tstats.kb_used); | |
2111 | formatter->dump_int("total_avail", tstats.kb_avail); | |
2112 | formatter->dump_int("total_space", tstats.kb); | |
2113 | formatter->close_section(); | |
2114 | formatter->flush(cout); | |
2115 | } | |
2116 | } | |
2117 | ||
2118 | else if (strcmp(nargs[0], "ls") == 0) { | |
2119 | if (!pool_name) { | |
2120 | cerr << "pool name was not specified" << std::endl; | |
2121 | ret = -1; | |
2122 | goto out; | |
2123 | } | |
2124 | ||
2125 | if (wildcard) | |
2126 | io_ctx.set_namespace(all_nspaces); | |
2127 | bool use_stdout = (nargs.size() < 2) || (strcmp(nargs[1], "-") == 0); | |
2128 | ostream *outstream; | |
2129 | if(use_stdout) | |
2130 | outstream = &cout; | |
2131 | else | |
2132 | outstream = new ofstream(nargs[1]); | |
2133 | ||
2134 | { | |
2135 | if (formatter) | |
2136 | formatter->open_array_section("objects"); | |
2137 | try { | |
2138 | librados::NObjectIterator i = io_ctx.nobjects_begin(); | |
2139 | librados::NObjectIterator i_end = io_ctx.nobjects_end(); | |
2140 | for (; i != i_end; ++i) { | |
2141 | if (use_striper) { | |
2142 | // in case of --striper option, we only list striped | |
2143 | // objects, so we only display the first object of | |
2144 | // each, without its suffix '.000...000' | |
2145 | size_t l = i->get_oid().length(); | |
2146 | if (l <= 17 || | |
2147 | (0 != i->get_oid().compare(l-17, 17,".0000000000000000"))) continue; | |
2148 | } | |
2149 | if (!formatter) { | |
2150 | // Only include namespace in output when wildcard specified | |
2151 | if (wildcard) | |
2152 | *outstream << i->get_nspace() << "\t"; | |
2153 | if (use_striper) { | |
2154 | *outstream << i->get_oid().substr(0, i->get_oid().length()-17); | |
2155 | } else { | |
2156 | *outstream << i->get_oid(); | |
2157 | } | |
2158 | if (i->get_locator().size()) | |
2159 | *outstream << "\t" << i->get_locator(); | |
2160 | *outstream << std::endl; | |
2161 | } else { | |
2162 | formatter->open_object_section("object"); | |
2163 | formatter->dump_string("namespace", i->get_nspace()); | |
2164 | if (use_striper) { | |
2165 | formatter->dump_string("name", i->get_oid().substr(0, i->get_oid().length()-17)); | |
2166 | } else { | |
2167 | formatter->dump_string("name", i->get_oid()); | |
2168 | } | |
2169 | if (i->get_locator().size()) | |
2170 | formatter->dump_string("locator", i->get_locator()); | |
2171 | formatter->close_section(); //object | |
2172 | } | |
2173 | } | |
2174 | } | |
2175 | catch (const std::runtime_error& e) { | |
2176 | cerr << e.what() << std::endl; | |
2177 | ret = -1; | |
2178 | goto out; | |
2179 | } | |
2180 | } | |
2181 | if (formatter) { | |
2182 | formatter->close_section(); //objects | |
2183 | formatter->flush(*outstream); | |
2184 | if (pretty_format) | |
2185 | *outstream << std::endl; | |
2186 | formatter->flush(*outstream); | |
2187 | } | |
2188 | if (!stdout) | |
2189 | delete outstream; | |
2190 | } | |
2191 | else if (strcmp(nargs[0], "chown") == 0) { | |
2192 | if (!pool_name || nargs.size() < 2) | |
2193 | usage_exit(); | |
2194 | ||
2195 | char* endptr = NULL; | |
2196 | uint64_t new_auid = strtol(nargs[1], &endptr, 10); | |
2197 | if (*endptr) { | |
2198 | cerr << "Invalid value for new-auid: '" << nargs[1] << "'" << std::endl; | |
2199 | ret = -1; | |
2200 | goto out; | |
2201 | } | |
2202 | ret = io_ctx.set_auid(new_auid); | |
2203 | if (ret < 0) { | |
2204 | cerr << "error changing auid on pool " << io_ctx.get_pool_name() << ':' | |
2205 | << cpp_strerror(ret) << std::endl; | |
2206 | } else cerr << "changed auid on pool " << io_ctx.get_pool_name() | |
2207 | << " to " << new_auid << std::endl; | |
2208 | } | |
2209 | else if (strcmp(nargs[0], "mapext") == 0) { | |
2210 | if (!pool_name || nargs.size() < 2) | |
2211 | usage_exit(); | |
2212 | string oid(nargs[1]); | |
2213 | std::map<uint64_t,uint64_t> m; | |
2214 | ret = io_ctx.mapext(oid, 0, -1, m); | |
2215 | if (ret < 0) { | |
2216 | cerr << "mapext error on " << pool_name << "/" << oid << ": " << cpp_strerror(ret) << std::endl; | |
2217 | goto out; | |
2218 | } | |
2219 | std::map<uint64_t,uint64_t>::iterator iter; | |
2220 | for (iter = m.begin(); iter != m.end(); ++iter) { | |
2221 | cout << hex << iter->first << "\t" << iter->second << dec << std::endl; | |
2222 | } | |
2223 | } | |
2224 | else if (strcmp(nargs[0], "stat") == 0) { | |
2225 | if (!pool_name || nargs.size() < 2) | |
2226 | usage_exit(); | |
2227 | string oid(nargs[1]); | |
2228 | uint64_t size; | |
2229 | time_t mtime; | |
2230 | if (use_striper) { | |
2231 | ret = striper.stat(oid, &size, &mtime); | |
2232 | } else { | |
2233 | ret = io_ctx.stat(oid, &size, &mtime); | |
2234 | } | |
2235 | if (ret < 0) { | |
2236 | cerr << " error stat-ing " << pool_name << "/" << oid << ": " | |
2237 | << cpp_strerror(ret) << std::endl; | |
2238 | goto out; | |
2239 | } else { | |
2240 | utime_t t(mtime, 0); | |
2241 | cout << pool_name << "/" << oid | |
2242 | << " mtime " << t << ", size " << size << std::endl; | |
2243 | } | |
2244 | } | |
2245 | else if (strcmp(nargs[0], "get") == 0) { | |
2246 | if (!pool_name || nargs.size() < 3) | |
2247 | usage_exit(); | |
2248 | ret = do_get(io_ctx, striper, nargs[1], nargs[2], op_size, use_striper); | |
2249 | if (ret < 0) { | |
2250 | cerr << "error getting " << pool_name << "/" << nargs[1] << ": " << cpp_strerror(ret) << std::endl; | |
2251 | goto out; | |
2252 | } | |
2253 | } | |
2254 | else if (strcmp(nargs[0], "put") == 0) { | |
2255 | if (!pool_name || nargs.size() < 3) | |
2256 | usage_exit(); | |
2257 | ret = do_put(io_ctx, striper, nargs[1], nargs[2], op_size, obj_offset, use_striper); | |
2258 | if (ret < 0) { | |
2259 | cerr << "error putting " << pool_name << "/" << nargs[1] << ": " << cpp_strerror(ret) << std::endl; | |
2260 | goto out; | |
2261 | } | |
2262 | } | |
2263 | else if (strcmp(nargs[0], "append") == 0) { | |
2264 | if (!pool_name || nargs.size() < 3) | |
2265 | usage_exit(); | |
2266 | ret = do_append(io_ctx, striper, nargs[1], nargs[2], op_size, use_striper); | |
2267 | if (ret < 0) { | |
2268 | cerr << "error appending " << pool_name << "/" << nargs[1] << ": " << cpp_strerror(ret) << std::endl; | |
2269 | goto out; | |
2270 | } | |
2271 | } | |
2272 | else if (strcmp(nargs[0], "truncate") == 0) { | |
2273 | if (!pool_name || nargs.size() < 3) | |
2274 | usage_exit(); | |
2275 | ||
2276 | string oid(nargs[1]); | |
2277 | char* endptr = NULL; | |
2278 | long size = strtoll(nargs[2], &endptr, 10); | |
2279 | if (*endptr) { | |
2280 | cerr << "Invalid value for size: '" << nargs[2] << "'" << std::endl; | |
2281 | ret = -EINVAL; | |
2282 | goto out; | |
2283 | } | |
2284 | if (size < 0) { | |
2285 | cerr << "error, cannot truncate to negative value" << std::endl; | |
2286 | usage_exit(); | |
2287 | } | |
2288 | if (use_striper) { | |
2289 | ret = striper.trunc(oid, size); | |
2290 | } else { | |
2291 | ret = io_ctx.trunc(oid, size); | |
2292 | } | |
2293 | if (ret < 0) { | |
2294 | cerr << "error truncating oid " | |
2295 | << oid << " to " << size << ": " | |
2296 | << cpp_strerror(ret) << std::endl; | |
2297 | } else { | |
2298 | ret = 0; | |
2299 | } | |
2300 | } | |
2301 | else if (strcmp(nargs[0], "setxattr") == 0) { | |
2302 | if (!pool_name || nargs.size() < 3 || nargs.size() > 4) | |
2303 | usage_exit(); | |
2304 | ||
2305 | string oid(nargs[1]); | |
2306 | string attr_name(nargs[2]); | |
2307 | bufferlist bl; | |
2308 | if (nargs.size() == 4) { | |
2309 | string attr_val(nargs[3]); | |
2310 | bl.append(attr_val.c_str(), attr_val.length()); | |
2311 | } else { | |
2312 | do { | |
2313 | ret = bl.read_fd(STDIN_FILENO, 1024); // from stdin | |
2314 | if (ret < 0) | |
2315 | goto out; | |
2316 | } while (ret > 0); | |
2317 | } | |
2318 | ||
2319 | if (use_striper) { | |
2320 | ret = striper.setxattr(oid, attr_name.c_str(), bl); | |
2321 | } else { | |
2322 | ret = io_ctx.setxattr(oid, attr_name.c_str(), bl); | |
2323 | } | |
2324 | if (ret < 0) { | |
2325 | cerr << "error setting xattr " << pool_name << "/" << oid << "/" << attr_name << ": " << cpp_strerror(ret) << std::endl; | |
2326 | goto out; | |
2327 | } | |
2328 | else | |
2329 | ret = 0; | |
2330 | } | |
2331 | else if (strcmp(nargs[0], "getxattr") == 0) { | |
2332 | if (!pool_name || nargs.size() < 3) | |
2333 | usage_exit(); | |
2334 | ||
2335 | string oid(nargs[1]); | |
2336 | string attr_name(nargs[2]); | |
2337 | ||
2338 | bufferlist bl; | |
2339 | if (use_striper) { | |
2340 | ret = striper.getxattr(oid, attr_name.c_str(), bl); | |
2341 | } else { | |
2342 | ret = io_ctx.getxattr(oid, attr_name.c_str(), bl); | |
2343 | } | |
2344 | if (ret < 0) { | |
2345 | cerr << "error getting xattr " << pool_name << "/" << oid << "/" << attr_name << ": " << cpp_strerror(ret) << std::endl; | |
2346 | goto out; | |
2347 | } | |
2348 | else | |
2349 | ret = 0; | |
2350 | string s(bl.c_str(), bl.length()); | |
2351 | cout << s; | |
2352 | } else if (strcmp(nargs[0], "rmxattr") == 0) { | |
2353 | if (!pool_name || nargs.size() < 3) | |
2354 | usage_exit(); | |
2355 | ||
2356 | string oid(nargs[1]); | |
2357 | string attr_name(nargs[2]); | |
2358 | ||
2359 | if (use_striper) { | |
2360 | ret = striper.rmxattr(oid, attr_name.c_str()); | |
2361 | } else { | |
2362 | ret = io_ctx.rmxattr(oid, attr_name.c_str()); | |
2363 | } | |
2364 | if (ret < 0) { | |
2365 | cerr << "error removing xattr " << pool_name << "/" << oid << "/" << attr_name << ": " << cpp_strerror(ret) << std::endl; | |
2366 | goto out; | |
2367 | } | |
2368 | } else if (strcmp(nargs[0], "listxattr") == 0) { | |
2369 | if (!pool_name || nargs.size() < 2) | |
2370 | usage_exit(); | |
2371 | ||
2372 | string oid(nargs[1]); | |
2373 | map<std::string, bufferlist> attrset; | |
2374 | bufferlist bl; | |
2375 | if (use_striper) { | |
2376 | ret = striper.getxattrs(oid, attrset); | |
2377 | } else { | |
2378 | ret = io_ctx.getxattrs(oid, attrset); | |
2379 | } | |
2380 | if (ret < 0) { | |
2381 | cerr << "error getting xattr set " << pool_name << "/" << oid << ": " << cpp_strerror(ret) << std::endl; | |
2382 | goto out; | |
2383 | } | |
2384 | ||
2385 | for (map<std::string, bufferlist>::iterator iter = attrset.begin(); | |
2386 | iter != attrset.end(); ++iter) { | |
2387 | cout << iter->first << std::endl; | |
2388 | } | |
2389 | } else if (strcmp(nargs[0], "getomapheader") == 0) { | |
2390 | if (!pool_name || nargs.size() < 2) | |
2391 | usage_exit(); | |
2392 | ||
2393 | string oid(nargs[1]); | |
2394 | string outfile; | |
2395 | if (nargs.size() >= 3) { | |
2396 | outfile = nargs[2]; | |
2397 | } | |
2398 | ||
2399 | bufferlist header; | |
2400 | ret = io_ctx.omap_get_header(oid, &header); | |
2401 | if (ret < 0) { | |
2402 | cerr << "error getting omap header " << pool_name << "/" << oid | |
2403 | << ": " << cpp_strerror(ret) << std::endl; | |
2404 | goto out; | |
2405 | } else { | |
2406 | if (!outfile.empty()) { | |
2407 | cerr << "Writing to " << outfile << std::endl; | |
2408 | dump_data(outfile, header); | |
2409 | } else { | |
2410 | cout << "header (" << header.length() << " bytes) :\n"; | |
2411 | header.hexdump(cout); | |
2412 | cout << std::endl; | |
2413 | } | |
2414 | ret = 0; | |
2415 | } | |
2416 | } else if (strcmp(nargs[0], "setomapheader") == 0) { | |
2417 | if (!pool_name || nargs.size() < 3) | |
2418 | usage_exit(); | |
2419 | ||
2420 | string oid(nargs[1]); | |
2421 | string val(nargs[2]); | |
2422 | ||
2423 | bufferlist bl; | |
2424 | bl.append(val); | |
2425 | ||
2426 | ret = io_ctx.omap_set_header(oid, bl); | |
2427 | if (ret < 0) { | |
2428 | cerr << "error setting omap value " << pool_name << "/" << oid | |
2429 | << ": " << cpp_strerror(ret) << std::endl; | |
2430 | goto out; | |
2431 | } else { | |
2432 | ret = 0; | |
2433 | } | |
2434 | } else if (strcmp(nargs[0], "setomapval") == 0) { | |
2435 | uint32_t min_args = (omap_key_valid ? 2 : 3); | |
2436 | if (!pool_name || nargs.size() < min_args || nargs.size() > min_args + 1) { | |
2437 | usage_exit(); | |
2438 | } | |
2439 | ||
2440 | string oid(nargs[1]); | |
2441 | if (!omap_key_valid) { | |
2442 | omap_key = nargs[2]; | |
2443 | omap_key_pretty = omap_key; | |
2444 | } | |
2445 | ||
2446 | bufferlist bl; | |
2447 | if (nargs.size() > min_args) { | |
2448 | string val(nargs[min_args]); | |
2449 | bl.append(val); | |
2450 | } else { | |
2451 | do { | |
2452 | ret = bl.read_fd(STDIN_FILENO, 1024); // from stdin | |
2453 | if (ret < 0) { | |
2454 | goto out; | |
2455 | } | |
2456 | } while (ret > 0); | |
2457 | } | |
2458 | ||
2459 | map<string, bufferlist> values; | |
2460 | values[omap_key] = bl; | |
2461 | ||
2462 | ret = io_ctx.omap_set(oid, values); | |
2463 | if (ret < 0) { | |
2464 | cerr << "error setting omap value " << pool_name << "/" << oid << "/" | |
2465 | << omap_key_pretty << ": " << cpp_strerror(ret) << std::endl; | |
2466 | goto out; | |
2467 | } else { | |
2468 | ret = 0; | |
2469 | } | |
2470 | } else if (strcmp(nargs[0], "getomapval") == 0) { | |
2471 | uint32_t min_args = (omap_key_valid ? 2 : 3); | |
2472 | if (!pool_name || nargs.size() < min_args || nargs.size() > min_args + 1) { | |
2473 | usage_exit(); | |
2474 | } | |
2475 | ||
2476 | string oid(nargs[1]); | |
2477 | if (!omap_key_valid) { | |
2478 | omap_key = nargs[2]; | |
2479 | omap_key_pretty = omap_key; | |
2480 | } | |
2481 | ||
2482 | set<string> keys; | |
2483 | keys.insert(omap_key); | |
2484 | ||
2485 | std::string outfile; | |
2486 | if (nargs.size() > min_args) { | |
2487 | outfile = nargs[min_args]; | |
2488 | } | |
2489 | ||
2490 | map<string, bufferlist> values; | |
2491 | ret = io_ctx.omap_get_vals_by_keys(oid, keys, &values); | |
2492 | if (ret < 0) { | |
2493 | cerr << "error getting omap value " << pool_name << "/" << oid << "/" | |
2494 | << omap_key_pretty << ": " << cpp_strerror(ret) << std::endl; | |
2495 | goto out; | |
2496 | } else { | |
2497 | ret = 0; | |
2498 | } | |
2499 | ||
2500 | if (values.size() && values.begin()->first == omap_key) { | |
2501 | if (!outfile.empty()) { | |
2502 | cerr << "Writing to " << outfile << std::endl; | |
2503 | dump_data(outfile, values.begin()->second); | |
2504 | } else { | |
2505 | cout << "value (" << values.begin()->second.length() << " bytes) :\n"; | |
2506 | values.begin()->second.hexdump(cout); | |
2507 | cout << std::endl; | |
2508 | } | |
2509 | ret = 0; | |
2510 | } else { | |
2511 | cout << "No such key: " << pool_name << "/" << oid << "/" | |
2512 | << omap_key_pretty << std::endl; | |
2513 | ret = -1; | |
2514 | goto out; | |
2515 | } | |
2516 | } else if (strcmp(nargs[0], "rmomapkey") == 0) { | |
2517 | uint32_t num_args = (omap_key_valid ? 2 : 3); | |
2518 | if (!pool_name || nargs.size() != num_args) { | |
2519 | usage_exit(); | |
2520 | } | |
2521 | ||
2522 | string oid(nargs[1]); | |
2523 | if (!omap_key_valid) { | |
2524 | omap_key = nargs[2]; | |
2525 | omap_key_pretty = omap_key; | |
2526 | } | |
2527 | set<string> keys; | |
2528 | keys.insert(omap_key); | |
2529 | ||
2530 | ret = io_ctx.omap_rm_keys(oid, keys); | |
2531 | if (ret < 0) { | |
2532 | cerr << "error removing omap key " << pool_name << "/" << oid << "/" | |
2533 | << omap_key_pretty << ": " << cpp_strerror(ret) << std::endl; | |
2534 | goto out; | |
2535 | } else { | |
2536 | ret = 0; | |
2537 | } | |
2538 | } else if (strcmp(nargs[0], "listomapvals") == 0) { | |
2539 | if (!pool_name || nargs.size() < 2) | |
2540 | usage_exit(); | |
2541 | ||
2542 | string oid(nargs[1]); | |
2543 | string last_read = ""; | |
2544 | int MAX_READ = 512; | |
2545 | do { | |
2546 | map<string, bufferlist> values; | |
2547 | ret = io_ctx.omap_get_vals(oid, last_read, MAX_READ, &values); | |
2548 | if (ret < 0) { | |
2549 | cerr << "error getting omap keys " << pool_name << "/" << oid << ": " | |
2550 | << cpp_strerror(ret) << std::endl; | |
2551 | return 1; | |
2552 | } | |
2553 | ret = values.size(); | |
2554 | for (map<string, bufferlist>::const_iterator it = values.begin(); | |
2555 | it != values.end(); ++it) { | |
2556 | last_read = it->first; | |
2557 | // dump key in hex if it contains nonprintable characters | |
2558 | if (std::count_if(it->first.begin(), it->first.end(), | |
2559 | (int (*)(int))isprint) < (int)it->first.length()) { | |
2560 | cout << "key (" << it->first.length() << " bytes):\n"; | |
2561 | bufferlist keybl; | |
2562 | keybl.append(it->first); | |
2563 | keybl.hexdump(cout); | |
2564 | } else { | |
2565 | cout << it->first; | |
2566 | } | |
2567 | cout << std::endl; | |
2568 | cout << "value (" << it->second.length() << " bytes) :\n"; | |
2569 | it->second.hexdump(cout); | |
2570 | cout << std::endl; | |
2571 | } | |
2572 | } while (ret == MAX_READ); | |
2573 | ret = 0; | |
2574 | } | |
2575 | else if (strcmp(nargs[0], "cp") == 0) { | |
2576 | if (!pool_name) | |
2577 | usage_exit(); | |
2578 | ||
2579 | if (nargs.size() < 2 || nargs.size() > 3) | |
2580 | usage_exit(); | |
2581 | ||
2582 | const char *target = target_pool_name; | |
2583 | if (!target) | |
2584 | target = pool_name; | |
2585 | ||
2586 | const char *target_obj; | |
2587 | if (nargs.size() < 3) { | |
2588 | if (strcmp(target, pool_name) == 0) { | |
2589 | cerr << "cannot copy object into itself" << std::endl; | |
2590 | ret = -1; | |
2591 | goto out; | |
2592 | } | |
2593 | target_obj = nargs[1]; | |
2594 | } else { | |
2595 | target_obj = nargs[2]; | |
2596 | } | |
2597 | ||
2598 | // open io context. | |
2599 | IoCtx target_ctx; | |
2600 | ret = rados.ioctx_create(target, target_ctx); | |
2601 | if (ret < 0) { | |
2602 | cerr << "error opening target pool " << target << ": " | |
2603 | << cpp_strerror(ret) << std::endl; | |
2604 | goto out; | |
2605 | } | |
2606 | if (target_oloc.size()) { | |
2607 | target_ctx.locator_set_key(target_oloc); | |
2608 | } | |
2609 | if (target_nspace.size()) { | |
2610 | target_ctx.set_namespace(target_nspace); | |
2611 | } | |
2612 | ||
2613 | ret = do_copy(io_ctx, nargs[1], target_ctx, target_obj); | |
2614 | if (ret < 0) { | |
2615 | cerr << "error copying " << pool_name << "/" << nargs[1] << " => " << target << "/" << target_obj << ": " << cpp_strerror(ret) << std::endl; | |
2616 | goto out; | |
2617 | } | |
2618 | } else if (strcmp(nargs[0], "rm") == 0) { | |
2619 | if (!pool_name || nargs.size() < 2) | |
2620 | usage_exit(); | |
2621 | vector<const char *>::iterator iter = nargs.begin(); | |
2622 | ++iter; | |
2623 | for (; iter != nargs.end(); ++iter) { | |
2624 | const string & oid = *iter; | |
2625 | if (use_striper) { | |
2626 | if (forcefull) { | |
2627 | ret = striper.remove(oid, CEPH_OSD_FLAG_FULL_FORCE); | |
2628 | } else { | |
2629 | ret = striper.remove(oid); | |
2630 | } | |
2631 | } else { | |
2632 | if (forcefull) { | |
2633 | ret = io_ctx.remove(oid, CEPH_OSD_FLAG_FULL_FORCE); | |
2634 | } else { | |
2635 | ret = io_ctx.remove(oid); | |
2636 | } | |
2637 | } | |
2638 | if (ret < 0) { | |
2639 | string name = (nspace.size() ? nspace + "/" : "" ) + oid; | |
2640 | cerr << "error removing " << pool_name << ">" << name << ": " << cpp_strerror(ret) << std::endl; | |
2641 | goto out; | |
2642 | } | |
2643 | } | |
2644 | } | |
2645 | else if (strcmp(nargs[0], "create") == 0) { | |
2646 | if (!pool_name || nargs.size() < 2) | |
2647 | usage_exit(); | |
2648 | string oid(nargs[1]); | |
2649 | ret = io_ctx.create(oid, true); | |
2650 | if (ret < 0) { | |
2651 | cerr << "error creating " << pool_name << "/" << oid << ": " << cpp_strerror(ret) << std::endl; | |
2652 | goto out; | |
2653 | } | |
2654 | } | |
2655 | ||
2656 | else if (strcmp(nargs[0], "tmap") == 0) { | |
2657 | if (nargs.size() < 3) | |
2658 | usage_exit(); | |
2659 | if (strcmp(nargs[1], "dump") == 0) { | |
2660 | bufferlist outdata; | |
2661 | string oid(nargs[2]); | |
2662 | ret = io_ctx.read(oid, outdata, 0, 0); | |
2663 | if (ret < 0) { | |
2664 | cerr << "error reading " << pool_name << "/" << oid << ": " << cpp_strerror(ret) << std::endl; | |
2665 | goto out; | |
2666 | } | |
2667 | bufferlist::iterator p = outdata.begin(); | |
2668 | bufferlist header; | |
2669 | map<string, bufferlist> kv; | |
2670 | try { | |
2671 | ::decode(header, p); | |
2672 | ::decode(kv, p); | |
2673 | } | |
2674 | catch (buffer::error& e) { | |
2675 | cerr << "error decoding tmap " << pool_name << "/" << oid << std::endl; | |
2676 | ret = -EINVAL; | |
2677 | goto out; | |
2678 | } | |
2679 | cout << "header (" << header.length() << " bytes):\n"; | |
2680 | header.hexdump(cout); | |
2681 | cout << "\n"; | |
2682 | cout << kv.size() << " keys\n"; | |
2683 | for (map<string,bufferlist>::iterator q = kv.begin(); q != kv.end(); ++q) { | |
2684 | cout << "key '" << q->first << "' (" << q->second.length() << " bytes):\n"; | |
2685 | q->second.hexdump(cout); | |
2686 | cout << "\n"; | |
2687 | } | |
2688 | } | |
2689 | else if (strcmp(nargs[1], "set") == 0 || | |
2690 | strcmp(nargs[1], "create") == 0) { | |
2691 | if (nargs.size() < 5) | |
2692 | usage_exit(); | |
2693 | string oid(nargs[2]); | |
2694 | string k(nargs[3]); | |
2695 | string v(nargs[4]); | |
2696 | bufferlist bl; | |
2697 | char c = (strcmp(nargs[1], "set") == 0) ? CEPH_OSD_TMAP_SET : CEPH_OSD_TMAP_CREATE; | |
2698 | ::encode(c, bl); | |
2699 | ::encode(k, bl); | |
2700 | ::encode(v, bl); | |
2701 | ret = io_ctx.tmap_update(oid, bl); | |
2702 | } | |
2703 | } | |
2704 | ||
2705 | else if (strcmp(nargs[0], "tmap-to-omap") == 0) { | |
2706 | if (!pool_name || nargs.size() < 2) | |
2707 | usage_exit(); | |
2708 | string oid(nargs[1]); | |
2709 | ||
2710 | bufferlist bl; | |
2711 | int r = io_ctx.tmap_get(oid, bl); | |
2712 | if (r < 0) { | |
2713 | ret = r; | |
2714 | cerr << "error reading tmap " << pool_name << "/" << oid | |
2715 | << ": " << cpp_strerror(ret) << std::endl; | |
2716 | goto out; | |
2717 | } | |
2718 | bufferlist hdr; | |
2719 | map<string, bufferlist> kv; | |
2720 | bufferlist::iterator p = bl.begin(); | |
2721 | try { | |
2722 | ::decode(hdr, p); | |
2723 | ::decode(kv, p); | |
2724 | } | |
2725 | catch (buffer::error& e) { | |
2726 | cerr << "error decoding tmap " << pool_name << "/" << oid << std::endl; | |
2727 | ret = -EINVAL; | |
2728 | goto out; | |
2729 | } | |
2730 | if (!p.end()) { | |
2731 | cerr << "error decoding tmap (stray trailing data) in " << pool_name << "/" << oid << std::endl; | |
2732 | ret = -EINVAL; | |
2733 | goto out; | |
2734 | } | |
2735 | librados::ObjectWriteOperation wr; | |
2736 | wr.omap_set_header(hdr); | |
2737 | wr.omap_set(kv); | |
2738 | wr.truncate(0); // delete the old tmap data | |
2739 | r = io_ctx.operate(oid, &wr); | |
2740 | if (r < 0) { | |
2741 | ret = r; | |
2742 | cerr << "error writing tmap data as omap on " << pool_name << "/" << oid | |
2743 | << ": " << cpp_strerror(ret) << std::endl; | |
2744 | goto out; | |
2745 | } | |
2746 | ret = 0; | |
2747 | } | |
2748 | ||
2749 | else if (strcmp(nargs[0], "mkpool") == 0) { | |
2750 | int auid = 0; | |
2751 | __u8 crush_rule = 0; | |
2752 | if (nargs.size() < 2) | |
2753 | usage_exit(); | |
2754 | if (nargs.size() > 2) { | |
2755 | char* endptr = NULL; | |
2756 | auid = strtol(nargs[2], &endptr, 10); | |
2757 | if (*endptr) { | |
2758 | cerr << "Invalid value for auid: '" << nargs[2] << "'" << std::endl; | |
2759 | ret = -EINVAL; | |
2760 | goto out; | |
2761 | } | |
2762 | cerr << "setting auid:" << auid << std::endl; | |
2763 | if (nargs.size() > 3) { | |
2764 | crush_rule = (__u8)strtol(nargs[3], &endptr, 10); | |
2765 | if (*endptr) { | |
2766 | cerr << "Invalid value for crush-rule: '" << nargs[3] << "'" << std::endl; | |
2767 | ret = -EINVAL; | |
2768 | goto out; | |
2769 | } | |
2770 | cerr << "using crush rule " << (int)crush_rule << std::endl; | |
2771 | } | |
2772 | } | |
2773 | ret = rados.pool_create(nargs[1], auid, crush_rule); | |
2774 | if (ret < 0) { | |
2775 | cerr << "error creating pool " << nargs[1] << ": " | |
2776 | << cpp_strerror(ret) << std::endl; | |
2777 | goto out; | |
2778 | } | |
2779 | cout << "successfully created pool " << nargs[1] << std::endl; | |
2780 | } | |
2781 | else if (strcmp(nargs[0], "cppool") == 0) { | |
2782 | bool force = nargs.size() == 4 && !strcmp(nargs[3], "--yes-i-really-mean-it"); | |
2783 | if (nargs.size() != 3 && !(nargs.size() == 4 && force)) | |
2784 | usage_exit(); | |
2785 | const char *src_pool = nargs[1]; | |
2786 | const char *target_pool = nargs[2]; | |
2787 | ||
2788 | if (strcmp(src_pool, target_pool) == 0) { | |
2789 | cerr << "cannot copy pool into itself" << std::endl; | |
2790 | ret = -1; | |
2791 | goto out; | |
2792 | } | |
2793 | ||
2794 | cerr << "WARNING: pool copy does not preserve user_version, which some " | |
2795 | << " apps may rely on." << std::endl; | |
2796 | ||
2797 | if (rados.get_pool_is_selfmanaged_snaps_mode(src_pool)) { | |
2798 | cerr << "WARNING: pool " << src_pool << " has selfmanaged snaps, which are not preserved\n" | |
2799 | << " by the cppool operation. This will break any snapshot user." | |
2800 | << std::endl; | |
2801 | if (!force) { | |
2802 | cerr << " If you insist on making a broken copy, you can pass\n" | |
2803 | << " --yes-i-really-mean-it to proceed anyway." | |
2804 | << std::endl; | |
2805 | exit(1); | |
2806 | } | |
2807 | } | |
2808 | ||
2809 | ret = do_copy_pool(rados, src_pool, target_pool); | |
2810 | if (ret < 0) { | |
2811 | cerr << "error copying pool " << src_pool << " => " << target_pool << ": " | |
2812 | << cpp_strerror(ret) << std::endl; | |
2813 | goto out; | |
2814 | } | |
2815 | cout << "successfully copied pool " << nargs[1] << std::endl; | |
2816 | } | |
2817 | else if (strcmp(nargs[0], "rmpool") == 0) { | |
2818 | if (nargs.size() < 2) | |
2819 | usage_exit(); | |
2820 | if (nargs.size() < 4 || | |
2821 | strcmp(nargs[1], nargs[2]) != 0 || | |
2822 | strcmp(nargs[3], "--yes-i-really-really-mean-it") != 0) { | |
2823 | cerr << "WARNING:\n" | |
2824 | << " This will PERMANENTLY DESTROY an entire pool of objects with no way back.\n" | |
2825 | << " To confirm, pass the pool to remove twice, followed by\n" | |
2826 | << " --yes-i-really-really-mean-it" << std::endl; | |
2827 | ret = -1; | |
2828 | goto out; | |
2829 | } | |
2830 | ret = rados.pool_delete(nargs[1]); | |
2831 | if (ret >= 0) { | |
2832 | cout << "successfully deleted pool " << nargs[1] << std::endl; | |
2833 | } else { //error | |
2834 | cerr << "pool " << nargs[1] << " could not be removed" << std::endl; | |
2835 | cerr << "Check your monitor configuration - `mon allow pool delete` is set to false by default," | |
2836 | << " change it to true to allow deletion of pools" << std::endl; | |
2837 | } | |
2838 | } | |
2839 | else if (strcmp(nargs[0], "purge") == 0) { | |
2840 | if (nargs.size() < 2) | |
2841 | usage_exit(); | |
2842 | if (nargs.size() < 3 || | |
2843 | strcmp(nargs[2], "--yes-i-really-really-mean-it") != 0) { | |
2844 | cerr << "WARNING:\n" | |
2845 | << " This will PERMANENTLY DESTROY all objects from a pool with no way back.\n" | |
2846 | << " To confirm, follow pool with --yes-i-really-really-mean-it" << std::endl; | |
2847 | ret = -1; | |
2848 | goto out; | |
2849 | } | |
2850 | ret = rados.ioctx_create(nargs[1], io_ctx); | |
2851 | if (ret < 0) { | |
2852 | cerr << "error pool " << nargs[1] << ": " | |
2853 | << cpp_strerror(ret) << std::endl; | |
2854 | goto out; | |
2855 | } | |
2856 | io_ctx.set_namespace(all_nspaces); | |
2857 | io_ctx.set_osdmap_full_try(); | |
2858 | RadosBencher bencher(g_ceph_context, rados, io_ctx); | |
2859 | ret = bencher.clean_up_slow("", concurrent_ios); | |
2860 | if (ret >= 0) { | |
2861 | cout << "successfully purged pool " << nargs[1] << std::endl; | |
2862 | } else { //error | |
2863 | cerr << "pool " << nargs[1] << " could not be purged" << std::endl; | |
2864 | cerr << "Check your monitor configuration - `mon allow pool delete` is set to false by default," | |
2865 | << " change it to true to allow deletion of pools" << std::endl; | |
2866 | } | |
2867 | } | |
2868 | else if (strcmp(nargs[0], "lssnap") == 0) { | |
2869 | if (!pool_name || nargs.size() != 1) | |
2870 | usage_exit(); | |
2871 | ||
2872 | vector<snap_t> snaps; | |
2873 | io_ctx.snap_list(&snaps); | |
2874 | for (vector<snap_t>::iterator i = snaps.begin(); | |
2875 | i != snaps.end(); | |
2876 | ++i) { | |
2877 | string s; | |
2878 | time_t t; | |
2879 | if (io_ctx.snap_get_name(*i, &s) < 0) | |
2880 | continue; | |
2881 | if (io_ctx.snap_get_stamp(*i, &t) < 0) | |
2882 | continue; | |
2883 | struct tm bdt; | |
2884 | localtime_r(&t, &bdt); | |
2885 | cout << *i << "\t" << s << "\t"; | |
2886 | ||
2887 | std::ios_base::fmtflags original_flags = cout.flags(); | |
2888 | cout.setf(std::ios::right); | |
2889 | cout.fill('0'); | |
2890 | cout << std::setw(4) << (bdt.tm_year+1900) | |
2891 | << '.' << std::setw(2) << (bdt.tm_mon+1) | |
2892 | << '.' << std::setw(2) << bdt.tm_mday | |
2893 | << ' ' | |
2894 | << std::setw(2) << bdt.tm_hour | |
2895 | << ':' << std::setw(2) << bdt.tm_min | |
2896 | << ':' << std::setw(2) << bdt.tm_sec | |
2897 | << std::endl; | |
2898 | cout.flags(original_flags); | |
2899 | } | |
2900 | cout << snaps.size() << " snaps" << std::endl; | |
2901 | } | |
2902 | ||
2903 | else if (strcmp(nargs[0], "mksnap") == 0) { | |
2904 | if (!pool_name || nargs.size() < 2) | |
2905 | usage_exit(); | |
2906 | ||
2907 | ret = io_ctx.snap_create(nargs[1]); | |
2908 | if (ret < 0) { | |
2909 | cerr << "error creating pool " << pool_name << " snapshot " << nargs[1] | |
2910 | << ": " << cpp_strerror(ret) << std::endl; | |
2911 | goto out; | |
2912 | } | |
2913 | cout << "created pool " << pool_name << " snap " << nargs[1] << std::endl; | |
2914 | } | |
2915 | ||
2916 | else if (strcmp(nargs[0], "rmsnap") == 0) { | |
2917 | if (!pool_name || nargs.size() < 2) | |
2918 | usage_exit(); | |
2919 | ||
2920 | ret = io_ctx.snap_remove(nargs[1]); | |
2921 | if (ret < 0) { | |
2922 | cerr << "error removing pool " << pool_name << " snapshot " << nargs[1] | |
2923 | << ": " << cpp_strerror(ret) << std::endl; | |
2924 | goto out; | |
2925 | } | |
2926 | cout << "removed pool " << pool_name << " snap " << nargs[1] << std::endl; | |
2927 | } | |
2928 | ||
2929 | else if (strcmp(nargs[0], "rollback") == 0) { | |
2930 | if (!pool_name || nargs.size() < 3) | |
2931 | usage_exit(); | |
2932 | ||
2933 | ret = io_ctx.snap_rollback(nargs[1], nargs[2]); | |
2934 | if (ret < 0) { | |
2935 | cerr << "error rolling back pool " << pool_name << " to snapshot " << nargs[1] | |
2936 | << cpp_strerror(ret) << std::endl; | |
2937 | goto out; | |
2938 | } | |
2939 | cout << "rolled back pool " << pool_name | |
2940 | << " to snapshot " << nargs[2] << std::endl; | |
2941 | } | |
2942 | else if (strcmp(nargs[0], "bench") == 0) { | |
2943 | if (!pool_name || nargs.size() < 3) | |
2944 | usage_exit(); | |
2945 | char* endptr = NULL; | |
2946 | int seconds = strtol(nargs[1], &endptr, 10); | |
2947 | if (*endptr) { | |
2948 | cerr << "Invalid value for seconds: '" << nargs[1] << "'" << std::endl; | |
2949 | ret = -EINVAL; | |
2950 | goto out; | |
2951 | } | |
2952 | int operation = 0; | |
2953 | if (strcmp(nargs[2], "write") == 0) | |
2954 | operation = OP_WRITE; | |
2955 | else if (strcmp(nargs[2], "seq") == 0) | |
2956 | operation = OP_SEQ_READ; | |
2957 | else if (strcmp(nargs[2], "rand") == 0) | |
2958 | operation = OP_RAND_READ; | |
2959 | else | |
2960 | usage_exit(); | |
2961 | if (operation != OP_WRITE) { | |
2962 | if (block_size_specified) { | |
224ce89b | 2963 | cerr << "-b|--block_size option can be used only with 'write' bench test" |
7c673cae FG |
2964 | << std::endl; |
2965 | ret = -EINVAL; | |
2966 | goto out; | |
2967 | } | |
2968 | if (bench_write_dest != 0) { | |
2969 | cerr << "--write-object, --write-omap and --write-xattr options can " | |
2970 | "only be used with the 'write' bench test" | |
2971 | << std::endl; | |
2972 | ret = -EINVAL; | |
2973 | goto out; | |
2974 | } | |
2975 | } | |
2976 | else if (bench_write_dest == 0) { | |
2977 | bench_write_dest = OP_WRITE_DEST_OBJ; | |
2978 | } | |
2979 | ||
2980 | if (!formatter && output) { | |
224ce89b | 2981 | cerr << "-o|--output option can only be used with '--format' option" |
7c673cae FG |
2982 | << std::endl; |
2983 | ret = -EINVAL; | |
2984 | goto out; | |
2985 | } | |
2986 | RadosBencher bencher(g_ceph_context, rados, io_ctx); | |
2987 | bencher.set_show_time(show_time); | |
2988 | bencher.set_write_destination(static_cast<OpWriteDest>(bench_write_dest)); | |
2989 | ||
2990 | ostream *outstream = NULL; | |
2991 | if (formatter) { | |
2992 | bencher.set_formatter(formatter); | |
2993 | if (output) | |
2994 | outstream = new ofstream(output); | |
2995 | else | |
2996 | outstream = &cout; | |
2997 | bencher.set_outstream(*outstream); | |
2998 | } | |
2999 | if (!object_size) | |
3000 | object_size = op_size; | |
3001 | else if (object_size < op_size) | |
3002 | op_size = object_size; | |
3003 | cout << "hints = " << (int)hints << std::endl; | |
3004 | ret = bencher.aio_bench(operation, seconds, | |
3005 | concurrent_ios, op_size, object_size, | |
3006 | max_objects, cleanup, hints, run_name, no_verify); | |
3007 | if (ret != 0) | |
224ce89b | 3008 | cerr << "error during benchmark: " << cpp_strerror(ret) << std::endl; |
7c673cae FG |
3009 | if (formatter && output) |
3010 | delete outstream; | |
3011 | } | |
3012 | else if (strcmp(nargs[0], "cleanup") == 0) { | |
3013 | if (!pool_name) | |
3014 | usage_exit(); | |
3015 | if (wildcard) | |
3016 | io_ctx.set_namespace(all_nspaces); | |
3017 | RadosBencher bencher(g_ceph_context, rados, io_ctx); | |
3018 | ret = bencher.clean_up(prefix, concurrent_ios, run_name); | |
3019 | if (ret != 0) | |
224ce89b | 3020 | cerr << "error during cleanup: " << cpp_strerror(ret) << std::endl; |
7c673cae FG |
3021 | } |
3022 | else if (strcmp(nargs[0], "watch") == 0) { | |
3023 | if (!pool_name || nargs.size() < 2) | |
3024 | usage_exit(); | |
3025 | string oid(nargs[1]); | |
3026 | RadosWatchCtx ctx(io_ctx, oid.c_str()); | |
3027 | uint64_t cookie; | |
3028 | ret = io_ctx.watch2(oid, &cookie, &ctx); | |
3029 | if (ret != 0) | |
224ce89b | 3030 | cerr << "error calling watch: " << cpp_strerror(ret) << std::endl; |
7c673cae FG |
3031 | else { |
3032 | cout << "press enter to exit..." << std::endl; | |
3033 | getchar(); | |
3034 | io_ctx.unwatch2(cookie); | |
3035 | rados.watch_flush(); | |
3036 | } | |
3037 | } | |
3038 | else if (strcmp(nargs[0], "notify") == 0) { | |
3039 | if (!pool_name || nargs.size() < 3) | |
3040 | usage_exit(); | |
3041 | string oid(nargs[1]); | |
3042 | string msg(nargs[2]); | |
3043 | bufferlist bl, replybl; | |
3044 | ::encode(msg, bl); | |
3045 | ret = io_ctx.notify2(oid, bl, 10000, &replybl); | |
3046 | if (ret != 0) | |
224ce89b | 3047 | cerr << "error calling notify: " << cpp_strerror(ret) << std::endl; |
7c673cae FG |
3048 | if (replybl.length()) { |
3049 | map<pair<uint64_t,uint64_t>,bufferlist> rm; | |
3050 | set<pair<uint64_t,uint64_t> > missed; | |
3051 | bufferlist::iterator p = replybl.begin(); | |
3052 | ::decode(rm, p); | |
3053 | ::decode(missed, p); | |
3054 | for (map<pair<uint64_t,uint64_t>,bufferlist>::iterator p = rm.begin(); | |
3055 | p != rm.end(); | |
3056 | ++p) { | |
3057 | cout << "reply client." << p->first.first | |
3058 | << " cookie " << p->first.second | |
3059 | << " : " << p->second.length() << " bytes" << std::endl; | |
3060 | if (p->second.length()) | |
3061 | p->second.hexdump(cout); | |
3062 | } | |
3063 | for (multiset<pair<uint64_t,uint64_t> >::iterator p = missed.begin(); | |
3064 | p != missed.end(); ++p) { | |
3065 | cout << "timeout client." << p->first | |
3066 | << " cookie " << p->second << std::endl; | |
3067 | } | |
3068 | } | |
3069 | } else if (strcmp(nargs[0], "set-alloc-hint") == 0) { | |
3070 | if (!pool_name || nargs.size() < 4) | |
3071 | usage_exit(); | |
3072 | string err; | |
3073 | string oid(nargs[1]); | |
3074 | uint64_t expected_object_size = strict_strtoll(nargs[2], 10, &err); | |
3075 | if (!err.empty()) { | |
3076 | cerr << "couldn't parse expected_object_size: " << err << std::endl; | |
3077 | usage_exit(); | |
3078 | } | |
3079 | uint64_t expected_write_size = strict_strtoll(nargs[3], 10, &err); | |
3080 | if (!err.empty()) { | |
3081 | cerr << "couldn't parse expected_write_size: " << err << std::endl; | |
3082 | usage_exit(); | |
3083 | } | |
3084 | ret = io_ctx.set_alloc_hint(oid, expected_object_size, expected_write_size); | |
3085 | if (ret < 0) { | |
3086 | cerr << "error setting alloc-hint " << pool_name << "/" << oid << ": " | |
3087 | << cpp_strerror(ret) << std::endl; | |
3088 | goto out; | |
3089 | } | |
3090 | } else if (strcmp(nargs[0], "load-gen") == 0) { | |
3091 | if (!pool_name) { | |
3092 | cerr << "error: must specify pool" << std::endl; | |
3093 | usage_exit(); | |
3094 | } | |
3095 | LoadGen lg(&rados); | |
3096 | if (min_obj_len) | |
3097 | lg.min_obj_len = min_obj_len; | |
3098 | if (max_obj_len) | |
3099 | lg.max_obj_len = max_obj_len; | |
3100 | if (min_op_len) | |
3101 | lg.min_op_len = min_op_len; | |
3102 | if (max_op_len) | |
3103 | lg.max_op_len = max_op_len; | |
3104 | if (max_ops) | |
3105 | lg.max_ops = max_ops; | |
3106 | if (max_backlog) | |
3107 | lg.max_backlog = max_backlog; | |
3108 | if (target_throughput) | |
3109 | lg.target_throughput = target_throughput << 20; | |
3110 | if (read_percent >= 0) | |
3111 | lg.read_percent = read_percent; | |
3112 | if (num_objs) | |
3113 | lg.num_objs = num_objs; | |
3114 | if (run_length) | |
3115 | lg.run_length = run_length; | |
3116 | ||
3117 | cout << "run length " << run_length << " seconds" << std::endl; | |
3118 | cout << "preparing " << lg.num_objs << " objects" << std::endl; | |
3119 | ret = lg.bootstrap(pool_name); | |
3120 | if (ret < 0) { | |
3121 | cerr << "load-gen bootstrap failed" << std::endl; | |
3122 | exit(1); | |
3123 | } | |
3124 | cout << "load-gen will run " << lg.run_length << " seconds" << std::endl; | |
3125 | lg.run(); | |
3126 | lg.cleanup(); | |
3127 | } else if (strcmp(nargs[0], "listomapkeys") == 0) { | |
3128 | if (!pool_name || nargs.size() < 2) | |
3129 | usage_exit(); | |
3130 | ||
3131 | set<string> out_keys; | |
3132 | ret = io_ctx.omap_get_keys(nargs[1], "", LONG_MAX, &out_keys); | |
3133 | if (ret < 0) { | |
3134 | cerr << "error getting omap key set " << pool_name << "/" | |
3135 | << nargs[1] << ": " << cpp_strerror(ret) << std::endl; | |
3136 | goto out; | |
3137 | } | |
3138 | ||
3139 | for (set<string>::iterator iter = out_keys.begin(); | |
3140 | iter != out_keys.end(); ++iter) { | |
3141 | cout << *iter << std::endl; | |
3142 | } | |
3143 | } else if (strcmp(nargs[0], "lock") == 0) { | |
3144 | if (!pool_name) | |
3145 | usage_exit(); | |
3146 | ||
3147 | if (!formatter) { | |
3148 | formatter = new JSONFormatter(pretty_format); | |
3149 | } | |
3150 | ret = do_lock_cmd(nargs, opts, &io_ctx, formatter); | |
3151 | } else if (strcmp(nargs[0], "listwatchers") == 0) { | |
3152 | if (!pool_name || nargs.size() < 2) | |
3153 | usage_exit(); | |
3154 | ||
3155 | string oid(nargs[1]); | |
3156 | std::list<obj_watch_t> lw; | |
3157 | ||
3158 | ret = io_ctx.list_watchers(oid, &lw); | |
3159 | if (ret < 0) { | |
3160 | cerr << "error listing watchers " << pool_name << "/" << oid << ": " << cpp_strerror(ret) << std::endl; | |
3161 | goto out; | |
3162 | } | |
3163 | else | |
3164 | ret = 0; | |
3165 | ||
3166 | for (std::list<obj_watch_t>::iterator i = lw.begin(); i != lw.end(); ++i) { | |
3167 | cout << "watcher=" << i->addr << " client." << i->watcher_id << " cookie=" << i->cookie << std::endl; | |
3168 | } | |
3169 | } else if (strcmp(nargs[0], "listsnaps") == 0) { | |
3170 | if (!pool_name || nargs.size() < 2) | |
3171 | usage_exit(); | |
3172 | ||
3173 | string oid(nargs[1]); | |
3174 | snap_set_t ls; | |
3175 | ||
3176 | io_ctx.snap_set_read(LIBRADOS_SNAP_DIR); | |
3177 | ret = io_ctx.list_snaps(oid, &ls); | |
3178 | if (ret < 0) { | |
3179 | cerr << "error listing snap shots " << pool_name << "/" << oid << ": " << cpp_strerror(ret) << std::endl; | |
3180 | goto out; | |
3181 | } | |
3182 | else | |
3183 | ret = 0; | |
3184 | ||
3185 | map<snap_t,string> snamemap; | |
3186 | if (formatter || pretty_format) { | |
3187 | vector<snap_t> snaps; | |
3188 | io_ctx.snap_list(&snaps); | |
3189 | for (vector<snap_t>::iterator i = snaps.begin(); | |
3190 | i != snaps.end(); ++i) { | |
3191 | string s; | |
3192 | if (io_ctx.snap_get_name(*i, &s) < 0) | |
3193 | continue; | |
3194 | snamemap.insert(pair<snap_t,string>(*i, s)); | |
3195 | } | |
3196 | } | |
3197 | ||
3198 | if (formatter) { | |
3199 | formatter->open_object_section("object"); | |
3200 | formatter->dump_string("name", oid); | |
3201 | formatter->open_array_section("clones"); | |
3202 | } else { | |
3203 | cout << oid << ":" << std::endl; | |
3204 | cout << "cloneid snaps size overlap" << std::endl; | |
3205 | } | |
3206 | ||
3207 | for (std::vector<clone_info_t>::iterator ci = ls.clones.begin(); | |
3208 | ci != ls.clones.end(); ++ci) { | |
3209 | ||
3210 | if (formatter) formatter->open_object_section("clone"); | |
3211 | ||
3212 | if (ci->cloneid == librados::SNAP_HEAD) { | |
3213 | if (formatter) | |
3214 | formatter->dump_string("id", "head"); | |
3215 | else | |
3216 | cout << "head"; | |
3217 | } else { | |
3218 | if (formatter) | |
3219 | formatter->dump_unsigned("id", ci->cloneid); | |
3220 | else | |
3221 | cout << ci->cloneid; | |
3222 | } | |
3223 | ||
3224 | if (formatter) | |
3225 | formatter->open_array_section("snapshots"); | |
3226 | else | |
3227 | cout << "\t"; | |
3228 | ||
3229 | if (!formatter && ci->snaps.empty()) { | |
3230 | cout << "-"; | |
3231 | } | |
3232 | for (std::vector<snap_t>::const_iterator snapindex = ci->snaps.begin(); | |
3233 | snapindex != ci->snaps.end(); ++snapindex) { | |
3234 | ||
3235 | map<snap_t,string>::iterator si; | |
3236 | ||
3237 | if (formatter || pretty_format) si = snamemap.find(*snapindex); | |
3238 | ||
3239 | if (formatter) { | |
3240 | formatter->open_object_section("snapshot"); | |
3241 | formatter->dump_unsigned("id", *snapindex); | |
3242 | if (si != snamemap.end()) | |
3243 | formatter->dump_string("name", si->second); | |
3244 | formatter->close_section(); //snapshot | |
3245 | } else { | |
3246 | if (snapindex != ci->snaps.begin()) cout << ","; | |
3247 | if (!pretty_format || (si == snamemap.end())) | |
3248 | cout << *snapindex; | |
3249 | else | |
3250 | cout << si->second << "(" << *snapindex << ")"; | |
3251 | } | |
3252 | } | |
3253 | ||
3254 | if (formatter) { | |
3255 | formatter->close_section(); //Snapshots | |
3256 | formatter->dump_unsigned("size", ci->size); | |
3257 | } else { | |
3258 | cout << "\t" << ci->size; | |
3259 | } | |
3260 | ||
3261 | if (ci->cloneid != librados::SNAP_HEAD) { | |
3262 | if (formatter) | |
3263 | formatter->open_array_section("overlaps"); | |
3264 | else | |
3265 | cout << "\t["; | |
3266 | ||
3267 | for (std::vector< std::pair<uint64_t,uint64_t> >::iterator ovi = ci->overlap.begin(); | |
3268 | ovi != ci->overlap.end(); ++ovi) { | |
3269 | if (formatter) { | |
3270 | formatter->open_object_section("section"); | |
3271 | formatter->dump_unsigned("start", ovi->first); | |
3272 | formatter->dump_unsigned("length", ovi->second); | |
3273 | formatter->close_section(); //section | |
3274 | } else { | |
3275 | if (ovi != ci->overlap.begin()) cout << ","; | |
3276 | cout << ovi->first << "~" << ovi->second; | |
3277 | } | |
3278 | } | |
3279 | if (formatter) | |
3280 | formatter->close_section(); //overlaps | |
3281 | else | |
3282 | cout << "]" << std::endl; | |
3283 | } | |
3284 | if (formatter) formatter->close_section(); //clone | |
3285 | } | |
3286 | if (formatter) { | |
3287 | formatter->close_section(); //clones | |
3288 | formatter->close_section(); //object | |
3289 | formatter->flush(cout); | |
3290 | } else { | |
3291 | cout << std::endl; | |
3292 | } | |
3293 | } else if (strcmp(nargs[0], "list-inconsistent-pg") == 0) { | |
3294 | if (!formatter) { | |
3295 | formatter = new JSONFormatter(pretty_format); | |
3296 | } | |
3297 | ret = do_get_inconsistent_pg_cmd(nargs, rados, *formatter); | |
3298 | } else if (strcmp(nargs[0], "list-inconsistent-obj") == 0) { | |
3299 | if (!formatter) { | |
3300 | formatter = new JSONFormatter(pretty_format); | |
3301 | } | |
3302 | ret = do_get_inconsistent_cmd<inconsistent_obj_t>(nargs, rados, *formatter); | |
3303 | } else if (strcmp(nargs[0], "list-inconsistent-snapset") == 0) { | |
3304 | if (!formatter) { | |
3305 | formatter = new JSONFormatter(pretty_format); | |
3306 | } | |
3307 | ret = do_get_inconsistent_cmd<inconsistent_snapset_t>(nargs, rados, *formatter); | |
3308 | } else if (strcmp(nargs[0], "cache-flush") == 0) { | |
3309 | if (!pool_name || nargs.size() < 2) | |
3310 | usage_exit(); | |
3311 | string oid(nargs[1]); | |
3312 | if (with_clones) { | |
3313 | snap_set_t ls; | |
3314 | io_ctx.snap_set_read(LIBRADOS_SNAP_DIR); | |
3315 | ret = io_ctx.list_snaps(oid, &ls); | |
3316 | if (ret < 0) { | |
3317 | cerr << "error listing snapshots " << pool_name << "/" << oid << ": " | |
3318 | << cpp_strerror(ret) << std::endl; | |
3319 | goto out; | |
3320 | } | |
3321 | for (std::vector<clone_info_t>::iterator ci = ls.clones.begin(); | |
3322 | ci != ls.clones.end(); ++ci) { | |
3323 | if (snapid != CEPH_NOSNAP && ci->cloneid > snapid) | |
3324 | break; | |
3325 | io_ctx.snap_set_read(ci->cloneid); | |
3326 | ret = do_cache_flush(io_ctx, oid); | |
3327 | if (ret < 0) { | |
3328 | cerr << "error from cache-flush " << oid << ": " | |
3329 | << cpp_strerror(ret) << std::endl; | |
3330 | goto out; | |
3331 | } | |
3332 | } | |
3333 | } else { | |
3334 | ret = do_cache_flush(io_ctx, oid); | |
3335 | if (ret < 0) { | |
3336 | cerr << "error from cache-flush " << oid << ": " | |
3337 | << cpp_strerror(ret) << std::endl; | |
3338 | goto out; | |
3339 | } | |
3340 | } | |
3341 | } else if (strcmp(nargs[0], "cache-try-flush") == 0) { | |
3342 | if (!pool_name || nargs.size() < 2) | |
3343 | usage_exit(); | |
3344 | string oid(nargs[1]); | |
3345 | if (with_clones) { | |
3346 | snap_set_t ls; | |
3347 | io_ctx.snap_set_read(LIBRADOS_SNAP_DIR); | |
3348 | ret = io_ctx.list_snaps(oid, &ls); | |
3349 | if (ret < 0) { | |
3350 | cerr << "error listing snapshots " << pool_name << "/" << oid << ": " | |
3351 | << cpp_strerror(ret) << std::endl; | |
3352 | goto out; | |
3353 | } | |
3354 | for (std::vector<clone_info_t>::iterator ci = ls.clones.begin(); | |
3355 | ci != ls.clones.end(); ++ci) { | |
3356 | if (snapid != CEPH_NOSNAP && ci->cloneid > snapid) | |
3357 | break; | |
3358 | io_ctx.snap_set_read(ci->cloneid); | |
3359 | ret = do_cache_try_flush(io_ctx, oid); | |
3360 | if (ret < 0) { | |
3361 | cerr << "error from cache-flush " << oid << ": " | |
3362 | << cpp_strerror(ret) << std::endl; | |
3363 | goto out; | |
3364 | } | |
3365 | } | |
3366 | } else { | |
3367 | ret = do_cache_try_flush(io_ctx, oid); | |
3368 | if (ret < 0) { | |
3369 | cerr << "error from cache-flush " << oid << ": " | |
3370 | << cpp_strerror(ret) << std::endl; | |
3371 | goto out; | |
3372 | } | |
3373 | } | |
3374 | } else if (strcmp(nargs[0], "cache-evict") == 0) { | |
3375 | if (!pool_name || nargs.size() < 2) | |
3376 | usage_exit(); | |
3377 | string oid(nargs[1]); | |
3378 | if (with_clones) { | |
3379 | snap_set_t ls; | |
3380 | io_ctx.snap_set_read(LIBRADOS_SNAP_DIR); | |
3381 | ret = io_ctx.list_snaps(oid, &ls); | |
3382 | if (ret < 0) { | |
3383 | cerr << "error listing snapshots " << pool_name << "/" << oid << ": " | |
3384 | << cpp_strerror(ret) << std::endl; | |
3385 | goto out; | |
3386 | } | |
3387 | for (std::vector<clone_info_t>::iterator ci = ls.clones.begin(); | |
3388 | ci != ls.clones.end(); ++ci) { | |
3389 | if (snapid != CEPH_NOSNAP && ci->cloneid > snapid) | |
3390 | break; | |
3391 | io_ctx.snap_set_read(ci->cloneid); | |
3392 | ret = do_cache_evict(io_ctx, oid); | |
3393 | if (ret < 0) { | |
3394 | cerr << "error from cache-flush " << oid << ": " | |
3395 | << cpp_strerror(ret) << std::endl; | |
3396 | goto out; | |
3397 | } | |
3398 | } | |
3399 | } else { | |
3400 | ret = do_cache_evict(io_ctx, oid); | |
3401 | if (ret < 0) { | |
3402 | cerr << "error from cache-flush " << oid << ": " | |
3403 | << cpp_strerror(ret) << std::endl; | |
3404 | goto out; | |
3405 | } | |
3406 | } | |
3407 | } else if (strcmp(nargs[0], "cache-flush-evict-all") == 0) { | |
3408 | if (!pool_name) | |
3409 | usage_exit(); | |
3410 | ret = do_cache_flush_evict_all(io_ctx, true); | |
3411 | if (ret < 0) { | |
3412 | cerr << "error from cache-flush-evict-all: " | |
3413 | << cpp_strerror(ret) << std::endl; | |
3414 | goto out; | |
3415 | } | |
3416 | } else if (strcmp(nargs[0], "cache-try-flush-evict-all") == 0) { | |
3417 | if (!pool_name) | |
3418 | usage_exit(); | |
3419 | ret = do_cache_flush_evict_all(io_ctx, false); | |
3420 | if (ret < 0) { | |
3421 | cerr << "error from cache-try-flush-evict-all: " | |
3422 | << cpp_strerror(ret) << std::endl; | |
3423 | goto out; | |
3424 | } | |
31f18b77 FG |
3425 | } else if (strcmp(nargs[0], "set-redirect") == 0) { |
3426 | if (!pool_name) | |
3427 | usage_exit(); | |
3428 | ||
3429 | const char *target = target_pool_name; | |
3430 | if (!target) | |
3431 | target = pool_name; | |
3432 | ||
3433 | const char *target_obj; | |
3434 | if (nargs.size() < 3) { | |
3435 | if (strcmp(target, pool_name) == 0) { | |
3436 | cerr << "cannot copy object into itself" << std::endl; | |
3437 | ret = -1; | |
3438 | goto out; | |
3439 | } | |
3440 | target_obj = nargs[1]; | |
3441 | } else { | |
3442 | target_obj = nargs[2]; | |
3443 | } | |
3444 | ||
3445 | IoCtx target_ctx; | |
3446 | ret = rados.ioctx_create(target, target_ctx); | |
3447 | if (target_oloc.size()) { | |
3448 | target_ctx.locator_set_key(target_oloc); | |
3449 | } | |
3450 | if (target_nspace.size()) { | |
3451 | target_ctx.set_namespace(target_nspace); | |
3452 | } | |
3453 | ||
3454 | ObjectWriteOperation op; | |
3455 | op.set_redirect(target_obj, target_ctx, 0); | |
3456 | ret = io_ctx.operate(nargs[1], &op); | |
3457 | if (ret < 0) { | |
3458 | cerr << "error set-redirect " << pool_name << "/" << nargs[1] << " => " << target << "/" << target_obj << ": " << cpp_strerror(ret) << std::endl; | |
3459 | goto out; | |
3460 | } | |
7c673cae FG |
3461 | } else if (strcmp(nargs[0], "export") == 0) { |
3462 | // export [filename] | |
3463 | if (!pool_name || nargs.size() > 2) { | |
3464 | usage_exit(); | |
3465 | } | |
3466 | ||
3467 | int file_fd; | |
3468 | if (nargs.size() < 2 || std::string(nargs[1]) == "-") { | |
3469 | file_fd = STDOUT_FILENO; | |
3470 | } else { | |
3471 | file_fd = open(nargs[1], O_WRONLY|O_CREAT|O_TRUNC, 0666); | |
3472 | if (file_fd < 0) { | |
3473 | cerr << "Error opening '" << nargs[1] << "': " | |
3474 | << cpp_strerror(file_fd) << std::endl; | |
3475 | ret = file_fd; | |
3476 | goto out; | |
3477 | } | |
3478 | } | |
3479 | ||
3480 | ret = PoolDump(file_fd).dump(&io_ctx); | |
3481 | ||
3482 | if (file_fd != STDIN_FILENO) { | |
3483 | VOID_TEMP_FAILURE_RETRY(::close(file_fd)); | |
3484 | } | |
3485 | ||
3486 | if (ret < 0) { | |
3487 | cerr << "error from export: " | |
3488 | << cpp_strerror(ret) << std::endl; | |
3489 | goto out; | |
3490 | } | |
3491 | } else if (strcmp(nargs[0], "import") == 0) { | |
3492 | // import [--no-overwrite] [--dry-run] <filename | - > | |
3493 | if (!pool_name || nargs.size() > 4 || nargs.size() < 2) { | |
3494 | usage_exit(); | |
3495 | } | |
3496 | ||
3497 | // Last arg is the filename | |
3498 | std::string const filename = nargs[nargs.size() - 1]; | |
3499 | ||
3500 | // All other args may be flags | |
3501 | bool dry_run = false; | |
3502 | bool no_overwrite = false; | |
3503 | for (unsigned i = 1; i < nargs.size() - 1; ++i) { | |
3504 | std::string arg(nargs[i]); | |
3505 | ||
3506 | if (arg == std::string("--no-overwrite")) { | |
3507 | no_overwrite = true; | |
3508 | } else if (arg == std::string("--dry-run")) { | |
3509 | dry_run = true; | |
3510 | } else { | |
3511 | std::cerr << "Invalid argument '" << arg << "'" << std::endl; | |
3512 | ret = -EINVAL; | |
3513 | goto out; | |
3514 | } | |
3515 | } | |
3516 | ||
3517 | int file_fd; | |
3518 | if (filename == "-") { | |
3519 | file_fd = STDIN_FILENO; | |
3520 | } else { | |
3521 | file_fd = open(filename.c_str(), O_RDONLY); | |
3522 | if (file_fd < 0) { | |
3523 | cerr << "Error opening '" << filename << "': " | |
3524 | << cpp_strerror(file_fd) << std::endl; | |
3525 | ret = file_fd; | |
3526 | goto out; | |
3527 | } | |
3528 | } | |
3529 | ||
3530 | ret = RadosImport(file_fd, 0, dry_run).import(io_ctx, no_overwrite); | |
3531 | ||
3532 | if (file_fd != STDIN_FILENO) { | |
3533 | VOID_TEMP_FAILURE_RETRY(::close(file_fd)); | |
3534 | } | |
3535 | ||
3536 | if (ret < 0) { | |
3537 | cerr << "error from import: " | |
3538 | << cpp_strerror(ret) << std::endl; | |
3539 | goto out; | |
3540 | } | |
3541 | } else { | |
3542 | cerr << "unrecognized command " << nargs[0] << "; -h or --help for usage" << std::endl; | |
3543 | ret = -EINVAL; | |
3544 | goto out; | |
3545 | } | |
3546 | ||
3547 | if (ret < 0) | |
3548 | cerr << "error " << (-ret) << ": " << cpp_strerror(ret) << std::endl; | |
3549 | ||
3550 | out: | |
3551 | delete formatter; | |
3552 | return (ret < 0) ? 1 : 0; | |
3553 | } | |
3554 | ||
3555 | int main(int argc, const char **argv) | |
3556 | { | |
3557 | vector<const char*> args; | |
3558 | argv_to_vec(argc, argv, args); | |
3559 | env_to_vec(args); | |
3560 | ||
3561 | std::map < std::string, std::string > opts; | |
3562 | std::string val; | |
3563 | ||
3564 | // Necessary to support usage of -f for formatting, | |
3565 | // since global_init will remove the -f using ceph | |
3566 | // argparse procedures. | |
3567 | for (auto j = args.begin(); j != args.end(); ++j) { | |
3568 | if (strcmp(*j, "--") == 0) { | |
3569 | break; | |
3570 | } else if ((j+1) == args.end()) { | |
3571 | // This can't be a formatting call (no format arg) | |
3572 | break; | |
3573 | } else if (strcmp(*j, "-f") == 0) { | |
3574 | val = *(j+1); | |
3575 | unique_ptr<Formatter> formatter(Formatter::create(val.c_str())); | |
3576 | ||
3577 | if (formatter) { | |
3578 | j = args.erase(j); | |
3579 | opts["format"] = val; | |
3580 | ||
3581 | j = args.erase(j); | |
3582 | break; | |
3583 | } | |
3584 | } | |
3585 | } | |
3586 | ||
3587 | auto cct = global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, | |
3588 | CODE_ENVIRONMENT_UTILITY, 0); | |
3589 | common_init_finish(g_ceph_context); | |
3590 | ||
3591 | std::vector<const char*>::iterator i; | |
3592 | for (i = args.begin(); i != args.end(); ) { | |
3593 | if (ceph_argparse_double_dash(args, i)) { | |
3594 | break; | |
3595 | } else if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) { | |
3596 | usage(cout); | |
3597 | exit(0); | |
3598 | } else if (ceph_argparse_flag(args, i, "-f", "--force", (char*)NULL)) { | |
3599 | opts["force"] = "true"; | |
3600 | } else if (ceph_argparse_flag(args, i, "--force-full", (char*)NULL)) { | |
3601 | opts["force-full"] = "true"; | |
3602 | } else if (ceph_argparse_flag(args, i, "-d", "--delete-after", (char*)NULL)) { | |
3603 | opts["delete-after"] = "true"; | |
3604 | } else if (ceph_argparse_flag(args, i, "-C", "--create", "--create-pool", | |
3605 | (char*)NULL)) { | |
3606 | opts["create"] = "true"; | |
3607 | } else if (ceph_argparse_flag(args, i, "--pretty-format", (char*)NULL)) { | |
3608 | opts["pretty-format"] = "true"; | |
3609 | } else if (ceph_argparse_flag(args, i, "--show-time", (char*)NULL)) { | |
3610 | opts["show-time"] = "true"; | |
3611 | } else if (ceph_argparse_flag(args, i, "--no-cleanup", (char*)NULL)) { | |
3612 | opts["no-cleanup"] = "true"; | |
3613 | } else if (ceph_argparse_flag(args, i, "--no-hints", (char*)NULL)) { | |
3614 | opts["no-hints"] = "true"; | |
3615 | } else if (ceph_argparse_flag(args, i, "--no-verify", (char*)NULL)) { | |
3616 | opts["no-verify"] = "true"; | |
3617 | } else if (ceph_argparse_witharg(args, i, &val, "--run-name", (char*)NULL)) { | |
3618 | opts["run-name"] = val; | |
3619 | } else if (ceph_argparse_witharg(args, i, &val, "--prefix", (char*)NULL)) { | |
3620 | opts["prefix"] = val; | |
3621 | } else if (ceph_argparse_witharg(args, i, &val, "-p", "--pool", (char*)NULL)) { | |
3622 | opts["pool"] = val; | |
3623 | } else if (ceph_argparse_witharg(args, i, &val, "--target-pool", (char*)NULL)) { | |
3624 | opts["target_pool"] = val; | |
3625 | } else if (ceph_argparse_witharg(args, i, &val, "--object-locator" , (char *)NULL)) { | |
3626 | opts["object_locator"] = val; | |
3627 | } else if (ceph_argparse_witharg(args, i, &val, "--target-locator" , (char *)NULL)) { | |
3628 | opts["target_locator"] = val; | |
3629 | } else if (ceph_argparse_witharg(args, i, &val, "--target-nspace" , (char *)NULL)) { | |
3630 | opts["target_nspace"] = val; | |
3631 | } else if (ceph_argparse_flag(args, i, "--striper" , (char *)NULL)) { | |
3632 | opts["striper"] = "true"; | |
3633 | } else if (ceph_argparse_witharg(args, i, &val, "-t", "--concurrent-ios", (char*)NULL)) { | |
3634 | opts["concurrent-ios"] = val; | |
3635 | } else if (ceph_argparse_witharg(args, i, &val, "--block-size", (char*)NULL)) { | |
3636 | opts["block-size"] = val; | |
3637 | } else if (ceph_argparse_witharg(args, i, &val, "-b", (char*)NULL)) { | |
3638 | opts["block-size"] = val; | |
3639 | } else if (ceph_argparse_witharg(args, i, &val, "--object-size", (char*)NULL)) { | |
3640 | opts["object-size"] = val; | |
3641 | } else if (ceph_argparse_witharg(args, i, &val, "--max-objects", (char*)NULL)) { | |
3642 | opts["max-objects"] = val; | |
3643 | } else if (ceph_argparse_witharg(args, i, &val, "--offset", (char*)NULL)) { | |
3644 | opts["offset"] = val; | |
3645 | } else if (ceph_argparse_witharg(args, i, &val, "-o", (char*)NULL)) { | |
3646 | opts["object-size"] = val; | |
3647 | } else if (ceph_argparse_witharg(args, i, &val, "-s", "--snap", (char*)NULL)) { | |
3648 | opts["snap"] = val; | |
3649 | } else if (ceph_argparse_witharg(args, i, &val, "-S", "--snapid", (char*)NULL)) { | |
3650 | opts["snapid"] = val; | |
3651 | } else if (ceph_argparse_witharg(args, i, &val, "--min-object-size", (char*)NULL)) { | |
3652 | opts["min-object-size"] = val; | |
3653 | } else if (ceph_argparse_witharg(args, i, &val, "--max-object-size", (char*)NULL)) { | |
3654 | opts["max-object-size"] = val; | |
3655 | } else if (ceph_argparse_witharg(args, i, &val, "--min-op-len", (char*)NULL)) { | |
3656 | opts["min-op-len"] = val; | |
3657 | } else if (ceph_argparse_witharg(args, i, &val, "--max-op-len", (char*)NULL)) { | |
3658 | opts["max-op-len"] = val; | |
3659 | } else if (ceph_argparse_witharg(args, i, &val, "--max-ops", (char*)NULL)) { | |
3660 | opts["max-ops"] = val; | |
3661 | } else if (ceph_argparse_witharg(args, i, &val, "--max-backlog", (char*)NULL)) { | |
3662 | opts["max-backlog"] = val; | |
3663 | } else if (ceph_argparse_witharg(args, i, &val, "--target-throughput", (char*)NULL)) { | |
3664 | opts["target-throughput"] = val; | |
3665 | } else if (ceph_argparse_witharg(args, i, &val, "--read-percent", (char*)NULL)) { | |
3666 | opts["read-percent"] = val; | |
3667 | } else if (ceph_argparse_witharg(args, i, &val, "--num-objects", (char*)NULL)) { | |
3668 | opts["num-objects"] = val; | |
3669 | } else if (ceph_argparse_witharg(args, i, &val, "--run-length", (char*)NULL)) { | |
3670 | opts["run-length"] = val; | |
3671 | } else if (ceph_argparse_witharg(args, i, &val, "--workers", (char*)NULL)) { | |
3672 | opts["workers"] = val; | |
3673 | } else if (ceph_argparse_witharg(args, i, &val, "--format", (char*)NULL)) { | |
3674 | opts["format"] = val; | |
3675 | } else if (ceph_argparse_witharg(args, i, &val, "--lock-tag", (char*)NULL)) { | |
3676 | opts["lock-tag"] = val; | |
3677 | } else if (ceph_argparse_witharg(args, i, &val, "--lock-cookie", (char*)NULL)) { | |
3678 | opts["lock-cookie"] = val; | |
3679 | } else if (ceph_argparse_witharg(args, i, &val, "--lock-description", (char*)NULL)) { | |
3680 | opts["lock-description"] = val; | |
3681 | } else if (ceph_argparse_witharg(args, i, &val, "--lock-duration", (char*)NULL)) { | |
3682 | opts["lock-duration"] = val; | |
3683 | } else if (ceph_argparse_witharg(args, i, &val, "--lock-type", (char*)NULL)) { | |
3684 | opts["lock-type"] = val; | |
3685 | } else if (ceph_argparse_witharg(args, i, &val, "-N", "--namespace", (char*)NULL)) { | |
3686 | opts["namespace"] = val; | |
3687 | } else if (ceph_argparse_flag(args, i, "--all", (char*)NULL)) { | |
3688 | opts["all"] = "true"; | |
3689 | } else if (ceph_argparse_flag(args, i, "--default", (char*)NULL)) { | |
3690 | opts["default"] = "true"; | |
3691 | } else if (ceph_argparse_witharg(args, i, &val, "-o", "--output", (char*)NULL)) { | |
3692 | opts["output"] = val; | |
3693 | } else if (ceph_argparse_flag(args, i, "--write-omap", (char*)NULL)) { | |
3694 | opts["write-dest-omap"] = "true"; | |
3695 | } else if (ceph_argparse_flag(args, i, "--write-object", (char*)NULL)) { | |
3696 | opts["write-dest-obj"] = "true"; | |
3697 | } else if (ceph_argparse_flag(args, i, "--write-xattr", (char*)NULL)) { | |
3698 | opts["write-dest-xattr"] = "true"; | |
3699 | } else if (ceph_argparse_flag(args, i, "--with-clones", (char*)NULL)) { | |
3700 | opts["with-clones"] = "true"; | |
3701 | } else if (ceph_argparse_witharg(args, i, &val, "--omap-key-file", (char*)NULL)) { | |
3702 | opts["omap-key-file"] = val; | |
3703 | } else { | |
3704 | if (val[0] == '-') | |
3705 | usage_exit(); | |
3706 | ++i; | |
3707 | } | |
3708 | } | |
3709 | ||
3710 | if (args.empty()) { | |
3711 | cerr << "rados: you must give an action. Try --help" << std::endl; | |
3712 | return 1; | |
3713 | } | |
3714 | ||
3715 | return rados_tool_common(opts, args); | |
3716 | } |