]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd-replay: Add --map-image option for image name mapping
authorAdam Crume <adamcrume@gmail.com>
Tue, 22 Jul 2014 23:06:52 +0000 (16:06 -0700)
committerSage Weil <sage@redhat.com>
Thu, 21 Aug 2014 17:57:31 +0000 (10:57 -0700)
Signed-off-by: Adam Crume <adamcrume@gmail.com>
src/rbd_replay/ImageNameMap.cc [new file with mode: 0644]
src/rbd_replay/ImageNameMap.hpp [new file with mode: 0644]
src/rbd_replay/Makefile.am
src/rbd_replay/NameMap.cc [new file with mode: 0644]
src/rbd_replay/NameMap.hpp [new file with mode: 0644]
src/rbd_replay/Replayer.cc
src/rbd_replay/Replayer.hpp
src/rbd_replay/actions.cc
src/rbd_replay/actions.hpp
src/rbd_replay/rbd-replay.cc

diff --git a/src/rbd_replay/ImageNameMap.cc b/src/rbd_replay/ImageNameMap.cc
new file mode 100644 (file)
index 0000000..f17c750
--- /dev/null
@@ -0,0 +1,83 @@
+// -*- 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) 2014 Adam Crume <adamcrume@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#include "ImageNameMap.hpp"
+#include <boost/foreach.hpp>
+#include "include/assert.h"
+
+
+using namespace std;
+using namespace rbd_replay;
+
+
+static ImageNameMap::Name bad_mapping_abort(ImageNameMap::Name input, string output) {
+  assertf(false, "Bad value returned from mapping.  Image: '%s', snap: '%s', output: '%s'", input.first.c_str(), input.second.c_str(), output.c_str());
+}
+
+ImageNameMap::ImageNameMap()
+  : m_name("((?:[^/\\\\]|\\\\.)*)/((?:[^/\\\\]|\\\\.)*)"),
+    m_escaped_slash("\\\\/"),
+    m_escaped_backslash("\\\\\\\\"),
+    m_slash("/"),
+    m_backslash("\\\\"),
+    m_bad_mapping_fallback(bad_mapping_abort) {
+}
+
+bool ImageNameMap::parse_mapping(string mapping_string, Mapping *mapping) const {
+  return m_map.parse_mapping(mapping_string, mapping);
+}
+
+void ImageNameMap::add_mapping(Mapping mapping) {
+  m_map.add_mapping(mapping);
+}
+
+string ImageNameMap::unescape_slash(string s) const {
+  s = boost::regex_replace(s, m_escaped_slash, "/");
+  return  boost::regex_replace(s, m_escaped_backslash, "\\\\");
+}
+
+string ImageNameMap::escape_slash(string s) const {
+  string result = boost::regex_replace(s, m_backslash, "\\\\");
+  return boost::regex_replace(result, m_slash, "\\\\/");
+}
+
+bool ImageNameMap::parse_name(string name_string, Name *name) const {
+  boost::smatch what;
+  if (boost::regex_match(name_string, what, m_name)) {
+    string image(unescape_slash(what[1]));
+    string snap(unescape_slash(what[2]));
+    *name = Name(image, snap);
+    return true;
+  } else {
+    return false;
+  }
+}
+
+string ImageNameMap::format_name(ImageNameMap::Name name) const {
+  return escape_slash(name.first) + "/" + escape_slash(name.second);
+}
+
+ImageNameMap::Name ImageNameMap::map(ImageNameMap::Name name) const {
+  string in_string(format_name(name));
+  string out_string(m_map.map(in_string));
+  Name out;
+  if (!parse_name(out_string, &out)) {
+    return m_bad_mapping_fallback(name, out_string);
+  }
+  return out;
+}
+
+void ImageNameMap::set_bad_mapping_fallback(BadMappingFallback bad_mapping_fallback) {
+  m_bad_mapping_fallback = bad_mapping_fallback;
+}
diff --git a/src/rbd_replay/ImageNameMap.hpp b/src/rbd_replay/ImageNameMap.hpp
new file mode 100644 (file)
index 0000000..5e9fa8c
--- /dev/null
@@ -0,0 +1,74 @@
+// -*- 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) 2014 Adam Crume <adamcrume@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#ifndef _INCLUDED_RBD_REPLAY_IMAGENAMEMAP_HPP
+#define _INCLUDED_RBD_REPLAY_IMAGENAMEMAP_HPP
+
+#include <map>
+#include <string>
+#include <vector>
+#include <boost/function.hpp>
+#include <boost/regex.hpp>
+#include "NameMap.hpp"
+
+namespace rbd_replay {
+
+class ImageNameMap {
+public:
+  typedef std::pair<boost::regex, std::string> Mapping;
+
+  typedef std::pair<std::string, std::string> Name;
+
+  typedef boost::function<Name (Name input, std::string output)> BadMappingFallback;
+
+  ImageNameMap();
+
+  bool parse_mapping(std::string mapping_string, Mapping *mapping) const;
+
+  void add_mapping(Mapping mapping);
+
+  bool parse_name(std::string name_string, Name *name) const;
+
+  std::string format_name(Name name) const;
+
+  Name map(Name name) const;
+
+  void set_bad_mapping_fallback(BadMappingFallback bad_mapping_fallback);
+
+private:
+  std::string escape_slash(std::string s) const;
+
+  std::string unescape_slash(std::string s) const;
+
+  NameMap m_map;
+
+  // Split "a/b" into "a" and "b", allowing for escaped slashes
+  boost::regex m_name;
+
+  // We don't have to worry about an even number of backslahes followed by a slash,
+  // because that slash would have already been matched and removed.
+  boost::regex m_escaped_slash;
+
+  boost::regex m_escaped_backslash;
+
+  boost::regex m_slash;
+
+  boost::regex m_backslash;
+
+  BadMappingFallback m_bad_mapping_fallback;
+};
+
+}
+
+#endif
index 5425229e417fc7b87495e555e1bf2f0ba1078283..aff2e9208c32a9fcad0539fb9eb18b4672b8f525 100644 (file)
@@ -1,12 +1,19 @@
 rbd_replay_SOURCES = rbd_replay/rbd-replay.cc \
        rbd_replay/actions.cc \
        rbd_replay/Deser.cc \
