]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
crushtool: add --compare command
authorSage Weil <sage@redhat.com>
Mon, 8 Oct 2018 19:58:38 +0000 (14:58 -0500)
committerSage Weil <sage@redhat.com>
Tue, 16 Oct 2018 13:50:23 +0000 (08:50 -0500)
Compare two maps over the same input space as --test would.

Signed-off-by: Sage Weil <sage@redhat.com>
src/crush/CrushTester.cc
src/crush/CrushTester.h
src/test/cli/crushtool/help.t
src/tools/crushtool.cc

index b6b4b2f20c3b17d784c8dbf7204106d85ccde405..86f91ef3d22587bbcd16c39d811de5989abcd2a3 100644 (file)
@@ -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<int,int> 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<int> out;
+       crush.do_rule(r, x, out, nr, weight, 0);
+       vector<int> 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;
+}
index e58c24875749ccb24542dc6df709700de616ccb8..c4257b63d6bc1ac7f4eac19856b37664d6a54924 100644 (file)
@@ -359,6 +359,8 @@ public:
   void check_overlapped_rules() const;
   int test();
   int test_with_fork(int timeout);
+
+  int compare(CrushWrapper& other);
 };
 
 #endif
index de42cd5c78505fd079a8e3b46798290e298d624b..f343c0e4f3a2585f294b4075073b539432c66d3c 100644 (file)
                            by adding classes
         --reclassify-bucket <bucket-match> <class> <default-parent>
         --reclassify-root <bucket-name> <class>
+     --compare <otherfile> compare two maps using --test parameters
   
   Options for the output stage
   
index 3d153e6b76584fac349f5a116b37faece8607487..8bc1c3fd5e321a82170405fc52139e0ce50c466e 100644 (file)
@@ -222,6 +222,7 @@ void usage()
   cout << "                         by adding classes\n";
   cout << "      --reclassify-bucket <bucket-match> <class> <default-parent>\n";
   cout << "      --reclassify-root <bucket-name> <class>\n";
+  cout << "   --compare <otherfile> 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<string,pair<string,string>> reclassify_bucket; // %suffix or prefix% -> class, default_root
   map<string,string> 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();