unittest_util_LDADD = $(LIBCOMMON) -lm $(UNITTEST_LDADD) $(CRYPTO_LIBS) $(EXTRALIBS)
check_PROGRAMS += unittest_util
-unittest_crush_indep_SOURCES = test/crush/indep.cc
-unittest_crush_indep_CXXFLAGS = $(UNITTEST_CXXFLAGS)
-unittest_crush_indep_LDADD = $(LIBCOMMON) -lm $(UNITTEST_LDADD) $(CEPH_CRUSH) $(EXTRALIBS) $(CEPH_GLOBAL)
-check_PROGRAMS += unittest_crush_indep
+unittest_crush_wrapper_SOURCES = test/crush/CrushWrapper.cc
+unittest_crush_wrapper_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) $(LIBCRUSH)
+unittest_crush_wrapper_CXXFLAGS = $(UNITTEST_CXXFLAGS) -O2
+check_PROGRAMS += unittest_crush_wrapper
+
+unittest_crush_SOURCES = test/crush/crush.cc
+unittest_crush_CXXFLAGS = $(UNITTEST_CXXFLAGS)
+unittest_crush_LDADD = $(LIBCOMMON) -lm $(UNITTEST_LDADD) $(CEPH_CRUSH) $(EXTRALIBS) $(CEPH_GLOBAL)
+check_PROGRAMS += unittest_crush
unittest_osdmap_SOURCES = test/osd/TestOSDMap.cc
unittest_osdmap_CXXFLAGS = $(UNITTEST_CXXFLAGS)
unittest_throttle_CXXFLAGS = $(UNITTEST_CXXFLAGS) -O2
check_PROGRAMS += unittest_throttle
-unittest_crush_wrapper_SOURCES = test/crush/TestCrushWrapper.cc
-unittest_crush_wrapper_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) $(LIBCRUSH)
-unittest_crush_wrapper_CXXFLAGS = $(UNITTEST_CXXFLAGS) -O2
-check_PROGRAMS += unittest_crush_wrapper
-
unittest_base64_SOURCES = test/base64.cc
unittest_base64_LDADD = $(LIBCEPHFS) -lm $(UNITTEST_LDADD)
unittest_base64_CXXFLAGS = $(UNITTEST_CXXFLAGS)
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
+ * Copyright (C) 2014 Red Hat <contact@redhat.com>
+ *
+ * Author: Loic Dachary <loic@dachary.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library Public License for more details.
+ *
+ */
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "include/stringify.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+#include "include/Context.h"
+#include "osd/osd_types.h"
+
+#include "crush/CrushWrapper.h"
+
+TEST(CrushWrapper, get_immediate_parent) {
+ CrushWrapper *c = new CrushWrapper;
+
+ const int ROOT_TYPE = 1;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ int item = 0;
+
+ pair <string,string> loc;
+ int ret;
+ loc = c->get_immediate_parent(item, &ret);
+ EXPECT_EQ(-ENOENT, ret);
+
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd.0", loc));
+ }
+
+ loc = c->get_immediate_parent(item, &ret);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ("root", loc.first);
+ EXPECT_EQ("default", loc.second);
+
+ delete c;
+}
+
+TEST(CrushWrapper, straw_zero) {
+ // zero weight items should have no effect on placement.
+
+ CrushWrapper *c = new CrushWrapper;
+ const int ROOT_TYPE = 1;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int n = 5;
+ int items[n], weights[n];
+ for (int i=0; i <n; ++i) {
+ items[i] = i;
+ weights[i] = 0x10000 * (n-i-1);
+ }
+
+ c->set_max_devices(n);
+
+ string root_name0("root0");
+ int root0;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, root_name0));
+
+ string name0("rule0");
+ int ruleset0 = c->add_simple_ruleset(name0, root_name0, "osd",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(0, ruleset0);
+
+ string root_name1("root1");
+ int root1;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n-1, items, weights, &root1));
+ EXPECT_EQ(0, c->set_item_name(root1, root_name1));
+
+ string name1("rule1");
+ int ruleset1 = c->add_simple_ruleset(name1, root_name1, "osd",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(1, ruleset1);
+
+ vector<unsigned> reweight(n, 0x10000);
+ for (int i=0; i<10000; ++i) {
+ vector<int> out0, out1;
+ c->do_rule(ruleset0, i, out0, 1, reweight);
+ ASSERT_EQ(1u, out0.size());
+ c->do_rule(ruleset1, i, out1, 1, reweight);
+ ASSERT_EQ(1u, out1.size());
+ ASSERT_EQ(out0[0], out1[0]);
+ //cout << i << "\t" << out0 << "\t" << out1 << std::endl;
+ }
+}
+
+TEST(CrushWrapper, straw_same) {
+ // items with the same weight should map about the same as items
+ // with very similar weights.
+ //
+ // give the 0 vector a paired stair pattern, with dup weights. note
+ // that the original straw flaw does not appear when there are 2 of
+ // the initial weight, but it does when there is just 1.
+ //
+ // give the 1 vector a similar stair pattern, but make the same
+ // steps weights slightly different (no dups). this works.
+ //
+ // compare the result and verify that the resulting mapping is
+ // almost identical.
+
+ CrushWrapper *c = new CrushWrapper;
+ const int ROOT_TYPE = 1;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int n = 10;
+ int items[n], weights[n];
+ for (int i=0; i <n; ++i) {
+ items[i] = i;
+ weights[i] = 0x10000 * ((i+1)/2 + 1);
+ }
+
+ c->set_max_devices(n);
+
+ string root_name0("root0");
+ int root0;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, root_name0));
+
+ string name0("rule0");
+ int ruleset0 = c->add_simple_ruleset(name0, root_name0, "osd",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(0, ruleset0);
+
+ for (int i=0; i <n; ++i) {
+ items[i] = i;
+ weights[i] = 0x10000 * ((i+1)/2 + 1) + (i%2)*100;
+ }
+
+ string root_name1("root1");
+ int root1;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, n, items, weights, &root1));
+ EXPECT_EQ(0, c->set_item_name(root1, root_name1));
+
+ string name1("rule1");
+ int ruleset1 = c->add_simple_ruleset(name1, root_name1, "osd",
+ "firstn", pg_pool_t::TYPE_REPLICATED);
+ EXPECT_EQ(1, ruleset1);
+
+ if (0) {
+ crush_bucket_straw *sb0 = reinterpret_cast<crush_bucket_straw*>(c->get_crush_map()->buckets[-1-root0]);
+ crush_bucket_straw *sb1 = reinterpret_cast<crush_bucket_straw*>(c->get_crush_map()->buckets[-1-root1]);
+
+ for (int i=0; i<n; ++i) {
+ cout << i
+ << "\t" << sb0->item_weights[i]
+ << "\t" << sb1->item_weights[i]
+ << "\t"
+ << "\t" << sb0->straws[i]
+ << "\t" << sb1->straws[i]
+ << std::endl;
+ }
+ }
+
+ if (0) {
+ JSONFormatter jf(true);
+ jf.open_object_section("crush");
+ c->dump(&jf);
+ jf.close_section();
+ jf.flush(cout);
+ }
+
+ vector<int> sum0(n, 0), sum1(n, 0);
+ vector<unsigned> reweight(n, 0x10000);
+ int different = 0;
+ int max = 100000;
+ for (int i=0; i<max; ++i) {
+ vector<int> out0, out1;
+ c->do_rule(ruleset0, i, out0, 1, reweight);
+ ASSERT_EQ(1u, out0.size());
+ c->do_rule(ruleset1, i, out1, 1, reweight);
+ ASSERT_EQ(1u, out1.size());
+ sum0[out0[0]]++;
+ sum1[out1[0]]++;
+ if (out0[0] != out1[0])
+ different++;
+ }
+ for (int i=0; i<n; ++i) {
+ cout << i
+ << "\t" << ((double)weights[i] / (double)weights[0])
+ << "\t" << sum0[i] << "\t" << ((double)sum0[i]/(double)sum0[0])
+ << "\t" << sum1[i] << "\t" << ((double)sum1[i]/(double)sum1[0])
+ << std::endl;
+ }
+ double ratio = ((double)different / (double)max);
+ cout << different << " of " << max << " = "
+ << ratio
+ << " different" << std::endl;
+ ASSERT_LT(ratio, .001);
+}
+
+TEST(CrushWrapper, move_bucket) {
+ CrushWrapper *c = new CrushWrapper;
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int root0;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, "root0"));
+
+ {
+ map<string,string> loc;
+ loc["root"] = "root0";
+ loc["host"] = "host0";
+
+ int item = 0;
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd.0", loc));
+ }
+ int host0 = c->get_item_id("host0");
+
+ int root1;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &root1));
+ EXPECT_EQ(0, c->set_item_name(root1, "root1"));
+
+ map<string,string> loc;
+ loc["root"] = "root1";
+
+ // 0 is not a valid bucket number, must be negative
+ EXPECT_EQ(-EINVAL, c->move_bucket(g_ceph_context, 0, loc));
+ // -100 is not an existing bucket
+ EXPECT_EQ(-ENOENT, c->move_bucket(g_ceph_context, -100, loc));
+ // move host0 from root0 to root1
+ {
+ pair <string,string> loc;
+ int ret;
+ loc = c->get_immediate_parent(host0, &ret);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ("root", loc.first);
+ EXPECT_EQ("root0", loc.second);
+ }
+ EXPECT_EQ(0, c->move_bucket(g_ceph_context, host0, loc));
+ {
+ pair <string,string> loc;
+ int ret;
+ loc = c->get_immediate_parent(host0, &ret);
+ EXPECT_EQ(0, ret);
+ EXPECT_EQ("root", loc.first);
+ EXPECT_EQ("root1", loc.second);
+ }
+
+ delete c;
+}
+
+TEST(CrushWrapper, rename_bucket_or_item) {
+ CrushWrapper *c = new CrushWrapper;
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int root0;
+ EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &root0));
+ EXPECT_EQ(0, c->set_item_name(root0, "root0"));
+
+ int item = 0;
+ {
+ map<string,string> loc;
+ loc["root"] = "root0";
+ loc["host"] = "host0";
+
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd.0", loc));
+ }
+ item++;
+ {
+ map<string,string> loc;
+ loc["root"] = "root0";
+ loc["host"] = "host1";
+
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd.1", loc));
+ }
+
+ stringstream ss;
+ EXPECT_EQ(-EINVAL, c->can_rename_item("host0", "????", &ss));
+ EXPECT_EQ(-EINVAL, c->rename_item("host0", "????", &ss));
+ EXPECT_EQ(-EINVAL, c->can_rename_bucket("host0", "????", &ss));
+ EXPECT_EQ(-EINVAL, c->rename_bucket("host0", "????", &ss));
+
+ EXPECT_EQ(-EEXIST, c->can_rename_item("host0", "host1", &ss));
+ EXPECT_EQ(-EEXIST, c->rename_item("host0", "host1", &ss));
+ EXPECT_EQ(-EEXIST, c->can_rename_bucket("host0", "host1", &ss));
+ EXPECT_EQ(-EEXIST, c->rename_bucket("host0", "host1", &ss));
+
+ EXPECT_EQ(-EALREADY, c->can_rename_item("gone", "host1", &ss));
+ EXPECT_EQ(-EALREADY, c->rename_item("gone", "host1", &ss));
+ EXPECT_EQ(-EALREADY, c->can_rename_bucket("gone", "host1", &ss));
+ EXPECT_EQ(-EALREADY, c->rename_bucket("gone", "host1", &ss));
+
+ EXPECT_EQ(-ENOENT, c->can_rename_item("doesnotexist", "somethingelse", &ss));
+ EXPECT_EQ(-ENOENT, c->rename_item("doesnotexist", "somethingelse", &ss));
+ EXPECT_EQ(-ENOENT, c->can_rename_bucket("doesnotexist", "somethingelse", &ss));
+ EXPECT_EQ(-ENOENT, c->rename_bucket("doesnotexist", "somethingelse", &ss));
+
+ EXPECT_EQ(-ENOTDIR, c->can_rename_bucket("osd.1", "somethingelse", &ss));
+ EXPECT_EQ(-ENOTDIR, c->rename_bucket("osd.1", "somethingelse", &ss));
+
+ int host0id = c->get_item_id("host0");
+ EXPECT_EQ(0, c->rename_bucket("host0", "host0renamed", &ss));
+ EXPECT_EQ(host0id, c->get_item_id("host0renamed"));
+
+ int osd0id = c->get_item_id("osd0");
+ EXPECT_EQ(0, c->rename_item("osd.0", "osd0renamed", &ss));
+ EXPECT_EQ(osd0id, c->get_item_id("osd0renamed"));
+
+ delete c;
+}
+
+TEST(CrushWrapper, check_item_loc) {
+ CrushWrapper *c = new CrushWrapper;
+ int item = 0;
+ float expected_weight = 1.0;
+
+ // fail if loc is empty
+ {
+ float weight;
+ map<string,string> loc;
+ EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ }
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ // fail because the item is not found at the specified location
+ {
+ float weight;
+ map<string,string> loc;
+ loc["root"] = "default";
+ EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ }
+ // fail because the bucket name does not match an existing bucket
+ {
+ float weight;
+ map<string,string> loc;
+ loc["root"] = "default";
+ const string HOST("host0");
+ loc["host"] = HOST;
+ EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ }
+ const string OSD("osd.0");
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, expected_weight,
+ OSD, loc));
+ }
+ // fail because osd.0 is not a bucket and must not be in loc, in
+ // addition to being of the wrong type
+ {
+ float weight;
+ map<string,string> loc;
+ loc["root"] = "osd.0";
+ EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ }
+ // succeed and retrieves the expected weight
+ {
+ float weight;
+ map<string,string> loc;
+ loc["root"] = "default";
+ EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ EXPECT_EQ(expected_weight, weight);
+ }
+
+ delete c;
+}
+
+TEST(CrushWrapper, update_item) {
+ CrushWrapper *c = new CrushWrapper;
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ const string HOST0("host0");
+ int host0;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &host0);
+ c->set_item_name(host0, HOST0);
+
+ const string HOST1("host1");
+ int host1;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &host1);
+ c->set_item_name(host1, HOST1);
+
+ int item = 0;
+
+ // fail if invalid names anywhere in loc
+ {
+ map<string,string> loc;
+ loc["rack"] = "\001";
+ EXPECT_EQ(-EINVAL, c->update_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ // fail if invalid item name
+ {
+ map<string,string> loc;
+ EXPECT_EQ(-EINVAL, c->update_item(g_ceph_context, item, 1.0,
+ "\005", loc));
+ }
+ const string OSD0("osd.0");
+ const string OSD1("osd.1");
+ float original_weight = 1.0;
+ float modified_weight = 2.0;
+ float weight;
+
+ map<string,string> loc;
+ loc["root"] = "default";
+ loc["host"] = HOST0;
+ EXPECT_GE(0.0, c->get_item_weightf(host0));
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, original_weight,
+ OSD0, loc));
+
+ // updating nothing changes nothing
+ EXPECT_EQ(OSD0, c->get_item_name(item));
+ EXPECT_EQ(original_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ EXPECT_EQ(0, c->update_item(g_ceph_context, item, original_weight,
+ OSD0, loc));
+ EXPECT_EQ(OSD0, c->get_item_name(item));
+ EXPECT_EQ(original_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+
+ // update the name and weight of the item but not the location
+ EXPECT_EQ(OSD0, c->get_item_name(item));
+ EXPECT_EQ(original_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ EXPECT_EQ(1, c->update_item(g_ceph_context, item, modified_weight,
+ OSD1, loc));
+ EXPECT_EQ(OSD1, c->get_item_name(item));
+ EXPECT_EQ(modified_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ c->set_item_name(item, OSD0);
+ c->adjust_item_weightf(g_ceph_context, item, original_weight);
+
+ // update the name and weight of the item and change its location
+ map<string,string> other_loc;
+ other_loc["root"] = "default";
+ other_loc["host"] = HOST1;
+
+ EXPECT_EQ(OSD0, c->get_item_name(item));
+ EXPECT_EQ(original_weight, c->get_item_weightf(item));
+ EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, other_loc, &weight));
+ EXPECT_EQ(1, c->update_item(g_ceph_context, item, modified_weight,
+ OSD1, other_loc));
+ EXPECT_EQ(OSD1, c->get_item_name(item));
+ EXPECT_EQ(modified_weight, c->get_item_weightf(item));
+ EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
+ EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, other_loc, &weight));
+
+ delete c;
+}
+
+TEST(CrushWrapper, adjust_item_weight) {
+ CrushWrapper *c = new CrushWrapper;
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ const string HOST0("host0");
+ int host0;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &host0);
+ c->set_item_name(host0, HOST0);
+
+ const string FAKE("fake");
+ int hostfake;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ HOST_TYPE, 0, NULL, NULL, &hostfake);
+ c->set_item_name(hostfake, FAKE);
+
+ int item = 0;
+
+ // construct crush map
+
+ {
+ map<string,string> loc;
+ loc["host"] = "host0";
+ float host_weight = 2.0;
+ int bucket_id = 0;
+
+ item = 0;
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ item = 1;
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+
+ bucket_id = c->get_item_id("host0");
+ EXPECT_EQ(true, c->bucket_exists(bucket_id));
+ EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
+
+ }
+
+ {
+ map<string,string> loc;
+ loc["host"] = "fake";
+ float host_weight = 2.0;
+ int bucket_id = 0;
+
+ item = 0;
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ item = 1;
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+
+ bucket_id = c->get_item_id("fake");
+ EXPECT_EQ(true, c->bucket_exists(bucket_id));
+ EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
+ }
+
+ //
+ // When there is:
+ //
+ // default --> host0 --> osd.0 1.0
+ // | |
+ // | +-> osd.1 1.0
+ // |
+ // +-> fake --> osd.0 1.0
+ // |
+ // +-> osd.1 1.0
+ //
+ // Trying to adjust osd.0 weight to 2.0 in all buckets
+ // Trying to adjust osd.1 weight to 2.0 in host=fake
+ //
+ // So the crush map will be:
+ //
+ // default --> host0 --> osd.0 2.0
+ // | |
+ // | +-> osd.1 1.0
+ // |
+ // +-> fake --> osd.0 2.0
+ // |
+ // +-> osd.1 2.0
+ //
+
+ float original_weight = 1.0;
+ float modified_weight = 2.0;
+ map<string,string> loc_one, loc_two;
+ loc_one["host"] = "host0";
+ loc_two["host"] = "fake";
+
+ item = 0;
+ EXPECT_EQ(2, c->adjust_item_weightf(g_ceph_context, item, modified_weight));
+ EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_one));
+ EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_two));
+
+ item = 1;
+ EXPECT_EQ(1, c->adjust_item_weightf_in_loc(g_ceph_context, item, modified_weight, loc_two));
+ EXPECT_EQ(original_weight, c->get_item_weightf_in_loc(item, loc_one));
+ EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_two));
+}
+
+TEST(CrushWrapper, insert_item) {
+ CrushWrapper *c = new CrushWrapper;
+
+ const int ROOT_TYPE = 2;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int HOST_TYPE = 1;
+ c->set_type_name(HOST_TYPE, "host");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ int item = 0;
+
+ // invalid names anywhere in loc trigger an error
+ {
+ map<string,string> loc;
+ loc["host"] = "\001";
+ EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+
+ // insert an item in an existing bucket
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ item++;
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ int another_item = item + 1;
+ EXPECT_EQ(-EEXIST, c->insert_item(g_ceph_context, another_item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ // implicit creation of a bucket
+ {
+ string name = "NAME";
+ map<string,string> loc;
+ loc["root"] = "default";
+ loc["host"] = name;
+
+ item++;
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ // adding to an existing item name that is not associated with a bucket
+ {
+ string name = "ITEM_WITHOUT_BUCKET";
+ map<string,string> loc;
+ loc["root"] = "default";
+ loc["host"] = name;
+ item++;
+ c->set_item_name(item, name);
+
+ item++;
+ EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ //
+ // When there is:
+ //
+ // default --> host0 --> item
+ //
+ // Trying to insert the same item higher in the hirarchy will fail
+ // because it would create a loop.
+ //
+ // default --> host0 --> item
+ // |
+ // +-> item
+ //
+ {
+ item++;
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+ loc["host"] = "host0";
+
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ {
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ }
+ //
+ // When there is:
+ //
+ // default --> host0
+ //
+ // Trying to insert default under host0 must fail
+ // because it would create a loop.
+ //
+ // default --> host0 --> default
+ //
+ {
+ map<string,string> loc;
+ loc["host"] = "host0";
+
+ EXPECT_EQ(-ELOOP, c->insert_item(g_ceph_context, rootno, 1.0,
+ "default", loc));
+ }
+ // fail when mapping a bucket to the wrong type
+ {
+ // create an OSD bucket
+ int osdno;
+ int r = c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ 10, 0, NULL, NULL, &osdno);
+ ASSERT_EQ(0, r);
+ c->set_item_name(osdno, "myosd");
+ map<string,string> loc;
+ loc["root"] = "default";
+ // wrongfully pretend the osd is of type host
+ loc["host"] = "myosd";
+
+ item++;
+ EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+ // fail when no location
+ {
+ map<string,string> loc;
+ item++;
+ EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
+ "osd." + stringify(item), loc));
+ }
+
+ delete c;
+}
+
+TEST(CrushWrapper, item_bucket_names) {
+ CrushWrapper *c = new CrushWrapper;
+ int index = 123;
+ string name = "NAME";
+ EXPECT_EQ(-EINVAL, c->set_item_name(index, "\001"));
+ EXPECT_EQ(0, c->set_item_name(index, name));
+ EXPECT_TRUE(c->name_exists(name));
+ EXPECT_TRUE(c->item_exists(index));
+ EXPECT_EQ(index, c->get_item_id(name));
+ EXPECT_EQ(name, c->get_item_name(index));
+ delete c;
+}
+
+TEST(CrushWrapper, bucket_types) {
+ CrushWrapper *c = new CrushWrapper;
+ int index = 123;
+ string name = "NAME";
+ c->set_type_name(index, name);
+ EXPECT_EQ(1, c->get_num_type_names());
+ EXPECT_EQ(index, c->get_type_id(name));
+ EXPECT_EQ(name, c->get_type_name(index));
+ delete c;
+}
+
+TEST(CrushWrapper, is_valid_crush_name) {
+ EXPECT_TRUE(CrushWrapper::is_valid_crush_name("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012456789-_"));
+ EXPECT_FALSE(CrushWrapper::is_valid_crush_name(""));
+ EXPECT_FALSE(CrushWrapper::is_valid_crush_name("\001"));
+}
+
+TEST(CrushWrapper, is_valid_crush_loc) {
+ map<string,string> loc;
+ EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
+ loc["good"] = "better";
+ EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
+ {
+ map<string,string> loc;
+ loc["\005"] = "default";
+ EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
+ }
+ {
+ map<string,string> loc;
+ loc["host"] = "\003";
+ EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
+ }
+}
+
+TEST(CrushWrapper, dump_rules) {
+ CrushWrapper *c = new CrushWrapper;
+
+ const int ROOT_TYPE = 1;
+ c->set_type_name(ROOT_TYPE, "root");
+ const int OSD_TYPE = 0;
+ c->set_type_name(OSD_TYPE, "osd");
+
+ string failure_domain_type("osd");
+ string root_name("default");
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ ROOT_TYPE, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, root_name);
+
+ int item = 0;
+
+ pair <string,string> loc;
+ int ret;
+ loc = c->get_immediate_parent(item, &ret);
+ EXPECT_EQ(-ENOENT, ret);
+
+ {
+ map<string,string> loc;
+ loc["root"] = root_name;
+
+ EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
+ "osd.0", loc));
+ }
+
+ // no ruleset by default
+ {
+ Formatter *f = Formatter::create("json-pretty");
+ c->dump_rules(f);
+ stringstream ss;
+ f->flush(ss);
+ delete f;
+ EXPECT_EQ("\n", ss.str());
+ }
+
+ string name("NAME");
+ int ruleset = c->add_simple_ruleset(name, root_name, failure_domain_type,
+ "firstn", pg_pool_t::TYPE_ERASURE);
+ EXPECT_EQ(0, ruleset);
+
+ {
+ Formatter *f = Formatter::create("xml");
+ c->dump_rules(f);
+ stringstream ss;
+ f->flush(ss);
+ delete f;
+ EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
+ }
+
+ {
+ Formatter *f = Formatter::create("xml");
+ c->dump_rule(ruleset, f);
+ stringstream ss;
+ f->flush(ss);
+ delete f;
+ EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
+ EXPECT_NE(string::npos,
+ ss.str().find("<item_name>default</item_name></step>"));
+ }
+
+ map<int,float> wm;
+ c->get_rule_weight_osd_map(0, &wm);
+ ASSERT_TRUE(wm.size() == 1);
+ ASSERT_TRUE(wm[0] == 1.0);
+
+ delete c;
+}
+
+TEST(CrushWrapper, distance) {
+ CrushWrapper c;
+ c.create();
+ c.set_type_name(1, "host");
+ c.set_type_name(2, "rack");
+ c.set_type_name(3, "root");
+ int bno;
+ int r = c.add_bucket(0, CRUSH_BUCKET_STRAW,
+ CRUSH_HASH_DEFAULT, 3, 0, NULL,
+ NULL, &bno);
+ ASSERT_EQ(0, r);
+ ASSERT_EQ(-1, bno);
+ c.set_item_name(bno, "default");
+
+ c.set_max_devices(10);
+
+ //JSONFormatter jf(true);
+
+ map<string,string> loc;
+ loc["host"] = "a1";
+ loc["rack"] = "a";
+ loc["root"] = "default";
+ c.insert_item(g_ceph_context, 0, 1, "osd.0", loc);
+
+ loc.clear();
+ loc["host"] = "a2";
+ loc["rack"] = "a";
+ loc["root"] = "default";
+ c.insert_item(g_ceph_context, 1, 1, "osd.1", loc);
+
+ loc.clear();
+ loc["host"] = "b1";
+ loc["rack"] = "b";
+ loc["root"] = "default";
+ c.insert_item(g_ceph_context, 2, 1, "osd.2", loc);
+
+ loc.clear();
+ loc["host"] = "b2";
+ loc["rack"] = "b";
+ loc["root"] = "default";
+ c.insert_item(g_ceph_context, 3, 1, "osd.3", loc);
+
+ vector<pair<string,string> > ol;
+ c.get_full_location_ordered(3, ol);
+ ASSERT_EQ(3u, ol.size());
+ ASSERT_EQ(make_pair(string("host"),string("b2")), ol[0]);
+ ASSERT_EQ(make_pair(string("rack"),string("b")), ol[1]);
+ ASSERT_EQ(make_pair(string("root"),string("default")), ol[2]);
+
+ //c.dump(&jf);
+ //jf.flush(cout);
+
+ multimap<string,string> p;
+ p.insert(make_pair("host","b2"));
+ p.insert(make_pair("rack","b"));
+ p.insert(make_pair("root","default"));
+ ASSERT_EQ(3, c.get_common_ancestor_distance(g_ceph_context, 0, p));
+ ASSERT_EQ(3, c.get_common_ancestor_distance(g_ceph_context, 1, p));
+ ASSERT_EQ(2, c.get_common_ancestor_distance(g_ceph_context, 2, p));
+ ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 3, p));
+ ASSERT_EQ(-ENOENT, c.get_common_ancestor_distance(g_ceph_context, 123, p));
+
+ // make sure a "multipath" location will reflect a minimal
+ // distance for both paths
+ p.insert(make_pair("host","b1"));
+ ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 2, p));
+ ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 3, p));
+}
+
+int main(int argc, char **argv) {
+ vector<const char*> args;
+ argv_to_vec(argc, (const char **)argv, args);
+
+ vector<const char*> def_args;
+ def_args.push_back("--debug-crush=0");
+ global_init(&def_args, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
+ common_init_finish(g_ceph_context);
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd ../.. ; make -j4 unittest_crush_wrapper &&
+ * valgrind \
+ * --max-stackframe=20000000 --tool=memcheck \
+ * ./unittest_crush_wrapper --log-to-stderr=true --debug-crush=20 \
+ * # --gtest_filter=CrushWrapper.insert_item"
+ * End:
+ */
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2013 Cloudwatt <libre.licensing@cloudwatt.com>
- * Copyright (C) 2014 Red Hat <contact@redhat.com>
- *
- * Author: Loic Dachary <loic@dachary.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library Public License for more details.
- *
- */
-
-#include <iostream>
-#include <gtest/gtest.h>
-
-#include "include/stringify.h"
-#include "common/ceph_argparse.h"
-#include "global/global_init.h"
-#include "global/global_context.h"
-#include "include/Context.h"
-#include "osd/osd_types.h"
-
-#include "crush/CrushWrapper.h"
-
-TEST(CrushWrapper, get_immediate_parent) {
- CrushWrapper *c = new CrushWrapper;
-
- const int ROOT_TYPE = 1;
- c->set_type_name(ROOT_TYPE, "root");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int rootno;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &rootno);
- c->set_item_name(rootno, "default");
-
- int item = 0;
-
- pair <string,string> loc;
- int ret;
- loc = c->get_immediate_parent(item, &ret);
- EXPECT_EQ(-ENOENT, ret);
-
- {
- map<string,string> loc;
- loc["root"] = "default";
-
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd.0", loc));
- }
-
- loc = c->get_immediate_parent(item, &ret);
- EXPECT_EQ(0, ret);
- EXPECT_EQ("root", loc.first);
- EXPECT_EQ("default", loc.second);
-
- delete c;
-}
-
-TEST(CrushWrapper, straw_zero) {
- // zero weight items should have no effect on placement.
-
- CrushWrapper *c = new CrushWrapper;
- const int ROOT_TYPE = 1;
- c->set_type_name(ROOT_TYPE, "root");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int n = 5;
- int items[n], weights[n];
- for (int i=0; i <n; ++i) {
- items[i] = i;
- weights[i] = 0x10000 * (n-i-1);
- }
-
- c->set_max_devices(n);
-
- string root_name0("root0");
- int root0;
- EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, n, items, weights, &root0));
- EXPECT_EQ(0, c->set_item_name(root0, root_name0));
-
- string name0("rule0");
- int ruleset0 = c->add_simple_ruleset(name0, root_name0, "osd",
- "firstn", pg_pool_t::TYPE_REPLICATED);
- EXPECT_EQ(0, ruleset0);
-
- string root_name1("root1");
- int root1;
- EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, n-1, items, weights, &root1));
- EXPECT_EQ(0, c->set_item_name(root1, root_name1));
-
- string name1("rule1");
- int ruleset1 = c->add_simple_ruleset(name1, root_name1, "osd",
- "firstn", pg_pool_t::TYPE_REPLICATED);
- EXPECT_EQ(1, ruleset1);
-
- vector<unsigned> reweight(n, 0x10000);
- for (int i=0; i<10000; ++i) {
- vector<int> out0, out1;
- c->do_rule(ruleset0, i, out0, 1, reweight);
- ASSERT_EQ(1u, out0.size());
- c->do_rule(ruleset1, i, out1, 1, reweight);
- ASSERT_EQ(1u, out1.size());
- ASSERT_EQ(out0[0], out1[0]);
- //cout << i << "\t" << out0 << "\t" << out1 << std::endl;
- }
-}
-
-TEST(CrushWrapper, straw_same) {
- // items with the same weight should map about the same as items
- // with very similar weights.
- //
- // give the 0 vector a paired stair pattern, with dup weights. note
- // that the original straw flaw does not appear when there are 2 of
- // the initial weight, but it does when there is just 1.
- //
- // give the 1 vector a similar stair pattern, but make the same
- // steps weights slightly different (no dups). this works.
- //
- // compare the result and verify that the resulting mapping is
- // almost identical.
-
- CrushWrapper *c = new CrushWrapper;
- const int ROOT_TYPE = 1;
- c->set_type_name(ROOT_TYPE, "root");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int n = 10;
- int items[n], weights[n];
- for (int i=0; i <n; ++i) {
- items[i] = i;
- weights[i] = 0x10000 * ((i+1)/2 + 1);
- }
-
- c->set_max_devices(n);
-
- string root_name0("root0");
- int root0;
- EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, n, items, weights, &root0));
- EXPECT_EQ(0, c->set_item_name(root0, root_name0));
-
- string name0("rule0");
- int ruleset0 = c->add_simple_ruleset(name0, root_name0, "osd",
- "firstn", pg_pool_t::TYPE_REPLICATED);
- EXPECT_EQ(0, ruleset0);
-
- for (int i=0; i <n; ++i) {
- items[i] = i;
- weights[i] = 0x10000 * ((i+1)/2 + 1) + (i%2)*100;
- }
-
- string root_name1("root1");
- int root1;
- EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, n, items, weights, &root1));
- EXPECT_EQ(0, c->set_item_name(root1, root_name1));
-
- string name1("rule1");
- int ruleset1 = c->add_simple_ruleset(name1, root_name1, "osd",
- "firstn", pg_pool_t::TYPE_REPLICATED);
- EXPECT_EQ(1, ruleset1);
-
- if (0) {
- crush_bucket_straw *sb0 = reinterpret_cast<crush_bucket_straw*>(c->get_crush_map()->buckets[-1-root0]);
- crush_bucket_straw *sb1 = reinterpret_cast<crush_bucket_straw*>(c->get_crush_map()->buckets[-1-root1]);
-
- for (int i=0; i<n; ++i) {
- cout << i
- << "\t" << sb0->item_weights[i]
- << "\t" << sb1->item_weights[i]
- << "\t"
- << "\t" << sb0->straws[i]
- << "\t" << sb1->straws[i]
- << std::endl;
- }
- }
-
- if (0) {
- JSONFormatter jf(true);
- jf.open_object_section("crush");
- c->dump(&jf);
- jf.close_section();
- jf.flush(cout);
- }
-
- vector<int> sum0(n, 0), sum1(n, 0);
- vector<unsigned> reweight(n, 0x10000);
- int different = 0;
- int max = 100000;
- for (int i=0; i<max; ++i) {
- vector<int> out0, out1;
- c->do_rule(ruleset0, i, out0, 1, reweight);
- ASSERT_EQ(1u, out0.size());
- c->do_rule(ruleset1, i, out1, 1, reweight);
- ASSERT_EQ(1u, out1.size());
- sum0[out0[0]]++;
- sum1[out1[0]]++;
- if (out0[0] != out1[0])
- different++;
- }
- for (int i=0; i<n; ++i) {
- cout << i
- << "\t" << ((double)weights[i] / (double)weights[0])
- << "\t" << sum0[i] << "\t" << ((double)sum0[i]/(double)sum0[0])
- << "\t" << sum1[i] << "\t" << ((double)sum1[i]/(double)sum1[0])
- << std::endl;
- }
- double ratio = ((double)different / (double)max);
- cout << different << " of " << max << " = "
- << ratio
- << " different" << std::endl;
- ASSERT_LT(ratio, .001);
-}
-
-TEST(CrushWrapper, move_bucket) {
- CrushWrapper *c = new CrushWrapper;
-
- const int ROOT_TYPE = 2;
- c->set_type_name(ROOT_TYPE, "root");
- const int HOST_TYPE = 1;
- c->set_type_name(HOST_TYPE, "host");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int root0;
- EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &root0));
- EXPECT_EQ(0, c->set_item_name(root0, "root0"));
-
- {
- map<string,string> loc;
- loc["root"] = "root0";
- loc["host"] = "host0";
-
- int item = 0;
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd.0", loc));
- }
- int host0 = c->get_item_id("host0");
-
- int root1;
- EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &root1));
- EXPECT_EQ(0, c->set_item_name(root1, "root1"));
-
- map<string,string> loc;
- loc["root"] = "root1";
-
- // 0 is not a valid bucket number, must be negative
- EXPECT_EQ(-EINVAL, c->move_bucket(g_ceph_context, 0, loc));
- // -100 is not an existing bucket
- EXPECT_EQ(-ENOENT, c->move_bucket(g_ceph_context, -100, loc));
- // move host0 from root0 to root1
- {
- pair <string,string> loc;
- int ret;
- loc = c->get_immediate_parent(host0, &ret);
- EXPECT_EQ(0, ret);
- EXPECT_EQ("root", loc.first);
- EXPECT_EQ("root0", loc.second);
- }
- EXPECT_EQ(0, c->move_bucket(g_ceph_context, host0, loc));
- {
- pair <string,string> loc;
- int ret;
- loc = c->get_immediate_parent(host0, &ret);
- EXPECT_EQ(0, ret);
- EXPECT_EQ("root", loc.first);
- EXPECT_EQ("root1", loc.second);
- }
-
- delete c;
-}
-
-TEST(CrushWrapper, rename_bucket_or_item) {
- CrushWrapper *c = new CrushWrapper;
-
- const int ROOT_TYPE = 2;
- c->set_type_name(ROOT_TYPE, "root");
- const int HOST_TYPE = 1;
- c->set_type_name(HOST_TYPE, "host");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int root0;
- EXPECT_EQ(0, c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &root0));
- EXPECT_EQ(0, c->set_item_name(root0, "root0"));
-
- int item = 0;
- {
- map<string,string> loc;
- loc["root"] = "root0";
- loc["host"] = "host0";
-
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd.0", loc));
- }
- item++;
- {
- map<string,string> loc;
- loc["root"] = "root0";
- loc["host"] = "host1";
-
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd.1", loc));
- }
-
- stringstream ss;
- EXPECT_EQ(-EINVAL, c->can_rename_item("host0", "????", &ss));
- EXPECT_EQ(-EINVAL, c->rename_item("host0", "????", &ss));
- EXPECT_EQ(-EINVAL, c->can_rename_bucket("host0", "????", &ss));
- EXPECT_EQ(-EINVAL, c->rename_bucket("host0", "????", &ss));
-
- EXPECT_EQ(-EEXIST, c->can_rename_item("host0", "host1", &ss));
- EXPECT_EQ(-EEXIST, c->rename_item("host0", "host1", &ss));
- EXPECT_EQ(-EEXIST, c->can_rename_bucket("host0", "host1", &ss));
- EXPECT_EQ(-EEXIST, c->rename_bucket("host0", "host1", &ss));
-
- EXPECT_EQ(-EALREADY, c->can_rename_item("gone", "host1", &ss));
- EXPECT_EQ(-EALREADY, c->rename_item("gone", "host1", &ss));
- EXPECT_EQ(-EALREADY, c->can_rename_bucket("gone", "host1", &ss));
- EXPECT_EQ(-EALREADY, c->rename_bucket("gone", "host1", &ss));
-
- EXPECT_EQ(-ENOENT, c->can_rename_item("doesnotexist", "somethingelse", &ss));
- EXPECT_EQ(-ENOENT, c->rename_item("doesnotexist", "somethingelse", &ss));
- EXPECT_EQ(-ENOENT, c->can_rename_bucket("doesnotexist", "somethingelse", &ss));
- EXPECT_EQ(-ENOENT, c->rename_bucket("doesnotexist", "somethingelse", &ss));
-
- EXPECT_EQ(-ENOTDIR, c->can_rename_bucket("osd.1", "somethingelse", &ss));
- EXPECT_EQ(-ENOTDIR, c->rename_bucket("osd.1", "somethingelse", &ss));
-
- int host0id = c->get_item_id("host0");
- EXPECT_EQ(0, c->rename_bucket("host0", "host0renamed", &ss));
- EXPECT_EQ(host0id, c->get_item_id("host0renamed"));
-
- int osd0id = c->get_item_id("osd0");
- EXPECT_EQ(0, c->rename_item("osd.0", "osd0renamed", &ss));
- EXPECT_EQ(osd0id, c->get_item_id("osd0renamed"));
-
- delete c;
-}
-
-TEST(CrushWrapper, check_item_loc) {
- CrushWrapper *c = new CrushWrapper;
- int item = 0;
- float expected_weight = 1.0;
-
- // fail if loc is empty
- {
- float weight;
- map<string,string> loc;
- EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- }
-
- const int ROOT_TYPE = 2;
- c->set_type_name(ROOT_TYPE, "root");
- const int HOST_TYPE = 1;
- c->set_type_name(HOST_TYPE, "host");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int rootno;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &rootno);
- c->set_item_name(rootno, "default");
-
- // fail because the item is not found at the specified location
- {
- float weight;
- map<string,string> loc;
- loc["root"] = "default";
- EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- }
- // fail because the bucket name does not match an existing bucket
- {
- float weight;
- map<string,string> loc;
- loc["root"] = "default";
- const string HOST("host0");
- loc["host"] = HOST;
- EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- }
- const string OSD("osd.0");
- {
- map<string,string> loc;
- loc["root"] = "default";
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, expected_weight,
- OSD, loc));
- }
- // fail because osd.0 is not a bucket and must not be in loc, in
- // addition to being of the wrong type
- {
- float weight;
- map<string,string> loc;
- loc["root"] = "osd.0";
- EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- }
- // succeed and retrieves the expected weight
- {
- float weight;
- map<string,string> loc;
- loc["root"] = "default";
- EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- EXPECT_EQ(expected_weight, weight);
- }
-
- delete c;
-}
-
-TEST(CrushWrapper, update_item) {
- CrushWrapper *c = new CrushWrapper;
-
- const int ROOT_TYPE = 2;
- c->set_type_name(ROOT_TYPE, "root");
- const int HOST_TYPE = 1;
- c->set_type_name(HOST_TYPE, "host");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int rootno;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &rootno);
- c->set_item_name(rootno, "default");
-
- const string HOST0("host0");
- int host0;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- HOST_TYPE, 0, NULL, NULL, &host0);
- c->set_item_name(host0, HOST0);
-
- const string HOST1("host1");
- int host1;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- HOST_TYPE, 0, NULL, NULL, &host1);
- c->set_item_name(host1, HOST1);
-
- int item = 0;
-
- // fail if invalid names anywhere in loc
- {
- map<string,string> loc;
- loc["rack"] = "\001";
- EXPECT_EQ(-EINVAL, c->update_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- }
- // fail if invalid item name
- {
- map<string,string> loc;
- EXPECT_EQ(-EINVAL, c->update_item(g_ceph_context, item, 1.0,
- "\005", loc));
- }
- const string OSD0("osd.0");
- const string OSD1("osd.1");
- float original_weight = 1.0;
- float modified_weight = 2.0;
- float weight;
-
- map<string,string> loc;
- loc["root"] = "default";
- loc["host"] = HOST0;
- EXPECT_GE(0.0, c->get_item_weightf(host0));
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, original_weight,
- OSD0, loc));
-
- // updating nothing changes nothing
- EXPECT_EQ(OSD0, c->get_item_name(item));
- EXPECT_EQ(original_weight, c->get_item_weightf(item));
- EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- EXPECT_EQ(0, c->update_item(g_ceph_context, item, original_weight,
- OSD0, loc));
- EXPECT_EQ(OSD0, c->get_item_name(item));
- EXPECT_EQ(original_weight, c->get_item_weightf(item));
- EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
-
- // update the name and weight of the item but not the location
- EXPECT_EQ(OSD0, c->get_item_name(item));
- EXPECT_EQ(original_weight, c->get_item_weightf(item));
- EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- EXPECT_EQ(1, c->update_item(g_ceph_context, item, modified_weight,
- OSD1, loc));
- EXPECT_EQ(OSD1, c->get_item_name(item));
- EXPECT_EQ(modified_weight, c->get_item_weightf(item));
- EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- c->set_item_name(item, OSD0);
- c->adjust_item_weightf(g_ceph_context, item, original_weight);
-
- // update the name and weight of the item and change its location
- map<string,string> other_loc;
- other_loc["root"] = "default";
- other_loc["host"] = HOST1;
-
- EXPECT_EQ(OSD0, c->get_item_name(item));
- EXPECT_EQ(original_weight, c->get_item_weightf(item));
- EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, other_loc, &weight));
- EXPECT_EQ(1, c->update_item(g_ceph_context, item, modified_weight,
- OSD1, other_loc));
- EXPECT_EQ(OSD1, c->get_item_name(item));
- EXPECT_EQ(modified_weight, c->get_item_weightf(item));
- EXPECT_FALSE(c->check_item_loc(g_ceph_context, item, loc, &weight));
- EXPECT_TRUE(c->check_item_loc(g_ceph_context, item, other_loc, &weight));
-
- delete c;
-}
-
-TEST(CrushWrapper, adjust_item_weight) {
- CrushWrapper *c = new CrushWrapper;
-
- const int ROOT_TYPE = 2;
- c->set_type_name(ROOT_TYPE, "root");
- const int HOST_TYPE = 1;
- c->set_type_name(HOST_TYPE, "host");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int rootno;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &rootno);
- c->set_item_name(rootno, "default");
-
- const string HOST0("host0");
- int host0;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- HOST_TYPE, 0, NULL, NULL, &host0);
- c->set_item_name(host0, HOST0);
-
- const string FAKE("fake");
- int hostfake;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- HOST_TYPE, 0, NULL, NULL, &hostfake);
- c->set_item_name(hostfake, FAKE);
-
- int item = 0;
-
- // construct crush map
-
- {
- map<string,string> loc;
- loc["host"] = "host0";
- float host_weight = 2.0;
- int bucket_id = 0;
-
- item = 0;
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- item = 1;
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
-
- bucket_id = c->get_item_id("host0");
- EXPECT_EQ(true, c->bucket_exists(bucket_id));
- EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
-
- }
-
- {
- map<string,string> loc;
- loc["host"] = "fake";
- float host_weight = 2.0;
- int bucket_id = 0;
-
- item = 0;
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- item = 1;
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
-
- bucket_id = c->get_item_id("fake");
- EXPECT_EQ(true, c->bucket_exists(bucket_id));
- EXPECT_EQ(host_weight, c->get_bucket_weightf(bucket_id));
- }
-
- //
- // When there is:
- //
- // default --> host0 --> osd.0 1.0
- // | |
- // | +-> osd.1 1.0
- // |
- // +-> fake --> osd.0 1.0
- // |
- // +-> osd.1 1.0
- //
- // Trying to adjust osd.0 weight to 2.0 in all buckets
- // Trying to adjust osd.1 weight to 2.0 in host=fake
- //
- // So the crush map will be:
- //
- // default --> host0 --> osd.0 2.0
- // | |
- // | +-> osd.1 1.0
- // |
- // +-> fake --> osd.0 2.0
- // |
- // +-> osd.1 2.0
- //
-
- float original_weight = 1.0;
- float modified_weight = 2.0;
- map<string,string> loc_one, loc_two;
- loc_one["host"] = "host0";
- loc_two["host"] = "fake";
-
- item = 0;
- EXPECT_EQ(2, c->adjust_item_weightf(g_ceph_context, item, modified_weight));
- EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_one));
- EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_two));
-
- item = 1;
- EXPECT_EQ(1, c->adjust_item_weightf_in_loc(g_ceph_context, item, modified_weight, loc_two));
- EXPECT_EQ(original_weight, c->get_item_weightf_in_loc(item, loc_one));
- EXPECT_EQ(modified_weight, c->get_item_weightf_in_loc(item, loc_two));
-}
-
-TEST(CrushWrapper, insert_item) {
- CrushWrapper *c = new CrushWrapper;
-
- const int ROOT_TYPE = 2;
- c->set_type_name(ROOT_TYPE, "root");
- const int HOST_TYPE = 1;
- c->set_type_name(HOST_TYPE, "host");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- int rootno;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &rootno);
- c->set_item_name(rootno, "default");
-
- int item = 0;
-
- // invalid names anywhere in loc trigger an error
- {
- map<string,string> loc;
- loc["host"] = "\001";
- EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- }
-
- // insert an item in an existing bucket
- {
- map<string,string> loc;
- loc["root"] = "default";
-
- item++;
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- int another_item = item + 1;
- EXPECT_EQ(-EEXIST, c->insert_item(g_ceph_context, another_item, 1.0,
- "osd." + stringify(item), loc));
- }
- // implicit creation of a bucket
- {
- string name = "NAME";
- map<string,string> loc;
- loc["root"] = "default";
- loc["host"] = name;
-
- item++;
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- }
- // adding to an existing item name that is not associated with a bucket
- {
- string name = "ITEM_WITHOUT_BUCKET";
- map<string,string> loc;
- loc["root"] = "default";
- loc["host"] = name;
- item++;
- c->set_item_name(item, name);
-
- item++;
- EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- }
- //
- // When there is:
- //
- // default --> host0 --> item
- //
- // Trying to insert the same item higher in the hirarchy will fail
- // because it would create a loop.
- //
- // default --> host0 --> item
- // |
- // +-> item
- //
- {
- item++;
- {
- map<string,string> loc;
- loc["root"] = "default";
- loc["host"] = "host0";
-
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- }
- {
- map<string,string> loc;
- loc["root"] = "default";
-
- EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- }
- }
- //
- // When there is:
- //
- // default --> host0
- //
- // Trying to insert default under host0 must fail
- // because it would create a loop.
- //
- // default --> host0 --> default
- //
- {
- map<string,string> loc;
- loc["host"] = "host0";
-
- EXPECT_EQ(-ELOOP, c->insert_item(g_ceph_context, rootno, 1.0,
- "default", loc));
- }
- // fail when mapping a bucket to the wrong type
- {
- // create an OSD bucket
- int osdno;
- int r = c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- 10, 0, NULL, NULL, &osdno);
- ASSERT_EQ(0, r);
- c->set_item_name(osdno, "myosd");
- map<string,string> loc;
- loc["root"] = "default";
- // wrongfully pretend the osd is of type host
- loc["host"] = "myosd";
-
- item++;
- EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- }
- // fail when no location
- {
- map<string,string> loc;
- item++;
- EXPECT_EQ(-EINVAL, c->insert_item(g_ceph_context, item, 1.0,
- "osd." + stringify(item), loc));
- }
-
- delete c;
-}
-
-TEST(CrushWrapper, item_bucket_names) {
- CrushWrapper *c = new CrushWrapper;
- int index = 123;
- string name = "NAME";
- EXPECT_EQ(-EINVAL, c->set_item_name(index, "\001"));
- EXPECT_EQ(0, c->set_item_name(index, name));
- EXPECT_TRUE(c->name_exists(name));
- EXPECT_TRUE(c->item_exists(index));
- EXPECT_EQ(index, c->get_item_id(name));
- EXPECT_EQ(name, c->get_item_name(index));
- delete c;
-}
-
-TEST(CrushWrapper, bucket_types) {
- CrushWrapper *c = new CrushWrapper;
- int index = 123;
- string name = "NAME";
- c->set_type_name(index, name);
- EXPECT_EQ(1, c->get_num_type_names());
- EXPECT_EQ(index, c->get_type_id(name));
- EXPECT_EQ(name, c->get_type_name(index));
- delete c;
-}
-
-TEST(CrushWrapper, is_valid_crush_name) {
- EXPECT_TRUE(CrushWrapper::is_valid_crush_name("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ012456789-_"));
- EXPECT_FALSE(CrushWrapper::is_valid_crush_name(""));
- EXPECT_FALSE(CrushWrapper::is_valid_crush_name("\001"));
-}
-
-TEST(CrushWrapper, is_valid_crush_loc) {
- map<string,string> loc;
- EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
- loc["good"] = "better";
- EXPECT_TRUE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
- {
- map<string,string> loc;
- loc["\005"] = "default";
- EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
- }
- {
- map<string,string> loc;
- loc["host"] = "\003";
- EXPECT_FALSE(CrushWrapper::is_valid_crush_loc(g_ceph_context, loc));
- }
-}
-
-TEST(CrushWrapper, dump_rules) {
- CrushWrapper *c = new CrushWrapper;
-
- const int ROOT_TYPE = 1;
- c->set_type_name(ROOT_TYPE, "root");
- const int OSD_TYPE = 0;
- c->set_type_name(OSD_TYPE, "osd");
-
- string failure_domain_type("osd");
- string root_name("default");
- int rootno;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- ROOT_TYPE, 0, NULL, NULL, &rootno);
- c->set_item_name(rootno, root_name);
-
- int item = 0;
-
- pair <string,string> loc;
- int ret;
- loc = c->get_immediate_parent(item, &ret);
- EXPECT_EQ(-ENOENT, ret);
-
- {
- map<string,string> loc;
- loc["root"] = root_name;
-
- EXPECT_EQ(0, c->insert_item(g_ceph_context, item, 1.0,
- "osd.0", loc));
- }
-
- // no ruleset by default
- {
- Formatter *f = Formatter::create("json-pretty");
- c->dump_rules(f);
- stringstream ss;
- f->flush(ss);
- delete f;
- EXPECT_EQ("\n", ss.str());
- }
-
- string name("NAME");
- int ruleset = c->add_simple_ruleset(name, root_name, failure_domain_type,
- "firstn", pg_pool_t::TYPE_ERASURE);
- EXPECT_EQ(0, ruleset);
-
- {
- Formatter *f = Formatter::create("xml");
- c->dump_rules(f);
- stringstream ss;
- f->flush(ss);
- delete f;
- EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
- }
-
- {
- Formatter *f = Formatter::create("xml");
- c->dump_rule(ruleset, f);
- stringstream ss;
- f->flush(ss);
- delete f;
- EXPECT_EQ((unsigned)0, ss.str().find("<rule><rule_id>0</rule_id><rule_name>NAME</rule_name>"));
- EXPECT_NE(string::npos,
- ss.str().find("<item_name>default</item_name></step>"));
- }
-
- map<int,float> wm;
- c->get_rule_weight_osd_map(0, &wm);
- ASSERT_TRUE(wm.size() == 1);
- ASSERT_TRUE(wm[0] == 1.0);
-
- delete c;
-}
-
-TEST(CrushWrapper, distance) {
- CrushWrapper c;
- c.create();
- c.set_type_name(1, "host");
- c.set_type_name(2, "rack");
- c.set_type_name(3, "root");
- int bno;
- int r = c.add_bucket(0, CRUSH_BUCKET_STRAW,
- CRUSH_HASH_DEFAULT, 3, 0, NULL,
- NULL, &bno);
- ASSERT_EQ(0, r);
- ASSERT_EQ(-1, bno);
- c.set_item_name(bno, "default");
-
- c.set_max_devices(10);
-
- //JSONFormatter jf(true);
-
- map<string,string> loc;
- loc["host"] = "a1";
- loc["rack"] = "a";
- loc["root"] = "default";
- c.insert_item(g_ceph_context, 0, 1, "osd.0", loc);
-
- loc.clear();
- loc["host"] = "a2";
- loc["rack"] = "a";
- loc["root"] = "default";
- c.insert_item(g_ceph_context, 1, 1, "osd.1", loc);
-
- loc.clear();
- loc["host"] = "b1";
- loc["rack"] = "b";
- loc["root"] = "default";
- c.insert_item(g_ceph_context, 2, 1, "osd.2", loc);
-
- loc.clear();
- loc["host"] = "b2";
- loc["rack"] = "b";
- loc["root"] = "default";
- c.insert_item(g_ceph_context, 3, 1, "osd.3", loc);
-
- vector<pair<string,string> > ol;
- c.get_full_location_ordered(3, ol);
- ASSERT_EQ(3u, ol.size());
- ASSERT_EQ(make_pair(string("host"),string("b2")), ol[0]);
- ASSERT_EQ(make_pair(string("rack"),string("b")), ol[1]);
- ASSERT_EQ(make_pair(string("root"),string("default")), ol[2]);
-
- //c.dump(&jf);
- //jf.flush(cout);
-
- multimap<string,string> p;
- p.insert(make_pair("host","b2"));
- p.insert(make_pair("rack","b"));
- p.insert(make_pair("root","default"));
- ASSERT_EQ(3, c.get_common_ancestor_distance(g_ceph_context, 0, p));
- ASSERT_EQ(3, c.get_common_ancestor_distance(g_ceph_context, 1, p));
- ASSERT_EQ(2, c.get_common_ancestor_distance(g_ceph_context, 2, p));
- ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 3, p));
- ASSERT_EQ(-ENOENT, c.get_common_ancestor_distance(g_ceph_context, 123, p));
-
- // make sure a "multipath" location will reflect a minimal
- // distance for both paths
- p.insert(make_pair("host","b1"));
- ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 2, p));
- ASSERT_EQ(1, c.get_common_ancestor_distance(g_ceph_context, 3, p));
-}
-
-int main(int argc, char **argv) {
- vector<const char*> args;
- argv_to_vec(argc, (const char **)argv, args);
-
- vector<const char*> def_args;
- def_args.push_back("--debug-crush=0");
- global_init(&def_args, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
- common_init_finish(g_ceph_context);
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
-
-/*
- * Local Variables:
- * compile-command: "cd ../.. ; make -j4 unittest_crush_wrapper &&
- * valgrind \
- * --max-stackframe=20000000 --tool=memcheck \
- * ./unittest_crush_wrapper --log-to-stderr=true --debug-crush=20 \
- * # --gtest_filter=CrushWrapper.insert_item"
- * End:
- */
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank <info@inktank.com>
+ *
+ * LGPL2.1 (see COPYING-LGPL2.1) or later
+ */
+
+#include <iostream>
+#include <gtest/gtest.h>
+
+#include "include/stringify.h"
+#include "common/ceph_argparse.h"
+#include "global/global_init.h"
+#include "global/global_context.h"
+
+#include "crush/CrushWrapper.h"
+
+#include <set>
+
+CrushWrapper *build_indep_map(CephContext *cct, int num_rack, int num_host,
+ int num_osd)
+{
+ CrushWrapper *c = new CrushWrapper;
+ c->create();
+
+ c->set_type_name(5, "root");
+ c->set_type_name(4, "row");
+ c->set_type_name(3, "rack");
+ c->set_type_name(2, "chasis");
+ c->set_type_name(1, "host");
+ c->set_type_name(0, "osd");
+
+ int rootno;
+ c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
+ 5, 0, NULL, NULL, &rootno);
+ c->set_item_name(rootno, "default");
+
+ map<string,string> loc;
+ loc["root"] = "default";
+
+ int osd = 0;
+ for (int r=0; r<num_rack; ++r) {
+ loc["rack"] = string("rack-") + stringify(r);
+ for (int h=0; h<num_host; ++h) {
+ loc["host"] = string("host-") + stringify(r) + string("-") + stringify(h);
+ for (int o=0; o<num_osd; ++o, ++osd) {
+ c->insert_item(cct, osd, 1.0, string("osd.") + stringify(osd), loc);
+ }
+ }
+ }
+ int ret;
+ int ruleno = 0;
+ int ruleset = 0;
+ ruleno = ruleset;
+ ret = c->add_rule(4, ruleset, 123, 1, 20, ruleno);
+ assert(ret == ruleno);
+ ret = c->set_rule_step(ruleno, 0, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 10, 0);
+ assert(ret == 0);
+ ret = c->set_rule_step(ruleno, 1, CRUSH_RULE_TAKE, rootno, 0);
+ assert(ret == 0);
+ ret = c->set_rule_step(ruleno, 2, CRUSH_RULE_CHOOSELEAF_INDEP, CRUSH_CHOOSE_N, 1);
+ assert(ret == 0);
+ ret = c->set_rule_step(ruleno, 3, CRUSH_RULE_EMIT, 0, 0);
+ assert(ret == 0);
+ c->set_rule_name(ruleno, "data");
+
+ if (false) {
+ Formatter *f = Formatter::create("json-pretty");
+ f->open_object_section("crush_map");
+ c->dump(f);
+ f->close_section();
+ f->flush(cout);
+ delete f;
+ }
+
+ return c;
+}
+
+int get_num_dups(const vector<int>& v)
+{
+ std::set<int> s;
+ int dups = 0;
+ for (unsigned i=0; i<v.size(); ++i) {
+ if (s.count(v[i]))
+ ++dups;
+ else if (v[i] != CRUSH_ITEM_NONE)
+ s.insert(v[i]);
+ }
+ return dups;
+}
+
+TEST(CRUSH, indep_toosmall) {
+ CrushWrapper *c = build_indep_map(g_ceph_context, 1, 3, 1);
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+ c->dump_tree(weight, &cout, NULL);
+
+ for (int x = 0; x < 100; ++x) {
+ vector<int> out;
+ c->do_rule(0, x, out, 5, weight);
+ cout << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned i=0; i<out.size(); ++i) {
+ if (out[i] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(2, num_none);
+ ASSERT_EQ(0, get_num_dups(out));
+ }
+ delete c;
+}
+
+TEST(CRUSH, indep_basic) {
+ CrushWrapper *c = build_indep_map(g_ceph_context, 3, 3, 3);
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+ c->dump_tree(weight, &cout, NULL);
+
+ for (int x = 0; x < 100; ++x) {
+ vector<int> out;
+ c->do_rule(0, x, out, 5, weight);
+ cout << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned i=0; i<out.size(); ++i) {
+ if (out[i] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(0, num_none);
+ ASSERT_EQ(0, get_num_dups(out));
+ }
+ delete c;
+}
+
+TEST(CRUSH, indep_out_alt) {
+ CrushWrapper *c = build_indep_map(g_ceph_context, 3, 3, 3);
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+
+ // mark a bunch of osds out
+ int num = 3*3*3;
+ for (int i=0; i<num / 2; ++i)
+ weight[i*2] = 0;
+ c->dump_tree(weight, &cout, NULL);
+
+ // need more retries to get 9/9 hosts for x in 0..99
+ c->set_choose_total_tries(100);
+ for (int x = 0; x < 100; ++x) {
+ vector<int> out;
+ c->do_rule(0, x, out, 9, weight);
+ cout << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned i=0; i<out.size(); ++i) {
+ if (out[i] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(0, num_none);
+ ASSERT_EQ(0, get_num_dups(out));
+ }
+ delete c;
+}
+
+TEST(CRUSH, indep_out_contig) {
+ CrushWrapper *c = build_indep_map(g_ceph_context, 3, 3, 3);
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+
+ // mark a bunch of osds out
+ int num = 3*3*3;
+ for (int i=0; i<num / 3; ++i)
+ weight[i] = 0;
+ c->dump_tree(weight, &cout, NULL);
+
+ c->set_choose_total_tries(100);
+ for (int x = 0; x < 100; ++x) {
+ vector<int> out;
+ c->do_rule(0, x, out, 7, weight);
+ cout << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned i=0; i<out.size(); ++i) {
+ if (out[i] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(1, num_none);
+ ASSERT_EQ(0, get_num_dups(out));
+ }
+ delete c;
+}
+
+
+TEST(CRUSH, indep_out_progressive) {
+ CrushWrapper *c = build_indep_map(g_ceph_context, 3, 3, 3);
+ c->set_choose_total_tries(100);
+ vector<__u32> tweight(c->get_max_devices(), 0x10000);
+ c->dump_tree(tweight, &cout, NULL);
+
+ int tchanged = 0;
+ for (int x = 1; x < 5; ++x) {
+ vector<__u32> weight(c->get_max_devices(), 0x10000);
+
+ std::map<int,unsigned> pos;
+ vector<int> prev;
+ for (unsigned i=0; i<weight.size(); ++i) {
+ vector<int> out;
+ c->do_rule(0, x, out, 7, weight);
+ cout << "(" << i << "/" << weight.size() << " out) "
+ << x << " -> " << out << std::endl;
+ int num_none = 0;
+ for (unsigned k=0; k<out.size(); ++k) {
+ if (out[k] == CRUSH_ITEM_NONE)
+ num_none++;
+ }
+ ASSERT_EQ(0, get_num_dups(out));
+
+ // make sure nothing moved
+ int moved = 0;
+ int changed = 0;
+ for (unsigned j=0; j<out.size(); ++j) {
+ if (i && out[j] != prev[j]) {
+ ++changed;
+ ++tchanged;
+ }
+ if (out[j] == CRUSH_ITEM_NONE) {
+ continue;
+ }
+ if (i && pos.count(out[j])) {
+ // result shouldn't have moved position
+ if (j != pos[out[j]]) {
+ cout << " " << out[j] << " moved from " << pos[out[j]] << " to " << j << std::endl;
+ ++moved;
+ }
+ //ASSERT_EQ(j, pos[out[j]]);
+ }
+ }
+ if (moved || changed)
+ cout << " " << moved << " moved, " << changed << " changed" << std::endl;
+ ASSERT_LE(moved, 1);
+ ASSERT_LE(changed, 3);
+
+ // mark another osd out
+ weight[i] = 0;
+ prev = out;
+ pos.clear();
+ for (unsigned j=0; j<out.size(); ++j) {
+ if (out[j] != CRUSH_ITEM_NONE)
+ pos[out[j]] = j;
+ }
+ }
+ }
+ cout << tchanged << " total changed" << std::endl;
+
+ delete c;
+}
+
+
+
+
+int main(int argc, char **argv) {
+ vector<const char*> args;
+ argv_to_vec(argc, (const char **)argv, args);
+
+ global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
+ common_init_finish(g_ceph_context);
+
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2013 Inktank <info@inktank.com>
- *
- * LGPL2.1 (see COPYING-LGPL2.1) or later
- */
-
-#include <iostream>
-#include <gtest/gtest.h>
-
-#include "include/stringify.h"
-#include "common/ceph_argparse.h"
-#include "global/global_init.h"
-#include "global/global_context.h"
-
-#include "crush/CrushWrapper.h"
-
-#include <set>
-
-CrushWrapper *build_indep_map(CephContext *cct, int num_rack, int num_host,
- int num_osd)
-{
- CrushWrapper *c = new CrushWrapper;
- c->create();
-
- c->set_type_name(5, "root");
- c->set_type_name(4, "row");
- c->set_type_name(3, "rack");
- c->set_type_name(2, "chasis");
- c->set_type_name(1, "host");
- c->set_type_name(0, "osd");
-
- int rootno;
- c->add_bucket(0, CRUSH_BUCKET_STRAW, CRUSH_HASH_RJENKINS1,
- 5, 0, NULL, NULL, &rootno);
- c->set_item_name(rootno, "default");
-
- map<string,string> loc;
- loc["root"] = "default";
-
- int osd = 0;
- for (int r=0; r<num_rack; ++r) {
- loc["rack"] = string("rack-") + stringify(r);
- for (int h=0; h<num_host; ++h) {
- loc["host"] = string("host-") + stringify(r) + string("-") + stringify(h);
- for (int o=0; o<num_osd; ++o, ++osd) {
- c->insert_item(cct, osd, 1.0, string("osd.") + stringify(osd), loc);
- }
- }
- }
- int ret;
- int ruleno = 0;
- int ruleset = 0;
- ruleno = ruleset;
- ret = c->add_rule(4, ruleset, 123, 1, 20, ruleno);
- assert(ret == ruleno);
- ret = c->set_rule_step(ruleno, 0, CRUSH_RULE_SET_CHOOSELEAF_TRIES, 10, 0);
- assert(ret == 0);
- ret = c->set_rule_step(ruleno, 1, CRUSH_RULE_TAKE, rootno, 0);
- assert(ret == 0);
- ret = c->set_rule_step(ruleno, 2, CRUSH_RULE_CHOOSELEAF_INDEP, CRUSH_CHOOSE_N, 1);
- assert(ret == 0);
- ret = c->set_rule_step(ruleno, 3, CRUSH_RULE_EMIT, 0, 0);
- assert(ret == 0);
- c->set_rule_name(ruleno, "data");
-
- if (false) {
- Formatter *f = Formatter::create("json-pretty");
- f->open_object_section("crush_map");
- c->dump(f);
- f->close_section();
- f->flush(cout);
- delete f;
- }
-
- return c;
-}
-
-int get_num_dups(const vector<int>& v)
-{
- std::set<int> s;
- int dups = 0;
- for (unsigned i=0; i<v.size(); ++i) {
- if (s.count(v[i]))
- ++dups;
- else if (v[i] != CRUSH_ITEM_NONE)
- s.insert(v[i]);
- }
- return dups;
-}
-
-TEST(CRUSH, indep_toosmall) {
- CrushWrapper *c = build_indep_map(g_ceph_context, 1, 3, 1);
- vector<__u32> weight(c->get_max_devices(), 0x10000);
- c->dump_tree(weight, &cout, NULL);
-
- for (int x = 0; x < 100; ++x) {
- vector<int> out;
- c->do_rule(0, x, out, 5, weight);
- cout << x << " -> " << out << std::endl;
- int num_none = 0;
- for (unsigned i=0; i<out.size(); ++i) {
- if (out[i] == CRUSH_ITEM_NONE)
- num_none++;
- }
- ASSERT_EQ(2, num_none);
- ASSERT_EQ(0, get_num_dups(out));
- }
- delete c;
-}
-
-TEST(CRUSH, indep_basic) {
- CrushWrapper *c = build_indep_map(g_ceph_context, 3, 3, 3);
- vector<__u32> weight(c->get_max_devices(), 0x10000);
- c->dump_tree(weight, &cout, NULL);
-
- for (int x = 0; x < 100; ++x) {
- vector<int> out;
- c->do_rule(0, x, out, 5, weight);
- cout << x << " -> " << out << std::endl;
- int num_none = 0;
- for (unsigned i=0; i<out.size(); ++i) {
- if (out[i] == CRUSH_ITEM_NONE)
- num_none++;
- }
- ASSERT_EQ(0, num_none);
- ASSERT_EQ(0, get_num_dups(out));
- }
- delete c;
-}
-
-TEST(CRUSH, indep_out_alt) {
- CrushWrapper *c = build_indep_map(g_ceph_context, 3, 3, 3);
- vector<__u32> weight(c->get_max_devices(), 0x10000);
-
- // mark a bunch of osds out
- int num = 3*3*3;
- for (int i=0; i<num / 2; ++i)
- weight[i*2] = 0;
- c->dump_tree(weight, &cout, NULL);
-
- // need more retries to get 9/9 hosts for x in 0..99
- c->set_choose_total_tries(100);
- for (int x = 0; x < 100; ++x) {
- vector<int> out;
- c->do_rule(0, x, out, 9, weight);
- cout << x << " -> " << out << std::endl;
- int num_none = 0;
- for (unsigned i=0; i<out.size(); ++i) {
- if (out[i] == CRUSH_ITEM_NONE)
- num_none++;
- }
- ASSERT_EQ(0, num_none);
- ASSERT_EQ(0, get_num_dups(out));
- }
- delete c;
-}
-
-TEST(CRUSH, indep_out_contig) {
- CrushWrapper *c = build_indep_map(g_ceph_context, 3, 3, 3);
- vector<__u32> weight(c->get_max_devices(), 0x10000);
-
- // mark a bunch of osds out
- int num = 3*3*3;
- for (int i=0; i<num / 3; ++i)
- weight[i] = 0;
- c->dump_tree(weight, &cout, NULL);
-
- c->set_choose_total_tries(100);
- for (int x = 0; x < 100; ++x) {
- vector<int> out;
- c->do_rule(0, x, out, 7, weight);
- cout << x << " -> " << out << std::endl;
- int num_none = 0;
- for (unsigned i=0; i<out.size(); ++i) {
- if (out[i] == CRUSH_ITEM_NONE)
- num_none++;
- }
- ASSERT_EQ(1, num_none);
- ASSERT_EQ(0, get_num_dups(out));
- }
- delete c;
-}
-
-
-TEST(CRUSH, indep_out_progressive) {
- CrushWrapper *c = build_indep_map(g_ceph_context, 3, 3, 3);
- c->set_choose_total_tries(100);
- vector<__u32> tweight(c->get_max_devices(), 0x10000);
- c->dump_tree(tweight, &cout, NULL);
-
- int tchanged = 0;
- for (int x = 1; x < 5; ++x) {
- vector<__u32> weight(c->get_max_devices(), 0x10000);
-
- std::map<int,unsigned> pos;
- vector<int> prev;
- for (unsigned i=0; i<weight.size(); ++i) {
- vector<int> out;
- c->do_rule(0, x, out, 7, weight);
- cout << "(" << i << "/" << weight.size() << " out) "
- << x << " -> " << out << std::endl;
- int num_none = 0;
- for (unsigned k=0; k<out.size(); ++k) {
- if (out[k] == CRUSH_ITEM_NONE)
- num_none++;
- }
- ASSERT_EQ(0, get_num_dups(out));
-
- // make sure nothing moved
- int moved = 0;
- int changed = 0;
- for (unsigned j=0; j<out.size(); ++j) {
- if (i && out[j] != prev[j]) {
- ++changed;
- ++tchanged;
- }
- if (out[j] == CRUSH_ITEM_NONE) {
- continue;
- }
- if (i && pos.count(out[j])) {
- // result shouldn't have moved position
- if (j != pos[out[j]]) {
- cout << " " << out[j] << " moved from " << pos[out[j]] << " to " << j << std::endl;
- ++moved;
- }
- //ASSERT_EQ(j, pos[out[j]]);
- }
- }
- if (moved || changed)
- cout << " " << moved << " moved, " << changed << " changed" << std::endl;
- ASSERT_LE(moved, 1);
- ASSERT_LE(changed, 3);
-
- // mark another osd out
- weight[i] = 0;
- prev = out;
- pos.clear();
- for (unsigned j=0; j<out.size(); ++j) {
- if (out[j] != CRUSH_ITEM_NONE)
- pos[out[j]] = j;
- }
- }
- }
- cout << tchanged << " total changed" << std::endl;
-
- delete c;
-}
-
-
-
-
-int main(int argc, char **argv) {
- vector<const char*> args;
- argv_to_vec(argc, (const char **)argv, args);
-
- global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0);
- common_init_finish(g_ceph_context);
-
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}