crush/CrushWrapper: add get_common_ancestor_distance()

Calculate closest common ancestor (type) in the hierarchy.

Signed-off-by: Sage Weil <sage@inktank.com>
This commit is contained in:
Sage Weil 2013-10-30 08:59:00 -07:00
parent 0903f3fa46
commit dcc5e3559f
4 changed files with 152 additions and 0 deletions

View File

@ -193,6 +193,33 @@ int CrushWrapper::remove_item_under(CephContext *cct, int item, int ancestor, bo
return ret;
}
int CrushWrapper::get_common_ancestor_distance(CephContext *cct, int id,
const std::multimap<string,string>& loc)
{
ldout(cct, 5) << __func__ << " " << id << " " << loc << dendl;
if (!item_exists(id))
return -ENOENT;
map<string,string> id_loc = get_full_location(id);
ldout(cct, 20) << " id is at " << id_loc << dendl;
for (map<int,string>::const_iterator p = type_map.begin();
p != type_map.end();
++p) {
map<string,string>::iterator ip = id_loc.find(p->second);
if (ip == id_loc.end())
continue;
for (std::multimap<string,string>::const_iterator q = loc.find(p->second);
q != loc.end();
++q) {
if (q->first != p->second)
break;
if (q->second == ip->second)
return p->first;
}
}
return -ERANGE;
}
int CrushWrapper::parse_loc_map(const std::vector<string>& args,
std::map<string,string> *ploc)
{

View File

@ -472,6 +472,19 @@ private:
public:
int remove_item_under(CephContext *cct, int id, int ancestor, bool unlink_only);
/**
* calculate the locality/distance from a given id to a crush location map
*
* Specifically, we look for the lowest-valued type for which the
* location of id matches that described in loc.
*
* @param cct cct
* @param id the existing id in the map
* @param loc a set of key=value pairs describing a location in the hierarchy
*/
int get_common_ancestor_distance(CephContext *cct, int id,
const std::multimap<string,string>& loc);
/**
* parse a set of key/value pairs out of a string vector
*

View File

@ -274,6 +274,11 @@ unittest_str_map_CXXFLAGS = $(UNITTEST_CXXFLAGS)
unittest_str_map_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL)
check_PROGRAMS += unittest_str_map
unittest_crushwrapper_SOURCES = test/test_crushwrapper.cc
unittest_crushwrapper_CXXFLAGS = $(UNITTEST_CXXFLAGS)
unittest_crushwrapper_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) $(LIBCRUSH)
check_PROGRAMS += unittest_crushwrapper
unittest_sharedptr_registry_SOURCES = test/common/test_sharedptr_registry.cc
unittest_sharedptr_registry_CXXFLAGS = $(UNITTEST_CXXFLAGS)
unittest_sharedptr_registry_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL)

View File

@ -0,0 +1,107 @@
// -*- 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>
*
* 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 "crush/CrushWrapper.h"
#include "gtest/gtest.h"
#include "global/global_init.h"
#include "global/global_context.h"
#include "common/ceph_argparse.h"
#include "common/Formatter.h"
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);
env_to_vec(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();
}