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