summaryrefslogtreecommitdiffstats
path: root/src/cls/user
diff options
context:
space:
mode:
Diffstat (limited to 'src/cls/user')
-rw-r--r--src/cls/user/cls_user.cc455
-rw-r--r--src/cls/user/cls_user_client.cc158
-rw-r--r--src/cls/user/cls_user_client.h36
-rw-r--r--src/cls/user/cls_user_ops.cc114
-rw-r--r--src/cls/user/cls_user_ops.h204
-rw-r--r--src/cls/user/cls_user_types.cc104
-rw-r--r--src/cls/user/cls_user_types.h226
7 files changed, 1297 insertions, 0 deletions
diff --git a/src/cls/user/cls_user.cc b/src/cls/user/cls_user.cc
new file mode 100644
index 00000000..d3015a12
--- /dev/null
+++ b/src/cls/user/cls_user.cc
@@ -0,0 +1,455 @@
+// -*- 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"
+
+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 (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 (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 (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 (list<cls_user_bucket_entry>::iterator 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 (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 (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 (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());
+
+ list<cls_user_bucket_entry>& entries = ret.entries;
+ map<string, bufferlist>::iterator 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 (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 (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 (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 (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);
+}
+
+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_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);
+ return;
+}
+
diff --git a/src/cls/user/cls_user_client.cc b/src/cls/user/cls_user_client.cc
new file mode 100644
index 00000000..c18be4a2
--- /dev/null
+++ b/src/cls/user/cls_user_client.cc
@@ -0,0 +1,158 @@
+// -*- 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 namespace librados;
+
+
+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 (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 (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 */
+ AioCompletion *c = librados::Rados::aio_create_completion(NULL, NULL, NULL);
+ 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 00000000..077c15dc
--- /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, 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 string& in_marker,
+ const string& end_marker,
+ int max_entries,
+ list<cls_user_bucket_entry>& entries,
+ 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, 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 00000000..65d889bb
--- /dev/null
+++ b/src/cls/user/cls_user_ops.cc
@@ -0,0 +1,114 @@
+// -*- 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"
+
+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 00000000..fa4b1f31
--- /dev/null
+++ b/src/cls/user/cls_user_ops.h
@@ -0,0 +1,204 @@
+// -*- 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 {
+ list<cls_user_bucket_entry> entries;
+ bool add;
+ real_time time; /* op time */
+
+ cls_user_set_buckets_op() : add(false) {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(entries, bl);
+ encode(add, bl);
+ encode(time, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(entries, bl);
+ decode(add, bl);
+ decode(time, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ static void generate_test_instances(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(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(bucket, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(bucket, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_user_remove_bucket_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_remove_bucket_op)
+
+struct cls_user_list_buckets_op {
+ string marker;
+ 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(bufferlist& bl) const {
+ ENCODE_START(2, 1, bl);
+ encode(marker, bl);
+ encode(max_entries, bl);
+ encode(end_marker, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::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(Formatter *f) const;
+ static void generate_test_instances(list<cls_user_list_buckets_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_list_buckets_op)
+
+struct cls_user_list_buckets_ret {
+ list<cls_user_bucket_entry> 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::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(entries, bl);
+ decode(marker, bl);
+ decode(truncated, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ static void generate_test_instances(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(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_user_get_header_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_get_header_op)
+
+struct cls_user_reset_stats_op {
+ real_time time;
+ cls_user_reset_stats_op() {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(time, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(time, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_user_reset_stats_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_reset_stats_op);
+
+struct cls_user_get_header_ret {
+ cls_user_header header;
+
+ cls_user_get_header_ret() {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(header, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(header, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_user_get_header_ret*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_get_header_ret)
+
+struct cls_user_complete_stats_sync_op {
+ real_time time;
+
+ cls_user_complete_stats_sync_op() {}
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(time, bl);
+ ENCODE_FINISH(bl);
+ }
+
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(time, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(Formatter *f) const;
+ static void generate_test_instances(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 00000000..be3280ca
--- /dev/null
+++ b/src/cls/user/cls_user_types.cc
@@ -0,0 +1,104 @@
+// -*- 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"
+
+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 00000000..8efd8bc5
--- /dev/null
+++ b/src/cls/user/cls_user_types.h
@@ -0,0 +1,226 @@
+// -*- 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(bufferlist& 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(bufferlist::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(Formatter *f) const;
+ static void generate_test_instances(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(bufferlist& bl) const {
+ ENCODE_START(9, 5, bl);
+ uint64_t s = size;
+ __u32 mt = ceph::real_clock::to_time_t(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(user_stats_sync, bl);
+ encode(creation_time, bl);
+ //::encode(placement_rule, bl); removed in v9
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::const_iterator& bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(9, 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;
+ 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(Formatter *f) const;
+ static void generate_test_instances(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(bufferlist& 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(bufferlist::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(Formatter *f) const;
+ static void generate_test_instances(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(bufferlist& 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(bufferlist::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(Formatter *f) const;
+ static void generate_test_instances(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
+
+