Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / crush / CrushCompiler.cc
1
2 #include "CrushCompiler.h"
3
4 #if defined(_AIX)
5 #define EBADE ECORRUPT
6 #endif
7
8 #ifndef EBADE
9 #define EBADE EFTYPE
10 #endif
11 #include <string>
12 #include "common/errno.h"
13 #include <boost/algorithm/string.hpp>
14
15 // -------------
16
17 static void print_type_name(ostream& out, int t, CrushWrapper &crush)
18 {
19   const char *name = crush.get_type_name(t);
20   if (name)
21     out << name;
22   else if (t == 0)
23     out << "device";
24   else
25     out << "type" << t;
26 }
27
28 static void print_item_name(ostream& out, int t, CrushWrapper &crush)
29 {
30   const char *name = crush.get_item_name(t);
31   if (name)
32     out << name;
33   else if (t >= 0)
34     out << "device" << t;
35   else
36     out << "bucket" << (-1-t);
37 }
38
39 static void print_bucket_class_ids(ostream& out, int t, CrushWrapper &crush)
40 {
41   if (crush.class_bucket.count(t) == 0)
42     return;
43   auto &class_to_id = crush.class_bucket[t];
44   for (auto &i : class_to_id) {
45     int c = i.first;
46     int cid = i.second;
47     const char* class_name = crush.get_class_name(c);
48     assert(class_name);
49     out << "\tid " << cid << " class " << class_name << "\t\t# do not change unnecessarily\n";
50   }
51 }
52
53 static void print_item_class(ostream& out, int t, CrushWrapper &crush)
54 {
55   const char *c = crush.get_item_class(t);
56   if (c)
57     out << " class " << c;
58 }
59
60 static void print_class(ostream& out, int t, CrushWrapper &crush)
61 {
62   const char *c = crush.get_class_name(t);
63   if (c)
64     out << " class " << c;
65   else
66     out << " # unexpected class " << t;
67 }
68
69 static void print_rule_name(ostream& out, int t, CrushWrapper &crush)
70 {
71   const char *name = crush.get_rule_name(t);
72   if (name)
73     out << name;
74   else
75     out << "rule" << t;
76 }
77
78 static void print_fixedpoint(ostream& out, int i)
79 {
80   char s[20];
81   snprintf(s, sizeof(s), "%.3f", (float)i / (float)0x10000);
82   out << s;
83 }
84
85 int CrushCompiler::decompile_bucket_impl(int i, ostream &out)
86 {
87   const char *name = crush.get_item_name(i);
88   if (name && !crush.is_valid_crush_name(name))
89     return 0;
90   int type = crush.get_bucket_type(i);
91   print_type_name(out, type, crush);
92   out << " ";
93   print_item_name(out, i, crush);
94   out << " {\n";
95   out << "\tid " << i << "\t\t# do not change unnecessarily\n";
96   print_bucket_class_ids(out, i, crush);
97
98   out << "\t# weight ";
99   print_fixedpoint(out, crush.get_bucket_weight(i));
100   out << "\n";
101
102   int n = crush.get_bucket_size(i);
103
104   int alg = crush.get_bucket_alg(i);
105   out << "\talg " << crush_bucket_alg_name(alg);
106
107   // notate based on alg type
108   bool dopos = false;
109   switch (alg) {
110   case CRUSH_BUCKET_UNIFORM:
111     out << "\t# do not change bucket size (" << n << ") unnecessarily";
112     dopos = true;
113     break;
114   case CRUSH_BUCKET_LIST:
115     out << "\t# add new items at the end; do not change order unnecessarily";
116     break;
117   case CRUSH_BUCKET_TREE:
118     out << "\t# do not change pos for existing items unnecessarily";
119     dopos = true;
120     break;
121   }
122   out << "\n";
123
124   int hash = crush.get_bucket_hash(i);
125   out << "\thash " << hash << "\t# " << crush_hash_name(hash) << "\n";
126
127   for (int j=0; j<n; j++) {
128     int item = crush.get_bucket_item(i, j);
129     int w = crush.get_bucket_item_weight(i, j);
130     out << "\titem ";
131     print_item_name(out, item, crush);
132     out << " weight ";
133     print_fixedpoint(out, w);
134     if (dopos) 
135       out << " pos " << j;
136     
137     out << "\n";
138   }
139   out << "}\n";
140   return 0;
141 }
142
143 /* Basically, we just descend recursively into all of the buckets,
144  * executing a depth-first traversal of the graph. Since the buckets form a
145  * directed acyclic graph, this should work just fine. The graph isn't
146  * necessarily a tree, so we have to keep track of what buckets we already
147  * outputted. We don't want to output anything twice. We also keep track of
148  * what buckets are in progress so that we can detect cycles. These can
149  * arise through user error.
150  */
151 int CrushCompiler::decompile_bucket(int cur,
152                                     std::map<int, dcb_state_t>& dcb_states,
153                                     ostream &out)
154 {
155   if ((cur == 0) || (!crush.bucket_exists(cur)))
156     return 0;
157
158   std::map<int, dcb_state_t>::iterator c = dcb_states.find(cur);
159   if (c == dcb_states.end()) {
160     // Mark this bucket as "in progress."
161     std::map<int, dcb_state_t>::value_type val(cur, DCB_STATE_IN_PROGRESS);
162     std::pair <std::map<int, dcb_state_t>::iterator, bool> rval
163       (dcb_states.insert(val));
164     assert(rval.second);
165     c = rval.first;
166   }
167   else if (c->second == DCB_STATE_DONE) {
168     // We already did this bucket.
169     return 0;
170   }
171   else if (c->second == DCB_STATE_IN_PROGRESS) {
172     err << "decompile_crush_bucket: logic error: tried to decompile "
173         "a bucket that is already being decompiled" << std::endl;
174     return -EBADE;
175   }
176   else {
177     err << "decompile_crush_bucket: logic error: illegal bucket state! "
178          << c->second << std::endl;
179     return -EBADE;
180   }
181
182   int bsize = crush.get_bucket_size(cur);
183   for (int i = 0; i < bsize; ++i) {
184     int item = crush.get_bucket_item(cur, i);
185     std::map<int, dcb_state_t>::iterator d = dcb_states.find(item);
186     if (d == dcb_states.end()) {
187       int ret = decompile_bucket(item, dcb_states, out);
188       if (ret)
189         return ret;
190     }
191     else if (d->second == DCB_STATE_IN_PROGRESS) {
192       err << "decompile_crush_bucket: error: while trying to output bucket "
193            << cur << ", we found out that it contains one of the buckets that "
194            << "contain it. This is not allowed. The buckets must form a "
195            <<  "directed acyclic graph." << std::endl;
196       return -EINVAL;
197     }
198     else if (d->second != DCB_STATE_DONE) {
199       err << "decompile_crush_bucket: logic error: illegal bucket state "
200            << d->second << std::endl;
201       return -EBADE;
202     }
203   }
204   decompile_bucket_impl(cur, out);
205   c->second = DCB_STATE_DONE;
206   return 0;
207 }
208
209 int CrushCompiler::decompile_weight_set_weights(crush_weight_set weight_set,
210                                                 ostream &out)
211 {
212   out << "      [ ";
213   for (__u32 i = 0; i < weight_set.size; i++) {
214     print_fixedpoint(out, weight_set.weights[i]);
215     out << " ";
216   }
217   out << "]\n";
218   return 0;
219 }
220
221 int CrushCompiler::decompile_weight_set(crush_weight_set *weight_set,
222                                         __u32 size,
223                                         ostream &out)
224 {
225   out << "    weight_set [\n";
226   for (__u32 i = 0; i < size; i++) {
227     int r = decompile_weight_set_weights(weight_set[i], out);
228     if (r < 0)
229       return r;
230   }
231   out << "    ]\n";
232   return 0;
233 }
234
235 int CrushCompiler::decompile_ids(__s32 *ids,
236                                  __u32 size,
237                                  ostream &out)
238 {
239   out << "    ids [ ";
240   for (__u32 i = 0; i < size; i++)
241     out << ids[i] << " ";
242   out << "]\n";
243   return 0;
244 }
245
246 int CrushCompiler::decompile_choose_arg(crush_choose_arg *arg,
247                                         int bucket_id,
248                                         ostream &out)
249 {
250   int r;
251   out << "  {\n";
252   out << "    bucket_id " << bucket_id << "\n";
253   if (arg->weight_set_size > 0) {
254     r = decompile_weight_set(arg->weight_set, arg->weight_set_size, out);
255     if (r < 0)
256       return r;
257   }
258   if (arg->ids_size > 0) {
259     r = decompile_ids(arg->ids, arg->ids_size, out);
260     if (r < 0)
261       return r;
262   }
263   out << "  }\n";
264   return 0;
265 }
266
267 int CrushCompiler::decompile_choose_arg_map(crush_choose_arg_map arg_map,
268                                             ostream &out)
269 {
270   for (__u32 i = 0; i < arg_map.size; i++) {
271     if ((arg_map.args[i].ids_size == 0) &&
272         (arg_map.args[i].weight_set_size == 0))
273       continue;
274     int r = decompile_choose_arg(&arg_map.args[i], -1-i, out);
275     if (r < 0)
276       return r;
277   }
278   return 0;
279 }
280
281 int CrushCompiler::decompile_choose_args(const std::pair<const long unsigned int, crush_choose_arg_map> &i,
282                                          ostream &out)
283 {
284   out << "choose_args " << i.first << " {\n";
285   int r = decompile_choose_arg_map(i.second, out);
286   if (r < 0)
287     return r;
288   out << "}\n";
289   return 0;
290 }
291
292 int CrushCompiler::decompile(ostream &out)
293 {
294   out << "# begin crush map\n";
295
296   // only dump tunables if they differ from the defaults
297   if (crush.get_choose_local_tries() != 2)
298     out << "tunable choose_local_tries " << crush.get_choose_local_tries() << "\n";
299   if (crush.get_choose_local_fallback_tries() != 5)
300     out << "tunable choose_local_fallback_tries " << crush.get_choose_local_fallback_tries() << "\n";
301   if (crush.get_choose_total_tries() != 19)
302     out << "tunable choose_total_tries " << crush.get_choose_total_tries() << "\n";
303   if (crush.get_chooseleaf_descend_once() != 0)
304     out << "tunable chooseleaf_descend_once " << crush.get_chooseleaf_descend_once() << "\n";
305   if (crush.get_chooseleaf_vary_r() != 0)
306     out << "tunable chooseleaf_vary_r " << crush.get_chooseleaf_vary_r() << "\n";
307   if (crush.get_chooseleaf_stable() != 0)
308     out << "tunable chooseleaf_stable " << crush.get_chooseleaf_stable() << "\n";
309   if (crush.get_straw_calc_version() != 0)
310     out << "tunable straw_calc_version " << crush.get_straw_calc_version() << "\n";
311   if (crush.get_allowed_bucket_algs() != CRUSH_LEGACY_ALLOWED_BUCKET_ALGS)
312     out << "tunable allowed_bucket_algs " << crush.get_allowed_bucket_algs()
313         << "\n";
314
315   out << "\n# devices\n";
316   for (int i=0; i<crush.get_max_devices(); i++) {
317     out << "device " << i << " ";
318     print_item_name(out, i, crush);
319     print_item_class(out, i, crush);
320     out << "\n";
321   }
322   
323   out << "\n# types\n";
324   int n = crush.get_num_type_names();
325   for (int i=0; n; i++) {
326     const char *name = crush.get_type_name(i);
327     if (!name) {
328       if (i == 0) out << "type 0 osd\n";
329       continue;
330     }
331     n--;
332     out << "type " << i << " " << name << "\n";
333   }
334
335   out << "\n# buckets\n";
336   std::map<int, dcb_state_t> dcb_states;
337   for (int bucket = -1; bucket > -1-crush.get_max_buckets(); --bucket) {
338     int ret = decompile_bucket(bucket, dcb_states, out);
339     if (ret)
340       return ret;
341   }
342
343   out << "\n# rules\n";
344   for (int i=0; i<crush.get_max_rules(); i++) {
345     if (!crush.rule_exists(i))
346       continue;
347     out << "rule ";
348     if (crush.get_rule_name(i))
349       print_rule_name(out, i, crush);
350     out << " {\n";
351     out << "\tid " << i << "\n";
352     if (i != crush.get_rule_mask_ruleset(i)) {
353       out << "\t# WARNING: ruleset " << crush.get_rule_mask_ruleset(i) << " != id " << i << "; this will not recompile to the same map\n";
354     }
355
356     switch (crush.get_rule_mask_type(i)) {
357     case CEPH_PG_TYPE_REPLICATED:
358       out << "\ttype replicated\n";
359       break;
360     case CEPH_PG_TYPE_ERASURE:
361       out << "\ttype erasure\n";
362       break;
363     default:
364       out << "\ttype " << crush.get_rule_mask_type(i) << "\n";
365     }
366
367     out << "\tmin_size " << crush.get_rule_mask_min_size(i) << "\n";
368     out << "\tmax_size " << crush.get_rule_mask_max_size(i) << "\n";
369
370     for (int j=0; j<crush.get_rule_len(i); j++) {
371       switch (crush.get_rule_op(i, j)) {
372       case CRUSH_RULE_NOOP:
373         out << "\tstep noop\n";
374         break;
375       case CRUSH_RULE_TAKE:
376         out << "\tstep take ";
377         {
378           int step_item = crush.get_rule_arg1(i, j);
379           int original_item;
380           int c;
381           int res = crush.split_id_class(step_item, &original_item, &c);
382           if (res < 0)
383             return res;
384           if (c >= 0)
385             step_item = original_item;
386           print_item_name(out, step_item, crush);
387           if (c >= 0)
388             print_class(out, c, crush);
389         }
390         out << "\n";
391         break;
392       case CRUSH_RULE_EMIT:
393         out << "\tstep emit\n";
394         break;
395       case CRUSH_RULE_SET_CHOOSE_TRIES:
396         out << "\tstep set_choose_tries " << crush.get_rule_arg1(i, j)
397             << "\n";
398         break;
399       case CRUSH_RULE_SET_CHOOSE_LOCAL_TRIES:
400         out << "\tstep set_choose_local_tries " << crush.get_rule_arg1(i, j)
401             << "\n";
402         break;
403       case CRUSH_RULE_SET_CHOOSE_LOCAL_FALLBACK_TRIES:
404         out << "\tstep set_choose_local_fallback_tries " << crush.get_rule_arg1(i, j)
405             << "\n";
406         break;
407       case CRUSH_RULE_SET_CHOOSELEAF_TRIES:
408         out << "\tstep set_chooseleaf_tries " << crush.get_rule_arg1(i, j)
409             << "\n";
410         break;
411       case CRUSH_RULE_SET_CHOOSELEAF_VARY_R:
412         out << "\tstep set_chooseleaf_vary_r " << crush.get_rule_arg1(i, j)
413             << "\n";
414         break;
415       case CRUSH_RULE_SET_CHOOSELEAF_STABLE:
416         out << "\tstep set_chooseleaf_stable " << crush.get_rule_arg1(i, j)
417             << "\n";
418         break;
419       case CRUSH_RULE_CHOOSE_FIRSTN:
420         out << "\tstep choose firstn "
421             << crush.get_rule_arg1(i, j) 
422             << " type ";
423         print_type_name(out, crush.get_rule_arg2(i, j), crush);
424         out << "\n";
425         break;
426       case CRUSH_RULE_CHOOSE_INDEP:
427         out << "\tstep choose indep "
428             << crush.get_rule_arg1(i, j) 
429             << " type ";
430         print_type_name(out, crush.get_rule_arg2(i, j), crush);
431         out << "\n";
432         break;
433       case CRUSH_RULE_CHOOSELEAF_FIRSTN:
434         out << "\tstep chooseleaf firstn "
435             << crush.get_rule_arg1(i, j) 
436             << " type ";
437         print_type_name(out, crush.get_rule_arg2(i, j), crush);
438         out << "\n";
439         break;
440       case CRUSH_RULE_CHOOSELEAF_INDEP:
441         out << "\tstep chooseleaf indep "
442             << crush.get_rule_arg1(i, j) 
443             << " type ";
444         print_type_name(out, crush.get_rule_arg2(i, j), crush);
445         out << "\n";
446         break;
447       }
448     }
449     out << "}\n";
450   }
451   if (crush.choose_args.size() > 0) {
452     out << "\n# choose_args\n";
453     for (auto i : crush.choose_args) {
454       int ret = decompile_choose_args(i, out);
455       if (ret)
456         return ret;
457     }
458   }
459   out << "\n# end crush map" << std::endl;
460   return 0;
461 }
462
463
464 // ================================================================
465
466 string CrushCompiler::string_node(node_t &node)
467 {
468   return boost::trim_copy(string(node.value.begin(), node.value.end()));
469 }
470
471 int CrushCompiler::int_node(node_t &node) 
472 {
473   string str = string_node(node);
474   return strtol(str.c_str(), 0, 10);
475 }
476
477 float CrushCompiler::float_node(node_t &node)
478 {
479   string s = string_node(node);
480   return strtof(s.c_str(), 0);
481 }
482
483 int CrushCompiler::parse_device(iter_t const& i)
484 {
485   int id = int_node(i->children[1]);
486
487   string name = string_node(i->children[2]);
488   crush.set_item_name(id, name.c_str());
489   if (item_id.count(name)) {
490     err << "item " << name << " defined twice" << std::endl;
491     return -1;
492   }    
493   item_id[name] = id;
494   id_item[id] = name;
495
496   if (verbose) err << "device " << id << " '" << name << "'";
497
498   if (i->children.size() > 3) {
499     string c = string_node(i->children[4]);
500     crush.set_item_class(id, c);
501     if (verbose) err << " class" << " '" << c << "'" << std::endl;
502   } else {
503     if (verbose) err << std::endl;
504   }
505   return 0;
506 }
507
508 int CrushCompiler::parse_tunable(iter_t const& i)
509 {
510   string name = string_node(i->children[1]);
511   int val = int_node(i->children[2]);
512
513   if (name == "choose_local_tries")
514     crush.set_choose_local_tries(val);
515   else if (name == "choose_local_fallback_tries")
516     crush.set_choose_local_fallback_tries(val);
517   else if (name == "choose_total_tries")
518     crush.set_choose_total_tries(val);
519   else if (name == "chooseleaf_descend_once")
520     crush.set_chooseleaf_descend_once(val);
521   else if (name == "chooseleaf_vary_r")
522     crush.set_chooseleaf_vary_r(val);
523   else if (name == "chooseleaf_stable")
524     crush.set_chooseleaf_stable(val);
525   else if (name == "straw_calc_version")
526     crush.set_straw_calc_version(val);
527   else if (name == "allowed_bucket_algs")
528     crush.set_allowed_bucket_algs(val);
529   else {
530     err << "tunable " << name << " not recognized" << std::endl;
531     return -1;
532   }
533
534   /*
535
536     current crop of tunables are all now "safe".  re-enable this when we
537     add new ones that are ... new.
538
539   if (!unsafe_tunables) {
540     err << "tunables are NOT FULLY IMPLEMENTED; enable with --enable-unsafe-tunables to enable this feature" << std::endl;
541     return -1;
542   }
543   */
544
545   if (verbose) err << "tunable " << name << " " << val << std::endl;
546   return 0;
547 }
548
549 int CrushCompiler::parse_bucket_type(iter_t const& i)
550 {
551   int id = int_node(i->children[1]);
552   string name = string_node(i->children[2]);
553   if (verbose) err << "type " << id << " '" << name << "'" << std::endl;
554   type_id[name] = id;
555   crush.set_type_name(id, name.c_str());
556   return 0;
557 }
558
559 int CrushCompiler::parse_bucket(iter_t const& i)
560 {
561   string tname = string_node(i->children[0]);
562   if (!type_id.count(tname)) {
563     err << "bucket type '" << tname << "' is not defined" << std::endl;
564     return -1;
565   }
566   int type = type_id[tname];
567
568   string name = string_node(i->children[1]);
569   if (item_id.count(name)) {
570     err << "bucket or device '" << name << "' is already defined" << std::endl;
571     return -1;
572   }
573
574   int id = 0;  // none, yet!
575   int alg = -1;
576   int hash = 0;
577   set<int> used_items;
578   int size = 0;
579   map<int32_t, int32_t> class_id;
580   
581   for (unsigned p=3; p<i->children.size()-1; p++) {
582     iter_t sub = i->children.begin() + p;
583     string tag = string_node(sub->children[0]);
584     //err << "tag " << tag << std::endl;
585     if (tag == "id") {
586       int maybe_id = int_node(sub->children[1]);
587       if (verbose) err << "bucket " << name << " id " << maybe_id;
588       if (sub->children.size() > 2) {
589         string class_name = string_node(sub->children[3]);
590         // note that we do not verify class existence here,
591         // as this bucket might come from an empty shadow tree
592         // which currently has no OSDs but is still referenced by a rule!
593         int cid = crush.get_or_create_class_id(class_name);
594         if (class_id.count(cid) != 0) {
595           err << "duplicate device class " << class_name << " for bucket " << name << std::endl;
596           return -ERANGE;
597         }
598         class_id[cid] = maybe_id;
599         if (verbose) err << " class" << " '" << class_name << "'" << std::endl;
600       } else {
601         id = maybe_id;
602         if (verbose) err << std::endl;
603       }
604     } else if (tag == "alg") {
605       string a = string_node(sub->children[1]);
606       if (a == "uniform")
607         alg = CRUSH_BUCKET_UNIFORM;
608       else if (a == "list")
609         alg = CRUSH_BUCKET_LIST;
610       else if (a == "tree")
611         alg = CRUSH_BUCKET_TREE;
612       else if (a == "straw")
613         alg = CRUSH_BUCKET_STRAW;
614       else if (a == "straw2")
615         alg = CRUSH_BUCKET_STRAW2;
616       else {
617         err << "unknown bucket alg '" << a << "'" << std::endl << std::endl;
618         return -EINVAL;
619       }
620     }
621     else if (tag == "hash") {
622       string a = string_node(sub->children[1]);
623       if (a == "rjenkins1")
624         hash = CRUSH_HASH_RJENKINS1;
625       else
626         hash = atoi(a.c_str());
627     }
628     else if (tag == "item") {
629       // first, just determine which item pos's are already used
630       size++;
631       for (unsigned q = 2; q < sub->children.size(); q++) {
632         string tag = string_node(sub->children[q++]);
633         if (tag == "pos") {
634           int pos = int_node(sub->children[q]);
635           if (used_items.count(pos)) {
636             err << "item '" << string_node(sub->children[1]) << "' in bucket '" << name << "' has explicit pos " << pos << ", which is occupied" << std::endl;
637             return -1;
638           }
639           used_items.insert(pos);
640         }
641       }
642     }
643     else ceph_abort();
644   }
645
646   // now do the items.
647   if (!used_items.empty())
648     size = MAX(size, *used_items.rbegin());
649   vector<int> items(size);
650   vector<int> weights(size);
651
652   int curpos = 0;
653   unsigned bucketweight = 0;
654   bool have_uniform_weight = false;
655   unsigned uniform_weight = 0;
656   for (unsigned p=3; p<i->children.size()-1; p++) {
657     iter_t sub = i->children.begin() + p;
658     string tag = string_node(sub->children[0]);
659     if (tag == "item") {
660
661       string iname = string_node(sub->children[1]);
662       if (!item_id.count(iname)) {
663         err << "item '" << iname << "' in bucket '" << name << "' is not defined" << std::endl;
664         return -1;
665       }
666       int itemid = item_id[iname];
667
668       unsigned weight = 0x10000;
669       if (item_weight.count(itemid))
670         weight = item_weight[itemid];
671
672       int pos = -1;
673       for (unsigned q = 2; q < sub->children.size(); q++) {
674         string tag = string_node(sub->children[q++]);
675         if (tag == "weight") {
676           weight = float_node(sub->children[q]) * (float)0x10000;
677           if (weight > CRUSH_MAX_DEVICE_WEIGHT && itemid >= 0) {
678             err << "device weight limited to " << CRUSH_MAX_DEVICE_WEIGHT / 0x10000 << std::endl;
679             return -ERANGE;
680           }
681           else if (weight > CRUSH_MAX_BUCKET_WEIGHT && itemid < 0) {
682             err << "bucket weight limited to " << CRUSH_MAX_BUCKET_WEIGHT / 0x10000
683                 << " to prevent overflow" << std::endl;
684             return -ERANGE;
685           }
686         }
687         else if (tag == "pos") 
688           pos = int_node(sub->children[q]);
689         else
690           ceph_abort();
691
692       }
693       if (alg == CRUSH_BUCKET_UNIFORM) {
694         if (!have_uniform_weight) {
695           have_uniform_weight = true;
696           uniform_weight = weight;
697         } else {
698           if (uniform_weight != weight) {
699             err << "item '" << iname << "' in uniform bucket '" << name << "' has weight " << weight
700                 << " but previous item(s) have weight " << (float)uniform_weight/(float)0x10000
701                 << "; uniform bucket items must all have identical weights." << std::endl;
702             return -1;
703           }
704         }
705       }
706
707       if (pos >= size) {
708         err << "item '" << iname << "' in bucket '" << name << "' has pos " << pos << " >= size " << size << std::endl;
709         return -1;
710       }
711       if (pos < 0) {
712         while (used_items.count(curpos)) curpos++;
713         pos = curpos++;
714       }
715       //err << " item " << iname << " (" << itemid << ") pos " << pos << " weight " << weight << std::endl;
716       items[pos] = itemid;
717       weights[pos] = weight;
718
719       if (crush_addition_is_unsafe(bucketweight, weight)) {
720         err << "oh no! our bucket weights are overflowing all over the place, better lower the item weights" << std::endl;
721         return -ERANGE;
722       }
723
724       bucketweight += weight;
725     }
726   }
727
728   if (id == 0) {
729     for (id=-1; id_item.count(id); id--) ;
730     //err << "assigned id " << id << std::endl;
731   }
732
733   for (auto &i : class_id)
734     class_bucket[id][i.first] = i.second;
735
736   if (verbose) err << "bucket " << name << " (" << id << ") " << size << " items and weight "
737                    << (float)bucketweight / (float)0x10000 << std::endl;
738   id_item[id] = name;
739   item_id[name] = id;
740   item_weight[id] = bucketweight;
741   
742   assert(id != 0);
743   int idout;
744   int r = crush.add_bucket(id, alg, hash, type, size,
745                            &items[0], &weights[0], &idout);
746   if (r < 0) {
747     if (r == -EEXIST)
748       err << "Duplicate bucket id " << id << std::endl;
749     else
750       err << "add_bucket failed " << cpp_strerror(r) << std::endl;
751     return r;
752   }
753   r = crush.set_item_name(id, name.c_str());
754   return r;
755 }
756
757 int CrushCompiler::parse_rule(iter_t const& i)
758 {
759   int start;  // rule name is optional!
760  
761   string rname = string_node(i->children[1]);
762   if (rname != "{") {
763     if (rule_id.count(rname)) {
764       err << "rule name '" << rname << "' already defined\n" << std::endl;
765       return -1;
766     }
767     start = 4;
768   } else {
769     rname = string();
770     start = 3;
771   }
772
773   int ruleno = int_node(i->children[start]);
774
775   string tname = string_node(i->children[start+2]);
776   int type;
777   if (tname == "replicated")
778     type = CEPH_PG_TYPE_REPLICATED;
779   else if (tname == "erasure")
780     type = CEPH_PG_TYPE_ERASURE;
781   else 
782     ceph_abort();
783
784   int minsize = int_node(i->children[start+4]);
785   int maxsize = int_node(i->children[start+6]);
786   
787   int steps = i->children.size() - start - 8;
788   //err << "num steps " << steps << std::endl;
789
790   if (crush.rule_exists(ruleno)) {
791     err << "rule " << ruleno << " already exists" << std::endl;
792     return -1;
793   }
794   int r = crush.add_rule(ruleno, steps, type, minsize, maxsize);
795   if (r != ruleno) {
796     err << "unable to add rule id " << ruleno << " for rule '" << rname
797         << "'" << std::endl;
798     return -1;
799   }
800   if (rname.length()) {
801     crush.set_rule_name(ruleno, rname.c_str());
802     rule_id[rname] = ruleno;
803   }
804
805   int step = 0;
806   for (iter_t p = i->children.begin() + start + 7; step < steps; p++) {
807     iter_t s = p->children.begin() + 1;
808     int stepid = s->value.id().to_long();
809     switch (stepid) {
810     case crush_grammar::_step_take: 
811       {
812         string item = string_node(s->children[1]);
813         if (!item_id.count(item)) {
814           err << "in rule '" << rname << "' item '" << item << "' not defined" << std::endl;
815           return -1;
816         }
817         int id = item_id[item];
818         int c = -1;
819         string class_name;
820         if (s->children.size() > 2) {
821           class_name = string_node(s->children[3]);
822           c = crush.get_class_id(class_name);
823           if (c < 0)
824             return c;
825           if (crush.class_bucket.count(id) == 0) {
826             err << "in rule '" << rname << "' step take " << item
827                 << " has no class information" << std::endl;
828             return -EINVAL;
829           }
830           if (crush.class_bucket[id].count(c) == 0) {
831             err << "in rule '" << rname << "' step take " << item
832                 << " no matching bucket for class " << class_name << std::endl;
833             return -EINVAL;
834           }
835           id = crush.class_bucket[id][c];
836         }
837         if (verbose) {
838           err << "rule " << rname << " take " << item;
839           if (c < 0)
840             err << std::endl;
841           else
842             err << " remapped to " << crush.get_item_name(id) << std::endl;
843         }
844
845         crush.set_rule_step_take(ruleno, step++, id);
846       }
847       break;
848
849     case crush_grammar::_step_set_choose_tries:
850       {
851         int val = int_node(s->children[1]);
852         crush.set_rule_step_set_choose_tries(ruleno, step++, val);
853       }
854       break;
855
856     case crush_grammar::_step_set_choose_local_tries:
857       {
858         int val = int_node(s->children[1]);
859         crush.set_rule_step_set_choose_local_tries(ruleno, step++, val);
860       }
861       break;
862
863     case crush_grammar::_step_set_choose_local_fallback_tries:
864       {
865         int val = int_node(s->children[1]);
866         crush.set_rule_step_set_choose_local_fallback_tries(ruleno, step++, val);
867       }
868       break;
869
870     case crush_grammar::_step_set_chooseleaf_tries:
871       {
872         int val = int_node(s->children[1]);
873         crush.set_rule_step_set_chooseleaf_tries(ruleno, step++, val);
874       }
875       break;
876
877     case crush_grammar::_step_set_chooseleaf_vary_r:
878       {
879         int val = int_node(s->children[1]);
880         crush.set_rule_step_set_chooseleaf_vary_r(ruleno, step++, val);
881       }
882       break;
883
884     case crush_grammar::_step_set_chooseleaf_stable:
885       {
886         int val = int_node(s->children[1]);
887         crush.set_rule_step_set_chooseleaf_stable(ruleno, step++, val);
888       }
889       break;
890
891     case crush_grammar::_step_choose:
892     case crush_grammar::_step_chooseleaf:
893       {
894         string type = string_node(s->children[4]);
895         if (!type_id.count(type)) {
896           err << "in rule '" << rname << "' type '" << type << "' not defined" << std::endl;
897           return -1;
898         }
899         string choose = string_node(s->children[0]);
900         string mode = string_node(s->children[1]);
901         if (choose == "choose") {
902           if (mode == "firstn")
903             crush.set_rule_step_choose_firstn(ruleno, step++, int_node(s->children[2]), type_id[type]);
904           else if (mode == "indep")
905             crush.set_rule_step_choose_indep(ruleno, step++, int_node(s->children[2]), type_id[type]);
906           else ceph_abort();
907         } else if (choose == "chooseleaf") {
908           if (mode == "firstn") 
909             crush.set_rule_step_choose_leaf_firstn(ruleno, step++, int_node(s->children[2]), type_id[type]);
910           else if (mode == "indep")
911             crush.set_rule_step_choose_leaf_indep(ruleno, step++, int_node(s->children[2]), type_id[type]);
912           else ceph_abort();
913         } else ceph_abort();
914       }
915       break;
916
917     case crush_grammar::_step_emit:
918       crush.set_rule_step_emit(ruleno, step++);
919       break;
920
921     default:
922       err << "bad crush step " << stepid << std::endl;
923       return -1;
924     }
925   }
926   assert(step == steps);
927   return 0;
928 }
929
930 int CrushCompiler::parse_weight_set_weights(iter_t const& i, int bucket_id, crush_weight_set *weight_set)
931 {
932   // -2 for the enclosing [ ]
933   __u32 size = i->children.size() - 2;
934   __u32 bucket_size = crush.get_bucket_size(bucket_id);
935   if (size != bucket_size) {
936     err << bucket_id << " needs exactly " << bucket_size
937         << " weights but got " << size << std::endl;
938     return -1;
939   }
940   weight_set->size = size;
941   weight_set->weights = (__u32 *)calloc(weight_set->size, sizeof(__u32));
942   __u32 pos = 0;
943   for (iter_t p = i->children.begin() + 1; p != i->children.end(); p++, pos++)
944     if (pos < size)
945       weight_set->weights[pos] = float_node(*p) * (float)0x10000;
946   return 0;
947 }
948
949 int CrushCompiler::parse_weight_set(iter_t const& i, int bucket_id, crush_choose_arg *arg)
950 {
951   // -3 stands for the leading "weight_set" keyword and the enclosing [ ]
952   arg->weight_set_size = i->children.size() - 3;
953   arg->weight_set = (crush_weight_set *)calloc(arg->weight_set_size, sizeof(crush_weight_set));
954   __u32 pos = 0;
955   for (iter_t p = i->children.begin(); p != i->children.end(); p++) {
956     int r = 0;
957     switch((int)p->value.id().to_long()) {
958     case crush_grammar::_weight_set_weights:
959       if (pos < arg->weight_set_size) {
960         r = parse_weight_set_weights(p, bucket_id, &arg->weight_set[pos]);
961         pos++;
962       } else {
963         err << "invalid weight_set syntax" << std::endl;
964         r = -1;
965       }
966     }
967     if (r < 0)
968       return r;
969   }
970   return 0;
971 }
972
973 int CrushCompiler::parse_choose_arg_ids(iter_t const& i, int bucket_id, crush_choose_arg *arg)
974 {
975   // -3 for the leading "ids" keyword and the enclosing [ ]
976   __u32 size = i->children.size() - 3;
977   __u32 bucket_size = crush.get_bucket_size(bucket_id);
978   if (size != bucket_size) {
979     err << bucket_id << " needs exactly " << bucket_size
980         << " ids but got " << size << std::endl;
981     return -1;
982   }
983   arg->ids_size = size;
984   arg->ids = (__s32 *)calloc(arg->ids_size, sizeof(__s32));
985   __u32 pos = 0;
986   for (iter_t p = i->children.begin() + 2; pos < size; p++, pos++)
987     arg->ids[pos] = int_node(*p);
988   return 0;
989 }
990
991 int CrushCompiler::parse_choose_arg(iter_t const& i, crush_choose_arg *args)
992 {
993   int bucket_id = int_node(i->children[2]);
994   if (-1-bucket_id < 0 || -1-bucket_id >= crush.get_max_buckets()) {
995     err << bucket_id << " is out of range" << std::endl;
996     return -1;
997   }
998   if (!crush.bucket_exists(bucket_id)) {
999     err << bucket_id << " does not exist" << std::endl;
1000     return -1;
1001   }
1002   crush_choose_arg *arg = &args[-1-bucket_id];
1003   for (iter_t p = i->children.begin(); p != i->children.end(); p++) {
1004     int r = 0;
1005     switch((int)p->value.id().to_long()) {
1006     case crush_grammar::_weight_set:
1007       r = parse_weight_set(p, bucket_id, arg);
1008       break;
1009     case crush_grammar::_choose_arg_ids:
1010       r = parse_choose_arg_ids(p, bucket_id, arg);
1011       break;
1012     }
1013     if (r < 0)
1014       return r;
1015   }
1016   return 0;
1017 }
1018
1019 int CrushCompiler::parse_choose_args(iter_t const& i)
1020 {
1021   int choose_arg_index = int_node(i->children[1]);
1022   if (crush.choose_args.find(choose_arg_index) != crush.choose_args.end()) {
1023     err << choose_arg_index << " duplicated" << std::endl;
1024     return -1;
1025   }
1026   crush_choose_arg_map arg_map;
1027   arg_map.size = crush.get_max_buckets();
1028   arg_map.args = (crush_choose_arg *)calloc(arg_map.size, sizeof(crush_choose_arg));
1029   for (iter_t p = i->children.begin() + 2; p != i->children.end(); p++) {
1030     int r = 0;
1031     switch((int)p->value.id().to_long()) {
1032     case crush_grammar::_choose_arg:
1033       r = parse_choose_arg(p, arg_map.args);
1034       break;
1035     }
1036     if (r < 0) {
1037       crush.destroy_choose_args(arg_map);
1038       return r;
1039     }
1040   }
1041   crush.choose_args[choose_arg_index] = arg_map;
1042   return 0;
1043 }
1044
1045 void CrushCompiler::find_used_bucket_ids(iter_t const& i)
1046 {
1047   for (iter_t p = i->children.begin(); p != i->children.end(); p++) {
1048     if ((int)p->value.id().to_long() == crush_grammar::_bucket) {
1049       iter_t firstline = p->children.begin() + 3;
1050       string tag = string_node(firstline->children[0]);
1051       if (tag == "id") {
1052         int id = int_node(firstline->children[1]);
1053         //err << "saw bucket id " << id << std::endl;
1054         id_item[id] = string();
1055       }
1056     }
1057   }
1058 }
1059
1060 int CrushCompiler::parse_crush(iter_t const& i) 
1061
1062   find_used_bucket_ids(i);
1063   bool saw_rule = false;
1064   for (iter_t p = i->children.begin(); p != i->children.end(); p++) {
1065     int r = 0;
1066     switch (p->value.id().to_long()) {
1067     case crush_grammar::_tunable:
1068       r = parse_tunable(p);
1069       break;
1070     case crush_grammar::_device: 
1071       r = parse_device(p);
1072       break;
1073     case crush_grammar::_bucket_type: 
1074       r = parse_bucket_type(p);
1075       break;
1076     case crush_grammar::_bucket:
1077       if (saw_rule) {
1078         err << "buckets must be defined before rules" << std::endl;
1079         return -1;
1080       }
1081       r = parse_bucket(p);
1082       break;
1083     case crush_grammar::_crushrule:
1084       if (!saw_rule) {
1085         saw_rule = true;
1086         crush.populate_classes(class_bucket);
1087       }
1088       r = parse_rule(p);
1089       break;
1090     case crush_grammar::_choose_args:
1091       r = parse_choose_args(p);
1092       break;
1093     default:
1094       ceph_abort();
1095     }
1096     if (r < 0) {
1097       return r;
1098     }
1099   }
1100
1101   //err << "max_devices " << crush.get_max_devices() << std::endl;
1102   crush.finalize();
1103
1104   return 0;
1105
1106
1107 // squash runs of whitespace to one space, excepting newlines
1108 string CrushCompiler::consolidate_whitespace(string in)
1109 {
1110   string out;
1111
1112   bool white = false;
1113   for (unsigned p=0; p<in.length(); p++) {
1114     if (isspace(in[p]) && in[p] != '\n') {
1115       if (white)
1116         continue;
1117       white = true;
1118     } else {
1119       if (white) {
1120         if (out.length()) out += " ";
1121         white = false;
1122       }
1123       out += in[p];
1124     }
1125   }
1126   if (verbose > 3)
1127     err << " \"" << in << "\" -> \"" << out << "\"" << std::endl;
1128   return out;
1129 }
1130
1131 void CrushCompiler::dump(iter_t const& i, int ind) 
1132 {
1133   err << "dump"; 
1134   for (int j=0; j<ind; j++)
1135     cout << "\t"; 
1136   long id = i->value.id().to_long();
1137   err << id << "\t"; 
1138   err << "'" << string(i->value.begin(), i->value.end())  
1139       << "' " << i->children.size() << " children" << std::endl; 
1140   for (unsigned int j = 0; j < i->children.size(); j++)  
1141     dump(i->children.begin() + j, ind+1); 
1142 }
1143
1144 /**
1145 *  This function fix the problem like below
1146 *   rack using_foo { item foo }  
1147 *   host foo { ... }
1148 *
1149 *  if an item being used by a bucket is defined after that bucket. 
1150 *  CRUSH compiler will create a map by which we can 
1151 *  not identify that item when selecting in that bucket.
1152 **/
1153 int CrushCompiler::adjust_bucket_item_place(iter_t const &i)
1154 {
1155   map<string,set<string> > bucket_items;
1156   map<string,iter_t> bucket_itrer;
1157   vector<string> buckets;
1158   for (iter_t p = i->children.begin(); p != i->children.end(); ++p) {
1159     if ((int)p->value.id().to_long() == crush_grammar::_bucket) {
1160       string name = string_node(p->children[1]);
1161       buckets.push_back(name);
1162       bucket_itrer[name] = p;
1163       //skip non-bucket-item children in the bucket's parse tree
1164       for (unsigned q=3; q < p->children.size()-1; ++q) {
1165         iter_t sub = p->children.begin() + q;
1166         if ((int)sub->value.id().to_long() 
1167           == crush_grammar::_bucket_item) {
1168           string iname = string_node(sub->children[1]);
1169           bucket_items[name].insert(iname);
1170         }         
1171       }       
1172     }     
1173   }
1174   
1175   //adjust the bucket
1176   for (unsigned i=0; i < buckets.size(); ++i) { 
1177     for (unsigned j=i+1; j < buckets.size(); ++j) {
1178       if (bucket_items[buckets[i]].count(buckets[j])) {
1179         if (bucket_items[buckets[j]].count(buckets[i])) {
1180           err << "bucket  '" <<  buckets[i] << "' and bucket '"
1181           << buckets[j] << "' are included each other" << std::endl;
1182           return -1; 
1183         } else {  
1184            std::iter_swap(bucket_itrer[buckets[i]], bucket_itrer[buckets[j]]);
1185         } 
1186       } 
1187     }
1188   }
1189         
1190   return 0;
1191 }
1192
1193 int CrushCompiler::compile(istream& in, const char *infn)
1194 {
1195   if (!infn)
1196     infn = "<input>";
1197
1198   // always start with legacy tunables, so that the compiled result of
1199   // a given crush file is fixed for all time.
1200   crush.set_tunables_legacy();
1201
1202   string big;
1203   string str;
1204   int line = 1;
1205   map<int,int> line_pos;  // pos -> line
1206   map<int,string> line_val;
1207   while (getline(in, str)) {
1208     // remove newline
1209     int l = str.length();
1210     if (l && str[l - 1] == '\n')
1211       str.erase(l-1, 1);
1212
1213     line_val[line] = str;
1214
1215     // strip comment
1216     int n = str.find("#");
1217     if (n >= 0)
1218       str.erase(n, str.length()-n);
1219     
1220     if (verbose>1) err << line << ": " << str << std::endl;
1221
1222     // work around spirit crankiness by removing extraneous
1223     // whitespace.  there is probably a more elegant solution, but
1224     // this only broke with the latest spirit (with the switchover to
1225     // "classic"), i don't want to spend too much time figuring it
1226     // out.
1227     string stripped = consolidate_whitespace(str);
1228     if (stripped.length() && big.length() && big[big.length()-1] != ' ') big += " ";
1229
1230     line_pos[big.length()] = line;
1231     line++;
1232     big += stripped;
1233   }
1234   
1235   if (verbose > 2) err << "whole file is: \"" << big << "\"" << std::endl;
1236   
1237   crush_grammar crushg;
1238   const char *start = big.c_str();
1239   //tree_parse_info<const char *> info = ast_parse(start, crushg, space_p);
1240   tree_parse_info<> info = ast_parse(start, crushg, space_p);
1241   
1242   // parse error?
1243   if (!info.full) {
1244     int cpos = info.stop - start;
1245     //out << "cpos " << cpos << std::endl;
1246     //out << " linemap " << line_pos << std::endl;
1247     assert(!line_pos.empty());
1248     map<int,int>::iterator p = line_pos.upper_bound(cpos);
1249     if (p != line_pos.begin())
1250       --p;
1251     int line = p->second;
1252     int pos = cpos - p->first;
1253     err << infn << ":" << line //<< ":" << (pos+1)
1254         << " error: parse error at '" << line_val[line].substr(pos) << "'" << std::endl;
1255     return -1;
1256   }
1257   
1258   int r = adjust_bucket_item_place(info.trees.begin());
1259   if (r < 0) {
1260     return r;
1261   }
1262   //out << "parsing succeeded\n";
1263   //dump(info.trees.begin());
1264   return parse_crush(info.trees.begin());
1265 }