From d67192799f8847cd06b01da452dcddb471c9be71 Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Mon, 8 Oct 2018 14:58:38 -0500 Subject: [PATCH] crushtool: add --compare command Compare two maps over the same input space as --test would. Signed-off-by: Sage Weil --- src/crush/CrushTester.cc | 77 +++++++++++++++++++++++++++++++++++ src/crush/CrushTester.h | 2 + src/test/cli/crushtool/help.t | 1 + src/tools/crushtool.cc | 30 +++++++++++++- 4 files changed, 109 insertions(+), 1 deletion(-) diff --git a/src/crush/CrushTester.cc b/src/crush/CrushTester.cc index b6b4b2f20c3..86f91ef3d22 100644 --- a/src/crush/CrushTester.cc +++ b/src/crush/CrushTester.cc @@ -723,3 +723,80 @@ int CrushTester::test() return 0; } + +int CrushTester::compare(CrushWrapper& crush2) +{ + if (min_rule < 0 || max_rule < 0) { + min_rule = 0; + max_rule = crush.get_max_rules() - 1; + } + if (min_x < 0 || max_x < 0) { + min_x = 0; + max_x = 1023; + } + + // initial osd weights + vector<__u32> weight; + + /* + * note device weight is set by crushtool + * (likely due to a given a command line option) + */ + for (int o = 0; o < crush.get_max_devices(); o++) { + if (device_weight.count(o)) { + weight.push_back(device_weight[o]); + } else if (crush.check_item_present(o)) { + weight.push_back(0x10000); + } else { + weight.push_back(0); + } + } + + // make adjustments + adjust_weights(weight); + + map bad_by_rule; + + int ret = 0; + for (int r = min_rule; r < crush.get_max_rules() && r <= max_rule; r++) { + if (!crush.rule_exists(r)) { + if (output_statistics) + err << "rule " << r << " dne" << std::endl; + continue; + } + if (ruleset >= 0 && + crush.get_rule_mask_ruleset(r) != ruleset) { + continue; + } + int minr = min_rep, maxr = max_rep; + if (min_rep < 0 || max_rep < 0) { + minr = crush.get_rule_mask_min_size(r); + maxr = crush.get_rule_mask_max_size(r); + } + int bad = 0; + for (int nr = minr; nr <= maxr; nr++) { + for (int x = min_x; x <= max_x; ++x) { + vector out; + crush.do_rule(r, x, out, nr, weight, 0); + vector out2; + crush2.do_rule(r, x, out2, nr, weight, 0); + if (out != out2) { + ++bad; + } + } + } + if (bad) { + ret = -1; + } + int max = (maxr - minr + 1) * (max_x - min_x + 1); + double ratio = (double)bad / (double)max; + cout << "rule " << r << " had " << bad << "/" << max + << " mismatched mappings (" << ratio << ")" << std::endl; + } + if (ret) { + cerr << "warning: maps are NOT equivalent" << std::endl; + } else { + cout << "maps appear equivalent" << std::endl; + } + return ret; +} diff --git a/src/crush/CrushTester.h b/src/crush/CrushTester.h index e58c2487574..c4257b63d6b 100644 --- a/src/crush/CrushTester.h +++ b/src/crush/CrushTester.h @@ -359,6 +359,8 @@ public: void check_overlapped_rules() const; int test(); int test_with_fork(int timeout); + + int compare(CrushWrapper& other); }; #endif diff --git a/src/test/cli/crushtool/help.t b/src/test/cli/crushtool/help.t index de42cd5c785..f343c0e4f3a 100644 --- a/src/test/cli/crushtool/help.t +++ b/src/test/cli/crushtool/help.t @@ -114,6 +114,7 @@ by adding classes --reclassify-bucket --reclassify-root + --compare compare two maps using --test parameters Options for the output stage diff --git a/src/tools/crushtool.cc b/src/tools/crushtool.cc index 3d153e6b765..8bc1c3fd5e3 100644 --- a/src/tools/crushtool.cc +++ b/src/tools/crushtool.cc @@ -222,6 +222,7 @@ void usage() cout << " by adding classes\n"; cout << " --reclassify-bucket \n"; cout << " --reclassify-root \n"; + cout << " --compare compare two maps using --test parameters\n"; cout << "\n"; cout << "Options for the output stage\n"; cout << "\n"; @@ -416,6 +417,8 @@ int main(int argc, const char **argv) map> reclassify_bucket; // %suffix or prefix% -> class, default_root map reclassify_root; // bucket -> class + string compare; + CrushWrapper crush; CrushTester tester(crush, cout); @@ -450,6 +453,8 @@ 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_witharg(args, i, &val, "--compare", (char*)NULL)) { + compare = val; } else if (ceph_argparse_flag(args, i, "--reclassify", (char*)NULL)) { reclassify = true; } else if (ceph_argparse_witharg(args, i, &val, "--reclassify-bucket", @@ -795,7 +800,7 @@ int main(int argc, const char **argv) } } - if (test && !check && !display && !write_to_file) { + if (test && !check && !display && !write_to_file && compare.empty()) { cerr << "WARNING: no output selected; use --output-csv or --show-X" << std::endl; } @@ -806,6 +811,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 && + compare.empty() && remove_name.empty() && reweight_name.empty()) { cerr << "no action specified; -h for help" << std::endl; return EXIT_FAILURE; @@ -1219,6 +1225,28 @@ int main(int argc, const char **argv) return EXIT_FAILURE; } + if (compare.size()) { + CrushWrapper crush2; + bufferlist in; + string error; + int r = in.read_file(compare.c_str(), &error); + if (r < 0) { + cerr << me << ": error reading '" << compare << "': " + << error << std::endl; + return EXIT_FAILURE; + } + auto p = in.cbegin(); + try { + crush2.decode(p); + } catch(...) { + cerr << me << ": unable to decode " << compare << std::endl; + return EXIT_FAILURE; + } + r = tester.compare(crush2); + if (r < 0) + return EXIT_FAILURE; + } + // output --- if (modified) { crush.finalize(); -- 2.39.5