]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/crushtool.cc
import ceph quincy 17.2.6
[ceph.git] / ceph / src / tools / crushtool.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
8 *
9 * Author: Loic Dachary <loic@dachary.org>
10 *
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.
15 *
16 */
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <errno.h>
22
23 #include <fstream>
24 #include <type_traits>
25
26 #include "common/debug.h"
27 #include "common/errno.h"
28 #include "common/config.h"
29 #include "common/Formatter.h"
30
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"
40
41 #define dout_context g_ceph_context
42 #define dout_subsys ceph_subsys_crush
43
44 using std::cerr;
45 using std::cout;
46 using std::decay_t;
47 using std::ifstream;
48 using std::ios;
49 using std::is_same_v;
50 using std::map;
51 using std::ofstream;
52 using std::pair;
53 using std::set;
54 using std::string;
55 using std::vector;
56
57 const char *infn = "stdin";
58
59 static int get_fd_data(int fd, bufferlist &bl)
60 {
61
62 uint64_t total = 0;
63 do {
64 ssize_t bytes = bl.read_fd(fd, 1024*1024);
65 if (bytes < 0) {
66 cerr << "read_fd error " << cpp_strerror(-bytes) << "\n";
67 return -1;
68 }
69
70 if (bytes == 0)
71 break;
72
73 total += bytes;
74 } while(true);
75
76 ceph_assert(bl.length() == total);
77 return 0;
78 }
79
80 ////////////////////////////////////////////////////////////////////////////
81
82 void data_analysis_usage()
83 {
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";
118 }
119
120 void usage()
121 {
122 cout << "usage: crushtool ...\n";
123 cout << "\n";
124 cout << "Display, modify and test a crush map\n";
125 cout << "\n";
126 cout << "There are five stages, running one after the other:\n";
127 cout << "\n";
128 cout << " - input/build\n";
129 cout << " - tunables adjustments\n";
130 cout << " - modifications\n";
131 cout << " - display/test\n";
132 cout << " - output\n";
133 cout << "\n";
134 cout << "Options that are not specific to a stage.\n";
135 cout << "\n";
136 cout << " [--infn|-i infile]\n";
137 cout << " read the crush map from infile\n";
138 cout << "\n";
139 cout << "Options for the input/build stage\n";
140 cout << "\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";
150 cout << "\n";
151 cout << "Options for the tunables adjustments stage\n";
152 cout << "\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";
166 cout << "\n";
167 cout << "Options for the modifications stage\n";
168 cout << "\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"
182 << " location\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";
199 cout << "\n";
200 cout << "Options for the display/test stage\n";
201 cout << "\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";
244 cout << "\n";
245 cout << "Options for the output stage\n";
246 cout << "\n";
247 cout << " [--outfn|-o outfile]\n";
248 cout << " specify output for modified crush map\n";
249 cout << "\n";
250 }
251
252 struct bucket_types_t {
253 const char *name;
254 int type;
255 } bucket_types[] = {
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 },
261 { 0, 0 },
262 };
263
264 struct layer_t {
265 const char *name;
266 const char *buckettype;
267 int size;
268 };
269
270 template<typename... Args>
271 bool argparse_withargs(std::vector<const char*> &args,
272 std::vector<const char*>::iterator& i,
273 std::ostream& oss,
274 const char* opt,
275 Args*... opts)
276 {
277 if (!ceph_argparse_flag(args, i, opt, nullptr)) {
278 return false;
279 }
280 auto parse = [&](auto& opt) {
281 if (i == args.end()) {
282 oss << "expecting additional argument to " << opt;
283 return false;
284 }
285 using opt_t = std::remove_pointer_t<decay_t<decltype(opt)>>;
286 string err;
287 if constexpr (std::is_same_v<opt_t, string>) {
288 opt->assign(*i);
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);
293 }
294 i = args.erase(i);
295 if (err.empty())
296 return true;
297 else {
298 oss << err;
299 return false;
300 }
301 };
302 (... && parse(opts));
303 return true;
304 }
305
306 int do_add_bucket(CephContext* cct,
307 const char* me,
308 CrushWrapper& crush,
309 const string& add_name,
310 const string& add_type,
311 const map<string,string>& add_loc) {
312 int bucketno;
313 if (crush.name_exists(add_name)) {
314 cerr << me << " bucket '" << add_name << "' already exists" << std::endl;
315 return -EEXIST;
316 }
317 int type = crush.get_type_id(add_type);
318 if (type <= 0) {
319 cerr << me << " bad bucket type: " << add_type << std::endl;
320 return -EINVAL;
321 }
322 if (int r = crush.add_bucket(0, 0, CRUSH_HASH_DEFAULT, type, 0, nullptr, nullptr, &bucketno);
323 r < 0) {
324 cerr << me << " unable to add bucket: " << cpp_strerror(r) << std::endl;
325 return r;
326 }
327 if (int r = crush.set_item_name(bucketno, add_name); r < 0) {
328 cerr << me << " bad bucket name: " << add_name << std::endl;
329 return r;
330 }
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;
335 return r;
336 }
337 }
338 }
339 return 0;
340 }
341
342 // return 1 for no change, 0 for successful change, negative on error
343 int do_move_item(CephContext* cct,
344 const char *me,
345 CrushWrapper& crush,
346 const string& name,
347 const map<string,string>& loc)
348 {
349 if (!crush.name_exists(name)) {
350 cerr << me << " item '" << name << "' does not exist" << std::endl;
351 return -ENOENT;
352 }
353 int id = crush.get_item_id(name);
354 if (loc.empty()) {
355 cerr << me << " expecting additional --loc argument to --move" << std::endl;
356 return -EINVAL;
357 }
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;
361 return 1;
362 }
363 if (id >= 0) {
364 switch (int r = crush.create_or_move_item(cct, id, 0, name, loc)) {
365 case 0:
366 return 1;
367 case 1:
368 return 0;
369 default:
370 return r;
371 }
372 } else {
373 return crush.move_bucket(cct, id, loc);
374 }
375 }
376
377 int main(int argc, const char **argv)
378 {
379 auto args = argv_to_vec(argc, argv);
380 if (args.empty()) {
381 cerr << argv[0] << ": -h or --help for usage" << std::endl;
382 exit(1);
383 }
384 if (ceph_argparse_need_usage(args)) {
385 usage();
386 exit(0);
387 }
388
389 const char *me = argv[0];
390
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;
396 bool check = false;
397 int max_id = -1;
398 bool test = false;
399 bool display = false;
400 bool tree = false;
401 bool bucket_tree = false;
402 string dump_format = "json-pretty";
403 bool dump = false;
404 int full_location = -1;
405 bool write_to_file = false;
406 int verbose = 0;
407 bool unsafe_tunables = false;
408
409 bool rebuild_class_roots = false;
410
411 bool reweight = false;
412 int add_item = -1;
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;
422
423 bool adjust = false;
424
425 int build = 0;
426 int num_osds =0;
427 vector<layer_t> layers;
428
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;
437
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
442
443 string compare;
444
445 CrushWrapper crush;
446
447 CrushTester tester(crush, cout);
448
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.
457 cct->get();
458 common_init_finish(g_ceph_context);
459
460 int x;
461 float y;
462 long long z;
463
464 std::string val;
465 std::ostringstream err;
466 int tmp;
467 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
468 if (ceph_argparse_double_dash(args, i)) {
469 break;
470 } else if (ceph_argparse_witharg(args, i, &val, "-d", "--decompile", (char*)NULL)) {
471 infn = val;
472 decompile = true;
473 } else if (ceph_argparse_witharg(args, i, &val, "-i", "--infn", (char*)NULL)) {
474 infn = val;
475 } else if (ceph_argparse_witharg(args, i, &val, "-o", "--outfn", (char*)NULL)) {
476 outfn = val;
477 } else if (ceph_argparse_flag(args, i, "-v", "--verbose", (char*)NULL)) {
478 verbose += 1;
479 } else if (ceph_argparse_witharg(args, i, &val, "--compare", (char*)NULL)) {
480 compare = val;
481 } else if (ceph_argparse_flag(args, i, "--reclassify", (char*)NULL)) {
482 reclassify = true;
483 } else if (ceph_argparse_witharg(args, i, &val, "--reclassify-bucket",
484 (char*)NULL)) {
485 if (i == args.end()) {
486 cerr << "expecting additional argument" << std::endl;
487 return EXIT_FAILURE;
488 }
489 string c = *i;
490 i = args.erase(i);
491 if (i == args.end()) {
492 cerr << "expecting additional argument" << std::endl;
493 return EXIT_FAILURE;
494 }
495 reclassify_bucket[val] = make_pair(c, *i);
496 i = args.erase(i);
497 } else if (ceph_argparse_witharg(args, i, &val, "--reclassify-root",
498 (char*)NULL)) {
499 if (i == args.end()) {
500 cerr << "expecting additional argument" << std::endl;
501 return EXIT_FAILURE;
502 }
503 reclassify_root[val] = *i;
504 i = args.erase(i);
505 } else if (ceph_argparse_witharg(args, i, &val, "--set-subtree-class",
506 (char*)NULL)) {
507 if (i == args.end()) {
508 cerr << "expecting additional argument" << std::endl;
509 return EXIT_FAILURE;
510 }
511 set_subtree_class[val] = *i;
512 i = args.erase(i);
513 } else if (ceph_argparse_flag(args, i, "--tree", (char*)NULL)) {
514 tree = true;
515 } else if (ceph_argparse_flag(args, i, "--bucket-tree", (char*)NULL)) {
516 bucket_tree = true;
517 } else if (ceph_argparse_witharg(args, i, &val, "-b", "--bucket-name", (char*)NULL)) {
518 bucket_name = val;
519 } else if (ceph_argparse_witharg(args, i, &val, "-f", "--format", (char*)NULL)) {
520 dump_format = val;
521 } else if (ceph_argparse_flag(args, i, "--dump", (char*)NULL)) {
522 dump = true;
523 } else if (ceph_argparse_flag(args, i, "--show_utilization", (char*)NULL)) {
524 display = true;
525 tester.set_output_utilization(true);
526 } else if (ceph_argparse_flag(args, i, "--show_utilization_all", (char*)NULL)) {
527 display = true;
528 tester.set_output_utilization_all(true);
529 } else if (ceph_argparse_flag(args, i, "--show_statistics", (char*)NULL)) {
530 display = true;
531 tester.set_output_statistics(true);
532 } else if (ceph_argparse_flag(args, i, "--show_mappings", (char*)NULL)) {
533 display = true;
534 tester.set_output_mappings(true);
535 } else if (ceph_argparse_flag(args, i, "--show_bad_mappings", (char*)NULL)) {
536 display = true;
537 tester.set_output_bad_mappings(true);
538 } else if (ceph_argparse_flag(args, i, "--show_choose_tries", (char*)NULL)) {
539 display = true;
540 tester.set_output_choose_tries(true);
541 } else if (ceph_argparse_witharg(args, i, &val, "-c", "--compile", (char*)NULL)) {
542 srcfn = val;
543 compile = true;
544 } else if (ceph_argparse_witharg(args, i, &max_id, err, "--check", (char*)NULL)) {
545 check = true;
546 } else if (ceph_argparse_flag(args, i, "-t", "--test", (char*)NULL)) {
547 test = true;
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)) {
555 adjust = true;
556 } else if (ceph_argparse_witharg(args, i, &choose_local_fallback_tries, err,
557 "--set_choose_local_fallback_tries", (char*)NULL)) {
558 adjust = true;
559 } else if (ceph_argparse_witharg(args, i, &choose_total_tries, err,
560 "--set_choose_total_tries", (char*)NULL)) {
561 adjust = true;
562 } else if (ceph_argparse_witharg(args, i, &chooseleaf_descend_once, err,
563 "--set_chooseleaf_descend_once", (char*)NULL)) {
564 adjust = true;
565 } else if (ceph_argparse_witharg(args, i, &chooseleaf_vary_r, err,
566 "--set_chooseleaf_vary_r", (char*)NULL)) {
567 adjust = true;
568 } else if (ceph_argparse_witharg(args, i, &chooseleaf_stable, err,
569 "--set_chooseleaf_stable", (char*)NULL)) {
570 adjust = true;
571 } else if (ceph_argparse_witharg(args, i, &straw_calc_version, err,
572 "--set_straw_calc_version", (char*)NULL)) {
573 adjust = true;
574 } else if (ceph_argparse_witharg(args, i, &allowed_bucket_algs, err,
575 "--set_allowed_bucket_algs", (char*)NULL)) {
576 adjust = true;
577 } else if (ceph_argparse_flag(args, i, "--reweight", (char*)NULL)) {
578 reweight = true;
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;
584 return EXIT_FAILURE;
585 }
586 if (i == args.end()) {
587 cerr << "expecting additional argument to --add-item" << std::endl;
588 return EXIT_FAILURE;
589 }
590 add_weight = atof(*i);
591 i = args.erase(i);
592 if (i == args.end()) {
593 cerr << "expecting additional argument to --add-item" << std::endl;
594 return EXIT_FAILURE;
595 }
596 add_name.assign(*i);
597 i = args.erase(i);
598 } else if (ceph_argparse_witharg(args, i, &add_item, err, "--update_item", (char*)NULL)) {
599 update_item = true;
600 if (!err.str().empty()) {
601 cerr << err.str() << std::endl;
602 return EXIT_FAILURE;
603 }
604 if (i == args.end()) {
605 cerr << "expecting additional argument to --update-item" << std::endl;
606 return EXIT_FAILURE;
607 }
608 add_weight = atof(*i);
609 i = args.erase(i);
610 if (i == args.end()) {
611 cerr << "expecting additional argument to --update-item" << std::endl;
612 return EXIT_FAILURE;
613 }
614 add_name.assign(*i);
615 i = args.erase(i);
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;
620 return EXIT_FAILURE;
621 }
622 add_bucket = true;
623 } else if (argparse_withargs(args, i, err, "--move",
624 &move_name)) {
625 if (!err.str().empty()) {
626 cerr << err.str() << std::endl;
627 return EXIT_FAILURE;
628 }
629 move_item = true;
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;
634 return EXIT_FAILURE;
635 }
636 if (i == args.end()) {
637 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
638 return EXIT_FAILURE;
639 }
640
641 rule_root.assign(*i);
642 i = args.erase(i);
643 if (i == args.end()) {
644 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
645 return EXIT_FAILURE;
646 }
647
648 rule_type.assign(*i);
649 i = args.erase(i);
650 if (i == args.end()) {
651 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
652 return EXIT_FAILURE;
653 }
654
655 rule_mode.assign(*i);
656 i = args.erase(i);
657
658 cout << "--create-simple-rule:"
659 << " name=" << rule_name
660 << " root=" << rule_root
661 << " type=" << rule_type
662 << " mode=" << rule_mode
663 << std::endl;
664 add_rule = true;
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;
669 return EXIT_FAILURE;
670 }
671 if (i == args.end()) {
672 cerr << "expecting additional argument to --create-replicated-rule" << std::endl;
673 return EXIT_FAILURE;
674 }
675
676 rule_root.assign(*i);
677 i = args.erase(i);
678 if (i == args.end()) {
679 cerr << "expecting additional argument to --create-replicated-rule" << std::endl;
680 return EXIT_FAILURE;
681 }
682
683 rule_type.assign(*i);
684 i = args.erase(i);
685 rule_mode = "firstn";
686
687 cout << "--create-replicated-rule:"
688 << " name=" << rule_name
689 << " root=" << rule_root
690 << " type=" << rule_type
691 << std::endl;
692 add_rule = true;
693
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;
698 return EXIT_FAILURE;
699 }
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;
704 return EXIT_FAILURE;
705 }
706 del_rule = true;
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;
711 return EXIT_FAILURE;
712 }
713 std::string name(*i);
714 i = args.erase(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();
722 return EXIT_SUCCESS;
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;
727 return EXIT_FAILURE;
728 }
729 else {
730 tester.set_output_data_file_name(name + "-");
731 }
732 } else if (ceph_argparse_witharg(args, i, &val, "--remove_item", (char*)NULL)) {
733 remove_name = val;
734 } else if (ceph_argparse_witharg(args, i, &val, "--reweight_item", (char*)NULL)) {
735 reweight_name = val;
736 if (i == args.end()) {
737 cerr << "expecting additional argument to --reweight-item" << std::endl;
738 return EXIT_FAILURE;
739 }
740 reweight_weight = atof(*i);
741 i = args.erase(i);
742 } else if (ceph_argparse_flag(args, i, "--build", (char*)NULL)) {
743 build = true;
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;
747 return EXIT_FAILURE;
748 }
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;
752 return EXIT_FAILURE;
753 }
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;
758 return EXIT_FAILURE;
759 }
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;
764 return EXIT_FAILURE;
765 }
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;
770 return EXIT_FAILURE;
771 }
772 tester.set_max_x(x);
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;
776 return EXIT_FAILURE;
777 }
778 tester.set_min_x(x);
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;
782 return EXIT_FAILURE;
783 }
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;
788 return EXIT_FAILURE;
789 }
790 tester.set_x(x);
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;
794 return EXIT_FAILURE;
795 }
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;
800 return EXIT_FAILURE;
801 }
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;
806 return EXIT_FAILURE;
807 }
808 tester.set_rule(x);
809 } else if (ceph_argparse_witharg(args, i, &x, err, "--batches", (char*)NULL)) {
810 if (!err.str().empty()) {
811 cerr << err.str() << std::endl;
812 return EXIT_FAILURE;
813 }
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;
818 return EXIT_FAILURE;
819 }
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;
824 return EXIT_FAILURE;
825 }
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;
830 return EXIT_FAILURE;
831 }
832 int dev = tmp;
833 if (i == args.end()) {
834 cerr << "expecting additional argument to --weight" << std::endl;
835 return EXIT_FAILURE;
836 }
837 float f = atof(*i);
838 i = args.erase(i);
839 tester.set_device_weight(dev, f);
840 }
841 else {
842 ++i;
843 }
844 }
845
846 if (test && !check && !display && !write_to_file && compare.empty()) {
847 cerr << "WARNING: no output selected; use --output-csv or --show-X" << std::endl;
848 }
849
850 if (decompile + compile + build > 1) {
851 cerr << "cannot specify more than one of compile, decompile, and build" << std::endl;
852 return EXIT_FAILURE;
853 }
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 &&
856 !bucket_tree &&
857 !reclassify && !rebuild_class_roots &&
858 compare.empty() &&
859
860 remove_name.empty() && reweight_name.empty()) {
861 cerr << "no action specified; -h for help" << std::endl;
862 return EXIT_FAILURE;
863 }
864 if ((!build) && (!args.empty())) {
865 cerr << "unrecognized arguments: " << args << std::endl;
866 return EXIT_FAILURE;
867 }
868 else {
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)"
872 << std::endl;
873 return EXIT_FAILURE;
874 }
875 for (size_t j = 0; j < args.size(); j += 3) {
876 layer_t l;
877 l.name = args[j];
878 l.buckettype = args[j+1];
879 l.size = atoi(args[j+2]);
880 layers.push_back(l);
881 }
882 }
883
884 /*
885 if (outfn) cout << "outfn " << outfn << std::endl;
886 if (cinfn) cout << "cinfn " << cinfn << std::endl;
887 if (dinfn) cout << "dinfn " << dinfn << std::endl;
888 */
889
890 bool modified = false;
891
892 // input ----
893
894 if (!infn.empty()) {
895 bufferlist bl;
896 std::string error;
897
898 int r = 0;
899 if (infn == "-") {
900 if (isatty(STDIN_FILENO)) {
901 cerr << "stdin must not be from a tty" << std::endl;
902 return EXIT_FAILURE;
903 }
904 r = get_fd_data(STDIN_FILENO, bl);
905 if (r < 0) {
906 cerr << "error reading data from STDIN" << std::endl;
907 return EXIT_FAILURE;
908 }
909 } else {
910 r = bl.read_file(infn.c_str(), &error);
911 if (r < 0) {
912 cerr << me << ": error reading '" << infn << "': "
913 << error << std::endl;
914 return EXIT_FAILURE;
915 }
916 }
917 auto p = bl.cbegin();
918 try {
919 crush.decode(p);
920 } catch(...) {
921 cerr << me << ": unable to decode " << infn << std::endl;
922 return EXIT_FAILURE;
923 }
924 }
925
926 if (compile) {
927 crush.create();
928
929 // read the file
930 ifstream in(srcfn.c_str());
931 if (!in.is_open()) {
932 cerr << "input file " << srcfn << " not found" << std::endl;
933 return -ENOENT;
934 }
935
936 CrushCompiler cc(crush, cerr, verbose);
937 if (unsafe_tunables)
938 cc.enable_unsafe_tunables();
939 int r = cc.compile(in, srcfn.c_str());
940 if (r < 0)
941 return EXIT_FAILURE;
942
943 modified = true;
944 }
945
946 if (build) {
947 if (layers.empty()) {
948 cerr << me << ": must specify at least one layer" << std::endl;
949 return EXIT_FAILURE;
950 }
951
952 crush.create();
953
954 vector<int> lower_items;
955 vector<int> lower_weights;
956
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));
962 }
963
964 crush.set_type_name(0, "osd");
965 int type = 1;
966 for (vector<layer_t>::iterator p = layers.begin(); p != layers.end(); ++p, type++) {
967 layer_t &l = *p;
968
969 dout(2) << "layer " << type
970 << " " << l.name
971 << " bucket type " << l.buckettype
972 << " " << l.size
973 << dendl;
974
975 crush.set_type_name(type, l.name);
976
977 int buckettype = -1;
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;
981 break;
982 }
983 if (buckettype < 0) {
984 cerr << "unknown bucket type '" << l.buckettype << "'" << std::endl;
985 return EXIT_FAILURE;
986 }
987
988 // build items
989 vector<int> cur_items;
990 vector<int> cur_weights;
991 unsigned lower_pos = 0; // lower pos
992
993 dout(2) << "lower_items " << lower_items << dendl;
994 dout(2) << "lower_weights " << lower_weights << dendl;
995
996 int i = 0;
997 while (1) {
998 if (lower_pos == lower_items.size())
999 break;
1000
1001 int items[num_osds];
1002 int weights[num_osds];
1003
1004 int weight = 0;
1005 int j;
1006 for (j=0; j<l.size || l.size==0; j++) {
1007 if (lower_pos == lower_items.size())
1008 break;
1009 items[j] = lower_items[lower_pos];
1010 weights[j] = lower_weights[lower_pos];
1011 weight += weights[j];
1012 lower_pos++;
1013 dout(2) << " item " << items[j] << " weight " << weights[j] << dendl;
1014 }
1015
1016 int id;
1017 int r = crush.add_bucket(0, buckettype, CRUSH_HASH_DEFAULT, type, j, items, weights, &id);
1018 if (r < 0) {
1019 cerr << " Couldn't add bucket: " << cpp_strerror(r) << std::endl;
1020 return r;
1021 }
1022
1023 char format[20];
1024 format[sizeof(format)-1] = '\0';
1025 if (l.size)
1026 snprintf(format, sizeof(format)-1, "%s%%d", l.name);
1027 else
1028 strncpy(format, l.name, sizeof(format)-1);
1029 char name[20];
1030 snprintf(name, sizeof(name), format, i);
1031 crush.set_item_name(id, name);
1032
1033 dout(2) << " in bucket " << id << " '" << name << "' size " << j << " weight " << weight << dendl;
1034
1035 cur_items.push_back(id);
1036 cur_weights.push_back(weight);
1037 i++;
1038 }
1039
1040 lower_items.swap(cur_items);
1041 lower_weights.swap(cur_weights);
1042 }
1043
1044 string root = layers.back().size == 0 ? layers.back().name :
1045 string(layers.back().name) + "0";
1046
1047 {
1048 set<int> roots;
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"
1056 << std::endl;
1057 }
1058 }
1059
1060 if (OSDMap::build_simple_crush_rules(g_ceph_context, crush, root, &cerr))
1061 return EXIT_FAILURE;
1062
1063 modified = true;
1064 }
1065
1066 // mutate ----
1067
1068 if (choose_local_tries >= 0) {
1069 crush.set_choose_local_tries(choose_local_tries);
1070 modified = true;
1071 }
1072 if (choose_local_fallback_tries >= 0) {
1073 crush.set_choose_local_fallback_tries(choose_local_fallback_tries);
1074 modified = true;
1075 }
1076 if (choose_total_tries >= 0) {
1077 crush.set_choose_total_tries(choose_total_tries);
1078 modified = true;
1079 }
1080 if (chooseleaf_descend_once >= 0) {
1081 crush.set_chooseleaf_descend_once(chooseleaf_descend_once);
1082 modified = true;
1083 }
1084 if (chooseleaf_vary_r >= 0) {
1085 crush.set_chooseleaf_vary_r(chooseleaf_vary_r);
1086 modified = true;
1087 }
1088 if (chooseleaf_stable >= 0) {
1089 crush.set_chooseleaf_stable(chooseleaf_stable);
1090 modified = true;
1091 }
1092 if (straw_calc_version >= 0) {
1093 crush.set_straw_calc_version(straw_calc_version);
1094 modified = true;
1095 }
1096 if (allowed_bucket_algs >= 0) {
1097 crush.set_allowed_bucket_algs(allowed_bucket_algs);
1098 modified = true;
1099 }
1100
1101 if (!reweight_name.empty()) {
1102 cout << me << " reweighting item " << reweight_name << " to " << reweight_weight << std::endl;
1103 int r;
1104 if (!crush.name_exists(reweight_name)) {
1105 cerr << " name " << reweight_name << " dne" << std::endl;
1106 r = -ENOENT;
1107 } else {
1108 int item = crush.get_item_id(reweight_name);
1109 r = crush.adjust_item_weightf(g_ceph_context, item, reweight_weight);
1110 }
1111 if (r >= 0)
1112 modified = true;
1113 else {
1114 cerr << me << " " << cpp_strerror(r) << std::endl;
1115 return r;
1116 }
1117 }
1118
1119 if (!remove_name.empty()) {
1120 cout << me << " removing item " << remove_name << std::endl;
1121 int r;
1122 if (!crush.name_exists(remove_name)) {
1123 cerr << " name " << remove_name << " dne" << std::endl;
1124 r = -ENOENT;
1125 } else {
1126 int remove_item = crush.get_item_id(remove_name);
1127 r = crush.remove_item(g_ceph_context, remove_item, false);
1128 }
1129 if (r == 0)
1130 modified = true;
1131 else {
1132 cerr << me << " " << cpp_strerror(r) << std::endl;
1133 return r;
1134 }
1135 }
1136
1137 if (add_item >= 0) {
1138 int r;
1139 if (update_item) {
1140 r = crush.update_item(g_ceph_context, add_item, add_weight, add_name.c_str(), add_loc);
1141 } else {
1142 r = crush.insert_item(g_ceph_context, add_item, add_weight, add_name.c_str(), add_loc);
1143 }
1144 if (r >= 0) {
1145 modified = true;
1146 } else {
1147 cerr << me << " " << cpp_strerror(r) << std::endl;
1148 return r;
1149 }
1150 }
1151
1152 if (add_bucket) {
1153 if (int r = do_add_bucket(cct.get(), me, crush, add_name, add_type, add_loc); !r) {
1154 modified = true;
1155 } else {
1156 return r;
1157 }
1158 }
1159
1160 if (move_item) {
1161 if (int r = do_move_item(cct.get(), me, crush, move_name, add_loc); !r) {
1162 modified = true;
1163 } else {
1164 return r;
1165 }
1166 }
1167 if (add_rule) {
1168 if (crush.rule_exists(rule_name)) {
1169 cerr << "rule " << rule_name << " already exists" << std::endl;
1170 return EXIT_FAILURE;
1171 }
1172 int r = crush.add_simple_rule(rule_name, rule_root, rule_type,
1173 rule_device_class,
1174 rule_mode, pg_pool_t::TYPE_REPLICATED, &err);
1175 if (r < 0) {
1176 cerr << err.str() << std::endl;
1177 return EXIT_FAILURE;
1178 }
1179 modified = true;
1180 }
1181
1182 if (del_rule) {
1183 if (!crush.rule_exists(rule_name)) {
1184 cerr << "rule " << rule_name << " does not exist" << std::endl;
1185 return 0;
1186 }
1187 int ruleno = crush.get_rule_id(rule_name);
1188 ceph_assert(ruleno >= 0);
1189 int r = crush.remove_rule(ruleno);
1190 if (r < 0) {
1191 cerr << "fail to remove rule " << rule_name << std::endl;
1192 return EXIT_FAILURE;
1193 }
1194 modified = true;
1195 }
1196
1197 if (reweight) {
1198 crush.reweight(g_ceph_context);
1199 modified = true;
1200 }
1201 if (rebuild_class_roots) {
1202 int r = crush.rebuild_roots_with_classes(g_ceph_context);
1203 if (r < 0) {
1204 cerr << "failed to rebuidl roots with classes" << std::endl;
1205 return EXIT_FAILURE;
1206 }
1207 modified = true;
1208 }
1209
1210 for (auto& i : set_subtree_class) {
1211 crush.set_subtree_class(i.first, i.second);
1212 modified = true;
1213 }
1214 if (reclassify) {
1215 int r = crush.reclassify(
1216 g_ceph_context,
1217 cout,
1218 reclassify_root,
1219 reclassify_bucket);
1220 if (r < 0) {
1221 cerr << "failed to reclassify map" << std::endl;
1222 return EXIT_FAILURE;
1223 }
1224 modified = true;
1225 }
1226
1227 // display ---
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();
1231 p != loc.end();
1232 ++p) {
1233 cout << p->first << "\t" << p->second << std::endl;
1234 }
1235 }
1236
1237 if (tree) {
1238 crush.dump_tree(&cout, NULL, {}, true);
1239 }
1240
1241 if (bucket_tree) {
1242 if (bucket_name.empty()) {
1243 cerr << ": error bucket_name is empty" << std::endl;
1244 }
1245 else {
1246 set<int> osd_ids;
1247 crush.get_leaves(bucket_name.c_str(), &osd_ids);
1248 for (auto &id : osd_ids) {
1249 cout << "osd." << id << std::endl;
1250 }
1251 }
1252 }
1253
1254 if (dump) {
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());
1258 f->close_section();
1259 f->flush(cout);
1260 cout << "\n";
1261 }
1262
1263 if (decompile) {
1264 CrushCompiler cc(crush, cerr, verbose);
1265 if (!outfn.empty()) {
1266 ofstream o;
1267 o.open(outfn.c_str(), ios::out | ios::binary | ios::trunc);
1268 if (!o.is_open()) {
1269 cerr << me << ": error writing '" << outfn << "'" << std::endl;
1270 return EXIT_FAILURE;
1271 }
1272 cc.decompile(o);
1273 o.close();
1274 } else {
1275 cc.decompile(cout);
1276 }
1277 }
1278
1279 if (check) {
1280 if (max_id >= 0) {
1281 if (!tester.check_name_maps(max_id)) {
1282 return EXIT_FAILURE;
1283 }
1284 }
1285 }
1286
1287 if (test) {
1288 if (tester.get_output_utilization_all() ||
1289 tester.get_output_utilization())
1290 tester.set_output_statistics(true);
1291
1292 int r = tester.test(cct->get());
1293 if (r < 0)
1294 return EXIT_FAILURE;
1295 }
1296
1297 if (compare.size()) {
1298 CrushWrapper crush2;
1299 bufferlist in;
1300 string error;
1301 int r = in.read_file(compare.c_str(), &error);
1302 if (r < 0) {
1303 cerr << me << ": error reading '" << compare << "': "
1304 << error << std::endl;
1305 return EXIT_FAILURE;
1306 }
1307 auto p = in.cbegin();
1308 try {
1309 crush2.decode(p);
1310 } catch(...) {
1311 cerr << me << ": unable to decode " << compare << std::endl;
1312 return EXIT_FAILURE;
1313 }
1314 r = tester.compare(crush2);
1315 if (r < 0)
1316 return EXIT_FAILURE;
1317 }
1318
1319 // output ---
1320 if (modified) {
1321 crush.finalize();
1322
1323 if (outfn.empty()) {
1324 cout << me << " successfully built or modified map. Use '-o <file>' to write it out." << std::endl;
1325 } else {
1326 bufferlist bl;
1327 crush.encode(bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
1328 int r = bl.write_file(outfn.c_str());
1329 if (r < 0) {
1330 cerr << me << ": error writing '" << outfn << "': " << cpp_strerror(r) << std::endl;
1331 return EXIT_FAILURE;
1332 }
1333 if (verbose)
1334 cout << "wrote crush map to " << outfn << std::endl;
1335 }
1336 }
1337
1338 return 0;
1339 }
1340 /*
1341 * Local Variables:
1342 * compile-command: "cd .. ; make crushtool && test/run-cli-tests"
1343 * End:
1344 */