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