Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / crush / CrushWrapper.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) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
7  * Copyright (C) 2014 Red Hat <contact@redhat.com>
8  *
9  * Author: Loic Dachary <loic@dachary.org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Library Public License as published by
13  * the Free Software Foundation; either version 2, or (at your option)
14  * any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Library Public License for more details.
20  *
21  */
22
23 #include <iostream>
24 #include <gtest/gtest.h>
25
26 #include "include/stringify.h"
27 #include "common/ceph_argparse.h"
28 #include "global/global_init.h"
29 #include "global/global_context.h"
30 #include "include/Context.h"
31 #include "osd/osd_types.h"
32
33 #include "crush/CrushWrapper.h"
34
35 TEST(CrushWrapper, get_immediate_parent) {
36   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
37
38   const int ROOT_TYPE = 1;
39   c->set_type_name(ROOT_TYPE, "root");
40   const int OSD_TYPE = 0;
41   c->set_type_name(OSD_TYPE, "osd");
42
43   int rootno;
44   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
45                 ROOT_TYPE, 0, NULL, NULL, &rootno);
46   c->set_item_name(rootno, "default");
47
48   int item = 0;
49
50   pair <string,string> loc;
51   int ret;
52   loc = c->get_immediate_parent(item, &ret);
53   EXPECT_EQ(-ENOENT, ret);
54
55   {
56     map<string,string> loc;
57     loc["root"] = "default";
58
59     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
60                                 "osd.0", loc));
61   }
62
63   loc = c->get_immediate_parent(item, &ret);
64   EXPECT_EQ(0, ret);
65   EXPECT_EQ("root", loc.first);
66   EXPECT_EQ("default", loc.second);
67 }
68
69 TEST(CrushWrapper, move_bucket) {
70   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
71
72   const int ROOT_TYPE = 2;
73   c->set_type_name(ROOT_TYPE, "root");
74   const int HOST_TYPE = 1;
75   c->set_type_name(HOST_TYPE, "host");
76   const int OSD_TYPE = 0;
77   c->set_type_name(OSD_TYPE, "osd");
78
79   int root0;
80   EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
81                              ROOT_TYPE, 0, NULL, NULL, &root0));
82   EXPECT_EQ(0, c->set_item_name(root0, "root0"));
83
84   {
85     map<string,string> loc;
86     loc["root"] = "root0";
87     loc["host"] = "host0";
88
89     int item = 0;
90     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
91                                 "osd.0", loc));
92   }
93   int host0 = c->get_item_id("host0");
94
95   int root1;
96   EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
97                              ROOT_TYPE, 0, NULL, NULL, &root1));
98   EXPECT_EQ(0, c->set_item_name(root1, "root1"));
99
100   map<string,string> loc;
101   loc["root"] = "root1";
102
103   // 0 is not a valid bucket number, must be negative
104   EXPECT_EQ(-EINVAL, c->move_bucket(g_ceph_context, 0, loc));
105   // -100 is not an existing bucket
106   EXPECT_EQ(-ENOENT, c->move_bucket(g_ceph_context, -100, loc));
107   // move host0 from root0 to root1
108   {
109     pair <string,string> loc;
110     int ret;
111     loc = c->get_immediate_parent(host0, &ret);
112     EXPECT_EQ(0, ret);
113     EXPECT_EQ("root", loc.first);
114     EXPECT_EQ("root0", loc.second);
115   }
116   EXPECT_EQ(0, c->move_bucket(g_ceph_context, host0, loc));
117   {
118     pair <string,string> loc;
119     int ret;
120     loc = c->get_immediate_parent(host0, &ret);
121     EXPECT_EQ(0, ret);
122     EXPECT_EQ("root", loc.first);
123     EXPECT_EQ("root1", loc.second);
124   }
125 }
126
127 TEST(CrushWrapper, swap_bucket) {
128   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
129
130   const int ROOT_TYPE = 2;
131   c->set_type_name(ROOT_TYPE, "root");
132   const int HOST_TYPE = 1;
133   c->set_type_name(HOST_TYPE, "host");
134   const int OSD_TYPE = 0;
135   c->set_type_name(OSD_TYPE, "osd");
136
137   int root;
138   EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
139                              ROOT_TYPE, 0, NULL, NULL, &root));
140   EXPECT_EQ(0, c->set_item_name(root, "root"));
141
142   int a, b;
143   EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
144                              HOST_TYPE, 0, NULL, NULL, &a));
145   EXPECT_EQ(0, c->set_item_name(a, "a"));
146   EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW2, CRUSH_HASH_RJENKINS1,
147                              HOST_TYPE, 0, NULL, NULL, &b));
148   EXPECT_EQ(0, c->set_item_name(b, "b"));
149
150   {
151     map<string,string> loc;
152     loc["root"] = "root";
153     EXPECT_EQ(0, c->move_bucket(g_ceph_context, a, loc));
154   }
155   {
156     map<string,string> loc;
157     loc["root"] = "root";
158     loc["host"] = "a";
159     EXPECT_EQ(0, c->insert_item(g_ceph_context, 0, 1.0, "osd.0", loc));
160     EXPECT_EQ(0, c->insert_item(g_ceph_context, 1, 1.0, "osd.1", loc));
161     EXPECT_EQ(0, c->insert_item(g_ceph_context, 2, 1.0, "osd.2", loc));
162   }
163   {
164     map<string,string> loc;
165     loc["host"] = "b";
166     EXPECT_EQ(0, c->insert_item(g_ceph_context, 3, 1.0, "osd.3", loc));
167   }
168   ASSERT_EQ(0x30000, c->get_item_weight(a));
169   ASSERT_EQ(string("a"), c->get_item_name(a));
170   ASSERT_EQ(0x10000, c->get_item_weight(b));
171   ASSERT_EQ(string("b"), c->get_item_name(b));
172   ASSERT_EQ(a, c->get_bucket_item(root, 0));
173   ASSERT_EQ(0, c->get_bucket_item(a, 0));
174   ASSERT_EQ(1, c->get_bucket_item(a, 1));
175   ASSERT_EQ(2, c->get_bucket_item(a, 2));
176   ASSERT_EQ(3, c->get_bucket_item(b, 0));
177
178   c->swap_bucket(g_ceph_context, a, b);
179   ASSERT_EQ(0x30000, c->get_item_weight(b));
180   ASSERT_EQ(string("a"), c->get_item_name(b));
181   ASSERT_EQ(0x10000, c->get_item_weight(a));
182   ASSERT_EQ(string("b"), c->get_item_name(a));
183   ASSERT_EQ(a, c->get_bucket_item(root, 0));
184   ASSERT_EQ(0, c->get_bucket_item(b, 0));
185   ASSERT_EQ(1, c->get_bucket_item(b, 1));
186   ASSERT_EQ(2, c->get_bucket_item(b, 2));
187   ASSERT_EQ(3, c->get_bucket_item(a, 0));
188 }
189
190 TEST(CrushWrapper, rename_bucket_or_item) {
191   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
192
193   const int ROOT_TYPE = 2;
194   c->set_type_name(ROOT_TYPE, "root");
195   const int HOST_TYPE = 1;
196   c->set_type_name(HOST_TYPE, "host");
197   const int OSD_TYPE = 0;
198   c->set_type_name(OSD_TYPE, "osd");
199
200   int root0;
201   EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
202                              ROOT_TYPE, 0, NULL, NULL, &root0));
203   EXPECT_EQ(0, c->set_item_name(root0, "root0"));
204
205   int item = 0;
206   {
207     map<string,string> loc;
208     loc["root"] = "root0";
209     loc["host"] = "host0";
210
211     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
212                                 "osd.0", loc));
213   }
214   item++;
215   {
216     map<string,string> loc;
217     loc["root"] = "root0";
218     loc["host"] = "host1";
219
220     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
221                                 "osd.1", loc));
222   }
223
224   stringstream ss;
225   EXPECT_EQ(-EINVAL, c->can_rename_item("host0", "????", &ss));
226   EXPECT_EQ(-EINVAL, c->rename_item("host0", "????", &ss));
227   EXPECT_EQ(-EINVAL, c->can_rename_bucket("host0", "????", &ss));
228   EXPECT_EQ(-EINVAL, c->rename_bucket("host0", "????", &ss));
229
230   EXPECT_EQ(-EEXIST, c->can_rename_item("host0", "host1", &ss));
231   EXPECT_EQ(-EEXIST, c->rename_item("host0", "host1", &ss));
232   EXPECT_EQ(-EEXIST, c->can_rename_bucket("host0", "host1", &ss));
233   EXPECT_EQ(-EEXIST, c->rename_bucket("host0", "host1", &ss));
234
235   EXPECT_EQ(-EALREADY, c->can_rename_item("gone", "host1", &ss));
236   EXPECT_EQ(-EALREADY, c->rename_item("gone", "host1", &ss));
237   EXPECT_EQ(-EALREADY, c->can_rename_bucket("gone", "host1", &ss));
238   EXPECT_EQ(-EALREADY, c->rename_bucket("gone", "host1", &ss));
239
240   EXPECT_EQ(-ENOENT, c->can_rename_item("doesnotexist", "somethingelse", &ss));
241   EXPECT_EQ(-ENOENT, c->rename_item("doesnotexist", "somethingelse", &ss));
242   EXPECT_EQ(-ENOENT, c->can_rename_bucket("doesnotexist", "somethingelse", &ss));
243   EXPECT_EQ(-ENOENT, c->rename_bucket("doesnotexist", "somethingelse", &ss));
244
245   EXPECT_EQ(-ENOTDIR, c->can_rename_bucket("osd.1", "somethingelse", &ss));
246   EXPECT_EQ(-ENOTDIR, c->rename_bucket("osd.1", "somethingelse", &ss));
247
248   int host0id = c->get_item_id("host0");
249   EXPECT_EQ(0, c->rename_bucket("host0", "host0renamed", &ss));
250   EXPECT_EQ(host0id, c->get_item_id("host0renamed"));
251
252   int osd0id = c->get_item_id("osd0");
253   EXPECT_EQ(0, c->rename_item("osd.0", "osd0renamed", &ss));
254   EXPECT_EQ(osd0id, c->get_item_id("osd0renamed"));
255 }
256
257 TEST(CrushWrapper, check_item_loc) {
258   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
259   int item = 0;
260   float expected_weight = 1.0;
261
262   // fail if loc is empty
263   {
264     float weight;
265     map<string,string> loc;
266     EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
267   }
268
269   const int ROOT_TYPE = 2;
270   c->set_type_name(ROOT_TYPE, "root");
271   const int HOST_TYPE = 1;
272   c->set_type_name(HOST_TYPE, "host");
273   const int OSD_TYPE = 0;
274   c->set_type_name(OSD_TYPE, "osd");
275
276   int rootno;
277   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
278                 ROOT_TYPE, 0, NULL, NULL, &rootno);
279   c->set_item_name(rootno, "default");
280
281   // fail because the item is not found at the specified location
282   {
283     float weight;
284     map<string,string> loc;
285     loc["root"] = "default";
286     EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
287   }
288   // fail because the bucket name does not match an existing bucket
289   {
290     float weight;
291     map<string,string> loc;
292     loc["root"] = "default";
293     const string HOST("host0");
294     loc["host"] = HOST;
295     EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
296   }
297   const string OSD("osd.0");
298   {
299     map<string,string> loc;
300     loc["root"] = "default";
301     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, expected_weight,
302                                 OSD, loc));
303   }
304   // fail because osd.0 is not a bucket and must not be in loc, in
305   // addition to being of the wrong type
306   {
307     float weight;
308     map<string,string> loc;
309     loc["root"] = "osd.0";
310     EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
311   }
312   // succeed and retrieves the expected weight
313   {
314     float weight;
315     map<string,string> loc;
316     loc["root"] = "default";
317     EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
318     EXPECT_EQ(expected_weight, weight);
319   }
320 }
321
322 TEST(CrushWrapper, update_item) {
323   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
324
325   const int ROOT_TYPE = 2;
326   c->set_type_name(ROOT_TYPE, "root");
327   const int HOST_TYPE = 1;
328   c->set_type_name(HOST_TYPE, "host");
329   const int OSD_TYPE = 0;
330   c->set_type_name(OSD_TYPE, "osd");
331
332   int rootno;
333   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
334                 ROOT_TYPE, 0, NULL, NULL, &rootno);
335   c->set_item_name(rootno, "default");
336
337   const string HOST0("host0");
338   int host0;
339   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
340                 HOST_TYPE, 0, NULL, NULL, &host0);
341   c->set_item_name(host0, HOST0);
342
343   const string HOST1("host1");
344   int host1;
345   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
346                 HOST_TYPE, 0, NULL, NULL, &host1);
347   c->set_item_name(host1, HOST1);
348
349   int item = 0;
350
351   // fail if invalid names anywhere in loc
352   {
353     map<string,string> loc;
354     loc["rack"] = "\001";
355     EXPECT_EQ(-EINVAL, c->update_item(g_ceph_context, item, 1.0,
356                                       "osd." + stringify(item), loc));
357   }
358   // fail if invalid item name
359   {
360     map<string,string> loc;
361     EXPECT_EQ(-EINVAL, c->update_item(g_ceph_context, item, 1.0,
362                                       "\005", loc));
363   }
364   const string OSD0("osd.0");
365   const string OSD1("osd.1");
366   float original_weight = 1.0;
367   float modified_weight = 2.0;
368   float weight;
369
370   map<string,string> loc;
371   loc["root"] = "default";
372   loc["host"] = HOST0;
373   EXPECT_GE(0.0, c->get_item_weightf(host0));
374   EXPECT_EQ(0, c->insert_item(g_ceph_context, item, original_weight,
375                               OSD0, loc));
376
377   // updating nothing changes nothing
378   EXPECT_EQ(OSD0, c->get_item_name(item));
379   EXPECT_EQ(original_weight, c->get_item_weightf(item));
380   EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
381   EXPECT_EQ(0, c->update_item(g_ceph_context, item, original_weight,
382                               OSD0, loc));
383   EXPECT_EQ(OSD0, c->get_item_name(item));
384   EXPECT_EQ(original_weight, c->get_item_weightf(item));
385   EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
386
387   // update the name and weight of the item but not the location
388   EXPECT_EQ(OSD0, c->get_item_name(item));
389   EXPECT_EQ(original_weight, c->get_item_weightf(item));
390   EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
391   EXPECT_EQ(1, c->update_item(g_ceph_context, item, modified_weight,
392                               OSD1, loc));
393   EXPECT_EQ(OSD1, c->get_item_name(item));
394   EXPECT_EQ(modified_weight, c->get_item_weightf(item));
395   EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
396   c->set_item_name(item, OSD0);
397   c->adjust_item_weightf(g_ceph_context, item, original_weight);
398
399   // update the name and weight of the item and change its location
400   map<string,string> other_loc;
401   other_loc["root"] = "default";
402   other_loc["host"] = HOST1;
403
404   EXPECT_EQ(OSD0, c->get_item_name(item));
405   EXPECT_EQ(original_weight, c->get_item_weightf(item));
406   EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
407   EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, other_loc, &weight));
408   EXPECT_EQ(1, c->update_item(g_ceph_context, item, modified_weight,
409                               OSD1, other_loc));
410   EXPECT_EQ(OSD1, c->get_item_name(item));
411   EXPECT_EQ(modified_weight, c->get_item_weightf(item));
412   EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
413   EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, other_loc, &weight));
414 }
415
416 TEST(CrushWrapper, adjust_item_weight) {
417   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
418
419   const int ROOT_TYPE = 2;
420   c->set_type_name(ROOT_TYPE, "root");
421   const int HOST_TYPE = 1;
422   c->set_type_name(HOST_TYPE, "host");
423   const int OSD_TYPE = 0;
424   c->set_type_name(OSD_TYPE, "osd");
425
426   int rootno;
427   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
428                 ROOT_TYPE, 0, NULL, NULL, &rootno);
429   c->set_item_name(rootno, "default");
430
431   const string HOST0("host0");
432   int host0;
433   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
434                 HOST_TYPE, 0, NULL, NULL, &host0);
435   c->set_item_name(host0, HOST0);
436
437   const string FAKE("fake");
438   int hostfake;
439   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
440                 HOST_TYPE, 0, NULL, NULL, &hostfake);
441   c->set_item_name(hostfake, FAKE);
442
443   int item = 0;
444
445   // construct crush map
446
447   {
448     map<string,string> loc;
449     loc["host"] = "host0";
450     float host_weight = 2.0;
451     int bucket_id = 0;
452
453     item = 0;
454     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
455                                 "osd." + stringify(item), loc));
456     item = 1;
457     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
458                                 "osd." + stringify(item), loc));
459
460     bucket_id = c->get_item_id("host0");
461     EXPECT_EQ(true, c->bucket_exists(bucket_id));
462     EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
463
464     map<string,string> bloc;
465     bloc["root"] = "default";
466     EXPECT_EQ(0, c->insert_item(g_ceph_context, host0, host_weight,
467                                 HOST0, bloc));
468   }
469
470   {
471     map<string,string> loc;
472     loc["host"] = "fake";
473     float host_weight = 2.0;
474     int bucket_id = 0;
475
476     item = 0;
477     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
478                                 "osd." + stringify(item), loc));
479     item = 1;
480     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
481                                 "osd." + stringify(item), loc));
482
483     bucket_id = c->get_item_id("fake");
484     EXPECT_EQ(true, c->bucket_exists(bucket_id));
485     EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
486
487     map<string,string> bloc;
488     bloc["root"] = "default";
489     EXPECT_EQ(0, c->insert_item(g_ceph_context, hostfake, host_weight,
490                                 FAKE, bloc));
491   }
492
493   //
494   //   When there is:
495   //
496   //   default --> host0 --> osd.0 1.0
497   //           |         |
498   //           |         +-> osd.1 1.0
499   //           |
500   //           +-> fake  --> osd.0 1.0
501   //                     |
502   //                     +-> osd.1 1.0
503   //
504   //   Trying to adjust osd.0 weight to 2.0 in all buckets
505   //   Trying to adjust osd.1 weight to 2.0 in host=fake
506   //
507   //   So the crush map will be:
508   //
509   //   default --> host0 --> osd.0 2.0
510   //           |         |
511   //           |         +-> osd.1 1.0
512   //           |
513   //           +-> fake  --> osd.0 2.0
514   //                     |
515   //                     +-> osd.1 2.0
516   //
517
518   float original_weight = 1.0;
519   float modified_weight = 2.0;
520   map<string,string> loc_one, loc_two;
521   loc_one["host"] = "host0";
522   loc_two["host"] = "fake";
523
524   item = 0;
525   EXPECT_EQ(2, c->adjust_item_weightf(g_ceph_context, item, modified_weight));
526   EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_one));
527   EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_two));
528
529   item = 1;
530   EXPECT_EQ(1, c->adjust_item_weightf_in_loc(g_ceph_context, item, modified_weight, loc_two));
531   EXPECT_EQ(original_weight, c->get_item_weightf_in_loc(item, loc_one));
532   EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_two));
533 }
534
535 TEST(CrushWrapper, adjust_subtree_weight) {
536   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
537
538   const int ROOT_TYPE = 2;
539   c->set_type_name(ROOT_TYPE, "root");
540   const int HOST_TYPE = 1;
541   c->set_type_name(HOST_TYPE, "host");
542   const int OSD_TYPE = 0;
543   c->set_type_name(OSD_TYPE, "osd");
544
545   int rootno;
546   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
547                 ROOT_TYPE, 0, NULL, NULL, &rootno);
548   c->set_item_name(rootno, "default");
549
550   const string HOST0("host0");
551   int host0;
552   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
553                 HOST_TYPE, 0, NULL, NULL, &host0);
554   c->set_item_name(host0, HOST0);
555
556   const string FAKE("fake");
557   int hostfake;
558   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
559                 HOST_TYPE, 0, NULL, NULL, &hostfake);
560   c->set_item_name(hostfake, FAKE);
561
562   int item = 0;
563
564   // construct crush map
565
566   {
567     map<string,string> loc;
568     loc["host"] = "host0";
569     float host_weight = 2.0;
570     int bucket_id = 0;
571
572     item = 0;
573     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
574                                 "osd." + stringify(item), loc));
575     item = 1;
576     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
577                                 "osd." + stringify(item), loc));
578
579     bucket_id = c->get_item_id("host0");
580     EXPECT_EQ(true, c->bucket_exists(bucket_id));
581     EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
582
583     map<string,string> bloc;
584     bloc["root"] = "default";
585     EXPECT_EQ(0, c->insert_item(g_ceph_context, host0, host_weight,
586                                 HOST0, bloc));
587   }
588
589   {
590     map<string,string> loc;
591     loc["host"] = "fake";
592     float host_weight = 2.0;
593     int bucket_id = 0;
594
595     item = 0;
596     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
597                                 "osd." + stringify(item), loc));
598     item = 1;
599     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
600                                 "osd." + stringify(item), loc));
601
602     bucket_id = c->get_item_id("fake");
603     EXPECT_EQ(true, c->bucket_exists(bucket_id));
604     EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
605
606     map<string,string> bloc;
607     bloc["root"] = "default";
608     EXPECT_EQ(0, c->insert_item(g_ceph_context, hostfake, host_weight,
609                                 FAKE, bloc));
610   }
611
612   //cout << "--------before---------" << std::endl;
613   //c->dump_tree(&cout, NULL);
614   ASSERT_EQ(c->get_bucket_weight(host0), 131072);
615   ASSERT_EQ(c->get_bucket_weight(rootno), 262144);
616
617   int r = c->adjust_subtree_weightf(g_ceph_context, host0, 2.0);
618   ASSERT_EQ(r, 2); // 2 items changed
619
620   //cout << "--------after---------" << std::endl;
621   //c->dump_tree(&cout, NULL);
622
623   ASSERT_EQ(c->get_bucket_weight(host0), 262144);
624   ASSERT_EQ(c->get_item_weight(host0), 262144);
625   ASSERT_EQ(c->get_bucket_weight(rootno), 262144 + 131072);
626 }
627
628 TEST(CrushWrapper, insert_item) {
629   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
630
631   const int ROOT_TYPE = 2;
632   c->set_type_name(ROOT_TYPE, "root");
633   const int HOST_TYPE = 1;
634   c->set_type_name(HOST_TYPE, "host");
635   const int OSD_TYPE = 0;
636   c->set_type_name(OSD_TYPE, "osd");
637
638   int rootno;
639   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
640                 ROOT_TYPE, 0, NULL, NULL, &rootno);
641   c->set_item_name(rootno, "default");
642
643   int item = 0;
644
645   // invalid names anywhere in loc trigger an error
646   {
647     map<string,string> loc;
648     loc["host"] = "\001";
649     EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
650                                       "osd." + stringify(item), loc));
651   }
652
653   // insert an item in an existing bucket
654   {
655     map<string,string> loc;
656     loc["root"] = "default";
657
658     item++;
659     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
660                                 "osd." + stringify(item), loc));
661     int another_item = item + 1;
662     EXPECT_EQ(-EEXIST, c->insert_item(g_ceph_context, another_item, 1.0,
663                                       "osd." + stringify(item), loc));
664   }
665   // implicit creation of a bucket 
666   {
667     string name = "NAME";
668     map<string,string> loc;
669     loc["root"] = "default";
670     loc["host"] = name;
671
672     item++;
673     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
674                                 "osd." + stringify(item), loc));
675   }
676   // adding to an existing item name that is not associated with a bucket
677   {
678     string name = "ITEM_WITHOUT_BUCKET";
679     map<string,string> loc;
680     loc["root"] = "default";
681     loc["host"] = name;
682     item++;
683     c->set_item_name(item, name);
684
685     item++;
686     EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
687                                       "osd." + stringify(item), loc));
688   }
689   // 
690   //   When there is:
691   //
692   //   default --> host0 --> item
693   //
694   //   Trying to insert the same item higher in the hirarchy will fail
695   //   because it would create a loop.
696   //
697   //   default --> host0 --> item
698   //           |
699   //           +-> item 
700   //
701   {
702     item++;
703     {
704       map<string,string> loc;
705       loc["root"] = "default";
706       loc["host"] = "host0";
707
708       EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
709                                   "osd." + stringify(item), loc));
710     }
711     {
712       map<string,string> loc;
713       loc["root"] = "default";
714
715       EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
716                                         "osd." + stringify(item), loc));
717     }
718   }
719   // 
720   //   When there is:
721   //
722   //   default --> host0
723   //
724   //   Trying to insert default under host0 must fail
725   //   because it would create a loop.
726   //
727   //   default --> host0 --> default
728   //
729   {
730     map<string,string> loc;
731     loc["host"] = "host0";
732
733     EXPECT_EQ(-ELOOP, c->insert_item(g_ceph_context, rootno, 1.0,
734                                      "default", loc));
735   }
736   // fail when mapping a bucket to the wrong type
737   {
738     // create an OSD bucket
739     int osdno;
740     int r = c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
741                           10, 0, NULL, NULL, &osdno);
742     ASSERT_EQ(0, r);
743     c->set_item_name(osdno, "myosd");
744     map<string,string> loc;
745     loc["root"] = "default";
746     // wrongfully pretend the osd is of type host
747     loc["host"] = "myosd";
748
749     item++;
750     EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
751                                       "osd." + stringify(item), loc));
752   }
753   // fail when no location 
754   {
755     map<string,string> loc;
756     item++;
757     EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
758                                       "osd." + stringify(item), loc));
759   }
760 }
761
762 TEST(CrushWrapper, remove_item) {
763   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
764
765   const int ROOT_TYPE = 2;
766   c->set_type_name(ROOT_TYPE, "root");
767   const int HOST_TYPE = 1;
768   c->set_type_name(HOST_TYPE, "host");
769   const int OSD_TYPE = 0;
770   c->set_type_name(OSD_TYPE, "osd");
771
772   {
773     int root;
774     ASSERT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
775                                ROOT_TYPE, 0, NULL, NULL, &root));
776     c->set_item_name(root, "root0");
777   }
778
779   {
780     int host;
781     c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
782                   HOST_TYPE, 0, NULL, NULL, &host);
783     c->set_item_name(host, "host0");
784   }
785
786   const int num_osd = 12;
787   {
788     map<string, string> loc = {{"root", "root0"},
789                                {"host", "host0"}};
790     string name{"osd."};
791     for (int item = 0; item < num_osd; item++) {
792       ASSERT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
793                                   name + to_string(item), loc));
794     }
795   }
796   const int item_to_remove = num_osd / 2;
797   map<string, string> loc;
798   loc.insert(c->get_immediate_parent(item_to_remove));
799   ASSERT_EQ(0, c->remove_item(g_ceph_context, item_to_remove, true));
800   float weight;
801   EXPECT_FALSE(c->check_item_loc(g_ceph_context, item_to_remove, loc, &weight));
802 }
803
804 TEST(CrushWrapper, item_bucket_names) {
805   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
806   int index = 123;
807   string name = "NAME";
808   EXPECT_EQ(-EINVAL, c->set_item_name(index, "\001"));
809   EXPECT_EQ(0, c->set_item_name(index, name));
810   EXPECT_TRUE(c->name_exists(name));
811   EXPECT_TRUE(c->item_exists(index));
812   EXPECT_EQ(index, c->get_item_id(name));
813   EXPECT_EQ(name, c->get_item_name(index));
814 }
815
816 TEST(CrushWrapper, bucket_types) {
817   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
818   int index = 123;
819   string name = "NAME";
820   c->set_type_name(index, name);
821   EXPECT_EQ(1, c->get_num_type_names());
822   EXPECT_EQ(index, c->get_type_id(name));
823   EXPECT_EQ(name, c->get_type_name(index));
824 }
825
826 TEST(CrushWrapper, is_valid_crush_name) {
827   EXPECT_TRUE(CrushWrapper::is_valid_crush_name("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012456789-_"));
828   EXPECT_FALSE(CrushWrapper::is_valid_crush_name(""));
829   EXPECT_FALSE(CrushWrapper::is_valid_crush_name("\001"));
830 }
831
832 TEST(CrushWrapper, is_valid_crush_loc) {
833   map<string,string> loc;
834   EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
835   loc["good"] = "better";
836   EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
837   {
838     map<string,string> loc;
839     loc["\005"] = "default";
840     EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
841   }
842   {
843     map<string,string> loc;
844     loc["host"] = "\003";
845     EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
846   }
847 }
848
849 TEST(CrushWrapper, dump_rules) {
850   std::unique_ptr<CrushWrapper> c(new CrushWrapper);
851
852   const int ROOT_TYPE = 1;
853   c->set_type_name(ROOT_TYPE, "root");
854   const int OSD_TYPE = 0;
855   c->set_type_name(OSD_TYPE, "osd");
856
857   string failure_domain_type("osd");
858   string root_name("default");
859   int rootno;
860   c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
861                 ROOT_TYPE, 0, NULL, NULL, &rootno);
862   c->set_item_name(rootno, root_name);
863
864   int item = 0;
865
866   pair <string,string> loc;
867   int ret;
868   loc = c->get_immediate_parent(item, &ret);
869   EXPECT_EQ(-ENOENT, ret);
870
871   {
872     map<string,string> loc;
873     loc["root"] = root_name;
874
875     EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
876                                 "osd.0", loc));
877   }
878
879   // no rule by default
880   {
881     Formatter *f = Formatter::create("json-pretty");
882     f->open_array_section("rules");
883     c->dump_rules(f);
884     f->close_section();
885     stringstream ss;
886     f->flush(ss);
887     delete f;
888     EXPECT_EQ("[]\n", ss.str());
889   }
890
891   string name("NAME");
892   int rule = c->add_simple_rule(name, root_name, failure_domain_type, "",
893                                    "firstn", pg_pool_t::TYPE_ERASURE);
894   EXPECT_EQ(0, rule);
895
896   {
897     Formatter *f = Formatter::create("xml");
898     c->dump_rules(f);
899     stringstream ss;
900     f->flush(ss);
901     delete f;
902     EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
903   }
904
905   {
906     Formatter *f = Formatter::create("xml");
907     c->dump_rule(rule, f);
908     stringstream ss;
909     f->flush(ss);
910     delete f;
911     EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
912     EXPECT_NE(string::npos,
913               ss.str().find("<item_name>default</item_name></step>"));
914   }
915
916   map<int,float> wm;
917   c->get_rule_weight_osd_map(0, &wm);
918   ASSERT_TRUE(wm.size() == 1);
919   ASSERT_TRUE(wm[0] == 1.0);
920 }
921
922 TEST(CrushWrapper, distance) {
923   CrushWrapper c;
924   c.create();
925   c.set_type_name(1, "host");
926   c.set_type_name(2, "rack");
927   c.set_type_name(3, "root");
928   int bno;
929   int r = c.add_bucket(0, CRUSH_BUCKET_STRAW,
930                        CRUSH_HASH_DEFAULT, 3, 0, NULL,
931                        NULL, &bno);
932   ASSERT_EQ(0, r);
933   ASSERT_EQ(-1, bno);
934   c.set_item_name(bno, "default");
935
936   c.set_max_devices(10);
937
938   //JSONFormatter jf(true);
939
940   map<string,string> loc;
941   loc["host"] = "a1";
942   loc["rack"] = "a";
943   loc["root"] = "default";
944   c.insert_item(g_ceph_context, 0, 1, "osd.0", loc);
945
946   loc.clear();
947   loc["host"] = "a2";
948   loc["rack"] = "a";
949   loc["root"] = "default";
950   c.insert_item(g_ceph_context, 1, 1, "osd.1", loc);
951
952   loc.clear();
953   loc["host"] = "b1";
954   loc["rack"] = "b";
955   loc["root"] = "default";
956   c.insert_item(g_ceph_context, 2, 1, "osd.2", loc);
957
958   loc.clear();
959   loc["host"] = "b2";
960   loc["rack"] = "b";
961   loc["root"] = "default";
962   c.insert_item(g_ceph_context, 3, 1, "osd.3", loc);
963
964   vector<pair<string,string> > ol;
965   c.get_full_location_ordered(3, ol);
966   ASSERT_EQ(3u, ol.size());
967   ASSERT_EQ(make_pair(string("host"),string("b2")), ol[0]);
968   ASSERT_EQ(make_pair(string("rack"),string("b")), ol[1]);
969   ASSERT_EQ(make_pair(string("root"),string("default")), ol[2]);
970
971   //c.dump(&jf);
972   //jf.flush(cout);
973
974   multimap<string,string> p;
975   p.insert(make_pair("host","b2"));
976   p.insert(make_pair("rack","b"));
977   p.insert(make_pair("root","default"));
978   ASSERT_EQ(3, c.get_common_ancestor_distance(g_ceph_context, 0, p));
979   ASSERT_EQ(3, c.get_common_ancestor_distance(g_ceph_context, 1, p));
980   ASSERT_EQ(2, c.get_common_ancestor_distance(g_ceph_context, 2, p));
981   ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 3, p));
982   ASSERT_EQ(-ENOENT, c.get_common_ancestor_distance(g_ceph_context, 123, p));
983
984   // make sure a "multipath" location will reflect a minimal
985   // distance for both paths
986   p.insert(make_pair("host","b1"));
987   ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 2, p));
988   ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 3, p));
989 }
990
991 TEST(CrushWrapper, choose_args_compat) {
992   CrushWrapper c;
993   c.create();
994   c.set_type_name(1, "host");
995   c.set_type_name(2, "rack");
996   c.set_type_name(3, "root");
997
998   int weight = 12;
999
1000   map<string,string> loc;
1001   loc["host"] = "b1";
1002   loc["rack"] = "r11";
1003   loc["root"] = "default";
1004   int item = 1;
1005   c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1006
1007   loc["host"] = "b2";
1008   loc["rack"] = "r12";
1009   loc["root"] = "default";
1010   item = 2;
1011   c.insert_item(g_ceph_context, item, weight, "osd.2", loc);
1012
1013   assert(c.add_simple_rule("rule1", "r11", "host", "",
1014                            "firstn", pg_pool_t::TYPE_ERASURE) >= 0);
1015
1016   int id = c.get_item_id("b1");
1017
1018   __u32 weights = 666 * 0x10000;
1019   crush_weight_set weight_set;
1020   weight_set.size = 1;
1021   weight_set.weights = &weights;
1022   int maxbuckets = c.get_max_buckets();
1023   assert(maxbuckets > 0);
1024   crush_choose_arg choose_args[maxbuckets];
1025   memset(choose_args, '\0', sizeof(crush_choose_arg) * maxbuckets);
1026   choose_args[-1-id].ids_size = 0;
1027   choose_args[-1-id].weight_set_size = 1;
1028   choose_args[-1-id].weight_set = &weight_set;
1029   crush_choose_arg_map arg_map;
1030   arg_map.size = c.get_max_buckets();
1031   arg_map.args = choose_args;
1032
1033   uint64_t features = CEPH_FEATURE_CRUSH_TUNABLES5|CEPH_FEATURE_INCARNATION_2;
1034   int64_t caid = CrushWrapper::DEFAULT_CHOOSE_ARGS;
1035
1036   // if the client is capable, encode choose_args
1037   {
1038     c.choose_args[caid] = arg_map;
1039     bufferlist bl;
1040     c.encode(bl, features|CEPH_FEATURE_CRUSH_CHOOSE_ARGS);
1041     bufferlist::iterator i(bl.begin());
1042     CrushWrapper c_new;
1043     c_new.decode(i);
1044     ASSERT_EQ(1u, c_new.choose_args.size());
1045     ASSERT_EQ(1u, c_new.choose_args[caid].args[-1-id].weight_set_size);
1046     ASSERT_EQ(weights, c_new.choose_args[caid].args[-1-id].weight_set[0].weights[0]);
1047     ASSERT_EQ(weight, c_new.get_bucket_item_weightf(id, 0));
1048   }
1049
1050   // if the client is not compatible, copy choose_arg in the weights
1051   {
1052     c.choose_args[caid] = arg_map;
1053     bufferlist bl;
1054     c.encode(bl, features);
1055     c.choose_args.clear();
1056     bufferlist::iterator i(bl.begin());
1057     CrushWrapper c_new;
1058     c_new.decode(i);
1059     ASSERT_EQ(0u, c_new.choose_args.size());
1060     ASSERT_EQ((int)weights, c_new.get_bucket_item_weight(id, 0));
1061   }
1062 }
1063
1064 TEST(CrushWrapper, remove_root) {
1065   CrushWrapper c;
1066   c.create();
1067   c.set_type_name(1, "host");
1068   c.set_type_name(2, "rack");
1069   c.set_type_name(3, "root");
1070
1071   int weight = 1;
1072
1073   map<string,string> loc;
1074   loc["host"] = "b1";
1075   loc["rack"] = "r11";
1076   loc["root"] = "default";
1077   int item = 1;
1078   c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1079   item = 2;
1080   loc["host"] = "b2";
1081   loc["rack"] = "r12";
1082   loc["root"] = "default";
1083   c.insert_item(g_ceph_context, item, weight, "osd.2", loc);
1084
1085   assert(c.add_simple_rule("rule1", "r11", "host", "",
1086                            "firstn", pg_pool_t::TYPE_ERASURE) >= 0);
1087   ASSERT_TRUE(c.name_exists("default"));
1088   ASSERT_TRUE(c.name_exists("r11"));
1089   ASSERT_TRUE(c.name_exists("r12"));
1090   ASSERT_EQ(c.remove_root(c.get_item_id("default")), 0);
1091   ASSERT_FALSE(c.name_exists("default"));
1092   ASSERT_FALSE(c.name_exists("r11"));
1093   ASSERT_FALSE(c.name_exists("r12"));
1094 }
1095
1096 TEST(CrushWrapper, trim_roots_with_class) {
1097   CrushWrapper c;
1098   c.create();
1099   c.set_type_name(1, "root");
1100
1101   int weight = 1;
1102   map<string,string> loc;
1103   loc["root"] = "default";
1104
1105   int item = 1;
1106   c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1107   int cl = c.get_or_create_class_id("ssd");
1108   c.class_map[item] = cl;
1109
1110
1111   int root_id = c.get_item_id("default");
1112   int clone_id;
1113   map<int32_t, map<int32_t, int32_t>> old_class_bucket;
1114   map<int,map<int,vector<int>>> cmap_item_weight; // cargs -> bno -> weights
1115   set<int32_t> used_ids;
1116
1117   ASSERT_EQ(c.device_class_clone(root_id, cl, old_class_bucket, used_ids,
1118                                  &clone_id, &cmap_item_weight), 0);
1119
1120   ASSERT_TRUE(c.name_exists("default"));
1121   ASSERT_TRUE(c.name_exists("default~ssd"));
1122   c.trim_roots_with_class();
1123   ASSERT_TRUE(c.name_exists("default"));
1124   ASSERT_FALSE(c.name_exists("default~ssd"));
1125 }
1126
1127 TEST(CrushWrapper, device_class_clone) {
1128   CrushWrapper c;
1129   c.create();
1130   c.set_type_name(1, "host");
1131   c.set_type_name(2, "root");
1132
1133   map<string,string> loc;
1134   loc["host"] = "b1";
1135   loc["root"] = "default";
1136   int weight = 1;
1137
1138   int item = 1;
1139   c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1140   int cl = c.get_or_create_class_id("ssd");
1141   c.class_map[item] = cl;
1142
1143   int item_no_class = 2;
1144   c.insert_item(g_ceph_context, item_no_class, weight, "osd.2", loc);
1145
1146   c.reweight(g_ceph_context);
1147
1148   map<int32_t, map<int32_t, int32_t>> old_class_bucket;
1149   map<int,map<int,vector<int>>> cmap_item_weight; // cargs -> bno -> weights
1150   set<int32_t> used_ids;
1151   int root_id = c.get_item_id("default");
1152   int clone_id;
1153   ASSERT_EQ(c.device_class_clone(root_id, cl, old_class_bucket, used_ids,
1154                                  &clone_id, &cmap_item_weight), 0);
1155   ASSERT_TRUE(c.name_exists("default~ssd"));
1156   ASSERT_EQ(clone_id, c.get_item_id("default~ssd"));
1157   ASSERT_TRUE(c.subtree_contains(clone_id, item));
1158   ASSERT_FALSE(c.subtree_contains(clone_id, item_no_class));
1159   ASSERT_TRUE(c.subtree_contains(root_id, item_no_class));
1160   ASSERT_EQ(c.get_item_weightf(root_id), 2);
1161   ASSERT_EQ(c.get_item_weightf(clone_id), 1);
1162   // cloning again does nothing and returns the existing one
1163   int other_clone_id;
1164   ASSERT_EQ(c.device_class_clone(root_id, cl, old_class_bucket, used_ids,
1165                                  &other_clone_id, &cmap_item_weight), 0);
1166   ASSERT_EQ(clone_id, other_clone_id);
1167   // invalid arguments
1168   ASSERT_EQ(c.device_class_clone(12345, cl, old_class_bucket, used_ids,
1169                                  &other_clone_id, &cmap_item_weight), -ECHILD);
1170   ASSERT_EQ(c.device_class_clone(root_id, 12345, old_class_bucket, used_ids,
1171                                  &other_clone_id, &cmap_item_weight), -EBADF);
1172 }
1173
1174 TEST(CrushWrapper, split_id_class) {
1175   CrushWrapper c;
1176   c.create();
1177   c.set_type_name(1, "root");
1178
1179   int weight = 1;
1180   map<string,string> loc;
1181   loc["root"] = "default";
1182
1183   int item = 1;
1184   c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1185   int class_id = c.get_or_create_class_id("ssd");
1186   c.class_map[item] = class_id;
1187
1188   map<int32_t, map<int32_t, int32_t>> old_class_bucket;
1189   map<int,map<int,vector<int>>> cmap_item_weight; // cargs -> bno -> weights
1190   set<int32_t> used_ids;
1191   int item_id = c.get_item_id("default");
1192   int clone_id;
1193   ASSERT_EQ(c.device_class_clone(item_id, class_id, old_class_bucket, used_ids,
1194                                  &clone_id, &cmap_item_weight), 0);
1195   int retrieved_item_id;
1196   int retrieved_class_id;
1197   ASSERT_EQ(c.split_id_class(clone_id, &retrieved_item_id, &retrieved_class_id), 0);
1198   ASSERT_EQ(item_id, retrieved_item_id);
1199   ASSERT_EQ(class_id, retrieved_class_id);
1200
1201   ASSERT_EQ(c.split_id_class(item_id, &retrieved_item_id, &retrieved_class_id), 0);
1202   ASSERT_EQ(item_id, retrieved_item_id);
1203   ASSERT_EQ(-1, retrieved_class_id);
1204 }
1205
1206 TEST(CrushWrapper, populate_classes) {
1207   CrushWrapper c;
1208   c.create();
1209   c.set_type_name(1, "root");
1210
1211   int weight = 1;
1212   map<string,string> loc;
1213   loc["root"] = "default";
1214
1215   int item = 1;
1216   c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1217   int class_id = c.get_or_create_class_id("ssd");
1218   c.class_map[item] = class_id;
1219
1220   map<int32_t, map<int32_t, int32_t>> old_class_bucket;
1221   ASSERT_EQ(c.populate_classes(old_class_bucket), 0);
1222
1223   ASSERT_TRUE(c.name_exists("default~ssd"));
1224
1225   old_class_bucket = c.class_bucket;
1226   ASSERT_EQ(c.populate_classes(old_class_bucket), 0);
1227   ASSERT_EQ(old_class_bucket, c.class_bucket);
1228 }
1229
1230 TEST(CrushWrapper, remove_class_name) {
1231   CrushWrapper c;
1232   c.create();
1233
1234   ASSERT_EQ(-ENOENT, c.remove_class_name("ssd"));
1235   ASSERT_GE(0, c.get_or_create_class_id("ssd"));
1236   ASSERT_EQ(0, c.remove_class_name("ssd"));
1237   ASSERT_EQ(-ENOENT, c.remove_class_name("ssd"));
1238 }
1239
1240 TEST(CrushWrapper, try_remap_rule) {
1241   // build a simple 2 level map
1242   CrushWrapper c;
1243   c.create();
1244   c.set_type_name(0, "osd");
1245   c.set_type_name(1, "host");
1246   c.set_type_name(2, "rack");
1247   c.set_type_name(3, "root");
1248   int bno;
1249   int r = c.add_bucket(0, CRUSH_BUCKET_STRAW2,
1250                        CRUSH_HASH_DEFAULT, 3, 0, NULL,
1251                        NULL, &bno);
1252   ASSERT_EQ(0, r);
1253   ASSERT_EQ(-1, bno);
1254   c.set_item_name(bno, "default");
1255
1256   c.set_max_devices(20);
1257
1258   //JSONFormatter jf(true);
1259
1260   map<string,string> loc;
1261   loc["host"] = "foo";
1262   loc["rack"] = "a";
1263   loc["root"] = "default";
1264   c.insert_item(g_ceph_context, 0, 1, "osd.0", loc);
1265   c.insert_item(g_ceph_context, 1, 1, "osd.1", loc);
1266   c.insert_item(g_ceph_context, 2, 1, "osd.2", loc);
1267
1268   loc.clear();
1269   loc["host"] = "bar";
1270   loc["rack"] = "a";
1271   loc["root"] = "default";
1272   c.insert_item(g_ceph_context, 3, 1, "osd.3", loc);
1273   c.insert_item(g_ceph_context, 4, 1, "osd.4", loc);
1274   c.insert_item(g_ceph_context, 5, 1, "osd.5", loc);
1275
1276   loc.clear();
1277   loc["host"] = "baz";
1278   loc["rack"] = "b";
1279   loc["root"] = "default";
1280   c.insert_item(g_ceph_context, 6, 1, "osd.6", loc);
1281   c.insert_item(g_ceph_context, 7, 1, "osd.7", loc);
1282   c.insert_item(g_ceph_context, 8, 1, "osd.8", loc);
1283
1284   loc.clear();
1285   loc["host"] = "qux";
1286   loc["rack"] = "b";
1287   loc["root"] = "default";
1288   c.insert_item(g_ceph_context, 9, 1, "osd.9", loc);
1289   c.insert_item(g_ceph_context, 10, 1, "osd.10", loc);
1290   c.insert_item(g_ceph_context, 11, 1, "osd.11", loc);
1291   c.finalize();
1292
1293   loc.clear();
1294   loc["host"] = "bif";
1295   loc["rack"] = "c";
1296   loc["root"] = "default";
1297   c.insert_item(g_ceph_context, 12, 1, "osd.12", loc);
1298   c.insert_item(g_ceph_context, 13, 1, "osd.13", loc);
1299   c.insert_item(g_ceph_context, 14, 1, "osd.14", loc);
1300   c.finalize();
1301
1302   loc.clear();
1303   loc["host"] = "pop";
1304   loc["rack"] = "c";
1305   loc["root"] = "default";
1306   c.insert_item(g_ceph_context, 15, 1, "osd.15", loc);
1307   c.insert_item(g_ceph_context, 16, 1, "osd.16", loc);
1308   c.insert_item(g_ceph_context, 17, 1, "osd.17", loc);
1309   c.finalize();
1310
1311   //c.dump(&jf);
1312   //jf.flush(cout);
1313
1314   // take + emit
1315   {
1316   }
1317
1318   // take + choose device + emit
1319   {
1320     cout << "take + choose + emit" << std::endl;
1321     ostringstream err;
1322     int rule = c.add_simple_rule("one", "default", "osd", "",
1323                                  "firstn", 0, &err);
1324     ASSERT_EQ(rule, 0);
1325
1326     vector<int> orig = { 0, 3, 9 };
1327     set<int> overfull = { 3 };
1328     vector<int> underfull = { 0, 2, 5, 8, 11 };
1329     vector<int> out;
1330     int r = c.try_remap_rule(g_ceph_context, rule, 3,
1331                               overfull, underfull,
1332                               orig, &out);
1333     cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1334     ASSERT_EQ(r, 0);
1335     ASSERT_EQ(3u, out.size());
1336     ASSERT_EQ(0, out[0]);
1337     ASSERT_EQ(2, out[1]);
1338     ASSERT_EQ(9, out[2]);
1339
1340     // make sure we cope with dups between underfull and future values in orig
1341     underfull = {9, 0, 2, 5};
1342     orig = {1, 3, 9};
1343
1344     r = c.try_remap_rule(g_ceph_context, rule, 3,
1345                          overfull, underfull,
1346                          orig, &out);
1347     cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1348     ASSERT_EQ(r, 0);
1349     ASSERT_EQ(3u, out.size());
1350     ASSERT_EQ(1, out[0]);
1351     ASSERT_EQ(0, out[1]);
1352     ASSERT_EQ(9, out[2]);
1353   }
1354
1355   // chooseleaf
1356   {
1357     cout << "take + chooseleaf + emit" << std::endl;
1358     ostringstream err;
1359     int rule = c.add_simple_rule("two", "default", "host", "",
1360                                  "firstn", 0, &err);
1361     ASSERT_EQ(rule, 1);
1362
1363     vector<int> orig = { 0, 3, 9 };
1364     set<int> overfull = { 3 };
1365     vector<int> underfull = { 0, 2, 5, 8, 11 };
1366     vector<int> out;
1367     int r = c.try_remap_rule(g_ceph_context, rule, 3,
1368                               overfull, underfull,
1369                               orig, &out);
1370     cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1371     ASSERT_EQ(r, 0);
1372     ASSERT_EQ(3u, out.size());
1373     ASSERT_EQ(0, out[0]);
1374     ASSERT_EQ(5, out[1]);
1375     ASSERT_EQ(9, out[2]);
1376   }
1377
1378   // choose + choose
1379   {
1380     cout << "take + choose + choose + choose + emit" << std::endl;
1381     int rule = c.add_rule(2, 5, 0, 1, 10);
1382     ASSERT_EQ(2, rule);
1383     c.set_rule_step_take(rule, 0, bno);
1384     c.set_rule_step_choose_indep(rule, 1, 2, 2);
1385     c.set_rule_step_choose_indep(rule, 2, 2, 1);
1386     c.set_rule_step_choose_indep(rule, 3, 1, 0);
1387     c.set_rule_step_emit(rule, 4);
1388
1389     vector<int> orig = { 0, 3, 16, 12 };
1390     set<int> overfull = { 3, 12 };
1391     vector<int> underfull = { 6, 7, 9, 3, 0, 1, 15, 16, 13, 2, 5, 8, 11 };
1392     vector<int> out;
1393     int r = c.try_remap_rule(g_ceph_context, rule, 3,
1394                               overfull, underfull,
1395                               orig, &out);
1396     cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1397     ASSERT_EQ(r, 0);
1398     ASSERT_EQ(4u, out.size());
1399     ASSERT_EQ(0, out[0]);
1400     ASSERT_EQ(5, out[1]);
1401     ASSERT_EQ(16, out[2]);
1402     ASSERT_EQ(13, out[3]);
1403
1404     orig.pop_back();
1405     out.clear();
1406     r = c.try_remap_rule(g_ceph_context, rule, 3,
1407                          overfull, underfull,
1408                          orig, &out);
1409     cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1410     ASSERT_EQ(r, 0);
1411     ASSERT_EQ(3u, out.size());
1412     ASSERT_EQ(0, out[0]);
1413     ASSERT_EQ(5, out[1]);
1414     ASSERT_EQ(16, out[2]);
1415   }
1416 }
1417
1418 int main(int argc, char **argv) {
1419   vector<const char*> args;
1420   argv_to_vec(argc, (const char **)argv, args);
1421   env_to_vec(args);
1422
1423   vector<const char*> def_args;
1424   def_args.push_back("--debug-crush=0");
1425   auto cct = global_init(&def_args, args, CEPH_ENTITY_TYPE_CLIENT,
1426                          CODE_ENVIRONMENT_UTILITY, 0);
1427   common_init_finish(g_ceph_context);
1428   ::testing::InitGoogleTest(&argc, argv);
1429   return RUN_ALL_TESTS();
1430 }
1431 // Local Variables:
1432 // compile-command: "cd ../../../build ; make -j4 unittest_crush_wrapper && valgrind --tool=memcheck bin/unittest_crush_wrapper"
1433 // End: