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