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 // check if it can swap parent with child
179 ASSERT_EQ(-EINVAL
, c
->swap_bucket(g_ceph_context
, root
, a
));
181 c
->swap_bucket(g_ceph_context
, a
, b
);
182 ASSERT_EQ(0x30000, c
->get_item_weight(b
));
183 ASSERT_EQ(string("a"), c
->get_item_name(b
));
184 ASSERT_EQ(0x10000, c
->get_item_weight(a
));
185 ASSERT_EQ(string("b"), c
->get_item_name(a
));
186 ASSERT_EQ(a
, c
->get_bucket_item(root
, 0));
187 ASSERT_EQ(0, c
->get_bucket_item(b
, 0));
188 ASSERT_EQ(1, c
->get_bucket_item(b
, 1));
189 ASSERT_EQ(2, c
->get_bucket_item(b
, 2));
190 ASSERT_EQ(3, c
->get_bucket_item(a
, 0));
193 TEST(CrushWrapper
, rename_bucket_or_item
) {
194 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
196 const int ROOT_TYPE
= 2;
197 c
->set_type_name(ROOT_TYPE
, "root");
198 const int HOST_TYPE
= 1;
199 c
->set_type_name(HOST_TYPE
, "host");
200 const int OSD_TYPE
= 0;
201 c
->set_type_name(OSD_TYPE
, "osd");
204 EXPECT_EQ(0, c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
205 ROOT_TYPE
, 0, NULL
, NULL
, &root0
));
206 EXPECT_EQ(0, c
->set_item_name(root0
, "root0"));
210 map
<string
,string
> loc
;
211 loc
["root"] = "root0";
212 loc
["host"] = "host0";
214 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
219 map
<string
,string
> loc
;
220 loc
["root"] = "root0";
221 loc
["host"] = "host1";
223 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
228 EXPECT_EQ(-EINVAL
, c
->can_rename_item("host0", "????", &ss
));
229 EXPECT_EQ(-EINVAL
, c
->rename_item("host0", "????", &ss
));
230 EXPECT_EQ(-EINVAL
, c
->can_rename_bucket("host0", "????", &ss
));
231 EXPECT_EQ(-EINVAL
, c
->rename_bucket("host0", "????", &ss
));
233 EXPECT_EQ(-EEXIST
, c
->can_rename_item("host0", "host1", &ss
));
234 EXPECT_EQ(-EEXIST
, c
->rename_item("host0", "host1", &ss
));
235 EXPECT_EQ(-EEXIST
, c
->can_rename_bucket("host0", "host1", &ss
));
236 EXPECT_EQ(-EEXIST
, c
->rename_bucket("host0", "host1", &ss
));
238 EXPECT_EQ(-EALREADY
, c
->can_rename_item("gone", "host1", &ss
));
239 EXPECT_EQ(-EALREADY
, c
->rename_item("gone", "host1", &ss
));
240 EXPECT_EQ(-EALREADY
, c
->can_rename_bucket("gone", "host1", &ss
));
241 EXPECT_EQ(-EALREADY
, c
->rename_bucket("gone", "host1", &ss
));
243 EXPECT_EQ(-ENOENT
, c
->can_rename_item("doesnotexist", "somethingelse", &ss
));
244 EXPECT_EQ(-ENOENT
, c
->rename_item("doesnotexist", "somethingelse", &ss
));
245 EXPECT_EQ(-ENOENT
, c
->can_rename_bucket("doesnotexist", "somethingelse", &ss
));
246 EXPECT_EQ(-ENOENT
, c
->rename_bucket("doesnotexist", "somethingelse", &ss
));
248 EXPECT_EQ(-ENOTDIR
, c
->can_rename_bucket("osd.1", "somethingelse", &ss
));
249 EXPECT_EQ(-ENOTDIR
, c
->rename_bucket("osd.1", "somethingelse", &ss
));
251 int host0id
= c
->get_item_id("host0");
252 EXPECT_EQ(0, c
->rename_bucket("host0", "host0renamed", &ss
));
253 EXPECT_EQ(host0id
, c
->get_item_id("host0renamed"));
255 int osd0id
= c
->get_item_id("osd0");
256 EXPECT_EQ(0, c
->rename_item("osd.0", "osd0renamed", &ss
));
257 EXPECT_EQ(osd0id
, c
->get_item_id("osd0renamed"));
260 TEST(CrushWrapper
, check_item_loc
) {
261 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
263 float expected_weight
= 1.0;
265 // fail if loc is empty
268 map
<string
,string
> loc
;
269 EXPECT_FALSE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
272 const int ROOT_TYPE
= 2;
273 c
->set_type_name(ROOT_TYPE
, "root");
274 const int HOST_TYPE
= 1;
275 c
->set_type_name(HOST_TYPE
, "host");
276 const int OSD_TYPE
= 0;
277 c
->set_type_name(OSD_TYPE
, "osd");
280 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
281 ROOT_TYPE
, 0, NULL
, NULL
, &rootno
);
282 c
->set_item_name(rootno
, "default");
284 // fail because the item is not found at the specified location
287 map
<string
,string
> loc
;
288 loc
["root"] = "default";
289 EXPECT_FALSE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
291 // fail because the bucket name does not match an existing bucket
294 map
<string
,string
> loc
;
295 loc
["root"] = "default";
296 const string
HOST("host0");
298 EXPECT_FALSE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
300 const string
OSD("osd.0");
302 map
<string
,string
> loc
;
303 loc
["root"] = "default";
304 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, expected_weight
,
307 // fail because osd.0 is not a bucket and must not be in loc, in
308 // addition to being of the wrong type
311 map
<string
,string
> loc
;
312 loc
["root"] = "osd.0";
313 EXPECT_FALSE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
315 // succeed and retrieves the expected weight
318 map
<string
,string
> loc
;
319 loc
["root"] = "default";
320 EXPECT_TRUE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
321 EXPECT_EQ(expected_weight
, weight
);
325 TEST(CrushWrapper
, update_item
) {
326 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
328 const int ROOT_TYPE
= 2;
329 c
->set_type_name(ROOT_TYPE
, "root");
330 const int HOST_TYPE
= 1;
331 c
->set_type_name(HOST_TYPE
, "host");
332 const int OSD_TYPE
= 0;
333 c
->set_type_name(OSD_TYPE
, "osd");
336 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
337 ROOT_TYPE
, 0, NULL
, NULL
, &rootno
);
338 c
->set_item_name(rootno
, "default");
340 const string
HOST0("host0");
342 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
343 HOST_TYPE
, 0, NULL
, NULL
, &host0
);
344 c
->set_item_name(host0
, HOST0
);
346 const string
HOST1("host1");
348 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
349 HOST_TYPE
, 0, NULL
, NULL
, &host1
);
350 c
->set_item_name(host1
, HOST1
);
354 // fail if invalid names anywhere in loc
356 map
<string
,string
> loc
;
357 loc
["rack"] = "\001";
358 EXPECT_EQ(-EINVAL
, c
->update_item(g_ceph_context
, item
, 1.0,
359 "osd." + stringify(item
), loc
));
361 // fail if invalid item name
363 map
<string
,string
> loc
;
364 EXPECT_EQ(-EINVAL
, c
->update_item(g_ceph_context
, item
, 1.0,
367 const string
OSD0("osd.0");
368 const string
OSD1("osd.1");
369 float original_weight
= 1.0;
370 float modified_weight
= 2.0;
373 map
<string
,string
> loc
;
374 loc
["root"] = "default";
376 EXPECT_GE(0.0, c
->get_item_weightf(host0
));
377 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, original_weight
,
380 // updating nothing changes nothing
381 EXPECT_EQ(OSD0
, c
->get_item_name(item
));
382 EXPECT_EQ(original_weight
, c
->get_item_weightf(item
));
383 EXPECT_TRUE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
384 EXPECT_EQ(0, c
->update_item(g_ceph_context
, item
, original_weight
,
386 EXPECT_EQ(OSD0
, c
->get_item_name(item
));
387 EXPECT_EQ(original_weight
, c
->get_item_weightf(item
));
388 EXPECT_TRUE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
390 // update the name and weight of the item but not the location
391 EXPECT_EQ(OSD0
, c
->get_item_name(item
));
392 EXPECT_EQ(original_weight
, c
->get_item_weightf(item
));
393 EXPECT_TRUE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
394 EXPECT_EQ(1, c
->update_item(g_ceph_context
, item
, modified_weight
,
396 EXPECT_EQ(OSD1
, c
->get_item_name(item
));
397 EXPECT_EQ(modified_weight
, c
->get_item_weightf(item
));
398 EXPECT_TRUE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
399 c
->set_item_name(item
, OSD0
);
400 c
->adjust_item_weightf(g_ceph_context
, item
, original_weight
);
402 // update the name and weight of the item and change its location
403 map
<string
,string
> other_loc
;
404 other_loc
["root"] = "default";
405 other_loc
["host"] = HOST1
;
407 EXPECT_EQ(OSD0
, c
->get_item_name(item
));
408 EXPECT_EQ(original_weight
, c
->get_item_weightf(item
));
409 EXPECT_TRUE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
410 EXPECT_FALSE(c
->check_item_loc(g_ceph_context
, item
, other_loc
, &weight
));
411 EXPECT_EQ(1, c
->update_item(g_ceph_context
, item
, modified_weight
,
413 EXPECT_EQ(OSD1
, c
->get_item_name(item
));
414 EXPECT_EQ(modified_weight
, c
->get_item_weightf(item
));
415 EXPECT_FALSE(c
->check_item_loc(g_ceph_context
, item
, loc
, &weight
));
416 EXPECT_TRUE(c
->check_item_loc(g_ceph_context
, item
, other_loc
, &weight
));
419 TEST(CrushWrapper
, adjust_item_weight
) {
420 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
422 const int ROOT_TYPE
= 2;
423 c
->set_type_name(ROOT_TYPE
, "root");
424 const int HOST_TYPE
= 1;
425 c
->set_type_name(HOST_TYPE
, "host");
426 const int OSD_TYPE
= 0;
427 c
->set_type_name(OSD_TYPE
, "osd");
430 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
431 ROOT_TYPE
, 0, NULL
, NULL
, &rootno
);
432 c
->set_item_name(rootno
, "default");
434 const string
HOST0("host0");
436 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
437 HOST_TYPE
, 0, NULL
, NULL
, &host0
);
438 c
->set_item_name(host0
, HOST0
);
440 const string
FAKE("fake");
442 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
443 HOST_TYPE
, 0, NULL
, NULL
, &hostfake
);
444 c
->set_item_name(hostfake
, FAKE
);
448 // construct crush map
451 map
<string
,string
> loc
;
452 loc
["host"] = "host0";
453 float host_weight
= 2.0;
457 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
458 "osd." + stringify(item
), loc
));
460 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
461 "osd." + stringify(item
), loc
));
463 bucket_id
= c
->get_item_id("host0");
464 EXPECT_EQ(true, c
->bucket_exists(bucket_id
));
465 EXPECT_EQ(host_weight
, c
->get_bucket_weightf(bucket_id
));
467 map
<string
,string
> bloc
;
468 bloc
["root"] = "default";
469 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, host0
, host_weight
,
474 map
<string
,string
> loc
;
475 loc
["host"] = "fake";
476 float host_weight
= 2.0;
480 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
481 "osd." + stringify(item
), loc
));
483 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
484 "osd." + stringify(item
), loc
));
486 bucket_id
= c
->get_item_id("fake");
487 EXPECT_EQ(true, c
->bucket_exists(bucket_id
));
488 EXPECT_EQ(host_weight
, c
->get_bucket_weightf(bucket_id
));
490 map
<string
,string
> bloc
;
491 bloc
["root"] = "default";
492 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, hostfake
, host_weight
,
499 // default --> host0 --> osd.0 1.0
503 // +-> fake --> osd.0 1.0
507 // Trying to adjust osd.0 weight to 2.0 in all buckets
508 // Trying to adjust osd.1 weight to 2.0 in host=fake
510 // So the crush map will be:
512 // default --> host0 --> osd.0 2.0
516 // +-> fake --> osd.0 2.0
521 float original_weight
= 1.0;
522 float modified_weight
= 2.0;
523 map
<string
,string
> loc_one
, loc_two
;
524 loc_one
["host"] = "host0";
525 loc_two
["host"] = "fake";
528 EXPECT_EQ(2, c
->adjust_item_weightf(g_ceph_context
, item
, modified_weight
));
529 EXPECT_EQ(modified_weight
, c
->get_item_weightf_in_loc(item
, loc_one
));
530 EXPECT_EQ(modified_weight
, c
->get_item_weightf_in_loc(item
, loc_two
));
533 EXPECT_EQ(1, c
->adjust_item_weightf_in_loc(g_ceph_context
, item
, modified_weight
, loc_two
));
534 EXPECT_EQ(original_weight
, c
->get_item_weightf_in_loc(item
, loc_one
));
535 EXPECT_EQ(modified_weight
, c
->get_item_weightf_in_loc(item
, loc_two
));
538 TEST(CrushWrapper
, adjust_subtree_weight
) {
539 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
541 const int ROOT_TYPE
= 2;
542 c
->set_type_name(ROOT_TYPE
, "root");
543 const int HOST_TYPE
= 1;
544 c
->set_type_name(HOST_TYPE
, "host");
545 const int OSD_TYPE
= 0;
546 c
->set_type_name(OSD_TYPE
, "osd");
549 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
550 ROOT_TYPE
, 0, NULL
, NULL
, &rootno
);
551 c
->set_item_name(rootno
, "default");
553 const string
HOST0("host0");
555 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
556 HOST_TYPE
, 0, NULL
, NULL
, &host0
);
557 c
->set_item_name(host0
, HOST0
);
559 const string
FAKE("fake");
561 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
562 HOST_TYPE
, 0, NULL
, NULL
, &hostfake
);
563 c
->set_item_name(hostfake
, FAKE
);
567 // construct crush map
570 map
<string
,string
> loc
;
571 loc
["host"] = "host0";
572 float host_weight
= 2.0;
576 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
577 "osd." + stringify(item
), loc
));
579 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
580 "osd." + stringify(item
), loc
));
582 bucket_id
= c
->get_item_id("host0");
583 EXPECT_EQ(true, c
->bucket_exists(bucket_id
));
584 EXPECT_EQ(host_weight
, c
->get_bucket_weightf(bucket_id
));
586 map
<string
,string
> bloc
;
587 bloc
["root"] = "default";
588 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, host0
, host_weight
,
593 map
<string
,string
> loc
;
594 loc
["host"] = "fake";
595 float host_weight
= 2.0;
599 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
600 "osd." + stringify(item
), loc
));
602 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
603 "osd." + stringify(item
), loc
));
605 bucket_id
= c
->get_item_id("fake");
606 EXPECT_EQ(true, c
->bucket_exists(bucket_id
));
607 EXPECT_EQ(host_weight
, c
->get_bucket_weightf(bucket_id
));
609 map
<string
,string
> bloc
;
610 bloc
["root"] = "default";
611 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, hostfake
, host_weight
,
615 //cout << "--------before---------" << std::endl;
616 //c->dump_tree(&cout, NULL);
617 ASSERT_EQ(c
->get_bucket_weight(host0
), 131072);
618 ASSERT_EQ(c
->get_bucket_weight(rootno
), 262144);
620 int r
= c
->adjust_subtree_weightf(g_ceph_context
, host0
, 2.0);
621 ASSERT_EQ(r
, 2); // 2 items changed
623 //cout << "--------after---------" << std::endl;
624 //c->dump_tree(&cout, NULL);
626 ASSERT_EQ(c
->get_bucket_weight(host0
), 262144);
627 ASSERT_EQ(c
->get_item_weight(host0
), 262144);
628 ASSERT_EQ(c
->get_bucket_weight(rootno
), 262144 + 131072);
631 TEST(CrushWrapper
, insert_item
) {
632 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
634 const int ROOT_TYPE
= 2;
635 c
->set_type_name(ROOT_TYPE
, "root");
636 const int HOST_TYPE
= 1;
637 c
->set_type_name(HOST_TYPE
, "host");
638 const int OSD_TYPE
= 0;
639 c
->set_type_name(OSD_TYPE
, "osd");
642 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
643 ROOT_TYPE
, 0, NULL
, NULL
, &rootno
);
644 c
->set_item_name(rootno
, "default");
648 // invalid names anywhere in loc trigger an error
650 map
<string
,string
> loc
;
651 loc
["host"] = "\001";
652 EXPECT_EQ(-EINVAL
, c
->insert_item(g_ceph_context
, item
, 1.0,
653 "osd." + stringify(item
), loc
));
656 // insert an item in an existing bucket
658 map
<string
,string
> loc
;
659 loc
["root"] = "default";
662 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
663 "osd." + stringify(item
), loc
));
664 int another_item
= item
+ 1;
665 EXPECT_EQ(-EEXIST
, c
->insert_item(g_ceph_context
, another_item
, 1.0,
666 "osd." + stringify(item
), loc
));
668 // implicit creation of a bucket
670 string name
= "NAME";
671 map
<string
,string
> loc
;
672 loc
["root"] = "default";
676 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
677 "osd." + stringify(item
), loc
));
679 // adding to an existing item name that is not associated with a bucket
681 string name
= "ITEM_WITHOUT_BUCKET";
682 map
<string
,string
> loc
;
683 loc
["root"] = "default";
686 c
->set_item_name(item
, name
);
689 EXPECT_EQ(-EINVAL
, c
->insert_item(g_ceph_context
, item
, 1.0,
690 "osd." + stringify(item
), loc
));
695 // default --> host0 --> item
697 // Trying to insert the same item higher in the hirarchy will fail
698 // because it would create a loop.
700 // default --> host0 --> item
707 map
<string
,string
> loc
;
708 loc
["root"] = "default";
709 loc
["host"] = "host0";
711 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
712 "osd." + stringify(item
), loc
));
715 map
<string
,string
> loc
;
716 loc
["root"] = "default";
718 EXPECT_EQ(-EINVAL
, c
->insert_item(g_ceph_context
, item
, 1.0,
719 "osd." + stringify(item
), loc
));
727 // Trying to insert default under host0 must fail
728 // because it would create a loop.
730 // default --> host0 --> default
733 map
<string
,string
> loc
;
734 loc
["host"] = "host0";
736 EXPECT_EQ(-ELOOP
, c
->insert_item(g_ceph_context
, rootno
, 1.0,
739 // fail when mapping a bucket to the wrong type
741 // create an OSD bucket
743 int r
= c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
744 10, 0, NULL
, NULL
, &osdno
);
746 c
->set_item_name(osdno
, "myosd");
747 map
<string
,string
> loc
;
748 loc
["root"] = "default";
749 // wrongfully pretend the osd is of type host
750 loc
["host"] = "myosd";
753 EXPECT_EQ(-EINVAL
, c
->insert_item(g_ceph_context
, item
, 1.0,
754 "osd." + stringify(item
), loc
));
756 // fail when no location
758 map
<string
,string
> loc
;
760 EXPECT_EQ(-EINVAL
, c
->insert_item(g_ceph_context
, item
, 1.0,
761 "osd." + stringify(item
), loc
));
765 TEST(CrushWrapper
, remove_item
) {
766 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
768 const int ROOT_TYPE
= 2;
769 c
->set_type_name(ROOT_TYPE
, "root");
770 const int HOST_TYPE
= 1;
771 c
->set_type_name(HOST_TYPE
, "host");
772 const int OSD_TYPE
= 0;
773 c
->set_type_name(OSD_TYPE
, "osd");
777 ASSERT_EQ(0, c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
778 ROOT_TYPE
, 0, NULL
, NULL
, &root
));
779 c
->set_item_name(root
, "root0");
784 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
785 HOST_TYPE
, 0, NULL
, NULL
, &host
);
786 c
->set_item_name(host
, "host0");
789 const int num_osd
= 12;
791 map
<string
, string
> loc
= {{"root", "root0"},
794 for (int item
= 0; item
< num_osd
; item
++) {
795 ASSERT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
796 name
+ to_string(item
), loc
));
799 const int item_to_remove
= num_osd
/ 2;
800 map
<string
, string
> loc
;
801 loc
.insert(c
->get_immediate_parent(item_to_remove
));
802 ASSERT_EQ(0, c
->remove_item(g_ceph_context
, item_to_remove
, true));
804 EXPECT_FALSE(c
->check_item_loc(g_ceph_context
, item_to_remove
, loc
, &weight
));
807 TEST(CrushWrapper
, item_bucket_names
) {
808 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
810 string name
= "NAME";
811 EXPECT_EQ(-EINVAL
, c
->set_item_name(index
, "\001"));
812 EXPECT_EQ(0, c
->set_item_name(index
, name
));
813 EXPECT_TRUE(c
->name_exists(name
));
814 EXPECT_TRUE(c
->item_exists(index
));
815 EXPECT_EQ(index
, c
->get_item_id(name
));
816 EXPECT_EQ(name
, c
->get_item_name(index
));
819 TEST(CrushWrapper
, bucket_types
) {
820 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
822 string name
= "NAME";
823 c
->set_type_name(index
, name
);
824 EXPECT_EQ(1, c
->get_num_type_names());
825 EXPECT_EQ(index
, c
->get_type_id(name
));
826 EXPECT_EQ(name
, c
->get_type_name(index
));
829 TEST(CrushWrapper
, is_valid_crush_name
) {
830 EXPECT_TRUE(CrushWrapper::is_valid_crush_name("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012456789-_"));
831 EXPECT_FALSE(CrushWrapper::is_valid_crush_name(""));
832 EXPECT_FALSE(CrushWrapper::is_valid_crush_name("\001"));
835 TEST(CrushWrapper
, is_valid_crush_loc
) {
836 map
<string
,string
> loc
;
837 EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(g_ceph_context
, loc
));
838 loc
["good"] = "better";
839 EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(g_ceph_context
, loc
));
841 map
<string
,string
> loc
;
842 loc
["\005"] = "default";
843 EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context
, loc
));
846 map
<string
,string
> loc
;
847 loc
["host"] = "\003";
848 EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context
, loc
));
852 TEST(CrushWrapper
, dump_rules
) {
853 std::unique_ptr
<CrushWrapper
> c(new CrushWrapper
);
855 const int ROOT_TYPE
= 1;
856 c
->set_type_name(ROOT_TYPE
, "root");
857 const int OSD_TYPE
= 0;
858 c
->set_type_name(OSD_TYPE
, "osd");
860 string
failure_domain_type("osd");
861 string
root_name("default");
863 c
->add_bucket(0, CRUSH_BUCKET_STRAW
, CRUSH_HASH_RJENKINS1
,
864 ROOT_TYPE
, 0, NULL
, NULL
, &rootno
);
865 c
->set_item_name(rootno
, root_name
);
869 pair
<string
,string
> loc
;
871 loc
= c
->get_immediate_parent(item
, &ret
);
872 EXPECT_EQ(-ENOENT
, ret
);
875 map
<string
,string
> loc
;
876 loc
["root"] = root_name
;
878 EXPECT_EQ(0, c
->insert_item(g_ceph_context
, item
, 1.0,
882 // no rule by default
884 Formatter
*f
= Formatter::create("json-pretty");
885 f
->open_array_section("rules");
891 EXPECT_EQ("[]\n", ss
.str());
895 int rule
= c
->add_simple_rule(name
, root_name
, failure_domain_type
, "",
896 "firstn", pg_pool_t::TYPE_ERASURE
);
900 Formatter
*f
= Formatter::create("xml");
905 EXPECT_EQ((unsigned)0, ss
.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
909 Formatter
*f
= Formatter::create("xml");
910 c
->dump_rule(rule
, f
);
914 EXPECT_EQ((unsigned)0, ss
.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
915 EXPECT_NE(string::npos
,
916 ss
.str().find("<item_name>default</item_name></step>"));
920 c
->get_rule_weight_osd_map(0, &wm
);
921 ASSERT_TRUE(wm
.size() == 1);
922 ASSERT_TRUE(wm
[0] == 1.0);
925 TEST(CrushWrapper
, distance
) {
928 c
.set_type_name(1, "host");
929 c
.set_type_name(2, "rack");
930 c
.set_type_name(3, "root");
932 int r
= c
.add_bucket(0, CRUSH_BUCKET_STRAW
,
933 CRUSH_HASH_DEFAULT
, 3, 0, NULL
,
937 c
.set_item_name(bno
, "default");
939 c
.set_max_devices(10);
941 //JSONFormatter jf(true);
943 map
<string
,string
> loc
;
946 loc
["root"] = "default";
947 c
.insert_item(g_ceph_context
, 0, 1, "osd.0", loc
);
952 loc
["root"] = "default";
953 c
.insert_item(g_ceph_context
, 1, 1, "osd.1", loc
);
958 loc
["root"] = "default";
959 c
.insert_item(g_ceph_context
, 2, 1, "osd.2", loc
);
964 loc
["root"] = "default";
965 c
.insert_item(g_ceph_context
, 3, 1, "osd.3", loc
);
967 vector
<pair
<string
,string
> > ol
;
968 c
.get_full_location_ordered(3, ol
);
969 ASSERT_EQ(3u, ol
.size());
970 ASSERT_EQ(make_pair(string("host"),string("b2")), ol
[0]);
971 ASSERT_EQ(make_pair(string("rack"),string("b")), ol
[1]);
972 ASSERT_EQ(make_pair(string("root"),string("default")), ol
[2]);
977 multimap
<string
,string
> p
;
978 p
.insert(make_pair("host","b2"));
979 p
.insert(make_pair("rack","b"));
980 p
.insert(make_pair("root","default"));
981 ASSERT_EQ(3, c
.get_common_ancestor_distance(g_ceph_context
, 0, p
));
982 ASSERT_EQ(3, c
.get_common_ancestor_distance(g_ceph_context
, 1, p
));
983 ASSERT_EQ(2, c
.get_common_ancestor_distance(g_ceph_context
, 2, p
));
984 ASSERT_EQ(1, c
.get_common_ancestor_distance(g_ceph_context
, 3, p
));
985 ASSERT_EQ(-ENOENT
, c
.get_common_ancestor_distance(g_ceph_context
, 123, p
));
987 // make sure a "multipath" location will reflect a minimal
988 // distance for both paths
989 p
.insert(make_pair("host","b1"));
990 ASSERT_EQ(1, c
.get_common_ancestor_distance(g_ceph_context
, 2, p
));
991 ASSERT_EQ(1, c
.get_common_ancestor_distance(g_ceph_context
, 3, p
));
994 TEST(CrushWrapper
, choose_args_compat
) {
997 c
.set_type_name(1, "host");
998 c
.set_type_name(2, "rack");
999 c
.set_type_name(3, "root");
1003 map
<string
,string
> loc
;
1005 loc
["rack"] = "r11";
1006 loc
["root"] = "default";
1008 c
.insert_item(g_ceph_context
, item
, weight
, "osd.1", loc
);
1011 loc
["rack"] = "r12";
1012 loc
["root"] = "default";
1014 c
.insert_item(g_ceph_context
, item
, weight
, "osd.2", loc
);
1016 ceph_assert(c
.add_simple_rule("rule1", "r11", "host", "",
1017 "firstn", pg_pool_t::TYPE_ERASURE
) >= 0);
1019 int id
= c
.get_item_id("b1");
1021 __u32 weights
= 666 * 0x10000;
1022 crush_weight_set weight_set
;
1023 weight_set
.size
= 1;
1024 weight_set
.weights
= &weights
;
1025 int maxbuckets
= c
.get_max_buckets();
1026 ceph_assert(maxbuckets
> 0);
1027 crush_choose_arg choose_args
[maxbuckets
];
1028 memset(choose_args
, '\0', sizeof(crush_choose_arg
) * maxbuckets
);
1029 choose_args
[-1-id
].ids_size
= 0;
1030 choose_args
[-1-id
].weight_set_positions
= 1;
1031 choose_args
[-1-id
].weight_set
= &weight_set
;
1032 crush_choose_arg_map arg_map
;
1033 arg_map
.size
= c
.get_max_buckets();
1034 arg_map
.args
= choose_args
;
1036 uint64_t features
= CEPH_FEATURE_CRUSH_TUNABLES5
|CEPH_FEATURE_INCARNATION_2
;
1037 int64_t caid
= CrushWrapper::DEFAULT_CHOOSE_ARGS
;
1039 // if the client is capable, encode choose_args
1041 c
.choose_args
[caid
] = arg_map
;
1043 c
.encode(bl
, features
|CEPH_FEATURE_CRUSH_CHOOSE_ARGS
);
1044 auto i
= bl
.cbegin();
1047 ASSERT_EQ(1u, c_new
.choose_args
.size());
1048 ASSERT_EQ(1u, c_new
.choose_args
[caid
].args
[-1-id
].weight_set_positions
);
1049 ASSERT_EQ(weights
, c_new
.choose_args
[caid
].args
[-1-id
].weight_set
[0].weights
[0]);
1050 ASSERT_EQ(weight
, c_new
.get_bucket_item_weightf(id
, 0));
1053 // if the client is not compatible, copy choose_arg in the weights
1055 c
.choose_args
[caid
] = arg_map
;
1057 c
.encode(bl
, features
);
1058 c
.choose_args
.clear();
1059 auto i
= bl
.cbegin();
1062 ASSERT_EQ(0u, c_new
.choose_args
.size());
1063 ASSERT_EQ((int)weights
, c_new
.get_bucket_item_weight(id
, 0));
1067 TEST(CrushWrapper
, remove_root
) {
1070 c
.set_type_name(1, "host");
1071 c
.set_type_name(2, "rack");
1072 c
.set_type_name(3, "root");
1076 map
<string
,string
> loc
;
1078 loc
["rack"] = "r11";
1079 loc
["root"] = "default";
1081 c
.insert_item(g_ceph_context
, item
, weight
, "osd.1", loc
);
1084 loc
["rack"] = "r12";
1085 loc
["root"] = "default";
1086 c
.insert_item(g_ceph_context
, item
, weight
, "osd.2", loc
);
1088 ceph_assert(c
.add_simple_rule("rule1", "r11", "host", "",
1089 "firstn", pg_pool_t::TYPE_ERASURE
) >= 0);
1090 ASSERT_TRUE(c
.name_exists("default"));
1091 ASSERT_TRUE(c
.name_exists("r11"));
1092 ASSERT_TRUE(c
.name_exists("r12"));
1093 ASSERT_EQ(c
.remove_root(g_ceph_context
, c
.get_item_id("default")), 0);
1094 ASSERT_FALSE(c
.name_exists("default"));
1095 ASSERT_FALSE(c
.name_exists("r11"));
1096 ASSERT_FALSE(c
.name_exists("r12"));
1099 TEST(CrushWrapper
, trim_roots_with_class
) {
1102 c
.set_type_name(1, "root");
1105 map
<string
,string
> loc
;
1106 loc
["root"] = "default";
1109 c
.insert_item(g_ceph_context
, item
, weight
, "osd.1", loc
);
1110 int cl
= c
.get_or_create_class_id("ssd");
1111 c
.class_map
[item
] = cl
;
1114 int root_id
= c
.get_item_id("default");
1116 map
<int32_t, map
<int32_t, int32_t>> old_class_bucket
;
1117 map
<int,map
<int,vector
<int>>> cmap_item_weight
; // cargs -> bno -> weights
1118 set
<int32_t> used_ids
;
1120 ASSERT_EQ(c
.device_class_clone(root_id
, cl
, old_class_bucket
, used_ids
,
1121 &clone_id
, &cmap_item_weight
), 0);
1123 ASSERT_TRUE(c
.name_exists("default"));
1124 ASSERT_TRUE(c
.name_exists("default~ssd"));
1125 c
.trim_roots_with_class(g_ceph_context
);
1126 ASSERT_TRUE(c
.name_exists("default"));
1127 ASSERT_FALSE(c
.name_exists("default~ssd"));
1130 TEST(CrushWrapper
, device_class_clone
) {
1133 c
.set_type_name(1, "host");
1134 c
.set_type_name(2, "root");
1136 map
<string
,string
> loc
;
1138 loc
["root"] = "default";
1142 c
.insert_item(g_ceph_context
, item
, weight
, "osd.1", loc
);
1143 int cl
= c
.get_or_create_class_id("ssd");
1144 c
.class_map
[item
] = cl
;
1146 int item_no_class
= 2;
1147 c
.insert_item(g_ceph_context
, item_no_class
, weight
, "osd.2", loc
);
1149 c
.reweight(g_ceph_context
);
1151 map
<int32_t, map
<int32_t, int32_t>> old_class_bucket
;
1152 map
<int,map
<int,vector
<int>>> cmap_item_weight
; // cargs -> bno -> weights
1153 set
<int32_t> used_ids
;
1154 int root_id
= c
.get_item_id("default");
1156 ASSERT_EQ(c
.device_class_clone(root_id
, cl
, old_class_bucket
, used_ids
,
1157 &clone_id
, &cmap_item_weight
), 0);
1158 ASSERT_TRUE(c
.name_exists("default~ssd"));
1159 ASSERT_EQ(clone_id
, c
.get_item_id("default~ssd"));
1160 ASSERT_TRUE(c
.subtree_contains(clone_id
, item
));
1161 ASSERT_FALSE(c
.subtree_contains(clone_id
, item_no_class
));
1162 ASSERT_TRUE(c
.subtree_contains(root_id
, item_no_class
));
1163 ASSERT_EQ(c
.get_item_weightf(root_id
), 2);
1164 ASSERT_EQ(c
.get_item_weightf(clone_id
), 1);
1165 // cloning again does nothing and returns the existing one
1167 ASSERT_EQ(c
.device_class_clone(root_id
, cl
, old_class_bucket
, used_ids
,
1168 &other_clone_id
, &cmap_item_weight
), 0);
1169 ASSERT_EQ(clone_id
, other_clone_id
);
1170 // invalid arguments
1171 ASSERT_EQ(c
.device_class_clone(12345, cl
, old_class_bucket
, used_ids
,
1172 &other_clone_id
, &cmap_item_weight
), -ECHILD
);
1173 ASSERT_EQ(c
.device_class_clone(root_id
, 12345, old_class_bucket
, used_ids
,
1174 &other_clone_id
, &cmap_item_weight
), -EBADF
);
1177 TEST(CrushWrapper
, split_id_class
) {
1180 c
.set_type_name(1, "root");
1183 map
<string
,string
> loc
;
1184 loc
["root"] = "default";
1187 c
.insert_item(g_ceph_context
, item
, weight
, "osd.1", loc
);
1188 int class_id
= c
.get_or_create_class_id("ssd");
1189 c
.class_map
[item
] = class_id
;
1191 map
<int32_t, map
<int32_t, int32_t>> old_class_bucket
;
1192 map
<int,map
<int,vector
<int>>> cmap_item_weight
; // cargs -> bno -> weights
1193 set
<int32_t> used_ids
;
1194 int item_id
= c
.get_item_id("default");
1196 ASSERT_EQ(c
.device_class_clone(item_id
, class_id
, old_class_bucket
, used_ids
,
1197 &clone_id
, &cmap_item_weight
), 0);
1198 int retrieved_item_id
;
1199 int retrieved_class_id
;
1200 ASSERT_EQ(c
.split_id_class(clone_id
, &retrieved_item_id
, &retrieved_class_id
), 0);
1201 ASSERT_EQ(item_id
, retrieved_item_id
);
1202 ASSERT_EQ(class_id
, retrieved_class_id
);
1204 ASSERT_EQ(c
.split_id_class(item_id
, &retrieved_item_id
, &retrieved_class_id
), 0);
1205 ASSERT_EQ(item_id
, retrieved_item_id
);
1206 ASSERT_EQ(-1, retrieved_class_id
);
1209 TEST(CrushWrapper
, populate_classes
) {
1212 c
.set_type_name(1, "root");
1215 map
<string
,string
> loc
;
1216 loc
["root"] = "default";
1219 c
.insert_item(g_ceph_context
, item
, weight
, "osd.1", loc
);
1220 int class_id
= c
.get_or_create_class_id("ssd");
1221 c
.class_map
[item
] = class_id
;
1223 map
<int32_t, map
<int32_t, int32_t>> old_class_bucket
;
1224 ASSERT_EQ(c
.populate_classes(old_class_bucket
), 0);
1226 ASSERT_TRUE(c
.name_exists("default~ssd"));
1228 old_class_bucket
= c
.class_bucket
;
1229 ASSERT_EQ(c
.populate_classes(old_class_bucket
), 0);
1230 ASSERT_EQ(old_class_bucket
, c
.class_bucket
);
1233 TEST(CrushWrapper
, remove_class_name
) {
1237 ASSERT_EQ(-ENOENT
, c
.remove_class_name("ssd"));
1238 ASSERT_GE(0, c
.get_or_create_class_id("ssd"));
1239 ASSERT_EQ(0, c
.remove_class_name("ssd"));
1240 ASSERT_EQ(-ENOENT
, c
.remove_class_name("ssd"));
1243 TEST(CrushWrapper
, try_remap_rule
) {
1244 // build a simple 2 level map
1247 c
.set_type_name(0, "osd");
1248 c
.set_type_name(1, "host");
1249 c
.set_type_name(2, "rack");
1250 c
.set_type_name(3, "root");
1252 int r
= c
.add_bucket(0, CRUSH_BUCKET_STRAW2
,
1253 CRUSH_HASH_DEFAULT
, 3, 0, NULL
,
1257 c
.set_item_name(bno
, "default");
1259 c
.set_max_devices(20);
1261 //JSONFormatter jf(true);
1263 map
<string
,string
> loc
;
1264 loc
["host"] = "foo";
1266 loc
["root"] = "default";
1267 c
.insert_item(g_ceph_context
, 0, 1, "osd.0", loc
);
1268 c
.insert_item(g_ceph_context
, 1, 1, "osd.1", loc
);
1269 c
.insert_item(g_ceph_context
, 2, 1, "osd.2", loc
);
1272 loc
["host"] = "bar";
1274 loc
["root"] = "default";
1275 c
.insert_item(g_ceph_context
, 3, 1, "osd.3", loc
);
1276 c
.insert_item(g_ceph_context
, 4, 1, "osd.4", loc
);
1277 c
.insert_item(g_ceph_context
, 5, 1, "osd.5", loc
);
1280 loc
["host"] = "baz";
1282 loc
["root"] = "default";
1283 c
.insert_item(g_ceph_context
, 6, 1, "osd.6", loc
);
1284 c
.insert_item(g_ceph_context
, 7, 1, "osd.7", loc
);
1285 c
.insert_item(g_ceph_context
, 8, 1, "osd.8", loc
);
1288 loc
["host"] = "qux";
1290 loc
["root"] = "default";
1291 c
.insert_item(g_ceph_context
, 9, 1, "osd.9", loc
);
1292 c
.insert_item(g_ceph_context
, 10, 1, "osd.10", loc
);
1293 c
.insert_item(g_ceph_context
, 11, 1, "osd.11", loc
);
1297 loc
["host"] = "bif";
1299 loc
["root"] = "default";
1300 c
.insert_item(g_ceph_context
, 12, 1, "osd.12", loc
);
1301 c
.insert_item(g_ceph_context
, 13, 1, "osd.13", loc
);
1302 c
.insert_item(g_ceph_context
, 14, 1, "osd.14", loc
);
1306 loc
["host"] = "pop";
1308 loc
["root"] = "default";
1309 c
.insert_item(g_ceph_context
, 15, 1, "osd.15", loc
);
1310 c
.insert_item(g_ceph_context
, 16, 1, "osd.16", loc
);
1311 c
.insert_item(g_ceph_context
, 17, 1, "osd.17", loc
);
1321 // take + choose device + emit
1323 cout
<< "take + choose + emit" << std::endl
;
1325 int rule
= c
.add_simple_rule("one", "default", "osd", "",
1329 vector
<int> orig
= { 0, 3, 9 };
1330 set
<int> overfull
= { 3 };
1331 vector
<int> underfull
= { 0, 2, 5, 8, 11 };
1333 int r
= c
.try_remap_rule(g_ceph_context
, rule
, 3,
1334 overfull
, underfull
,
1336 cout
<< orig
<< " -> r = " << (int)r
<< " out " << out
<< std::endl
;
1338 ASSERT_EQ(3u, out
.size());
1339 ASSERT_EQ(0, out
[0]);
1340 ASSERT_EQ(2, out
[1]);
1341 ASSERT_EQ(9, out
[2]);
1343 // make sure we cope with dups between underfull and future values in orig
1344 underfull
= {9, 0, 2, 5};
1347 r
= c
.try_remap_rule(g_ceph_context
, rule
, 3,
1348 overfull
, underfull
,
1350 cout
<< orig
<< " -> r = " << (int)r
<< " out " << out
<< std::endl
;
1352 ASSERT_EQ(3u, out
.size());
1353 ASSERT_EQ(1, out
[0]);
1354 ASSERT_EQ(0, out
[1]);
1355 ASSERT_EQ(9, out
[2]);
1360 cout
<< "take + chooseleaf + emit" << std::endl
;
1362 int rule
= c
.add_simple_rule("two", "default", "host", "",
1366 vector
<int> orig
= { 0, 3, 9 };
1367 set
<int> overfull
= { 3 };
1368 vector
<int> underfull
= { 0, 2, 5, 8, 11 };
1370 int r
= c
.try_remap_rule(g_ceph_context
, rule
, 3,
1371 overfull
, underfull
,
1373 cout
<< orig
<< " -> r = " << (int)r
<< " out " << out
<< std::endl
;
1375 ASSERT_EQ(3u, out
.size());
1376 ASSERT_EQ(0, out
[0]);
1377 ASSERT_EQ(5, out
[1]);
1378 ASSERT_EQ(9, out
[2]);
1383 cout
<< "take + choose + choose + choose + emit" << std::endl
;
1384 int rule
= c
.add_rule(2, 5, 0, 1, 10);
1386 c
.set_rule_step_take(rule
, 0, bno
);
1387 c
.set_rule_step_choose_indep(rule
, 1, 2, 2);
1388 c
.set_rule_step_choose_indep(rule
, 2, 2, 1);
1389 c
.set_rule_step_choose_indep(rule
, 3, 1, 0);
1390 c
.set_rule_step_emit(rule
, 4);
1392 vector
<int> orig
= { 0, 3, 16, 12 };
1393 set
<int> overfull
= { 3, 12 };
1394 vector
<int> underfull
= { 6, 7, 9, 3, 0, 1, 15, 16, 13, 2, 5, 8, 11 };
1396 int r
= c
.try_remap_rule(g_ceph_context
, rule
, 3,
1397 overfull
, underfull
,
1399 cout
<< orig
<< " -> r = " << (int)r
<< " out " << out
<< std::endl
;
1401 ASSERT_EQ(4u, out
.size());
1402 ASSERT_EQ(0, out
[0]);
1403 ASSERT_EQ(5, out
[1]);
1404 ASSERT_EQ(16, out
[2]);
1405 ASSERT_EQ(13, out
[3]);
1409 r
= c
.try_remap_rule(g_ceph_context
, rule
, 3,
1410 overfull
, underfull
,
1412 cout
<< orig
<< " -> r = " << (int)r
<< " out " << out
<< std::endl
;
1414 ASSERT_EQ(3u, out
.size());
1415 ASSERT_EQ(0, out
[0]);
1416 ASSERT_EQ(5, out
[1]);
1417 ASSERT_EQ(16, out
[2]);
1421 int main(int argc
, char **argv
) {
1422 vector
<const char*> args
;
1423 argv_to_vec(argc
, (const char **)argv
, args
);
1425 map
<string
,string
> defaults
= {
1426 { "debug_crush", "0" }
1428 auto cct
= global_init(&defaults
, args
, CEPH_ENTITY_TYPE_CLIENT
,
1429 CODE_ENVIRONMENT_UTILITY
,
1430 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE
);
1431 common_init_finish(g_ceph_context
);
1432 ::testing::InitGoogleTest(&argc
, argv
);
1433 return RUN_ALL_TESTS();
1436 // compile-command: "cd ../../../build ; make -j4 unittest_crush_wrapper && valgrind --tool=memcheck bin/unittest_crush_wrapper"