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