Skip to content

Commit cc1a0e5

Browse files
committed
rgw/logging: support source and destination buckets on different tenants
Signed-off-by: Yuval Lifshitz <[email protected]>
1 parent eb54228 commit cc1a0e5

File tree

3 files changed

+77
-44
lines changed

3 files changed

+77
-44
lines changed

doc/radosgw/bucket_logging.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ has the following format:
7272

7373
::
7474

75-
<prefix><bucket owner>/<source region>/<bucket name>/<year>/<month>/<day>/<year-month-day-hour-minute-second>-<16 bytes unique-id>
75+
<prefix><bucket owner>/<source region>/[tenant:]<bucket name>/<year>/<month>/<day>/<year-month-day-hour-minute-second>-<16 bytes unique-id>
7676

7777
For example:
7878

@@ -90,7 +90,7 @@ Journal
9090
minimum amount of data used for journaling bucket changes (this is a Ceph extension).
9191

9292
- bucket owner (or dash if empty)
93-
- bucket name (or dash if empty)
93+
- bucket name (or dash if empty). in the format: ``[tenant:]<bucket name>``
9494
- time in the following format: ``[day/month/year:hour:minute:second timezone]``
9595
- object key (or dash if empty)
9696
- operation in the following format: ``WEBSITE/REST.<HTTP method>.<resource>``
@@ -111,7 +111,7 @@ Standard
111111
based on `AWS Logging Record Format`_.
112112

113113
- bucket owner (or dash if empty)
114-
- bucket name (or dash if empty)
114+
- bucket name (or dash if empty). in the format: ``[tenant:]<bucket name>``
115115
- time
116116
- remote IP (not supported, always a dash)
117117
- user or account (or dash if empty)

src/rgw/rgw_bucket_logging.cc

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,13 @@ ceph::coarse_real_time time_from_name(const std::string& obj_name, const DoutPre
206206
return extracted_time;
207207
}
208208

