]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/crushtool.cc
update sources to v12.1.1
[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
25 #include "common/debug.h"
26 #include "common/errno.h"
27 #include "common/config.h"
28 #include "common/Formatter.h"
29
30 #include "common/ceph_argparse.h"
31 #include "include/stringify.h"
32 #include "global/global_context.h"
33 #include "global/global_init.h"
34 #include "osd/OSDMap.h"
35 #include "crush/CrushWrapper.h"
36 #include "crush/CrushCompiler.h"
37 #include "crush/CrushTester.h"
38 #include "include/assert.h"
39
40 #define dout_context g_ceph_context
41 #define dout_subsys ceph_subsys_crush
42
43 using namespace std;
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 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 --reweight recalculate all bucket weights\n";
169 cout << " -i mapfn --create-simple-rule name root type mode\n"
170 << " create crush rule <name> to start from <root>,\n"
171 << " replicate across buckets of type <type>, using\n"
172 << " a choose mode of <firstn|indep>\n";
173 cout << " -i mapfn --create-replicated-rule name root type\n"
174 << " create crush rule <name> to start from <root>,\n"
175 << " replicate across buckets of type <type>\n";
176 cout << " --device-class <class>\n";
177 cout << " use device class <class> for new rule\n";
178 cout << " -i mapfn --remove-rule name\n"
179 << " remove the specified crush rule\n";
180 cout << "\n";
181 cout << "Options for the display/test stage\n";
182 cout << "\n";
183 cout << " -f --format the format of --dump, defaults to json-pretty\n";
184 cout << " can be one of json, json-pretty, xml, xml-pretty,\n";
185 cout << " table, table-kv, html, html-pretty\n";
186 cout << " --dump dump the crush map\n";
187 cout << " --tree print map summary as a tree\n";
188 cout << " --check [max_id] check if any item is referencing an unknown name/type\n";
189 cout << " -i mapfn --show-location id\n";
190 cout << " show location for given device id\n";
191 cout << " -i mapfn --test test a range of inputs on the map\n";
192 cout << " [--min-x x] [--max-x x] [--x x]\n";
193 cout << " [--min-rule r] [--max-rule r] [--rule r] [--ruleset rs]\n";
194 cout << " [--num-rep n]\n";
195 cout << " [--pool-id n] specifies pool id\n";
196 cout << " [--batches b] split the CRUSH mapping into b > 1 rounds\n";
197 cout << " [--weight|-w devno weight]\n";
198 cout << " where weight is 0 to 1.0\n";
199 cout << " [--simulate] simulate placements using a random\n";
200 cout << " number generator in place of the CRUSH\n";
201 cout << " algorithm\n";
202 cout << " --show-utilization show OSD usage\n";
203 cout << " --show-utilization-all\n";
204 cout << " include zero weight items\n";
205 cout << " --show-statistics show chi squared statistics\n";
206 cout << " --show-mappings show mappings\n";
207 cout << " --show-bad-mappings show bad mappings\n";
208 cout << " --show-choose-tries show choose tries histogram\n";
209 cout << " --output-name name\n";
210 cout << " prepend the data file(s) generated during the\n";
211 cout << " testing routine with name\n";
212 cout << " --output-csv\n";
213 cout << " export select data generated during testing routine\n";
214 cout << " to CSV files for off-line post-processing\n";
215 cout << " use --help-output for more information\n";
216 cout << "\n";
217 cout << "Options for the output stage\n";
218 cout << "\n";
219 cout << " [--outfn|-o outfile]\n";
220 cout << " specify output for modified crush map\n";
221 cout << "\n";
222 }
223
224 struct bucket_types_t {
225 const char *name;
226 int type;
227 } bucket_types[] = {
228 { "uniform", CRUSH_BUCKET_UNIFORM },
229 { "list", CRUSH_BUCKET_LIST },
230 { "straw", CRUSH_BUCKET_STRAW },
231 { "straw2", CRUSH_BUCKET_STRAW2 },
232 { "tree", CRUSH_BUCKET_TREE },
233 { 0, 0 },
234 };
235
236 struct layer_t {
237 const char *name;
238 const char *buckettype;
239 int size;
240 };
241
242 int main(int argc, const char **argv)
243 {
244 vector<const char*> args;
245 argv_to_vec(argc, argv, args);
246
247 const char *me = argv[0];
248 std::string infn, srcfn, outfn, add_name, remove_name, reweight_name;
249 bool compile = false;
250 bool decompile = false;
251 bool check = false;
252 int max_id = -1;
253 bool test = false;
254 bool display = false;
255 bool tree = false;
256 string dump_format = "json-pretty";
257 bool dump = false;
258 int full_location = -1;
259 bool write_to_file = false;
260 int verbose = 0;
261 bool unsafe_tunables = false;
262
263 bool reweight = false;
264 int add_item = -1;
265 bool update_item = false;
266 bool add_rule = false;
267 std::string rule_name, rule_root, rule_type, rule_mode, rule_device_class;
268 bool del_rule = false;
269 float add_weight = 0;
270 map<string,string> add_loc;
271 float reweight_weight = 0;
272
273 bool adjust = false;
274
275 int build = 0;
276 int num_osds =0;
277 vector<layer_t> layers;
278
279 int choose_local_tries = -1;
280 int choose_local_fallback_tries = -1;
281 int choose_total_tries = -1;
282 int chooseleaf_descend_once = -1;
283 int chooseleaf_vary_r = -1;
284 int chooseleaf_stable = -1;
285 int straw_calc_version = -1;
286 int allowed_bucket_algs = -1;
287
288 CrushWrapper crush;
289
290 CrushTester tester(crush, cout);
291
292 // we use -c, don't confuse the generic arg parsing
293 // only parse arguments from CEPH_ARGS, if in the environment
294 vector<const char *> env_args;
295 env_to_vec(env_args);
296 auto cct = global_init(NULL, env_args, CEPH_ENTITY_TYPE_CLIENT,
297 CODE_ENVIRONMENT_UTILITY,
298 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
299 // crushtool times out occasionally when quits. so do not
300 // release the g_ceph_context.
301 cct->get();
302 common_init_finish(g_ceph_context);
303
304 int x;
305 float y;
306 long long z;
307
308 std::string val;
309 std::ostringstream err;
310 int tmp;
311 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
312 if (ceph_argparse_double_dash(args, i)) {
313 break;
314 } else if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
315 usage();
316 return EXIT_SUCCESS;
317 } else if (ceph_argparse_witharg(args, i, &val, "-d", "--decompile", (char*)NULL)) {
318 infn = val;
319 decompile = true;
320 } else if (ceph_argparse_witharg(args, i, &val, "-i", "--infn", (char*)NULL)) {
321 infn = val;
322 } else if (ceph_argparse_witharg(args, i, &val, "-o", "--outfn", (char*)NULL)) {
323 outfn = val;
324 } else if (ceph_argparse_flag(args, i, "-v", "--verbose", (char*)NULL)) {
325 verbose += 1;
326 } else if (ceph_argparse_flag(args, i, "--tree", (char*)NULL)) {
327 tree = true;
328 } else if (ceph_argparse_witharg(args, i, &val, "-f", "--format", (char*)NULL)) {
329 dump_format = val;
330 } else if (ceph_argparse_flag(args, i, "--dump", (char*)NULL)) {
331 dump = true;
332 } else if (ceph_argparse_flag(args, i, "--show_utilization", (char*)NULL)) {
333 display = true;
334 tester.set_output_utilization(true);
335 } else if (ceph_argparse_flag(args, i, "--show_utilization_all", (char*)NULL)) {
336 display = true;
337 tester.set_output_utilization_all(true);
338 } else if (ceph_argparse_flag(args, i, "--show_statistics", (char*)NULL)) {
339 display = true;
340 tester.set_output_statistics(true);
341 } else if (ceph_argparse_flag(args, i, "--show_mappings", (char*)NULL)) {
342 display = true;
343 tester.set_output_mappings(true);
344 } else if (ceph_argparse_flag(args, i, "--show_bad_mappings", (char*)NULL)) {
345 display = true;
346 tester.set_output_bad_mappings(true);
347 } else if (ceph_argparse_flag(args, i, "--show_choose_tries", (char*)NULL)) {
348 display = true;
349 tester.set_output_choose_tries(true);
350 } else if (ceph_argparse_witharg(args, i, &val, "-c", "--compile", (char*)NULL)) {
351 srcfn = val;
352 compile = true;
353 } else if (ceph_argparse_witharg(args, i, &max_id, err, "--check", (char*)NULL)) {
354 check = true;
355 } else if (ceph_argparse_flag(args, i, "-t", "--test", (char*)NULL)) {
356 test = true;
357 } else if (ceph_argparse_witharg(args, i, &full_location, err, "--show-location", (char*)NULL)) {
358 } else if (ceph_argparse_flag(args, i, "-s", "--simulate", (char*)NULL)) {
359 tester.set_random_placement();
360 } else if (ceph_argparse_flag(args, i, "--enable-unsafe-tunables", (char*)NULL)) {
361 unsafe_tunables = true;
362 } else if (ceph_argparse_witharg(args, i, &choose_local_tries, err,
363 "--set_choose_local_tries", (char*)NULL)) {
364 adjust = true;
365 } else if (ceph_argparse_witharg(args, i, &choose_local_fallback_tries, err,
366 "--set_choose_local_fallback_tries", (char*)NULL)) {
367 adjust = true;
368 } else if (ceph_argparse_witharg(args, i, &choose_total_tries, err,
369 "--set_choose_total_tries", (char*)NULL)) {
370 adjust = true;
371 } else if (ceph_argparse_witharg(args, i, &chooseleaf_descend_once, err,
372 "--set_chooseleaf_descend_once", (char*)NULL)) {
373 adjust = true;
374 } else if (ceph_argparse_witharg(args, i, &chooseleaf_vary_r, err,
375 "--set_chooseleaf_vary_r", (char*)NULL)) {
376 adjust = true;
377 } else if (ceph_argparse_witharg(args, i, &chooseleaf_stable, err,
378 "--set_chooseleaf_stable", (char*)NULL)) {
379 adjust = true;
380 } else if (ceph_argparse_witharg(args, i, &straw_calc_version, err,
381 "--set_straw_calc_version", (char*)NULL)) {
382 adjust = true;
383 } else if (ceph_argparse_witharg(args, i, &allowed_bucket_algs, err,
384 "--set_allowed_bucket_algs", (char*)NULL)) {
385 adjust = true;
386 } else if (ceph_argparse_flag(args, i, "--reweight", (char*)NULL)) {
387 reweight = true;
388 } else if (ceph_argparse_witharg(args, i, &add_item, err, "--add_item", (char*)NULL)) {
389 if (!err.str().empty()) {
390 cerr << err.str() << std::endl;
391 return EXIT_FAILURE;
392 }
393 if (i == args.end()) {
394 cerr << "expecting additional argument to --add-item" << std::endl;
395 return EXIT_FAILURE;
396 }
397 add_weight = atof(*i);
398 i = args.erase(i);
399 if (i == args.end()) {
400 cerr << "expecting additional argument to --add-item" << std::endl;
401 return EXIT_FAILURE;
402 }
403 add_name.assign(*i);
404 i = args.erase(i);
405 } else if (ceph_argparse_witharg(args, i, &add_item, err, "--update_item", (char*)NULL)) {
406 update_item = true;
407 if (!err.str().empty()) {
408 cerr << err.str() << std::endl;
409 return EXIT_FAILURE;
410 }
411 if (i == args.end()) {
412 cerr << "expecting additional argument to --update-item" << std::endl;
413 return EXIT_FAILURE;
414 }
415 add_weight = atof(*i);
416 i = args.erase(i);
417 if (i == args.end()) {
418 cerr << "expecting additional argument to --update-item" << std::endl;
419 return EXIT_FAILURE;
420 }
421 add_name.assign(*i);
422 i = args.erase(i);
423 } else if (ceph_argparse_witharg(args, i, &val, err, "--create-simple-rule", (char*)NULL)) {
424 rule_name.assign(val);
425 if (!err.str().empty()) {
426 cerr << err.str() << std::endl;
427 return EXIT_FAILURE;
428 }
429 if (i == args.end()) {
430 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
431 return EXIT_FAILURE;
432 }
433
434 rule_root.assign(*i);
435 i = args.erase(i);
436 if (i == args.end()) {
437 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
438 return EXIT_FAILURE;
439 }
440
441 rule_type.assign(*i);
442 i = args.erase(i);
443 if (i == args.end()) {
444 cerr << "expecting additional argument to --create-simple-rule" << std::endl;
445 return EXIT_FAILURE;
446 }
447
448 rule_mode.assign(*i);
449 i = args.erase(i);
450
451 cout << "--create-simple-rule:"
452 << " name=" << rule_name
453 << " root=" << rule_root
454 << " type=" << rule_type
455 << " mode=" << rule_mode
456 << std::endl;
457 add_rule = true;
458 } else if (ceph_argparse_witharg(args, i, &val, err, "--create-replicated-rule", (char*)NULL)) {
459 rule_name.assign(val);
460 if (!err.str().empty()) {
461 cerr << err.str() << std::endl;
462 return EXIT_FAILURE;
463 }
464 if (i == args.end()) {
465 cerr << "expecting additional argument to --create-replicated-rule" << std::endl;
466 return EXIT_FAILURE;
467 }
468
469 rule_root.assign(*i);
470 i = args.erase(i);
471 if (i == args.end()) {
472 cerr << "expecting additional argument to --create-replicated-rule" << std::endl;
473 return EXIT_FAILURE;
474 }
475
476 rule_type.assign(*i);
477 i = args.erase(i);
478 rule_mode = "firstn";
479
480 cout << "--create-replicated-rule:"
481 << " name=" << rule_name
482 << " root=" << rule_root
483 << " type=" << rule_type
484 << std::endl;
485 add_rule = true;
486
487 } else if (ceph_argparse_witharg(args, i, &val, "--device-class", (char*)NULL)) {
488 rule_device_class.assign(val);
489 if (!err.str().empty()) {
490 cerr << err.str() << std::endl;
491 return EXIT_FAILURE;
492 }
493 } else if (ceph_argparse_witharg(args, i, &val, "--remove-rule", (char*)NULL)) {
494 rule_name.assign(val);
495 if (!err.str().empty()) {
496 cerr << err.str() << std::endl;
497 return EXIT_FAILURE;
498 }
499 del_rule = true;
500 } else if (ceph_argparse_witharg(args, i, &val, "--loc", (char*)NULL)) {
501 std::string type(val);
502 if (i == args.end()) {
503 cerr << "expecting additional argument to --loc" << std::endl;
504 return EXIT_FAILURE;
505 }
506 std::string name(*i);
507 i = args.erase(i);
508 add_loc[type] = name;
509 } else if (ceph_argparse_flag(args, i, "--output-csv", (char*)NULL)) {
510 write_to_file = true;
511 tester.set_output_data_file(true);
512 tester.set_output_csv(true);
513 } else if (ceph_argparse_flag(args, i, "--help-output", (char*)NULL)) {
514 data_analysis_usage();
515 return EXIT_SUCCESS;
516 } else if (ceph_argparse_witharg(args, i, &val, "--output-name", (char*)NULL)) {
517 std::string name(val);
518 if (i == args.end()) {
519 cerr << "expecting additional argument to --output-name" << std::endl;
520 return EXIT_FAILURE;
521 }
522 else {
523 tester.set_output_data_file_name(name + "-");
524 }
525 } else if (ceph_argparse_witharg(args, i, &val, "--remove_item", (char*)NULL)) {
526 remove_name = val;
527 } else if (ceph_argparse_witharg(args, i, &val, "--reweight_item", (char*)NULL)) {
528 reweight_name = val;
529 if (i == args.end()) {
530 cerr << "expecting additional argument to --reweight-item" << std::endl;
531 return EXIT_FAILURE;
532 }
533 reweight_weight = atof(*i);
534 i = args.erase(i);
535 } else if (ceph_argparse_flag(args, i, "--build", (char*)NULL)) {
536 build = true;
537 } else if (ceph_argparse_witharg(args, i, &num_osds, err, "--num_osds", (char*)NULL)) {
538 if (!err.str().empty()) {
539 cerr << err.str() << std::endl;
540 return EXIT_FAILURE;
541 }
542 } else if (ceph_argparse_witharg(args, i, &x, err, "--num_rep", (char*)NULL)) {
543 if (!err.str().empty()) {
544 cerr << err.str() << std::endl;
545 return EXIT_FAILURE;
546 }
547 tester.set_num_rep(x);
548 } else if (ceph_argparse_witharg(args, i, &x, err, "--max_x", (char*)NULL)) {
549 if (!err.str().empty()) {
550 cerr << err.str() << std::endl;
551 return EXIT_FAILURE;
552 }
553 tester.set_max_x(x);
554 } else if (ceph_argparse_witharg(args, i, &x, err, "--min_x", (char*)NULL)) {
555 if (!err.str().empty()) {
556 cerr << err.str() << std::endl;
557 return EXIT_FAILURE;
558 }
559 tester.set_min_x(x);
560 } else if (ceph_argparse_witharg(args, i, &z, err, "--pool_id", (char*)NULL)) {
561 if (!err.str().empty()) {
562 cerr << err.str() << std::endl;
563 return EXIT_FAILURE;
564 }
565 tester.set_pool_id(z);
566 } else if (ceph_argparse_witharg(args, i, &x, err, "--x", (char*)NULL)) {
567 if (!err.str().empty()) {
568 cerr << err.str() << std::endl;
569 return EXIT_FAILURE;
570 }
571 tester.set_x(x);
572 } else if (ceph_argparse_witharg(args, i, &x, err, "--max_rule", (char*)NULL)) {
573 if (!err.str().empty()) {
574 cerr << err.str() << std::endl;
575 return EXIT_FAILURE;
576 }
577 tester.set_max_rule(x);
578 } else if (ceph_argparse_witharg(args, i, &x, err, "--min_rule", (char*)NULL)) {
579 if (!err.str().empty()) {
580 cerr << err.str() << std::endl;
581 return EXIT_FAILURE;
582 }
583 tester.set_min_rule(x);
584 } else if (ceph_argparse_witharg(args, i, &x, err, "--rule", (char*)NULL)) {
585 if (!err.str().empty()) {
586 cerr << err.str() << std::endl;
587 return EXIT_FAILURE;
588 }
589 tester.set_rule(x);
590 } else if (ceph_argparse_witharg(args, i, &x, err, "--ruleset", (char*)NULL)) {
591 if (!err.str().empty()) {
592 cerr << err.str() << std::endl;
593 return EXIT_FAILURE;
594 }
595 tester.set_ruleset(x);
596 } else if (ceph_argparse_witharg(args, i, &x, err, "--batches", (char*)NULL)) {
597 if (!err.str().empty()) {
598 cerr << err.str() << std::endl;
599 return EXIT_FAILURE;
600 }
601 tester.set_batches(x);
602 } else if (ceph_argparse_witharg(args, i, &y, err, "--mark-down-ratio", (char*)NULL)) {
603 if (!err.str().empty()) {
604 cerr << err.str() << std::endl;
605 return EXIT_FAILURE;
606 }
607 tester.set_device_down_ratio(y);
608 } else if (ceph_argparse_witharg(args, i, &y, err, "--mark-down-bucket-ratio", (char*)NULL)) {
609 if (!err.str().empty()) {
610 cerr << err.str() << std::endl;
611 return EXIT_FAILURE;
612 }
613 tester.set_bucket_down_ratio(y);
614 } else if (ceph_argparse_witharg(args, i, &tmp, err, "--weight", (char*)NULL)) {
615 if (!err.str().empty()) {
616 cerr << err.str() << std::endl;
617 return EXIT_FAILURE;
618 }
619 int dev = tmp;
620 if (i == args.end()) {
621 cerr << "expecting additional argument to --weight" << std::endl;
622 return EXIT_FAILURE;
623 }
624 float f = atof(*i);
625 i = args.erase(i);
626 tester.set_device_weight(dev, f);
627 }
628 else {
629 ++i;
630 }
631 }
632
633 if (test && !check && !display && !write_to_file) {
634 cerr << "WARNING: no output selected; use --output-csv or --show-X" << std::endl;
635 }
636
637 if (decompile + compile + build > 1) {
638 cerr << "cannot specify more than one of compile, decompile, and build" << std::endl;
639 return EXIT_FAILURE;
640 }
641 if (!check && !compile && !decompile && !build && !test && !reweight && !adjust && !tree && !dump &&
642 add_item < 0 && !add_rule && !del_rule && full_location < 0 &&
643 remove_name.empty() && reweight_name.empty()) {
644 cerr << "no action specified; -h for help" << std::endl;
645 return EXIT_FAILURE;
646 }
647 if ((!build) && (!args.empty())) {
648 cerr << "unrecognized arguments: " << args << std::endl;
649 return EXIT_FAILURE;
650 }
651 else {
652 if ((args.size() % 3) != 0U) {
653 cerr << "remaining args: " << args << std::endl;
654 cerr << "layers must be specified with 3-tuples of (name, buckettype, size)"
655 << std::endl;
656 return EXIT_FAILURE;
657 }
658 for (size_t j = 0; j < args.size(); j += 3) {
659 layer_t l;
660 l.name = args[j];
661 l.buckettype = args[j+1];
662 l.size = atoi(args[j+2]);
663 layers.push_back(l);
664 }
665 }
666
667 /*
668 if (outfn) cout << "outfn " << outfn << std::endl;
669 if (cinfn) cout << "cinfn " << cinfn << std::endl;
670 if (dinfn) cout << "dinfn " << dinfn << std::endl;
671 */
672
673 bool modified = false;
674
675 // input ----
676
677 if (!infn.empty()) {
678 bufferlist bl;
679 std::string error;
680
681 int r = 0;
682 if (infn == "-") {
683 if (isatty(STDIN_FILENO)) {
684 cerr << "stdin must not be from a tty" << std::endl;
685 return EXIT_FAILURE;
686 }
687 r = get_fd_data(STDIN_FILENO, bl);
688 if (r < 0) {
689 cerr << "error reading data from STDIN" << std::endl;
690 return EXIT_FAILURE;
691 }
692 } else {
693 r = bl.read_file(infn.c_str(), &error);
694 if (r < 0) {
695 cerr << me << ": error reading '" << infn << "': "
696 << error << std::endl;
697 return EXIT_FAILURE;
698 }
699 }
700 bufferlist::iterator p = bl.begin();
701 try {
702 crush.decode(p);
703 } catch(...) {
704 cerr << me << ": unable to decode " << infn << std::endl;
705 return EXIT_FAILURE;
706 }
707 }
708
709 if (compile) {
710 crush.create();
711
712 // read the file
713 ifstream in(srcfn.c_str());
714 if (!in.is_open()) {
715 cerr << "input file " << srcfn << " not found" << std::endl;
716 return -ENOENT;
717 }
718
719 CrushCompiler cc(crush, cerr, verbose);
720 if (unsafe_tunables)
721 cc.enable_unsafe_tunables();
722 int r = cc.compile(in, srcfn.c_str());
723 if (r < 0)
724 return EXIT_FAILURE;
725
726 modified = true;
727 }
728
729 if (build) {
730 if (layers.empty()) {
731 cerr << me << ": must specify at least one layer" << std::endl;
732 return EXIT_FAILURE;
733 }
734
735 crush.create();
736
737 vector<int> lower_items;
738 vector<int> lower_weights;
739
740 crush.set_max_devices(num_osds);
741 for (int i=0; i<num_osds; i++) {
742 lower_items.push_back(i);
743 lower_weights.push_back(0x10000);
744 crush.set_item_name(i, "osd." + stringify(i));
745 }
746
747 crush.set_type_name(0, "osd");
748 int type = 1;
749 for (vector<layer_t>::iterator p = layers.begin(); p != layers.end(); ++p, type++) {
750 layer_t &l = *p;
751
752 dout(2) << "layer " << type
753 << " " << l.name
754 << " bucket type " << l.buckettype
755 << " " << l.size
756 << dendl;
757
758 crush.set_type_name(type, l.name);
759
760 int buckettype = -1;
761 for (int i = 0; bucket_types[i].name; i++)
762 if (l.buckettype && strcmp(l.buckettype, bucket_types[i].name) == 0) {
763 buckettype = bucket_types[i].type;
764 break;
765 }
766 if (buckettype < 0) {
767 cerr << "unknown bucket type '" << l.buckettype << "'" << std::endl;
768 return EXIT_FAILURE;
769 }
770
771 // build items
772 vector<int> cur_items;
773 vector<int> cur_weights;
774 unsigned lower_pos = 0; // lower pos
775
776 dout(2) << "lower_items " << lower_items << dendl;
777 dout(2) << "lower_weights " << lower_weights << dendl;
778
779 int i = 0;
780 while (1) {
781 if (lower_pos == lower_items.size())
782 break;
783
784 int items[num_osds];
785 int weights[num_osds];
786
787 int weight = 0;
788 int j;
789 for (j=0; j<l.size || l.size==0; j++) {
790 if (lower_pos == lower_items.size())
791 break;
792 items[j] = lower_items[lower_pos];
793 weights[j] = lower_weights[lower_pos];
794 weight += weights[j];
795 lower_pos++;
796 dout(2) << " item " << items[j] << " weight " << weights[j] << dendl;
797 }
798
799 int id;
800 int r = crush.add_bucket(0, buckettype, CRUSH_HASH_DEFAULT, type, j, items, weights, &id);
801 if (r < 0) {
802 cerr << " Couldn't add bucket: " << cpp_strerror(r) << std::endl;
803 return r;
804 }
805
806 char format[20];
807 format[sizeof(format)-1] = '\0';
808 if (l.size)
809 snprintf(format, sizeof(format)-1, "%s%%d", l.name);
810 else
811 strncpy(format, l.name, sizeof(format)-1);
812 char name[20];
813 snprintf(name, sizeof(name), format, i);
814 crush.set_item_name(id, name);
815
816 dout(2) << " in bucket " << id << " '" << name << "' size " << j << " weight " << weight << dendl;
817
818 cur_items.push_back(id);
819 cur_weights.push_back(weight);
820 i++;
821 }
822
823 lower_items.swap(cur_items);
824 lower_weights.swap(cur_weights);
825 }
826
827 string root = layers.back().size == 0 ? layers.back().name :
828 string(layers.back().name) + "0";
829
830 {
831 set<int> roots;
832 crush.find_roots(roots);
833 if (roots.size() > 1)
834 dout(1) << "The crush rulesets will use the root " << root << "\n"
835 << "and ignore the others.\n"
836 << "There are " << roots.size() << " roots, they can be\n"
837 << "grouped into a single root by appending something like:\n"
838 << " root straw 0\n"
839 << dendl;
840 }
841
842 if (OSDMap::build_simple_crush_rules(g_ceph_context, crush, root, &cerr))
843 return EXIT_FAILURE;
844
845 modified = true;
846 }
847
848 // mutate ----
849
850 if (choose_local_tries >= 0) {
851 crush.set_choose_local_tries(choose_local_tries);
852 modified = true;
853 }
854 if (choose_local_fallback_tries >= 0) {
855 crush.set_choose_local_fallback_tries(choose_local_fallback_tries);
856 modified = true;
857 }
858 if (choose_total_tries >= 0) {
859 crush.set_choose_total_tries(choose_total_tries);
860 modified = true;
861 }
862 if (chooseleaf_descend_once >= 0) {
863 crush.set_chooseleaf_descend_once(chooseleaf_descend_once);
864 modified = true;
865 }
866 if (chooseleaf_vary_r >= 0) {
867 crush.set_chooseleaf_vary_r(chooseleaf_vary_r);
868 modified = true;
869 }
870 if (chooseleaf_stable >= 0) {
871 crush.set_chooseleaf_stable(chooseleaf_stable);
872 modified = true;
873 }
874 if (straw_calc_version >= 0) {
875 crush.set_straw_calc_version(straw_calc_version);
876 modified = true;
877 }
878 if (allowed_bucket_algs >= 0) {
879 crush.set_allowed_bucket_algs(allowed_bucket_algs);
880 modified = true;
881 }
882
883 if (!reweight_name.empty()) {
884 cout << me << " reweighting item " << reweight_name << " to " << reweight_weight << std::endl;
885 int r;
886 if (!crush.name_exists(reweight_name)) {
887 cerr << " name " << reweight_name << " dne" << std::endl;
888 r = -ENOENT;
889 } else {
890 int item = crush.get_item_id(reweight_name);
891 r = crush.adjust_item_weightf(g_ceph_context, item, reweight_weight);
892 }
893 if (r >= 0)
894 modified = true;
895 else {
896 cerr << me << " " << cpp_strerror(r) << std::endl;
897 return r;
898 }
899 }
900
901 if (!remove_name.empty()) {
902 cout << me << " removing item " << remove_name << std::endl;
903 int r;
904 if (!crush.name_exists(remove_name)) {
905 cerr << " name " << remove_name << " dne" << std::endl;
906 r = -ENOENT;
907 } else {
908 int remove_item = crush.get_item_id(remove_name);
909 r = crush.remove_item(g_ceph_context, remove_item, false);
910 }
911 if (r == 0)
912 modified = true;
913 else {
914 cerr << me << " " << cpp_strerror(r) << std::endl;
915 return r;
916 }
917 }
918
919 if (add_item >= 0) {
920 int r;
921 if (update_item) {
922 r = crush.update_item(g_ceph_context, add_item, add_weight, add_name.c_str(), add_loc);
923 } else {
924 r = crush.insert_item(g_ceph_context, add_item, add_weight, add_name.c_str(), add_loc);
925 }
926 if (r >= 0) {
927 modified = true;
928 } else {
929 cerr << me << " " << cpp_strerror(r) << std::endl;
930 return r;
931 }
932 }
933
934 if (add_rule) {
935 if (crush.rule_exists(rule_name)) {
936 cerr << "rule " << rule_name << " already exists" << std::endl;
937 return EXIT_FAILURE;
938 }
939 int r = crush.add_simple_rule(rule_name, rule_root, rule_type,
940 rule_device_class,
941 rule_mode, pg_pool_t::TYPE_REPLICATED, &err);
942 if (r < 0) {
943 cerr << err.str() << std::endl;
944 return EXIT_FAILURE;
945 }
946 modified = true;
947 }
948
949 if (del_rule) {
950 if (!crush.rule_exists(rule_name)) {
951 cerr << "rule " << rule_name << " does not exist" << std::endl;
952 return 0;
953 }
954 int ruleno = crush.get_rule_id(rule_name);
955 assert(ruleno >= 0);
956 int r = crush.remove_rule(ruleno);
957 if (r < 0) {
958 cerr << "fail to remove rule " << rule_name << std::endl;
959 return EXIT_FAILURE;
960 }
961 modified = true;
962 }
963
964 if (reweight) {
965 crush.reweight(g_ceph_context);
966 modified = true;
967 }
968
969
970 // display ---
971 if (full_location >= 0) {
972 map<string, string> loc = crush.get_full_location(full_location);
973 for (map<string,string>::iterator p = loc.begin();
974 p != loc.end();
975 ++p) {
976 cout << p->first << "\t" << p->second << std::endl;
977 }
978 }
979
980 if (tree) {
981 crush.dump_tree(&cout, NULL);
982 }
983
984 if (dump) {
985 boost::scoped_ptr<Formatter> f(Formatter::create(dump_format, "json-pretty", "json-pretty"));
986 f->open_object_section("crush_map");
987 crush.dump(f.get());
988 f->close_section();
989 f->flush(cout);
990 cout << "\n";
991 }
992
993 if (decompile) {
994 CrushCompiler cc(crush, cerr, verbose);
995 if (!outfn.empty()) {
996 ofstream o;
997 o.open(outfn.c_str(), ios::out | ios::binary | ios::trunc);
998 if (!o.is_open()) {
999 cerr << me << ": error writing '" << outfn << "'" << std::endl;
1000 return EXIT_FAILURE;
1001 }
1002 cc.decompile(o);
1003 o.close();
1004 } else {
1005 cc.decompile(cout);
1006 }
1007 }
1008
1009 if (check) {
1010 tester.check_overlapped_rules();
1011 if (max_id >= 0) {
1012 if (!tester.check_name_maps(max_id)) {
1013 return EXIT_FAILURE;
1014 }
1015 }
1016 }
1017
1018 if (test) {
1019 if (tester.get_output_utilization_all() ||
1020 tester.get_output_utilization())
1021 tester.set_output_statistics(true);
1022
1023 int r = tester.test();
1024 if (r < 0)
1025 return EXIT_FAILURE;
1026 }
1027
1028 // output ---
1029 if (modified) {
1030 crush.finalize();
1031
1032 if (outfn.empty()) {
1033 cout << me << " successfully built or modified map. Use '-o <file>' to write it out." << std::endl;
1034 } else {
1035 bufferlist bl;
1036 crush.encode(bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
1037 int r = bl.write_file(outfn.c_str());
1038 if (r < 0) {
1039 cerr << me << ": error writing '" << outfn << "': " << cpp_strerror(r) << std::endl;
1040 return EXIT_FAILURE;
1041 }
1042 if (verbose)
1043 cout << "wrote crush map to " << outfn << std::endl;
1044 }
1045 }
1046
1047 return 0;
1048 }
1049 /*
1050 * Local Variables:
1051 * compile-command: "cd .. ; make crushtool && test/run-cli-tests"
1052 * End:
1053 */