]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: admin: introduce objects expire-stale list and rm
authorAbhishek Lekshmanan <abhishek@suse.com>
Mon, 29 Apr 2019 15:30:46 +0000 (17:30 +0200)
committerAbhishek Lekshmanan <abhishek@suse.com>
Mon, 6 May 2019 09:52:13 +0000 (11:52 +0200)
These commands help cleanup stale expired objects after a reshard has happened
Contains the following changesets:

* rgw_bucket: add a AdminOp function to cleanup stale expired objects
There is also a dry run option, which will allow listing to just view the
objects before we actually delete them
* rgw_bucket: introduce rgw_object_get_attr helper function
Since this could be re-used across other functions that try to get a single
xattr, both get acl and the new object expire stale commands now use this
function to read the value of a single xattr

Signed-off-by: Abhishek Lekshmanan <abhishek@suse.com>
src/rgw/rgw_admin.cc
src/rgw/rgw_bucket.cc
src/rgw/rgw_bucket.h
src/test/cli/radosgw-admin/help.t

index 6457050511e4e6326d0dc2cda8c97eb24b01c40d..b554d324ae80003b8be74c3e8ae689209de93d1c 100644 (file)
@@ -116,6 +116,8 @@ void usage()
   cout << "  object unlink              unlink object from bucket index\n";
   cout << "  object rewrite             rewrite the specified object\n";
   cout << "  objects expire             run expired objects cleanup\n";
+  cout << "  objects expire-stale list  list stale expired objects (caused by reshard)\n";
+  cout << "  objects expire-stale rm    remove stale expired objects\n";
   cout << "  period rm                  remove a period\n";
   cout << "  period get                 get period info\n";
   cout << "  period get-current         get current period info\n";
@@ -423,6 +425,8 @@ enum {
   OPT_OBJECT_STAT,
   OPT_OBJECT_REWRITE,
   OPT_OBJECTS_EXPIRE,
+  OPT_OBJECTS_EXPIRE_STALE_LIST,
+  OPT_OBJECTS_EXPIRE_STALE_RM,
   OPT_BI_GET,
   OPT_BI_PUT,
   OPT_BI_LIST,
@@ -567,6 +571,7 @@ static int get_cmd(const char *cmd, const char *prev_cmd, const char *prev_prev_
       strcmp(cmd, "datalog") == 0 ||
       strcmp(cmd, "error") == 0 ||
       strcmp(cmd, "event") == 0 ||
+      strcmp(cmd, "expire-stale") == 0 ||
       strcmp(cmd, "gc") == 0 ||
       strcmp(cmd, "global") == 0 ||
       strcmp(cmd, "key") == 0 ||
@@ -744,6 +749,12 @@ static int get_cmd(const char *cmd, const char *prev_cmd, const char *prev_prev_
   } else if (strcmp(prev_cmd, "objects") == 0) {
     if (strcmp(cmd, "expire") == 0)
       return OPT_OBJECTS_EXPIRE;
+  } else if ((prev_prev_cmd && strcmp(prev_prev_cmd, "objects") == 0) &&
+            (strcmp(prev_cmd, "expire-stale") == 0)) {
+    if (strcmp(cmd, "list") == 0)
+      return OPT_OBJECTS_EXPIRE_STALE_LIST;
+    if (strcmp(cmd, "rm") == 0)
+      return OPT_OBJECTS_EXPIRE_STALE_RM;
   } else if (strcmp(prev_cmd, "olh") == 0) {
     if (strcmp(cmd, "get") == 0)
       return OPT_OLH_GET;
@@ -6081,6 +6092,22 @@ next:
     }
   }
 
+  if (opt_cmd == OPT_OBJECTS_EXPIRE_STALE_LIST) {
+    ret = RGWBucketAdminOp::fix_obj_expiry(store, bucket_op, f, true);
+    if (ret < 0) {
+      cerr << "ERROR: listing returned " << cpp_strerror(-ret) << std::endl;
+      return -ret;
+    }
+  }
+
+  if (opt_cmd == OPT_OBJECTS_EXPIRE_STALE_RM) {
+    ret = RGWBucketAdminOp::fix_obj_expiry(store, bucket_op, f, false);
+    if (ret < 0) {
+      cerr << "ERROR: removing returned " << cpp_strerror(-ret) << std::endl;
+      return -ret;
+    }
+  }
+
   if (opt_cmd == OPT_BUCKET_REWRITE) {
     if (bucket_name.empty()) {
       cerr << "ERROR: bucket not specified" << std::endl;
index a8f491943237d405a6316717b6fbf9f41438fda3..df154df8f0c6840987672882b35b82a4c039ebdf 100644 (file)
@@ -1212,31 +1212,26 @@ int RGWBucket::check_index(RGWBucketAdminOpState& op_state,
   return 0;
 }
 
-
 int RGWBucket::policy_bl_to_stream(bufferlist& bl, ostream& o)
 {
   RGWAccessControlPolicy_S3 policy(g_ceph_context);
-  auto iter = bl.cbegin();
-  try {
-    policy.decode(iter);
-  } catch (buffer::error& err) {
-    dout(0) << "ERROR: caught buffer::error, could not decode policy" << dendl;
-    return -EIO;
+  int ret = decode_bl(bl, policy);
+  if (ret < 0) {
+    ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl;
   }
   policy.to_xml(o);
   return 0;
 }
 
-static int policy_decode(RGWRados *store, bufferlist& bl, RGWAccessControlPolicy& policy)
+int rgw_object_get_attr(RGWRados* store, const RGWBucketInfo& bucket_info,
+                       const rgw_obj& obj, const char* attr_name,
+                       bufferlist& out_bl)
 {
-  auto iter = bl.cbegin();
-  try {
-    policy.decode(iter);
-  } catch (buffer::error& err) {
-    ldout(store->ctx(), 0) << "ERROR: caught buffer::error, could not decode policy" << dendl;
-    return -EIO;
-  }
-  return 0;
+  RGWObjectCtx obj_ctx(store);
+  RGWRados::Object op_target(store, bucket_info, obj_ctx, obj);
+  RGWRados::Object::Read rop(&op_target);
+
+  return rop.get_attr(attr_name, out_bl);
 }
 
 int RGWBucket::get_policy(RGWBucketAdminOpState& op_state, RGWAccessControlPolicy& policy)
@@ -1244,7 +1239,6 @@ int RGWBucket::get_policy(RGWBucketAdminOpState& op_state, RGWAccessControlPolic
   std::string object_name = op_state.get_object_name();
   rgw_bucket bucket = op_state.get_bucket();
   auto sysobj_ctx = store->svc.sysobj->init_obj_ctx();
-  RGWObjectCtx obj_ctx(store);
 
   RGWBucketInfo bucket_info;
   map<string, bufferlist> attrs;
@@ -1257,14 +1251,16 @@ int RGWBucket::get_policy(RGWBucketAdminOpState& op_state, RGWAccessControlPolic
     bufferlist bl;
     rgw_obj obj(bucket, object_name);
 
-    RGWRados::Object op_target(store, bucket_info, obj_ctx, obj);
-    RGWRados::Object::Read rop(&op_target);
-
-    int ret = rop.get_attr(RGW_ATTR_ACL, bl);
-    if (ret < 0)
+    ret = rgw_object_get_attr(store, bucket_info, obj, RGW_ATTR_ACL, bl);
+    if (ret < 0){
       return ret;
+    }
 
-    return policy_decode(store, bl, policy);
+    ret = decode_bl(bl, policy);
+    if (ret < 0) {
+      ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl;
+    }
+    return ret;
   }
 
   map<string, bufferlist>::iterator aiter = attrs.find(RGW_ATTR_ACL);
@@ -1272,7 +1268,12 @@ int RGWBucket::get_policy(RGWBucketAdminOpState& op_state, RGWAccessControlPolic
     return -ENOENT;
   }
 
-  return policy_decode(store, aiter->second, policy);
+  ret = decode_bl(aiter->second, policy);
+  if (ret < 0) {
+    ldout(store->ctx(),0) << "failed to decode RGWAccessControlPolicy" << dendl;
+  }
+
+  return ret;
 }
 
 
@@ -1966,6 +1967,95 @@ int RGWBucketAdminOp::fix_lc_shards(RGWRados *store,
 
 }
 
+static bool has_object_expired(RGWRados *store, const RGWBucketInfo& bucket_info,
+                              const rgw_obj_key& key, utime_t& delete_at)
+{
+  rgw_obj obj(bucket_info.bucket, key);
+  bufferlist delete_at_bl;
+
+  int ret = rgw_object_get_attr(store, bucket_info, obj, RGW_ATTR_DELETE_AT, delete_at_bl);
+  if (ret < 0) {
+    return false;  // no delete at attr, proceed
+  }
+
+  ret = decode_bl(delete_at_bl, delete_at);
+  if (ret < 0) {
+    return false;  // failed to parse
+  }
+
+  if (delete_at <= ceph_clock_now() && !delete_at.is_zero()) {
+    return true;
+  }
+
+  return false;
+}
+
+static int fix_bucket_obj_expiry(RGWRados *store, const RGWBucketInfo& bucket_info,
+                                RGWFormatterFlusher& flusher, bool dry_run)
+{
+  if (bucket_info.bucket.bucket_id == bucket_info.bucket.marker) {
+    lderr(store->ctx()) << "Not a resharded bucket skipping" << dendl;
+    return 0;  // not a resharded bucket, move along
+  }
+
+  Formatter *formatter = flusher.get_formatter();
+  formatter->open_array_section("expired_deletion_status");
+  auto sg = make_scope_guard([&formatter] {
+                              formatter->close_section();
+                              formatter->flush(std::cout);
+                            });
+
+  RGWRados::Bucket target(store, bucket_info);
+  RGWRados::Bucket::List list_op(&target);
+
+  list_op.params.list_versions = bucket_info.versioned();
+  list_op.params.allow_unordered = true;
+
+  constexpr auto max_objects = 1000;
+  bool is_truncated {false};
+  do {
+    std::vector<rgw_bucket_dir_entry> objs;
+
+    int ret = list_op.list_objects(max_objects, &objs, nullptr, &is_truncated);
+    if (ret < 0) {
+      lderr(store->ctx()) << "ERROR failed to list objects in the bucket" << dendl;
+      return ret;
+    }
+    for (const auto& obj : objs) {
+      rgw_obj_key key(obj.key);
+      utime_t delete_at;
+      if (has_object_expired(store, bucket_info, key, delete_at)) {
+       formatter->open_object_section("object_status");
+       formatter->dump_string("object", key.name);
+       formatter->dump_stream("delete_at") << delete_at;
+
+       if (!dry_run) {
+         ret = rgw_remove_object(store, bucket_info, bucket_info.bucket, key);
+         formatter->dump_int("status", ret);
+       }
+
+       formatter->close_section();  // object_status
+      }
+    }
+    formatter->flush(cout); // regularly flush every 1k entries
+  } while (is_truncated);
+
+  return 0;
+}
+
+int RGWBucketAdminOp::fix_obj_expiry(RGWRados *store, RGWBucketAdminOpState& op_state,
+                                    RGWFormatterFlusher& flusher, bool dry_run)
+{
+  RGWBucket admin_bucket;
+  int ret = admin_bucket.init(store, op_state);
+  if (ret < 0) {
+    lderr(store->ctx()) << "failed to initialize bucket" << dendl;
+    return ret;
+  }
+
+  return fix_bucket_obj_expiry(store, admin_bucket.get_bucket_info(), flusher, dry_run);
+}
+
 void rgw_data_change::dump(Formatter *f) const
 {
   string type;
index 2c483c0d3aba6bedacd4004e947310033465942d..fec77ba39b3cd6692d574a651765fa15ba67515c 100644 (file)
@@ -215,6 +215,9 @@ extern int rgw_remove_bucket_bypass_gc(RGWRados *store, rgw_bucket& bucket, int
 extern int rgw_bucket_set_attrs(RGWRados *store, RGWBucketInfo& bucket_info,
                                 map<string, bufferlist>& attrs,
                                 RGWObjVersionTracker *objv_tracker);
+extern int rgw_object_get_attr(RGWRados* store, const RGWBucketInfo& bucket_info,
+                               const rgw_obj& obj, const char* attr_name,
+                               bufferlist& out_bl);
 
 extern void check_bad_user_bucket_mapping(RGWRados *store, const rgw_user& user_id, bool fix);
 
@@ -333,6 +336,8 @@ public:
   int get_policy(RGWBucketAdminOpState& op_state, RGWAccessControlPolicy& policy);
 
   void clear_failure() { failure = false; }
+
+  const RGWBucketInfo& get_bucket_info() const { return bucket_info; }
 };
 
 class RGWBucketAdminOp
@@ -367,6 +372,8 @@ public:
                                   RGWFormatterFlusher& flusher);
   static int fix_lc_shards(RGWRados *store, RGWBucketAdminOpState& op_state,
                            RGWFormatterFlusher& flusher);
+  static int fix_obj_expiry(RGWRados *store, RGWBucketAdminOpState& op_state,
+                           RGWFormatterFlusher& flusher, bool dry_run = false);
 };
 
 
index 1aa039e06175c1def26f092fdb67fc971694fcd0..9ae2580e916141e46637dc94cbfd18b3df542d0b 100644 (file)
@@ -38,6 +38,8 @@
     object unlink              unlink object from bucket index
     object rewrite             rewrite the specified object
     objects expire             run expired objects cleanup
+    objects expire-stale list  list stale expired objects (caused by reshard)
+    objects expire-stale rm    remove stale expired objects
     period rm                  remove a period
     period get                 get period info
     period get-current         get current period info