diff --git a/src/crush/CrushCompiler.cc b/src/crush/CrushCompiler.cc index 22e59134899..1ce72fbbcbd 100644 --- a/src/crush/CrushCompiler.cc +++ b/src/crush/CrushCompiler.cc @@ -815,6 +815,54 @@ void CrushCompiler::dump(iter_t const& i, int ind) dump(i->children.begin() + j, ind+1); } +/** +* This function fix the problem like below +* rack using_foo { item foo } +* host foo { ... } +* +* if an item being used by a bucket is defined after that bucket. +* CRUSH compiler will create a map by which we can +* not identify that item when selecting in that bucket. +**/ +int CrushCompiler::adjust_bucket_item_place(iter_t const &i) +{ + map > bucket_items; + map bucket_itrer; + vector buckets; + for (iter_t p = i->children.begin(); p != i->children.end(); ++p) { + if ((int)p->value.id().to_long() == crush_grammar::_bucket) { + string name = string_node(p->children[1]); + buckets.push_back(name); + bucket_itrer[name] = p; + //skip non-bucket-item children in the bucket's parse tree + for (unsigned q=3; q < p->children.size()-1; ++q) { + iter_t sub = p->children.begin() + q; + if ((int)sub->value.id().to_long() + == crush_grammar::_bucket_item) { + string iname = string_node(sub->children[1]); + bucket_items[name].insert(iname); + } + } + } + } + + //adjust the bucket + for (unsigned i=0; i < buckets.size(); ++i) { + for (unsigned j=i+1; j < buckets.size(); ++j) { + if (bucket_items[buckets[i]].count(buckets[j])) { + if (bucket_items[buckets[j]].count(buckets[i])) { + err << "bucket '" << buckets[i] << "' and bucket '" + << buckets[j] << "' are included each other" << std::endl; + return -1; + } else { + std::iter_swap(bucket_itrer[buckets[i]], bucket_itrer[buckets[j]]); + } + } + } + } + + return 0; +} int CrushCompiler::compile(istream& in, const char *infn) { @@ -880,7 +928,11 @@ int CrushCompiler::compile(istream& in, const char *infn) << " error: parse error at '" << line_val[line].substr(pos) << "'" << std::endl; return -1; } - + + int r = adjust_bucket_item_place(info.trees.begin()); + if (r < 0) { + return r; + } //out << "parsing succeeded\n"; //dump(info.trees.begin()); return parse_crush(info.trees.begin()); diff --git a/src/crush/CrushCompiler.h b/src/crush/CrushCompiler.h index d488735bb9e..4e1542f5b09 100644 --- a/src/crush/CrushCompiler.h +++ b/src/crush/CrushCompiler.h @@ -52,8 +52,8 @@ class CrushCompiler { void find_used_bucket_ids(iter_t const& i); int parse_crush(iter_t const& i); void dump(iter_t const& i, int ind=1); - string consolidate_whitespace(string in); + int adjust_bucket_item_place(iter_t const &i); public: CrushCompiler(CrushWrapper& c, ostream& eo, int verbosity=0) diff --git a/src/test/Makefile.am b/src/test/Makefile.am index fb30394db39..854632c42b9 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -71,6 +71,7 @@ check_SCRIPTS += \ test/cephtool-test-mds.sh \ test/cephtool-test-rados.sh \ test/test_pool_create.sh \ + test/test_crush_bucket.sh \ unittest_bufferlist.sh \ test/encoding/check-generated.sh \ test/mon/osd-pool-create.sh \ diff --git a/src/test/test_crush_bucket.sh b/src/test/test_crush_bucket.sh new file mode 100755 index 00000000000..8ae89721b30 --- /dev/null +++ b/src/test/test_crush_bucket.sh @@ -0,0 +1,51 @@ +#!/bin/bash + + +#Generic test_crush_bucket test +# + +# Includes +source ../qa/workunits/ceph-helpers.sh +function run() { + local dir=$1 + shift + + export CEPH_MON="127.0.0.1:17119" + export CEPH_ARGS + CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none " + CEPH_ARGS+="--mon-host=$CEPH_MON " + + local funcs=${@:-$(set | sed -n -e 's/^\(TEST_[0-9a-z_]*\) .*/\1/p')} + for func in $funcs ; do + $func $dir || return 1 + done +} + +function TEST_crush_bucket() { + local dir=$1 + setup $dir || return 1 + run_mon $dir a || return 1 + run_osd $dir 0 || return 1 + run_osd $dir 1 || return 1 + run_osd $dir 2 || return 1 + + + ceph osd getcrushmap -o "$dir/map1" || return 1 + crushtool -d "$dir/map1" -o "$dir/map1.txt"|| return 1 + local var=`ceph osd crush dump|grep -w id|grep '-'|grep -Eo '[0-9]+'|sort|uniq|sed -n '$p'` + local id=`expr $var + 1` + local item=`sed -n '/^root/,/}/p' $dir/map1.txt|grep 'item'|head -1` + local weight=`sed -n '/^root/,/}/p' $dir/map1.txt|grep 'item'|head -1|awk '{print $4}'` + local bucket="host test {\n id -$id\n # weight $weight\n alg straw \n hash 0 # rjenkins1 \n $item\n}\n" + sed -i "/# buckets/a\ $bucket" "$dir/map1.txt" + crushtool -c "$dir/map1.txt" -o "$dir/map1.bin" 2>"$dir/rev" + local result=$(cat "$dir/rev") + if [ "$result" != "" ]; + then + return 1 + fi + +} + +main testcrushbucket +