key = buf;
}
-static void usage_record_prefix_by_user(string& user, uint64_t epoch, string& key)
+static void usage_record_prefix_by_user(const string& user, uint64_t epoch, string& key)
{
char buf[user.size() + 32];
snprintf(buf, sizeof(buf), "%s_%011llu_", user.c_str(), (long long unsigned)epoch);
key = buf;
}
-static void usage_record_name_by_time(uint64_t epoch, const string& user, string& bucket, string& key)
+static void usage_record_name_by_time(uint64_t epoch, const string& user, const string& bucket, string& key)
{
char buf[32 + user.size() + bucket.size()];
snprintf(buf, sizeof(buf), "%011llu_%s_%s", (long long unsigned)epoch, user.c_str(), bucket.c_str());
key = buf;
}
-static void usage_record_name_by_user(const string& user, uint64_t epoch, string& bucket, string& key)
+static void usage_record_name_by_user(const string& user, uint64_t epoch, const string& bucket, string& key)
{
char buf[32 + user.size() + bucket.size()];
snprintf(buf, sizeof(buf), "%s_%011llu_%s", user.c_str(), (long long unsigned)epoch, bucket.c_str());
return 0;
}
-static int usage_iterate_range(cls_method_context_t hctx, uint64_t start, uint64_t end,
- string& user, string& key_iter, uint32_t max_entries, bool *truncated,
+static int usage_iterate_range(cls_method_context_t hctx, uint64_t start, uint64_t end, const string& user,
+ const string& bucket, string& key_iter, uint32_t max_entries, bool *truncated,
int (*cb)(cls_method_context_t, const string&, rgw_usage_log_entry&, void *),
void *param)
{
if (ret < 0)
return ret;
+ if (!bucket.empty() && bucket.compare(e.bucket))
+ continue;
+
if (e.epoch < start)
continue;
string iter = op.iter;
#define MAX_ENTRIES 1000
uint32_t max_entries = (op.max_entries ? op.max_entries : MAX_ENTRIES);
- int ret = usage_iterate_range(hctx, op.start_epoch, op.end_epoch, op.owner, iter, max_entries, &ret_info.truncated, usage_log_read_cb, (void *)usage);
+ int ret = usage_iterate_range(hctx, op.start_epoch, op.end_epoch, op.owner, op.bucket, iter, max_entries, &ret_info.truncated, usage_log_read_cb, (void *)usage);
if (ret < 0)
return ret;
bool more;
bool found = false;
#define MAX_USAGE_TRIM_ENTRIES 128
- ret = usage_iterate_range(hctx, op.start_epoch, op.end_epoch, op.user, iter, MAX_USAGE_TRIM_ENTRIES, &more, usage_log_trim_cb, (void *)&found);
+ ret = usage_iterate_range(hctx, op.start_epoch, op.end_epoch, op.user, op.bucket, iter, MAX_USAGE_TRIM_ENTRIES, &more, usage_log_trim_cb, (void *)&found);
if (ret < 0)
return ret;
return 0;
}
-int cls_rgw_usage_log_read(IoCtx& io_ctx, string& oid, string& user,
+int cls_rgw_usage_log_read(IoCtx& io_ctx, const string& oid, const string& user, const string& bucket,
uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries,
string& read_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage,
bool *is_truncated)
call.end_epoch = end_epoch;
call.owner = user;
call.max_entries = max_entries;
+ call.bucket = bucket;
call.iter = read_iter;
encode(call, in);
int r = io_ctx.exec(oid, RGW_CLASS, RGW_USER_USAGE_LOG_READ, in, out);
return 0;
}
-int cls_rgw_usage_log_trim(IoCtx& io_ctx, const string& oid, string& user,
+int cls_rgw_usage_log_trim(IoCtx& io_ctx, const string& oid, const string& user, const string& bucket,
uint64_t start_epoch, uint64_t end_epoch)
{
bufferlist in;
call.start_epoch = start_epoch;
call.end_epoch = end_epoch;
call.user = user;
+ call.bucket = bucket;
encode(call, in);
bool done = false;
void cls_rgw_suggest_changes(librados::ObjectWriteOperation& o, bufferlist& updates);
/* usage logging */
-int cls_rgw_usage_log_read(librados::IoCtx& io_ctx, string& oid, string& user,
- uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries,
- string& read_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage,
- bool *is_truncated);
+int cls_rgw_usage_log_read(librados::IoCtx& io_ctx, const string& oid, const string& user, const string& bucket,
+ uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries, string& read_iter,
+ map<rgw_user_bucket, rgw_usage_log_entry>& usage, bool *is_truncated);
-int cls_rgw_usage_log_trim(librados::IoCtx& io_ctx, const string& oid, string& user,
+int cls_rgw_usage_log_trim(librados::IoCtx& io_ctx, const string& oid, const string& user, const string& bucket,
uint64_t start_epoch, uint64_t end_epoch);
void cls_rgw_usage_log_clear(librados::ObjectWriteOperation& op);
uint64_t start_epoch;
uint64_t end_epoch;
string owner;
+ string bucket;
string iter; // should be empty for the first call, non empty for subsequent calls
uint32_t max_entries;
void encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
+ ENCODE_START(2, 1, bl);
encode(start_epoch, bl);
encode(end_epoch, bl);
encode(owner, bl);
encode(iter, bl);
encode(max_entries, bl);
+ encode(bucket, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::const_iterator& bl) {
- DECODE_START(1, bl);
+ DECODE_START(2, bl);
decode(start_epoch, bl);
decode(end_epoch, bl);
decode(owner, bl);
decode(iter, bl);
decode(max_entries, bl);
+ if (struct_v >= 2) {
+ decode(bucket, bl);
+ }
DECODE_FINISH(bl);
}
};
uint64_t start_epoch;
uint64_t end_epoch;
string user;
+ string bucket;
void encode(bufferlist& bl) const {
ENCODE_START(2, 2, bl);
encode(start_epoch, bl);
encode(end_epoch, bl);
encode(user, bl);
+ encode(bucket, bl);
ENCODE_FINISH(bl);
}
decode(start_epoch, bl);
decode(end_epoch, bl);
decode(user, bl);
+ if (struct_v >= 2) {
+ decode(bucket, bl);
+ }
DECODE_FINISH(bl);
}
};
cout << " (NOTE: required to specify formatting of date\n";
cout << " to \"YYYY-MM-DD-hh\")\n";
cout << " log rm remove log object\n";
- cout << " usage show show usage (by user, date range)\n";
- cout << " usage trim trim usage (by user, date range)\n";
+ cout << " usage show show usage (by user, by bucket, date range)\n";
+ cout << " usage trim trim usage (by user, by bucket, date range)\n";
cout << " usage clear reset all the usage stats for the cluster\n";
cout << " gc list dump expired garbage collection objects (specify\n";
cout << " --include-all to list all entries, including unexpired)\n";
}
- ret = RGWUsage::show(store, user_id, start_epoch, end_epoch,
+ ret = RGWUsage::show(store, user_id, bucket_name, start_epoch, end_epoch,
show_log_entries, show_log_sum, &categories,
f);
if (ret < 0) {
}
if (opt_cmd == OPT_USAGE_TRIM) {
- if (user_id.empty() && !yes_i_really_mean_it) {
- cerr << "usage trim without user specified will remove *all* users data" << std::endl;
+ if (user_id.empty() && bucket_name.empty() &&
+ start_date.empty() && end_date.empty() && !yes_i_really_mean_it) {
+ cerr << "usage trim without user/date/bucket specified will remove *all* users data" << std::endl;
cerr << "do you really mean it? (requires --yes-i-really-mean-it)" << std::endl;
return 1;
}
}
}
- ret = RGWUsage::trim(store, user_id, start_epoch, end_epoch);
+ ret = RGWUsage::trim(store, user_id, bucket_name, start_epoch, end_epoch);
if (ret < 0) {
cerr << "ERROR: read_usage() returned ret=" << ret << std::endl;
return 1;
RGWUsageIter usage_iter;
while (is_truncated) {
- op_ret = store->read_usage(s->user->user_id, start_epoch, end_epoch, max_entries,
+ op_ret = store->read_usage(s->user->user_id, s->bucket_name, start_epoch, end_epoch, max_entries,
&is_truncated, usage_iter, usage);
if (op_ret == -ENOENT) {
return 0;
}
-int RGWRados::read_usage(const rgw_user& user, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries,
- bool *is_truncated, RGWUsageIter& usage_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage)
+int RGWRados::read_usage(const rgw_user& user, const string& bucket_name, uint64_t start_epoch, uint64_t end_epoch,
+ uint32_t max_entries, bool *is_truncated, RGWUsageIter& usage_iter, map<rgw_user_bucket,
+ rgw_usage_log_entry>& usage)
{
uint32_t num = max_entries;
string hash, first_hash;
map<rgw_user_bucket, rgw_usage_log_entry> ret_usage;
map<rgw_user_bucket, rgw_usage_log_entry>::iterator iter;
- int ret = cls_obj_usage_log_read(hash, user_str, start_epoch, end_epoch, num,
+ int ret = cls_obj_usage_log_read(hash, user_str, bucket_name, start_epoch, end_epoch, num,
usage_iter.read_iter, ret_usage, is_truncated);
if (ret == -ENOENT)
goto next;
return 0;
}
-int RGWRados::trim_usage(rgw_user& user, uint64_t start_epoch, uint64_t end_epoch)
+int RGWRados::trim_usage(const rgw_user& user, const string& bucket_name, uint64_t start_epoch, uint64_t end_epoch)
{
uint32_t index = 0;
string hash, first_hash;
hash = first_hash;
do {
- int ret = cls_obj_usage_log_trim(hash, user_str, start_epoch, end_epoch);
+ int ret = cls_obj_usage_log_trim(hash, user_str, bucket_name, start_epoch, end_epoch);
if (ret < 0 && ret != -ENOENT)
return ret;
return r;
}
-int RGWRados::cls_obj_usage_log_read(string& oid, string& user, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries,
- string& read_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage, bool *is_truncated)
+int RGWRados::cls_obj_usage_log_read(const string& oid, const string& user, const string& bucket,
+ uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries,
+ string& read_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage,
+ bool *is_truncated)
{
rgw_raw_obj obj(get_zone_params().usage_log_pool, oid);
*is_truncated = false;
- r = cls_rgw_usage_log_read(ref.ioctx, ref.oid, user, start_epoch, end_epoch,
+ r = cls_rgw_usage_log_read(ref.ioctx, ref.oid, user, bucket, start_epoch, end_epoch,
max_entries, read_iter, usage, is_truncated);
return r;
}
-int RGWRados::cls_obj_usage_log_trim(string& oid, string& user, uint64_t start_epoch, uint64_t end_epoch)
+int RGWRados::cls_obj_usage_log_trim(const string& oid, const string& user, const string& bucket,
+ uint64_t start_epoch, uint64_t end_epoch)
{
rgw_raw_obj obj(get_zone_params().usage_log_pool, oid);
return r;
}
- r = cls_rgw_usage_log_trim(ref.ioctx, ref.oid, user, start_epoch, end_epoch);
+ r = cls_rgw_usage_log_trim(ref.ioctx, ref.oid, user, bucket, start_epoch, end_epoch);
return r;
}
// log bandwidth info
int log_usage(map<rgw_user_bucket, RGWUsageBatch>& usage_info);
- int read_usage(const rgw_user& user, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries,
- bool *is_truncated, RGWUsageIter& read_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage);
- int trim_usage(rgw_user& user, uint64_t start_epoch, uint64_t end_epoch);
+ int read_usage(const rgw_user& user, const string& bucket_name, uint64_t start_epoch, uint64_t end_epoch,
+ uint32_t max_entries, bool *is_truncated, RGWUsageIter& read_iter, map<rgw_user_bucket,
+ rgw_usage_log_entry>& usage);
+ int trim_usage(const rgw_user& user, const string& bucket_name, uint64_t start_epoch, uint64_t end_epoch);
int clear_usage();
int create_pool(const rgw_pool& pool);
int bi_remove(BucketShard& bs);
int cls_obj_usage_log_add(const string& oid, rgw_usage_log_info& info);
- int cls_obj_usage_log_read(string& oid, string& user, uint64_t start_epoch, uint64_t end_epoch, uint32_t max_entries,
- string& read_iter, map<rgw_user_bucket, rgw_usage_log_entry>& usage, bool *is_truncated);
- int cls_obj_usage_log_trim(string& oid, string& user, uint64_t start_epoch, uint64_t end_epoch);
+ int cls_obj_usage_log_read(const string& oid, const string& user, const string& bucket, uint64_t start_epoch,
+ uint64_t end_epoch, uint32_t max_entries, string& read_iter, map<rgw_user_bucket,
+ rgw_usage_log_entry>& usage, bool *is_truncated);
+ int cls_obj_usage_log_trim(const string& oid, const string& user, const string& bucket, uint64_t start_epoch,
+ uint64_t end_epoch);
int cls_obj_usage_log_clear(string& oid);
int key_to_shard_id(const string& key, int max_shards);
map<std::string, bool> categories;
string uid_str;
+ string bucket_name;
uint64_t start, end;
bool show_entries;
bool show_summary;
RESTArgs::get_string(s, "uid", uid_str, &uid_str);
+ RESTArgs::get_string(s, "bucket", bucket_name, &bucket_name);
rgw_user uid(uid_str);
RESTArgs::get_epoch(s, "start", 0, &start);
}
}
- http_ret = RGWUsage::show(store, uid, start, end, show_entries, show_summary, &categories, flusher);
+ http_ret = RGWUsage::show(store, uid, bucket_name, start, end, show_entries, show_summary, &categories, flusher);
}
class RGWOp_Usage_Delete : public RGWRESTOp {
void RGWOp_Usage_Delete::execute() {
string uid_str;
+ string bucket_name;
uint64_t start, end;
RESTArgs::get_string(s, "uid", uid_str, &uid_str);
+ RESTArgs::get_string(s, "bucket", bucket_name, &bucket_name);
rgw_user uid(uid_str);
RESTArgs::get_epoch(s, "start", 0, &start);
RESTArgs::get_epoch(s, "end", (uint64_t)-1, &end);
if (uid.empty() &&
+ !bucket_name.empty() &&
!start &&
end == (uint64_t)-1) {
bool remove_all;
}
}
- http_ret = RGWUsage::trim(store, uid, start, end);
+ http_ret = RGWUsage::trim(store, uid, bucket_name, start, end);
}
RGWOp *RGWHandler_Usage::op_get()
formatter->close_section(); // categories
}
-int RGWUsage::show(RGWRados *store, rgw_user& uid, uint64_t start_epoch,
- uint64_t end_epoch, bool show_log_entries, bool show_log_sum,
- map<string, bool> *categories,
+int RGWUsage::show(RGWRados *store, const rgw_user& uid, const string& bucket_name, uint64_t start_epoch,
+ uint64_t end_epoch, bool show_log_entries, bool show_log_sum, map<string, bool> *categories,
RGWFormatterFlusher& flusher)
{
uint32_t max_entries = 1000;
bool user_section_open = false;
map<string, rgw_usage_log_entry> summary_map;
while (is_truncated) {
- int ret = store->read_usage(uid, start_epoch, end_epoch, max_entries,
+ int ret = store->read_usage(uid, bucket_name, start_epoch, end_epoch, max_entries,
&is_truncated, usage_iter, usage);
if (ret == -ENOENT) {
return 0;
}
-int RGWUsage::trim(RGWRados *store, rgw_user& uid, uint64_t start_epoch,
- uint64_t end_epoch)
+int RGWUsage::trim(RGWRados *store, const rgw_user& uid, const string& bucket_name, uint64_t start_epoch,
+ uint64_t end_epoch)
{
- return store->trim_usage(uid, start_epoch, end_epoch);
+ return store->trim_usage(uid, bucket_name, start_epoch, end_epoch);
}
int RGWUsage::clear(RGWRados *store)
class RGWUsage
{
public:
- static int show(RGWRados *store, rgw_user& uid, uint64_t start_epoch,
+ static int show(RGWRados *store, const rgw_user& uid, const string& bucket_name, uint64_t start_epoch,
uint64_t end_epoch, bool show_log_entries, bool show_log_sum,
- std::map<std::string, bool> *categories,
- RGWFormatterFlusher& flusher);
+ std::map<std::string, bool> *categories, RGWFormatterFlusher& flusher);
- static int trim(RGWRados *store, rgw_user& uid, uint64_t start_epoch,
+ static int trim(RGWRados *store, const rgw_user& uid, const string& bucket_name, uint64_t start_epoch,
uint64_t end_epoch);
static int clear(RGWRados *store);
(NOTE: required to specify formatting of date
to "YYYY-MM-DD-hh")
log rm remove log object
- usage show show usage (by user, date range)
- usage trim trim usage (by user, date range)
+ usage show show usage (by user, by bucket, date range)
+ usage trim trim usage (by user, by bucket, date range)
usage clear reset all the usage stats for the cluster
gc list dump expired garbage collection objects (specify
--include-all to list all entries, including unexpired)
return info;
}
+auto gen_usage_log_info(std::string payer, std::string bucket, int total_usage_entries)
+{
+ rgw_usage_log_info info;
+ for (int i=0; i < total_usage_entries; i++){
+ auto user = str_int("user", i);
+ info.entries.emplace_back(rgw_usage_log_entry(user, payer, bucket));
+ }
+
+ return info;
+}
+
TEST(cls_rgw, usage_basic)
{
string oid="usage.1";
bool truncated;
- int ret = cls_rgw_usage_log_read(ioctx, oid, user, start_epoch, end_epoch,
+ int ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
max_entries, read_iter, usage, &truncated);
// read the entries, and see that we have all the added entries
ASSERT_EQ(0, ret);
ASSERT_EQ(total_usage_entries, usage.size());
// delete and read to assert that we've deleted all the values
- ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, user, start_epoch, end_epoch));
+ ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, user, "", start_epoch, end_epoch));
- ret = cls_rgw_usage_log_read(ioctx, oid, user, start_epoch, end_epoch,
+ ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
max_entries, read_iter, usage2, &truncated);
ASSERT_EQ(0, ret);
ASSERT_EQ(0, usage2.size());
+ // add and read to assert that bucket option is valid for usage reading
+ string bucket1 = "bucket-usage-1";
+ string bucket2 = "bucket-usage-2";
+ info = gen_usage_log_info(payer, bucket1, 100);
+ cls_rgw_usage_log_add(op, info);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+
+ info = gen_usage_log_info(payer, bucket2, 100);
+ cls_rgw_usage_log_add(op, info);
+ ASSERT_EQ(0, ioctx.operate(oid, &op));
+ ret = cls_rgw_usage_log_read(ioctx, oid, "", bucket1, start_epoch, end_epoch,
+ max_entries, read_iter, usage2, &truncated);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(100, usage2.size());
+
+ // delete and read to assert that bucket option is valid for usage trim
+ ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, "", bucket1, start_epoch, end_epoch));
+
+ ret = cls_rgw_usage_log_read(ioctx, oid, "", bucket1, start_epoch, end_epoch,
+ max_entries, read_iter, usage2, &truncated);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, usage2.size());
+ ASSERT_EQ(0, cls_rgw_usage_log_trim(ioctx, oid, "", bucket2, start_epoch, end_epoch));
}
TEST(cls_rgw, usage_clear_no_obj)
bool truncated;
uint64_t start_epoch{0}, end_epoch{(uint64_t) -1};
string read_iter;
- ret = cls_rgw_usage_log_read(ioctx, oid, user, start_epoch, end_epoch,
+ ret = cls_rgw_usage_log_read(ioctx, oid, user, "", start_epoch, end_epoch,
max_entries, read_iter, usage, &truncated);
ASSERT_EQ(0, ret);
ASSERT_EQ(0, usage.size());