]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
cls_rgw, rgw-admin: create bi list operation
authorYehuda Sadeh <yehuda@redhat.com>
Tue, 21 Oct 2014 23:35:55 +0000 (16:35 -0700)
committerYehuda Sadeh <yehuda@redhat.com>
Mon, 19 Jan 2015 23:57:47 +0000 (15:57 -0800)
Can now list bucket index entries that correspond to a single key. This
is useful when debugging versioned objects issues.

Signed-off-by: Yehuda Sadeh <yehuda@redhat.com>
src/cls/rgw/cls_rgw.cc
src/cls/rgw/cls_rgw_client.cc
src/cls/rgw/cls_rgw_client.h
src/cls/rgw/cls_rgw_ops.h
src/rgw/rgw_admin.cc
src/rgw/rgw_rados.cc
src/rgw/rgw_rados.h

index ce9729edebf71ac9ff2cd7e56fb8426fbcd4149f..6c13d67881243ebc943a7c74ed66674d71bc6be6 100644 (file)
@@ -34,6 +34,7 @@ cls_method_handle_t h_rgw_bucket_read_olh_log;
 cls_method_handle_t h_rgw_bucket_trim_olh_log;
 cls_method_handle_t h_rgw_obj_remove;
 cls_method_handle_t h_rgw_bi_get_op;
+cls_method_handle_t h_rgw_bi_list_op;
 cls_method_handle_t h_rgw_bi_log_list_op;
 cls_method_handle_t h_rgw_dir_suggest_changes;
 cls_method_handle_t h_rgw_user_usage_log_add;
@@ -84,7 +85,7 @@ int bi_entry_type(const string& s)
        ++i) {
     const string& t = bucket_index_prefixes[i];
 
-    if (s.compare(0, t.size(), t) == 0) {
+    if (s.compare(1, t.size(), t) == 0) {
       return i;
     }
   }
@@ -92,6 +93,20 @@ int bi_entry_type(const string& s)
   return -EINVAL;
 }
 
