diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-21 11:54:28 +0000 |
commit | e6918187568dbd01842d8d1d2c808ce16a894239 (patch) | |
tree | 64f88b554b444a49f656b6c656111a145cbbaa28 /src/cls/user | |
parent | Initial commit. (diff) | |
download | ceph-e6918187568dbd01842d8d1d2c808ce16a894239.tar.xz ceph-e6918187568dbd01842d8d1d2c808ce16a894239.zip |
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cls/user')
-rw-r--r-- | src/cls/user/cls_user.cc | 531 | ||||
-rw-r--r-- | src/cls/user/cls_user_client.cc | 164 | ||||
-rw-r--r-- | src/cls/user/cls_user_client.h | 36 | ||||
-rw-r--r-- | src/cls/user/cls_user_ops.cc | 118 | ||||
-rw-r--r-- | src/cls/user/cls_user_ops.h | 267 | ||||
-rw-r--r-- | src/cls/user/cls_user_types.cc | 111 | ||||
-rw-r--r-- | src/cls/user/cls_user_types.h | 224 |
7 files changed, 1451 insertions, 0 deletions
diff --git a/src/cls/user/cls_user.cc b/src/cls/user/cls_user.cc new file mode 100644 index 000000000..e278ad7fc --- /dev/null +++ b/src/cls/user/cls_user.cc @@ -0,0 +1,531 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include <errno.h> + +#include "include/utime.h" +#include "objclass/objclass.h" + +#include "cls_user_ops.h" + +using std::map; +using std::string; + +using ceph::bufferlist; +using ceph::decode; +using ceph::encode; + +CLS_VER(1,0) +CLS_NAME(user) + +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 { + auto iter = bl.cbegin(); + decode(entry, iter); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: failed to decode entry %s", key.c_str()); + return -EIO; + } + + return 0; +} + +static int read_header(cls_method_context_t hctx, cls_user_header *header) +{ + bufferlist bl; + + int ret = cls_cxx_map_read_header(hctx, &bl); + if (ret < 0) + return ret; + + if (bl.length() == 0) { + *header = cls_user_header(); + return 0; + } + + try { + decode(*header, bl); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: failed to decode user header"); + return -EIO; + } + + return 0; +} + +static void add_header_stats(cls_user_stats *stats, cls_user_bucket_entry& entry) +{ + stats->total_entries += entry.count; + stats->total_bytes += entry.size; + stats->total_bytes_rounded += entry.size_rounded; +} + +static void dec_header_stats(cls_user_stats *stats, cls_user_bucket_entry& entry) +{ + stats->total_bytes -= entry.size; + stats->total_bytes_rounded -= entry.size_rounded; + stats->total_entries -= entry.count; +} + +static void apply_entry_stats(const cls_user_bucket_entry& src_entry, cls_user_bucket_entry *target_entry) +{ + target_entry->size = src_entry.size; + target_entry->size_rounded = src_entry.size_rounded; + target_entry->count = src_entry.count; +} + +static int cls_user_set_buckets_info(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + auto in_iter = in->cbegin(); + + cls_user_set_buckets_op op; + try { + decode(op, in_iter); + } catch (ceph::buffer::error& err) { + CLS_LOG(1, "ERROR: cls_user_add_op(): failed to decode op"); + return -EINVAL; + } + + cls_user_header header; + int ret = read_header(hctx, &header); + if (ret < 0) { + CLS_LOG(0, "ERROR: failed to read user info header ret=%d", ret); + return ret; + } + + for (auto iter = op.entries.begin(); iter != op.entries.end(); ++iter) { + cls_user_bucket_entry& update_entry = *iter; + + string key; + + get_key_by_bucket_name(update_entry.bucket.name, &key); + + cls_user_bucket_entry entry; + ret = get_existing_bucket_entry(hctx, key, entry); + + if (ret == -ENOENT) { + if (!op.add) + continue; /* racing bucket removal */ + + entry = update_entry; + + ret = 0; + } else if (op.add) { + // bucket id may have changed (ie reshard) + entry.bucket.bucket_id = update_entry.bucket.bucket_id; + // creation date may have changed (ie delete/recreate bucket) + entry.creation_time = update_entry.creation_time; + } + + if (ret < 0) { + CLS_LOG(0, "ERROR: get_existing_bucket_entry() key=%s returned %d", key.c_str(), ret); + return ret; + } else if (ret >= 0 && entry.user_stats_sync) { + dec_header_stats(&header.stats, entry); + } + + CLS_LOG(20, "storing entry for key=%s size=%lld count=%lld", + key.c_str(), (long long)update_entry.size, (long long)update_entry.count); + + // sync entry stats when not an op.add, as when the case is op.add if its a + // new entry we already have copied update_entry earlier, OTOH, for an existing entry + // we end up clobbering the existing stats for the bucket + if (!op.add){ + apply_entry_stats(update_entry, &entry); + } + entry.user_stats_sync = true; + + ret = write_entry(hctx, key, entry); + if (ret < 0) + return ret; + + add_header_stats(&header.stats, entry); + } + + bufferlist bl; + + CLS_LOG(20, "header: total bytes=%lld entries=%lld", (long long)header.stats.total_bytes, (long long)header.stats.total_entries); + + if (header.last_stats_update < op.time) + header.last_stats_update = op.time; + + encode(header, bl); + + ret = cls_cxx_map_write_header(hctx, &bl); + if (ret < 0) + return ret; + + return 0; +} + +static int cls_user_complete_stats_sync(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + auto in_iter = in->cbegin(); + + cls_user_complete_stats_sync_op op; + try { + decode(op, in_iter); + } catch (ceph::buffer::error& err) { + CLS_LOG(1, "ERROR: cls_user_add_op(): failed to decode op"); + return -EINVAL; + } + + cls_user_header header; + int ret = read_header(hctx, &header); + if (ret < 0) { + CLS_LOG(0, "ERROR: failed to read user info header ret=%d", ret); + return ret; + } + + if (header.last_stats_sync < op.time) + header.last_stats_sync = op.time; + + bufferlist bl; + + encode(header, bl); + + ret = cls_cxx_map_write_header(hctx, &bl); + if (ret < 0) + return ret; + + return 0; +} + +static int cls_user_remove_bucket(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + auto in_iter = in->cbegin(); + + cls_user_remove_bucket_op op; + try { + decode(op, in_iter); + } catch (ceph::buffer::error& err) { + CLS_LOG(1, "ERROR: cls_user_add_op(): failed to decode op"); + return -EINVAL; + } + + cls_user_header header; + int ret = read_header(hctx, &header); + if (ret < 0) { + CLS_LOG(0, "ERROR: failed to read user info header ret=%d", ret); + return ret; + } + + string key; + + get_key_by_bucket_name(op.bucket.name, &key); + + cls_user_bucket_entry entry; + ret = get_existing_bucket_entry(hctx, key, entry); + if (ret == -ENOENT) { + return 0; /* idempotent removal */ + } + if (ret < 0) { + CLS_LOG(0, "ERROR: get existing bucket entry, key=%s ret=%d", key.c_str(), ret); + return ret; + } + + CLS_LOG(20, "removing entry at %s", key.c_str()); + + ret = remove_entry(hctx, key); + if (ret < 0) + return ret; + + if (!entry.user_stats_sync) { + return 0; + } + + dec_header_stats(&header.stats, entry); + + CLS_LOG(20, "header: total bytes=%lld entries=%lld", (long long)header.stats.total_bytes, (long long)header.stats.total_entries); + + bufferlist bl; + encode(header, bl); + return cls_cxx_map_write_header(hctx, &bl); +} + +static int cls_user_list_buckets(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + auto in_iter = in->cbegin(); + + cls_user_list_buckets_op op; + try { + decode(op, in_iter); + } catch (ceph::buffer::error& err) { + CLS_LOG(1, "ERROR: cls_user_list_op(): failed to decode op"); + return -EINVAL; + } + + map<string, bufferlist> keys; + + const string& from_index = op.marker; + const string& to_index = op.end_marker; + const bool to_index_valid = !to_index.empty(); + +#define MAX_ENTRIES 1000 + size_t max_entries = op.max_entries; + if (max_entries > MAX_ENTRIES) + max_entries = MAX_ENTRIES; + + string match_prefix; + cls_user_list_buckets_ret ret; + + int rc = cls_cxx_map_get_vals(hctx, from_index, match_prefix, max_entries, &keys, &ret.truncated); + if (rc < 0) + return rc; + + CLS_LOG(20, "from_index=%s to_index=%s match_prefix=%s", + from_index.c_str(), + to_index.c_str(), + match_prefix.c_str()); + + auto& entries = ret.entries; + auto iter = keys.begin(); + + string marker; + + for (; iter != keys.end(); ++iter) { + const string& index = iter->first; + marker = index; + + if (to_index_valid && to_index.compare(index) <= 0) { + ret.truncated = false; + break; + } + + bufferlist& bl = iter->second; + auto biter = bl.cbegin(); + try { + cls_user_bucket_entry e; + decode(e, biter); + entries.push_back(e); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: cls_user_list: could not decode entry, index=%s", index.c_str()); + } + } + + if (ret.truncated) { + ret.marker = marker; + } + + encode(ret, *out); + + return 0; +} + +static int cls_user_get_header(cls_method_context_t hctx, bufferlist *in, bufferlist *out) +{ + auto in_iter = in->cbegin(); + + cls_user_get_header_op op; + try { + decode(op, in_iter); + } catch (ceph::buffer::error& err) { + CLS_LOG(1, "ERROR: cls_user_get_header_op(): failed to decode op"); + return -EINVAL; + } + + cls_user_get_header_ret op_ret; + + int ret = read_header(hctx, &op_ret.header); + if (ret < 0) + return ret; + + encode(op_ret, *out); + + return 0; +} + +/// A method to reset the user.buckets header stats in accordance to +/// the values seen in the user.buckets omap keys. This is not be +/// equivalent to --sync-stats which also re-calculates the stats for +/// each bucket. +static int cls_user_reset_stats(cls_method_context_t hctx, + bufferlist *in, bufferlist *out /*ignore*/) +{ + cls_user_reset_stats_op op; + + try { + auto bliter = in->cbegin(); + decode(op, bliter); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: %s failed to decode op", __func__); + return -EINVAL; + } + + cls_user_header header; + bool truncated = false; + string from_index, prefix; + do { + map<string, bufferlist> keys; + int rc = cls_cxx_map_get_vals(hctx, from_index, prefix, MAX_ENTRIES, + &keys, &truncated); + if (rc < 0) { + CLS_LOG(0, "ERROR: %s failed to retrieve omap key-values", __func__); + return rc; + } + CLS_LOG(20, "%s: read %lu key-values, truncated=%d", + __func__, keys.size(), truncated); + + for (const auto& kv : keys) { + cls_user_bucket_entry e; + try { + auto bl = kv.second; + auto bliter = bl.cbegin(); + decode(e, bliter); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: %s failed to decode bucket entry for %s", + __func__, kv.first.c_str()); + return -EIO; + } + add_header_stats(&header.stats, e); + } + if (!keys.empty()) { + from_index = keys.rbegin()->first; + } + } while (truncated); + + bufferlist bl; + header.last_stats_update = op.time; + encode(header, bl); + + CLS_LOG(20, "%s: updating header", __func__); + return cls_cxx_map_write_header(hctx, &bl); +} /* legacy cls_user_reset_stats */ + +/// A method to reset the user.buckets header stats in accordance to +/// the values seen in the user.buckets omap keys. This is not be +/// equivalent to --sync-stats which also re-calculates the stats for +/// each bucket. +static int cls_user_reset_stats2(cls_method_context_t hctx, + buffer::list *in, buffer::list *out) +{ + cls_user_reset_stats2_op op; + + try { + auto bliter = in->cbegin(); + decode(op, bliter); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: %s failed to decode op", __func__); + return -EINVAL; + } + + cls_user_header header; + string from_index{op.marker}, prefix; + cls_user_reset_stats2_ret ret; + + map<string, buffer::list> keys; + int rc = cls_cxx_map_get_vals(hctx, from_index, prefix, MAX_ENTRIES, + &keys, &ret.truncated); + if (rc < 0) { + CLS_LOG(0, "ERROR: %s failed to retrieve omap key-values", __func__); + return rc; + } + CLS_LOG(20, "%s: read %lu key-values, truncated=%d", + __func__, keys.size(), ret.truncated); + + for (const auto& kv : keys) { + cls_user_bucket_entry e; + try { + auto& bl = kv.second; + auto bliter = bl.cbegin(); + decode(e, bliter); + } catch (ceph::buffer::error& err) { + CLS_LOG(0, "ERROR: %s failed to decode bucket entry for %s", + __func__, kv.first.c_str()); + return -EIO; + } + add_header_stats(&ret.acc_stats, e); + } + + /* try-update marker */ + if(!keys.empty()) + ret.marker = (--keys.cend())->first; + + if (! ret.truncated) { + buffer::list bl; + header.last_stats_update = op.time; + header.stats = ret.acc_stats; + encode(header, bl); + + CLS_LOG(20, "%s: updating header", __func__); + rc = cls_cxx_map_write_header(hctx, &bl); + + /* return final result */ + encode(ret, *out); + return rc; + } + + /* return partial result */ + encode(ret, *out); + return 0; +} /* cls_user_reset_stats2 */ + +CLS_INIT(user) +{ + CLS_LOG(1, "Loaded user class!"); + + cls_handle_t h_class; + cls_method_handle_t h_user_set_buckets_info; + cls_method_handle_t h_user_complete_stats_sync; + cls_method_handle_t h_user_remove_bucket; + cls_method_handle_t h_user_list_buckets; + cls_method_handle_t h_user_get_header; + cls_method_handle_t h_user_reset_stats; + cls_method_handle_t h_user_reset_stats2; + + 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, "complete_stats_sync", CLS_METHOD_RD | CLS_METHOD_WR, + cls_user_complete_stats_sync, &h_user_complete_stats_sync); + 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); + cls_register_cxx_method(h_class, "get_header", CLS_METHOD_RD, cls_user_get_header, &h_user_get_header); + cls_register_cxx_method(h_class, "reset_user_stats", CLS_METHOD_RD | CLS_METHOD_WR, cls_user_reset_stats, &h_user_reset_stats); + cls_register_cxx_method(h_class, "reset_user_stats2", CLS_METHOD_RD | CLS_METHOD_WR, cls_user_reset_stats2, &h_user_reset_stats2); + + return; +} diff --git a/src/cls/user/cls_user_client.cc b/src/cls/user/cls_user_client.cc new file mode 100644 index 000000000..b74f55b48 --- /dev/null +++ b/src/cls/user/cls_user_client.cc @@ -0,0 +1,164 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include <errno.h> + +#include "cls/user/cls_user_client.h" +#include "include/rados/librados.hpp" + +using std::list; +using std::string; + +using ceph::bufferlist; +using ceph::real_clock; + +using librados::IoCtx; +using librados::ObjectOperationCompletion; +using librados::ObjectReadOperation; + +void cls_user_set_buckets(librados::ObjectWriteOperation& op, list<cls_user_bucket_entry>& entries, bool add) +{ + bufferlist in; + cls_user_set_buckets_op call; + call.entries = entries; + call.add = add; + call.time = real_clock::now(); + encode(call, in); + op.exec("user", "set_buckets_info", in); +} + +void cls_user_complete_stats_sync(librados::ObjectWriteOperation& op) +{ + bufferlist in; + cls_user_complete_stats_sync_op call; + call.time = real_clock::now(); + encode(call, in); + op.exec("user", "complete_stats_sync", 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<cls_user_bucket_entry> *entries; + string *marker; + bool *truncated; + int *pret; +public: + ClsUserListCtx(list<cls_user_bucket_entry> *_entries, string *_marker, bool *_truncated, int *_pret) : + entries(_entries), marker(_marker), truncated(_truncated), pret(_pret) {} + void handle_completion(int r, bufferlist& outbl) override { + if (r >= 0) { + cls_user_list_buckets_ret ret; + try { + auto iter = outbl.cbegin(); + decode(ret, iter); + if (entries) + *entries = ret.entries; + if (truncated) + *truncated = ret.truncated; + if (marker) + *marker = ret.marker; + } catch (ceph::buffer::error& err) { + r = -EIO; + } + } + if (pret) { + *pret = r; + } + } +}; + +void cls_user_bucket_list(librados::ObjectReadOperation& op, + const string& in_marker, + const string& end_marker, + int max_entries, + list<cls_user_bucket_entry>& entries, + string *out_marker, + bool *truncated, + int *pret) +{ + bufferlist inbl; + cls_user_list_buckets_op call; + call.marker = in_marker; + call.end_marker = end_marker; + call.max_entries = max_entries; + + encode(call, inbl); + + op.exec("user", "list_buckets", inbl, new ClsUserListCtx(&entries, out_marker, truncated, pret)); +} + +class ClsUserGetHeaderCtx : public ObjectOperationCompletion { + cls_user_header *header; + RGWGetUserHeader_CB *ret_ctx; + int *pret; +public: + ClsUserGetHeaderCtx(cls_user_header *_h, RGWGetUserHeader_CB *_ctx, int *_pret) : header(_h), ret_ctx(_ctx), pret(_pret) {} + ~ClsUserGetHeaderCtx() override { + if (ret_ctx) { + ret_ctx->put(); + } + } + void handle_completion(int r, bufferlist& outbl) override { + if (r >= 0) { + cls_user_get_header_ret ret; + try { + auto iter = outbl.cbegin(); + decode(ret, iter); + if (header) + *header = ret.header; + } catch (ceph::buffer::error& err) { + r = -EIO; + } + if (ret_ctx) { + ret_ctx->handle_response(r, ret.header); + } + } + if (pret) { + *pret = r; + } + } +}; + +void cls_user_get_header(librados::ObjectReadOperation& op, + cls_user_header *header, int *pret) +{ + bufferlist inbl; + cls_user_get_header_op call; + + encode(call, inbl); + + op.exec("user", "get_header", inbl, new ClsUserGetHeaderCtx(header, NULL, pret)); +} + +void cls_user_reset_stats(librados::ObjectWriteOperation &op) +{ + bufferlist inbl; + cls_user_reset_stats_op call; + call.time = real_clock::now(); + encode(call, inbl); + op.exec("user", "reset_user_stats", inbl); +} + +int cls_user_get_header_async(IoCtx& io_ctx, string& oid, RGWGetUserHeader_CB *ctx) +{ + bufferlist in, out; + cls_user_get_header_op call; + encode(call, in); + ObjectReadOperation op; + op.exec("user", "get_header", in, new ClsUserGetHeaderCtx(NULL, ctx, NULL)); /* no need to pass pret, as we'll call ctx->handle_response() with correct error */ + auto c = librados::Rados::aio_create_completion(nullptr, nullptr); + int r = io_ctx.aio_operate(oid, c, &op, NULL); + c->release(); + if (r < 0) + return r; + + return 0; +} diff --git a/src/cls/user/cls_user_client.h b/src/cls/user/cls_user_client.h new file mode 100644 index 000000000..03d975c59 --- /dev/null +++ b/src/cls/user/cls_user_client.h @@ -0,0 +1,36 @@ +// -*- 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/rados/librados_fwd.hpp" +#include "cls_user_ops.h" +#include "common/RefCountedObj.h" + +class RGWGetUserHeader_CB : public RefCountedObject { +public: + ~RGWGetUserHeader_CB() override {} + virtual void handle_response(int r, cls_user_header& header) = 0; +}; + +/* + * user objclass + */ + +void cls_user_set_buckets(librados::ObjectWriteOperation& op, std::list<cls_user_bucket_entry>& entries, bool add); +void cls_user_complete_stats_sync(librados::ObjectWriteOperation& op); +void cls_user_remove_bucket(librados::ObjectWriteOperation& op, const cls_user_bucket& bucket); +void cls_user_bucket_list(librados::ObjectReadOperation& op, + const std::string& in_marker, + const std::string& end_marker, + int max_entries, + std::list<cls_user_bucket_entry>& entries, + std::string *out_marker, + bool *truncated, + int *pret); +void cls_user_get_header(librados::ObjectReadOperation& op, cls_user_header *header, int *pret); +int cls_user_get_header_async(librados::IoCtx& io_ctx, std::string& oid, RGWGetUserHeader_CB *ctx); +void cls_user_reset_stats(librados::ObjectWriteOperation& op); + +#endif diff --git a/src/cls/user/cls_user_ops.cc b/src/cls/user/cls_user_ops.cc new file mode 100644 index 000000000..5ae9d2c93 --- /dev/null +++ b/src/cls/user/cls_user_ops.cc @@ -0,0 +1,118 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "cls/user/cls_user_ops.h" +#include "common/Formatter.h" +#include "common/ceph_json.h" + +using std::list; + +using ceph::Formatter; + +void cls_user_set_buckets_op::dump(Formatter *f) const +{ + encode_json("entries", entries, f); + encode_json("add", add, f); + encode_json("time", utime_t(time), f); +} + +void cls_user_set_buckets_op::generate_test_instances(list<cls_user_set_buckets_op*>& ls) +{ + ls.push_back(new cls_user_set_buckets_op); + cls_user_set_buckets_op *op = new cls_user_set_buckets_op; + for (int i = 0; i < 3; i++) { + cls_user_bucket_entry e; + cls_user_gen_test_bucket_entry(&e, i); + op->entries.push_back(e); + } + op->add = true; + op->time = utime_t(1, 0).to_real_time(); + ls.push_back(op); +} + +void cls_user_remove_bucket_op::dump(Formatter *f) const +{ + encode_json("bucket", bucket, f); +} + +void cls_user_remove_bucket_op::generate_test_instances(list<cls_user_remove_bucket_op*>& ls) +{ + ls.push_back(new cls_user_remove_bucket_op); + cls_user_remove_bucket_op *op = new cls_user_remove_bucket_op; + cls_user_gen_test_bucket(&op->bucket, 0); + ls.push_back(op); +} + +void cls_user_list_buckets_op::dump(Formatter *f) const +{ + encode_json("marker", marker, f); + encode_json("max_entries", max_entries, f); +} + +void cls_user_list_buckets_op::generate_test_instances(list<cls_user_list_buckets_op*>& ls) +{ + ls.push_back(new cls_user_list_buckets_op); + cls_user_list_buckets_op *op = new cls_user_list_buckets_op; + op->marker = "marker"; + op->max_entries = 1000; + ls.push_back(op); +} + +void cls_user_list_buckets_ret::dump(Formatter *f) const +{ + encode_json("entries", entries, f); + encode_json("marker", marker, f); + encode_json("truncated", truncated, f); +} + +void cls_user_list_buckets_ret::generate_test_instances(list<cls_user_list_buckets_ret*>& ls) +{ + ls.push_back(new cls_user_list_buckets_ret); + cls_user_list_buckets_ret *ret = new cls_user_list_buckets_ret; + for (int i = 0; i < 3; i++) { + cls_user_bucket_entry e; + cls_user_gen_test_bucket_entry(&e, i); + ret->entries.push_back(e); + } + ret->marker = "123"; + ret->truncated = true; + ls.push_back(ret); +} + +void cls_user_get_header_op::dump(Formatter *f) const +{ + // empty! +} + +void cls_user_get_header_op::generate_test_instances(list<cls_user_get_header_op*>& ls) +{ + ls.push_back(new cls_user_get_header_op); +} + +void cls_user_get_header_ret::dump(Formatter *f) const +{ + encode_json("header", header, f); +} + +void cls_user_get_header_ret::generate_test_instances(list<cls_user_get_header_ret*>& ls) +{ + ls.push_back(new cls_user_get_header_ret); + cls_user_get_header_ret *ret = new cls_user_get_header_ret; + cls_user_gen_test_header(&ret->header); + ls.push_back(ret); +} + +void cls_user_complete_stats_sync_op::dump(Formatter *f) const +{ + encode_json("time", utime_t(time), f); +} + +void cls_user_complete_stats_sync_op::generate_test_instances(list<cls_user_complete_stats_sync_op*>& ls) +{ + ls.push_back(new cls_user_complete_stats_sync_op); + cls_user_complete_stats_sync_op *op = new cls_user_complete_stats_sync_op; + op->time = utime_t(12345, 0).to_real_time(); + ls.push_back(op); +} + + diff --git a/src/cls/user/cls_user_ops.h b/src/cls/user/cls_user_ops.h new file mode 100644 index 000000000..7edd1bc15 --- /dev/null +++ b/src/cls/user/cls_user_ops.h @@ -0,0 +1,267 @@ +// -*- 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 "cls_user_types.h" + +struct cls_user_set_buckets_op { + std::list<cls_user_bucket_entry> entries; + bool add; + ceph::real_time time; /* op time */ + + cls_user_set_buckets_op() : add(false) {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(entries, bl); + encode(add, bl); + encode(time, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(entries, bl); + decode(add, bl); + decode(time, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_set_buckets_op*>& ls); +}; +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(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(bucket, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(bucket, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_remove_bucket_op*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_remove_bucket_op) + +struct cls_user_list_buckets_op { + std::string marker; + std::string end_marker; + int max_entries; /* upperbound to returned num of entries + might return less than that and still be truncated */ + + cls_user_list_buckets_op() + : max_entries(0) {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(2, 1, bl); + encode(marker, bl); + encode(max_entries, bl); + encode(end_marker, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(2, bl); + decode(marker, bl); + decode(max_entries, bl); + if (struct_v >= 2) { + decode(end_marker, bl); + } + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_list_buckets_op*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_list_buckets_op) + +struct cls_user_list_buckets_ret { + std::list<cls_user_bucket_entry> entries; + std::string marker; + bool truncated; + + cls_user_list_buckets_ret() : truncated(false) {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(entries, bl); + encode(marker, bl); + encode(truncated, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(entries, bl); + decode(marker, bl); + decode(truncated, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_list_buckets_ret*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_list_buckets_ret) + + +struct cls_user_get_header_op { + cls_user_get_header_op() {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_get_header_op*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_get_header_op) + +struct cls_user_reset_stats_op { + ceph::real_time time; + cls_user_reset_stats_op() {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(time, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(time, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_reset_stats_op*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_reset_stats_op); + +struct cls_user_reset_stats2_op { + ceph::real_time time; + std::string marker; + cls_user_stats acc_stats; + + cls_user_reset_stats2_op() {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(time, bl); + encode(marker, bl); + encode(acc_stats, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(time, bl); + decode(marker, bl); + decode(acc_stats, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_reset_stats2_op*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_reset_stats2_op); + +struct cls_user_reset_stats2_ret { + std::string marker; + cls_user_stats acc_stats; /* 0-initialized */ + bool truncated; + + cls_user_reset_stats2_ret() + : truncated(false) {} + + void update_call(cls_user_reset_stats2_op& call) { + call.marker = marker; + call.acc_stats = acc_stats; + } + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(marker, bl); + encode(acc_stats, bl); + encode(truncated, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(marker, bl); + decode(acc_stats, bl); + decode(truncated, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances( + std::list<cls_user_reset_stats2_ret*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_reset_stats2_ret); + +struct cls_user_get_header_ret { + cls_user_header header; + + cls_user_get_header_ret() {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(header, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(header, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_get_header_ret*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_get_header_ret) + +struct cls_user_complete_stats_sync_op { + ceph::real_time time; + + cls_user_complete_stats_sync_op() {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(time, bl); + ENCODE_FINISH(bl); + } + + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(time, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_complete_stats_sync_op*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_complete_stats_sync_op) + + +#endif diff --git a/src/cls/user/cls_user_types.cc b/src/cls/user/cls_user_types.cc new file mode 100644 index 000000000..0d823f0be --- /dev/null +++ b/src/cls/user/cls_user_types.cc @@ -0,0 +1,111 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "cls/user/cls_user_types.h" +#include "common/Formatter.h" +#include "common/ceph_json.h" +#include "include/utime.h" + +using std::list; +using std::string; + +using ceph::Formatter; +using ceph::bufferlist; +using ceph::real_clock; + +void cls_user_gen_test_bucket(cls_user_bucket *bucket, int i) +{ + char buf[16]; + snprintf(buf, sizeof(buf), ".%d", i); + + bucket->name = string("buck") + buf; + bucket->marker = string("mark") + buf; + bucket->bucket_id = string("bucket.id") + buf; +} + +void cls_user_bucket::dump(Formatter *f) const +{ + encode_json("name", name, f); + encode_json("marker", marker,f); + encode_json("bucket_id", bucket_id,f); +} + +void cls_user_bucket::generate_test_instances(list<cls_user_bucket*>& ls) +{ + ls.push_back(new cls_user_bucket); + cls_user_bucket *b = new cls_user_bucket; + cls_user_gen_test_bucket(b, 0); + ls.push_back(b); +} + +void cls_user_bucket_entry::dump(Formatter *f) const +{ + encode_json("bucket", bucket, f); + encode_json("size", size, f); + encode_json("size_rounded", size_rounded, f); + encode_json("creation_time", utime_t(creation_time), f); + encode_json("count", count, f); + encode_json("user_stats_sync", user_stats_sync, f); +} + +void cls_user_gen_test_bucket_entry(cls_user_bucket_entry *entry, int i) +{ + cls_user_gen_test_bucket(&entry->bucket, i); + entry->size = i + 1; + entry->size_rounded = i + 2; + entry->creation_time = real_clock::from_time_t(i + 3); + entry->count = i + 4; + entry->user_stats_sync = true; +} + +void cls_user_bucket_entry::generate_test_instances(list<cls_user_bucket_entry*>& ls) +{ + ls.push_back(new cls_user_bucket_entry); + cls_user_bucket_entry *entry = new cls_user_bucket_entry; + cls_user_gen_test_bucket_entry(entry, 0); + ls.push_back(entry); +} + +void cls_user_gen_test_stats(cls_user_stats *s) +{ + s->total_entries = 1; + s->total_bytes = 2; + s->total_bytes_rounded = 3; +} + +void cls_user_stats::dump(Formatter *f) const +{ + f->dump_int("total_entries", total_entries); + f->dump_int("total_bytes", total_bytes); + f->dump_int("total_bytes_rounded", total_bytes_rounded); +} + +void cls_user_stats::generate_test_instances(list<cls_user_stats*>& ls) +{ + ls.push_back(new cls_user_stats); + cls_user_stats *s = new cls_user_stats; + cls_user_gen_test_stats(s); + ls.push_back(s); +} + +void cls_user_gen_test_header(cls_user_header *h) +{ + cls_user_gen_test_stats(&h->stats); + h->last_stats_sync = utime_t(1, 0).to_real_time(); + h->last_stats_update = utime_t(2, 0).to_real_time(); +} + +void cls_user_header::dump(Formatter *f) const +{ + encode_json("stats", stats, f); + encode_json("last_stats_sync", utime_t(last_stats_sync), f); + encode_json("last_stats_update", utime_t(last_stats_update), f); +} + +void cls_user_header::generate_test_instances(list<cls_user_header*>& ls) +{ + ls.push_back(new cls_user_header); + cls_user_header *h = new cls_user_header; + cls_user_gen_test_header(h); + ls.push_back(h); +} diff --git a/src/cls/user/cls_user_types.h b/src/cls/user/cls_user_types.h new file mode 100644 index 000000000..a139449d3 --- /dev/null +++ b/src/cls/user/cls_user_types.h @@ -0,0 +1,224 @@ +// -*- 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" +#include "common/ceph_time.h" + +/* + * this needs to be compatible with rgw_bucket, as it replaces it + */ +struct cls_user_bucket { + std::string name; + std::string marker; + std::string bucket_id; + std::string placement_id; + struct { + std::string data_pool; + std::string index_pool; + std::string data_extra_pool; + } explicit_placement; + + void encode(ceph::buffer::list& bl) const { + /* since new version of this structure is not backward compatible, + * we have older rgw running against newer osd if we encode it + * in the new way. Only encode newer version if placement_id is + * not empty, otherwise keep handling it as before + */ + if (!placement_id.empty()) { + ENCODE_START(9, 8, bl); + encode(name, bl); + encode(marker, bl); + encode(bucket_id, bl); + encode(placement_id, bl); + ENCODE_FINISH(bl); + } else { + ENCODE_START(7, 3, bl); + encode(name, bl); + encode(explicit_placement.data_pool, bl); + encode(marker, bl); + encode(bucket_id, bl); + encode(explicit_placement.index_pool, bl); + encode(explicit_placement.data_extra_pool, bl); + ENCODE_FINISH(bl); + } + } + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START_LEGACY_COMPAT_LEN(8, 3, 3, bl); + decode(name, bl); + if (struct_v < 8) { + decode(explicit_placement.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 < 8) { + if (struct_v >= 5) { + decode(explicit_placement.index_pool, bl); + } else { + explicit_placement.index_pool = explicit_placement.data_pool; + } + if (struct_v >= 7) { + decode(explicit_placement.data_extra_pool, bl); + } + } else { + decode(placement_id, bl); + if (struct_v == 8 && placement_id.empty()) { + decode(explicit_placement.data_pool, bl); + decode(explicit_placement.index_pool, bl); + decode(explicit_placement.data_extra_pool, bl); + } + } + DECODE_FINISH(bl); + } + + bool operator<(const cls_user_bucket& b) const { + return name.compare(b.name) < 0; + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_bucket*>& ls); +}; +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; + ceph::real_time creation_time; + uint64_t count; + bool user_stats_sync; + + cls_user_bucket_entry() : size(0), size_rounded(0), count(0), user_stats_sync(false) {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(9, 5, bl); + uint64_t s = size; + __u32 mt = ceph::real_clock::to_time_t(creation_time); + std::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(user_stats_sync, bl); + encode(creation_time, bl); + //::encode(placement_rule, bl); removed in v9 + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START_LEGACY_COMPAT_LEN(9, 5, 5, bl); + __u32 mt; + uint64_t s; + std::string empty_str; // backward compatibility + decode(empty_str, bl); + decode(s, bl); + decode(mt, bl); + size = s; + if (struct_v < 7) { + creation_time = ceph::real_clock::from_time_t(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; + if (struct_v >= 6) + decode(user_stats_sync, bl); + if (struct_v >= 7) + decode(creation_time, bl); + if (struct_v == 8) { // added in v8, removed in v9 + std::string placement_rule; + decode(placement_rule, bl); + } + DECODE_FINISH(bl); + } + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_bucket_entry*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_bucket_entry) + +struct cls_user_stats { + uint64_t total_entries; + uint64_t total_bytes; + uint64_t total_bytes_rounded; + + cls_user_stats() + : total_entries(0), + total_bytes(0), + total_bytes_rounded(0) {} + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(total_entries, bl); + encode(total_bytes, bl); + encode(total_bytes_rounded, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(total_entries, bl); + decode(total_bytes, bl); + decode(total_bytes_rounded, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_stats*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_stats) + +/* + * this needs to be compatible with rgw_bucket, as it replaces it + */ +struct cls_user_header { + cls_user_stats stats; + ceph::real_time last_stats_sync; /* last time a full stats sync completed */ + ceph::real_time last_stats_update; /* last time a stats update was done */ + + void encode(ceph::buffer::list& bl) const { + ENCODE_START(1, 1, bl); + encode(stats, bl); + encode(last_stats_sync, bl); + encode(last_stats_update, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator& bl) { + DECODE_START(1, bl); + decode(stats, bl); + decode(last_stats_sync, bl); + decode(last_stats_update, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter *f) const; + static void generate_test_instances(std::list<cls_user_header*>& ls); +}; +WRITE_CLASS_ENCODER(cls_user_header) + +void cls_user_gen_test_bucket(cls_user_bucket *bucket, int i); +void cls_user_gen_test_bucket_entry(cls_user_bucket_entry *entry, int i); +void cls_user_gen_test_stats(cls_user_stats *stats); +void cls_user_gen_test_header(cls_user_header *h); + +#endif |