From 854bb63ee3128e32c9fe2b3a54a5ac6781c7dafd Mon Sep 17 00:00:00 2001 From: Seena Fallah Date: Mon, 8 Apr 2024 15:06:14 +0200 Subject: [PATCH] rgw: allow disabling bucket stats on head bucket The HeadBucket API now reports the `X-RGW-Bytes-Used` and `X-RGW-Object-Count` headers only when the `read-stats` querystring is explicitly included in the API request. Fixes: https://tracker.ceph.com/issues/65369 Signed-off-by: Seena Fallah --- PendingReleaseNotes | 4 ++++ src/rgw/rgw_file_int.h | 24 +++++++++++++---------- src/rgw/rgw_op.cc | 12 +++++++----- src/rgw/rgw_op.h | 3 ++- src/rgw/rgw_rest.cc | 1 - src/rgw/rgw_rest_s3.cc | 41 +++++++++++++++++++++++---------------- src/rgw/rgw_rest_s3.h | 1 + src/rgw/rgw_rest_swift.cc | 8 +++----- src/rgw/rgw_rest_swift.h | 1 + 9 files changed, 56 insertions(+), 39 deletions(-) diff --git a/PendingReleaseNotes b/PendingReleaseNotes index 146cab64d6f..1b0a75e01a1 100644 --- a/PendingReleaseNotes +++ b/PendingReleaseNotes @@ -48,6 +48,10 @@ CephFS does not support disk space reservation. The only flags supported are `FALLOC_FL_KEEP_SIZE` and `FALLOC_FL_PUNCH_HOLE`. +* The HeadBucket API now reports the `X-RGW-Bytes-Used` and `X-RGW-Object-Count` + headers only when the `read-stats` querystring is explicitly included in the + API request. + >=19.2.1 * CephFS: Command `fs subvolume create` now allows tagging subvolumes through option diff --git a/src/rgw/rgw_file_int.h b/src/rgw/rgw_file_int.h index 0a1db645207..84eff1e252e 100644 --- a/src/rgw/rgw_file_int.h +++ b/src/rgw/rgw_file_int.h @@ -2298,6 +2298,8 @@ public: std::string uri; std::map attrs; RGWLibFS::BucketStats& bs; + real_time ctime; + bool name_matched = false; RGWStatBucketRequest(CephContext* _cct, std::unique_ptr _user, const std::string& _path, @@ -2312,9 +2314,7 @@ public: return (iter != attrs.end()) ? &(iter->second) : nullptr; } - real_time get_ctime() const { - return bucket->get_creation_time(); - } + real_time get_ctime() { return ctime; } bool only_bucket() override { return false; } @@ -2342,22 +2342,26 @@ public: return 0; } - virtual int get_params() { - return 0; + int get_params(optional_yield) override { return 0; } + + void complete() override { + // get_state() will no longer be there after execute_req() + // so save what we need from get_state()->bucket + ctime = get_state()->bucket->get_creation_time(); + name_matched = get_state()->bucket->get_name().length() > 0; + + RGWOp::complete(); } void send_response() override { - bucket->get_creation_time() = get_state()->bucket->get_info().creation_time; bs.size = stats.size; bs.size_rounded = stats.size_rounded; - bs.creation_time = bucket->get_creation_time(); + bs.creation_time = get_state()->bucket->get_info().creation_time; bs.num_entries = stats.num_objects; std::swap(attrs, get_state()->bucket_attrs); } - bool matched() { - return (bucket->get_name().length() > 0); - } + bool matched() { return name_matched; } }; /* RGWStatBucketRequest */ diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 8378d9ef22e..17140436eeb 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -3135,17 +3135,19 @@ static int load_bucket_stats(const DoutPrefixProvider* dpp, optional_yield y, void RGWStatBucket::execute(optional_yield y) { - if (!s->bucket_exists) { - op_ret = -ERR_NO_SUCH_BUCKET; + op_ret = get_params(y); + if (op_ret < 0) { return; } - op_ret = driver->load_bucket(this, s->bucket->get_key(), &bucket, y); - if (op_ret) { + if (!s->bucket_exists) { + op_ret = -ERR_NO_SUCH_BUCKET; return; } - op_ret = load_bucket_stats(this, y, *s->bucket, stats); + if (report_stats) { + op_ret = load_bucket_stats(this, y, *s->bucket, stats); + } } int RGWListBucket::verify_permission(optional_yield y) diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index 0098b67e032..9f747501729 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -1087,14 +1087,15 @@ public: class RGWStatBucket : public RGWOp { protected: - std::unique_ptr bucket; RGWStorageStats stats; + bool report_stats{true}; public: int verify_permission(optional_yield y) override; void pre_exec() override; void execute(optional_yield y) override; + virtual int get_params(optional_yield y) = 0; void send_response() override = 0; const char* name() const override { return "stat_bucket"; } RGWOpType get_type() override { return RGW_OP_STAT_BUCKET; } diff --git a/src/rgw/rgw_rest.cc b/src/rgw/rgw_rest.cc index 03241b7369a..bf3d4ad9967 100644 --- a/src/rgw/rgw_rest.cc +++ b/src/rgw/rgw_rest.cc @@ -1668,7 +1668,6 @@ int RGWDeleteMultiObj_ObjStore::get_params(optional_yield y) return op_ret; } - void RGWRESTOp::send_response() { if (!flusher.did_start()) { diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index b7046118ef3..4b337f317ce 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -2401,34 +2401,41 @@ void RGWGetBucketWebsite_ObjStore_S3::send_response() rgw_flush_formatter_and_reset(s, s->formatter); } -static void dump_bucket_metadata(req_state *s, rgw::sal::Bucket* bucket, +static void dump_bucket_metadata(req_state *s, RGWStorageStats& stats) { dump_header(s, "X-RGW-Object-Count", static_cast(stats.num_objects)); dump_header(s, "X-RGW-Bytes-Used", static_cast(stats.size)); +} - // only bucket's owner is allowed to get the quota settings of the account - if (s->auth.identity->is_owner_of(bucket->get_owner())) { - const auto& user_info = s->user->get_info(); - const auto& bucket_quota = s->bucket->get_info().quota; // bucket quota - dump_header(s, "X-RGW-Quota-Max-Buckets", static_cast(user_info.max_buckets)); - - if (user_info.quota.user_quota.enabled){ - dump_header(s, "X-RGW-Quota-User-Size", static_cast(user_info.quota.user_quota.max_size)); - dump_header(s, "X-RGW-Quota-User-Objects", static_cast(user_info.quota.user_quota.max_objects)); - } +int RGWStatBucket_ObjStore_S3::get_params(optional_yield y) +{ + report_stats = s->info.args.exists("read-stats"); - if (bucket_quota.enabled){ - dump_header(s, "X-RGW-Quota-Bucket-Size", static_cast(bucket_quota.max_size)); - dump_header(s, "X-RGW-Quota-Bucket-Objects", static_cast(bucket_quota.max_objects)); - } - } + return 0; } void RGWStatBucket_ObjStore_S3::send_response() { if (op_ret >= 0) { - dump_bucket_metadata(s, bucket.get(), stats); + if (report_stats) { + dump_bucket_metadata(s, stats); + } + // only bucket's owner is allowed to get the quota settings of the account + if (s->auth.identity->is_owner_of(s->bucket->get_owner())) { + const auto& user_info = s->user->get_info(); + const auto& bucket_quota = s->bucket->get_info().quota; // bucket quota + + dump_header(s, "X-RGW-Quota-Max-Buckets", static_cast(user_info.max_buckets)); + if (user_info.quota.user_quota.enabled) { + dump_header(s, "X-RGW-Quota-User-Size", static_cast(user_info.quota.user_quota.max_size)); + dump_header(s, "X-RGW-Quota-User-Objects", static_cast(user_info.quota.user_quota.max_objects)); + } + if (bucket_quota.enabled) { + dump_header(s, "X-RGW-Quota-Bucket-Size", static_cast(bucket_quota.max_size)); + dump_header(s, "X-RGW-Quota-Bucket-Objects", static_cast(bucket_quota.max_objects)); + } + } } set_req_state_err(s, op_ret); diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index c47edb38855..50160d79a42 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -242,6 +242,7 @@ public: ~RGWStatBucket_ObjStore_S3() override {} void send_response() override; + int get_params(optional_yield y) override; }; class RGWCreateBucket_ObjStore_S3 : public RGWCreateBucket_ObjStore { diff --git a/src/rgw/rgw_rest_swift.cc b/src/rgw/rgw_rest_swift.cc index 35c36d1ae1a..b8ff3ca2fe8 100644 --- a/src/rgw/rgw_rest_swift.cc +++ b/src/rgw/rgw_rest_swift.cc @@ -447,7 +447,6 @@ int RGWListBucket_ObjStore_SWIFT::get_params(optional_yield y) } static void dump_container_metadata(req_state *, - const rgw::sal::Bucket*, const std::optional& stats, const RGWQuotaInfo&, const RGWBucketWebsiteConf&); @@ -458,7 +457,7 @@ void RGWListBucket_ObjStore_SWIFT::send_response() map::iterator pref_iter = common_prefixes.begin(); dump_start(s); - dump_container_metadata(s, s->bucket.get(), stats, quota.bucket_quota, + dump_container_metadata(s, stats, quota.bucket_quota, s->bucket->get_info().website_conf); s->formatter->open_array_section_with_attrs("container", @@ -558,7 +557,6 @@ next: } // RGWListBucket_ObjStore_SWIFT::send_response static void dump_container_metadata(req_state *s, - const rgw::sal::Bucket* bucket, const std::optional& stats, const RGWQuotaInfo& quota, const RGWBucketWebsiteConf& ws_conf) @@ -683,7 +681,7 @@ void RGWStatBucket_ObjStore_SWIFT::send_response() { if (op_ret >= 0) { op_ret = STATUS_NO_CONTENT; - dump_container_metadata(s, bucket.get(), stats, quota.bucket_quota, + dump_container_metadata(s, stats, quota.bucket_quota, s->bucket->get_info().website_conf); } @@ -2640,7 +2638,7 @@ RGWOp* RGWSwiftWebsiteHandler::get_ws_listing_op() /* Generate the header now. */ set_req_state_err(s, op_ret); dump_errno(s); - dump_container_metadata(s, s->bucket.get(), stats, quota.bucket_quota, + dump_container_metadata(s, stats, quota.bucket_quota, s->bucket->get_info().website_conf); end_header(s, this, "text/html"); if (op_ret < 0) { diff --git a/src/rgw/rgw_rest_swift.h b/src/rgw/rgw_rest_swift.h index eb1c4422e34..ec206a5160f 100644 --- a/src/rgw/rgw_rest_swift.h +++ b/src/rgw/rgw_rest_swift.h @@ -86,6 +86,7 @@ public: RGWStatBucket_ObjStore_SWIFT() {} ~RGWStatBucket_ObjStore_SWIFT() override {} + int get_params(optional_yield y) override { return 0; } void send_response() override; }; -- 2.39.5