// vim: ts=8 sw=2 smarttab
#include <errno.h>
+#include <algorithm>
+#include <cctype>
#include "include/utime.h"
#include "objclass/objclass.h"
#include "cls_user_ops.h"
+#include "rgw/rgw_string.h"
using std::map;
using std::string;
return 0;
}
-static int read_header(cls_method_context_t hctx, cls_user_header *header)
+template <typename T>
+static int read_header(cls_method_context_t hctx, T *header)
{
bufferlist bl;
return ret;
if (bl.length() == 0) {
- *header = cls_user_header();
+ *header = T();
return 0;
}
return 0;
} /* cls_user_reset_stats2 */
+
+// account resource names must be unique and aren't distinguished by case, so
+// convert all keys to lowercase
+static std::string resource_key(std::string_view name)
+{
+ std::string key;
+ key.resize(name.size());
+ std::transform(name.begin(), name.end(), key.begin(),
+ [](unsigned char c) { return std::tolower(c); });
+ return key;
+}
+
+static int cls_account_resource_add(cls_method_context_t hctx,
+ buffer::list *in, buffer::list *out)
+{
+ cls_user_account_resource_add_op op;
+ try {
+ auto bliter = in->cbegin();
+ decode(op, bliter);
+ } catch (const ceph::buffer::error& err) {
+ CLS_LOG(0, "ERROR: %s failed to decode op", __func__);
+ return -EINVAL;
+ }
+
+ CLS_LOG(20, "adding account resource name=%s path=%s",
+ op.entry.name.c_str(), op.entry.path.c_str());
+
+ const std::string key = resource_key(op.entry.name);
+
+ // does this resource entry exist?
+ bufferlist readbl; // unused
+ int ret = cls_cxx_map_get_val(hctx, key, &readbl);
+ if (ret < 0 && ret != -ENOENT) {
+ return ret;
+ }
+ const bool exists = (ret == 0);
+
+ std::optional<cls_user_account_header> header;
+ if (!exists) {
+ // if this is a new entry, update the resource count in the account header
+ ret = read_header(hctx, &header.emplace());
+ if (ret < 0) {
+ CLS_LOG(0, "ERROR: failed to read account header ret=%d", ret);
+ return ret;
+ }
+ if (header->count >= op.limit) {
+ CLS_LOG(4, "account resource limit exceeded, %u >= %u",
+ header->count, op.limit);
+ return -EUSERS; // too many users
+ }
+ header->count++;
+ } else if (op.exclusive) {
+ return -EEXIST;
+ }
+
+ // write/overwrite the entry
+ bufferlist writebl;
+ encode(op.entry, writebl);
+ ret = cls_cxx_map_set_val(hctx, key, &writebl);
+ if (ret < 0) {
+ CLS_LOG(0, "ERROR: failed to write account resource: %d", ret);
+ return ret;
+ }
+
+ // write the updated account header
+ if (header) {
+ bufferlist headerbl;
+ encode(*header, headerbl);
+ return cls_cxx_map_write_header(hctx, &headerbl);
+ }
+ return 0;
+} // cls_account_resource_add
+
+static int cls_account_resource_get(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ cls_user_account_resource_get_op op;
+ try {
+ auto p = in->cbegin();
+ decode(op, p);
+ } catch (const ceph::buffer::error& err) {
+ CLS_LOG(0, "ERROR: %s failed to decode op", __func__);
+ return -EINVAL;
+ }
+
+ CLS_LOG(20, "reading account resource name=%s", op.name.c_str());
+
+ const std::string key = resource_key(op.name);
+
+ bufferlist bl;
+ int r = cls_cxx_map_get_val(hctx, key, &bl);
+ if (r < 0) {
+ return r;
+ }
+
+ cls_user_account_resource_get_ret ret;
+ try {
+ auto iter = bl.cbegin();
+ decode(ret.entry, iter);
+ } catch (ceph::buffer::error& err) {
+ CLS_LOG(0, "ERROR: failed to decode entry %s", key.c_str());
+ return -EIO;
+ }
+
+ encode(ret, *out);
+ return 0;
+} // cls_account_resource_get
+
+static int cls_account_resource_rm(cls_method_context_t hctx,
+ buffer::list *in, buffer::list *out)
+{
+ cls_user_account_resource_rm_op op;
+ try {
+ auto bliter = in->cbegin();
+ decode(op, bliter);
+ } catch (const ceph::buffer::error& err) {
+ CLS_LOG(0, "ERROR: %s failed to decode op", __func__);
+ return -EINVAL;
+ }
+
+ CLS_LOG(20, "removing account resource name=%s", op.name.c_str());
+
+ const std::string key = resource_key(op.name);
+
+ // verify that the resource entry exists, so we can return ENOENT otherwise.
+ // remove_key() alone would return success either way
+ bufferlist readbl; // unused
+ int ret = cls_cxx_map_get_val(hctx, key, &readbl);
+ if (ret < 0) {
+ return ret;
+ }
+
+ // remove the resource entry
+ ret = cls_cxx_map_remove_key(hctx, key);
+ if (ret < 0) {
+ CLS_LOG(0, "ERROR: failed to remove account resource: %d", ret);
+ return ret;
+ }
+
+ // update resource count in the account header
+ cls_user_account_header header;
+ ret = read_header(hctx, &header);
+ if (ret < 0) {
+ CLS_LOG(0, "ERROR: failed to read account header ret=%d", ret);
+ return ret;
+ }
+ if (header.count) { // guard underflow
+ header.count--;
+ }
+
+ bufferlist headerbl;
+ encode(header, headerbl);
+ return cls_cxx_map_write_header(hctx, &headerbl);
+} // cls_account_resource_rm
+
+static int cls_account_resource_list(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ cls_user_account_resource_list_op op;
+ try {
+ auto p = in->cbegin();
+ decode(op, p);
+ } catch (const ceph::buffer::error& err) {
+ CLS_LOG(0, "ERROR: %s failed to decode op", __func__);
+ return -EINVAL;
+ }
+ CLS_LOG(20, "listing account resources from marker=%s path_prefix=%s max_entries=%d",
+ op.marker.c_str(), op.path_prefix.c_str(), (int)op.max_entries);
+
+ const std::string prefix; // empty
+ const uint32_t max_entries = std::min(op.max_entries, 1000u);
+ std::map<std::string, bufferlist> entries;
+ bool truncated = false;
+
+ int rc = cls_cxx_map_get_vals(hctx, op.marker, prefix, max_entries,
+ &entries, &truncated);
+ if (rc < 0) {
+ return rc;
+ }
+
+ cls_user_account_resource_list_ret ret;
+
+ // copy matching decoded omap values into a vector
+ for (auto& [key, bl] : entries) {
+ // decode as cls_user_account_resource
+ cls_user_account_resource entry;
+ try {
+ auto p = bl.cbegin();
+ decode(entry, p);
+ } catch (const ceph::buffer::error& e) {
+ CLS_LOG(1, "ERROR: %s failed to decode resource entry at key=%s",
+ __func__, key.c_str());
+ return -EIO;
+ }
+
+ // filter entries by path prefix
+ if (entry.path.starts_with(op.path_prefix)) {
+ CLS_LOG(20, "included resource path=%s name=%s",
+ entry.path.c_str(), entry.name.c_str());
+ ret.entries.push_back(std::move(entry));
+ }
+ }
+
+ ret.truncated = truncated;
+ if (!entries.empty()) {
+ ret.marker = entries.rbegin()->first;
+ }
+ CLS_LOG(20, "entries=%d next_marker=%s truncated=%d",
+ (int)ret.entries.size(), ret.marker.c_str(), (int)ret.truncated);
+
+ encode(ret, *out);
+ return 0;
+} // cls_account_resource_list
+
+
CLS_INIT(user)
{
CLS_LOG(1, "Loaded user class!");
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;
+ // account
+ cls_method_handle_t h_account_resource_add;
+ cls_method_handle_t h_account_resource_get;
+ cls_method_handle_t h_account_resource_rm;
+ cls_method_handle_t h_account_resource_list;
+
+ cls_register_cxx_method(h_class, "account_resource_add", CLS_METHOD_RD | CLS_METHOD_WR,
+ cls_account_resource_add, &h_account_resource_add);
+ cls_register_cxx_method(h_class, "account_resource_get", CLS_METHOD_RD,
+ cls_account_resource_get, &h_account_resource_get);
+ cls_register_cxx_method(h_class, "account_resource_rm", CLS_METHOD_RD | CLS_METHOD_WR,
+ cls_account_resource_rm, &h_account_resource_rm);
+ cls_register_cxx_method(h_class, "account_resource_list", CLS_METHOD_RD,
+ cls_account_resource_list, &h_account_resource_list);
}
return 0;
}
+
+
+void cls_user_account_resource_add(librados::ObjectWriteOperation& op,
+ const cls_user_account_resource& entry,
+ bool exclusive, uint32_t limit)
+{
+ cls_user_account_resource_add_op call;
+ call.entry = entry;
+ call.exclusive = exclusive;
+ call.limit = limit;
+
+ bufferlist inbl;
+ encode(call, inbl);
+ op.exec("user", "account_resource_add", inbl);
+}
+
+class ResourceGetCB : public librados::ObjectOperationCompletion {
+ cls_user_account_resource* entry;
+ int* pret;
+public:
+ ResourceGetCB(cls_user_account_resource* entry, int* pret)
+ : entry(entry), pret(pret)
+ {}
+ void handle_completion(int r, bufferlist& outbl) override {
+ if (r >= 0) {
+ cls_user_account_resource_get_ret ret;
+ try {
+ auto iter = outbl.cbegin();
+ decode(ret, iter);
+ if (entry) {
+ *entry = std::move(ret.entry);
+ }
+ } catch (const ceph::buffer::error& err) {
+ r = -EIO;
+ }
+ }
+ if (pret) {
+ *pret = r;
+ }
+ }
+};
+
+void cls_user_account_resource_get(librados::ObjectReadOperation& op,
+ std::string_view name,
+ cls_user_account_resource& entry,
+ int* pret)
+{
+ cls_user_account_resource_get_op call;
+ call.name = name;
+
+ bufferlist inbl;
+ encode(call, inbl);
+ op.exec("user", "account_resource_get", inbl,
+ new ResourceGetCB(&entry, pret));
+}
+
+void cls_user_account_resource_rm(librados::ObjectWriteOperation& op,
+ std::string_view name)
+{
+ cls_user_account_resource_rm_op call;
+ call.name = name;
+
+ bufferlist inbl;
+ encode(call, inbl);
+ op.exec("user", "account_resource_rm", inbl);
+}
+
+class ResourceListCB : public librados::ObjectOperationCompletion {
+ std::vector<cls_user_account_resource>* entries;
+ bool* truncated;
+ std::string* next_marker;
+ int* pret;
+public:
+ ResourceListCB(std::vector<cls_user_account_resource>* entries,
+ bool* truncated, std::string* next_marker, int* pret)
+ : entries(entries), truncated(truncated),
+ next_marker(next_marker), pret(pret)
+ {}
+ void handle_completion(int r, bufferlist& outbl) override {
+ if (r >= 0) {
+ cls_user_account_resource_list_ret ret;
+ try {
+ auto iter = outbl.cbegin();
+ decode(ret, iter);
+ if (entries) {
+ *entries = std::move(ret.entries);
+ }
+ if (next_marker) {
+ *next_marker = std::move(ret.marker);
+ }
+ if (truncated) {
+ *truncated = ret.truncated;
+ }
+ } catch (const ceph::buffer::error& err) {
+ r = -EIO;
+ }
+ }
+ if (pret) {
+ *pret = r;
+ }
+ }
+};
+
+void cls_user_account_resource_list(librados::ObjectReadOperation& op,
+ std::string_view marker,
+ std::string_view path_prefix,
+ uint32_t max_entries,
+ std::vector<cls_user_account_resource>& entries,
+ bool* truncated, std::string* next_marker,
+ int* pret)
+{
+ cls_user_account_resource_list_op call;
+ call.marker = marker;
+ call.path_prefix = path_prefix;
+ call.max_entries = max_entries;
+
+ bufferlist inbl;
+ encode(call, inbl);
+ op.exec("user", "account_resource_list", inbl,
+ new ResourceListCB(&entries, truncated, next_marker, 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);
+// Account resources
+
+/// Add or overwrite an entry to the account's list of resources. Returns
+/// -EUSERS (Too many users) if the resource count would exceed the given limit.
+void cls_user_account_resource_add(librados::ObjectWriteOperation& op,
+ const cls_user_account_resource& entry,
+ bool exclusive, uint32_t limit);
+
+/// Look up an account resource by case-insensitive name.
+void cls_user_account_resource_get(librados::ObjectReadOperation& op,
+ std::string_view name,
+ cls_user_account_resource& entry,
+ int* pret);
+
+/// Remove an account resources by case-insensitive name.
+void cls_user_account_resource_rm(librados::ObjectWriteOperation& op,
+ std::string_view name);
+
+/// List the resources linked to an account.
+void cls_user_account_resource_list(librados::ObjectReadOperation& op,
+ std::string_view marker,
+ std::string_view path_prefix,
+ uint32_t max_entries,
+ std::vector<cls_user_account_resource>& entries,
+ bool* truncated, std::string* next_marker,
+ int* pret);
+
#endif
}
+void cls_user_account_resource_add_op::dump(Formatter *f) const
+{
+ encode_json("name", entry.name, f);
+ encode_json("path", entry.path, f);
+ encode_json("limit", limit, f);
+}
+
+void cls_user_account_resource_add_op::generate_test_instances(std::list<cls_user_account_resource_add_op*>& ls)
+{
+ ls.push_back(new cls_user_account_resource_add_op);
+ cls_user_account_resource_add_op *op = new cls_user_account_resource_add_op;
+ cls_user_gen_test_resource(op->entry);
+ ls.push_back(op);
+}
+
+void cls_user_account_resource_get_op::dump(Formatter *f) const
+{
+ encode_json("name", name, f);
+}
+
+void cls_user_account_resource_get_op::generate_test_instances(std::list<cls_user_account_resource_get_op*>& ls)
+{
+ ls.push_back(new cls_user_account_resource_get_op);
+ cls_user_account_resource_get_op *op = new cls_user_account_resource_get_op;
+ op->name = "name";
+ ls.push_back(op);
+}
+
+void cls_user_account_resource_get_ret::dump(Formatter *f) const
+{
+ encode_json("entry", entry, f);
+}
+
+void cls_user_account_resource_get_ret::generate_test_instances(std::list<cls_user_account_resource_get_ret*>& ls)
+{
+ ls.push_back(new cls_user_account_resource_get_ret);
+ cls_user_account_resource_get_ret *ret = new cls_user_account_resource_get_ret;
+ cls_user_gen_test_resource(ret->entry);
+ ls.push_back(ret);
+}
+
+void cls_user_account_resource_rm_op::dump(Formatter *f) const
+{
+ encode_json("name", name, f);
+}
+
+void cls_user_account_resource_rm_op::generate_test_instances(std::list<cls_user_account_resource_rm_op*>& ls)
+{
+ ls.push_back(new cls_user_account_resource_rm_op);
+ cls_user_account_resource_rm_op *op = new cls_user_account_resource_rm_op;
+ op->name = "name";
+ ls.push_back(op);
+}
+
+void cls_user_account_resource_list_op::dump(Formatter *f) const
+{
+ encode_json("marker", marker, f);
+ encode_json("path_prefix", path_prefix, f);
+ encode_json("max_entries", max_entries, f);
+}
+
+void cls_user_account_resource_list_op::generate_test_instances(std::list<cls_user_account_resource_list_op*>& ls)
+{
+ ls.push_back(new cls_user_account_resource_list_op);
+ cls_user_account_resource_list_op *op = new cls_user_account_resource_list_op;
+ op->marker = "marker";
+ op->path_prefix = "path";
+ op->max_entries = 20;
+ ls.push_back(op);
+}
+
+void cls_user_account_resource_list_ret::dump(Formatter *f) const
+{
+ encode_json("entries", entries, f);
+ encode_json("truncated", truncated, f);
+ encode_json("marker", marker, f);
+}
+
+void cls_user_account_resource_list_ret::generate_test_instances(std::list<cls_user_account_resource_list_ret*>& ls)
+{
+ ls.push_back(new cls_user_account_resource_list_ret);
+ cls_user_account_resource_list_ret *ret = new cls_user_account_resource_list_ret;
+ cls_user_gen_test_resource(ret->entries.emplace_back());
+ ret->truncated = true;
+ ls.push_back(ret);
+}
WRITE_CLASS_ENCODER(cls_user_complete_stats_sync_op)
+struct cls_user_account_resource_add_op {
+ cls_user_account_resource entry;
+ bool exclusive = false;
+ uint32_t limit = 0;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(entry, bl);
+ encode(exclusive, bl);
+ encode(limit, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(entry, bl);
+ decode(exclusive, bl);
+ decode(limit, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter* f) const;
+ static void generate_test_instances(std::list<cls_user_account_resource_add_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_account_resource_add_op)
+
+struct cls_user_account_resource_get_op {
+ std::string name;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(name, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(name, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter* f) const;
+ static void generate_test_instances(std::list<cls_user_account_resource_get_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_account_resource_get_op)
+
+struct cls_user_account_resource_get_ret {
+ cls_user_account_resource entry;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(entry, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(entry, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter* f) const;
+ static void generate_test_instances(std::list<cls_user_account_resource_get_ret*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_account_resource_get_ret)
+
+struct cls_user_account_resource_rm_op {
+ std::string name;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(name, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(name, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter* f) const;
+ static void generate_test_instances(std::list<cls_user_account_resource_rm_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_account_resource_rm_op)
+
+struct cls_user_account_resource_list_op {
+ std::string marker;
+ std::string path_prefix;
+ uint32_t max_entries = 0;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(marker, bl);
+ encode(path_prefix, bl);
+ encode(max_entries, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(marker, bl);
+ decode(path_prefix, bl);
+ decode(max_entries, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter* f) const;
+ static void generate_test_instances(std::list<cls_user_account_resource_list_op*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_account_resource_list_op)
+
+struct cls_user_account_resource_list_ret {
+ std::vector<cls_user_account_resource> entries;
+ bool truncated = false;
+ std::string marker;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(entries, bl);
+ encode(truncated, bl);
+ encode(marker, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(entries, bl);
+ decode(truncated, bl);
+ decode(marker, bl);
+ DECODE_FINISH(bl);
+ }
+
+ void dump(ceph::Formatter* f) const;
+ static void generate_test_instances(std::list<cls_user_account_resource_list_ret*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_account_resource_list_ret)
+
#endif
cls_user_gen_test_header(h);
ls.push_back(h);
}
+
+
+void cls_user_account_header::dump(ceph::Formatter* f) const
+{
+ encode_json("count", count, f);
+}
+
+void cls_user_account_header::generate_test_instances(std::list<cls_user_account_header*>& ls)
+{
+ ls.push_back(new cls_user_account_header);
+}
+
+void cls_user_account_resource::dump(ceph::Formatter* f) const
+{
+ encode_json("name", name, f);
+ encode_json("path", path, f);
+ // skip metadata
+}
+
+void cls_user_gen_test_resource(cls_user_account_resource& r)
+{
+ r.name = "name";
+ r.path = "path";
+}
+
+void cls_user_account_resource::generate_test_instances(std::list<cls_user_account_resource*>& ls)
+{
+ ls.push_back(new cls_user_account_resource);
+ auto p = new cls_user_account_resource;
+ cls_user_gen_test_resource(*p);
+ ls.push_back(p);
+}
};
WRITE_CLASS_ENCODER(cls_user_header)
+// omap header for an account index object
+struct cls_user_account_header {
+ uint32_t count = 0;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(count, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(count, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(ceph::Formatter* f) const;
+ static void generate_test_instances(std::list<cls_user_account_header*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_account_header)
+
+// account resource entry
+struct cls_user_account_resource {
+ // index by name for put/delete
+ std::string name;
+ // index by path for listing by PathPrefix
+ std::string path;
+ // additional opaque metadata depending on resource type
+ ceph::buffer::list metadata;
+
+ void encode(ceph::buffer::list& bl) const {
+ ENCODE_START(1, 1, bl);
+ encode(name, bl);
+ encode(path, bl);
+ encode(metadata, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(ceph::buffer::list::const_iterator& bl) {
+ DECODE_START(1, bl);
+ decode(name, bl);
+ decode(path, bl);
+ decode(metadata, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(ceph::Formatter* f) const;
+ static void generate_test_instances(std::list<cls_user_account_resource*>& ls);
+};
+WRITE_CLASS_ENCODER(cls_user_account_resource)
+
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);
+void cls_user_gen_test_resource(cls_user_account_resource& r);
#endif
TYPE(cls_user_bucket_entry)
TYPE(cls_user_stats)
TYPE(cls_user_header)
+TYPE(cls_user_account_header)
+TYPE(cls_user_account_resource)
#include "cls/user/cls_user_ops.h"
TYPE(cls_user_set_buckets_op)
TYPE(cls_user_get_header_op)
TYPE(cls_user_get_header_ret)
TYPE(cls_user_complete_stats_sync_op)
+TYPE(cls_user_account_resource_add_op)
+TYPE(cls_user_account_resource_get_op)
+TYPE(cls_user_account_resource_get_ret)
+TYPE(cls_user_account_resource_rm_op)
+TYPE(cls_user_account_resource_list_op)
+TYPE(cls_user_account_resource_list_ret)
#include "cls/journal/cls_journal_types.h"
TYPE(cls::journal::ObjectPosition)