+static bool bi_entry_gt(const string& first, const string& second)
+{
+  int fi = bi_entry_type(first);
+  int si = bi_entry_type(second);
+
+  if (fi > si) {
+    return true;
+  } else if (fi < si) {
+    return false;
+  }
+
+  return first > second;
+}
+
 static void get_time_key(utime_t& ut, string *key)
 {
   char buf[32];
@@ -1725,6 +1740,194 @@ static int rgw_bi_get_op(cls_method_context_t hctx, bufferlist *in, bufferlist *
   return 0;
 }
 
+static int list_plain_entries(cls_method_context_t hctx, const string& name, const string& marker, uint32_t max,
+                              list<rgw_cls_bi_entry> *entries)
+{
+  string filter = name;
+  string start_key = marker;
+  int count = 0;
+  map<string, bufferlist> keys;
+  do {
+    if (count >= (int)max) {
+      return count;
+    }
+    keys.clear();
+#define BI_GET_NUM_KEYS 128
+    int ret = cls_cxx_map_get_vals(hctx, start_key, filter, BI_GET_NUM_KEYS, &keys);
+    if (ret < 0) {
+      return ret;
+    }
+
+    map<string, bufferlist>::iterator iter;
+    for (iter = keys.begin(); iter != keys.end(); ++iter) {
+      rgw_cls_bi_entry entry;
+      entry.type = PlainIdx;
+      entry.idx = iter->first;
+      entry.data = iter->second;
+
+      bufferlist::iterator biter = entry.data.begin();
+
+      rgw_bucket_dir_entry e;
+      try {
+        ::decode(e, biter);
+      } catch (buffer::error& err) {
+        CLS_LOG(0, "ERROR: %s(): failed to decode buffer", __func__);
+        return -EIO;
+      }
+
+      if (e.key.name != name) {
+        return count;
+      }
+
+      entries->push_back(entry);
+      count++;
+      start_key = entry.idx;
+    }
+  } while (!keys.empty());
+
+  return count;
+}
+
+static int get_map_vals_starting_at(cls_method_context_t hctx,
+                                const string &start_at,
+                                const string &filter_prefix,
+                                uint64_t max_to_get,
+                                std::map<string, bufferlist> *vals)
+{
+  if (max_to_get == 0) {
+    return 0;
+  }
+  bufferlist bl;
+  bool found_first = false;
+
+  if (start_at.substr(0, filter_prefix.size()) == filter_prefix) {
+    int ret = cls_cxx_map_get_val(hctx, start_at, &bl);
+    if (ret < 0 && ret != -ENOENT) {
+      return ret;
+    }
+    found_first = (ret == 0);
+
+    if (found_first) {
+      max_to_get--;
+    }
+  }
+
+  if (max_to_get > 0) {
+    int ret = cls_cxx_map_get_vals(hctx, start_at, filter_prefix, max_to_get, vals);
+    if (ret < 0 && ret != -ENOENT) {
+      return ret;
+    }
+  }
+
+  if (found_first) {
+    (*vals)[start_at] = bl;
+  }
+
+  return 0;
+}
+
+static int list_instance_entries(cls_method_context_t hctx, const string& name, const string& marker, uint32_t max,
+                                 list<rgw_cls_bi_entry> *entries)
+{
+  cls_rgw_obj_key key(name);
+  string first_instance_idx;
+  encode_obj_versioned_data_key(key, &first_instance_idx);
+  string start_key = first_instance_idx;
+  if (bi_entry_gt(marker, start_key)) {
+    start_key = marker;
+  }
+  int count = 0;
+  map<string, bufferlist> keys;
+  do {
+    if (count >= (int)max) {
+      return count;
+    }
+    keys.clear();
+#define BI_GET_NUM_KEYS 128
+    string filter;
+    int ret = get_map_vals_starting_at(hctx, start_key, filter, BI_GET_NUM_KEYS, &keys);
+    if (ret < 0) {
+      return ret;
+    }
+
+    map<string, bufferlist>::iterator iter;
+    for (iter = keys.begin(); iter != keys.end(); ++iter) {
+      rgw_cls_bi_entry entry;
+      entry.type = InstanceIdx;
+      entry.idx = iter->first;
+      entry.data = iter->second;
+
+      bufferlist::iterator biter = entry.data.begin();
+
+      rgw_bucket_dir_entry e;
+      try {
+        ::decode(e, biter);
+      } catch (buffer::error& err) {
+        CLS_LOG(0, "ERROR: %s(): failed to decode buffer", __func__);
+        return -EIO;
+      }
+
+      if (e.key.name != name) {
+        return count;
+      }
+
+      entries->push_back(entry);
+      count++;
+      start_key = entry.idx;
+    }
+  } while (!keys.empty());
+
+  return count;
+}
+
+static int rgw_bi_list_op(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+  // decode request
+  rgw_cls_bi_list_op op;
+  bufferlist::iterator iter = in->begin();
+  try {
+    ::decode(op, iter);
+  } catch (buffer::error& err) {
+    CLS_LOG(0, "ERROR: %s(): failed to decode request", __func__);
+    return -EINVAL;
+  }
+
+  rgw_cls_bi_list_ret op_ret;
+
+  string filter = op.name;
+#define MAX_BI_LIST_ENTRIES 1000
+  int32_t max = (op.max < MAX_BI_LIST_ENTRIES ? op.max : MAX_BI_LIST_ENTRIES);
+  string start_key = op.marker;
+  int ret = list_plain_entries(hctx, op.name, op.marker, max, &op_ret.entries);
+  if (ret < 0) {
+    CLS_LOG(0, "ERROR: %s(): list_plain_entries retured ret=%d", __func__, ret);
+    return ret;
+  }
+  int count = ret;
+
+  ret = list_instance_entries(hctx, op.name, op.marker, max - count, &op_ret.entries);
+  if (ret < 0) {
+    CLS_LOG(0, "ERROR: %s(): list_instance_entries retured ret=%d", __func__, ret);
+    return ret;
+  }
+
+  cls_rgw_obj_key key(op.name);
+  rgw_cls_bi_entry entry;
+  encode_olh_data_key(key, &entry.idx);
+  ret = cls_cxx_map_get_val(hctx, entry.idx, &entry.data);
+  if (ret < 0 && ret != -ENOENT) {
+    CLS_LOG(0, "ERROR: %s(): cls_cxx_map_get_val retured ret=%d", __func__, ret);
+    return ret;
+  } else if (ret >= 0) {
+    entry.type = OLHIdx;
+    op_ret.entries.push_back(entry);
+  }
+
+  ::encode(op_ret, *out);
+
+  return 0;
+}
+
 int bi_log_record_decode(bufferlist& bl, rgw_bi_log_entry& e)
 {
   bufferlist::iterator iter = bl.begin();
@@ -2548,6 +2751,7 @@ void __cls_init()
   cls_register_cxx_method(h_class, "obj_remove", CLS_METHOD_RD | CLS_METHOD_WR, rgw_obj_remove, &h_rgw_obj_remove);
 
   cls_register_cxx_method(h_class, "bi_get", CLS_METHOD_RD, rgw_bi_get_op, &h_rgw_bi_get_op);
+  cls_register_cxx_method(h_class, "bi_list", CLS_METHOD_RD, rgw_bi_list_op, &h_rgw_bi_list_op);
 
   cls_register_cxx_method(h_class, "bi_log_list", CLS_METHOD_RD, rgw_bi_log_list, &h_rgw_bi_log_list_op);
   cls_register_cxx_method(h_class, "bi_log_trim", CLS_METHOD_RD | CLS_METHOD_WR, rgw_bi_log_trim, &h_rgw_bi_log_list_op);
index 4e1af1e1279085a8884990a7eb53643d66a2eb18..ee327f438eb83b8547fdbfc11533cc74f695fe0c 100644 (file)
@@ -129,6 +129,34 @@ int cls_rgw_bi_get(librados::IoCtx& io_ctx, const string oid,
   return 0;
 }
 
+int cls_rgw_bi_list(librados::IoCtx& io_ctx, const string oid,
+                   const string& name, const string& marker, uint32_t max,
+                   list<rgw_cls_bi_entry> *entries, bool *is_truncated)
+{
+  bufferlist in, out;
+  struct rgw_cls_bi_list_op call;
+  call.name = name;
+  call.marker = marker;
+  call.max = max;
+  ::encode(call, in);
+  int r = io_ctx.exec(oid, "rgw", "bi_list", in, out);
+  if (r < 0)
+    return r;
+
+  struct rgw_cls_bi_list_ret op_ret;
+  bufferlist::iterator iter = out.begin();
+  try {
+    ::decode(op_ret, iter);
+  } catch (buffer::error& err) {
+    return -EIO;
+  }
+
+  *entries = op_ret.entries;
+  *is_truncated = op_ret.is_truncated;
+
+  return 0;
+}
+
 int cls_rgw_bucket_link_olh(librados::IoCtx& io_ctx, const string& oid, const cls_rgw_obj_key& key,
                             bool delete_marker, const string& op_tag)
 {
index ed242baf338f6406c8e3eedf5af148a4b98f8aac..82ae1dc354b493c0ca38fd692a0f580c838a1eb7 100644 (file)
@@ -36,6 +36,10 @@ void cls_rgw_remove_obj(librados::ObjectWriteOperation& o, list<string>& keep_at
 int cls_rgw_bi_get(librados::IoCtx& io_ctx, const string oid,
                    BIIndexType index_type, cls_rgw_obj_key& key,
                    rgw_cls_bi_entry *entry);
+int cls_rgw_bi_list(librados::IoCtx& io_ctx, const string oid,
+                   const string& name, const string& marker, uint32_t max,
+                   list<rgw_cls_bi_entry> *entries, bool *is_truncated);
+
 
 int cls_rgw_bucket_link_olh(librados::IoCtx& io_ctx, const string& oid, const cls_rgw_obj_key& key,
                             bool delete_marker, const string& op_tag);
index 2963ad2bfa1b2761cf71c4bbd194790aee7b9bc9..a7a9179f8e41cc29d6fef88fc73d3b8a9cf0139c 100644 (file)
@@ -432,6 +432,53 @@ struct rgw_cls_bi_get_ret {
 };
 WRITE_CLASS_ENCODER(rgw_cls_bi_get_ret)
 
+struct rgw_cls_bi_list_op {
+  uint32_t max;
+  string name;
+  string marker;
+
+  rgw_cls_bi_list_op() : max(0) {}
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    ::encode(max, bl);
+    ::encode(name, bl);
+    ::encode(marker, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(1, bl);
+    ::decode(max, bl);
+    ::decode(name, bl);
+    ::decode(marker, bl);
+    DECODE_FINISH(bl);
+  }
+};
+WRITE_CLASS_ENCODER(rgw_cls_bi_list_op)
+
+struct rgw_cls_bi_list_ret {
+  list<rgw_cls_bi_entry> entries;
+  bool is_truncated;
+
+  rgw_cls_bi_list_ret() : is_truncated(false) {}
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    ::encode(entries, bl);
+    ::encode(is_truncated, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::iterator& bl) {
+    DECODE_START(1, bl);
+    ::decode(entries, bl);
+    ::decode(is_truncated, bl);
+    DECODE_FINISH(bl);
+  }
+};
+WRITE_CLASS_ENCODER(rgw_cls_bi_list_ret)
+
 struct rgw_cls_usage_log_read_op {
   uint64_t start_epoch;
   uint64_t end_epoch;
index 358c40bb43e893f8ee90595a8f8146aa6b26ca36..230b3ef8786f4e654ac99630f92b80ab5927ef21 100644 (file)
@@ -222,6 +222,7 @@ enum {
   OPT_OBJECT_STAT,
   OPT_OBJECT_REWRITE,
   OPT_BI_GET,
+  OPT_BI_LIST,
   OPT_OLH_GET,
   OPT_OLH_READLOG,
   OPT_QUOTA_SET,
@@ -391,6 +392,8 @@ static int get_cmd(const char *cmd, const char *prev_cmd, bool *need_more)
   } else if (strcmp(prev_cmd, "bi") == 0) {
     if (strcmp(cmd, "get") == 0)
       return OPT_BI_GET;
+    if (strcmp(cmd, "list") == 0)
+      return OPT_BI_LIST;
   } else if (strcmp(prev_cmd, "region") == 0) {
     if (strcmp(cmd, "get") == 0)
       return OPT_REGION_GET;
@@ -2006,6 +2009,43 @@ next:
     formatter->flush(cout);
   }
 
+  if (opt_cmd == OPT_BI_LIST) {
+    RGWBucketInfo bucket_info;
+    int ret = init_bucket(bucket_name, bucket_info, bucket);
+    if (ret < 0) {
+      cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
+      return -ret;
+    }
+
+    list<rgw_cls_bi_entry> entries;
+    bool is_truncated;
+    if (max_entries < 0) {
+      max_entries = 1000;
+    }
+
+
+    formatter->open_array_section("entries");
+
+    do {
+      entries.clear();
+      ret = store->bi_list(bucket, object, marker, max_entries, &entries, &is_truncated);
+      if (ret < 0) {
+        cerr << "ERROR: bi_list(): " << cpp_strerror(-ret) << std::endl;
+        return -ret;
+      }
+
+      list<rgw_cls_bi_entry>::iterator iter;
+      for (iter = entries.begin(); iter != entries.end(); ++iter) {
+        rgw_cls_bi_entry& entry = *iter;
+        encode_json("entry", entry, formatter);
+        marker = entry.idx;
+      }
+      formatter->flush(cout);
+    } while (is_truncated);
+    formatter->close_section();
+    formatter->flush(cout);
+  }
+
   if (opt_cmd == OPT_OBJECT_RM) {
     RGWBucketInfo bucket_info;
     int ret = init_bucket(bucket_name, bucket_info, bucket);
index b7aaf5171715a0a807ed4d93d652deeaa7b35c4a..8d5a721932be2c9512d88badbe36a05ebc9d3e41 100644 (file)
@@ -6590,6 +6590,21 @@ int RGWRados::bi_get(rgw_bucket& bucket, rgw_obj& obj, BIIndexType index_type, r
   return 0;
 }
 
+int RGWRados::bi_list(rgw_bucket& bucket, const string& obj_name, const string& marker, uint32_t max, list<rgw_cls_bi_entry> *entries, bool *is_truncated)
+{
+  librados::IoCtx index_ctx;
+  string oid;
+  int r = open_bucket_index(bucket, index_ctx, oid);
+  if (r < 0)
+    return r;
+
+  int ret = cls_rgw_bi_list(index_ctx, oid, obj_name, marker, max, entries, is_truncated);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
 int RGWRados::gc_operate(string& oid, librados::ObjectWriteOperation *op)
 {
   return gc_pool_ctx.operate(oid, op);
index 2079ca6d9e959c6cc488239d6c4a1b15b7754594..9bc3821b85a9536a18d7fb3e2287d143cfd03160 100644 (file)
@@ -1769,6 +1769,8 @@ public:
   int trim_bi_log_entries(rgw_bucket& bucket, string& marker, string& end_marker);
 
   int bi_get(rgw_bucket& bucket, rgw_obj& obj, BIIndexType index_type, rgw_cls_bi_entry *entry);
+  int bi_list(rgw_bucket& bucket, const string& obj_name, const string& marker, uint32_t max,
+              list<rgw_cls_bi_entry> *entries, bool *is_truncated);
 
   int cls_obj_usage_log_add(const string& oid, rgw_usage_log_info& info);
   int cls_obj_usage_log_read(string& oid, string& user, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries,