From 7d48f62f5c86913d8f00b44d46a04a52d338907c Mon Sep 17 00:00:00 2001 From: Ji Chen Date: Wed, 21 Oct 2015 10:10:39 +0800 Subject: [PATCH] LifeCycle feature As same as amazon S3 interface,"PUT Bucket lifecycle" and "DELETE Bucket lifecycle" have been implemented, "GET Bucket lifecycle" not realized yet as S3cmd has not realize it also. The feature`s main point is to remove expire file per day. Files transfer from hot layer to cold layer is not supported. ToDo:Maybe to transfer from replicate pool to EC pool or from ssd to sata pool will be valuable. Now put all buckets which should do lifecycle into shard objects in .rgw.lc pool. lifecycle config file format: sample-rule enable 1 Signed-off-by: Ji Chen --- src/CMakeLists.txt | 2 + src/cls/rgw/cls_rgw.cc | 150 ++++++++ src/cls/rgw/cls_rgw_client.cc | 90 +++++ src/cls/rgw/cls_rgw_client.h | 13 + src/cls/rgw/cls_rgw_ops.h | 134 ++++++++ src/cls/rgw/cls_rgw_types.h | 24 ++ src/common/config_opts.h | 7 + src/rgw/Makefile.am | 4 + src/rgw/rgw_admin.cc | 40 ++- src/rgw/rgw_common.cc | 1 + src/rgw/rgw_common.h | 1 + src/rgw/rgw_lc.cc | 535 +++++++++++++++++++++++++++++ src/rgw/rgw_lc.h | 227 ++++++++++++ src/rgw/rgw_lc_s3.cc | 112 ++++++ src/rgw/rgw_lc_s3.h | 104 ++++++ src/rgw/rgw_main.cc | 2 +- src/rgw/rgw_object_expirer.cc | 2 +- src/rgw/rgw_op.cc | 169 ++++++++- src/rgw/rgw_op.h | 52 +++ src/rgw/rgw_rados.cc | 47 ++- src/rgw/rgw_rados.h | 26 +- src/rgw/rgw_rest.cc | 24 ++ src/rgw/rgw_rest.h | 15 + src/rgw/rgw_rest_s3.cc | 25 ++ src/rgw/rgw_rest_s3.h | 19 + src/test/cli/radosgw-admin/help.t | 2 + src/test/test_rgw_admin_opstate.cc | 2 +- 27 files changed, 1817 insertions(+), 12 deletions(-) create mode 100644 src/rgw/rgw_lc.cc create mode 100644 src/rgw/rgw_lc.h create mode 100644 src/rgw/rgw_lc_s3.cc create mode 100644 src/rgw/rgw_lc_s3.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0d90ee231c28..554b015bd02f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1007,6 +1007,8 @@ if(${WITH_RADOSGW}) rgw/rgw_acl.cc rgw/rgw_acl_s3.cc rgw/rgw_acl_swift.cc + rgw/rgw_lc.cc + rgw/rgw_lc_s3.cc rgw/rgw_client_io.cc rgw/rgw_fcgi.cc rgw/rgw_xml.cc diff --git a/src/cls/rgw/cls_rgw.cc b/src/cls/rgw/cls_rgw.cc index b4892fde9d67..488dad929fa6 100644 --- a/src/cls/rgw/cls_rgw.cc +++ b/src/cls/rgw/cls_rgw.cc @@ -48,6 +48,12 @@ cls_method_handle_t h_rgw_user_usage_log_trim; cls_method_handle_t h_rgw_gc_set_entry; cls_method_handle_t h_rgw_gc_list; cls_method_handle_t h_rgw_gc_remove; +cls_method_handle_t h_rgw_lc_set_entry; +cls_method_handle_t h_rgw_lc_rm_entry; +cls_method_handle_t h_rgw_lc_get_entry; +cls_method_handle_t h_rgw_lc_put_head; +cls_method_handle_t h_rgw_lc_get_head; +cls_method_handle_t h_rgw_lc_list_entry; #define ROUND_BLOCK_SIZE 4096 @@ -3054,6 +3060,142 @@ static int rgw_cls_gc_remove(cls_method_context_t hctx, bufferlist *in, bufferli return gc_remove(hctx, op.tags); } +static int rgw_cls_lc_set_entry(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_rgw_lc_set_entry_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: rgw_cls_lc_set_entry(): failed to decode entry\n"); + return -EINVAL; + } + + bufferlist bl; + ::encode(op.entry, bl); + + int ret = cls_cxx_map_set_val(hctx, op.entry.first, &bl); + return ret; +} + +static int rgw_cls_lc_rm_entry(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_rgw_lc_rm_entry_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: rgw_cls_lc_rm_entry(): failed to decode entry\n"); + return -EINVAL; + } + + bufferlist bl; + ::encode(op.entry, bl); + + int ret = cls_cxx_map_remove_key(hctx, op.entry.first); + return ret; +} + +static int rgw_cls_lc_get_entry(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + cls_rgw_lc_get_entry_ret op_ret; + cls_rgw_lc_get_entry_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: rgw_cls_lc_rm_entry(): failed to decode entry\n"); + return -EINVAL; + } + + map vals; + string filter_prefix; + int ret = cls_cxx_map_get_vals(hctx, op.marker, filter_prefix, 1, &vals); + if (ret < 0) + return ret; + map::iterator it; + pair entry; + if (!vals.empty()) { + it=vals.begin(); + in_iter = it->second.begin(); + try { + ::decode(entry, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: rgw_cls_lc_get_entry(): failed to decode entry\n"); + return -EINVAL; + } + } + op_ret.entry = entry; + ::encode(op_ret, *out); + return 0; +} + +static int rgw_cls_lc_list_entry(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + cls_rgw_lc_list_entry_ret op_ret; + bufferlist::iterator iter; + map vals; + int ret = cls_cxx_map_get_all_vals(hctx, &vals); + if (ret < 0) + return ret; + map::iterator it; + pair entry; + for (it = vals.begin(); it != vals.end(); it++) { + iter = it->second.begin(); + try { + ::decode(entry, iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: rgw_cls_lc_list_entry(): failed to decode entry\n"); + return -EINVAL; + } + op_ret.entries.insert(entry); + } + ::encode(op_ret, *out); + return 0; +} + +static int rgw_cls_lc_put_head(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_rgw_lc_put_head_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: rgw_cls_lc_set_entry(): failed to decode entry\n"); + return -EINVAL; + } + + bufferlist bl; + ::encode(op.head, bl); + int ret = cls_cxx_map_write_header(hctx,&bl); + return ret; +} + +static int rgw_cls_lc_get_head(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist bl; + int ret = cls_cxx_map_read_header(hctx, &bl); + if (ret < 0) + return ret; + cls_rgw_lc_obj_head head; + if (bl.length() != 0) { + bufferlist::iterator iter = bl.begin(); + try { + ::decode(head, iter); + } catch (buffer::error& err) { + CLS_LOG(0, "ERROR: rgw_cls_lc_get_head(): failed to decode entry %s\n",err.what()); + return -EINVAL; + } + } + cls_rgw_lc_get_head_ret op_ret; + op_ret.head = head; + ::encode(op_ret, *out); + return 0; +} + void __cls_init() { CLS_LOG(1, "Loaded rgw class!"); @@ -3096,6 +3238,14 @@ void __cls_init() cls_register_cxx_method(h_class, "gc_list", CLS_METHOD_RD, rgw_cls_gc_list, &h_rgw_gc_list); cls_register_cxx_method(h_class, "gc_remove", CLS_METHOD_RD | CLS_METHOD_WR, rgw_cls_gc_remove, &h_rgw_gc_remove); + /* lifecycle bucket list */ + cls_register_cxx_method(h_class, "lc_set_entry", CLS_METHOD_RD | CLS_METHOD_WR, rgw_cls_lc_set_entry, &h_rgw_lc_set_entry); + cls_register_cxx_method(h_class, "lc_rm_entry", CLS_METHOD_RD | CLS_METHOD_WR, rgw_cls_lc_rm_entry, &h_rgw_lc_rm_entry); + cls_register_cxx_method(h_class, "lc_get_entry", CLS_METHOD_RD, rgw_cls_lc_get_entry, &h_rgw_lc_get_entry); + cls_register_cxx_method(h_class, "lc_put_head", CLS_METHOD_RD| CLS_METHOD_WR, rgw_cls_lc_put_head, &h_rgw_lc_put_head); + cls_register_cxx_method(h_class, "lc_get_head", CLS_METHOD_RD, rgw_cls_lc_get_head, &h_rgw_lc_get_head); + cls_register_cxx_method(h_class, "lc_list_entry", CLS_METHOD_RD, rgw_cls_lc_list_entry, &h_rgw_lc_list_entry); + return; } diff --git a/src/cls/rgw/cls_rgw_client.cc b/src/cls/rgw/cls_rgw_client.cc index e6ac56b822c3..8fcbe4db2620 100644 --- a/src/cls/rgw/cls_rgw_client.cc +++ b/src/cls/rgw/cls_rgw_client.cc @@ -627,3 +627,93 @@ void cls_rgw_gc_remove(librados::ObjectWriteOperation& op, const list& t ::encode(call, in); op.exec("rgw", "gc_remove", in); } + +int cls_rgw_lc_get_head(IoCtx& io_ctx, string& oid, cls_rgw_lc_obj_head& head) +{ + bufferlist in, out; + int r = io_ctx.exec(oid, "rgw", "lc_get_head", in, out); + if (r < 0) + return r; + + cls_rgw_lc_get_head_ret ret; + try { + bufferlist::iterator iter = out.begin(); + ::decode(ret, iter); + } catch (buffer::error& err) { + return -EIO; + } + head = ret.head; + + return r; +} + +int cls_rgw_lc_put_head(IoCtx& io_ctx, string& oid, cls_rgw_lc_obj_head& head) +{ + bufferlist in, out; + cls_rgw_lc_put_head_op call; + call.head = head; + ::encode(call, in); + int r = io_ctx.exec(oid, "rgw", "lc_put_head", in, out); + return r; +} + +int cls_rgw_lc_get_entry(IoCtx& io_ctx, string& oid, string& marker, pair& entry) +{ + bufferlist in, out; + cls_rgw_lc_get_entry_op call; + call.marker = marker; + ::encode(call, in); + int r = io_ctx.exec(oid, "rgw", "lc_get_entry", in, out); + if (r < 0) + return r; + + cls_rgw_lc_get_entry_ret ret; + try { + bufferlist::iterator iter = out.begin(); + ::decode(ret, iter); + } catch (buffer::error& err) { + return -EIO; + } + entry = ret.entry; + + return r; +} + +int cls_rgw_lc_rm_entry(IoCtx& io_ctx, string& oid, pair& entry) +{ + bufferlist in, out; + cls_rgw_lc_rm_entry_op call; + call.entry = entry; + ::encode(call, in); + int r = io_ctx.exec(oid, "rgw", "lc_rm_entry", in, out); + return r; +} + +int cls_rgw_lc_set_entry(IoCtx& io_ctx, string& oid, pair& entry) +{ + bufferlist in, out; + cls_rgw_lc_rm_entry_op call; + call.entry = entry; + ::encode(call, in); + int r = io_ctx.exec(oid, "rgw", "lc_set_entry", in, out); + return r; +} + +int cls_rgw_lc_list(IoCtx& io_ctx, string& oid, map& entries) +{ + bufferlist in, out; + int r = io_ctx.exec(oid, "rgw", "lc_list_entry", in, out); + if (r < 0) + return r; + + cls_rgw_lc_list_entry_ret ret; + try { + bufferlist::iterator iter = out.begin(); + ::decode(ret, iter); + } catch (buffer::error& err) { + return -EIO; + } + entries.insert(ret.entries.begin(),ret.entries.end()); + + return r; +} diff --git a/src/cls/rgw/cls_rgw_client.h b/src/cls/rgw/cls_rgw_client.h index 37c856fa07f6..f5a8833acd75 100644 --- a/src/cls/rgw/cls_rgw_client.h +++ b/src/cls/rgw/cls_rgw_client.h @@ -471,4 +471,17 @@ int cls_rgw_gc_list(librados::IoCtx& io_ctx, string& oid, string& marker, uint32 void cls_rgw_gc_remove(librados::ObjectWriteOperation& op, const list& tags); +/* lifecycle */ +int cls_rgw_lc_get_head(librados::IoCtx& io_ctx, string& oid, cls_rgw_lc_obj_head& head); +int cls_rgw_lc_put_head(librados::IoCtx& io_ctx, string& oid, cls_rgw_lc_obj_head& head); +int cls_rgw_lc_get_entry(librados::IoCtx& io_ctx, string& oid, string& marker, pair& entry); +int cls_rgw_lc_rm_entry(librados::IoCtx& io_ctx, string& oid, pair& entry); +int cls_rgw_lc_set_entry(librados::IoCtx& io_ctx, string& oid, pair& entry); +int cls_rgw_lc_list(librados::IoCtx& io_ctx, string& oid, map& entries); + + + + + + #endif diff --git a/src/cls/rgw/cls_rgw_ops.h b/src/cls/rgw/cls_rgw_ops.h index 0a0686fbccb1..ce8f6f1053d6 100644 --- a/src/cls/rgw/cls_rgw_ops.h +++ b/src/cls/rgw/cls_rgw_ops.h @@ -861,5 +861,139 @@ struct cls_rgw_bi_log_list_ret { }; WRITE_CLASS_ENCODER(cls_rgw_bi_log_list_ret) +struct cls_rgw_lc_get_entry_op { + string marker; + cls_rgw_lc_get_entry_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(marker, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(marker, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_rgw_lc_get_entry_op) + +struct cls_rgw_lc_get_entry_ret { + pair entry; + + cls_rgw_lc_get_entry_ret() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(entry, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(entry, bl); + DECODE_FINISH(bl); + } + +}; +WRITE_CLASS_ENCODER(cls_rgw_lc_get_entry_ret) + +struct cls_rgw_lc_rm_entry_op { + pair entry; + cls_rgw_lc_rm_entry_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(entry, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(entry, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_rgw_lc_rm_entry_op) + +struct cls_rgw_lc_set_entry_op { + pair entry; + cls_rgw_lc_set_entry_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(entry, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(entry, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_rgw_lc_set_entry_op) + +struct cls_rgw_lc_put_head_op { + cls_rgw_lc_obj_head head; + + + cls_rgw_lc_put_head_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(head, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(head, bl); + DECODE_FINISH(bl); + } + +}; +WRITE_CLASS_ENCODER(cls_rgw_lc_put_head_op) + +struct cls_rgw_lc_get_head_ret { + cls_rgw_lc_obj_head head; + + cls_rgw_lc_get_head_ret() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(head, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(head, bl); + DECODE_FINISH(bl); + } + +}; +WRITE_CLASS_ENCODER(cls_rgw_lc_get_head_ret) + +struct cls_rgw_lc_list_entry_ret { + map entries; + + cls_rgw_lc_list_entry_ret() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(entries, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(entries, bl); + DECODE_FINISH(bl); + } + +}; +WRITE_CLASS_ENCODER(cls_rgw_lc_list_entry_ret) #endif diff --git a/src/cls/rgw/cls_rgw_types.h b/src/cls/rgw/cls_rgw_types.h index dfa9286c0008..812cc38cb09b 100644 --- a/src/cls/rgw/cls_rgw_types.h +++ b/src/cls/rgw/cls_rgw_types.h @@ -891,4 +891,28 @@ struct cls_rgw_gc_obj_info }; WRITE_CLASS_ENCODER(cls_rgw_gc_obj_info) +struct cls_rgw_lc_obj_head +{ + time_t start_date; + string marker; + + cls_rgw_lc_obj_head() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(start_date, bl); + ::encode(marker, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(start_date, bl); + ::decode(marker, bl); + DECODE_FINISH(bl); + } + +}; +WRITE_CLASS_ENCODER(cls_rgw_lc_obj_head) + #endif diff --git a/src/common/config_opts.h b/src/common/config_opts.h index 1a7fe61ef8b6..2a72f5b5d890 100644 --- a/src/common/config_opts.h +++ b/src/common/config_opts.h @@ -1059,6 +1059,8 @@ OPTION(rgw_bucket_index_max_aio, OPT_U32, 8) */ OPTION(rgw_enable_quota_threads, OPT_BOOL, true) OPTION(rgw_enable_gc_threads, OPT_BOOL, true) +OPTION(rgw_enable_lc_threads, OPT_BOOL, true) + OPTION(rgw_data, OPT_STR, "/var/lib/ceph/radosgw/$cluster-$id") OPTION(rgw_enable_apis, OPT_STR, "s3, swift, swift_auth, admin") @@ -1069,6 +1071,11 @@ OPTION(rgw_host, OPT_STR, "") // host for radosgw, can be an IP, default is 0.0 OPTION(rgw_port, OPT_STR, "") // port to listen, format as "8080" "5000", if not specified, rgw will not run external fcgi OPTION(rgw_dns_name, OPT_STR, "") OPTION(rgw_content_length_compat, OPT_BOOL, false) // Check both HTTP_CONTENT_LENGTH and CONTENT_LENGTH in fcgi env +OPTION(rgw_lifecycle_enabled, OPT_BOOL, true) //rgw lifecycle enabled +OPTION(rgw_lifecycle_thread, OPT_INT, 1) //start lifecycle thread number per radosgw +OPTION(rgw_lifecycle_work_time, OPT_STR, "00:00-06:00") //job process lc at 00:00-06:00s +OPTION(rgw_lc_lock_max_time, OPT_INT, 60) // total run time for a single gc processor work +OPTION(rgw_lc_max_objs, OPT_INT, 32) OPTION(rgw_script_uri, OPT_STR, "") // alternative value for SCRIPT_URI if not set in request OPTION(rgw_request_uri, OPT_STR, "") // alternative value for REQUEST_URI if not set in request OPTION(rgw_swift_url, OPT_STR, "") // the swift url, being published by the internal swift auth diff --git a/src/rgw/Makefile.am b/src/rgw/Makefile.am index 98cd4b00484f..c3f835b80a58 100644 --- a/src/rgw/Makefile.am +++ b/src/rgw/Makefile.am @@ -17,6 +17,8 @@ librgw_la_SOURCES = \ rgw/rgw_acl.cc \ rgw/rgw_acl_s3.cc \ rgw/rgw_acl_swift.cc \ + rgw/rgw_lc.cc \ + rgw/rgw_lc_s3.cc \ rgw/rgw_client_io.cc \ rgw/rgw_fcgi.cc \ rgw/rgw_xml.cc \ @@ -128,6 +130,8 @@ noinst_HEADERS += \ rgw/rgw_acl.h \ rgw/rgw_acl_s3.h \ rgw/rgw_acl_swift.h \ + rgw/rgw_lc.h \ + rgw/rgw_lc_s3.h \ rgw/rgw_client_io.h \ rgw/rgw_fcgi.h \ rgw/rgw_xml.h \ diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc index 4eee6edb0272..b904bdc02930 100644 --- a/src/rgw/rgw_admin.cc +++ b/src/rgw/rgw_admin.cc @@ -27,6 +27,7 @@ using namespace std; #include "rgw_rados.h" #include "rgw_acl.h" #include "rgw_acl_s3.h" +#include "rgw_lc.h" #include "rgw_log.h" #include "rgw_formats.h" #include "rgw_usage.h" @@ -95,6 +96,8 @@ void _usage() cout << " gc list dump expired garbage collection objects (specify\n"; cout << " --include-all to list all entries, including unexpired)\n"; cout << " gc process manually process garbage\n"; + cout << " lc list list all bucket lifecycle progress\n"; + cout << " lc process manually process lifecycle\n"; cout << " metadata get get metadata info\n"; cout << " metadata put put metadata info\n"; cout << " metadata rm remove metadata info\n"; @@ -239,6 +242,8 @@ enum { OPT_QUOTA_DISABLE, OPT_GC_LIST, OPT_GC_PROCESS, + OPT_LC_LIST, + OPT_LC_PROCESS, OPT_ORPHANS_FIND, OPT_ORPHANS_FINISH, OPT_REGION_GET, @@ -285,6 +290,7 @@ static int get_cmd(const char *cmd, const char *prev_cmd, bool *need_more) strcmp(cmd, "gc") == 0 || strcmp(cmd, "key") == 0 || strcmp(cmd, "log") == 0 || + strcmp(cmd, "lc") == 0 || strcmp(cmd, "mdlog") == 0 || strcmp(cmd, "metadata") == 0 || strcmp(cmd, "object") == 0 || @@ -455,6 +461,11 @@ static int get_cmd(const char *cmd, const char *prev_cmd, bool *need_more) return OPT_GC_LIST; if (strcmp(cmd, "process") == 0) return OPT_GC_PROCESS; + } else if (strcmp(prev_cmd, "lc") == 0) { + if (strcmp(cmd, "list") == 0) + return OPT_LC_LIST; + if (strcmp(cmd, "process") == 0) + return OPT_LC_PROCESS; } else if (strcmp(prev_cmd, "orphans") == 0) { if (strcmp(cmd, "find") == 0) return OPT_ORPHANS_FIND; @@ -1477,7 +1488,7 @@ int main(int argc, char **argv) if (raw_storage_op) { store = RGWStoreManager::get_raw_storage(g_ceph_context); } else { - store = RGWStoreManager::get_storage(g_ceph_context, false, false); + store = RGWStoreManager::get_storage(g_ceph_context, false, false, false); } if (!store) { cerr << "couldn't init storage provider" << std::endl; @@ -2655,6 +2666,33 @@ next: } } + if (opt_cmd == OPT_LC_LIST) { + formatter->open_array_section("life cycle progress"); + map bucket_lc_map; + int ret = store->list_lc_progress(bucket_lc_map); + if (ret < 0) { + cerr << "ERROR: failed to list objs: " << cpp_strerror(-ret) << std::endl; + return 1; + } + map::iterator iter; + for (iter = bucket_lc_map.begin(); iter != bucket_lc_map.end(); ++iter) { + formatter->open_object_section("bucket_lc_info"); + formatter->dump_string("bucket", iter->first); + string lc_status = LC_STATUS[iter->second]; + formatter->dump_string("status", lc_status); + formatter->close_section(); // objs + formatter->flush(cout); + } + } + + if (opt_cmd == OPT_LC_PROCESS) { + int ret = store->process_lc(); + if (ret < 0) { + cerr << "ERROR: lc processing returned error: " << cpp_strerror(-ret) << std::endl; + return 1; + } + } + if (opt_cmd == OPT_ORPHANS_FIND) { RGWOrphanSearch search(store, max_concurrent_ios, orphan_stale_secs); diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index bb8ecf384e0e..6a050e9857ed 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -602,6 +602,7 @@ int RGWHTTPArgs::parse() } if ((name.compare("acl") == 0) || + (name.compare("lifecycle") == 0) || (name.compare("cors") == 0) || (name.compare("location") == 0) || (name.compare("logging") == 0) || diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index bff070a06ae4..e3ddf4e4af1a 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -56,6 +56,7 @@ using ceph::crypto::MD5; #define RGW_SYS_PARAM_PREFIX "rgwx-" #define RGW_ATTR_ACL RGW_ATTR_PREFIX "acl" +#define RGW_ATTR_LC RGW_ATTR_PREFIX "lc" #define RGW_ATTR_CORS RGW_ATTR_PREFIX "cors" #define RGW_ATTR_ETAG RGW_ATTR_PREFIX "etag" #define RGW_ATTR_BUCKETS RGW_ATTR_PREFIX "buckets" diff --git a/src/rgw/rgw_lc.cc b/src/rgw/rgw_lc.cc new file mode 100644 index 000000000000..99d79a97dcc5 --- /dev/null +++ b/src/rgw/rgw_lc.cc @@ -0,0 +1,535 @@ +#include +#include +#include + +#include "include/types.h" + +#include "common/Formatter.h" +#include +#include "auth/Crypto.h" +#include "include/rados/librados.hpp" +#include "cls/rgw/cls_rgw_client.h" +#include "cls/refcount/cls_refcount_client.h" +#include "cls/lock/cls_lock_client.h" +#include +#include "rgw_common.h" +#include "rgw_bucket.h" +#include "rgw_lc.h" +#include "rgw_lc_s3.h" + + + +#define dout_subsys ceph_subsys_rgw + +const char* LC_STATUS[] = { + "UNINITIAL", + "PROCESSING", + "FAILED", + "COMPLETE" +}; + +using namespace std; +using namespace librados; +void RGWLifecycleConfiguration::add_rule(LCRule *rule) +{ + string id; + rule->get_id(id); // not that this will return false for groups, but that's ok, we won't search groups + rule_map.insert(pair(id, *rule)); + _add_rule(rule); +} + +void RGWLifecycleConfiguration::_add_rule(LCRule *rule) +{ + string prefix; + LCExpiration expiration; + int days; + if (!rule->get_prefix(prefix)) { + ldout(cct, 5) << "ERROR: rule->get_prefix() failed" << dendl; + } + if (!rule->get_expiration(expiration)) { + ldout(cct, 5) << "ERROR: rule->get_expiration() failed" << dendl; + } + if (!expiration.get_days(&days)) { + ldout(cct, 5) << "ERROR: expiration->get_days() failed" << dendl; + } + prefix_map[prefix] = days; +} + +void *RGWLC::LCWorker::entry() { + do { + utime_t start = ceph_clock_now(cct); + if (should_work(start)) { + dout(5) << "life cycle: start" << dendl; + int r = lc->process(); + if (r < 0) { + dout(0) << "ERROR: do life cycle process() returned error r=" << r << dendl; + } + dout(5) << "life cycle: stop" << dendl; + } + if (lc->going_down()) + break; + + utime_t end = ceph_clock_now(cct); + int secs = shedule_next_start_time(end); + time_t next_time = end + secs; + char buf[30]; + char *nt = ctime_r(&next_time, buf); + dout(5) << "shedule life cycle next start time: " << nt <going_down()); + + return NULL; +} + +void RGWLC::initialize(CephContext *_cct, RGWRados *_store) { + cct = _cct; + store = _store; + max_objs = cct->_conf->rgw_lc_max_objs; + if (max_objs > HASH_PRIME) + max_objs = HASH_PRIME; + + obj_names = new string[max_objs]; + + for (int i = 0; i < max_objs; i++) { + obj_names[i] = lc_oid_prefix; + char buf[32]; + snprintf(buf, 32, ".%d", i); + obj_names[i].append(buf); + } +} + +void RGWLC::finalize() +{ + delete[] obj_names; +} + +bool RGWLC::if_already_run_today(time_t& start_date) +{ + struct tm bdt; + time_t begin_of_day; + utime_t now = ceph_clock_now(cct); + localtime_r(&start_date, &bdt); + bdt.tm_hour = 0; + bdt.tm_min = 0; + bdt.tm_sec = 0; + begin_of_day = mktime(&bdt); + if (now - begin_of_day < 24*60*60) + return true; + else + return false; +} + +static std::vector &split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +static std::vector split(const std::string &s, char delim) { + std::vector elems; + split(s, delim, elems); + return elems; +} + +int RGWLC::bucket_lc_prepare(int index) +{ + map entries; + int ret = cls_rgw_lc_list(store->lc_pool_ctx, obj_names[index], entries); + if (ret < 0) + return ret; + map::iterator iter; + for (iter = entries.begin(); iter != entries.end(); ++iter) { + pair entry(iter->first, lc_uninitial); + ret = cls_rgw_lc_set_entry(store->lc_pool_ctx, obj_names[index], entry); + if (ret < 0) { + dout(0) << "RGWLC::bucket_lc_prepare() failed to set entry " << obj_names[index] << dendl; + break; + } + } + return ret; +} +int RGWLC::bucket_lc_process(string& shard_id) +{ + RGWLifecycleConfiguration config(cct); + RGWBucketInfo bucket_info; + map bucket_attrs; + string prefix, delimiter, marker, next_marker, no_ns, end_marker, list_versions; + bool is_truncated; + bool default_config = false; + int default_days = 0; + vector objs; + RGWObjectCtx obj_ctx(store); + map common_prefixes; + vector result; + result = split(shard_id, ':'); + string bucket_tenant = result[0]; + string bucket_name = result[1]; + string bucket_id = result[2]; + int ret = store->get_bucket_info(obj_ctx, bucket_tenant, bucket_name, bucket_info, NULL, &bucket_attrs); + if (ret < 0) { + ldout(cct, 0) << "LC:get_bucket_info failed" << bucket_name <::iterator aiter = bucket_attrs.find(RGW_ATTR_LC); + if (aiter == bucket_attrs.end()) + return 0; + + bufferlist::iterator iter(&aiter->second); + try { + config.decode(iter); + } catch (const buffer::error& e) { + ldout(cct, 0) << __func__ << "decode life cycle config failed" << dendl; + return -1; + } + + map& prefix_map = config.get_prefix_map(); + for(map::iterator prefix_iter = prefix_map.begin(); prefix_iter != prefix_map.end(); prefix_iter++) { + if (prefix_iter->first.empty()) { + default_config = true; + default_days = prefix_iter->second; + continue; + } + } + if (default_config) { + do { + + objs.clear(); + list_op.params.marker = list_op.get_next_marker(); + ret = list_op.list_objects(1000, &objs, &common_prefixes, &is_truncated); + if (ret < 0) { + if (ret == -ENOENT) + return 0; + ldout(cct, 0) << "ERROR: store->list_objects():" <::iterator obj_iter; + int pos = 0; + utime_t now = ceph_clock_now(cct); + for (obj_iter = objs.begin(); obj_iter != objs.end(); obj_iter++) { + bool prefix_match = false; + int match_days = 0; + map& prefix_map = config.get_prefix_map(); + + for(map::iterator prefix_iter = prefix_map.begin(); prefix_iter != prefix_map.end(); prefix_iter++) { + if (prefix_iter->first.empty()) { + continue; + } + pos = (*obj_iter).key.name.find(prefix_iter->first, 0); + if (pos != 0) { + continue; + } + prefix_match = true; + match_days = prefix_iter->second; + break; + } + int days = 0; + if (prefix_match) { + days = match_days; + } else if (default_config) { + days = default_days; + } else { + continue; + } + if (now - (*obj_iter).mtime >= days*24*60*60) { + RGWObjectCtx rctx(store); + rgw_obj obj(bucket_info.bucket, (*obj_iter).key.name); + RGWObjState *state; + int ret = store->get_obj_state(&rctx, obj, &state, NULL, false); + if (ret < 0) { + return ret; + } + if (state->mtime != (*obj_iter).mtime) //Check mtime again to avoid delete a recently update object as much as possible + continue; + ret = rgw_remove_object(store, bucket_info, bucket_info.bucket, (*obj_iter).key); + if (ret < 0) { + ldout(cct, 0) << "ERROR: rgw_remove_object " << dendl; + } else { + ldout(cct, 10) << "DELETED:" << bucket_name << ":" << (*obj_iter).key.name <::iterator prefix_iter = prefix_map.begin(); prefix_iter != prefix_map.end(); prefix_iter++) { + if (prefix_iter->first.empty()) { + continue; + } + list_op.params.prefix = prefix_iter->first; + + do { + + objs.clear(); + list_op.params.marker = list_op.get_next_marker(); + ret = list_op.list_objects(1000, &objs, &common_prefixes, &is_truncated); + + if (ret < 0) { + if (ret == (-ENOENT)) + return 0; + ldout(cct, 0) << "ERROR: store->list_objects():" <::iterator obj_iter; + int days = prefix_iter->second; + utime_t now = ceph_clock_now(cct); + + for (obj_iter = objs.begin(); obj_iter != objs.end(); obj_iter++) { + if (now - (*obj_iter).mtime >= days*24*60*60) { + RGWObjectCtx rctx(store); + rgw_obj obj(bucket_info.bucket, (*obj_iter).key.name); + RGWObjState *state; + int ret = store->get_obj_state(&rctx, obj, &state, NULL, false); + if (ret < 0) { + return ret; + } + if (state->mtime != (*obj_iter).mtime)//Check mtime again to avoid delete a recently update object as much as possible + continue; + ret = rgw_remove_object(store, bucket_info, bucket_info.bucket, (*obj_iter).key); + if (ret < 0) { + ldout(cct, 0) << "ERROR: rgw_remove_object " << dendl; + } else { + ldout(cct, 10) << "DELETED:" << bucket_name << ":" << (*obj_iter).key.name << dendl; + } + } + } + } while (is_truncated); + } + } + + return ret; +} + +int RGWLC::bucket_lc_post(int index, int max_lock_sec, cls_rgw_lc_obj_head& head, + pair& entry, int& result) +{ + rados::cls::lock::Lock l(lc_index_lock_name); + do { + int ret = l.lock_exclusive(&store->lc_pool_ctx, obj_names[index]); + if (ret == -EBUSY) { /* already locked by another lc processor */ + dout(0) << "RGWLC::bucket_lc_post() failed to acquire lock on, sleep 5, try again" << obj_names[index] << dendl; + sleep(10); + continue; + } + if (ret < 0) + return 0; + dout(20) << "RGWLC::bucket_lc_post() get lock" << obj_names[index] << dendl; + if (result == -ENOENT) { + ret = cls_rgw_lc_rm_entry(store->lc_pool_ctx, obj_names[index], entry); + if (ret < 0) { + dout(0) << "RGWLC::bucket_lc_post() failed to remove entry " << obj_names[index] << dendl; + goto clean; + } + } else if (result < 0) { + entry.second = lc_failed; + } else { + entry.second = lc_complete; + } + + ret = cls_rgw_lc_set_entry(store->lc_pool_ctx, obj_names[index], entry); + if (ret < 0) { + dout(0) << "RGWLC::process() failed to set entry " << obj_names[index] << dendl; + } +clean: + l.unlock(&store->lc_pool_ctx, obj_names[index]); + dout(20) << "RGWLC::bucket_lc_post() unlock" << obj_names[index] << dendl; + return 0; + }while(1); +} + +int RGWLC::list_lc_progress(map& progress_map) +{ + int index = 0; + for(; index entries; + int ret = cls_rgw_lc_list(store->lc_pool_ctx, obj_names[index], entries); + if (ret < 0) + return ret; + map::iterator iter; + for (iter = entries.begin(); iter != entries.end(); ++iter) { + progress_map.insert(*iter); + } + } + return 0; +} + +int RGWLC::process() +{ + int max_secs = cct->_conf->rgw_lc_lock_max_time; + + unsigned start; + int ret = get_random_bytes((char *)&start, sizeof(start)); + if (ret < 0) + return ret; + + for (int i = 0; i < max_objs; i++) { + int index = (i + start) % max_objs; + ret = process(index, max_secs); + if (ret < 0) + return ret; + } + + return 0; +} + +int RGWLC::process(int index, int max_lock_secs) +{ + rados::cls::lock::Lock l(lc_index_lock_name); + do { + utime_t now = ceph_clock_now(g_ceph_context); + pair entry;//string = bucket_name:bucket_id ,int = LC_BUCKET_STATUS + if (max_lock_secs <= 0) + return -EAGAIN; + + utime_t time(max_lock_secs, 0); + l.set_duration(time); + + int ret = l.lock_exclusive(&store->lc_pool_ctx, obj_names[index]); + if (ret == -EBUSY) { /* already locked by another lc processor */ + dout(0) << "RGWLC::process() failed to acquire lock on, sleep 5, try again" << obj_names[index] << dendl; + sleep(10); + continue; + } + if (ret < 0) + return 0; + + string marker; + cls_rgw_lc_obj_head head; + ret = cls_rgw_lc_get_head(store->lc_pool_ctx, obj_names[index], head); + if (ret < 0) { + dout(0) << "RGWLC::process() failed to get obj head " << obj_names[index] << ret << dendl; + goto exit; + } + + if(!if_already_run_today(head.start_date)) { + head.start_date = now; + head.marker.clear(); + ret = bucket_lc_prepare(index); + if (ret < 0) { + dout(0) << "RGWLC::process() failed to update lc object " << obj_names[index] << ret << dendl; + goto exit; + } + } + + ret = cls_rgw_lc_get_entry(store->lc_pool_ctx, obj_names[index], head.marker, entry); + if (ret < 0) { + dout(0) << "RGWLC::process() failed to get obj entry " << obj_names[index] << dendl; + goto exit; + } + + if (entry.first.empty()) + goto exit; + + entry.second = lc_processing; + ret = cls_rgw_lc_set_entry(store->lc_pool_ctx, obj_names[index], entry); + if (ret < 0) { + dout(0) << "RGWLC::process() failed to set obj entry " << obj_names[index] << entry.first << entry.second << dendl; + goto exit; + } + + head.marker = entry.first; + ret = cls_rgw_lc_put_head(store->lc_pool_ctx, obj_names[index], head); + if (ret < 0) { + dout(0) << "RGWLC::process() failed to put head " << obj_names[index] << dendl; + goto exit; + } + l.unlock(&store->lc_pool_ctx, obj_names[index]); + ret = bucket_lc_process(entry.first); + ret = bucket_lc_post(index, max_lock_secs, head, entry, ret); + continue; +exit: + l.unlock(&store->lc_pool_ctx, obj_names[index]); + return 0; + + }while(1); + +} + +void RGWLC::start_processor() +{ + worker = new LCWorker(cct, this); + worker->create(); +} + +void RGWLC::stop_processor() +{ + if (worker) { + worker->stop(); + worker->join(); + } + delete worker; + worker = NULL; +} + +void RGWLC::LCWorker::stop() +{ + Mutex::Locker l(lock); + cond.Signal(); +} + +bool RGWLC::going_down() +{ + return false; +} + +bool RGWLC::LCWorker::should_work(utime_t& now) +{ + int start_hour; + int start_minite; + int end_hour; + int end_minite; + string worktime = cct->_conf->rgw_lifecycle_work_time; + sscanf(worktime.c_str(),"%d:%d-%d:%d",&start_hour, &start_minite, &end_hour, &end_minite); + struct tm bdt; + time_t tt = now.sec(); + localtime_r(&tt, &bdt); + if ((bdt.tm_hour*60 + bdt.tm_min >= start_hour*60 + start_minite)|| + (bdt.tm_hour*60 + bdt.tm_min <= end_hour*60 + end_minite)) { + return true; + } else { + return false; + } + +} + +int RGWLC::LCWorker::shedule_next_start_time(utime_t& now) +{ + int start_hour; + int start_minite; + int end_hour; + int end_minite; + string worktime = cct->_conf->rgw_lifecycle_work_time; + sscanf(worktime.c_str(),"%d:%d-%d:%d",&start_hour, &start_minite, &end_hour, &end_minite); + struct tm bdt; + time_t tt = now.sec(); + time_t nt; + localtime_r(&tt, &bdt); + bdt.tm_hour = start_hour; + bdt.tm_min = start_minite; + bdt.tm_sec = 0; + nt = mktime(&bdt); + return (nt+24*60*60 - tt); +} + diff --git a/src/rgw/rgw_lc.h b/src/rgw/rgw_lc.h new file mode 100644 index 000000000000..7d468b14374e --- /dev/null +++ b/src/rgw/rgw_lc.h @@ -0,0 +1,227 @@ +#ifndef CEPH_RGW_LC_H +#define CEPH_RGW_LC_H + +#include +#include +#include +#include + +#include "common/debug.h" + +#include "include/types.h" +#include "include/atomic.h" +#include "include/rados/librados.hpp" +#include "common/Mutex.h" +#include "common/Cond.h" +#include "common/Thread.h" +#include "rgw_common.h" +#include "rgw_rados.h" +#include "cls/rgw/cls_rgw_types.h" + +using namespace std; +#define HASH_PRIME 7877 +static string lc_oid_prefix = "lc"; +static string lc_index_lock_name = "lc_process"; + +extern const char* LC_STATUS[]; + +typedef enum { + lc_uninitial = 0, + lc_processing, + lc_failed, + lc_complete, +}LC_BUCKET_STATUS; + +class LCExpiration +{ +protected: + string days; +public: + LCExpiration() {} + ~LCExpiration() {} + + void encode(bufferlist& bl) const { + ENCODE_START(2, 2, bl); + ::encode(days, bl); + ENCODE_FINISH(bl); + } + void decode(bufferlist::iterator& bl) { + DECODE_START_LEGACY_COMPAT_LEN(2, 2, 2, bl); + ::decode(days, bl); + DECODE_FINISH(bl); + } + void dump(Formatter *f) const; +// static void generate_test_instances(list& o); + void set_days(const string& _days) { days = _days; } + bool get_days(int* _days) {*_days = atoi(days.c_str()); return true; } +}; +WRITE_CLASS_ENCODER(LCExpiration) + +class LCRule +{ +protected: + string id; + string prefix; + string status; + LCExpiration expiration; + +public: + + LCRule(){}; + ~LCRule(){}; + + bool get_id(string& _id) { + _id = id; + return true; + } + + bool get_status(string& _status) { + _status = status; + return true; + } + + bool get_prefix(string& _prefix) { + _prefix = prefix; + return true; + } + + bool get_expiration(LCExpiration& _expriation) { + _expriation = expiration; + return true; + } + + void set_id(string*_id) { + id = *_id; + } + + void set_prefix(string*_prefix) { + prefix = *_prefix; + } + + void set_status(string*_status) { + status = *_status; + } + + void set_expiration(LCExpiration*_expiration) { + expiration = *_expiration; + } + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(id, bl); + ::encode(prefix, bl); + ::encode(status, bl); + ::encode(expiration, bl); + ENCODE_FINISH(bl); + } + void decode(bufferlist::iterator& bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + ::decode(id, bl); + ::decode(prefix, bl); + ::decode(status, bl); + ::decode(expiration, bl); + DECODE_FINISH(bl); + } + +}; +WRITE_CLASS_ENCODER(LCRule) + +class RGWLifecycleConfiguration +{ +protected: + CephContext *cct; + map prefix_map; + multimap rule_map; + void _add_rule(LCRule *rule); +public: + RGWLifecycleConfiguration(CephContext *_cct) : cct(_cct) {} + RGWLifecycleConfiguration() : cct(NULL) {} + + void set_ctx(CephContext *ctx) { + cct = ctx; + } + + virtual ~RGWLifecycleConfiguration() {} + +// int get_perm(string& id, int perm_mask); +// int get_group_perm(ACLGroupTypeEnum group, int perm_mask); + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(rule_map, bl); + ENCODE_FINISH(bl); + } + void decode(bufferlist::iterator& bl) { + DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl); + ::decode(rule_map, bl); + multimap::iterator iter; + for (iter = rule_map.begin(); iter != rule_map.end(); ++iter) { + LCRule& rule = iter->second; + _add_rule(&rule); + } + DECODE_FINISH(bl); + } + void dump(Formatter *f) const; +// static void generate_test_instances(list& o); + + void add_rule(LCRule* rule); + + multimap& get_rule_map() { return rule_map; } + map& get_prefix_map() { return prefix_map; } +/* + void create_default(string id, string name) { + ACLGrant grant; + grant.set_canon(id, name, RGW_PERM_FULL_CONTROL); + add_grant(&grant); + } +*/ +}; +WRITE_CLASS_ENCODER(RGWLifecycleConfiguration) + +class RGWLC { + CephContext *cct; + RGWRados *store; + int max_objs; + string *obj_names; + + class LCWorker : public Thread { + CephContext *cct; + RGWLC *lc; + Mutex lock; + Cond cond; + + public: + LCWorker(CephContext *_cct, RGWLC *_lc) : cct(_cct), lc(_lc), lock("LCWorker") {} + void *entry(); + void stop(); + bool should_work(utime_t& now); + int shedule_next_start_time(utime_t& now); + }; + + public: + LCWorker *worker; +public: + RGWLC() : cct(NULL), store(NULL), worker(NULL) {} + ~RGWLC() { + stop_processor(); + finalize(); + } + + void initialize(CephContext *_cct, RGWRados *_store); + void finalize(); + + int process(); + int process(int index, int max_secs); + bool if_already_run_today(time_t& start_date); + int list_lc_progress(map& progress_map); + int bucket_lc_prepare(int index); + int bucket_lc_process(string& shard_id); + int bucket_lc_post(int index, int max_lock_sec, cls_rgw_lc_obj_head& head, + pair& entry, int& result); + bool going_down(); + void start_processor(); + void stop_processor(); +}; + + + +#endif diff --git a/src/rgw/rgw_lc_s3.cc b/src/rgw/rgw_lc_s3.cc new file mode 100644 index 000000000000..0ef01135f819 --- /dev/null +++ b/src/rgw/rgw_lc_s3.cc @@ -0,0 +1,112 @@ +#include + +#include +#include + +#include "include/types.h" + +#include "rgw_lc_s3.h" + + +#define dout_subsys ceph_subsys_rgw + +using namespace std; + +bool LCExpiration_S3::xml_end(const char * el) { + LCDays_S3 *lc_days = static_cast(find_first("Days")); + + // ID is mandatory + if (!lc_days) + return false; + days = lc_days->get_data(); + return true; +} + +bool RGWLifecycleConfiguration_S3::xml_end(const char *el) { + XMLObjIter iter = find("Rule"); + LCRule_S3 *rule = static_cast(iter.get_next()); + while (rule) { + add_rule(rule); + rule = static_cast(iter.get_next()); + } + return true; +} + +bool LCRule_S3::xml_end(const char *el) { + LCID_S3 *lc_id; + LCPrefix_S3 *lc_prefix; + LCStatus_S3 *lc_status; + LCExpiration_S3 *lc_expiration; + + id.clear(); + prefix.clear(); + status.clear(); + + lc_id = static_cast(find_first("ID")); + if (!lc_id) + return false; + id = lc_id->get_data(); + + lc_prefix = static_cast(find_first("Prefix")); + if (!lc_prefix) + return false; + prefix = lc_prefix->get_data(); + + lc_status = static_cast(find_first("Status")); + if (!lc_status) + return false; + status = lc_status->get_data(); + + lc_expiration = static_cast(find_first("Expiration")); + if (!lc_expiration) + return false; + expiration = *lc_expiration; + + return true; +} + +void LCRule_S3::to_xml(CephContext *cct, ostream& out) { + LCExpiration_S3& expir = static_cast(expiration); + out << "" ; + out << "" << id << ""; + out << "" << prefix << ""; + out << "" << status << ""; + expir.to_xml(out); + out << ""; +} + +int RGWLifecycleConfiguration_S3::rebuild(RGWRados *store, RGWLifecycleConfiguration& dest) +{ + multimap::iterator iter; + for (iter = rule_map.begin(); iter != rule_map.end(); ++iter) { + LCRule& src_rule = iter->second; + bool rule_ok = true; + + if (rule_ok) { + dest.add_rule(&src_rule); + } + } + + return 0; +} + +XMLObj *RGWLCXMLParser_S3::alloc_obj(const char *el) +{ + XMLObj * obj = NULL; + if (strcmp(el, "LifecycleConfiguration") == 0) { + obj = new RGWLifecycleConfiguration_S3(cct); + } else if (strcmp(el, "Rule") == 0) { + obj = new LCRule_S3(); + } else if (strcmp(el, "ID") == 0) { + obj = new LCID_S3(); + } else if (strcmp(el, "Prefix") == 0) { + obj = new LCPrefix_S3(); + } else if (strcmp(el, "Status") == 0) { + obj = new LCStatus_S3(); + } else if (strcmp(el, "Expiration") == 0) { + obj = new LCExpiration_S3(); + } else if (strcmp(el, "Days") == 0) { + obj = new LCDays_S3(); + } + return obj; +} diff --git a/src/rgw/rgw_lc_s3.h b/src/rgw/rgw_lc_s3.h new file mode 100644 index 000000000000..79e56ecfed56 --- /dev/null +++ b/src/rgw/rgw_lc_s3.h @@ -0,0 +1,104 @@ +#ifndef CEPH_RGW_LC_S3_H +#define CEPH_RGW_LC_S3_H + +#include +#include +#include +#include + +#include + +#include "include/str_list.h" +#include "rgw_lc.h" +#include "rgw_xml.h" + + + +using namespace std; + +class LCRule_S3 : public LCRule, public XMLObj +{ +public: + LCRule_S3() {} + ~LCRule_S3() {} + + void to_xml(CephContext *cct, ostream& out); + bool xml_end(const char *el); + bool xml_start(const char *el, const char **attr); +}; + +class LCID_S3 : public XMLObj +{ +public: + LCID_S3() {} + ~LCID_S3() {} + string& to_str() { return data; } +}; + +class LCPrefix_S3 : public XMLObj +{ +public: + LCPrefix_S3() {} + ~LCPrefix_S3() {} + string& to_str() { return data; } +}; + +class LCStatus_S3 : public XMLObj +{ +public: + LCStatus_S3() {} + ~LCStatus_S3() {} + string& to_str() { return data; } +}; + +class LCDays_S3 : public XMLObj +{ +public: + LCDays_S3() {} + ~LCDays_S3() {} + string& to_str() { return data; } +}; + +class LCExpiration_S3 : public LCExpiration, public XMLObj +{ +public: + LCExpiration_S3() {} + ~LCExpiration_S3() {} + + bool xml_end(const char *el); + void to_xml(ostream& out) { + out << "" << "" << days << ""<< ""; + } +}; + +class RGWLCXMLParser_S3 : public RGWXMLParser +{ + CephContext *cct; + + XMLObj *alloc_obj(const char *el); +public: + RGWLCXMLParser_S3(CephContext *_cct) : cct(_cct) {} +}; + +class RGWLifecycleConfiguration_S3 : public RGWLifecycleConfiguration, public XMLObj +{ +public: + RGWLifecycleConfiguration_S3(CephContext *_cct) : RGWLifecycleConfiguration(_cct) {} + ~RGWLifecycleConfiguration_S3() {} + + bool xml_end(const char *el); + + void to_xml(ostream& out) { + out << ""; + multimap::iterator iter; + for (iter = rule_map.begin(); iter != rule_map.end(); ++iter) { + LCRule_S3& rule = static_cast(iter->second); + rule.to_xml(cct, out); + } + out << ""; + } + int rebuild(RGWRados *store, RGWLifecycleConfiguration& dest); +}; + + +#endif diff --git a/src/rgw/rgw_main.cc b/src/rgw/rgw_main.cc index 2246b6a40ea1..1c8a38aa1462 100644 --- a/src/rgw/rgw_main.cc +++ b/src/rgw/rgw_main.cc @@ -1072,7 +1072,7 @@ int main(int argc, const char **argv) int r = 0; RGWRados *store = RGWStoreManager::get_storage(g_ceph_context, - g_conf->rgw_enable_gc_threads, g_conf->rgw_enable_quota_threads); + g_conf->rgw_enable_gc_threads, g_conf->rgw_enable_lc_threads, g_conf->rgw_enable_quota_threads); if (!store) { mutex.Lock(); init_timer.cancel_all_events(); diff --git a/src/rgw/rgw_object_expirer.cc b/src/rgw/rgw_object_expirer.cc index dcbfbc1d3a43..09350be3bb2b 100644 --- a/src/rgw/rgw_object_expirer.cc +++ b/src/rgw/rgw_object_expirer.cc @@ -78,7 +78,7 @@ int main(const int argc, const char **argv) common_init_finish(g_ceph_context); - store = RGWStoreManager::get_storage(g_ceph_context, false, false); + store = RGWStoreManager::get_storage(g_ceph_context, false, false, false); if (!store) { std::cerr << "couldn't init storage provider" << std::endl; return EIO; diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 815879d231b4..d0d77cc3fc28 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -25,14 +25,20 @@ #include "rgw_multi_del.h" #include "rgw_cors.h" #include "rgw_cors_s3.h" - +#include "rgw_lc.h" +#include "rgw_lc_s3.h" #include "rgw_client_io.h" +#include "cls/lock/cls_lock_client.h" +#include "cls/rgw/cls_rgw_client.h" + #define dout_subsys ceph_subsys_rgw using namespace std; +using namespace librados; using ceph::crypto::MD5; + static string mp_ns = RGW_OBJ_NS_MULTIPART; static string shadow_ns = RGW_OBJ_NS_SHADOW; @@ -2748,11 +2754,43 @@ int RGWPutACLs::verify_permission() return 0; } +int RGWPutLC::verify_permission() +{ + bool perm; + ldout(s->cct, 0) << "ccc" <bucket_acl << dendl; + perm = s->bucket_acl->verify_permission(s->user.user_id, RGW_PERM_WRITE_ACP, RGW_PERM_WRITE_ACP); + if (!perm) + return -EACCES; + + return 0; +} + +int RGWDeleteLC::verify_permission() +{ + bool perm; + ldout(s->cct, 0) << "ccc" <bucket_acl << dendl; + perm = s->bucket_acl->verify_permission(s->user.user_id, RGW_PERM_WRITE_ACP, RGW_PERM_WRITE_ACP); + if (!perm) + return -EACCES; + + return 0; +} + void RGWPutACLs::pre_exec() { rgw_bucket_object_pre_exec(s); } +void RGWPutLC::pre_exec() +{ + rgw_bucket_object_pre_exec(s); +} + +void RGWDeleteLC::pre_exec() +{ + rgw_bucket_object_pre_exec(s); +} + void RGWPutACLs::execute() { bufferlist bl; @@ -2847,6 +2885,135 @@ void RGWPutACLs::execute() } } +static void get_lc_oid(struct req_state *s, string& oid) +{ + string shard_id = s->bucket.name + ':' +s->bucket.bucket_id; + int max_objs = (s->cct->_conf->rgw_lc_max_objs > HASH_PRIME)?HASH_PRIME:s->cct->_conf->rgw_lc_max_objs; + int index = ceph_str_hash_linux(shard_id.c_str(), shard_id.size()) % HASH_PRIME % max_objs; + oid = lc_oid_prefix; + char buf[32]; + snprintf(buf, 32, ".%d", index); + oid.append(buf); + return; +} +void RGWPutLC::execute() +{ + bufferlist bl; + + RGWLifecycleConfiguration_S3 *config = NULL; + RGWLCXMLParser_S3 parser(s->cct); + RGWLifecycleConfiguration_S3 new_config(s->cct); + ret = 0; + + if (!parser.init()) { + ret = -EINVAL; + return; + } + + ret = get_params(); + if (ret < 0) + return; + + ldout(s->cct, 15) << "read len=" << len << " data=" << (data ? data : "") << dendl; + + if (!parser.parse(data, len, 1)) { + ret = -EACCES; + return; + } + config = static_cast(parser.find_first("LifecycleConfiguration")); + if (!config) { + ret = -EINVAL; + return; + } + + if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 15)) { + ldout(s->cct, 15) << "Old LifecycleConfiguration"; + config->to_xml(*_dout); + *_dout << dendl; + } + + ret = config->rebuild(store, new_config); + if (ret < 0) + return; + + if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 15)) { + ldout(s->cct, 15) << "New LifecycleConfiguration:"; + new_config.to_xml(*_dout); + *_dout << dendl; + } + + new_config.encode(bl); + map attrs; + attrs[RGW_ATTR_LC] = bl; + ret = rgw_bucket_set_attrs(store, s->bucket_info, attrs, NULL, NULL); + if (ret < 0) + return; + string shard_id = s->bucket.tenant + ':' + s->bucket.name + ':' + s->bucket.bucket_id; + string oid; + get_lc_oid(s, oid); + pair entry(shard_id, lc_uninitial); + int max_lock_secs = s->cct->_conf->rgw_lc_lock_max_time; + rados::cls::lock::Lock l(lc_index_lock_name); + utime_t time(max_lock_secs, 0); + l.set_duration(time); + librados::IoCtx *ctx = store->get_lc_pool_ctx(); + do { + ret = l.lock_exclusive(ctx, oid); + if (ret == -EBUSY) { + dout(0) << "RGWLC::RGWPutLC() failed to acquire lock on, sleep 5, try again" << oid << dendl; + sleep(5); + continue; + } + if (ret < 0) { + dout(0) << "RGWLC::RGWPutLC() failed to acquire lock " << oid << ret << dendl; + break; + } + ret = cls_rgw_lc_set_entry(*ctx, oid, entry); + if (ret < 0) { + dout(0) << "RGWLC::RGWPutLC() failed to set entry " << oid << ret << dendl; + } + break; + }while(1); + l.unlock(ctx, oid); + return; +} + +void RGWDeleteLC::execute() +{ + bufferlist bl; + map attrs, rmattrs; + rmattrs[RGW_ATTR_LC] = bl; + ret = rgw_bucket_set_attrs(store, s->bucket_info, attrs, &rmattrs, NULL); + string shard_id = s->bucket.name + ':' +s->bucket.bucket_id; + pair entry(shard_id, lc_uninitial); + string oid; + get_lc_oid(s, oid); + int max_lock_secs = s->cct->_conf->rgw_lc_lock_max_time; + librados::IoCtx *ctx = store->get_lc_pool_ctx(); + rados::cls::lock::Lock l(lc_index_lock_name); + utime_t time(max_lock_secs, 0); + l.set_duration(time); + do { + ret = l.lock_exclusive(ctx, oid); + if (ret == -EBUSY) { + dout(0) << "RGWLC::RGWPutLC() failed to acquire lock on, sleep 5, try again" << oid << dendl; + sleep(5); + continue; + } + if (ret < 0) { + dout(0) << "RGWLC::RGWPutLC() failed to acquire lock " << oid << ret << dendl; + break; + } + ret = cls_rgw_lc_rm_entry(*ctx, oid, entry); + if (ret < 0) { + dout(0) << "RGWLC::RGWPutLC() failed to set entry " << oid << ret << dendl; + } + break; + }while(1); + l.unlock(ctx, oid); + return; +} + int RGWGetCORS::verify_permission() { if (s->user.user_id.compare(s->bucket_owner.get_id()) != 0) diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index 0252ff8a65d2..de18804f01b9 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -763,6 +763,58 @@ public: virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; +class RGWPutLC : public RGWOp { +protected: + int ret; + size_t len; + char *data; + +public: + RGWPutLC() { + ret = 0; + len = 0; + data = NULL; + } + virtual ~RGWPutLC() { + free(data); + } + + int verify_permission(); + void pre_exec(); + void execute(); + +// virtual int get_policy_from_state(RGWRados *store, struct req_state *s, stringstream& ss) { return 0; } + virtual int get_params() = 0; + virtual void send_response() = 0; + virtual const string name() { return "put_lifecycle"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } +}; + +class RGWDeleteLC : public RGWOp { +protected: + int ret; + size_t len; + char *data; + +public: + RGWDeleteLC() { + ret = 0; + len = 0; + data = NULL; + } + virtual ~RGWDeleteLC() { + free(data); + } + + int verify_permission(); + void pre_exec(); + void execute(); + + virtual void send_response() = 0; + virtual const string name() { return "delete_lifecycle"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } +}; + class RGWGetCORS : public RGWOp { protected: int ret; diff --git a/src/rgw/rgw_rados.cc b/src/rgw/rgw_rados.cc index 3e38e9effefe..b5f23c94a1bd 100644 --- a/src/rgw/rgw_rados.cc +++ b/src/rgw/rgw_rados.cc @@ -17,6 +17,8 @@ #include "rgw_cache.h" #include "rgw_acl.h" #include "rgw_acl_s3.h" /* for dumping s3policy in debug log */ +#include "rgw_lc.h" +#include "rgw_lc_s3.h" #include "rgw_metadata.h" #include "rgw_bucket.h" @@ -48,6 +50,8 @@ using namespace librados; #include "rgw_log.h" #include "rgw_gc.h" +#include "rgw_lc.h" + #include "rgw_object_expirer_core.h" #define dout_subsys ceph_subsys_rgw @@ -311,6 +315,7 @@ void RGWZoneParams::init_default(RGWRados *store) domain_root = ".rgw"; control_pool = ".rgw.control"; gc_pool = ".rgw.gc"; + lc_pool = ".rgw.lc"; log_pool = ".log"; intent_log_pool = ".intent-log"; usage_log_pool = ".usage"; @@ -1645,6 +1650,10 @@ int RGWRados::init_complete() if (ret < 0) return ret; + ret = open_lc_pool_ctx(); + if (ret < 0) + return ret; + ret = open_objexp_pool_ctx(); if (ret < 0) return ret; @@ -1661,6 +1670,12 @@ int RGWRados::init_complete() obj_expirer->start_processor(); } + lc = new RGWLC(); + lc->initialize(cct, this); + + if (use_lc_thread) + lc->start_processor(); + quota_handler = RGWQuotaHandler::generate_handler(this, quota_threads); bucket_index_max_shards = (cct->_conf->rgw_override_bucket_index_max_shards ? cct->_conf->rgw_override_bucket_index_max_shards : @@ -1794,6 +1809,24 @@ int RGWRados::open_gc_pool_ctx() return r; } +int RGWRados::open_lc_pool_ctx() +{ + const char *lc_pool = zone.lc_pool.name.c_str(); + librados::Rados *rad = get_rados_handle(); + int r = rad->ioctx_create(lc_pool, lc_pool_ctx); + if (r == -ENOENT) { + r = rad->pool_create(lc_pool); + if (r == -EEXIST) + r = 0; + if (r < 0) + return r; + + r = rad->ioctx_create(lc_pool, lc_pool_ctx); + } + + return r; +} + int RGWRados::open_objexp_pool_ctx() { const char * const pool_name = zone.log_pool.name.c_str(); @@ -8204,6 +8237,16 @@ int RGWRados::process_gc() return gc->process(); } +int RGWRados::list_lc_progress(map& progress_map) +{ + return lc->list_lc_progress(progress_map); +} + +int RGWRados::process_lc() +{ + return lc->process(); +} + int RGWRados::process_expire_objects() { obj_expirer->inspect_all_shards(utime_t(), ceph_clock_now(cct)); @@ -9218,7 +9261,7 @@ uint64_t RGWRados::next_bucket_id() return ++max_bucket_id; } -RGWRados *RGWStoreManager::init_storage_provider(CephContext *cct, bool use_gc_thread, bool quota_threads) +RGWRados *RGWStoreManager::init_storage_provider(CephContext *cct, bool use_gc_thread, bool use_lc_thread, bool quota_threads) { int use_cache = cct->_conf->rgw_cache_enabled; RGWRados *store = NULL; @@ -9228,7 +9271,7 @@ RGWRados *RGWStoreManager::init_storage_provider(CephContext *cct, bool use_gc_t store = new RGWCache; } - if (store->initialize(cct, use_gc_thread, quota_threads) < 0) { + if (store->initialize(cct, use_gc_thread, use_lc_thread, quota_threads) < 0) { delete store; return NULL; } diff --git a/src/rgw/rgw_rados.h b/src/rgw/rgw_rados.h index 67adc60e4c73..7f38645311ef 100644 --- a/src/rgw/rgw_rados.h +++ b/src/rgw/rgw_rados.h @@ -22,6 +22,7 @@ class RGWWatcher; class SafeTimer; class ACLOwner; class RGWGC; +class RGWLC; class RGWObjectExpirer; /* flags for put_obj_meta() */ @@ -732,6 +733,7 @@ struct RGWZoneParams { rgw_bucket domain_root; rgw_bucket control_pool; rgw_bucket gc_pool; + rgw_bucket lc_pool; rgw_bucket log_pool; rgw_bucket intent_log_pool; rgw_bucket usage_log_pool; @@ -761,6 +763,7 @@ struct RGWZoneParams { ::encode(domain_root, bl); ::encode(control_pool, bl); ::encode(gc_pool, bl); + ::encode(lc_pool, bl); ::encode(log_pool, bl); ::encode(intent_log_pool, bl); ::encode(usage_log_pool, bl); @@ -779,6 +782,7 @@ struct RGWZoneParams { ::decode(domain_root, bl); ::decode(control_pool, bl); ::decode(gc_pool, bl); + ::decode(lc_pool, bl); ::decode(log_pool, bl); ::decode(intent_log_pool, bl); ::decode(usage_log_pool, bl); @@ -1207,6 +1211,7 @@ class Finisher; class RGWRados { friend class RGWGC; + friend class RGWLC; friend class RGWObjectExpirer; friend class RGWStateLog; friend class RGWReplicaLogger; @@ -1214,6 +1219,7 @@ class RGWRados /** Open the pool used as root for this gateway */ int open_root_pool_ctx(); int open_gc_pool_ctx(); + int open_lc_pool_ctx(); int open_objexp_pool_ctx(); int open_bucket_pool_ctx(const string& pool, librados::IoCtx& io_ctx); @@ -1251,8 +1257,10 @@ class RGWRados }; RGWGC *gc; + RGWLC *lc; RGWObjectExpirer *obj_expirer; bool use_gc_thread; + bool use_lc_thread; bool quota_threads; int num_watchers; @@ -1294,6 +1302,7 @@ protected: std::map rados_map; librados::IoCtx gc_pool_ctx; // .rgw.gc + librados::IoCtx lc_pool_ctx; // .rgw.lc librados::IoCtx objexp_pool_ctx; bool pools_initialized; @@ -1308,7 +1317,7 @@ protected: public: RGWRados() : max_req_id(0), lock("rados_timer_lock"), watchers_lock("watchers_lock"), timer(NULL), - gc(NULL), obj_expirer(NULL), use_gc_thread(false), quota_threads(false), + gc(NULL), obj_expirer(NULL), use_gc_thread(false), use_lc_thread(false), quota_threads(false), num_watchers(0), watchers(NULL), watch_initialized(false), bucket_id_lock("rados_bucket_id"), @@ -1326,6 +1335,9 @@ public: return max_req_id.inc(); } + librados::IoCtx* get_lc_pool_ctx() { + return &lc_pool_ctx; + } void set_context(CephContext *_cct) { cct = _cct; } @@ -1379,9 +1391,10 @@ public: CephContext *ctx() { return cct; } /** do all necessary setup of the storage device */ - int initialize(CephContext *_cct, bool _use_gc_thread, bool _quota_threads) { + int initialize(CephContext *_cct, bool _use_gc_thread, bool _use_lc_thread, bool _quota_threads) { set_context(_cct); use_gc_thread = _use_gc_thread; + use_lc_thread = _use_lc_thread; quota_threads = _quota_threads; return initialize(); } @@ -2165,6 +2178,9 @@ public: int process_expire_objects(); int defer_gc(void *ctx, rgw_obj& obj); + int process_lc(); + int list_lc_progress(map& progress_map); + int bucket_check_index(rgw_bucket& bucket, map *existing_stats, map *calculated_stats); @@ -2322,15 +2338,15 @@ public: class RGWStoreManager { public: RGWStoreManager() {} - static RGWRados *get_storage(CephContext *cct, bool use_gc_thread, bool quota_threads) { - RGWRados *store = init_storage_provider(cct, use_gc_thread, quota_threads); + static RGWRados *get_storage(CephContext *cct, bool use_gc_thread, bool use_lc_thread, bool quota_threads) { + RGWRados *store = init_storage_provider(cct, use_gc_thread, use_lc_thread, quota_threads); return store; } static RGWRados *get_raw_storage(CephContext *cct) { RGWRados *store = init_raw_storage_provider(cct); return store; } - static RGWRados *init_storage_provider(CephContext *cct, bool use_gc_thread, bool quota_threads); + static RGWRados *init_storage_provider(CephContext *cct, bool use_gc_thread, bool use_lc_thread, bool quota_threads); static RGWRados *init_raw_storage_provider(CephContext *cct); static void close_storage(RGWRados *store); diff --git a/src/rgw/rgw_rest.cc b/src/rgw/rgw_rest.cc index 767ad1ecfc3d..e20128d1aad5 100644 --- a/src/rgw/rgw_rest.cc +++ b/src/rgw/rgw_rest.cc @@ -930,6 +930,30 @@ int RGWPutACLs_ObjStore::get_params() return ret; } +int RGWPutLC_ObjStore::get_params() +{ + size_t cl = 0; + if (s->length) + cl = atoll(s->length); + if (cl) { + data = (char *)malloc(cl + 1); + if (!data) { + ret = -ENOMEM; + return ret; + } + int read_len; + int r = s->cio->read(data, cl, &read_len); + len = read_len; + if (r < 0) + return r; + data[len] = '\0'; + } else { + len = 0; + } + + return ret; +} + static int read_all_chunked_input(req_state *s, char **pdata, int *plen, int max_read) { #define READ_CHUNK 4096 diff --git a/src/rgw/rgw_rest.h b/src/rgw/rgw_rest.h index b9355e6457b0..4e8d81f11185 100644 --- a/src/rgw/rgw_rest.h +++ b/src/rgw/rgw_rest.h @@ -210,6 +210,21 @@ public: int get_params(); }; +class RGWPutLC_ObjStore : public RGWPutLC { +public: + RGWPutLC_ObjStore() {} + ~RGWPutLC_ObjStore() {} + + int get_params(); +}; + +class RGWDeleteLC_ObjStore : public RGWDeleteLC { +public: + RGWDeleteLC_ObjStore() {} + ~RGWDeleteLC_ObjStore() {} + +}; + class RGWGetCORS_ObjStore : public RGWGetCORS { public: RGWGetCORS_ObjStore() {} diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 8c00e192c03f..49eaeebbcf29 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -1612,6 +1612,27 @@ void RGWPutACLs_ObjStore_S3::send_response() dump_start(s); } +void RGWPutLC_ObjStore_S3::send_response() +{ + if (ret) + set_req_state_err(s, ret); + dump_errno(s); + end_header(s, this, "application/xml"); + dump_start(s); +} + +void RGWDeleteLC_ObjStore_S3::send_response() +{ + if (ret == 0) + ret = STATUS_NO_CONTENT; + if (ret) { + set_req_state_err(s, ret); + } + dump_errno(s); + end_header(s, this, "application/xml"); + dump_start(s); +} + void RGWGetCORS_ObjStore_S3::send_response() { if (ret) { @@ -2145,6 +2166,8 @@ RGWOp *RGWHandler_ObjStore_Bucket_S3::op_put() return new RGWPutCORS_ObjStore_S3; } else if (is_request_payment_op()) { return new RGWSetRequestPayment_ObjStore_S3; + } else if(is_lc_op()) { + return new RGWPutLC_ObjStore_S3; } return new RGWCreateBucket_ObjStore_S3; } @@ -2153,6 +2176,8 @@ RGWOp *RGWHandler_ObjStore_Bucket_S3::op_delete() { if (is_cors_op()) { return new RGWDeleteCORS_ObjStore_S3; + } else if(is_lc_op()) { + return new RGWDeleteLC_ObjStore_S3; } return new RGWDeleteBucket_ObjStore_S3; } diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index 1c2d5290f3e9..231b3c909517 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -202,6 +202,22 @@ public: void send_response(); }; +class RGWPutLC_ObjStore_S3 : public RGWPutLC_ObjStore { +public: + RGWPutLC_ObjStore_S3() {} + ~RGWPutLC_ObjStore_S3() {} + + void send_response(); +}; + +class RGWDeleteLC_ObjStore_S3 : public RGWDeleteLC_ObjStore { +public: + RGWDeleteLC_ObjStore_S3() {} + ~RGWDeleteLC_ObjStore_S3() {} + + void send_response(); +}; + class RGWGetCORS_ObjStore_S3 : public RGWGetCORS_ObjStore { public: RGWGetCORS_ObjStore_S3() {} @@ -415,6 +431,9 @@ protected: bool is_cors_op() { return s->info.args.exists("cors"); } + bool is_lc_op() { + return s->info.args.exists("lifecycle"); + } bool is_obj_update_op() { return is_acl_op() || is_cors_op(); } diff --git a/src/test/cli/radosgw-admin/help.t b/src/test/cli/radosgw-admin/help.t index 0b0f9005c007..fcb2c2fabaa5 100644 --- a/src/test/cli/radosgw-admin/help.t +++ b/src/test/cli/radosgw-admin/help.t @@ -52,6 +52,8 @@ gc list dump expired garbage collection objects (specify --include-all to list all entries, including unexpired) gc process manually process garbage + lc list list all bucket lifecycle progress + lc process manually process lifecycle metadata get get metadata info metadata put put metadata info metadata rm remove metadata info diff --git a/src/test/test_rgw_admin_opstate.cc b/src/test/test_rgw_admin_opstate.cc index 26568578c98a..3499aa46abd1 100644 --- a/src/test/test_rgw_admin_opstate.cc +++ b/src/test/test_rgw_admin_opstate.cc @@ -807,7 +807,7 @@ int main(int argc, char *argv[]){ global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, CODE_ENVIRONMENT_UTILITY, 0); common_init_finish(g_ceph_context); - store = RGWStoreManager::get_storage(g_ceph_context, false, false); + store = RGWStoreManager::get_storage(g_ceph_context, false, false, false); g_test = new admin_log::test_helper(); finisher = new Finisher(g_ceph_context); #ifdef GTEST -- 2.47.3