Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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         cerr << "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              << std::endl;
840       }
841     }
842     
843     if (OSDMap::build_simple_crush_rules(g_ceph_context, crush, root, &cerr))
844       return EXIT_FAILURE;
845
846     modified = true;
847   }
848
849   // mutate ----
850
851   if (choose_local_tries >= 0) {
852     crush.set_choose_local_tries(choose_local_tries);
853     modified = true;
854   }
855   if (choose_local_fallback_tries >= 0) {
856     crush.set_choose_local_fallback_tries(choose_local_fallback_tries);
857     modified = true;
858   }
859   if (choose_total_tries >= 0) {
860     crush.set_choose_total_tries(choose_total_tries);
861     modified = true;
862   }
863   if (chooseleaf_descend_once >= 0) {
864     crush.set_chooseleaf_descend_once(chooseleaf_descend_once);
865     modified = true;
866   }
867   if (chooseleaf_vary_r >= 0) {
868     crush.set_chooseleaf_vary_r(chooseleaf_vary_r);
869     modified = true;
870   }
871   if (chooseleaf_stable >= 0) {
872     crush.set_chooseleaf_stable(chooseleaf_stable);
873     modified = true;
874   }
875   if (straw_calc_version >= 0) {
876     crush.set_straw_calc_version(straw_calc_version);
877     modified = true;
878   }
879   if (allowed_bucket_algs >= 0) {
880     crush.set_allowed_bucket_algs(allowed_bucket_algs);
881     modified = true;
882   }
883
884   if (!reweight_name.empty()) {
885     cout << me << " reweighting item " << reweight_name << " to " << reweight_weight << std::endl;
886     int r;
887     if (!crush.name_exists(reweight_name)) {
888       cerr << " name " << reweight_name << " dne" << std::endl;
889       r = -ENOENT;
890     } else {
891       int item = crush.get_item_id(reweight_name);
892       r = crush.adjust_item_weightf(g_ceph_context, item, reweight_weight);
893     }
894     if (r >= 0)
895       modified = true;
896     else {
897       cerr << me << " " << cpp_strerror(r) << std::endl;
898       return r;
899     }
900   }
901
902   if (!remove_name.empty()) {
903     cout << me << " removing item " << remove_name << std::endl;
904     int r;
905     if (!crush.name_exists(remove_name)) {
906       cerr << " name " << remove_name << " dne" << std::endl;
907       r = -ENOENT;
908     } else {
909       int remove_item = crush.get_item_id(remove_name);
910       r = crush.remove_item(g_ceph_context, remove_item, false);
911     }
912     if (r == 0)
913       modified = true;
914     else {
915       cerr << me << " " << cpp_strerror(r) << std::endl;
916       return r;
917     }
918   }
919
920   if (add_item >= 0) {
921     int r;
922     if (update_item) {
923       r = crush.update_item(g_ceph_context, add_item, add_weight, add_name.c_str(), add_loc);
924     } else {
925       r = crush.insert_item(g_ceph_context, add_item, add_weight, add_name.c_str(), add_loc);
926     }
927     if (r >= 0) {
928       modified = true;
929     } else {
930       cerr << me << " " << cpp_strerror(r) << std::endl;
931       return r;
932     }
933   }
934
935   if (add_rule) {
936     if (crush.rule_exists(rule_name)) {
937       cerr << "rule " << rule_name << " already exists" << std::endl;
938       return EXIT_FAILURE;
939     }
940     int r = crush.add_simple_rule(rule_name, rule_root, rule_type,
941                                   rule_device_class,
942                                   rule_mode, pg_pool_t::TYPE_REPLICATED, &err);
943     if (r < 0) {
944       cerr << err.str() << std::endl;
945       return EXIT_FAILURE;
946     }
947     modified = true;
948   }
949
950   if (del_rule) {
951     if (!crush.rule_exists(rule_name)) {
952       cerr << "rule " << rule_name << " does not exist" << std::endl;
953       return 0;
954     }
955     int ruleno = crush.get_rule_id(rule_name);
956     assert(ruleno >= 0);
957     int r = crush.remove_rule(ruleno);
958     if (r < 0) {
959       cerr << "fail to remove rule " << rule_name << std::endl;
960       return EXIT_FAILURE;
961     }
962     modified = true;
963   }
964
965   if (reweight) {
966     crush.reweight(g_ceph_context);
967     modified = true;
968   }
969
970
971   // display ---
972   if (full_location >= 0) {
973     map<string, string> loc = crush.get_full_location(full_location);
974     for (map<string,string>::iterator p = loc.begin();
975          p != loc.end();
976          ++p) {
977       cout << p->first << "\t" << p->second << std::endl;
978     }
979   }
980
981   if (tree) {
982     crush.dump_tree(&cout, NULL);
983   }
984
985   if (dump) {
986     boost::scoped_ptr<Formatter> f(Formatter::create(dump_format, "json-pretty", "json-pretty"));
987     f->open_object_section("crush_map");
988     crush.dump(f.get());
989     f->close_section();
990     f->flush(cout);
991     cout << "\n";
992   }
993
994   if (decompile) {
995     CrushCompiler cc(crush, cerr, verbose);
996     if (!outfn.empty()) {
997       ofstream o;
998       o.open(outfn.c_str(), ios::out | ios::binary | ios::trunc);
999       if (!o.is_open()) {
1000         cerr << me << ": error writing '" << outfn << "'" << std::endl;
1001         return EXIT_FAILURE;
1002       }
1003       cc.decompile(o);
1004       o.close();
1005     } else {
1006       cc.decompile(cout);
1007     }
1008   }
1009
1010   if (check) {
1011     tester.check_overlapped_rules();
1012     if (max_id >= 0) {
1013       if (!tester.check_name_maps(max_id)) {
1014         return EXIT_FAILURE;
1015       }
1016     }
1017   }
1018
1019   if (test) {
1020     if (tester.get_output_utilization_all() ||
1021         tester.get_output_utilization())
1022       tester.set_output_statistics(true);
1023
1024     int r = tester.test();
1025     if (r < 0)
1026       return EXIT_FAILURE;
1027   }
1028
1029   // output ---
1030   if (modified) {
1031     crush.finalize();
1032
1033     if (outfn.empty()) {
1034       cout << me << " successfully built or modified map.  Use '-o <file>' to write it out." << std::endl;
1035     } else {
1036       bufferlist bl;
1037       crush.encode(bl, CEPH_FEATURES_SUPPORTED_DEFAULT);
1038       int r = bl.write_file(outfn.c_str());
1039       if (r < 0) {
1040         cerr << me << ": error writing '" << outfn << "': " << cpp_strerror(r) << std::endl;
1041         return EXIT_FAILURE;
1042       }
1043       if (verbose)
1044         cout << "wrote crush map to " << outfn << std::endl;
1045     }
1046   }
1047
1048   return 0;
1049 }
1050 /*
1051  * Local Variables:
1052  * compile-command: "cd .. ; make crushtool && test/run-cli-tests"
1053  * End:
1054  */