1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
7 * Copyright (C) 2014 Red Hat <contact@redhat.com>
9 * Author: Loic Dachary <loic@dachary.org>
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)
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.
24 #include <gtest/gtest.h>
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"
33 #include "crush/CrushWrapper.h"
35 TEST(CrushWrapper, get_immediate_parent) {
36 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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");
50 pair <string,string> loc;
52 loc = c->get_immediate_parent(item, &ret);
53 EXPECT_EQ(-ENOENT, ret);
56 map<string,string> loc;
57 loc["root"] = "default";
59 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
63 loc = c->get_immediate_parent(item, &ret);
65 EXPECT_EQ("root", loc.first);
66 EXPECT_EQ("default", loc.second);
69 TEST(CrushWrapper, move_bucket) {
70 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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"));
85 map<string,string> loc;
86 loc["root"] = "root0";
87 loc["host"] = "host0";
90 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
93 int host0 = c->get_item_id("host0");
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"));
100 map<string,string> loc;
101 loc["root"] = "root1";
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
109 pair <string,string> loc;
111 loc = c->get_immediate_parent(host0, &ret);
113 EXPECT_EQ("root", loc.first);
114 EXPECT_EQ("root0", loc.second);
116 EXPECT_EQ(0, c->move_bucket(g_ceph_context, host0, loc));
118 pair <string,string> loc;
120 loc = c->get_immediate_parent(host0, &ret);
122 EXPECT_EQ("root", loc.first);
123 EXPECT_EQ("root1", loc.second);
127 TEST(CrushWrapper, swap_bucket) {
128 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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"));
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"));
151 map<string,string> loc;
152 loc["root"] = "root";
153 EXPECT_EQ(0, c->move_bucket(g_ceph_context, a, loc));
156 map<string,string> loc;
157 loc["root"] = "root";
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));
164 map<string,string> loc;
166 EXPECT_EQ(0, c->insert_item(g_ceph_context, 3, 1.0, "osd.3", loc));
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));
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));
190 TEST(CrushWrapper, rename_bucket_or_item) {
191 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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"));
207 map<string,string> loc;
208 loc["root"] = "root0";
209 loc["host"] = "host0";
211 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
216 map<string,string> loc;
217 loc["root"] = "root0";
218 loc["host"] = "host1";
220 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
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));
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));
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));
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));
245 EXPECT_EQ(-ENOTDIR, c->can_rename_bucket("osd.1", "somethingelse", &ss));
246 EXPECT_EQ(-ENOTDIR, c->rename_bucket("osd.1", "somethingelse", &ss));
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"));
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"));
257 TEST(CrushWrapper, check_item_loc) {
258 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
260 float expected_weight = 1.0;
262 // fail if loc is empty
265 map<string,string> loc;
266 EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
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");
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");
281 // fail because the item is not found at the specified location
284 map<string,string> loc;
285 loc["root"] = "default";
286 EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
288 // fail because the bucket name does not match an existing bucket
291 map<string,string> loc;
292 loc["root"] = "default";
293 const string HOST("host0");
295 EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
297 const string OSD("osd.0");
299 map<string,string> loc;
300 loc["root"] = "default";
301 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, expected_weight,
304 // fail because osd.0 is not a bucket and must not be in loc, in
305 // addition to being of the wrong type
308 map<string,string> loc;
309 loc["root"] = "osd.0";
310 EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
312 // succeed and retrieves the expected 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);
322 TEST(CrushWrapper, update_item) {
323 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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");
337 const string HOST0("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);
343 const string HOST1("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);
351 // fail if invalid names anywhere in loc
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));
358 // fail if invalid item name
360 map<string,string> loc;
361 EXPECT_EQ(-EINVAL, c->update_item(g_ceph_context, item, 1.0,
364 const string OSD0("osd.0");
365 const string OSD1("osd.1");
366 float original_weight = 1.0;
367 float modified_weight = 2.0;
370 map<string,string> loc;
371 loc["root"] = "default";
373 EXPECT_GE(0.0, c->get_item_weightf(host0));
374 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, original_weight,
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,
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));
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,
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);
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;
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,
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));
416 TEST(CrushWrapper, adjust_item_weight) {
417 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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");
431 const string HOST0("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);
437 const string FAKE("fake");
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);
445 // construct crush map
448 map<string,string> loc;
449 loc["host"] = "host0";
450 float host_weight = 2.0;
454 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
455 "osd." + stringify(item), loc));
457 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
458 "osd." + stringify(item), loc));
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));
464 map<string,string> bloc;
465 bloc["root"] = "default";
466 EXPECT_EQ(0, c->insert_item(g_ceph_context, host0, host_weight,
471 map<string,string> loc;
472 loc["host"] = "fake";
473 float host_weight = 2.0;
477 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
478 "osd." + stringify(item), loc));
480 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
481 "osd." + stringify(item), loc));
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));
487 map<string,string> bloc;
488 bloc["root"] = "default";
489 EXPECT_EQ(0, c->insert_item(g_ceph_context, hostfake, host_weight,
496 // default --> host0 --> osd.0 1.0
500 // +-> fake --> osd.0 1.0
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
507 // So the crush map will be:
509 // default --> host0 --> osd.0 2.0
513 // +-> fake --> osd.0 2.0
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";
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));
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));
535 TEST(CrushWrapper, adjust_subtree_weight) {
536 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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");
550 const string HOST0("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);
556 const string FAKE("fake");
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);
564 // construct crush map
567 map<string,string> loc;
568 loc["host"] = "host0";
569 float host_weight = 2.0;
573 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
574 "osd." + stringify(item), loc));
576 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
577 "osd." + stringify(item), loc));
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));
583 map<string,string> bloc;
584 bloc["root"] = "default";
585 EXPECT_EQ(0, c->insert_item(g_ceph_context, host0, host_weight,
590 map<string,string> loc;
591 loc["host"] = "fake";
592 float host_weight = 2.0;
596 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
597 "osd." + stringify(item), loc));
599 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
600 "osd." + stringify(item), loc));
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));
606 map<string,string> bloc;
607 bloc["root"] = "default";
608 EXPECT_EQ(0, c->insert_item(g_ceph_context, hostfake, host_weight,
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);
617 int r = c->adjust_subtree_weightf(g_ceph_context, host0, 2.0);
618 ASSERT_EQ(r, 2); // 2 items changed
620 //cout << "--------after---------" << std::endl;
621 //c->dump_tree(&cout, NULL);
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);
628 TEST(CrushWrapper, insert_item) {
629 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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");
645 // invalid names anywhere in loc trigger an error
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));
653 // insert an item in an existing bucket
655 map<string,string> loc;
656 loc["root"] = "default";
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));
665 // implicit creation of a bucket
667 string name = "NAME";
668 map<string,string> loc;
669 loc["root"] = "default";
673 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
674 "osd." + stringify(item), loc));
676 // adding to an existing item name that is not associated with a bucket
678 string name = "ITEM_WITHOUT_BUCKET";
679 map<string,string> loc;
680 loc["root"] = "default";
683 c->set_item_name(item, name);
686 EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
687 "osd." + stringify(item), loc));
692 // default --> host0 --> item
694 // Trying to insert the same item higher in the hirarchy will fail
695 // because it would create a loop.
697 // default --> host0 --> item
704 map<string,string> loc;
705 loc["root"] = "default";
706 loc["host"] = "host0";
708 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
709 "osd." + stringify(item), loc));
712 map<string,string> loc;
713 loc["root"] = "default";
715 EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
716 "osd." + stringify(item), loc));
724 // Trying to insert default under host0 must fail
725 // because it would create a loop.
727 // default --> host0 --> default
730 map<string,string> loc;
731 loc["host"] = "host0";
733 EXPECT_EQ(-ELOOP, c->insert_item(g_ceph_context, rootno, 1.0,
736 // fail when mapping a bucket to the wrong type
738 // create an OSD bucket
740 int r = c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
741 10, 0, NULL, NULL, &osdno);
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";
750 EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
751 "osd." + stringify(item), loc));
753 // fail when no location
755 map<string,string> loc;
757 EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
758 "osd." + stringify(item), loc));
762 TEST(CrushWrapper, remove_item) {
763 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
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");
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");
786 const int num_osd = 12;
788 map<string, string> loc = {{"root", "root0"},
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));
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));
801 EXPECT_FALSE(c->check_item_loc(g_ceph_context, item_to_remove, loc, &weight));
804 TEST(CrushWrapper, item_bucket_names) {
805 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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));
816 TEST(CrushWrapper, bucket_types) {
817 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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));
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"));
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));
838 map<string,string> loc;
839 loc["\005"] = "default";
840 EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
843 map<string,string> loc;
844 loc["host"] = "\003";
845 EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
849 TEST(CrushWrapper, dump_rules) {
850 std::unique_ptr<CrushWrapper> c(new CrushWrapper);
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");
857 string failure_domain_type("osd");
858 string root_name("default");
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);
866 pair <string,string> loc;
868 loc = c->get_immediate_parent(item, &ret);
869 EXPECT_EQ(-ENOENT, ret);
872 map<string,string> loc;
873 loc["root"] = root_name;
875 EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
879 // no rule by default
881 Formatter *f = Formatter::create("json-pretty");
882 f->open_array_section("rules");
888 EXPECT_EQ("[]\n", ss.str());
892 int rule = c->add_simple_rule(name, root_name, failure_domain_type, "",
893 "firstn", pg_pool_t::TYPE_ERASURE);
897 Formatter *f = Formatter::create("xml");
902 EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
906 Formatter *f = Formatter::create("xml");
907 c->dump_rule(rule, 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>"));
917 c->get_rule_weight_osd_map(0, &wm);
918 ASSERT_TRUE(wm.size() == 1);
919 ASSERT_TRUE(wm[0] == 1.0);
922 TEST(CrushWrapper, distance) {
925 c.set_type_name(1, "host");
926 c.set_type_name(2, "rack");
927 c.set_type_name(3, "root");
929 int r = c.add_bucket(0, CRUSH_BUCKET_STRAW,
930 CRUSH_HASH_DEFAULT, 3, 0, NULL,
934 c.set_item_name(bno, "default");
936 c.set_max_devices(10);
938 //JSONFormatter jf(true);
940 map<string,string> loc;
943 loc["root"] = "default";
944 c.insert_item(g_ceph_context, 0, 1, "osd.0", loc);
949 loc["root"] = "default";
950 c.insert_item(g_ceph_context, 1, 1, "osd.1", loc);
955 loc["root"] = "default";
956 c.insert_item(g_ceph_context, 2, 1, "osd.2", loc);
961 loc["root"] = "default";
962 c.insert_item(g_ceph_context, 3, 1, "osd.3", loc);
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]);
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));
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));
991 TEST(CrushWrapper, choose_args_compat) {
994 c.set_type_name(1, "host");
995 c.set_type_name(2, "rack");
996 c.set_type_name(3, "root");
1000 map<string,string> loc;
1002 loc["rack"] = "r11";
1003 loc["root"] = "default";
1005 c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1008 loc["rack"] = "r12";
1009 loc["root"] = "default";
1011 c.insert_item(g_ceph_context, item, weight, "osd.2", loc);
1013 assert(c.add_simple_rule("rule1", "r11", "host", "",
1014 "firstn", pg_pool_t::TYPE_ERASURE) >= 0);
1016 int id = c.get_item_id("b1");
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;
1033 uint64_t features = CEPH_FEATURE_CRUSH_TUNABLES5|CEPH_FEATURE_INCARNATION_2;
1034 int64_t caid = CrushWrapper::DEFAULT_CHOOSE_ARGS;
1036 // if the client is capable, encode choose_args
1038 c.choose_args[caid] = arg_map;
1040 c.encode(bl, features|CEPH_FEATURE_CRUSH_CHOOSE_ARGS);
1041 bufferlist::iterator i(bl.begin());
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));
1050 // if the client is not compatible, copy choose_arg in the weights
1052 c.choose_args[caid] = arg_map;
1054 c.encode(bl, features);
1055 c.choose_args.clear();
1056 bufferlist::iterator i(bl.begin());
1059 ASSERT_EQ(0u, c_new.choose_args.size());
1060 ASSERT_EQ((int)weights, c_new.get_bucket_item_weight(id, 0));
1064 TEST(CrushWrapper, remove_root) {
1067 c.set_type_name(1, "host");
1068 c.set_type_name(2, "rack");
1069 c.set_type_name(3, "root");
1073 map<string,string> loc;
1075 loc["rack"] = "r11";
1076 loc["root"] = "default";
1078 c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1081 loc["rack"] = "r12";
1082 loc["root"] = "default";
1083 c.insert_item(g_ceph_context, item, weight, "osd.2", loc);
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"));
1096 TEST(CrushWrapper, trim_roots_with_class) {
1099 c.set_type_name(1, "root");
1102 map<string,string> loc;
1103 loc["root"] = "default";
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;
1111 int root_id = c.get_item_id("default");
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;
1117 ASSERT_EQ(c.device_class_clone(root_id, cl, old_class_bucket, used_ids,
1118 &clone_id, &cmap_item_weight), 0);
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"));
1127 TEST(CrushWrapper, device_class_clone) {
1130 c.set_type_name(1, "host");
1131 c.set_type_name(2, "root");
1133 map<string,string> loc;
1135 loc["root"] = "default";
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;
1143 int item_no_class = 2;
1144 c.insert_item(g_ceph_context, item_no_class, weight, "osd.2", loc);
1146 c.reweight(g_ceph_context);
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");
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
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);
1174 TEST(CrushWrapper, split_id_class) {
1177 c.set_type_name(1, "root");
1180 map<string,string> loc;
1181 loc["root"] = "default";
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;
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");
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);
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);
1206 TEST(CrushWrapper, populate_classes) {
1209 c.set_type_name(1, "root");
1212 map<string,string> loc;
1213 loc["root"] = "default";
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;
1220 map<int32_t, map<int32_t, int32_t>> old_class_bucket;
1221 ASSERT_EQ(c.populate_classes(old_class_bucket), 0);
1223 ASSERT_TRUE(c.name_exists("default~ssd"));
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);
1230 TEST(CrushWrapper, remove_class_name) {
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"));
1240 TEST(CrushWrapper, try_remap_rule) {
1241 // build a simple 2 level map
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");
1249 int r = c.add_bucket(0, CRUSH_BUCKET_STRAW2,
1250 CRUSH_HASH_DEFAULT, 3, 0, NULL,
1254 c.set_item_name(bno, "default");
1256 c.set_max_devices(20);
1258 //JSONFormatter jf(true);
1260 map<string,string> loc;
1261 loc["host"] = "foo";
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);
1269 loc["host"] = "bar";
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);
1277 loc["host"] = "baz";
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);
1285 loc["host"] = "qux";
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);
1294 loc["host"] = "bif";
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);
1303 loc["host"] = "pop";
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);
1318 // take + choose device + emit
1320 cout << "take + choose + emit" << std::endl;
1322 int rule = c.add_simple_rule("one", "default", "osd", "",
1326 vector<int> orig = { 0, 3, 9 };
1327 set<int> overfull = { 3 };
1328 vector<int> underfull = { 0, 2, 5, 8, 11 };
1330 int r = c.try_remap_rule(g_ceph_context, rule, 3,
1331 overfull, underfull,
1333 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1335 ASSERT_EQ(3u, out.size());
1336 ASSERT_EQ(0, out[0]);
1337 ASSERT_EQ(2, out[1]);
1338 ASSERT_EQ(9, out[2]);
1340 // make sure we cope with dups between underfull and future values in orig
1341 underfull = {9, 0, 2, 5};
1344 r = c.try_remap_rule(g_ceph_context, rule, 3,
1345 overfull, underfull,
1347 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1349 ASSERT_EQ(3u, out.size());
1350 ASSERT_EQ(1, out[0]);
1351 ASSERT_EQ(0, out[1]);
1352 ASSERT_EQ(9, out[2]);
1357 cout << "take + chooseleaf + emit" << std::endl;
1359 int rule = c.add_simple_rule("two", "default", "host", "",
1363 vector<int> orig = { 0, 3, 9 };
1364 set<int> overfull = { 3 };
1365 vector<int> underfull = { 0, 2, 5, 8, 11 };
1367 int r = c.try_remap_rule(g_ceph_context, rule, 3,
1368 overfull, underfull,
1370 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1372 ASSERT_EQ(3u, out.size());
1373 ASSERT_EQ(0, out[0]);
1374 ASSERT_EQ(5, out[1]);
1375 ASSERT_EQ(9, out[2]);
1380 cout << "take + choose + choose + choose + emit" << std::endl;
1381 int rule = c.add_rule(2, 5, 0, 1, 10);
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);
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 };
1393 int r = c.try_remap_rule(g_ceph_context, rule, 3,
1394 overfull, underfull,
1396 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
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]);
1406 r = c.try_remap_rule(g_ceph_context, rule, 3,
1407 overfull, underfull,
1409 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1411 ASSERT_EQ(3u, out.size());
1412 ASSERT_EQ(0, out[0]);
1413 ASSERT_EQ(5, out[1]);
1414 ASSERT_EQ(16, out[2]);
1418 int main(int argc, char **argv) {
1419 vector<const char*> args;
1420 argv_to_vec(argc, (const char **)argv, args);
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();
1432 // compile-command: "cd ../../../build ; make -j4 unittest_crush_wrapper && valgrind --tool=memcheck bin/unittest_crush_wrapper"