+       rbd_replay/ImageNameMap.cc \
+       rbd_replay/NameMap.cc \
        rbd_replay/PendingIO.cc \
        rbd_replay/Replayer.cc
-rbd_replay_LDADD = $(LIBRBD) $(LIBRADOS) $(CEPH_GLOBAL)
+rbd_replay_LDADD = $(LIBRBD) \
+       $(LIBRADOS) \
+       $(CEPH_GLOBAL) \
+       -lboost_regex
 noinst_HEADERS += rbd_replay/BoundedBuffer.hpp \
        rbd_replay/actions.hpp \
        rbd_replay/Deser.hpp \
+       rbd_replay/ImageNameMap.hpp \
+       rbd_replay/NameMap.hpp \
        rbd_replay/PendingIO.hpp \
        rbd_replay/rbd_replay_debug.hpp \
        rbd_replay/Replayer.hpp
diff --git a/src/rbd_replay/NameMap.cc b/src/rbd_replay/NameMap.cc
new file mode 100644 (file)
index 0000000..6496878
--- /dev/null
@@ -0,0 +1,52 @@
+// -*- 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) 2014 Adam Crume <adamcrume@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#include "NameMap.hpp"
+#include <boost/foreach.hpp>
+#include "include/assert.h"
+
+
+using namespace std;
+using namespace rbd_replay;
+
+
+bool NameMap::parse_mapping(string mapping_string, Mapping *mapping) const {
+  boost::smatch what;
+  if (boost::regex_match(mapping_string, what, m_keyval)) {
+      string pattern(what[1]);
+      string replacement(what[2]);
+
+      pattern = boost::regex_replace(pattern, m_escaped_equals, "=");
+      replacement = boost::regex_replace(replacement, m_escaped_equals, "=");
+
+      *mapping = Mapping(boost::regex(pattern), replacement);
+      return true;
+  } else {
+    return false;
+  }
+}
+
+void NameMap::add_mapping(Mapping mapping) {
+  m_mappings.push_back(mapping);
+}
+
+string NameMap::map(string name) const {
+  BOOST_FOREACH(const Mapping &m, m_mappings) {
+    boost::smatch what;
+    if (boost::regex_match(name, what, m.first)) {
+      return what.format(m.second);
+    }
+  }
+  return name;
+}
diff --git a/src/rbd_replay/NameMap.hpp b/src/rbd_replay/NameMap.hpp
new file mode 100644 (file)
index 0000000..68b0b65
--- /dev/null
@@ -0,0 +1,53 @@
+// -*- 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) 2014 Adam Crume <adamcrume@gmail.com>
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#ifndef _INCLUDED_RBD_REPLAY_NAMEMAP_HPP
+#define _INCLUDED_RBD_REPLAY_NAMEMAP_HPP
+
+#include <map>
+#include <string>
+#include <vector>
+#include <boost/regex.hpp>
+
+namespace rbd_replay {
+
+class NameMap {
+public:
+  typedef std::pair<boost::regex, std::string> Mapping;
+
+  NameMap()
+    : m_keyval("((?:[^=\\\\]|\\\\.)*)=((?:[^=\\\\]|\\\\.)*)"),
+      m_escaped_equals("\\\\=") {
+  }
+
+  bool parse_mapping(std::string mapping_string, Mapping *mapping) const;
+
+  void add_mapping(Mapping mapping);
+
+  std::string map(std::string name) const;
+
+private:
+  std::vector<Mapping> m_mappings;
+
+  // Split "a=b" into "a" and "b", allowing for escaped equal signs
+  boost::regex m_keyval;
+
+  // We don't have to worry about an even number of backslahes followed by an equal sign,
+  // because that equal sign would have already been matched and removed.
+  boost::regex m_escaped_equals;
+};
+
+}
+
+#endif
index c7de2062b1e1bf066fa7e784839e04e5ea24215f..54b61e1ef7493178e98ea99aa3bb35f4968c5f32 100644 (file)
@@ -128,6 +128,10 @@ bool Worker::readonly() const {
   return m_replayer.readonly();
 }
 
