From: Sage Weil Date: Fri, 5 Oct 2018 17:47:37 +0000 (-0500) Subject: crushtool: implement --reclassify X-Git-Tag: v14.1.0~788^2~11 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=43ce1877a714b04f2c1c6ad09b1d60944d25dfeb;p=ceph.git crushtool: implement --reclassify Add two modes of reclassification of existing hierarchies: --classify-root will rewrite a hierarchy from an existing root so that all of its devices are a different class. Rules that reference that root will be implicitly adjusted to 'take class '. Ids will be preserved. --classify-bucket will match a pattern in the bucket name, where % at the beginning or end of the string is used as a wildcard (e.g., "%-ssd" will match an "-ssd" suffix, "foo-%" will match a "foo-" prefix). Each such bucket is mapped to a "base" bucket (with the suffix or prefix), items are labeled with the appropriate class and the moved to that base bucket, and rules adjusted. The is used as the parent if the base bucket doesn't exist and has to be created. Similarly, --classify-bucket does the same but a single existing bucket is mapped to an existing base bucket. For example, there is often an 'ssd' bucket that is the counterpart for the 'default' root; '--classify-bucket ssd ssd default' will map it over. Signed-off-by: Sage Weil --- diff --git a/src/crush/CrushWrapper.cc b/src/crush/CrushWrapper.cc index d4585ce0ea00..2b735d93f8cf 100644 --- a/src/crush/CrushWrapper.cc +++ b/src/crush/CrushWrapper.cc @@ -1645,6 +1645,324 @@ int32_t CrushWrapper::_alloc_class_id() const { ceph_abort_msg("no available class id"); } +int CrushWrapper::reclassify( + CephContext *cct, + ostream& out, + const map& classify_root, + const map>& classify_bucket + ) +{ + map reclassified_bucket; // orig_id -> class + + // classify_root + for (auto& i : classify_root) { + string root = i.first; + if (!name_exists(root)) { + out << "root " << root << " does not exist" << std::endl; + return -EINVAL; + } + int root_id = get_item_id(root); + string new_class = i.second; + int new_class_id = -1; + out << "classify_root " << root << " (" << root_id + << ") as " << new_class << std::endl; + if (class_exists(new_class)) { + new_class_id = get_class_id(new_class); + out << " new class " << new_class << " exists as " << new_class_id + << std::endl; + } else { + for (new_class_id = 1; class_name.count(new_class_id); ++new_class_id) ; + class_name[new_class_id] = new_class; + class_rname[new_class] = new_class_id; + out << " created new class " << new_class << " as " << new_class_id + << std::endl; + } + + // validate rules + for (unsigned j = 0; j < crush->max_rules; j++) { + if (crush->rules[j]) { + auto rule = crush->rules[j]; + for (unsigned k = 0; k < rule->len; ++k) { + if (rule->steps[k].op == CRUSH_RULE_TAKE) { + int step_item = get_rule_arg1(j, k); + int original_item; + int c; + int res = split_id_class(step_item, &original_item, &c); + if (res < 0) + return res; + if (c >= 0) { + if (original_item == root_id) { + out << " rule " << j << " includes take on root " + << root << " class " << c << std::endl; + return -EINVAL; + } + } + } + } + } + } + + // rebuild new buckets for root + //cout << "before class_bucket: " << class_bucket << std::endl; + map renumber; + list q; + q.push_back(root_id); + while (!q.empty()) { + int id = q.front(); + q.pop_front(); + crush_bucket *bucket = get_bucket(id); + if (IS_ERR(bucket)) { + out << "cannot find bucket " << id + << ": " << cpp_strerror(PTR_ERR(bucket)) << std::endl; + return PTR_ERR(bucket); + } + + // move bucket + int new_id = get_new_bucket_id(); + out << " renumbering bucket " << id << " -> " << new_id << std::endl; + renumber[id] = new_id; + crush->buckets[-1-new_id] = bucket; + bucket->id = new_id; + crush->buckets[-1-id] = crush_make_bucket(crush, + bucket->alg, + bucket->hash, + bucket->type, + 0, NULL, NULL); + crush->buckets[-1-id]->id = id; + for (auto& i : choose_args) { + i.second.args[-1-new_id] = i.second.args[-1-id]; + memset(&i.second.args[-1-id], 0, sizeof(i.second.args[0])); + } + class_bucket.erase(id); + class_bucket[new_id][new_class_id] = id; + name_map[new_id] = string(get_item_name(id)); + name_map[id] = string(get_item_name(id)) + "~" + new_class; + + for (unsigned j = 0; j < bucket->size; ++j) { + if (bucket->items[j] < 0) { + q.push_front(bucket->items[j]); + } else { + // reclassify device + class_map[bucket->items[j]] = new_class_id; + } + } + } + //cout << "mid class_bucket: " << class_bucket << std::endl; + + for (int i = 0; i < crush->max_buckets; ++i) { + crush_bucket *b = crush->buckets[i]; + if (!b) { + continue; + } + for (unsigned j = 0; j < b->size; ++j) { + if (renumber.count(b->items[j])) { + b->items[j] = renumber[b->items[j]]; + } + } + } + + int r = rebuild_roots_with_classes(); + if (r < 0) { + out << "failed to rebuild_roots_with_classes: " << cpp_strerror(r) + << std::endl; + return r; + } + //cout << "final class_bucket: " << class_bucket << std::endl; + } + + // classify_bucket + map send_to; // source bucket -> dest bucket + map> new_class_bucket; + map new_bucket_names; + map> new_buckets; + for (auto& i : classify_bucket) { + const string& match = i.first; // prefix% or %suffix + const string& new_class = i.second.first; + const string& default_parent = i.second.second; + if (!name_exists(default_parent)) { + out << "default parent " << default_parent << " does not exist" + << std::endl; + return -EINVAL; + } + int default_parent_id = get_item_id(default_parent); + crush_bucket *default_parent_bucket = get_bucket(default_parent_id); + assert(default_parent_bucket); + string default_parent_type_name = get_type_name(default_parent_bucket->type); + + out << "classify_bucket " << match << " as " << new_class + << " default bucket " << default_parent + << " (" << default_parent_type_name << ")" << std::endl; + + int new_class_id = -1; + if (class_exists(new_class)) { + new_class_id = get_class_id(new_class); + out << " new class " << new_class << " exists as " << new_class_id + << std::endl; + } else { + for (new_class_id = 1; class_name.count(new_class_id); ++new_class_id) { + } + class_name[new_class_id] = new_class; + class_rname[new_class] = new_class_id; + out << " created new class " << new_class << " as " << new_class_id + << std::endl; + } + + for (int j = 0; j < crush->max_buckets; ++j) { + crush_bucket *b = crush->buckets[j]; + if (!b || is_shadow_item(b->id)) { + continue; + } + string name = get_item_name(b->id); + if (name.length() < match.length()) { + continue; + } + string basename; + if (match[0] == '%') { + if (match.substr(1) != name.substr(name.size() - match.size() + 1)) { + continue; + } + basename = name.substr(0, name.size() - match.size() + 1); + } else if (match[match.size() - 1] == '%') { + if (match.substr(0, match.size() - 1) != + name.substr(0, match.size() - 1)) { + continue; + } + basename = name.substr(match.size() - 1); + } else if (match == name) { + basename = default_parent; + } else { + continue; + } + cout << "match " << match << " to " << name << " basename " << basename + << std::endl; + // look up or create basename bucket + int base_id; + if (name_exists(basename)) { + base_id = get_item_id(basename); + cout << " have base " << base_id << std::endl; + } else { + base_id = get_new_bucket_id(); + crush->buckets[-1-base_id] = crush_make_bucket(crush, + b->alg, + b->hash, + b->type, + 0, NULL, NULL); + crush->buckets[-1-base_id]->id = base_id; + name_map[base_id] = basename; + cout << " created base " << base_id << std::endl; + + new_buckets[base_id][default_parent_type_name] = default_parent; + } + send_to[b->id] = base_id; + new_class_bucket[base_id][new_class_id] = b->id; + new_bucket_names[b->id] = basename + "~" + get_class_name(new_class_id); + + // make sure devices are classified + for (unsigned i = 0; i < b->size; ++i) { + int item = b->items[i]; + if (item >= 0) { + class_map[item] = new_class_id; + } + } + } + } + + // no name_exists() works below, + have_rmaps = false; + + // copy items around + //cout << "send_to " << send_to << std::endl; + set roots; + find_roots(&roots); + for (auto& i : send_to) { + crush_bucket *from = get_bucket(i.first); + crush_bucket *to = get_bucket(i.second); + cout << "moving items from " << from->id << " (" << get_item_name(from->id) + << ") to " << to->id << " (" << get_item_name(to->id) << ")" + << std::endl; + for (unsigned j = 0; j < from->size; ++j) { + int item = from->items[j]; + int r; + map to_loc; + to_loc[get_type_name(to->type)] = get_item_name(to->id); + if (item >= 0) { + if (subtree_contains(to->id, item)) { + continue; + } + map from_loc; + from_loc[get_type_name(from->type)] = get_item_name(from->id); + auto w = get_item_weightf_in_loc(item, from_loc); + r = insert_item(cct, item, + w, + get_item_name(item), + to_loc); + } else { + if (!send_to.count(item)) { + lderr(cct) << "item " << item << " in bucket " << from->id + << " is not also a reclassified bucket" << dendl; + return -EINVAL; + } + int newitem = send_to[item]; + if (subtree_contains(to->id, newitem)) { + continue; + } + r = link_bucket(cct, newitem, to_loc); + } + if (r != 0) { + cout << __func__ << " err from insert_item: " << cpp_strerror(r) + << std::endl; + return r; + } + } + } + + // set class mappings + //cout << "pre class_bucket: " << class_bucket << std::endl; + for (auto& i : new_class_bucket) { + for (auto& j : i.second) { + class_bucket[i.first][j.first] = j.second; + } + + } + //cout << "post class_bucket: " << class_bucket << std::endl; + for (auto& i : new_bucket_names) { + name_map[i.first] = i.second; + } + + int r = rebuild_roots_with_classes(); + if (r < 0) { + out << "failed to rebuild_roots_with_classes: " << cpp_strerror(r) + << std::endl; + return r; + } + //cout << "final class_bucket: " << class_bucket << std::endl; + + return 0; +} + +int CrushWrapper::get_new_bucket_id() +{ + int id = -1; + while (crush->buckets[-1-id] && + -1-id < crush->max_buckets) { + id--; + } + if (-1-id == crush->max_buckets) { + ++crush->max_buckets; + crush->buckets = (struct crush_bucket**)realloc( + crush->buckets, + sizeof(crush->buckets[0]) * crush->max_buckets); + for (auto& i : choose_args) { + assert(i.second.size == crush->max_buckets - 1); + ++i.second.size; + i.second.args = (struct crush_choose_arg*)realloc( + i.second.args, + sizeof(i.second.args[0]) * i.second.size); + } + } + return id; +} + void CrushWrapper::reweight(CephContext *cct) { set roots; diff --git a/src/crush/CrushWrapper.h b/src/crush/CrushWrapper.h index cbe487ce5376..08c5759e85b4 100644 --- a/src/crush/CrushWrapper.h +++ b/src/crush/CrushWrapper.h @@ -1201,6 +1201,8 @@ private: **/ int detach_bucket(CephContext *cct, int item); + int get_new_bucket_id(); + public: int get_max_buckets() const { if (!crush) return -EINVAL; @@ -1301,6 +1303,13 @@ public: /* remove unused roots generated for class devices */ int trim_roots_with_class(); + int reclassify( + CephContext *cct, + ostream& out, + const map& classify_root, + const map>& classify_bucket + ); + void start_choose_profile() { free(crush->choose_tries); /* diff --git a/src/test/cli/crushtool/crush-classes/Eric.Smith@ccur.com b/src/test/cli/crushtool/crush-classes/Eric.Smith@ccur.com new file mode 100644 index 000000000000..e43163673e62 Binary files /dev/null and b/src/test/cli/crushtool/crush-classes/Eric.Smith@ccur.com differ diff --git a/src/test/cli/crushtool/crush-classes/dan@vanderster.com-beesly.crush b/src/test/cli/crushtool/crush-classes/dan@vanderster.com-beesly.crush new file mode 100644 index 000000000000..0048b148db11 Binary files /dev/null and b/src/test/cli/crushtool/crush-classes/dan@vanderster.com-beesly.crush differ diff --git a/src/test/cli/crushtool/crush-classes/dan@vanderster.com-flax.crush b/src/test/cli/crushtool/crush-classes/dan@vanderster.com-flax.crush new file mode 100644 index 000000000000..4f579dd7aa55 Binary files /dev/null and b/src/test/cli/crushtool/crush-classes/dan@vanderster.com-flax.crush differ diff --git a/src/test/cli/crushtool/crush-classes/dan@vanderster.com-gabe.crush b/src/test/cli/crushtool/crush-classes/dan@vanderster.com-gabe.crush new file mode 100644 index 000000000000..90e8ed7b940d Binary files /dev/null and b/src/test/cli/crushtool/crush-classes/dan@vanderster.com-gabe.crush differ diff --git a/src/test/cli/crushtool/crush-classes/iversons@rushville.k12.in.us b/src/test/cli/crushtool/crush-classes/iversons@rushville.k12.in.us new file mode 100644 index 000000000000..a1720c77305c Binary files /dev/null and b/src/test/cli/crushtool/crush-classes/iversons@rushville.k12.in.us differ diff --git a/src/test/cli/crushtool/crush-classes/jwillem@stads.net b/src/test/cli/crushtool/crush-classes/jwillem@stads.net new file mode 100644 index 000000000000..e709cb9a3f8a Binary files /dev/null and b/src/test/cli/crushtool/crush-classes/jwillem@stads.net differ diff --git a/src/test/cli/crushtool/crush-classes/michael.nieporte@uk-essen.de b/src/test/cli/crushtool/crush-classes/michael.nieporte@uk-essen.de new file mode 100644 index 000000000000..68c900f65528 Binary files /dev/null and b/src/test/cli/crushtool/crush-classes/michael.nieporte@uk-essen.de differ diff --git a/src/test/cli/crushtool/help.t b/src/test/cli/crushtool/help.t old mode 100755 new mode 100644 index 3db3c25d5233..de42cd5c7850 --- a/src/test/cli/crushtool/help.t +++ b/src/test/cli/crushtool/help.t @@ -110,6 +110,10 @@ export select data generated during testing routine to CSV files for off-line post-processing use --help-output for more information + --reclassify transform legacy CRUSH map buckets and rules + by adding classes + --reclassify-bucket + --reclassify-root Options for the output stage diff --git a/src/test/cli/crushtool/reclassify.t b/src/test/cli/crushtool/reclassify.t new file mode 100644 index 000000000000..fc5397ba8fe9 --- /dev/null +++ b/src/test/cli/crushtool/reclassify.t @@ -0,0 +1,385 @@ + $ crushtool -i $TESTDIR/crush-classes/Eric.Smith@ccur.com --reclassify --reclassify-bucket %-ssd ssd default --reclassify-bucket ssd ssd default --reclassify-root default hdd -o foo + classify_root default (-1) as hdd + created new class hdd as 1 + renumbering bucket -1 -> -13 + renumbering bucket -6 -> -14 + renumbering bucket -5 -> -15 + renumbering bucket -4 -> -16 + renumbering bucket -3 -> -17 + renumbering bucket -2 -> -18 + classify_bucket %-ssd as ssd default bucket default (root) + created new class ssd as 2 + match %-ssd to node-20-ssd basename node-20 + have base -18 + match %-ssd to node-21-ssd basename node-21 + created base -25 + match %-ssd to node-22-ssd basename node-22 + created base -26 + match %-ssd to node-23-ssd basename node-23 + created base -27 + match %-ssd to node-27-ssd basename node-27 + created base -28 + classify_bucket ssd as ssd default bucket default (root) + new class ssd exists as 2 + match ssd to ssd basename default + have base -13 + moving items from -12 (node-27-ssd) to -28 (node-27) + moving items from -11 (node-23-ssd) to -27 (node-23) + moving items from -10 (node-22-ssd) to -26 (node-22) + moving items from -9 (node-21-ssd) to -25 (node-21) + moving items from -8 (node-20-ssd) to -18 (node-20) + moving items from -7 (ssd) to -13 (default) + $ crushtool -i $TESTDIR/crush-classes/Eric.Smith@ccur.com --compare foo + rule 0 had 0/10240 mismatched mappings (0) + rule 1 had 0/10240 mismatched mappings (0) + maps appear equivalent + + $ crushtool -i $TESTDIR/crush-classes/Eric.Smith@ccur.com --reclassify --reclassify-bucket %-ssd ssd default --reclassify-bucket ssd ssd default --reclassify-root default hdd -o foo + classify_root default (-1) as hdd + created new class hdd as 1 + renumbering bucket -1 -> -13 + renumbering bucket -6 -> -14 + renumbering bucket -5 -> -15 + renumbering bucket -4 -> -16 + renumbering bucket -3 -> -17 + renumbering bucket -2 -> -18 + classify_bucket %-ssd as ssd default bucket default (root) + created new class ssd as 2 + match %-ssd to node-20-ssd basename node-20 + have base -18 + match %-ssd to node-21-ssd basename node-21 + created base -25 + match %-ssd to node-22-ssd basename node-22 + created base -26 + match %-ssd to node-23-ssd basename node-23 + created base -27 + match %-ssd to node-27-ssd basename node-27 + created base -28 + classify_bucket ssd as ssd default bucket default (root) + new class ssd exists as 2 + match ssd to ssd basename default + have base -13 + moving items from -12 (node-27-ssd) to -28 (node-27) + moving items from -11 (node-23-ssd) to -27 (node-23) + moving items from -10 (node-22-ssd) to -26 (node-22) + moving items from -9 (node-21-ssd) to -25 (node-21) + moving items from -8 (node-20-ssd) to -18 (node-20) + moving items from -7 (ssd) to -13 (default) + $ crushtool -i $TESTDIR/crush-classes/Eric.Smith@ccur.com --compare foo + rule 0 had 0/10240 mismatched mappings (0) + rule 1 had 0/10240 mismatched mappings (0) + maps appear equivalent + + $ crushtool -i $TESTDIR/crush-classes/iversons@rushville.k12.in.us --reclassify --reclassify-bucket ceph-osd-ssd-% ssd default --reclassify-bucket ssd-root ssd default --reclassify-root default hdd -o foo + classify_root default (-1) as hdd + new class hdd exists as 1 + renumbering bucket -1 -> -55 + renumbering bucket -34 -> -56 + renumbering bucket -20 -> -57 + renumbering bucket -14 -> -58 + renumbering bucket -15 -> -59 + renumbering bucket -16 -> -60 + renumbering bucket -52 -> -61 + renumbering bucket -46 -> -62 + renumbering bucket -40 -> -63 + classify_bucket ceph-osd-ssd-% as ssd default bucket default (root) + new class ssd exists as 0 + match ceph-osd-ssd-% to ceph-osd-ssd-node4 basename node4 + have base -57 + match ceph-osd-ssd-% to ceph-osd-ssd-node3 basename node3 + have base -58 + match ceph-osd-ssd-% to ceph-osd-ssd-node1 basename node1 + have base -60 + match ceph-osd-ssd-% to ceph-osd-ssd-node2 basename node2 + have base -59 + match ceph-osd-ssd-% to ceph-osd-ssd-node5 basename node5 + have base -56 + match ceph-osd-ssd-% to ceph-osd-ssd-node6 basename node6 + have base -63 + match ceph-osd-ssd-% to ceph-osd-ssd-node7 basename node7 + have base -62 + match ceph-osd-ssd-% to ceph-osd-ssd-node8 basename node8 + have base -61 + classify_bucket ssd-root as ssd default bucket default (root) + new class ssd exists as 0 + match ssd-root to ssd-root basename default + have base -55 + moving items from -49 (ceph-osd-ssd-node8) to -61 (node8) + moving items from -43 (ceph-osd-ssd-node7) to -62 (node7) + moving items from -37 (ceph-osd-ssd-node6) to -63 (node6) + moving items from -31 (ceph-osd-ssd-node5) to -56 (node5) + moving items from -18 (ssd-root) to -55 (default) + moving items from -9 (ceph-osd-ssd-node2) to -59 (node2) + moving items from -7 (ceph-osd-ssd-node1) to -60 (node1) + moving items from -5 (ceph-osd-ssd-node3) to -58 (node3) + moving items from -3 (ceph-osd-ssd-node4) to -57 (node4) + +this one has weird node weights, so *lots* of mappings change... + + $ crushtool -i $TESTDIR/crush-classes/iversons@rushville.k12.in.us --compare foo + rule 0 had 6540/10240 mismatched mappings (0.638672) + rule 1 had 8417/10240 mismatched mappings (0.821973) + warning: maps are NOT equivalent + [1] + + $ crushtool -i $TESTDIR/crush-classes/jwillem@stads.net --reclassify --reclassify-bucket %-SSD ssd default --reclassify-bucket ssd ssd default --reclassify-root default hdd -o foo + classify_root default (-1) as hdd + new class hdd exists as 0 + renumbering bucket -1 -> -55 + renumbering bucket -9 -> -56 + renumbering bucket -8 -> -57 + renumbering bucket -7 -> -58 + renumbering bucket -6 -> -59 + renumbering bucket -5 -> -60 + renumbering bucket -4 -> -61 + renumbering bucket -3 -> -62 + renumbering bucket -2 -> -63 + classify_bucket %-SSD as ssd default bucket default (root) + created new class ssd as 2 + match %-SSD to Ceph-Stor1-SSD basename Ceph-Stor1 + have base -63 + match %-SSD to Ceph-Stor2-SSD basename Ceph-Stor2 + have base -62 + match %-SSD to Ceph-Stor3-SSD basename Ceph-Stor3 + have base -61 + match %-SSD to Ceph-Stor4-SSD basename Ceph-Stor4 + have base -60 + match %-SSD to Ceph-Stor5-SSD basename Ceph-Stor5 + have base -59 + match %-SSD to Ceph-Stor6-SSD basename Ceph-Stor6 + have base -58 + match %-SSD to Ceph-Stor7-SSD basename Ceph-Stor7 + have base -57 + match %-SSD to Ceph-Stor8-SSD basename Ceph-Stor8 + have base -56 + classify_bucket ssd as ssd default bucket default (root) + new class ssd exists as 2 + match ssd to ssd basename default + have base -55 + moving items from -18 (ssd) to -55 (default) + moving items from -17 (Ceph-Stor8-SSD) to -56 (Ceph-Stor8) + moving items from -16 (Ceph-Stor7-SSD) to -57 (Ceph-Stor7) + moving items from -15 (Ceph-Stor6-SSD) to -58 (Ceph-Stor6) + moving items from -14 (Ceph-Stor5-SSD) to -59 (Ceph-Stor5) + moving items from -13 (Ceph-Stor4-SSD) to -60 (Ceph-Stor4) + moving items from -12 (Ceph-Stor3-SSD) to -61 (Ceph-Stor3) + moving items from -11 (Ceph-Stor2-SSD) to -62 (Ceph-Stor2) + moving items from -10 (Ceph-Stor1-SSD) to -63 (Ceph-Stor1) + +wonky crush weights on Ceph-Stor1, so a small number of mappings change +because the new map has a strictly summing hierarchy. + + $ crushtool -i $TESTDIR/crush-classes/jwillem@stads.net --compare foo + rule 0 had 158/10240 mismatched mappings (0.0154297) + rule 1 had 62/5120 mismatched mappings (0.0121094) + rule 2 had 0/10240 mismatched mappings (0) + warning: maps are NOT equivalent + [1] + + $ crushtool -i $TESTDIR/crush-classes/dan@vanderster.com-beesly.crush --reclassify --reclassify-root 0513-R-0050 hdd --reclassify-root 0513-R-0060 hdd -o foo + classify_root 0513-R-0050 (-2) as hdd + new class hdd exists as 0 + renumbering bucket -2 -> -131 + renumbering bucket -14 -> -132 + renumbering bucket -34 -> -133 + renumbering bucket -33 -> -134 + renumbering bucket -30 -> -135 + renumbering bucket -26 -> -136 + renumbering bucket -22 -> -137 + renumbering bucket -18 -> -138 + renumbering bucket -13 -> -139 + renumbering bucket -9 -> -140 + renumbering bucket -12 -> -141 + renumbering bucket -11 -> -142 + renumbering bucket -32 -> -143 + renumbering bucket -31 -> -144 + renumbering bucket -10 -> -145 + renumbering bucket -8 -> -146 + renumbering bucket -6 -> -147 + renumbering bucket -28 -> -148 + renumbering bucket -27 -> -149 + renumbering bucket -21 -> -150 + renumbering bucket -20 -> -151 + renumbering bucket -19 -> -152 + renumbering bucket -7 -> -153 + renumbering bucket -5 -> -154 + renumbering bucket -4 -> -155 + renumbering bucket -25 -> -156 + renumbering bucket -24 -> -157 + renumbering bucket -23 -> -158 + renumbering bucket -17 -> -159 + renumbering bucket -16 -> -160 + renumbering bucket -15 -> -161 + renumbering bucket -3 -> -162 + renumbering bucket -72 -> -163 + renumbering bucket -98 -> -164 + renumbering bucket -97 -> -165 + renumbering bucket -96 -> -166 + renumbering bucket -95 -> -167 + renumbering bucket -94 -> -168 + renumbering bucket -93 -> -169 + renumbering bucket -68 -> -170 + classify_root 0513-R-0060 (-65) as hdd + new class hdd exists as 0 + renumbering bucket -65 -> -35 + renumbering bucket -76 -> -36 + renumbering bucket -78 -> -37 + renumbering bucket -87 -> -38 + renumbering bucket -82 -> -39 + renumbering bucket -81 -> -40 + renumbering bucket -77 -> -41 + renumbering bucket -75 -> -42 + renumbering bucket -89 -> -43 + renumbering bucket -85 -> -44 + renumbering bucket -84 -> -45 + renumbering bucket -74 -> -46 + renumbering bucket -71 -> -47 + renumbering bucket -80 -> -48 + renumbering bucket -91 -> -49 + renumbering bucket -90 -> -50 + renumbering bucket -88 -> -51 + renumbering bucket -79 -> -52 + renumbering bucket -70 -> -53 + renumbering bucket -86 -> -54 + renumbering bucket -83 -> -55 + renumbering bucket -73 -> -56 + renumbering bucket -69 -> -57 + $ crushtool -i $TESTDIR/crush-classes/dan@vanderster.com-beesly.crush --compare foo + rule 0 had 0/10240 mismatched mappings (0) + rule 1 had 0/10240 mismatched mappings (0) + rule 2 had 0/10240 mismatched mappings (0) + rule 4 had 0/10240 mismatched mappings (0) + maps appear equivalent + + $ crushtool -i $TESTDIR/crush-classes/dan@vanderster.com-flax.crush --reclassify --reclassify-root default hdd -o foo + classify_root default (-1) as hdd + new class hdd exists as 0 + renumbering bucket -1 -> -5 + renumbering bucket -12 -> -7 + renumbering bucket -9 -> -8 + renumbering bucket -6 -> -10 + renumbering bucket -4 -> -11 + renumbering bucket -3 -> -13 + renumbering bucket -2 -> -14 + $ crushtool -i $TESTDIR/crush-classes/dan@vanderster.com-flax.crush --compare foo + rule 0 had 0/10240 mismatched mappings (0) + maps appear equivalent + + $ crushtool -i $TESTDIR/crush-classes/dan@vanderster.com-gabe.crush --reclassify --reclassify-root default hdd -o foo + classify_root default (-1) as hdd + new class hdd exists as 0 + rule 3 includes take on root default class 0 + failed to reclassify map + [1] + +above fails because of ec-rack-by-2-hdd also has take default class hdd + + + $ crushtool -i $TESTDIR/crush-classes/michael.nieporte@uk-essen.de --reclassify --reclassify-bucket %-hdd hdd default --reclassify-bucket %-ssd ssd default --reclassify-bucket ssd ssd default --reclassify-bucket hdd hdd default -o foo + classify_bucket %-hdd as hdd default bucket default (root) + new class hdd exists as 0 + match %-hdd to berta-hdd basename berta + have base -37 + match %-hdd to oelgard-hdd basename oelgard + have base -36 + match %-hdd to leonhard-hdd basename leonhard + have base -33 + match %-hdd to gottlieb-hdd basename gottlieb + have base -30 + match %-hdd to hieronymus-hdd basename hieronymus + have base -31 + match %-hdd to uhu-hdd basename uhu + have base -34 + match %-hdd to euphrosyne-hdd basename euphrosyne + have base -35 + match %-hdd to frauenhaus-hdd basename frauenhaus + created base -145 + match %-hdd to herrenhaus-hdd basename herrenhaus + created base -146 + match %-hdd to zoo-hdd basename zoo + created base -147 + match %-hdd to borkenkaefer-hdd basename borkenkaefer + have base -4 + match %-hdd to hirsch-hdd basename hirsch + have base -41 + match %-hdd to cassowary-hdd basename cassowary + created base -148 + match %-hdd to fuchs-hdd basename fuchs + created base -149 + match %-hdd to analia-hdd basename analia + created base -150 + match %-hdd to gundula-hdd basename gundula + created base -151 + match %-hdd to achim-hdd basename achim + created base -152 + match %-hdd to hugo-hdd basename hugo + created base -153 + match %-hdd to carl-hdd basename carl + have base -32 + classify_bucket %-ssd as ssd default bucket default (root) + new class ssd exists as 1 + match %-ssd to frauenhaus-ssd basename frauenhaus + created base -154 + match %-ssd to herrenhaus-ssd basename herrenhaus + created base -155 + match %-ssd to zoo-ssd basename zoo + created base -156 + match %-ssd to berta-ssd basename berta + have base -37 + match %-ssd to euphrosyne-ssd basename euphrosyne + have base -35 + match %-ssd to oelgard-ssd basename oelgard + have base -36 + match %-ssd to leonhard-ssd basename leonhard + have base -33 + match %-ssd to hieronymus-ssd basename hieronymus + have base -31 + match %-ssd to gottlieb-ssd basename gottlieb + have base -30 + match %-ssd to uhu-ssd basename uhu + have base -34 + match %-ssd to borkenkaefer-ssd basename borkenkaefer + have base -4 + match %-ssd to hirsch-ssd basename hirsch + have base -41 + match %-ssd to phaidon-ssd basename phaidon + created base -157 + match %-ssd to glykera-ssd basename glykera + created base -158 + match %-ssd to bonobo-ssd basename bonobo + created base -159 + classify_bucket hdd as hdd default bucket default (root) + new class hdd exists as 0 + match hdd to hdd basename default + have base -1 + classify_bucket ssd as ssd default bucket default (root) + new class ssd exists as 1 + match ssd to ssd basename default + have base -1 + moving items from -124 (bonobo-ssd) to -159 (bonobo) + moving items from -123 (glykera-ssd) to -158 (glykera) + moving items from -122 (phaidon-ssd) to -157 (phaidon) + moving items from -121 (carl-hdd) to -32 (carl) + moving items from -120 (hugo-hdd) to -153 (hugo) + moving items from -119 (achim-hdd) to -152 (achim) + moving items from -118 (gundula-hdd) to -151 (gundula) + moving items from -117 (analia-hdd) to -150 (analia) + moving items from -116 (fuchs-hdd) to -149 (fuchs) + moving items from -115 (cassowary-hdd) to -148 (cassowary) + moving items from -39 (hirsch-ssd) to -41 (hirsch) + moving items from -38 (hirsch-hdd) to -41 (hirsch) + moving items from -29 (borkenkaefer-ssd) to -4 (borkenkaefer) + moving items from -28 (hdd) to -1 (default) + moving items from -27 (ssd) to -1 (default) + reclassify err from insert_item: (17) File exists + failed to reclassify map + [1] + +this makes changes, but it doesn't really clean up the map, which is +a mess! + + $ crushtool -i $TESTDIR/crush-classes/michael.nieporte@uk-essen.de --compare foo + rule 0 had 3060/3072 mismatched mappings (0.996094) + rule 1 had 4096/4096 mismatched mappings (1) + warning: maps are NOT equivalent + [1] diff --git a/src/tools/crushtool.cc b/src/tools/crushtool.cc index 7006bbf9efa0..3d153e6b7658 100644 --- a/src/tools/crushtool.cc +++ b/src/tools/crushtool.cc @@ -218,6 +218,10 @@ void usage() cout << " export select data generated during testing routine\n"; cout << " to CSV files for off-line post-processing\n"; cout << " use --help-output for more information\n"; + cout << " --reclassify transform legacy CRUSH map buckets and rules\n"; + cout << " by adding classes\n"; + cout << " --reclassify-bucket \n"; + cout << " --reclassify-root \n"; cout << "\n"; cout << "Options for the output stage\n"; cout << "\n"; @@ -408,6 +412,10 @@ int main(int argc, const char **argv) int straw_calc_version = -1; int allowed_bucket_algs = -1; + bool reclassify = false; + map> reclassify_bucket; // %suffix or prefix% -> class, default_root + map reclassify_root; // bucket -> class + CrushWrapper crush; CrushTester tester(crush, cout); @@ -442,6 +450,30 @@ int main(int argc, const char **argv) outfn = val; } else if (ceph_argparse_flag(args, i, "-v", "--verbose", (char*)NULL)) { verbose += 1; + } else if (ceph_argparse_flag(args, i, "--reclassify", (char*)NULL)) { + reclassify = true; + } else if (ceph_argparse_witharg(args, i, &val, "--reclassify-bucket", + (char*)NULL)) { + if (i == args.end()) { + cerr << "expecting additional argument" << std::endl; + return EXIT_FAILURE; + } + string c = *i; + i = args.erase(i); + if (i == args.end()) { + cerr << "expecting additional argument" << std::endl; + return EXIT_FAILURE; + } + reclassify_bucket[val] = make_pair(c, *i); + i = args.erase(i); + } else if (ceph_argparse_witharg(args, i, &val, "--reclassify-root", + (char*)NULL)) { + if (i == args.end()) { + cerr << "expecting additional argument" << std::endl; + return EXIT_FAILURE; + } + reclassify_root[val] = *i; + i = args.erase(i); } else if (ceph_argparse_flag(args, i, "--tree", (char*)NULL)) { tree = true; } else if (ceph_argparse_witharg(args, i, &val, "-f", "--format", (char*)NULL)) { @@ -773,6 +805,7 @@ int main(int argc, const char **argv) } if (!check && !compile && !decompile && !build && !test && !reweight && !adjust && !tree && !dump && add_item < 0 && !add_bucket && !move_item && !add_rule && !del_rule && full_location < 0 && + !reclassify && remove_name.empty() && reweight_name.empty()) { cerr << "no action specified; -h for help" << std::endl; return EXIT_FAILURE; @@ -1115,6 +1148,18 @@ int main(int argc, const char **argv) modified = true; } + if (reclassify) { + int r = crush.reclassify( + g_ceph_context, + cout, + reclassify_root, + reclassify_bucket); + if (r < 0) { + cerr << "failed to reclassify map" << std::endl; + return EXIT_FAILURE; + } + modified = true; + } // display --- if (full_location >= 0) { @@ -1127,7 +1172,7 @@ int main(int argc, const char **argv) } if (tree) { - crush.dump_tree(&cout, NULL); + crush.dump_tree(&cout, NULL, {}, true); } if (dump) {