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