209+
std::string full_bucket_name(const std::unique_ptr<rgw::sal::Bucket>& bucket) {
210+
if (bucket->get_tenant().empty()) {
211+
return bucket->get_name();
212+
}
213+
return fmt::format("{}:{}", bucket->get_tenant(), bucket->get_name());
214+
}
215+
209216
int new_logging_object(const configuration& conf,
210217
const std::unique_ptr<rgw::sal::Bucket>& bucket,
211218
std::string& obj_name,
@@ -235,7 +242,7 @@ int new_logging_object(const configuration& conf,
235242
conf.target_prefix,
236243
to_string(bucket->get_owner()),
237244
source_region,
238-
bucket->get_name(),
245+
full_bucket_name(bucket),
239246
t,
240247
t,
241248
unique);
@@ -270,8 +277,11 @@ int rollover_logging_object(const configuration& conf,
270277
optional_yield y,
271278
bool must_commit,
272279
RGWObjVersionTracker* objv_tracker) {
273-
if (conf.target_bucket != bucket->get_name()) {
274-
ldpp_dout(dpp, 1) << "ERROR: bucket name mismatch: '" << conf.target_bucket << "' != '" << bucket->get_name() << "'" << dendl;
280+
std::string target_bucket_name;
281+
std::string target_tenant_name;
282+
std::ignore = rgw_parse_url_bucket(conf.target_bucket, bucket->get_tenant(), target_tenant_name, target_bucket_name);
283+
if (target_bucket_name != bucket->get_name() || target_tenant_name != bucket->get_tenant()) {
284+
ldpp_dout(dpp, 1) << "ERROR: bucket name mismatch: '" << conf.target_bucket << "' != '" << full_bucket_name(bucket) << "'" << dendl;
275285
return -EINVAL;
276286
}
277287
const auto old_obj = obj_name;
@@ -357,11 +367,19 @@ int log_record(rgw::sal::Driver* driver,
357367
ldpp_dout(dpp, 1) << "ERROR: only bucket operations are logged" << dendl;
358368
return -EINVAL;
359369
}
370+
std::string target_bucket_name;
371+
std::string target_tenant_name;
372+
auto ret = rgw_parse_url_bucket(conf.target_bucket, s->bucket_tenant, target_tenant_name, target_bucket_name);
373+
if (ret < 0) {
374+
ldpp_dout(dpp, 1) << "ERROR: failed to parse target bucket '" << conf.target_bucket << "', ret = " << ret << dendl;
375+
return ret;
376+
}
377+
const rgw_bucket target_bucket_id(target_tenant_name, target_bucket_name);
360378
std::unique_ptr<rgw::sal::Bucket> target_bucket;
361-
auto ret = driver->load_bucket(dpp, rgw_bucket(s->bucket_tenant, conf.target_bucket),
379+
ret = driver->load_bucket(dpp, target_bucket_id,
362380
&target_bucket, y);
363381
if (ret < 0) {
364-
ldpp_dout(dpp, 1) << "ERROR: failed to get target logging bucket '" << conf.target_bucket << "'. ret = " << ret << dendl;
382+
ldpp_dout(dpp, 1) << "ERROR: failed to get target logging bucket '" << target_bucket_id << "'. ret = " << ret << dendl;
365383
return ret;
366384
}
367385
std::string obj_name;
@@ -420,7 +438,7 @@ int log_record(rgw::sal::Driver* driver,
420438
bucket_name = s->src_bucket_name;
421439
} else {
422440
bucket_owner = to_string( s->bucket->get_owner());
423-
bucket_name = s->bucket->get_name();
441+
bucket_name = full_bucket_name(s->bucket);
424442
}
425443

426444
switch (conf.logging_type) {
@@ -459,7 +477,7 @@ int log_record(rgw::sal::Driver* driver,
459477
case LoggingType::Journal:
460478
record = fmt::format("{} {} [{:%d/%b/%Y:%H:%M:%S %z}] {} {} {} {} {}",
461479
dash_if_empty(to_string(s->bucket->get_owner())),
462-
dash_if_empty(s->bucket->get_name()),
480+
dash_if_empty(full_bucket_name(s->bucket)),
463481
t,
464482
op_name,
465483
dash_if_empty_or_null(obj, obj->get_name()),
@@ -543,10 +561,10 @@ int log_record(rgw::sal::Driver* driver,
543561
return 0;
544562
}
545563
}
546-
ldpp_dout(dpp, 20) << "INFO: found matching logging configuration of bucket '" << s->bucket->get_name() <<
564+
ldpp_dout(dpp, 20) << "INFO: found matching logging configuration of bucket '" << full_bucket_name(s->bucket) <<
547565
"' configuration: " << configuration.to_json_str() << dendl;
548566
if (auto ret = log_record(driver, obj, s, op_name, etag, size, configuration, dpp, y, async_completion, log_source_bucket); ret < 0) {
549-
ldpp_dout(dpp, 1) << "ERROR: failed to perform logging for bucket '" << s->bucket->get_name() <<
567+
ldpp_dout(dpp, 1) << "ERROR: failed to perform logging for bucket '" << full_bucket_name(s->bucket) <<
550568
"'. ret=" << ret << dendl;
551569
return ret;
552570
}

src/rgw/rgw_rest_bucket_logging.cc

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,15 @@ class RGWGetBucketLoggingOp : public RGWOp {
5858
return;
5959
}
6060

61-
std::unique_ptr<rgw::sal::Bucket> bucket;
62-
op_ret = driver->load_bucket(this, rgw_bucket(s->bucket_tenant, s->bucket_name),
63-
&bucket, y);
61+
const rgw_bucket src_bucket_id(s->bucket_tenant, s->bucket_name);
62+
std::unique_ptr<rgw::sal::Bucket> src_bucket;
63+
op_ret = driver->load_bucket(this, src_bucket_id,
64+
&src_bucket, y);
6465
if (op_ret < 0) {
65-
ldpp_dout(this, 1) << "ERROR: failed to get bucket '" <<
66-
(s->bucket_tenant.empty() ? s->bucket_name : s->bucket_tenant + ":" + s->bucket_name) <<
67-
"' info, ret = " << op_ret << dendl;
66+
ldpp_dout(this, 1) << "ERROR: failed to get bucket '" << src_bucket_id << "', ret = " << op_ret << dendl;
6867
return;
6968
}
70-
if (auto iter = bucket->get_attrs().find(RGW_ATTR_BUCKET_LOGGING); iter != bucket->get_attrs().end()) {
69+
if (auto iter = src_bucket->get_attrs().find(RGW_ATTR_BUCKET_LOGGING); iter != src_bucket->get_attrs().end()) {
7170
try {
7271
configuration.enabled = true;
7372
decode(configuration, iter->second);
@@ -78,10 +77,10 @@ class RGWGetBucketLoggingOp : public RGWOp {
7877
return;
7978
}
8079
} else {
81-
ldpp_dout(this, 5) << "WARNING: no logging configuration on bucket '" << bucket->get_name() << "'" << dendl;
80+
ldpp_dout(this, 5) << "WARNING: no logging configuration on bucket '" << src_bucket_id << "'" << dendl;
8281
return;
8382
}
84-
ldpp_dout(this, 20) << "INFO: found logging configuration on bucket '" << bucket->get_name() << "'"
83+
ldpp_dout(this, 20) << "INFO: found logging configuration on bucket '" << src_bucket_id << "'"
8584
<< "'. configuration: " << configuration.to_json_str() << dendl;
8685
}
8786

@@ -159,32 +158,40 @@ class RGWPutBucketLoggingOp : public RGWDefaultResponseOp {
159158
return;
160159
}
161160

162-
std::unique_ptr<rgw::sal::Bucket> bucket;
163-
op_ret = driver->load_bucket(this, rgw_bucket(s->bucket_tenant, s->bucket_name),
164-
&bucket, y);
161+
const rgw_bucket src_bucket_id(s->bucket_tenant, s->bucket_name);
162+
std::unique_ptr<rgw::sal::Bucket> src_bucket;
163+
op_ret = driver->load_bucket(this, src_bucket_id,
164+
&src_bucket, y);
165165
if (op_ret < 0) {
166-
ldpp_dout(this, 1) << "ERROR: failed to get bucket '" << s->bucket_name << "', ret = " << op_ret << dendl;
166+
ldpp_dout(this, 1) << "ERROR: failed to get bucket '" << src_bucket_id << "', ret = " << op_ret << dendl;
167167
return;
168168
}
169169

170-
171-
auto& attrs = bucket->get_attrs();
170+
auto& attrs = src_bucket->get_attrs();
172171
if (!configuration.enabled) {
173172
if (auto iter = attrs.find(RGW_ATTR_BUCKET_LOGGING); iter != attrs.end()) {
174173
attrs.erase(iter);
175174
}
176175
} else {
176+
std::string target_bucket_name;
177+
std::string target_tenant_name;
178+
op_ret = rgw_parse_url_bucket(configuration.target_bucket, s->bucket_tenant, target_tenant_name, target_bucket_name);
179+
if (op_ret < 0) {
180+
ldpp_dout(this, 1) << "ERROR: failed to parse target bucket '" << configuration.target_bucket << "', ret = " << op_ret << dendl;
181+
return;
182+
}
183+
const rgw_bucket target_bucket_id(target_tenant_name, target_bucket_name);
177184
std::unique_ptr<rgw::sal::Bucket> target_bucket;
178-
op_ret = driver->load_bucket(this, rgw_bucket(s->bucket_tenant, configuration.target_bucket),
185+
op_ret = driver->load_bucket(this, target_bucket_id,
179186
&target_bucket, y);
180187
if (op_ret < 0) {
181-
ldpp_dout(this, 1) << "ERROR: failed to get target bucket '" << configuration.target_bucket << "', ret = " << op_ret << dendl;
188+
ldpp_dout(this, 1) << "ERROR: failed to get target bucket '" << target_bucket_id << "', ret = " << op_ret << dendl;
182189
return;
183190
}
184191
const auto& target_attrs = target_bucket->get_attrs();
185192
if (target_attrs.find(RGW_ATTR_BUCKET_LOGGING) != target_attrs.end()) {
186193
// target bucket must not have logging set on it
187-
ldpp_dout(this, 1) << "ERROR: logging target bucket '" << configuration.target_bucket << "', is configured with bucket logging" << dendl;
194+
ldpp_dout(this, 1) << "ERROR: logging target bucket '" << target_bucket_id << "', is configured with bucket logging" << dendl;
188195
op_ret = -EINVAL;
189196
return;
190197
}
@@ -196,21 +203,20 @@ class RGWPutBucketLoggingOp : public RGWDefaultResponseOp {
196203
// if we do, how do we maintain it when bucket logging changes?
197204
}
198205
// TODO: use retry_raced_bucket_write from rgw_op.cc
199-
op_ret = bucket->merge_and_store_attrs(this, attrs, y);
206+
op_ret = src_bucket->merge_and_store_attrs(this, attrs, y);
200207
if (op_ret < 0) {
201208
ldpp_dout(this, 1) << "ERROR: failed to set logging attribute '" << RGW_ATTR_BUCKET_LOGGING << "' to bucket '" <<
202-
bucket->get_name() << "', ret = " << op_ret << dendl;
209+
src_bucket->get_name() << "', ret = " << op_ret << dendl;
203210
return;
204211
}
205212

206213
ldpp_dout(this, 20) << "INFO: " << (configuration.enabled ? "wrote" : "removed")
207-
<< " logging configuration. bucket '" << bucket->get_name() << "'. configuration: " <<
214+
<< " logging configuration. bucket '" << src_bucket_id << "'. configuration: " <<
208215
configuration.to_json_str() << dendl;
209216
}
210217
};
211218

212219
// Post /<bucket name>/?logging
213-
// actual configuration is XML encoded in the body of the message
214220
class RGWPostBucketLoggingOp : public RGWDefaultResponseOp {
215221
int verify_permission(optional_yield y) override {
216222
auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
@@ -234,17 +240,18 @@ class RGWPostBucketLoggingOp : public RGWDefaultResponseOp {
234240
return;
235241
}
236242

237-
std::unique_ptr<rgw::sal::Bucket> bucket;
238-
op_ret = driver->load_bucket(this, rgw_bucket(s->bucket_tenant, s->bucket_name),
239-
&bucket, y);
243+
const rgw_bucket src_bucket_id(s->bucket_tenant, s->bucket_name);
244+
std::unique_ptr<rgw::sal::Bucket> src_bucket;
245+
op_ret = driver->load_bucket(this, src_bucket_id,
246+
&src_bucket, y);
240247
if (op_ret < 0) {
241-
ldpp_dout(this, 1) << "ERROR: failed to get bucket '" << s->bucket_name << "', ret = " << op_ret << dendl;
248+
ldpp_dout(this, 1) << "ERROR: failed to get bucket '" << src_bucket_id << "', ret = " << op_ret << dendl;
242249
return;
243250
}
244-
const auto& bucket_attrs = bucket->get_attrs();
251+
const auto& bucket_attrs = src_bucket->get_attrs();
245252
auto iter = bucket_attrs.find(RGW_ATTR_BUCKET_LOGGING);
246253
if (iter == bucket_attrs.end()) {
247-
ldpp_dout(this, 1) << "WARNING: no logging configured on bucket" << dendl;
254+
ldpp_dout(this, 1) << "WARNING: no logging configured on bucket '" << src_bucket_id << "'" << dendl;
248255
return;
249256
}
250257
rgw::bucketlogging::configuration configuration;
@@ -258,24 +265,32 @@ class RGWPostBucketLoggingOp : public RGWDefaultResponseOp {
258265
return;
259266
}
260267

268+
std::string target_bucket_name;
269+
std::string target_tenant_name;
270+
op_ret = rgw_parse_url_bucket(configuration.target_bucket, s->bucket_tenant, target_tenant_name, target_bucket_name);
271+
if (op_ret < 0) {
272+
ldpp_dout(this, 1) << "ERROR: failed to parse target bucket '" << configuration.target_bucket << "', ret = " << op_ret << dendl;
273+
return;
274+
}
275+
const rgw_bucket target_bucket_id(target_tenant_name, target_bucket_name);
261276
std::unique_ptr<rgw::sal::Bucket> target_bucket;
262-
op_ret = driver->load_bucket(this, rgw_bucket(s->bucket_tenant, configuration.target_bucket),
277+
op_ret = driver->load_bucket(this, target_bucket_id,
263278
&target_bucket, y);
264279
if (op_ret < 0) {
265-
ldpp_dout(this, 1) << "ERROR: failed to get target bucket '" << configuration.target_bucket << "', ret = " << op_ret << dendl;
280+
ldpp_dout(this, 1) << "ERROR: failed to get target bucket '" << target_bucket_id << "', ret = " << op_ret << dendl;
266281
return;
267282
}
268283
std::string obj_name;
269284
RGWObjVersionTracker objv_tracker;
270285
op_ret = target_bucket->get_logging_object_name(obj_name, configuration.target_prefix, null_yield, this, &objv_tracker);
271286
if (op_ret < 0) {
272-
ldpp_dout(this, 1) << "ERROR: failed to get pending logging object name from target bucket '" << configuration.target_bucket << "'" << dendl;
287+
ldpp_dout(this, 1) << "ERROR: failed to get pending logging object name from target bucket '" << target_bucket_id << "'" << dendl;
273288
return;
274289
}
275290
op_ret = rgw::bucketlogging::rollover_logging_object(configuration, target_bucket, obj_name, this, null_yield, true, &objv_tracker);
276291
if (op_ret < 0) {
277292
ldpp_dout(this, 1) << "ERROR: failed to flush pending logging object '" << obj_name
278-
<< "' to target bucket '" << configuration.target_bucket << "'" << dendl;
293+
<< "' to target bucket '" << target_bucket_id << "'" << dendl;
279294
return;
280295
}
281296
ldpp_dout(this, 20) << "flushed pending logging object '" << obj_name

0 commit comments

Comments
 (0)