1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
9 * Author: Loic Dachary <loic@dachary.org>
11 * This is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License version 2.1, as published by the Free Software
14 * Foundation. See file COPYING.
18 #include <sys/types.h>
24 #include <type_traits>
26 #include "common/debug.h"
27 #include "common/errno.h"
28 #include "common/config.h"
29 #include "common/Formatter.h"
31 #include "common/ceph_argparse.h"
32 #include "include/stringify.h"
33 #include "global/global_context.h"
34 #include "global/global_init.h"
35 #include "osd/OSDMap.h"
36 #include "crush/CrushWrapper.h"
37 #include "crush/CrushCompiler.h"
38 #include "crush/CrushTester.h"
39 #include "include/ceph_assert.h"
41 #define dout_context g_ceph_context
42 #define dout_subsys ceph_subsys_crush
57 const char *infn
= "stdin";
59 static int get_fd_data(int fd
, bufferlist
&bl
)
64 ssize_t bytes
= bl
.read_fd(fd
, 1024*1024);
66 cerr
<< "read_fd error " << cpp_strerror(-bytes
) << "\n";
76 ceph_assert(bl
.length() == total
);
80 ////////////////////////////////////////////////////////////////////////////
82 void data_analysis_usage()
84 cout
<< "data output from testing routine ...\n";
85 cout
<< " absolute_weights\n";
86 cout
<< " the decimal weight of each OSD\n";
87 cout
<< " data layout: ROW MAJOR\n";
88 cout
<< " OSD id (int), weight (int)\n";
89 cout
<< " batch_device_expected_utilization_all\n";
90 cout
<< " the expected number of objects each OSD should receive per placement batch\n";
91 cout
<< " which may be a decimal value\n";
92 cout
<< " data layout: COLUMN MAJOR\n";
93 cout
<< " round (int), objects expected on OSD 0...OSD n (float)\n";
94 cout
<< " batch_device_utilization_all\n";
95 cout
<< " the number of objects stored on each OSD during each placement round\n";
96 cout
<< " data layout: COLUMN MAJOR\n";
97 cout
<< " round (int), objects stored on OSD 0...OSD n (int)\n";
98 cout
<< " device_utilization_all\n";
99 cout
<< " the number of objects stored on each OSD at the end of placements\n";
100 cout
<< " data_layout: ROW MAJOR\n";
101 cout
<< " OSD id (int), objects stored (int), objects expected (float)\n";
102 cout
<< " device_utilization\n";
103 cout
<< " the number of objects stored on each OSD marked 'up' at the end of placements\n";
104 cout
<< " data_layout: ROW MAJOR\n";
105 cout
<< " OSD id (int), objects stored (int), objects expected (float)\n";
106 cout
<< " placement_information\n";
107 cout
<< " the map of input -> OSD\n";
108 cout
<< " data_layout: ROW MAJOR\n";
109 cout
<< " input (int), OSD's mapped (int)\n";
110 cout
<< " proportional_weights_all\n";
111 cout
<< " the proportional weight of each OSD specified in the CRUSH map\n";
112 cout
<< " data_layout: ROW MAJOR\n";
113 cout
<< " OSD id (int), proportional weight (float)\n";
114 cout
<< " proportional_weights\n";
115 cout
<< " the proportional weight of each 'up' OSD specified in the CRUSH map\n";
116 cout
<< " data_layout: ROW MAJOR\n";
117 cout
<< " OSD id (int), proportional weight (float)\n";
122 cout
<< "usage: crushtool ...\n";
124 cout
<< "Display, modify and test a crush map\n";
126 cout
<< "There are five stages, running one after the other:\n";
128 cout
<< " - input/build\n";
129 cout
<< " - tunables adjustments\n";
130 cout
<< " - modifications\n";
131 cout
<< " - display/test\n";
132 cout
<< " - output\n";
134 cout
<< "Options that are not specific to a stage.\n";
136 cout
<< " [--infn|-i infile]\n";
137 cout
<< " read the crush map from infile\n";
139 cout
<< "Options for the input/build stage\n";
141 cout
<< " --decompile|-d map decompile a crush map to source\n";
142 cout
<< " [--outfn|-o outfile]\n";
143 cout
<< " specify output for for (de)compilation\n";
144 cout
<< " --compile|-c map.txt compile a map from source\n";
145 cout
<< " --enable-unsafe-tunables\n";
146 cout
<< " compile with unsafe tunables\n";
147 cout
<< " --build --num_osds N layer1 ...\n";
148 cout
<< " build a new map, where each 'layer' is\n";
149 cout
<< " 'name (uniform|straw2|straw|list|tree) size'\n";
151 cout
<< "Options for the tunables adjustments stage\n";
153 cout
<< " --set-choose-local-tries N\n";
154 cout
<< " set choose local retries before re-descent\n";
155 cout
<< " --set-choose-local-fallback-tries N\n";
156 cout
<< " set choose local retries using fallback\n";
157 cout
<< " permutation before re-descent\n";
158 cout
<< " --set-choose-total-tries N\n";
159 cout
<< " set choose total descent attempts\n";
160 cout
<< " --set-chooseleaf-descend-once <0|1>\n";
161 cout
<< " set chooseleaf to (not) retry the recursive descent\n";
162 cout
<< " --set-chooseleaf-vary-r <0|1>\n";
163 cout
<< " set chooseleaf to (not) vary r based on parent\n";
164 cout
<< " --set-chooseleaf-stable <0|1>\n";
165 cout
<< " set chooseleaf firstn to (not) return stable results\n";
167 cout
<< "Options for the modifications stage\n";
169 cout
<< " -i mapfn --add-item id weight name [--loc type name ...]\n";
170 cout
<< " insert an item into the hierarchy at the\n";
171 cout
<< " given location\n";
172 cout
<< " -i mapfn --update-item id weight name [--loc type name ...]\n";
173 cout
<< " insert or move an item into the hierarchy at the\n";
174 cout
<< " given location\n";
175 cout
<< " -i mapfn --remove-item name\n"
176 << " remove the given item\n";
177 cout
<< " -i mapfn --reweight-item name weight\n";
178 cout
<< " reweight a given item (and adjust ancestor\n"
179 << " weights as needed)\n";
180 cout
<< " -i mapfn --add-bucket name type [--loc type name ...]\n"
181 << " insert a bucket into the hierarchy at the given\n"
183 cout
<< " -i mapfn --move name --loc type name ...\n"
184 << " move the given item to specified location\n";
185 cout
<< " -i mapfn --reweight recalculate all bucket weights\n";
186 cout
<< " -i mapfn --rebuild-class-roots\n";
187 cout
<< " rebuild the per-class shadow trees (normally a no-op)\n";
188 cout
<< " -i mapfn --create-simple-rule name root type mode\n"
189 << " create crush rule <name> to start from <root>,\n"
190 << " replicate across buckets of type <type>, using\n"
191 << " a choose mode of <firstn|indep>\n";
192 cout
<< " -i mapfn --create-replicated-rule name root type\n"
193 << " create crush rule <name> to start from <root>,\n"
194 << " replicate across buckets of type <type>\n";
195 cout
<< " --device-class <class>\n";
196 cout
<< " use device class <class> for new rule\n";
197 cout
<< " -i mapfn --remove-rule name\n"
198 << " remove the specified crush rule\n";
200 cout
<< "Options for the display/test stage\n";
202 cout
<< " -f --format the format of --dump, defaults to json-pretty\n";
203 cout
<< " can be one of json, json-pretty, xml, xml-pretty,\n";
204 cout
<< " table, table-kv, html, html-pretty\n";
205 cout
<< " --dump dump the crush map\n";
206 cout
<< " --tree print map summary as a tree\n";
207 cout
<< " --bucket-tree print bucket map summary as a tree\n";
208 cout
<< " --bucket-name specify bucket bucket name for bucket-tree\n";
209 cout
<< " --check [max_id] check if any item is referencing an unknown name/type\n";
210 cout
<< " -i mapfn --show-location id\n";
211 cout
<< " show location for given device id\n";
212 cout
<< " -i mapfn --test test a range of inputs on the map\n";
213 cout
<< " [--min-x x] [--max-x x] [--x x]\n";
214 cout
<< " [--min-rule r] [--max-rule r] [--rule r]\n";
215 cout
<< " [--min-rep n] [--max-rep n] [--num-rep n]\n";
216 cout
<< " [--pool-id n] specifies pool id\n";
217 cout
<< " [--batches b] split the CRUSH mapping into b > 1 rounds\n";
218 cout
<< " [--weight|-w devno weight]\n";
219 cout
<< " where weight is 0 to 1.0\n";
220 cout
<< " [--simulate] simulate placements using a random\n";
221 cout
<< " number generator in place of the CRUSH\n";
222 cout
<< " algorithm\n";
223 cout
<< " --show-utilization show OSD usage\n";
224 cout
<< " --show-utilization-all\n";
225 cout
<< " include zero weight items\n";
226 cout
<< " --show-statistics show chi squared statistics\n";
227 cout
<< " --show-mappings show mappings\n";
228 cout
<< " --show-bad-mappings show bad mappings\n";
229 cout
<< " --show-choose-tries show choose tries histogram\n";
230 cout
<< " --output-name name\n";
231 cout
<< " prepend the data file(s) generated during the\n";
232 cout
<< " testing routine with name\n";
233 cout
<< " --output-csv\n";
234 cout
<< " export select data generated during testing routine\n";
235 cout
<< " to CSV files for off-line post-processing\n";
236 cout
<< " use --help-output for more information\n";
237 cout
<< " --reclassify transform legacy CRUSH map buckets and rules\n";
238 cout
<< " by adding classes\n";
239 cout
<< " --reclassify-bucket <bucket-match> <class> <default-parent>\n";
240 cout
<< " --reclassify-root <bucket-name> <class>\n";
241 cout
<< " --set-subtree-class <bucket-name> <class>\n";
242 cout
<< " set class for all items beneath bucket-name\n";
243 cout
<< " --compare <otherfile> compare two maps using --test parameters\n";
245 cout
<< "Options for the output stage\n";
247 cout
<< " [--outfn|-o outfile]\n";
248 cout
<< " specify output for modified crush map\n";
252 struct bucket_types_t
{
256 { "uniform", CRUSH_BUCKET_UNIFORM
},
257 { "list", CRUSH_BUCKET_LIST
},
258 { "straw", CRUSH_BUCKET_STRAW
},
259 { "straw2", CRUSH_BUCKET_STRAW2
},
260 { "tree", CRUSH_BUCKET_TREE
},
266 const char *buckettype
;
270 template<typename
... Args
>
271 bool argparse_withargs(std::vector
<const char*> &args
,
272 std::vector
<const char*>::iterator
& i
,
277 if (!ceph_argparse_flag(args
, i
, opt
, nullptr)) {
280 auto parse
= [&](auto& opt
) {
281 if (i
== args
.end()) {
282 oss
<< "expecting additional argument to " << opt
;
285 using opt_t
= std::remove_pointer_t
<decay_t
<decltype(opt
)>>;
287 if constexpr (std::is_same_v
<opt_t
, string
>) {
289 } else if constexpr (is_same_v
<opt_t
, int>) {
290 *opt
= strict_strtol(*i
, 10, &err
);
291 } else if constexpr (is_same_v
<opt_t
, float>) {
292 *opt
= strict_strtof(*i
, &err
);
302 (... && parse(opts
));
306 int do_add_bucket(CephContext
* cct
,
309 const string
& add_name
,
310 const string
& add_type
,
311 const map
<string
,string
>& add_loc
) {
313 if (crush
.name_exists(add_name
)) {
314 cerr
<< me
<< " bucket '" << add_name
<< "' already exists" << std::endl
;
317 int type
= crush
.get_type_id(add_type
);
319 cerr
<< me
<< " bad bucket type: " << add_type
<< std::endl
;
322 if (int r
= crush
.add_bucket(0, 0, CRUSH_HASH_DEFAULT
, type
, 0, nullptr, nullptr, &bucketno
);
324 cerr
<< me
<< " unable to add bucket: " << cpp_strerror(r
) << std::endl
;
327 if (int r
= crush
.set_item_name(bucketno
, add_name
); r
< 0) {
328 cerr
<< me
<< " bad bucket name: " << add_name
<< std::endl
;
331 if (!add_loc
.empty()) {
332 if (!crush
.check_item_loc(cct
, bucketno
, add_loc
, (int*)nullptr)) {
333 if (int r
= crush
.move_bucket(cct
, bucketno
, add_loc
); r
< 0) {
334 cerr
<< me
<< " error moving bucket '" << add_name
<< "' to " << add_loc
<< std::endl
;
342 // return 1 for no change, 0 for successful change, negative on error
343 int do_move_item(CephContext
* cct
,
347 const map
<string
,string
>& loc
)
349 if (!crush
.name_exists(name
)) {
350 cerr
<< me
<< " item '" << name
<< "' does not exist" << std::endl
;
353 int id
= crush
.get_item_id(name
);
355 cerr
<< me
<< " expecting additional --loc argument to --move" << std::endl
;
358 if (crush
.check_item_loc(cct
, id
, loc
, (int*)nullptr)) {
359 // it's already there
360 cerr
<< me
<< " item '" << name
<< "' already at " << loc
<< std::endl
;
364 switch (int r
= crush
.create_or_move_item(cct
, id
, 0, name
, loc
)) {
373 return crush
.move_bucket(cct
, id
, loc
);
377 int main(int argc
, const char **argv
)
379 auto args
= argv_to_vec(argc
, argv
);
381 cerr
<< argv
[0] << ": -h or --help for usage" << std::endl
;
384 if (ceph_argparse_need_usage(args
)) {
389 const char *me
= argv
[0];
391 std::string infn
, srcfn
, outfn
, add_name
, add_type
, remove_name
,
392 reweight_name
, bucket_name
;
393 std::string move_name
;
394 bool compile
= false;
395 bool decompile
= false;
399 bool display
= false;
401 bool bucket_tree
= false;
402 string dump_format
= "json-pretty";
404 int full_location
= -1;
405 bool write_to_file
= false;
407 bool unsafe_tunables
= false;
409 bool rebuild_class_roots
= false;
411 bool reweight
= false;
413 bool add_bucket
= false;
414 bool update_item
= false;
415 bool move_item
= false;
416 bool add_rule
= false;
417 std::string rule_name
, rule_root
, rule_type
, rule_mode
, rule_device_class
;
418 bool del_rule
= false;
419 float add_weight
= 0;
420 map
<string
,string
> add_loc
;
421 float reweight_weight
= 0;
427 vector
<layer_t
> layers
;
429 int choose_local_tries
= -1;
430 int choose_local_fallback_tries
= -1;
431 int choose_total_tries
= -1;
432 int chooseleaf_descend_once
= -1;
433 int chooseleaf_vary_r
= -1;
434 int chooseleaf_stable
= -1;
435 int straw_calc_version
= -1;
436 int allowed_bucket_algs
= -1;
438 bool reclassify
= false;
439 map
<string
,pair
<string
,string
>> reclassify_bucket
; // %suffix or prefix% -> class, default_root
440 map
<string
,string
> reclassify_root
; // bucket -> class
441 map
<string
,string
> set_subtree_class
; // bucket -> class
447 CrushTester
tester(crush
, cout
);
449 // we use -c, don't confuse the generic arg parsing
450 // only parse arguments from CEPH_ARGS, if in the environment
451 vector
<const char *> empty_args
;
452 auto cct
= global_init(NULL
, empty_args
, CEPH_ENTITY_TYPE_CLIENT
,
453 CODE_ENVIRONMENT_UTILITY
,
454 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
);
455 // crushtool times out occasionally when quits. so do not
456 // release the g_ceph_context.
458 common_init_finish(g_ceph_context
);
465 std::ostringstream err
;
467 for (std::vector
<const char*>::iterator i
= args
.begin(); i
!= args
.end(); ) {
468 if (ceph_argparse_double_dash(args
, i
)) {
470 } else if (ceph_argparse_witharg(args
, i
, &val
, "-d", "--decompile", (char*)NULL
)) {
473 } else if (ceph_argparse_witharg(args
, i
, &val
, "-i", "--infn", (char*)NULL
)) {
475 } else if (ceph_argparse_witharg(args
, i
, &val
, "-o", "--outfn", (char*)NULL
)) {
477 } else if (ceph_argparse_flag(args
, i
, "-v", "--verbose", (char*)NULL
)) {
479 } else if (ceph_argparse_witharg(args
, i
, &val
, "--compare", (char*)NULL
)) {
481 } else if (ceph_argparse_flag(args
, i
, "--reclassify", (char*)NULL
)) {
483 } else if (ceph_argparse_witharg(args
, i
, &val
, "--reclassify-bucket",
485 if (i
== args
.end()) {
486 cerr
<< "expecting additional argument" << std::endl
;
491 if (i
== args
.end()) {
492 cerr
<< "expecting additional argument" << std::endl
;
495 reclassify_bucket
[val
] = make_pair(c
, *i
);
497 } else if (ceph_argparse_witharg(args
, i
, &val
, "--reclassify-root",
499 if (i
== args
.end()) {
500 cerr
<< "expecting additional argument" << std::endl
;
503 reclassify_root
[val
] = *i
;
505 } else if (ceph_argparse_witharg(args
, i
, &val
, "--set-subtree-class",
507 if (i
== args
.end()) {
508 cerr
<< "expecting additional argument" << std::endl
;
511 set_subtree_class
[val
] = *i
;
513 } else if (ceph_argparse_flag(args
, i
, "--tree", (char*)NULL
)) {
515 } else if (ceph_argparse_flag(args
, i
, "--bucket-tree", (char*)NULL
)) {
517 } else if (ceph_argparse_witharg(args
, i
, &val
, "-b", "--bucket-name", (char*)NULL
)) {
519 } else if (ceph_argparse_witharg(args
, i
, &val
, "-f", "--format", (char*)NULL
)) {
521 } else if (ceph_argparse_flag(args
, i
, "--dump", (char*)NULL
)) {
523 } else if (ceph_argparse_flag(args
, i
, "--show_utilization", (char*)NULL
)) {
525 tester
.set_output_utilization(true);
526 } else if (ceph_argparse_flag(args
, i
, "--show_utilization_all", (char*)NULL
)) {
528 tester
.set_output_utilization_all(true);
529 } else if (ceph_argparse_flag(args
, i
, "--show_statistics", (char*)NULL
)) {
531 tester
.set_output_statistics(true);
532 } else if (ceph_argparse_flag(args
, i
, "--show_mappings", (char*)NULL
)) {
534 tester
.set_output_mappings(true);
535 } else if (ceph_argparse_flag(args
, i
, "--show_bad_mappings", (char*)NULL
)) {
537 tester
.set_output_bad_mappings(true);
538 } else if (ceph_argparse_flag(args
, i
, "--show_choose_tries", (char*)NULL
)) {
540 tester
.set_output_choose_tries(true);
541 } else if (ceph_argparse_witharg(args
, i
, &val
, "-c", "--compile", (char*)NULL
)) {
544 } else if (ceph_argparse_witharg(args
, i
, &max_id
, err
, "--check", (char*)NULL
)) {
546 } else if (ceph_argparse_flag(args
, i
, "-t", "--test", (char*)NULL
)) {
548 } else if (ceph_argparse_witharg(args
, i
, &full_location
, err
, "--show-location", (char*)NULL
)) {
549 } else if (ceph_argparse_flag(args
, i
, "-s", "--simulate", (char*)NULL
)) {
550 tester
.set_random_placement();
551 } else if (ceph_argparse_flag(args
, i
, "--enable-unsafe-tunables", (char*)NULL
)) {
552 unsafe_tunables
= true;
553 } else if (ceph_argparse_witharg(args
, i
, &choose_local_tries
, err
,
554 "--set_choose_local_tries", (char*)NULL
)) {
556 } else if (ceph_argparse_witharg(args
, i
, &choose_local_fallback_tries
, err
,
557 "--set_choose_local_fallback_tries", (char*)NULL
)) {
559 } else if (ceph_argparse_witharg(args
, i
, &choose_total_tries
, err
,
560 "--set_choose_total_tries", (char*)NULL
)) {
562 } else if (ceph_argparse_witharg(args
, i
, &chooseleaf_descend_once
, err
,
563 "--set_chooseleaf_descend_once", (char*)NULL
)) {
565 } else if (ceph_argparse_witharg(args
, i
, &chooseleaf_vary_r
, err
,
566 "--set_chooseleaf_vary_r", (char*)NULL
)) {
568 } else if (ceph_argparse_witharg(args
, i
, &chooseleaf_stable
, err
,
569 "--set_chooseleaf_stable", (char*)NULL
)) {
571 } else if (ceph_argparse_witharg(args
, i
, &straw_calc_version
, err
,
572 "--set_straw_calc_version", (char*)NULL
)) {
574 } else if (ceph_argparse_witharg(args
, i
, &allowed_bucket_algs
, err
,
575 "--set_allowed_bucket_algs", (char*)NULL
)) {
577 } else if (ceph_argparse_flag(args
, i
, "--reweight", (char*)NULL
)) {
579 } else if (ceph_argparse_flag(args
, i
, "--rebuild-class-roots", (char*)NULL
)) {
580 rebuild_class_roots
= true;
581 } else if (ceph_argparse_witharg(args
, i
, &add_item
, err
, "--add_item", (char*)NULL
)) {
582 if (!err
.str().empty()) {
583 cerr
<< err
.str() << std::endl
;
586 if (i
== args
.end()) {
587 cerr
<< "expecting additional argument to --add-item" << std::endl
;
590 add_weight
= atof(*i
);
592 if (i
== args
.end()) {
593 cerr
<< "expecting additional argument to --add-item" << std::endl
;
598 } else if (ceph_argparse_witharg(args
, i
, &add_item
, err
, "--update_item", (char*)NULL
)) {
600 if (!err
.str().empty()) {
601 cerr
<< err
.str() << std::endl
;
604 if (i
== args
.end()) {
605 cerr
<< "expecting additional argument to --update-item" << std::endl
;
608 add_weight
= atof(*i
);
610 if (i
== args
.end()) {
611 cerr
<< "expecting additional argument to --update-item" << std::endl
;
616 } else if (argparse_withargs(args
, i
, err
, "--add-bucket",
617 &add_name
, &add_type
)) {
618 if (!err
.str().empty()) {
619 cerr
<< err
.str() << std::endl
;
623 } else if (argparse_withargs(args
, i
, err
, "--move",
625 if (!err
.str().empty()) {
626 cerr
<< err
.str() << std::endl
;
630 } else if (ceph_argparse_witharg(args
, i
, &val
, err
, "--create-simple-rule", (char*)NULL
)) {
631 rule_name
.assign(val
);
632 if (!err
.str().empty()) {
633 cerr
<< err
.str() << std::endl
;
636 if (i
== args
.end()) {
637 cerr
<< "expecting additional argument to --create-simple-rule" << std::endl
;
641 rule_root
.assign(*i
);
643 if (i
== args
.end()) {
644 cerr
<< "expecting additional argument to --create-simple-rule" << std::endl
;
648 rule_type
.assign(*i
);
650 if (i
== args
.end()) {
651 cerr
<< "expecting additional argument to --create-simple-rule" << std::endl
;
655 rule_mode
.assign(*i
);
658 cout
<< "--create-simple-rule:"
659 << " name=" << rule_name
660 << " root=" << rule_root
661 << " type=" << rule_type
662 << " mode=" << rule_mode
665 } else if (ceph_argparse_witharg(args
, i
, &val
, err
, "--create-replicated-rule", (char*)NULL
)) {
666 rule_name
.assign(val
);
667 if (!err
.str().empty()) {
668 cerr
<< err
.str() << std::endl
;
671 if (i
== args
.end()) {
672 cerr
<< "expecting additional argument to --create-replicated-rule" << std::endl
;
676 rule_root
.assign(*i
);
678 if (i
== args
.end()) {
679 cerr
<< "expecting additional argument to --create-replicated-rule" << std::endl
;
683 rule_type
.assign(*i
);
685 rule_mode
= "firstn";
687 cout
<< "--create-replicated-rule:"
688 << " name=" << rule_name
689 << " root=" << rule_root
690 << " type=" << rule_type
694 } else if (ceph_argparse_witharg(args
, i
, &val
, "--device-class", (char*)NULL
)) {
695 rule_device_class
.assign(val
);
696 if (!err
.str().empty()) {
697 cerr
<< err
.str() << std::endl
;
700 } else if (ceph_argparse_witharg(args
, i
, &val
, "--remove-rule", (char*)NULL
)) {
701 rule_name
.assign(val
);
702 if (!err
.str().empty()) {
703 cerr
<< err
.str() << std::endl
;
707 } else if (ceph_argparse_witharg(args
, i
, &val
, "--loc", (char*)NULL
)) {
708 std::string
type(val
);
709 if (i
== args
.end()) {
710 cerr
<< "expecting additional argument to --loc" << std::endl
;
713 std::string
name(*i
);
715 add_loc
[type
] = name
;
716 } else if (ceph_argparse_flag(args
, i
, "--output-csv", (char*)NULL
)) {
717 write_to_file
= true;
718 tester
.set_output_data_file(true);
719 tester
.set_output_csv(true);
720 } else if (ceph_argparse_flag(args
, i
, "--help-output", (char*)NULL
)) {
721 data_analysis_usage();
723 } else if (ceph_argparse_witharg(args
, i
, &val
, "--output-name", (char*)NULL
)) {
724 std::string
name(val
);
725 if (i
== args
.end()) {
726 cerr
<< "expecting additional argument to --output-name" << std::endl
;
730 tester
.set_output_data_file_name(name
+ "-");
732 } else if (ceph_argparse_witharg(args
, i
, &val
, "--remove_item", (char*)NULL
)) {
734 } else if (ceph_argparse_witharg(args
, i
, &val
, "--reweight_item", (char*)NULL
)) {
736 if (i
== args
.end()) {
737 cerr
<< "expecting additional argument to --reweight-item" << std::endl
;
740 reweight_weight
= atof(*i
);
742 } else if (ceph_argparse_flag(args
, i
, "--build", (char*)NULL
)) {
744 } else if (ceph_argparse_witharg(args
, i
, &num_osds
, err
, "--num_osds", (char*)NULL
)) {
745 if (!err
.str().empty()) {
746 cerr
<< err
.str() << std::endl
;
749 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--num_rep", (char*)NULL
)) {
750 if (!err
.str().empty()) {
751 cerr
<< err
.str() << std::endl
;
754 tester
.set_num_rep(x
);
755 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--min_rep", (char*)NULL
)) {
756 if (!err
.str().empty()) {
757 cerr
<< err
.str() << std::endl
;
760 tester
.set_min_rep(x
);
761 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--max_rep", (char*)NULL
)) {
762 if (!err
.str().empty()) {
763 cerr
<< err
.str() << std::endl
;
766 tester
.set_max_rep(x
);
767 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--max_x", (char*)NULL
)) {
768 if (!err
.str().empty()) {
769 cerr
<< err
.str() << std::endl
;
773 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--min_x", (char*)NULL
)) {
774 if (!err
.str().empty()) {
775 cerr
<< err
.str() << std::endl
;
779 } else if (ceph_argparse_witharg(args
, i
, &z
, err
, "--pool_id", (char*)NULL
)) {
780 if (!err
.str().empty()) {
781 cerr
<< err
.str() << std::endl
;
784 tester
.set_pool_id(z
);
785 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--x", (char*)NULL
)) {
786 if (!err
.str().empty()) {
787 cerr
<< err
.str() << std::endl
;
791 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--max_rule", (char*)NULL
)) {
792 if (!err
.str().empty()) {
793 cerr
<< err
.str() << std::endl
;
796 tester
.set_max_rule(x
);
797 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--min_rule", (char*)NULL
)) {
798 if (!err
.str().empty()) {
799 cerr
<< err
.str() << std::endl
;
802 tester
.set_min_rule(x
);
803 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--rule", (char*)NULL
)) {
804 if (!err
.str().empty()) {
805 cerr
<< err
.str() << std::endl
;
809 } else if (ceph_argparse_witharg(args
, i
, &x
, err
, "--batches", (char*)NULL
)) {
810 if (!err
.str().empty()) {
811 cerr
<< err
.str() << std::endl
;
814 tester
.set_batches(x
);
815 } else if (ceph_argparse_witharg(args
, i
, &y
, err
, "--mark-down-ratio", (char*)NULL
)) {
816 if (!err
.str().empty()) {
817 cerr
<< err
.str() << std::endl
;
820 tester
.set_device_down_ratio(y
);
821 } else if (ceph_argparse_witharg(args
, i
, &y
, err
, "--mark-down-bucket-ratio", (char*)NULL
)) {
822 if (!err
.str().empty()) {
823 cerr
<< err
.str() << std::endl
;
826 tester
.set_bucket_down_ratio(y
);
827 } else if (ceph_argparse_witharg(args
, i
, &tmp
, err
, "--weight", (char*)NULL
)) {
828 if (!err
.str().empty()) {
829 cerr
<< err
.str() << std::endl
;
833 if (i
== args
.end()) {
834 cerr
<< "expecting additional argument to --weight" << std::endl
;
839 tester
.set_device_weight(dev
, f
);
846 if (test
&& !check
&& !display
&& !write_to_file
&& compare
.empty()) {
847 cerr
<< "WARNING: no output selected; use --output-csv or --show-X" << std::endl
;
850 if (decompile
+ compile
+ build
> 1) {
851 cerr
<< "cannot specify more than one of compile, decompile, and build" << std::endl
;
854 if (!check
&& !compile
&& !decompile
&& !build
&& !test
&& !reweight
&& !adjust
&& !tree
&& !dump
&&
855 add_item
< 0 && !add_bucket
&& !move_item
&& !add_rule
&& !del_rule
&& full_location
< 0 &&
857 !reclassify
&& !rebuild_class_roots
&&
860 remove_name
.empty() && reweight_name
.empty()) {
861 cerr
<< "no action specified; -h for help" << std::endl
;
864 if ((!build
) && (!args
.empty())) {
865 cerr
<< "unrecognized arguments: " << args
<< std::endl
;
869 if ((args
.size() % 3) != 0U) {
870 cerr
<< "remaining args: " << args
<< std::endl
;
871 cerr
<< "layers must be specified with 3-tuples of (name, buckettype, size)"
875 for (size_t j
= 0; j
< args
.size(); j
+= 3) {
878 l
.buckettype
= args
[j
+1];
879 l
.size
= atoi(args
[j
+2]);
885 if (outfn) cout << "outfn " << outfn << std::endl;
886 if (cinfn) cout << "cinfn " << cinfn << std::endl;
887 if (dinfn) cout << "dinfn " << dinfn << std::endl;
890 bool modified
= false;
900 if (isatty(STDIN_FILENO
)) {
901 cerr
<< "stdin must not be from a tty" << std::endl
;
904 r
= get_fd_data(STDIN_FILENO
, bl
);
906 cerr
<< "error reading data from STDIN" << std::endl
;
910 r
= bl
.read_file(infn
.c_str(), &error
);
912 cerr
<< me
<< ": error reading '" << infn
<< "': "
913 << error
<< std::endl
;
917 auto p
= bl
.cbegin();
921 cerr
<< me
<< ": unable to decode " << infn
<< std::endl
;
930 ifstream
in(srcfn
.c_str());
932 cerr
<< "input file " << srcfn
<< " not found" << std::endl
;
936 CrushCompiler
cc(crush
, cerr
, verbose
);
938 cc
.enable_unsafe_tunables();
939 int r
= cc
.compile(in
, srcfn
.c_str());
947 if (layers
.empty()) {
948 cerr
<< me
<< ": must specify at least one layer" << std::endl
;
954 vector
<int> lower_items
;
955 vector
<int> lower_weights
;
957 crush
.set_max_devices(num_osds
);
958 for (int i
=0; i
<num_osds
; i
++) {
959 lower_items
.push_back(i
);
960 lower_weights
.push_back(0x10000);
961 crush
.set_item_name(i
, "osd." + stringify(i
));
964 crush
.set_type_name(0, "osd");
966 for (vector
<layer_t
>::iterator p
= layers
.begin(); p
!= layers
.end(); ++p
, type
++) {
969 dout(2) << "layer " << type
971 << " bucket type " << l
.buckettype
975 crush
.set_type_name(type
, l
.name
);
978 for (int i
= 0; bucket_types
[i
].name
; i
++)
979 if (l
.buckettype
&& strcmp(l
.buckettype
, bucket_types
[i
].name
) == 0) {
980 buckettype
= bucket_types
[i
].type
;
983 if (buckettype
< 0) {
984 cerr
<< "unknown bucket type '" << l
.buckettype
<< "'" << std::endl
;
989 vector
<int> cur_items
;
990 vector
<int> cur_weights
;
991 unsigned lower_pos
= 0; // lower pos
993 dout(2) << "lower_items " << lower_items
<< dendl
;
994 dout(2) << "lower_weights " << lower_weights
<< dendl
;
998 if (lower_pos
== lower_items
.size())
1001 int items
[num_osds
];
1002 int weights
[num_osds
];
1006 for (j
=0; j
<l
.size
|| l
.size
==0; j
++) {
1007 if (lower_pos
== lower_items
.size())
1009 items
[j
] = lower_items
[lower_pos
];
1010 weights
[j
] = lower_weights
[lower_pos
];
1011 weight
+= weights
[j
];
1013 dout(2) << " item " << items
[j
] << " weight " << weights
[j
] << dendl
;
1017 int r
= crush
.add_bucket(0, buckettype
, CRUSH_HASH_DEFAULT
, type
, j
, items
, weights
, &id
);
1019 cerr
<< " Couldn't add bucket: " << cpp_strerror(r
) << std::endl
;
1024 format
[sizeof(format
)-1] = '\0';
1026 snprintf(format
, sizeof(format
)-1, "%s%%d", l
.name
);
1028 strncpy(format
, l
.name
, sizeof(format
)-1);
1030 snprintf(name
, sizeof(name
), format
, i
);
1031 crush
.set_item_name(id
, name
);
1033 dout(2) << " in bucket " << id
<< " '" << name
<< "' size " << j
<< " weight " << weight
<< dendl
;
1035 cur_items
.push_back(id
);
1036 cur_weights
.push_back(weight
);
1040 lower_items
.swap(cur_items
);
1041 lower_weights
.swap(cur_weights
);
1044 string root
= layers
.back().size
== 0 ? layers
.back().name
:
1045 string(layers
.back().name
) + "0";
1049 crush
.find_roots(&roots
);
1050 if (roots
.size() > 1) {
1051 cerr
<< "The crush rules will use the root " << root
<< "\n"
1052 << "and ignore the others.\n"
1053 << "There are " << roots
.size() << " roots, they can be\n"
1054 << "grouped into a single root by appending something like:\n"
1055 << " root straw 0\n"
1060 if (OSDMap::build_simple_crush_rules(g_ceph_context
, crush
, root
, &cerr
))
1061 return EXIT_FAILURE
;
1068 if (choose_local_tries
>= 0) {
1069 crush
.set_choose_local_tries(choose_local_tries
);
1072 if (choose_local_fallback_tries
>= 0) {
1073 crush
.set_choose_local_fallback_tries(choose_local_fallback_tries
);
1076 if (choose_total_tries
>= 0) {
1077 crush
.set_choose_total_tries(choose_total_tries
);
1080 if (chooseleaf_descend_once
>= 0) {
1081 crush
.set_chooseleaf_descend_once(chooseleaf_descend_once
);
1084 if (chooseleaf_vary_r
>= 0) {
1085 crush
.set_chooseleaf_vary_r(chooseleaf_vary_r
);
1088 if (chooseleaf_stable
>= 0) {
1089 crush
.set_chooseleaf_stable(chooseleaf_stable
);
1092 if (straw_calc_version
>= 0) {
1093 crush
.set_straw_calc_version(straw_calc_version
);
1096 if (allowed_bucket_algs
>= 0) {
1097 crush
.set_allowed_bucket_algs(allowed_bucket_algs
);
1101 if (!reweight_name
.empty()) {
1102 cout
<< me
<< " reweighting item " << reweight_name
<< " to " << reweight_weight
<< std::endl
;
1104 if (!crush
.name_exists(reweight_name
)) {
1105 cerr
<< " name " << reweight_name
<< " dne" << std::endl
;
1108 int item
= crush
.get_item_id(reweight_name
);
1109 r
= crush
.adjust_item_weightf(g_ceph_context
, item
, reweight_weight
);
1114 cerr
<< me
<< " " << cpp_strerror(r
) << std::endl
;
1119 if (!remove_name
.empty()) {
1120 cout
<< me
<< " removing item " << remove_name
<< std::endl
;
1122 if (!crush
.name_exists(remove_name
)) {
1123 cerr
<< " name " << remove_name
<< " dne" << std::endl
;
1126 int remove_item
= crush
.get_item_id(remove_name
);
1127 r
= crush
.remove_item(g_ceph_context
, remove_item
, false);
1132 cerr
<< me
<< " " << cpp_strerror(r
) << std::endl
;
1137 if (add_item
>= 0) {
1140 r
= crush
.update_item(g_ceph_context
, add_item
, add_weight
, add_name
.c_str(), add_loc
);
1142 r
= crush
.insert_item(g_ceph_context
, add_item
, add_weight
, add_name
.c_str(), add_loc
);
1147 cerr
<< me
<< " " << cpp_strerror(r
) << std::endl
;
1153 if (int r
= do_add_bucket(cct
.get(), me
, crush
, add_name
, add_type
, add_loc
); !r
) {
1161 if (int r
= do_move_item(cct
.get(), me
, crush
, move_name
, add_loc
); !r
) {
1168 if (crush
.rule_exists(rule_name
)) {
1169 cerr
<< "rule " << rule_name
<< " already exists" << std::endl
;
1170 return EXIT_FAILURE
;
1172 int r
= crush
.add_simple_rule(rule_name
, rule_root
, rule_type
,
1174 rule_mode
, pg_pool_t::TYPE_REPLICATED
, &err
);
1176 cerr
<< err
.str() << std::endl
;
1177 return EXIT_FAILURE
;
1183 if (!crush
.rule_exists(rule_name
)) {
1184 cerr
<< "rule " << rule_name
<< " does not exist" << std::endl
;
1187 int ruleno
= crush
.get_rule_id(rule_name
);
1188 ceph_assert(ruleno
>= 0);
1189 int r
= crush
.remove_rule(ruleno
);
1191 cerr
<< "fail to remove rule " << rule_name
<< std::endl
;
1192 return EXIT_FAILURE
;
1198 crush
.reweight(g_ceph_context
);
1201 if (rebuild_class_roots
) {
1202 int r
= crush
.rebuild_roots_with_classes(g_ceph_context
);
1204 cerr
<< "failed to rebuidl roots with classes" << std::endl
;
1205 return EXIT_FAILURE
;
1210 for (auto& i
: set_subtree_class
) {
1211 crush
.set_subtree_class(i
.first
, i
.second
);
1215 int r
= crush
.reclassify(
1221 cerr
<< "failed to reclassify map" << std::endl
;
1222 return EXIT_FAILURE
;
1228 if (full_location
>= 0) {
1229 map
<string
, string
> loc
= crush
.get_full_location(full_location
);
1230 for (map
<string
,string
>::iterator p
= loc
.begin();
1233 cout
<< p
->first
<< "\t" << p
->second
<< std::endl
;
1238 crush
.dump_tree(&cout
, NULL
, {}, true);
1242 if (bucket_name
.empty()) {
1243 cerr
<< ": error bucket_name is empty" << std::endl
;
1247 crush
.get_leaves(bucket_name
.c_str(), &osd_ids
);
1248 for (auto &id
: osd_ids
) {
1249 cout
<< "osd." << id
<< std::endl
;
1255 boost::scoped_ptr
<Formatter
> f(Formatter::create(dump_format
, "json-pretty", "json-pretty"));
1256 f
->open_object_section("crush_map");
1257 crush
.dump(f
.get());
1264 CrushCompiler
cc(crush
, cerr
, verbose
);
1265 if (!outfn
.empty()) {
1267 o
.open(outfn
.c_str(), ios::out
| ios::binary
| ios::trunc
);
1269 cerr
<< me
<< ": error writing '" << outfn
<< "'" << std::endl
;
1270 return EXIT_FAILURE
;
1281 if (!tester
.check_name_maps(max_id
)) {
1282 return EXIT_FAILURE
;
1288 if (tester
.get_output_utilization_all() ||
1289 tester
.get_output_utilization())
1290 tester
.set_output_statistics(true);
1292 int r
= tester
.test(cct
->get());
1294 return EXIT_FAILURE
;
1297 if (compare
.size()) {
1298 CrushWrapper crush2
;
1301 int r
= in
.read_file(compare
.c_str(), &error
);
1303 cerr
<< me
<< ": error reading '" << compare
<< "': "
1304 << error
<< std::endl
;
1305 return EXIT_FAILURE
;
1307 auto p
= in
.cbegin();
1311 cerr
<< me
<< ": unable to decode " << compare
<< std::endl
;
1312 return EXIT_FAILURE
;
1314 r
= tester
.compare(crush2
);
1316 return EXIT_FAILURE
;
1323 if (outfn
.empty()) {
1324 cout
<< me
<< " successfully built or modified map. Use '-o <file>' to write it out." << std::endl
;
1327 crush
.encode(bl
, CEPH_FEATURES_SUPPORTED_DEFAULT
);
1328 int r
= bl
.write_file(outfn
.c_str());
1330 cerr
<< me
<< ": error writing '" << outfn
<< "': " << cpp_strerror(r
) << std::endl
;
1331 return EXIT_FAILURE
;
1334 cout
<< "wrote crush map to " << outfn
<< std::endl
;
1342 * compile-command: "cd .. ; make crushtool && test/run-cli-tests"