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