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