+pair<string, string> Worker::map_image_name(string image_name, string snap_name) const {
+  return m_replayer.image_name_map().map(pair<string,string>(image_name, snap_name));
+}
+
 
 Replayer::Replayer(int num_action_trackers)
   : m_pool_name("rbd"),
index 870fabf2ecc80036b5d8e3475631dd22f74697c7..55654e08c51343ed103bf2decbfd50f2194a786f 100644 (file)
@@ -18,6 +18,7 @@
 #include <boost/thread/mutex.hpp>
 #include <boost/thread/shared_mutex.hpp>
 #include "BoundedBuffer.hpp"
+#include "ImageNameMap.hpp"
 #include "PendingIO.hpp"
 
 namespace rbd_replay {
@@ -55,6 +56,8 @@ public:
 
   bool readonly() const;
 
+  std::pair<std::string, std::string> map_image_name(std::string image_name, std::string snap_name) const;
+
 private:
   void run();
 
@@ -106,6 +109,14 @@ public:
 
   void set_readonly(bool readonly);
 
+  void set_image_name_map(const ImageNameMap &map) {
+    m_image_name_map = map;
+  }
+
+  const ImageNameMap &image_name_map() const {
+    return m_image_name_map;
+  }
+
 private:
   struct action_tracker_d {
     // Maps an action ID to the time the action completed
@@ -127,6 +138,7 @@ private:
   std::string m_pool_name;
   float m_latency_multiplier;
   bool m_readonly;
+  ImageNameMap m_image_name_map;
 
   std::map<imagectx_id_t, librbd::Image*> m_images;
   boost::shared_mutex m_images_mutex;
index e328e22b9754fe5b70cf4acbecd8f72837e22728..0ac7801365c01a1aee51b6f40786b6c724616f9d 100644 (file)
@@ -316,14 +316,20 @@ void OpenImageAction::perform(ActionCtx &worker) {
   worker.add_pending(io);
   librbd::Image *image = new librbd::Image();
   librbd::RBD *rbd = worker.rbd();
+  pair<string, string> name(worker.map_image_name(m_name, m_snap_name));
   int r;
   if (m_readonly || worker.readonly()) {
-    r = rbd->open_read_only(*worker.ioctx(), *image, m_name.c_str(), m_snap_name.c_str());
+    r = rbd->open_read_only(*worker.ioctx(), *image, name.first.c_str(), name.second.c_str());
   } else {
-    r = rbd->open(*worker.ioctx(), *image, m_name.c_str(), m_snap_name.c_str());
+    r = rbd->open(*worker.ioctx(), *image, name.first.c_str(), name.second.c_str());
   }
   if (r) {
-    cerr << "Unable to open image '" << m_name << "' with snap '" << m_snap_name << "' and readonly " << m_readonly << ": " << strerror(-r) << std::endl;
+    cerr << "Unable to open image '" << m_name
+        << "' with snap '" << m_snap_name
+        << "' (mapped to '" << name.first
+        << "' and '" << name.second
+        << "') and readonly " << m_readonly
+        << ": (" << -r << ") " << strerror(-r) << std::endl;
     exit(1);
   }
   worker.put_image(m_imagectx_id, image);
index 14bfeeeed1e34a781a9363f1137e1a3fecfce150..c384f62ad92cef96d229f17a565adc9a4cf0b734 100644 (file)
@@ -67,6 +67,8 @@ public:
   virtual void set_action_complete(action_id_t id) = 0;
 
   virtual void stop() = 0;
+
+  virtual std::pair<std::string, std::string> map_image_name(std::string image_name, std::string snap_name) const = 0;
 };
 
 
index bb95787fa4af1bdbcbc6de64d7bc628f1444bcfc..a90317e0e694c7696a9ec34a21b644eae758a512 100644 (file)
@@ -18,6 +18,7 @@
 #include "global/global_init.h"
 #include "Replayer.hpp"
 #include "rbd_replay_debug.hpp"
+#include "ImageNameMap.hpp"
 
 
 using namespace std;
@@ -39,6 +40,19 @@ static void usage(const char* program) {
   cout << "  -p, --pool-name <pool>          Name of the pool to use.  Default: rbd" << std::endl;
   cout << "  --latency-multiplier <float>    Multiplies inter-request latencies.  Default: 1" << std::endl;
   cout << "  --read-only                     Only perform non-destructive operations." << std::endl;
+  cout << "  --map-image <rule>              Add a rule to map image names in the trace to" << std::endl;
+  cout << "                                  image names in the replay cluster." << std::endl;
+  cout << std::endl;
+  cout << "Image mapping rules:" << std::endl;
+  cout << "A rule of image1/snap1=image2/snap2 would map snap1 of image1 to snap2 of" << std::endl;
+  cout << "image2. Regular expressions are used, so image/snap_(.*)=image_$1/ would map" << std::endl;
+  cout << "image/snap_1 to image_1/. (Note that an un-snapshotted image has the empty" << std::endl;
+  cout << "string for the snapshot name.)" << std::endl;
+}
+
+static ImageNameMap::Name bad_mapping(ImageNameMap::Name input, string output) {
+  dout(0) << "Bad value returned from mapping.  Image: '" << input.first << "', snap '" << input.second << "', output: '" << output << "'.  Using image: '" << output << ", snap ''." << dendl;
+  return ImageNameMap::Name(output, "");
 }
 
 int main(int argc, const char **argv) {
@@ -52,6 +66,8 @@ int main(int argc, const char **argv) {
   string pool_name = "rbd";
   float latency_multiplier = 1;
   bool readonly = false;
+  ImageNameMap image_name_map;
+  image_name_map.set_bad_mapping_fallback(bad_mapping);
   std::string val;
   std::ostringstream err;
   for (i = args.begin(); i != args.end(); ) {
@@ -67,6 +83,14 @@ int main(int argc, const char **argv) {
       }
     } else if (ceph_argparse_flag(args, i, "--read-only", (char*)NULL)) {
       readonly = true;
+    } else if (ceph_argparse_witharg(args, i, &val, "--map-image", (char*)NULL)) {
+      ImageNameMap::Mapping mapping;
+      if (image_name_map.parse_mapping(val, &mapping)) {
+       image_name_map.add_mapping(mapping);
+      } else {
+       cerr << "Unable to parse mapping string: '" << val << "'" << std::endl;
+       return 1;
+      }
     } else if (ceph_argparse_flag(args, i, "-h", "--help", (char*)NULL)) {
       usage(argv[0]);
       return 0;
@@ -95,5 +119,6 @@ int main(int argc, const char **argv) {
   replayer.set_latency_multiplier(latency_multiplier);
   replayer.set_pool_name(pool_name);
   replayer.set_readonly(readonly);
+  replayer.set_image_name_map(image_name_map);
   replayer.run(replay_file);
 }