From 5e5c0677ba69f0d162df1da9a22b1fb56028baa4 Mon Sep 17 00:00:00 2001 From: Radoslaw Zarzynski Date: Wed, 28 Dec 2016 19:22:32 +0100 Subject: [PATCH] rgw: calculate and print Swift's X-Account-Storage-Policy-* headers. Fixes: http://tracker.ceph.com/issues/17932 Signed-off-by: Radoslaw Zarzynski --- src/rgw/rgw_op.cc | 69 ++++++++++++++++++++++++++++----------- src/rgw/rgw_op.h | 47 +++++++++++++------------- src/rgw/rgw_rest.h | 19 +++++++++++ src/rgw/rgw_rest_swift.cc | 46 ++++++++++++++------------ 4 files changed, 117 insertions(+), 64 deletions(-) diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index a014aee7263fd..0390ea5dfd1e8 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -1844,15 +1844,32 @@ void RGWListBuckets::execute() << s->user->user_id << dendl; break; } - map& m = buckets.get_buckets(); - map::iterator iter; - for (iter = m.begin(); iter != m.end(); ++iter) { - RGWBucketEnt& bucket = iter->second; - buckets_size += bucket.size; - buckets_size_rounded += bucket.size_rounded; - buckets_objcount += bucket.count; - } - buckets_count += m.size(); + + /* We need to have stats for all our policies - even if a given policy + * isn't actually used in a given account. In such situation its usage + * stats would be simply full of zeros. */ + for (const auto& policy : store->get_zonegroup().placement_targets) { + policies_stats.emplace(policy.second.name, + decltype(policies_stats)::mapped_type()); + } + + std::map& m = buckets.get_buckets(); + for (const auto& kv : m) { + const auto& bucket = kv.second; + + global_stats.bytes_used += bucket.size; + global_stats.bytes_used_rounded += bucket.size_rounded; + global_stats.objects_count += bucket.count; + + /* operator[] still can create a new entry for storage policy seen + * for first time. */ + auto& policy_stats = policies_stats[bucket.placement_rule]; + policy_stats.bytes_used += bucket.size; + policy_stats.bytes_used_rounded += bucket.size_rounded; + policy_stats.buckets_count++; + policy_stats.objects_count += bucket.count; + } + global_stats.buckets_count += m.size(); total_count += m.size(); done = (m.size() < read_count || (limit >= 0 && total_count >= (uint64_t)limit)); @@ -1970,17 +1987,31 @@ void RGWStatAccount::execute() << s->user->user_id << dendl; break; } else { - map& m = buckets.get_buckets(); - map::iterator iter; - for (iter = m.begin(); iter != m.end(); ++iter) { - RGWBucketEnt& bucket = iter->second; - buckets_size += bucket.size; - buckets_size_rounded += bucket.size_rounded; - buckets_objcount += bucket.count; - - marker = iter->first; + /* We need to have stats for all our policies - even if a given policy + * isn't actually used in a given account. In such situation its usage + * stats would be simply full of zeros. */ + for (const auto& policy : store->get_zonegroup().placement_targets) { + policies_stats.emplace(policy.second.name, + decltype(policies_stats)::mapped_type()); + } + + std::map& m = buckets.get_buckets(); + for (const auto& kv : m) { + const auto& bucket = kv.second; + + global_stats.bytes_used += bucket.size; + global_stats.bytes_used_rounded += bucket.size_rounded; + global_stats.objects_count += bucket.count; + + /* operator[] still can create a new entry for storage policy seen + * for first time. */ + auto& policy_stats = policies_stats[bucket.placement_rule]; + policy_stats.bytes_used += bucket.size; + policy_stats.bytes_used_rounded += bucket.size_rounded; + policy_stats.buckets_count++; + policy_stats.objects_count += bucket.count; } - buckets_count += m.size(); + global_stats.buckets_count += m.size(); } } while (is_truncated); diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index 7edc02559d9d2..acee2492c098c 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -582,34 +582,38 @@ public: }; /* RGWBulkUploadOp::AlignedStreamGetter */ +struct RGWUsageStats { + uint64_t bytes_used = 0; + uint64_t bytes_used_rounded = 0; + uint64_t buckets_count = 0; + uint64_t objects_count = 0; +}; + #define RGW_LIST_BUCKETS_LIMIT_MAX 10000 class RGWListBuckets : public RGWOp { protected: bool sent_data; - string marker; - string end_marker; + std::string marker; + std::string end_marker; int64_t limit; uint64_t limit_max; - uint32_t buckets_count; - uint64_t buckets_objcount; - uint64_t buckets_size; - uint64_t buckets_size_rounded; - map attrs; + std::map attrs; bool is_truncated; + RGWUsageStats global_stats; + std::map policies_stats; + virtual uint64_t get_default_max() const { return 1000; } public: - RGWListBuckets() : sent_data(false) { - limit = limit_max = RGW_LIST_BUCKETS_LIMIT_MAX; - buckets_count = 0; - buckets_objcount = 0; - buckets_size = 0; - buckets_size_rounded = 0; - is_truncated = false; + RGWListBuckets() + : sent_data(false), + limit(RGW_LIST_BUCKETS_LIMIT_MAX), + limit_max(RGW_LIST_BUCKETS_LIMIT_MAX), + is_truncated(false) { } int verify_permission() override; @@ -666,24 +670,17 @@ public: class RGWStatAccount : public RGWOp { protected: - uint32_t buckets_count; - uint64_t buckets_objcount; - uint64_t buckets_size; - uint64_t buckets_size_rounded; + RGWUsageStats global_stats; + std::map policies_stats; public: - RGWStatAccount() { - buckets_count = 0; - buckets_objcount = 0; - buckets_size = 0; - buckets_size_rounded = 0; - } + RGWStatAccount() = default; int verify_permission() override; void execute() override; void send_response() override = 0; - const string name() override { return "stat_account"; } + const std::string name() override { return "stat_account"; } RGWOpType get_type() override { return RGW_OP_STAT_ACCOUNT; } uint32_t op_mask() override { return RGW_OP_TYPE_READ; } }; diff --git a/src/rgw/rgw_rest.h b/src/rgw/rgw_rest.h index f780ab4abacd7..515057dfcd679 100644 --- a/src/rgw/rgw_rest.h +++ b/src/rgw/rgw_rest.h @@ -662,6 +662,7 @@ extern void dump_header(struct req_state* s, extern void dump_header(struct req_state* s, const boost::string_ref& name, const utime_t& val); + template static inline void dump_header_prefixed(struct req_state* s, const boost::string_ref& name_prefix, @@ -677,6 +678,24 @@ static inline void dump_header_prefixed(struct req_state* s, return dump_header(s, std::move(full_name), std::forward(args)...); } +template +static inline void dump_header_infixed(struct req_state* s, + const boost::string_ref& prefix, + const boost::string_ref& infix, + const boost::string_ref& sufix, + Args&&... args) { + char full_name_buf[prefix.size() + infix.size() + sufix.size() + 1]; + const auto len = snprintf(full_name_buf, sizeof(full_name_buf), "%.*s%.*s%.*s", + static_cast(prefix.length()), + prefix.data(), + static_cast(infix.length()), + infix.data(), + static_cast(sufix.length()), + sufix.data()); + boost::string_ref full_name(full_name_buf, len); + return dump_header(s, std::move(full_name), std::forward(args)...); +} + template static inline void dump_header_quoted(struct req_state* s, const boost::string_ref& name, diff --git a/src/rgw/rgw_rest_swift.cc b/src/rgw/rgw_rest_swift.cc index aa9ce48e2ec42..17f9c4a8de211 100644 --- a/src/rgw/rgw_rest_swift.cc +++ b/src/rgw/rgw_rest_swift.cc @@ -78,10 +78,8 @@ int RGWListBuckets_ObjStore_SWIFT::get_params() } static void dump_account_metadata(struct req_state * const s, - const uint32_t buckets_count, - const uint64_t buckets_object_count, - const uint64_t buckets_size, - const uint64_t buckets_size_rounded, + const RGWUsageStats& global_stats, + const std::map policies_stats, /* const */map& attrs, const RGWQuotaInfo& quota, const RGWAccessControlPolicy_SWIFTAcct &policy) @@ -89,10 +87,24 @@ static void dump_account_metadata(struct req_state * const s, /* Adding X-Timestamp to keep align with Swift API */ dump_header(s, "X-Timestamp", ceph_clock_now()); - dump_header(s, "X-Account-Container-Count", buckets_count); - dump_header(s, "X-Account-Object-Count", buckets_object_count); - dump_header(s, "X-Account-Bytes-Used", buckets_size); - dump_header(s, "X-Account-Bytes-Used-Actual", buckets_size_rounded); + dump_header(s, "X-Account-Container-Count", global_stats.buckets_count); + dump_header(s, "X-Account-Object-Count", global_stats.objects_count); + dump_header(s, "X-Account-Bytes-Used", global_stats.bytes_used); + dump_header(s, "X-Account-Bytes-Used-Actual", global_stats.bytes_used_rounded); + + for (const auto& kv : policies_stats) { + const auto& policy_name = camelcase_dash_http_attr(kv.first); + const auto& policy_stats = kv.second; + + dump_header_infixed(s, "X-Account-Storage-Policy-", policy_name, + "-Container-Count", policy_stats.buckets_count); + dump_header_infixed(s, "X-Account-Storage-Policy-", policy_name, + "-Object-Count", policy_stats.objects_count); + dump_header_infixed(s, "X-Account-Storage-Policy-", policy_name, + "-Bytes-Used", policy_stats.bytes_used); + dump_header_infixed(s, "X-Account-Storage-Policy-", policy_name, + "-Bytes-Used-Actual", policy_stats.bytes_used_rounded); + } /* Dump TempURL-related stuff */ if (s->perm_mask == RGW_PERM_FULL_CONTROL) { @@ -155,10 +167,8 @@ void RGWListBuckets_ObjStore_SWIFT::send_response_begin(bool has_buckets) if (! s->cct->_conf->rgw_swift_enforce_content_length) { /* Adding account stats in the header to keep align with Swift API */ dump_account_metadata(s, - buckets_count, - buckets_objcount, - buckets_size, - buckets_size_rounded, + global_stats, + policies_stats, attrs, user_quota, static_cast(*s->user_acl)); @@ -262,10 +272,8 @@ void RGWListBuckets_ObjStore_SWIFT::send_response_end() if (s->cct->_conf->rgw_swift_enforce_content_length) { /* Adding account stats in the header to keep align with Swift API */ dump_account_metadata(s, - buckets_count, - buckets_objcount, - buckets_size, - buckets_size_rounded, + global_stats, + policies_stats, attrs, user_quota, static_cast(*s->user_acl)); @@ -524,10 +532,8 @@ void RGWStatAccount_ObjStore_SWIFT::send_response() if (op_ret >= 0) { op_ret = STATUS_NO_CONTENT; dump_account_metadata(s, - buckets_count, - buckets_objcount, - buckets_size, - buckets_size_rounded, + global_stats, + policies_stats, attrs, user_quota, static_cast(*s->user_acl)); -- 2.39.5