From: Yehuda Sadeh Date: Fri, 6 Dec 2013 15:32:08 +0000 (-0800) Subject: rgw: new user objclass X-Git-Tag: v0.78~270^2~36 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=248c4aefff9c65e50963aaa867c459c12b1b28fc;p=ceph.git rgw: new user objclass Signed-off-by: Yehuda Sadeh --- diff --git a/src/cls/Makefile.am b/src/cls/Makefile.am index 2d3d43cb1e3..b88b5cc3362 100644 --- a/src/cls/Makefile.am +++ b/src/cls/Makefile.am @@ -43,6 +43,11 @@ libcls_replica_log_la_LIBADD = $(PTHREAD_LIBS) $(EXTRALIBS) libcls_replica_log_la_LDFLAGS = ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-regex '.*__cls_.*' radoslib_LTLIBRARIES += libcls_replica_log.la +libcls_user_la_SOURCES = cls/user/cls_user.cc +libcls_user_la_LIBADD = $(PTHREAD_LIBS) $(EXTRALIBS) +libcls_user_la_LDFLAGS = ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-regex '.*__cls_.*' +radoslib_LTLIBRARIES += libcls_user.la + libcls_rgw_la_SOURCES = \ cls/rgw/cls_rgw.cc \ cls/rgw/cls_rgw_ops.cc \ @@ -95,6 +100,8 @@ DENCODER_DEPS += libcls_rgw_client.la libcls_rbd_client_la_SOURCES = cls/rbd/cls_rbd_client.cc noinst_LTLIBRARIES += libcls_rbd_client.la +libcls_user_client_a_SOURCES = cls/user/cls_user_client.cc +noinst_LIBRARIES += libcls_user_client.a noinst_HEADERS += \ cls/lock/cls_lock_types.h \ @@ -118,5 +125,8 @@ noinst_HEADERS += \ cls/replica_log/cls_replica_log_client.h \ cls/rgw/cls_rgw_client.h \ cls/rgw/cls_rgw_ops.h \ - cls/rgw/cls_rgw_types.h + cls/rgw/cls_rgw_types.h \ + cls/user/cls_user_client.h \ + cls/user/cls_user_ops.h \ + cls/user/cls_user_types.h diff --git a/src/cls/user/cls_user.cc b/src/cls/user/cls_user.cc new file mode 100644 index 00000000000..4f9446cb334 --- /dev/null +++ b/src/cls/user/cls_user.cc @@ -0,0 +1,211 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include + +#include +#include +#include + +#include "include/types.h" +#include "include/utime.h" +#include "objclass/objclass.h" + +#include "cls_user_types.h" +#include "cls_user_ops.h" + +CLS_VER(1,0) +CLS_NAME(user) + +cls_handle_t h_class; +cls_method_handle_t h_user_set_buckets_info; +cls_method_handle_t h_user_remove_bucket; +cls_method_handle_t h_user_list_buckets; + +static int write_entry(cls_method_context_t hctx, const string& key, const cls_user_bucket_entry& entry) +{ + bufferlist bl; + ::encode(entry, bl); + + int ret = cls_cxx_map_set_val(hctx, key, &bl); + if (ret < 0) + return ret; + + return 0; +} + +static int remove_entry(cls_method_context_t hctx, const string& key) +{ + int ret = cls_cxx_map_remove_key(hctx, key); + if (ret < 0) + return ret; + + return 0; +} + +static void get_key_by_bucket_name(const string& bucket_name, string *key) +{ + *key = bucket_name; +} + +static int get_existing_bucket_entry(cls_method_context_t hctx, const string& bucket_name, + cls_user_bucket_entry& entry) +{ + if (bucket_name.empty()) { + return -EINVAL; + } + + string key; + get_key_by_bucket_name(bucket_name, &key); + + bufferlist bl; + int rc = cls_cxx_map_get_val(hctx, key, &bl); + if (rc < 0) { + CLS_LOG(10, "could not read entry %s", key.c_str()); + return rc; + } + try { + bufferlist::iterator iter = bl.begin(); + ::decode(entry, iter); + } catch (buffer::error& err) { + CLS_LOG(0, "ERROR: failed to decode entry %s", key.c_str()); + return -EIO; + } + + return 0; +} + +static int cls_user_set_buckets_info(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_user_set_buckets_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_user_add_op(): failed to decode op"); + return -EINVAL; + } + + for (list::iterator iter = op.entries.begin(); + iter != op.entries.end(); ++iter) { + cls_user_bucket_entry& entry = *iter; + + string key; + + get_key_by_bucket_name(entry.bucket.name, &key); + + CLS_LOG(0, "storing entry by client/op at %s", key.c_str()); + + int ret = write_entry(hctx, key, entry); + if (ret < 0) + return ret; + } + + return 0; +} + +static int cls_user_remove_bucket(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_user_remove_bucket_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_user_add_op(): failed to decode op"); + return -EINVAL; + } + + string key; + + get_key_by_bucket_name(op.bucket.name, &key); + + CLS_LOG(20, "removing entry at %s", key.c_str()); + + int ret = remove_entry(hctx, key); + if (ret < 0) + return ret; + + return 0; +} + +static int cls_user_list_buckets(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + bufferlist::iterator in_iter = in->begin(); + + cls_user_list_buckets_op op; + try { + ::decode(op, in_iter); + } catch (buffer::error& err) { + CLS_LOG(1, "ERROR: cls_user_list_op(): failed to decode op"); + return -EINVAL; + } + + map keys; + + string from_index = op.marker; + +#define MAX_ENTRIES 1000 + size_t max_entries = op.max_entries; + if (!max_entries || max_entries > MAX_ENTRIES) + max_entries = MAX_ENTRIES; + + string match_prefix; + + int rc = cls_cxx_map_get_vals(hctx, from_index, match_prefix, max_entries + 1, &keys); + if (rc < 0) + return rc; + + CLS_LOG(20, "from_index=%s match_prefix=%s", from_index.c_str(), match_prefix.c_str()); + cls_user_list_buckets_ret ret; + + list& entries = ret.entries; + map::iterator iter = keys.begin(); + + bool done = false; + string marker; + + size_t i; + for (i = 0; i < max_entries && iter != keys.end(); ++i, ++iter) { + const string& index = iter->first; + marker = index; + + bufferlist& bl = iter->second; + bufferlist::iterator biter = bl.begin(); + try { + cls_user_bucket_entry e; + ::decode(e, biter); + entries.push_back(e); + } catch (buffer::error& err) { + CLS_LOG(0, "ERROR: cls_user_list: could not decode entry, index=%s", index.c_str()); + } + } + + if (iter == keys.end()) + done = true; + else + ret.marker = marker; + + ret.truncated = !done; + + ::encode(ret, *out); + + return 0; +} + +void __cls_init() +{ + CLS_LOG(1, "Loaded user class!"); + + cls_register("user", &h_class); + + /* log */ + cls_register_cxx_method(h_class, "set_buckets_info", CLS_METHOD_RD | CLS_METHOD_WR, + cls_user_set_buckets_info, &h_user_set_buckets_info); + cls_register_cxx_method(h_class, "remove_bucket", CLS_METHOD_RD | CLS_METHOD_WR, cls_user_remove_bucket, &h_user_remove_bucket); + cls_register_cxx_method(h_class, "list_buckets", CLS_METHOD_RD, cls_user_list_buckets, &h_user_list_buckets); + + return; +} + diff --git a/src/cls/user/cls_user_client.cc b/src/cls/user/cls_user_client.cc new file mode 100644 index 00000000000..2b1c1347fc0 --- /dev/null +++ b/src/cls/user/cls_user_client.cc @@ -0,0 +1,72 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include + +#include "include/types.h" +#include "cls/user/cls_user_ops.h" +#include "include/rados/librados.hpp" + + +using namespace librados; + + +void cls_user_set_buckets(librados::ObjectWriteOperation& op, list& entries) +{ + bufferlist in; + cls_user_set_buckets_op call; + call.entries = entries; + ::encode(call, in); + op.exec("user", "set_buckets_info", in); +} + +void cls_user_remove_bucket(librados::ObjectWriteOperation& op, const cls_user_bucket& bucket) +{ + bufferlist in; + cls_user_remove_bucket_op call; + call.bucket = bucket; + ::encode(call, in); + op.exec("user", "remove_bucket", in); +} + +class ClsUserListCtx : public ObjectOperationCompletion { + list *entries; + string *marker; + bool *truncated; +public: + ClsUserListCtx(list *_entries, string *_marker, bool *_truncated) : + entries(_entries), marker(_marker), truncated(_truncated) {} + void handle_completion(int r, bufferlist& outbl) { + if (r >= 0) { + cls_user_list_buckets_ret ret; + try { + bufferlist::iterator iter = outbl.begin(); + ::decode(ret, iter); + if (entries) + *entries = ret.entries; + if (truncated) + *truncated = ret.truncated; + if (marker) + *marker = ret.marker; + } catch (buffer::error& err) { + // nothing we can do about it atm + } + } + } +}; + +void cls_user_bucket_list(librados::ObjectReadOperation& op, + const string& in_marker, int max_entries, list& entries, + string *out_marker, bool *truncated) +{ + bufferlist inbl; + cls_user_list_buckets_op call; + call.marker = in_marker; + call.max_entries = max_entries; + + ::encode(call, inbl); + + op.exec("user", "list_buckets", inbl, new ClsUserListCtx(&entries, out_marker, truncated)); +} + + diff --git a/src/cls/user/cls_user_client.h b/src/cls/user/cls_user_client.h new file mode 100644 index 00000000000..0f65875dbd7 --- /dev/null +++ b/src/cls/user/cls_user_client.h @@ -0,0 +1,22 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_CLS_USER_CLIENT_H +#define CEPH_CLS_USER_CLIENT_H + +#include "include/types.h" +#include "include/rados/librados.hpp" +#include "cls_user_types.h" + +/* + * user objclass + */ + +void cls_user_set_buckets(librados::ObjectWriteOperation& op, list& entries); +void cls_user_remove_bucket(librados::ObjectWriteOperation& op, const cls_user_bucket& bucket); +void cls_user_bucket_list(librados::ObjectReadOperation& op, + const string& in_marker, int max_entries, + list& entries, + string *out_marker, bool *truncated); + +#endif diff --git a/src/cls/user/cls_user_ops.h b/src/cls/user/cls_user_ops.h new file mode 100644 index 00000000000..a142d4de7ef --- /dev/null +++ b/src/cls/user/cls_user_ops.h @@ -0,0 +1,97 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_CLS_USER_OPS_H +#define CEPH_CLS_USER_OPS_H + +#include "include/types.h" +#include "cls_user_types.h" + +struct cls_user_set_buckets_op { + list entries; + + cls_user_set_buckets_op() {} + + 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_user_set_buckets_op) + +struct cls_user_remove_bucket_op { + cls_user_bucket bucket; + + cls_user_remove_bucket_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(bucket, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(bucket, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_user_remove_bucket_op) + +struct cls_user_list_buckets_op { + string marker; + int max_entries; /* upperbound to returned num of entries + might return less than that and still be truncated */ + + cls_user_list_buckets_op() {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(marker, bl); + ::encode(max_entries, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(marker, bl); + ::decode(max_entries, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_user_list_buckets_op) + +struct cls_user_list_buckets_ret { + list entries; + string marker; + bool truncated; + + cls_user_list_buckets_ret() : truncated(false) {} + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + ::encode(entries, bl); + ::encode(marker, bl); + ::encode(truncated, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::iterator& bl) { + DECODE_START(1, bl); + ::decode(entries, bl); + ::decode(marker, bl); + ::decode(truncated, bl); + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_user_list_buckets_ret) + + +#endif diff --git a/src/cls/user/cls_user_types.h b/src/cls/user/cls_user_types.h new file mode 100644 index 00000000000..f4f0c14d78f --- /dev/null +++ b/src/cls/user/cls_user_types.h @@ -0,0 +1,111 @@ +// -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_CLS_USER_TYPES_H +#define CEPH_CLS_USER_TYPES_H + +#include "include/encoding.h" +#include "include/types.h" +#include "include/utime.h" + +/* + * this needs to be compatible with with rgw_bucket, as it replaces it + */ +struct cls_user_bucket { + std::string name; + std::string data_pool; + std::string index_pool; + std::string marker; + std::string bucket_id; + + void encode(bufferlist& bl) const { + ENCODE_START(6, 3, bl); + ::encode(name, bl); + ::encode(data_pool, bl); + ::encode(marker, bl); + ::encode(bucket_id, bl); + ::encode(index_pool, bl); + ENCODE_FINISH(bl); + } + void decode(bufferlist::iterator& bl) { + DECODE_START_LEGACY_COMPAT_LEN(6, 3, 3, bl); + ::decode(name, bl); + ::decode(data_pool, bl); + if (struct_v >= 2) { + ::decode(marker, bl); + if (struct_v <= 3) { + uint64_t id; + ::decode(id, bl); + char buf[16]; + snprintf(buf, sizeof(buf), "%llu", (long long)id); + bucket_id = buf; + } else { + ::decode(bucket_id, bl); + } + } + if (struct_v >= 5) { + ::decode(index_pool, bl); + } else { + index_pool = data_pool; + } + DECODE_FINISH(bl); + } + + bool operator<(const cls_user_bucket& b) const { + return name.compare(b.name) < 0; + } +}; +WRITE_CLASS_ENCODER(cls_user_bucket) + +/* + * this structure overrides RGWBucketEnt + */ +struct cls_user_bucket_entry { + cls_user_bucket bucket; + size_t size; + size_t size_rounded; + time_t creation_time; + uint64_t count; + + cls_user_bucket_entry() : size(0), size_rounded(0), creation_time(0), count(0) {} + + void encode(bufferlist& bl) const { + ENCODE_START(5, 5, bl); + uint64_t s = size; + __u32 mt = creation_time; + string empty_str; // originally had the bucket name here, but we encode bucket later + ::encode(empty_str, bl); + ::encode(s, bl); + ::encode(mt, bl); + ::encode(count, bl); + ::encode(bucket, bl); + s = size_rounded; + ::encode(s, bl); + ENCODE_FINISH(bl); + } + void decode(bufferlist::iterator& bl) { + DECODE_START_LEGACY_COMPAT_LEN(5, 5, 5, bl); + __u32 mt; + uint64_t s; + string empty_str; // backward compatibility + ::decode(empty_str, bl); + ::decode(s, bl); + ::decode(mt, bl); + size = s; + creation_time = mt; + if (struct_v >= 2) + ::decode(count, bl); + if (struct_v >= 3) + ::decode(bucket, bl); + if (struct_v >= 4) + ::decode(s, bl); + size_rounded = s; + DECODE_FINISH(bl); + } +}; +WRITE_CLASS_ENCODER(cls_user_bucket_entry) + + +#endif + +