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