]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/crush/CrushWrapper.cc
2d6945d8598acbfc568b28359ba77834c4662fbe
[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", "firstn", pg_pool_t::TYPE_ERASURE) >= 0);
1036
1037 int id = c.get_item_id("b1");
1038
1039 __u32 weights = 666 * 0x10000;
1040 crush_weight_set weight_set;
1041 weight_set.size = 1;
1042 weight_set.weights = &weights;
1043 int maxbuckets = c.get_max_buckets();
1044 assert(maxbuckets > 0);
1045 crush_choose_arg choose_args[maxbuckets];
1046 memset(choose_args, '\0', sizeof(crush_choose_arg) * maxbuckets);
1047 choose_args[-1-id].ids_size = 0;
1048 choose_args[-1-id].weight_set_size = 1;
1049 choose_args[-1-id].weight_set = &weight_set;
1050 crush_choose_arg_map arg_map;
1051 arg_map.size = c.get_max_buckets();
1052 arg_map.args = choose_args;
1053
1054 uint64_t features = CEPH_FEATURE_CRUSH_TUNABLES5|CEPH_FEATURE_INCARNATION_2;
1055
1056 // if the client is capable, encode choose_args
1057 {
1058 c.choose_args[0] = arg_map;
1059 bufferlist bl;
1060 c.encode(bl, features|CEPH_FEATURE_CRUSH_CHOOSE_ARGS);
1061 bufferlist::iterator i(bl.begin());
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[0].args[-1-id].weight_set_size);
1066 ASSERT_EQ(weights, c_new.choose_args[0].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[0] = arg_map;
1073 bufferlist bl;
1074 c.encode(bl, features);
1075 c.choose_args.clear();
1076 bufferlist::iterator i(bl.begin());
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(CrushWrapper, remove_unused_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(g_ceph_context, item, weight, "osd.1", loc);
1099 item = 2;
1100 loc["host"] = "b2";
1101 loc["rack"] = "r12";
1102 loc["root"] = "default";
1103 c.insert_item(g_ceph_context, item, weight, "osd.2", loc);
1104
1105 assert(c.add_simple_rule("rule1", "r11", "host", "firstn", pg_pool_t::TYPE_ERASURE) >= 0);
1106 ASSERT_TRUE(c.name_exists("default"));
1107 ASSERT_TRUE(c.name_exists("r11"));
1108 ASSERT_TRUE(c.name_exists("r12"));
1109 ASSERT_EQ(c.remove_root(c.get_item_id("default"), true), 0);
1110 ASSERT_FALSE(c.name_exists("default"));
1111 ASSERT_TRUE(c.name_exists("r11"));
1112 ASSERT_FALSE(c.name_exists("r12"));
1113 }
1114
1115 TEST(CrushWrapper, trim_roots_with_class) {
1116 CrushWrapper c;
1117 c.create();
1118 c.set_type_name(1, "root");
1119
1120 int weight = 1;
1121 map<string,string> loc;
1122 loc["root"] = "default";
1123
1124 int item = 1;
1125 c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1126 int cl = c.get_or_create_class_id("ssd");
1127 c.class_map[item] = cl;
1128
1129
1130 int root_id = c.get_item_id("default");
1131 int clone_id;
1132 ASSERT_EQ(c.device_class_clone(root_id, cl, &clone_id), 0);
1133
1134 ASSERT_TRUE(c.name_exists("default"));
1135 ASSERT_TRUE(c.name_exists("default~ssd"));
1136 c.trim_roots_with_class(true); // do nothing because still in use
1137 ASSERT_TRUE(c.name_exists("default"));
1138 ASSERT_TRUE(c.name_exists("default~ssd"));
1139 c.class_bucket.clear();
1140 c.trim_roots_with_class(true); // do nothing because still in use
1141 ASSERT_TRUE(c.name_exists("default"));
1142 ASSERT_FALSE(c.name_exists("default~ssd"));
1143 }
1144
1145 TEST(CrushWrapper, device_class_clone) {
1146 CrushWrapper c;
1147 c.create();
1148 c.set_type_name(1, "host");
1149 c.set_type_name(2, "root");
1150
1151 map<string,string> loc;
1152 loc["host"] = "b1";
1153 loc["root"] = "default";
1154 int weight = 1;
1155
1156 int item = 1;
1157 c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1158 int cl = c.get_or_create_class_id("ssd");
1159 c.class_map[item] = cl;
1160
1161 int item_no_class = 2;
1162 c.insert_item(g_ceph_context, item_no_class, weight, "osd.2", loc);
1163
1164 c.reweight(g_ceph_context);
1165
1166 int root_id = c.get_item_id("default");
1167 int clone_id;
1168 ASSERT_EQ(c.device_class_clone(root_id, cl, &clone_id), 0);
1169 ASSERT_TRUE(c.name_exists("default~ssd"));
1170 ASSERT_EQ(clone_id, c.get_item_id("default~ssd"));
1171 ASSERT_TRUE(c.subtree_contains(clone_id, item));
1172 ASSERT_FALSE(c.subtree_contains(clone_id, item_no_class));
1173 ASSERT_TRUE(c.subtree_contains(root_id, item_no_class));
1174 ASSERT_EQ(c.get_item_weightf(root_id), 2);
1175 ASSERT_EQ(c.get_item_weightf(clone_id), 1);
1176 // cloning again does nothing and returns the existing one
1177 int other_clone_id;
1178 ASSERT_EQ(c.device_class_clone(root_id, cl, &other_clone_id), 0);
1179 ASSERT_EQ(clone_id, other_clone_id);
1180 // invalid arguments
1181 ASSERT_EQ(c.device_class_clone(12345, cl, &other_clone_id), -ECHILD);
1182 ASSERT_EQ(c.device_class_clone(root_id, 12345, &other_clone_id), -EBADF);
1183 }
1184
1185 TEST(CrushWrapper, split_id_class) {
1186 CrushWrapper c;
1187 c.create();
1188 c.set_type_name(1, "root");
1189
1190 int weight = 1;
1191 map<string,string> loc;
1192 loc["root"] = "default";
1193
1194 int item = 1;
1195 c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1196 int class_id = c.get_or_create_class_id("ssd");
1197 c.class_map[item] = class_id;
1198
1199 int item_id = c.get_item_id("default");
1200 int clone_id;
1201 ASSERT_EQ(c.device_class_clone(item_id, class_id, &clone_id), 0);
1202 int retrieved_item_id;
1203 int retrieved_class_id;
1204 ASSERT_EQ(c.split_id_class(clone_id, &retrieved_item_id, &retrieved_class_id), 0);
1205 ASSERT_EQ(item_id, retrieved_item_id);
1206 ASSERT_EQ(class_id, retrieved_class_id);
1207
1208 ASSERT_EQ(c.split_id_class(item_id, &retrieved_item_id, &retrieved_class_id), 0);
1209 ASSERT_EQ(item_id, retrieved_item_id);
1210 ASSERT_EQ(-1, retrieved_class_id);
1211 }
1212
1213 TEST(CrushWrapper, populate_and_cleanup_classes) {
1214 CrushWrapper c;
1215 c.create();
1216 c.set_type_name(1, "root");
1217
1218 int weight = 1;
1219 map<string,string> loc;
1220 loc["root"] = "default";
1221
1222 int item = 1;
1223 c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1224 int class_id = c.get_or_create_class_id("ssd");
1225 c.class_map[item] = class_id;
1226
1227 ASSERT_EQ(c.populate_classes(), 0);
1228
1229 ASSERT_TRUE(c.name_exists("default~ssd"));
1230
1231 c.class_bucket.clear();
1232 ASSERT_EQ(c.cleanup_classes(), 0);
1233 ASSERT_FALSE(c.name_exists("default~ssd"));
1234 }
1235
1236 TEST(CrushWrapper, class_is_in_use) {
1237 CrushWrapper c;
1238 c.create();
1239 c.set_type_name(1, "root");
1240
1241 int weight = 1;
1242 map<string,string> loc;
1243 loc["root"] = "default";
1244
1245 ASSERT_FALSE(c.class_is_in_use(0));
1246
1247 int item = 1;
1248 c.insert_item(g_ceph_context, item, weight, "osd.1", loc);
1249 int class_id = c.get_or_create_class_id("ssd");
1250 c.class_map[item] = class_id;
1251
1252 ASSERT_TRUE(c.class_is_in_use(c.get_class_id("ssd")));
1253 ASSERT_EQ(0, c.remove_class_name("ssd"));
1254 ASSERT_FALSE(c.class_is_in_use(c.get_class_id("ssd")));
1255 }
1256
1257 TEST(CrushWrapper, remove_class_name) {
1258 CrushWrapper c;
1259 c.create();
1260
1261 ASSERT_EQ(-ENOENT, c.remove_class_name("ssd"));
1262 ASSERT_GE(0, c.get_or_create_class_id("ssd"));
1263 ASSERT_EQ(0, c.remove_class_name("ssd"));
1264 ASSERT_EQ(-ENOENT, c.remove_class_name("ssd"));
1265 }
1266
1267 TEST(CrushWrapper, try_remap_rule) {
1268 // build a simple 2 level map
1269 CrushWrapper c;
1270 c.create();
1271 c.set_type_name(0, "osd");
1272 c.set_type_name(1, "host");
1273 c.set_type_name(2, "rack");
1274 c.set_type_name(3, "root");
1275 int bno;
1276 int r = c.add_bucket(0, CRUSH_BUCKET_STRAW2,
1277 CRUSH_HASH_DEFAULT, 3, 0, NULL,
1278 NULL, &bno);
1279 ASSERT_EQ(0, r);
1280 ASSERT_EQ(-1, bno);
1281 c.set_item_name(bno, "default");
1282
1283 c.set_max_devices(20);
1284
1285 //JSONFormatter jf(true);
1286
1287 map<string,string> loc;
1288 loc["host"] = "foo";
1289 loc["rack"] = "a";
1290 loc["root"] = "default";
1291 c.insert_item(g_ceph_context, 0, 1, "osd.0", loc);
1292 c.insert_item(g_ceph_context, 1, 1, "osd.1", loc);
1293 c.insert_item(g_ceph_context, 2, 1, "osd.2", loc);
1294
1295 loc.clear();
1296 loc["host"] = "bar";
1297 loc["rack"] = "a";
1298 loc["root"] = "default";
1299 c.insert_item(g_ceph_context, 3, 1, "osd.3", loc);
1300 c.insert_item(g_ceph_context, 4, 1, "osd.4", loc);
1301 c.insert_item(g_ceph_context, 5, 1, "osd.5", loc);
1302
1303 loc.clear();
1304 loc["host"] = "baz";
1305 loc["rack"] = "b";
1306 loc["root"] = "default";
1307 c.insert_item(g_ceph_context, 6, 1, "osd.6", loc);
1308 c.insert_item(g_ceph_context, 7, 1, "osd.7", loc);
1309 c.insert_item(g_ceph_context, 8, 1, "osd.8", loc);
1310
1311 loc.clear();
1312 loc["host"] = "qux";
1313 loc["rack"] = "b";
1314 loc["root"] = "default";
1315 c.insert_item(g_ceph_context, 9, 1, "osd.9", loc);
1316 c.insert_item(g_ceph_context, 10, 1, "osd.10", loc);
1317 c.insert_item(g_ceph_context, 11, 1, "osd.11", loc);
1318 c.finalize();
1319
1320 loc.clear();
1321 loc["host"] = "bif";
1322 loc["rack"] = "c";
1323 loc["root"] = "default";
1324 c.insert_item(g_ceph_context, 12, 1, "osd.12", loc);
1325 c.insert_item(g_ceph_context, 13, 1, "osd.13", loc);
1326 c.insert_item(g_ceph_context, 14, 1, "osd.14", loc);
1327 c.finalize();
1328
1329 loc.clear();
1330 loc["host"] = "pop";
1331 loc["rack"] = "c";
1332 loc["root"] = "default";
1333 c.insert_item(g_ceph_context, 15, 1, "osd.15", loc);
1334 c.insert_item(g_ceph_context, 16, 1, "osd.16", loc);
1335 c.insert_item(g_ceph_context, 17, 1, "osd.17", loc);
1336 c.finalize();
1337
1338 //c.dump(&jf);
1339 //jf.flush(cout);
1340
1341 // take + emit
1342 {
1343 }
1344
1345 // take + choose device + emit
1346 {
1347 cout << "take + choose + emit" << std::endl;
1348 ostringstream err;
1349 int rule = c.add_simple_rule("one", "default", "osd", "firstn", 0, &err);
1350 ASSERT_EQ(rule, 0);
1351
1352 vector<int> orig = { 0, 3, 9 };
1353 set<int> overfull = { 3 };
1354 vector<int> underfull = { 0, 2, 5, 8, 11 };
1355 vector<int> out;
1356 int r = c.try_remap_rule(g_ceph_context, rule, 3,
1357 overfull, underfull,
1358 orig, &out);
1359 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1360 ASSERT_EQ(r, 0);
1361 ASSERT_EQ(3u, out.size());
1362 ASSERT_EQ(0, out[0]);
1363 ASSERT_EQ(2, out[1]);
1364 ASSERT_EQ(9, out[2]);
1365
1366 // make sure we cope with dups between underfull and future values in orig
1367 underfull = {9, 0, 2, 5};
1368 orig = {1, 3, 9};
1369
1370 r = c.try_remap_rule(g_ceph_context, rule, 3,
1371 overfull, underfull,
1372 orig, &out);
1373 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1374 ASSERT_EQ(r, 0);
1375 ASSERT_EQ(3u, out.size());
1376 ASSERT_EQ(1, out[0]);
1377 ASSERT_EQ(0, out[1]);
1378 ASSERT_EQ(9, out[2]);
1379 }
1380
1381 // chooseleaf
1382 {
1383 cout << "take + chooseleaf + emit" << std::endl;
1384 ostringstream err;
1385 int rule = c.add_simple_rule("two", "default", "host", "firstn", 0, &err);
1386 ASSERT_EQ(rule, 1);
1387
1388 vector<int> orig = { 0, 3, 9 };
1389 set<int> overfull = { 3 };
1390 vector<int> underfull = { 0, 2, 5, 8, 11 };
1391 vector<int> out;
1392 int r = c.try_remap_rule(g_ceph_context, rule, 3,
1393 overfull, underfull,
1394 orig, &out);
1395 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1396 ASSERT_EQ(r, 0);
1397 ASSERT_EQ(3u, out.size());
1398 ASSERT_EQ(0, out[0]);
1399 ASSERT_EQ(5, out[1]);
1400 ASSERT_EQ(9, out[2]);
1401 }
1402
1403 // choose + choose
1404 {
1405 cout << "take + choose + choose + choose + emit" << std::endl;
1406 int rule = c.add_rule(5, 2, 0, 1, 10, 2);
1407 ASSERT_EQ(2, rule);
1408 c.set_rule_step_take(rule, 0, bno);
1409 c.set_rule_step_choose_indep(rule, 1, 2, 2);
1410 c.set_rule_step_choose_indep(rule, 2, 2, 1);
1411 c.set_rule_step_choose_indep(rule, 3, 1, 0);
1412 c.set_rule_step_emit(rule, 4);
1413
1414 vector<int> orig = { 0, 3, 16, 12 };
1415 set<int> overfull = { 3, 12 };
1416 vector<int> underfull = { 6, 7, 9, 3, 0, 1, 15, 16, 13, 2, 5, 8, 11 };
1417 vector<int> out;
1418 int r = c.try_remap_rule(g_ceph_context, rule, 3,
1419 overfull, underfull,
1420 orig, &out);
1421 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1422 ASSERT_EQ(r, 0);
1423 ASSERT_EQ(4u, out.size());
1424 ASSERT_EQ(0, out[0]);
1425 ASSERT_EQ(5, out[1]);
1426 ASSERT_EQ(16, out[2]);
1427 ASSERT_EQ(13, out[3]);
1428
1429 orig.pop_back();
1430 out.clear();
1431 r = c.try_remap_rule(g_ceph_context, rule, 3,
1432 overfull, underfull,
1433 orig, &out);
1434 cout << orig << " -> r = " << (int)r << " out " << out << std::endl;
1435 ASSERT_EQ(r, 0);
1436 ASSERT_EQ(3u, out.size());
1437 ASSERT_EQ(0, out[0]);
1438 ASSERT_EQ(5, out[1]);
1439 ASSERT_EQ(16, out[2]);
1440 }
1441 }
1442
1443 int main(int argc, char **argv) {
1444 vector<const char*> args;
1445 argv_to_vec(argc, (const char **)argv, args);
1446 env_to_vec(args);
1447
1448 vector<const char*> def_args;
1449 def_args.push_back("--debug-crush=0");
1450 auto cct = global_init(&def_args, args, CEPH_ENTITY_TYPE_CLIENT,
1451 CODE_ENVIRONMENT_UTILITY, 0);
1452 common_init_finish(g_ceph_context);
1453 ::testing::InitGoogleTest(&argc, argv);
1454 return RUN_ALL_TESTS();
1455 }
1456 // Local Variables:
1457 // compile-command: "cd ../../../build ; make -j4 unittest_crush_wrapper && valgrind --tool=memcheck bin/unittest_crush_wrapper"
1458 // End: