From: Abhishek Lekshmanan Date: Mon, 29 Apr 2019 15:30:46 +0000 (+0200) Subject: rgw: admin: introduce objects expire-stale list and rm X-Git-Tag: v15.1.0~2690^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=d4c462a94c8308d5f45de32c2d0d25c7374c9613;p=ceph.git rgw: admin: introduce objects expire-stale list and rm 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 --- diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc index 6457050511e..b554d324ae8 100644 --- a/src/rgw/rgw_admin.cc +++ b/src/rgw/rgw_admin.cc @@ -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; diff --git a/src/rgw/rgw_bucket.cc b/src/rgw/rgw_bucket.cc index a8f49194323..df154df8f0c 100644 --- a/src/rgw/rgw_bucket.cc +++ b/src/rgw/rgw_bucket.cc @@ -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 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::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 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; diff --git a/src/rgw/rgw_bucket.h b/src/rgw/rgw_bucket.h index 2c483c0d3ab..fec77ba39b3 100644 --- a/src/rgw/rgw_bucket.h +++ b/src/rgw/rgw_bucket.h @@ -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& 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); }; diff --git a/src/test/cli/radosgw-admin/help.t b/src/test/cli/radosgw-admin/help.t index 1aa039e0617..9ae2580e916 100644 --- a/src/test/cli/radosgw-admin/help.t +++ b/src/test/cli/radosgw-admin/help.t @@ -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