]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/crushtool.cc
import 15.2.0 Octopus source
[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
45 const char *infn = "stdin";
46
47 static int get_fd_data(int fd, bufferlist &bl)
48 {
49
50 uint64_t total = 0;
51 do {
52 ssize_t bytes = bl.read_fd(fd, 1024*1024);
53 if (bytes < 0) {
54 cerr << "read_fd error " << cpp_strerror(-bytes) << "\n";
55 return -1;
56 }
57
58 if (bytes == 0)
59 break;
60
61 total += bytes;
62 } while(true);
63
64 ceph_assert(bl.length() == total);
65 return 0;
66 }
67
68 ////////////////////////////////////////////////////////////////////////////
69
70 void data_analysis_usage()
71 {
72 cout << "data output from testing routine ...\n";
73 cout << " absolute_weights\n";
74 cout << " the decimal weight of each OSD\n";
75 cout << " data layout: ROW MAJOR\n";
76 cout << " OSD id (int), weight (int)\n";
77 cout << " batch_device_expected_utilization_all\n";
78 cout << " the expected number of objects each OSD should receive per placement batch\n";
79 cout << " which may be a decimal value\n";
80 cout << " data layout: COLUMN MAJOR\n";
81 cout << " round (int), objects expected on OSD 0...OSD n (float)\n";
82 cout << " batch_device_utilization_all\n";
83 cout << " the number of objects stored on each OSD during each placement round\n";
84 cout << " data layout: COLUMN MAJOR\n";
85 cout << " round (int), objects stored on OSD 0...OSD n (int)\n";
86 cout << " device_utilization_all\n";
87 cout << " the number of objects stored on each OSD at the end of placements\n";
88 cout << " data_layout: ROW MAJOR\n";
89 cout << " OSD id (int), objects stored (int), objects expected (float)\n";
90 cout << " device_utilization\n";
91 cout << " the number of objects stored on each OSD marked 'up' at the end of placements\n";
92 cout << " data_layout: ROW MAJOR\n";
93 cout << " OSD id (int), objects stored (int), objects expected (float)\n";
94 cout << " placement_information\n";
95 cout << " the map of input -> OSD\n";
96 cout << " data_layout: ROW MAJOR\n";
97 cout << " input (int), OSD's mapped (int)\n";
98 cout << " proportional_weights_all\n";
99 cout << " the proportional weight of each OSD specified in the CRUSH map\n";
100 cout << " data_layout: ROW MAJOR\n";
101 cout << " OSD id (int), proportional weight (float)\n";
102 cout << " proportional_weights\n";
103 cout << " the proportional weight of each 'up' OSD specified in the CRUSH map\n";
104 cout << " data_layout: ROW MAJOR\n";
105 cout << " OSD id (int), proportional weight (float)\n";
106 }
107
108 void usage()
109 {
110 cout << "usage: crushtool ...\n";
111 cout << "\n";
112 cout << "Display, modify and test a crush map\n";
113 cout << "\n";
114 cout << "There are five stages, running one after the other:\n";
115 cout << "\n";
116 cout << " - input/build\n";
117 cout << " - tunables adjustments\n";
118 cout << " - modifications\n";
119 cout << " - display/test\n";
120 cout << " - output\n";
121 cout << "\n";
122 cout << "Options that are not specific to a stage.\n";
123 cout << "\n";
124 cout << " [--infn|-i infile]\n";
125 cout << " read the crush map from infile\n";
126 cout << "\n";
127 cout << "Options for the input/build stage\n";
128 cout << "\n";
129 cout << " --decompile|-d map decompile a crush map to source\n";
130 cout << " [--outfn|-o outfile]\n";
131 cout << " specify output for for (de)compilation\n";
132 cout << " --compile|-c map.txt compile a map from source\n";
133 cout << " --enable-unsafe-tunables\n";
134 cout << " compile with unsafe tunables\n";
135 cout << " --build --num_osds N layer1 ...\n";
136 cout << " build a new map, where each 'layer' is\n";
137 cout << " 'name (uniform|straw2|straw|list|tree) size'\n";
138 cout << "\n";
139 cout << "Options for the tunables adjustments stage\n";
140 cout << "\n";
141 cout << " --set-choose-local-tries N\n";
142 cout << " set choose local retries before re-descent\n";
143 cout << " --set-choose-local-fallback-tries N\n";
144 cout << " set choose local retries using fallback\n";
145 cout << " permutation before re-descent\n";
146 cout << " --set-choose-total-tries N\n";
147 cout << " set choose total descent attempts\n";
148 cout << " --set-chooseleaf-descend-once <0|1>\n";
149 cout << " set chooseleaf to (not) retry the recursive descent\n";
150 cout << " --set-chooseleaf-vary-r <0|1>\n";
151 cout << " set chooseleaf to (not) vary r based on parent\n";
152 cout << " --set-chooseleaf-stable <0|1>\n";
153 cout << " set chooseleaf firstn to (not) return stable results\n";
154 cout << "\n";
155 cout << "Options for the modifications stage\n";
156 cout << "\n";
157 cout << " -i mapfn --add-item id weight name [--loc type name ...]\n";
158 cout << " insert an item into the hierarchy at the\n";
159 cout << " given location\n";
160 cout << " -i mapfn --update-item id weight name [--loc type name ...]\n";
161 cout << " insert or move an item into the hierarchy at the\n";
162 cout << " given location\n";
163 cout << " -i mapfn --remove-item name\n"
164 << " remove the given item\n";
165 cout << " -i mapfn --reweight-item name weight\n";
166 cout << " reweight a given item (and adjust ancestor\n"
167 << " weights as needed)\n";
168 cout << " -i mapfn --add-bucket name type [--loc type name ...]\n"
169 << " insert a bucket into the hierarchy at the given\n"
170 << " location\n";
171 cout << " -i mapfn --move name --loc type name ...\n"
172 << " move the given item to specified location\n";
173 cout << " -i mapfn --reweight recalculate all bucket weights\n";
174 cout << " -i mapfn --rebuild-class-roots\n";
175 cout << " rebuild the per-class shadow trees (normally a no-op)\n";
176 cout << " -i mapfn --create-simple-rule name root type mode\n"
177 << " create crush rule <name> to start from <root>,\n"
178 << " replicate across buckets of type <type>, using\n"
179 << " a choose mode of <firstn|indep>\n";
180 cout << " -i mapfn --create-replicated-rule name root type\n"
181 << " create crush rule <name> to start from <root>,\n"
182 << " replicate across buckets of type <type>\n";
183 cout << " --device-class <class>\n";
184 cout << " use device class <class> for new rule\n";
185 cout << " -i mapfn --remove-rule name\n"
186 << " remove the specified crush rule\n";
187 cout << "\n";
188 cout << "Options for the display/test stage\n";
189 cout << "\n";
190 cout << " -f --format the format of --dump, defaults to json-pretty\n";
191 cout << " can be one of json, json-pretty, xml, xml-pretty,\n";
192 cout << " table, table-kv, html, html-pretty\n";
193 cout << " --dump dump the crush map\n";
194 cout << " --tree print map summary as a tree\n";
195 cout << " --bucket-tree print bucket map summary as a tree\n";
196 cout << " --bucket-name specify bucket bucket name for bucket-tree\n";
197 cout << " --check [max_id] check if any item is referencing an unknown name/type\n";
198 cout << " -i mapfn --show-location id\n";
199 cout << " show location for given device id\n";
200 cout << " -i mapfn --test test a range of inputs on the map\n";
201 cout << " [--min-x x] [--max-x x] [--x x]\n";
202 cout << " [--min-rule r] [--max-rule r] [--rule r] [--ruleset rs]\n";
203 cout << " [--num-rep n]\n";
204 cout << " [--pool-id n] specifies pool id\n";
205 cout << " [--batches b] split the CRUSH mapping into b > 1 rounds\n";
206 cout << " [--weight|-w devno weight]\n";
207 cout << " where weight is 0 to 1.0\n";
208 cout << " [--simulate] simulate placements using a random\n";
209 cout << " number generator in place of the CRUSH\n";
210 cout << " algorithm\n";
211 cout << " --show-utilization show OSD usage\n";
212 cout << " --show-utilization-all\n";
213 cout << " include zero weight items\n";
214 cout << " --show-statistics show chi squared statistics\n";
215 cout << " --show-mappings show mappings\n";
216 cout << " --show-bad-mappings show bad mappings\n";
217 cout << " --show-choose-tries show choose tries histogram\n";
218 cout << " --output-name name\n";
219 cout << " prepend the data file(s) generated during the\n";
220 cout << " testing routine with name\n";
221 cout << " --output-csv\n";
222 cout << " export select data generated during testing routine\n";
223 cout << " to CSV files for off-line post-processing\n";
224 cout << " use --help-output for more information\n";
225 cout << " --reclassify transform legacy CRUSH map buckets and rules\n";
226 cout << " by adding classes\n";
227 cout << " --reclassify-bucket <bucket-match> <class> <default-parent>\n";
228 cout << " --reclassify-root <bucket-name> <class>\n";
229 cout << " --set-subtree-class <bucket-name> <class>\n";
230 cout << " set class for all items beneath bucket-name\n";
231 cout << " --compare <otherfile> compare two maps using --test parameters\n";
232 cout << "\n";
233 cout << "Options for the output stage\n";
234 cout << "\n";
235 cout << " [--outfn|-o outfile]\n";
236 cout << " specify output for modified crush map\n";
237 cout << "\n";
238 }
239
240 struct bucket_types_t {
241 const char *name;
242 int type;
243 } bucket_types[] = {
244 { "uniform", CRUSH_BUCKET_UNIFORM },
245 { "list", CRUSH_BUCKET_LIST },
246 { "straw", CRUSH_BUCKET_STRAW },
247 { "straw2", CRUSH_BUCKET_STRAW2 },
248 { "tree", CRUSH_BUCKET_TREE },
249 { 0, 0 },
250 };
251
252 struct layer_t {
253 const char *name;
254 const char *buckettype;
255 int size;
256 };
257
258 template<typename... Args>
259 bool argparse_withargs(std::vector<const char*> &args,
260 std::vector<const char*>::iterator& i,
261 std::ostream& oss,
262 const char* opt,
263 Args*... opts)
264 {
265 if (!ceph_argparse_flag(args, i, opt, nullptr)) {
266 return false;
267 }
268 auto parse = [&](auto& opt) {
269 if (i == args.end()) {
270 oss << "expecting additional argument to " << opt;
271 return false;
272 }
273 using opt_t = std::remove_pointer_t<decay_t<decltype(opt)>>;
274 string err;
275 if constexpr (std::is_same_v<opt_t, string>) {
276 opt->assign(*i);
277 } else if constexpr (is_same_v<opt_t, int>) {
278 *opt = strict_strtol(*i, 10, &err);
279 } else if constexpr (is_same_v<opt_t, float>) {
280 *opt = strict_strtof(*i, &err);
281 }
282 i = args.erase(i);
283 if (err.empty())
284 return true;
285 else {
286 oss << err;
287 return false;
288 }
289 };
290 (... && parse(opts));
291 return true;
292 }
293
294 int do_add_bucket(CephContext* cct,
295 const char* me,
296 CrushWrapper& crush,
297 const string& add_name,
298 const string& add_type,
299 const map<string,string>& add_loc) {
300 int bucketno;
301 if (crush.name_exists(add_name)) {
302 cerr << me << " bucket '" << add_name << "' already exists" << std::endl;
303 return -EEXIST;
304 }
305 int type = crush.get_type_id(add_type);
306 if (type <= 0) {
307 cerr << me << " bad bucket type: " << add_type << std::endl;
308 return -EINVAL;
309 }
310 if (int r = crush.add_bucket(0, 0, CRUSH_HASH_DEFAULT, type, 0, nullptr, nullptr, &bucketno);
311 r < 0) {
312 cerr << me << " unable to add bucket: " << cpp_strerror(r) << std::endl;
313 return r;
314 }
315 if (int r = crush.set_item_name(bucketno, add_name); r < 0) {
316 cerr << me << " bad bucket name: " << add_name << std::endl;
317 return r;
318 }
319 if (!add_loc.empty()) {
320 if (!crush.check_item_loc(cct, bucketno, add_loc, (int*)nullptr)) {
321 if (int r = crush.move_bucket(cct, bucketno, add_loc); r < 0) {
322 cerr << me << " error moving bucket '" << add_name << "' to " << add_loc << std::endl;
323 return r;
324 }
325 }
326 }
327 return 0;
328 }
329
330 // return 1 for no change, 0 for successful change, negative on error
331 int do_move_item(CephContext* cct,
332 const char *me,
333 CrushWrapper& crush,
334 const string& name,
335 const map<string,string>& loc)
336 {
337 if (!crush.name_exists(name)) {
338 cerr << me << " item '" << name << "' does not exist" << std::endl;
339 return -ENOENT;
340 }
341 int id = crush.get_item_id(name);
342 if (loc.empty()) {
343 cerr << me << " expecting additional --loc argument to --move" << std::endl;
344 return -EINVAL;
345 }
346 if (crush.check_item_loc(cct, id, loc, (int*)nullptr)) {
347 // it's already there
348 cerr << me << " item '" << name << "' already at " << loc << std::endl;
349 return 1;
350 }
351 if (id >= 0) {
352 switch (int r = crush.create_or_move_item(cct, id, 0, name, loc)) {
353 case 0:
354 return 1;
355 case 1:
356 return 0;
357 default:
358 return r;
359 }
360 } else {
361 return crush.move_bucket(cct, id, loc);
362 }
363 }
364
365 int main(int argc, const char **argv)
366 {
367 vector<const char*> args;
368 argv_to_vec(argc, argv, args);
369 if (args.empty()) {
370 cerr << argv[0] << ": -h or --help for usage" << std::endl;
371 exit(1);
372 }
373 if (ceph_argparse_need_usage(args)) {
374 usage();
375 exit(0);
376 }
377
378 const char *me = argv[0];
379
380 std::string infn, srcfn, outfn, add_name, add_type, remove_name,
381 reweight_name, bucket_name;
382 std::string move_name;
383 bool compile = false;
384 bool decompile = false;
385 bool check = false;
386 int max_id = -1;
387 bool test = false;
388 bool display = false;
389 bool tree = false;
390 bool bucket_tree = false;
391 string dump_format = "json-pretty";
392 bool dump = false;
393 int full_location = -1;
394 bool write_to_file = false;
395 int verbose = 0;
396 bool unsafe_tunables = false;
397
398 bool rebuild_class_roots = false;
399
400 bool reweight = false;
401 int add_item = -1;
402 bool add_bucket = false;
403 bool update_item = false;
404 bool move_item = false;
405 bool add_rule = false;
406 std::string rule_name, rule_root, rule_type, rule_mode, rule_device_class;
407 bool del_rule = false;
408 float add_weight = 0;
409 map<string,string> add_loc;
410 float reweight_weight = 0;
411
412 bool adjust = false;
413
414 int build = 0;
415 int num_osds =0;
416 vector<layer_t> layers;
417
418 int choose_local_tries = -1;
419 int choose_local_fallback_tries = -1;
420 int choose_total_tries = -1;
421 int chooseleaf_descend_once = -1;
422 int chooseleaf_vary_r = -1;
423 int chooseleaf_stable = -1;
424 int straw_calc_version = -1;
425 int allowed_bucket_algs = -1;
426
427 bool reclassify = false;
428 map<string,pair<string,string>> reclassify_bucket; // %suffix or prefix% -> class, default_root
429 map<string,string> reclassify_root; // bucket -> class
430 map<string,string> set_subtree_class; // bucket -> class
431
432 string compare;
433
434 CrushWrapper crush;
435
436 CrushTester tester(crush, cout);
437
438 // we use -c, don't confuse the generic arg parsing
439 // only parse arguments from CEPH_ARGS, if in the environment
440 vector<const char *> empty_args;
441 auto cct = global_init(NULL, empty_args, CEPH_ENTITY_TYPE_CLIENT,
442 CODE_ENVIRONMENT_UTILITY,
443 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
444 // crushtool times out occasionally when quits. so do not
445 // release the g_ceph_context.
446 cct->get();
447 common_init_finish(g_ceph_context);
448
449 int x;
450 float y;
451 long long z;
452
453 std::string val;
454 std::ostringstream err;
455 int tmp;
456 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
457 if (ceph_argparse_double_dash(args, i)) {
458 break;
459 } else if (ceph_argparse_witharg(args, i, &val, "-d", "--decompile", (char*)NULL)) {
460 infn = val;
461 decompile = true;
462 } else if (ceph_argparse_witharg(args, i, &val, "-i", "--infn", (char*)NULL)) {
463 infn = val;
464 } else if (ceph_argparse_witharg(args, i, &val, "-o", "--outfn", (char*)NULL)) {
465 outfn = val;
466 } else if (ceph_argparse_flag(args, i, "-v", "--verbose", (char*)NULL)) {
467 verbose += 1;
468 } else if (ceph_argparse_witharg(args, i, &val, "--compare", (char*)NULL)) {
469 compare = val;
470 } else if (ceph_argparse_flag(args, i, "--reclassify", (char*)NULL)) {
471 reclassify = true;
472 } else if (ceph_argparse_witharg(args, i, &val, "--reclassify-bucket",
473 (char*)NULL)) {
474 if (i == args.end()) {
475 cerr << "expecting additional argument" << std::endl;
476 return EXIT_FAILURE;
477 }
478 string c = *i;
479 i = args.erase(i);
480 if (i == args.end()) {
481 cerr << "expecting additional argument" << std::endl;
482 return EXIT_FAILURE;
483 }
484 reclassify_bucket[val] = make_pair(c, *i);
485 i = args.erase(i);
486 } else if (ceph_argparse_witharg(args, i, &val, "--reclassify-root",
487 (char*)NULL)) {
488 if (i == args.end()) {
489 cerr << "expecting additional argument" << std::endl;
490 return EXIT_FAILURE;
491 }
492 reclassify_root[val] = *i;
493 i = args.erase(i);
494 } else if (ceph_argparse_witharg(args, i, &val, "--set-subtree-class",
495 (char*)NULL)) {
496 if (i == args.end()) {
497 cerr << "expecting additional argument" << std::endl;
498 return EXIT_FAILURE;
499 }
500 set_subtree_class[val] = *i;
501 i = args.erase(i);
502 } else if (ceph_argparse_flag(args, i, "--tree", (char*)NULL)) {
503 tree = true;
504 } else if (ceph_argparse_flag(args, i, "--bucket-tree", (char*)NULL)) {
505 bucket_tree = true;
506 } else if (ceph_argparse_witharg(args, i, &val, "-b", "--bucket-name", (char*)NULL)) {
507 bucket_name = val;
508 } else if (ceph_argparse_witharg(args, i, &val, "-f", "--format", (char*)NULL)) {
509 dump_format = val;
510 } else if (ceph_argparse_flag(args, i, "--dump", (char*)NULL)) {
511 dump = true;
512 } else if (ceph_argparse_flag(args, i, "--show_utilization", (char*)NULL)) {
513 display = true;
514 tester.set_output_utilization(true);
515 } else if (ceph_argparse_flag(args, i, "--show_utilization_all", (char*)NULL)) {
516 display = true;
517 tester.set_output_utilization_all(true);
518 } else if (ceph_argparse_flag(args, i, "--show_statistics", (char*)NULL)) {
519 display = true;
520 tester.set_output_statistics(true);
521 } else if (ceph_argparse_flag(args, i, "--show_mappings", (char*)NULL)) {
522 display = true;
523 tester.set_output_mappings(true);
524 } else if (ceph_argparse_flag(args, i, "--show_bad_mappings", (char*)NULL)) {
525 display = true;
526 tester.set_output_bad_mappings(true);
527 } else if (ceph_argparse_flag(args, i, "--show_choose_tries", (char*)NULL)) {
528 display = true;
529 tester.set_output_choose_tries(true);
530 } else if (ceph_argparse_witharg(args, i, &val, "-c", "--compile", (char*)NULL)) {
531 srcfn = val;
532 compile = true;
533 } else if (ceph_argparse_witharg(args, i, &max_id, err, "--check", (char*)NULL)) {
534 check = true;
535 } else if (ceph_argparse_flag(args, i, "-t", "--test", (char*)NULL)) {
536 test = true;
537 } else if (ceph_argparse_witharg(args, i, &full_location, err, "--show-location", (char*)NULL)) {
538 } else if (ceph_argparse_flag(args, i, "-s", "--simulate", (char*)NULL)) {
539 tester.set_random_placement();
540 } else if (ceph_argparse_flag(args, i, "--enable-unsafe-tunables", (char*)NULL)) {
541 unsafe_tunables = true;
542 } else if (ceph_argparse_witharg(args, i, &choose_local_tries, err,
543 "--set_choose_local_tries", (char*)NULL)) {
544 adjust = true;
545 } else if (ceph_argparse_witharg(args, i, &choose_local_fallback_tries, err,
546 "--set_choose_local_fallback_tries", (char*)NULL)) {
547 adjust = true;
548 } else if (ceph_argparse_witharg(args, i, &choose_total_tries, err,
549 "--set_choose_total_tries", (char*)NULL)) {
550 adjust = true;
551 } else if (ceph_argparse_witharg(args, i, &chooseleaf_descend_once, err,
552 "--set_chooseleaf_descend_once", (char*)NULL)) {
553 adjust = true;
554 } else if (ceph_argparse_witharg(args, i, &chooseleaf_vary_r, err,
555 "--set_chooseleaf_vary_r", (char*)NULL)) {
556 adjust = true;
557 } else if (ceph_argparse_witharg(args, i, &chooseleaf_stable, err,
558 "--set_chooseleaf_stable", (char*)NULL)) {
559 adjust = true;
560 } else if (ceph_argparse_witharg(args, i, &straw_calc_version, err,
561 "--set_straw_calc_version", (char*)NULL)) {
562 adjust = true;
563 } else if (ceph_argparse_witharg(args, i, &allowed_bucket_algs, err,
564 "--set_allowed_bucket_algs", (char*)NULL)) {
565 adjust = true;
566 } else if (ceph_argparse_flag(args, i, "--reweight", (char*)NULL)) {
567 reweight = true;
568 } else if (ceph_argparse_flag(args, i, "--rebuild-class-roots", (char*)NULL)) {
569 rebuild_class_roots = true;
570 } else if (ceph_argparse_witharg(args, i, &add_item, err, "--add_item", (char*)NULL)) {
571 if (!err.str().empty()) {
572 cerr << err.str() << std::endl;
573 return EXIT_FAILURE;
574 }
575 if (i == args.end()) {
576 cerr << "expecting additional argument to --add-item" << std::endl;
577 return EXIT_FAILURE;
578 }
579 add_weight = atof(*i);
580 i = args.erase(i);
581 if (i == args.end()) {
582 cerr << "expecting additional argument to --add-item" << std::endl;
583 return EXIT_FAILURE;
584 }
585 add_name.assign(*i);
586 i = args.erase(i);
587 } else if (ceph_argparse_witharg(args, i, &add_item, err, "--update_item", (char*)NULL)) {
588 update_item = true;
589 if (!err.str().empty()) {
590 cerr << err.str() << std::endl;
591 return EXIT_FAILURE;
592 }
593 if (i == args.end()) {
594 cerr << "expecting additional argument to --update-item" << std::endl;
595 return EXIT_FAILURE;
596 }
597 add_weight = atof(*i);
598 i = args.erase(i);
599 if (i == args.end()) {
600 cerr << "expecting additional argument to --update-item" << std::endl;
601 return EXIT_FAILURE;
602 }
603 add_name.assign(*i);
604 i = args.erase(i);
605 } else if (argparse_withargs(args, i, err, "--add-bucket",
606 &add_name, &add_type)) {
607 if (!err.str().empty()) {
608 cerr << err.str() << std::endl;
609 return EXIT_FAILURE;
610 }
611 add_bucket = true;
612 } else if (argparse_withargs(args, i, err, "--move",
613 &move_name)) {
614 if (!err.str().empty()) {
615 cerr << err.str() << std::endl;
616 return EXIT_FAILURE;
617 }
618 move_item = true;
619 } else if (ceph_argparse_witharg(args, i, &val, err, "--create-simple-rule", (char*)NULL)) {
620 rule_name.assign(val);
621 if (!err.str().empty()) {
622 cerr << err.str() << std::endl;
623 return EXIT_FAILURE;
624 }
625 if (i == args.end()) {
626 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
627 return EXIT_FAILURE;
628 }
629
630 rule_root.assign(*i);
631 i = args.erase(i);
632 if (i == args.end()) {
633 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
634 return EXIT_FAILURE;
635 }
636
637 rule_type.assign(*i);
638 i = args.erase(i);
639 if (i == args.end()) {
640 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
641 return EXIT_FAILURE;
642 }
643
644 rule_mode.assign(*i);
645 i = args.erase(i);
646
647 cout << "--create-simple-rule:"
648 << " name=" << rule_name
649 << " root=" << rule_root
650 << " type=" << rule_type
651 << " mode=" << rule_mode
652 << std::endl;
653 add_rule = true;
654 } else if (ceph_argparse_witharg(args, i, &val, err, "--create-replicated-rule", (char*)NULL)) {
655 rule_name.assign(val);
656 if (!err.str().empty()) {
657 cerr << err.str() << std::endl;
658 return EXIT_FAILURE;
659 }
660 if (i == args.end()) {
661 cerr << "expecting additional argument to --create-replicated-rule" << std::endl;
662 return EXIT_FAILURE;
663 }
664
665 rule_root.assign(*i);
666 i = args.erase(i);
667 if (i == args.end()) {
668 cerr << "expecting additional argument to --create-replicated-rule" << std::endl;
669 return EXIT_FAILURE;
670 }
671
672 rule_type.assign(*i);
673 i = args.erase(i);
674 rule_mode = "firstn";
675
676 cout << "--create-replicated-rule:"
677 << " name=" << rule_name
678 << " root=" << rule_root
679 << " type=" << rule_type
680 << std::endl;
681 add_rule = true;
682
683 } else if (ceph_argparse_witharg(args, i, &val, "--device-class", (char*)NULL)) {
684 rule_device_class.assign(val);
685 if (!err.str().empty()) {
686 cerr << err.str() << std::endl;
687 return EXIT_FAILURE;
688 }
689 } else if (ceph_argparse_witharg(args, i, &val, "--remove-rule", (char*)NULL)) {
690 rule_name.assign(val);
691 if (!err.str().empty()) {
692 cerr << err.str() << std::endl;
693 return EXIT_FAILURE;
694 }
695 del_rule = true;
696 } else if (ceph_argparse_witharg(args, i, &val, "--loc", (char*)NULL)) {
697 std::string type(val);
698 if (i == args.end()) {
699 cerr << "expecting additional argument to --loc" << std::endl;
700 return EXIT_FAILURE;
701 }
702 std::string name(*i);
703 i = args.erase(i);
704 add_loc[type] = name;
705 } else if (ceph_argparse_flag(args, i, "--output-csv", (char*)NULL)) {
706 write_to_file = true;
707 tester.set_output_data_file(true);
708 tester.set_output_csv(true);
709 } else if (ceph_argparse_flag(args, i, "--help-output", (char*)NULL)) {
710 data_analysis_usage();
711 return EXIT_SUCCESS;
712 } else if (ceph_argparse_witharg(args, i, &val, "--output-name", (char*)NULL)) {
713 std::string name(val);
714 if (i == args.end()) {
715 cerr << "expecting additional argument to --output-name" << std::endl;
716 return EXIT_FAILURE;
717 }
718 else {
719 tester.set_output_data_file_name(name + "-");
720 }
721 } else if (ceph_argparse_witharg(args, i, &val, "--remove_item", (char*)NULL)) {
722 remove_name = val;
723 } else if (ceph_argparse_witharg(args, i, &val, "--reweight_item", (char*)NULL)) {
724 reweight_name = val;
725 if (i == args.end()) {
726 cerr << "expecting additional argument to --reweight-item" << std::endl;
727 return EXIT_FAILURE;
728 }
729 reweight_weight = atof(*i);
730 i = args.erase(i);
731 } else if (ceph_argparse_flag(args, i, "--build", (char*)NULL)) {
732 build = true;
733 } else if (ceph_argparse_witharg(args, i, &num_osds, err, "--num_osds", (char*)NULL)) {
734 if (!err.str().empty()) {
735 cerr << err.str() << std::endl;
736 return EXIT_FAILURE;
737 }
738 } else if (ceph_argparse_witharg(args, i, &x, err, "--num_rep", (char*)NULL)) {
739 if (!err.str().empty()) {
740 cerr << err.str() << std::endl;
741 return EXIT_FAILURE;
742 }
743 tester.set_num_rep(x);
744 } else if (ceph_argparse_witharg(args, i, &x, err, "--max_x", (char*)NULL)) {
745 if (!err.str().empty()) {
746 cerr << err.str() << std::endl;
747 return EXIT_FAILURE;
748 }
749 tester.set_max_x(x);
750 } else if (ceph_argparse_witharg(args, i, &x, err, "--min_x", (char*)NULL)) {
751 if (!err.str().empty()) {
752 cerr << err.str() << std::endl;
753 return EXIT_FAILURE;
754 }
755 tester.set_min_x(x);
756 } else if (ceph_argparse_witharg(args, i, &z, err, "--pool_id", (char*)NULL)) {
757 if (!err.str().empty()) {
758 cerr << err.str() << std::endl;
759 return EXIT_FAILURE;
760 }
761 tester.set_pool_id(z);
762 } else if (ceph_argparse_witharg(args, i, &x, err, "--x", (char*)NULL)) {
763 if (!err.str().empty()) {
764 cerr << err.str() << std::endl;
765 return EXIT_FAILURE;
766 }
767 tester.set_x(x);
768 } else if (ceph_argparse_witharg(args, i, &x, err, "--max_rule", (char*)NULL)) {
769 if (!err.str().empty()) {
770 cerr << err.str() << std::endl;
771 return EXIT_FAILURE;
772 }
773 tester.set_max_rule(x);
774 } else if (ceph_argparse_witharg(args, i, &x, err, "--min_rule", (char*)NULL)) {
775 if (!err.str().empty()) {
776 cerr << err.str() << std::endl;
777 return EXIT_FAILURE;
778 }
779 tester.set_min_rule(x);
780 } else if (ceph_argparse_witharg(args, i, &x, err, "--rule", (char*)NULL)) {
781 if (!err.str().empty()) {
782 cerr << err.str() << std::endl;
783 return EXIT_FAILURE;
784 }
785 tester.set_rule(x);
786 } else if (ceph_argparse_witharg(args, i, &x, err, "--ruleset", (char*)NULL)) {
787 if (!err.str().empty()) {
788 cerr << err.str() << std::endl;
789 return EXIT_FAILURE;
790 }
791 tester.set_ruleset(x);
792 } else if (ceph_argparse_witharg(args, i, &x, err, "--batches", (char*)NULL)) {
793 if (!err.str().empty()) {
794 cerr << err.str() << std::endl;
795 return EXIT_FAILURE;
796 }
797 tester.set_batches(x);
798 } else if (ceph_argparse_witharg(args, i, &y, err, "--mark-down-ratio", (char*)NULL)) {
799 if (!err.str().empty()) {
800 cerr << err.str() << std::endl;
801 return EXIT_FAILURE;
802 }
803 tester.set_device_down_ratio(y);
804 } else if (ceph_argparse_witharg(args, i, &y, err, "--mark-down-bucket-ratio", (char*)NULL)) {
805 if (!err.str().empty()) {
806 cerr << err.str() << std::endl;
807 return EXIT_FAILURE;
808 }
809 tester.set_bucket_down_ratio(y);
810 } else if (ceph_argparse_witharg(args, i, &tmp, err, "--weight", (char*)NULL)) {
811 if (!err.str().empty()) {
812 cerr << err.str() << std::endl;
813 return EXIT_FAILURE;
814 }
815 int dev = tmp;
816 if (i == args.end()) {
817 cerr << "expecting additional argument to --weight" << std::endl;
818 return EXIT_FAILURE;
819 }
820 float f = atof(*i);
821 i = args.erase(i);
822 tester.set_device_weight(dev, f);
823 }
824 else {
825 ++i;
826 }
827 }
828
829 if (test && !check && !display && !write_to_file && compare.empty()) {
830 cerr << "WARNING: no output selected; use --output-csv or --show-X" << std::endl;
831 }
832
833 if (decompile + compile + build > 1) {
834 cerr << "cannot specify more than one of compile, decompile, and build" << std::endl;
835 return EXIT_FAILURE;
836 }
837 if (!check && !compile && !decompile && !build && !test && !reweight && !adjust && !tree && !dump &&
838 add_item < 0 && !add_bucket && !move_item && !add_rule && !del_rule && full_location < 0 &&
839 !bucket_tree &&
840 !reclassify && !rebuild_class_roots &&
841 compare.empty() &&
842
843 remove_name.empty() && reweight_name.empty()) {
844 cerr << "no action specified; -h for help" << std::endl;
845 return EXIT_FAILURE;
846 }
847 if ((!build) && (!args.empty())) {
848 cerr << "unrecognized arguments: " << args << std::endl;
849 return EXIT_FAILURE;
850 }
851 else {
852 if ((args.size() % 3) != 0U) {
853 cerr << "remaining args: " << args << std::endl;
854 cerr << "layers must be specified with 3-tuples of (name, buckettype, size)"
855 << std::endl;
856 return EXIT_FAILURE;
857 }
858 for (size_t j = 0; j < args.size(); j += 3) {
859 layer_t l;
860 l.name = args[j];
861 l.buckettype = args[j+1];
862 l.size = atoi(args[j+2]);
863 layers.push_back(l);
864 }
865 }
866
867 /*
868 if (outfn) cout << "outfn " << outfn << std::endl;
869 if (cinfn) cout << "cinfn " << cinfn << std::endl;
870 if (dinfn) cout << "dinfn " << dinfn << std::endl;
871 */
872
873 bool modified = false;
874
875 // input ----
876
877 if (!infn.empty()) {
878 bufferlist bl;
879 std::string error;
880
881 int r = 0;
882 if (infn == "-") {
883 if (isatty(STDIN_FILENO)) {
884 cerr << "stdin must not be from a tty" << std::endl;
885 return EXIT_FAILURE;
886 }
887 r = get_fd_data(STDIN_FILENO, bl);
888 if (r < 0) {
889 cerr << "error reading data from STDIN" << std::endl;
890 return EXIT_FAILURE;
891 }
892 } else {
893 r = bl.read_file(infn.c_str(), &error);
894 if (r < 0) {
895 cerr << me << ": error reading '" << infn << "': "
896 << error << std::endl;
897 return EXIT_FAILURE;
898 }
899 }
900 auto p = bl.cbegin();
901 try {
902 crush.decode(p);
903 } catch(...) {
904 cerr << me << ": unable to decode " << infn << std::endl;
905 return EXIT_FAILURE;
906 }
907 }
908
909 if (compile) {
910 crush.create();
911
912 // read the file
913 ifstream in(srcfn.c_str());
914 if (!in.is_open()) {
915 cerr << "input file " << srcfn << " not found" << std::endl;
916 return -ENOENT;
917 }
918
919 CrushCompiler cc(crush, cerr, verbose);
920 if (unsafe_tunables)
921 cc.enable_unsafe_tunables();
922 int r = cc.compile(in, srcfn.c_str());
923 if (r < 0)
924 return EXIT_FAILURE;
925
926 modified = true;
927 }
928
929 if (build) {
930 if (layers.empty()) {
931 cerr << me << ": must specify at least one layer" << std::endl;
932 return EXIT_FAILURE;
933 }
934
935 crush.create();
936
937 vector<int> lower_items;
938 vector<int> lower_weights;
939
940 crush.set_max_devices(num_osds);
941 for (int i=0; i<num_osds; i++) {
942 lower_items.push_back(i);
943 lower_weights.push_back(0x10000);
944 crush.set_item_name(i, "osd." + stringify(i));
945 }
946
947 crush.set_type_name(0, "osd");
948 int type = 1;
949 for (vector<layer_t>::iterator p = layers.begin(); p != layers.end(); ++p, type++) {
950 layer_t &l = *p;
951
952 dout(2) << "layer " << type
953 << " " << l.name
954 << " bucket type " << l.buckettype
955 << " " << l.size
956 << dendl;
957
958 crush.set_type_name(type, l.name);
959
960 int buckettype = -1;
961 for (int i = 0; bucket_types[i].name; i++)
962 if (l.buckettype && strcmp(l.buckettype, bucket_types[i].name) == 0) {
963 buckettype = bucket_types[i].type;
964 break;
965 }
966 if (buckettype < 0) {
967 cerr << "unknown bucket type '" << l.buckettype << "'" << std::endl;
968 return EXIT_FAILURE;
969 }
970
971 // build items
972 vector<int> cur_items;
973 vector<int> cur_weights;
974 unsigned lower_pos = 0; // lower pos
975
976 dout(2) << "lower_items " << lower_items << dendl;
977 dout(2) << "lower_weights " << lower_weights << dendl;
978
979 int i = 0;
980 while (1) {
981 if (lower_pos == lower_items.size())
982 break;
983
984 int items[num_osds];
985 int weights[num_osds];
986
987 int weight = 0;
988 int j;
989 for (j=0; j<l.size || l.size==0; j++) {
990 if (lower_pos == lower_items.size())
991 break;
992 items[j] = lower_items[lower_pos];
993 weights[j] = lower_weights[lower_pos];
994 weight += weights[j];
995 lower_pos++;
996 dout(2) << " item " << items[j] << " weight " << weights[j] << dendl;
997 }
998
999 int id;
1000 int r = crush.add_bucket(0, buckettype, CRUSH_HASH_DEFAULT, type, j, items, weights, &id);
1001 if (r < 0) {
1002 cerr << " Couldn't add bucket: " << cpp_strerror(r) << std::endl;
1003 return r;
1004 }
1005
1006 char format[20];
1007 format[sizeof(format)-1] = '\0';
1008 if (l.size)
1009 snprintf(format, sizeof(format)-1, "%s%%d", l.name);
1010 else
1011 strncpy(format, l.name, sizeof(format)-1);
1012 char name[20];
1013 snprintf(name, sizeof(name), format, i);
1014 crush.set_item_name(id, name);
1015
1016 dout(2) << " in bucket " << id << " '" << name << "' size " << j << " weight " << weight << dendl;
1017
1018 cur_items.push_back(id);
1019 cur_weights.push_back(weight);
1020 i++;
1021 }
1022
1023 lower_items.swap(cur_items);
1024 lower_weights.swap(cur_weights);
1025 }
1026
1027 string root = layers.back().size == 0 ? layers.back().name :
1028 string(layers.back().name) + "0";
1029
1030 {
1031 set<int> roots;
1032 crush.find_roots(&roots);
1033 if (roots.size() > 1) {
1034 cerr << "The crush rulesets will use the root " << root << "\n"
1035 << "and ignore the others.\n"
1036 << "There are " << roots.size() << " roots, they can be\n"
1037 << "grouped into a single root by appending something like:\n"
1038 << " root straw 0\n"
1039 << std::endl;
1040 }
1041 }
1042
1043 if (OSDMap::build_simple_crush_rules(g_ceph_context, crush, root, &cerr))
1044 return EXIT_FAILURE;
1045
1046 modified = true;
1047 }
1048
1049 // mutate ----
1050
1051 if (choose_local_tries >= 0) {
1052 crush.set_choose_local_tries(choose_local_tries);
1053 modified = true;
1054 }
1055 if (choose_local_fallback_tries >= 0) {
1056 crush.set_choose_local_fallback_tries(choose_local_fallback_tries);
1057 modified = true;
1058 }
1059 if (choose_total_tries >= 0) {
1060 crush.set_choose_total_tries(choose_total_tries);
1061 modified = true;
1062 }
1063 if (chooseleaf_descend_once >= 0) {
1064 crush.set_chooseleaf_descend_once(chooseleaf_descend_once);
1065 modified = true;
1066 }
1067 if (chooseleaf_vary_r >= 0) {
1068 crush.set_chooseleaf_vary_r(chooseleaf_vary_r);
1069 modified = true;
1070 }
1071 if (chooseleaf_stable >= 0) {
1072 crush.set_chooseleaf_stable(chooseleaf_stable);
1073 modified = true;
1074 }
1075 if (straw_calc_version >= 0) {
1076 crush.set_straw_calc_version(straw_calc_version);
1077 modified = true;
1078 }
1079 if (allowed_bucket_algs >= 0) {
1080 crush.set_allowed_bucket_algs(allowed_bucket_algs);
1081 modified = true;
1082 }
1083
1084 if (!reweight_name.empty()) {
1085 cout << me << " reweighting item " << reweight_name << " to " << reweight_weight << std::endl;
1086 int r;
1087 if (!crush.name_exists(reweight_name)) {
1088 cerr << " name " << reweight_name << " dne" << std::endl;
1089 r = -ENOENT;
1090 } else {
1091 int item = crush.get_item_id(reweight_name);
1092 r = crush.adjust_item_weightf(g_ceph_context, item, reweight_weight);
1093 }
1094 if (r >= 0)
1095 modified = true;
1096 else {
1097 cerr << me << " " << cpp_strerror(r) << std::endl;
1098 return r;
1099 }
1100 }
1101
1102 if (!remove_name.empty()) {
1103 cout << me << " removing item " << remove_name << std::endl;
1104 int r;
1105 if (!crush.name_exists(remove_name)) {
1106 cerr << " name " << remove_name << " dne" << std::endl;
1107 r = -ENOENT;
1108 } else {
1109 int remove_item = crush.get_item_id(remove_name);
1110 r = crush.remove_item(g_ceph_context, remove_item, false);
1111 }
1112 if (r == 0)
1113 modified = true;
1114 else {
1115 cerr << me << " " << cpp_strerror(r) << std::endl;
1116 return r;
1117 }
1118 }
1119
1120 if (add_item >= 0) {
1121 int r;
1122 if (update_item) {
1123 r = crush.update_item(g_ceph_context, add_item, add_weight, add_name.c_str(), add_loc);
1124 } else {
1125 r = crush.insert_item(g_ceph_context, add_item, add_weight, add_name.c_str(), add_loc);
1126 }
1127 if (r >= 0) {
1128 modified = true;
1129 } else {
1130 cerr << me << " " << cpp_strerror(r) << std::endl;
1131 return r;
1132 }
1133 }
1134
1135 if (add_bucket) {
1136 if (int r = do_add_bucket(cct.get(), me, crush, add_name, add_type, add_loc); !r) {
1137 modified = true;
1138 } else {
1139 return r;
1140 }
1141 }
1142
1143 if (move_item) {
1144 if (int r = do_move_item(cct.get(), me, crush, move_name, add_loc); !r) {
1145 modified = true;
1146 } else {
1147 return r;
1148 }
1149 }
1150 if (add_rule) {
1151 if (crush.rule_exists(rule_name)) {
1152 cerr << "rule " << rule_name << " already exists" << std::endl;
1153 return EXIT_FAILURE;
1154 }
1155 int r = crush.add_simple_rule(rule_name, rule_root, rule_type,
1156 rule_device_class,
1157 rule_mode, pg_pool_t::TYPE_REPLICATED, &err);
1158 if (r < 0) {
1159 cerr << err.str() << std::endl;
1160 return EXIT_FAILURE;
1161 }
1162 modified = true;
1163 }
1164
1165 if (del_rule) {
1166 if (!crush.rule_exists(rule_name)) {
1167 cerr << "rule " << rule_name << " does not exist" << std::endl;
1168 return 0;
1169 }
1170 int ruleno = crush.get_rule_id(rule_name);
1171 ceph_assert(ruleno >= 0);
1172 int r = crush.remove_rule(ruleno);
1173 if (r < 0) {
1174 cerr << "fail to remove rule " << rule_name << std::endl;
1175 return EXIT_FAILURE;
1176 }
1177 modified = true;
1178 }
1179
1180 if (reweight) {
1181 crush.reweight(g_ceph_context);
1182 modified = true;
1183 }
1184 if (rebuild_class_roots) {
1185 int r = crush.rebuild_roots_with_classes(g_ceph_context);
1186 if (r < 0) {
1187 cerr << "failed to rebuidl roots with classes" << std::endl;
1188 return EXIT_FAILURE;
1189 }
1190 modified = true;
1191 }
1192
1193 for (auto& i : set_subtree_class) {
1194 crush.set_subtree_class(i.first, i.second);
1195 modified = true;
1196 }
1197 if (reclassify) {
1198 int r = crush.reclassify(
1199 g_ceph_context,
1200 cout,
1201 reclassify_root,
1202 reclassify_bucket);
1203 if (r < 0) {
1204 cerr << "failed to reclassify map" << std::endl;
1205 return EXIT_FAILURE;
1206 }
1207 modified = true;
1208 }
1209
1210 // display ---
1211 if (full_location >= 0) {
1212 map<string, string> loc = crush.get_full_location(full_location);
1213 for (map<string,string>::iterator p = loc.begin();
1214 p != loc.end();
1215 ++p) {
1216 cout << p->first << "\t" << p->second << std::endl;
1217 }
1218 }
1219
1220 if (tree) {
1221 crush.dump_tree(&cout, NULL, {}, true);
1222 }
1223
1224 if (bucket_tree) {
1225 if (bucket_name.empty()) {
1226 cerr << ": error bucket_name is empty" << std::endl;
1227 }
1228 else {
1229 set<int> osd_ids;
1230 crush.get_leaves(bucket_name.c_str(), &osd_ids);
1231 for (auto &id : osd_ids) {
1232 cout << "osd." << id << std::endl;
1233 }
1234 }
1235 }
1236
1237 if (dump) {
1238 boost::scoped_ptr<Formatter> f(Formatter::create(dump_format, "json-pretty", "json-pretty"));
1239 f->open_object_section("crush_map");
1240 crush.dump(f.get());
1241 f->close_section();
1242 f->flush(cout);
1243 cout << "\n";
1244 }
1245
1246 if (decompile) {
1247 CrushCompiler cc(crush, cerr, verbose);
1248 if (!outfn.empty()) {
1249 ofstream o;
1250 o.open(outfn.c_str(), ios::out | ios::binary | ios::trunc);
1251 if (!o.is_open()) {
1252 cerr << me << ": error writing '" << outfn << "'" << std::endl;
1253 return EXIT_FAILURE;
1254 }
1255 cc.decompile(o);
1256 o.close();
1257 } else {
1258 cc.decompile(cout);
1259 }
1260 }
1261
1262 if (check) {
1263 tester.check_overlapped_rules();
1264 if (max_id >= 0) {
1265 if (!tester.check_name_maps(max_id)) {
1266 return EXIT_FAILURE;
1267 }
1268 }
1269 }
1270
1271 if (test) {
1272 if (tester.get_output_utilization_all() ||
1273 tester.get_output_utilization())
1274 tester.set_output_statistics(true);
1275
1276 int r = tester.test();
1277 if (r < 0)
1278 return EXIT_FAILURE;
1279 }
1280
1281 if (compare.size()) {
1282 CrushWrapper crush2;
1283 bufferlist in;
1284 string error;
1285 int r = in.read_file(compare.c_str(), &error);
1286 if (r < 0) {
1287 cerr << me << ": error reading '" << compare << "': "
1288 << error << std::endl;
1289 return EXIT_FAILURE;
1290 }
1291 auto p = in.cbegin();
1292 try {
1293 crush2.decode(p);
1294 } catch(...) {
1295 cerr << me << ": unable to decode " << compare << std::endl;
1296 return EXIT_FAILURE;
1297 }
1298 r = tester.compare(crush2);
1299 if (r < 0)
1300 return EXIT_FAILURE;
1301 }
1302
1303 // output ---
1304 if (modified) {
1305 crush.finalize();
1306
1307 if (outfn.empty()) {
1308 cout << me << " successfully built or modified map. Use '-o <file>' to write it out." << std::endl;
1309 } else {
1310 bufferlist bl;
1311 crush.encode(bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
1312 int r = bl.write_file(outfn.c_str());
1313 if (r < 0) {
1314 cerr << me << ": error writing '" << outfn << "': " << cpp_strerror(r) << std::endl;
1315 return EXIT_FAILURE;
1316 }
1317 if (verbose)
1318 cout << "wrote crush map to " << outfn << std::endl;
1319 }
1320 }
1321
1322 return 0;
1323 }
1324 /*
1325 * Local Variables:
1326 * compile-command: "cd .. ; make crushtool && test/run-cli-tests"
1327 * End:
1328 */