From: Yehuda Sadeh Date: Thu, 28 Nov 2019 04:00:34 +0000 (-0800) Subject: rgw: implement s3 get bucket replication api X-Git-Tag: v15.1.0~22^2~30 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=5386f987c10512850c08023d9cfb833ff85abd03;p=ceph-ci.git rgw: implement s3 get bucket replication api Signed-off-by: Yehuda Sadeh --- diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 207cc3e4b52..e02e1f9d965 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -1249,8 +1249,27 @@ void RGWDeleteBucketTags::execute() }); } +int RGWGetBucketReplication::verify_permission() +{ + if (!verify_bucket_permission(this, s, rgw::IAM::s3GetReplicationConfiguration)) { + return -EACCES; + } + + return 0; +} + +void RGWGetBucketReplication::pre_exec() +{ + rgw_bucket_object_pre_exec(s); +} + +void RGWGetBucketReplication::execute() +{ + send_response_data(); +} + int RGWPutBucketReplication::verify_permission() { - return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketTagging); + return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutReplicationConfiguration); } void RGWPutBucketReplication::execute() { @@ -1294,7 +1313,7 @@ void RGWDeleteBucketReplication::pre_exec() int RGWDeleteBucketReplication::verify_permission() { - return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketTagging); + return verify_bucket_owner_or_policy(s, rgw::IAM::s3DeleteReplicationConfiguration); } void RGWDeleteBucketReplication::execute() diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index 50c9b772b24..e6d90341c5f 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -468,6 +468,18 @@ public: struct rgw_sync_policy_group; +class RGWGetBucketReplication : public RGWOp { +public: + int verify_permission() override; + void execute() override; + void pre_exec() override; + + virtual void send_response_data() = 0; + const char* name() const override { return "get_bucket_replication"; } + virtual uint32_t op_mask() override { return RGW_OP_TYPE_READ; } + RGWOpType get_type() override { return RGW_OP_GET_BUCKET_REPLICATION; } +}; + class RGWPutBucketReplication : public RGWOp { protected: bufferlist in_data; diff --git a/src/rgw/rgw_rest.h b/src/rgw/rgw_rest.h index 1b315c7c76c..ce949845312 100644 --- a/src/rgw/rgw_rest.h +++ b/src/rgw/rgw_rest.h @@ -194,6 +194,12 @@ public: virtual ~RGWPutBucketTags_ObjStore() = default; }; +class RGWGetBucketReplication_ObjStore : public RGWGetBucketReplication { +public: + RGWGetBucketReplication_ObjStore() {}; + ~RGWGetBucketReplication_ObjStore() {}; +}; + class RGWPutBucketReplication_ObjStore: public RGWPutBucketReplication { public: RGWPutBucketReplication_ObjStore() = default; diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 40f2aff2b54..c2f7c298a01 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -623,6 +623,10 @@ struct ReplicationConfiguration { RGWXMLDecoder::decode_xml("Status", status, obj); } + void dump_xml(Formatter *f) const { + encode_xml("Status", status, f); + } + bool is_valid(CephContext *cct) const { bool result = is_valid_status(status); if (!result) { @@ -638,6 +642,10 @@ struct ReplicationConfiguration { void decode_xml(XMLObj *obj) { RGWXMLDecoder::decode_xml("Zone", zone_names, obj); } + + void dump_xml(Formatter *f) const { + encode_xml("Zone", zone_names, f); + } }; struct Destination { @@ -647,6 +655,9 @@ struct ReplicationConfiguration { void decode_xml(XMLObj *obj) { RGWXMLDecoder::decode_xml("Owner", owner, obj); } + void dump_xml(Formatter *f) const { + encode_xml("Owner", owner, f); + } }; std::optional acl_translation; @@ -668,6 +679,14 @@ struct ReplicationConfiguration { } RGWXMLDecoder::decode_xml("Zone", zone_names, obj); /* rgw extension */ } + + void dump_xml(Formatter *f) const { + encode_xml("AccessControlTranslation", acl_translation, f); + encode_xml("Account", account, f); + encode_xml("Bucket", bucket, f); + encode_xml("StorageClass", storage_class, f); + encode_xml("Zone", zone_names, f); + } }; struct Filter { @@ -683,6 +702,11 @@ struct ReplicationConfiguration { RGWXMLDecoder::decode_xml("Key", key, obj); RGWXMLDecoder::decode_xml("Value", value, obj); }; + + void dump_xml(Formatter *f) const { + encode_xml("Key", key, f); + encode_xml("Value", value, f); + } }; struct AndElements { @@ -707,12 +731,21 @@ struct ReplicationConfiguration { } } }; + + void dump_xml(Formatter *f) const { + encode_xml("Prefix", prefix, f); + encode_xml("Tag", tags, f); + } }; std::optional prefix; std::optional tag; std::optional and_elements; + bool empty() const { + return (!prefix && !tag && !and_elements); + } + void decode_xml(XMLObj *obj) { RGWXMLDecoder::decode_xml("Prefix", prefix, obj); if (prefix && prefix->empty()) { @@ -728,6 +761,12 @@ struct ReplicationConfiguration { } }; + void dump_xml(Formatter *f) const { + encode_xml("Prefix", prefix, f); + encode_xml("Tag", tag, f); + encode_xml("And", and_elements, f); + } + bool is_valid(CephContext *cct) const { if (tag && prefix) { ldout(cct, 5) << "NOTICE: both tag and prefix were provided in replication filter rule" << dendl; @@ -765,6 +804,42 @@ struct ReplicationConfiguration { } return 0; } + + void from_sync_pipe_filter(const rgw_sync_pipe_filter& f) { + if (f.prefix && f.tags.empty()) { + prefix = f.prefix; + return; + } + if (f.prefix) { + and_elements.emplace(); + and_elements->prefix = f.prefix; + } else if (f.tags.size() == 1) { + auto iter = f.tags.begin(); + if (iter == f.tags.end()) { + /* should never happen */ + return; + } + auto& t = *iter; + tag.emplace(); + tag->key = t.key; + tag->value = t.value; + return; + } + + if (f.tags.empty()) { + return; + } + + if (!and_elements) { + and_elements.emplace(); + } + + for (auto& t : f.tags) { + auto& tag = and_elements->tags.emplace_back(); + tag.key = t.key; + tag.value = t.value; + } + } }; set get_zone_ids_from_names(rgw::sal::RGWRadosStore *store, @@ -781,6 +856,20 @@ struct ReplicationConfiguration { return std::move(ids); } + vector get_zone_names_from_ids(rgw::sal::RGWRadosStore *store, + const set& zone_ids) const { + vector names; + + for (auto& id : zone_ids) { + RGWZone *zone; + if (store->svc()->zone->find_zone(id, &zone)) { + names.emplace_back(zone->name); + } + } + + return std::move(names); + } + std::optional delete_marker_replication; std::optional source; Destination destination; @@ -805,6 +894,9 @@ struct ReplicationConfiguration { if (!filter) { RGWXMLDecoder::decode_xml("Filter", filter, obj); } else { + /* don't want to have filter reset because it might have been initialized + * when decoding prefix + */ RGWXMLDecoder::decode_xml("Filter", *filter, obj); } @@ -812,6 +904,16 @@ struct ReplicationConfiguration { RGWXMLDecoder::decode_xml("Status", status, obj); } + void dump_xml(Formatter *f) const { + encode_xml("DeleteMarkerReplication", delete_marker_replication, f); + encode_xml("Source", source, f); + encode_xml("Destination", destination, f); + encode_xml("Filter", filter, f); + encode_xml("ID", id, f); + encode_xml("Priority", priority, f); + encode_xml("Status", status, f); + } + bool is_valid(CephContext *cct) const { if (!is_valid_status(status)) { ldout(cct, 5) << "NOTICE: bad status provided in rule (status=" << status << ")" << dendl; @@ -874,6 +976,46 @@ struct ReplicationConfiguration { return 0; } + + void from_sync_policy_pipe(rgw::sal::RGWRadosStore *store, + const rgw_sync_bucket_pipes& pipe, + bool enabled) { + id = pipe.id; + status = (enabled ? "Enabled" : "Disabled"); + priority = pipe.params.priority; + + if (pipe.source.all_zones) { + source.reset(); + } else if (pipe.source.zones) { + source.emplace(); + source->zone_names = get_zone_names_from_ids(store, *pipe.source.zones); + } + + if (!pipe.dest.all_zones && + pipe.dest.zones) { + destination.zone_names = get_zone_names_from_ids(store, *pipe.dest.zones); + } + + if (pipe.params.dest.acl_translation) { + destination.acl_translation.emplace(); + destination.acl_translation->owner = pipe.params.dest.acl_translation->owner.to_str(); + } + + if (pipe.params.dest.storage_class) { + destination.storage_class = *pipe.params.dest.storage_class; + } + + if (pipe.dest.bucket) { + destination.bucket = pipe.dest.bucket->get_key(); + } + + filter.emplace(); + filter->from_sync_pipe_filter(pipe.params.source.filter); + + if (filter->empty()) { + filter.reset(); + } + } }; std::vector rules; @@ -883,6 +1025,11 @@ struct ReplicationConfiguration { RGWXMLDecoder::decode_xml("Rule", rules, obj); } + void dump_xml(Formatter *f) const { + encode_xml("Role", role, f); + encode_xml("Rule", rules, f); + } + int to_sync_policy_groups(req_state *s, rgw::sal::RGWRadosStore *store, vector *result) const { result->resize(2); @@ -912,10 +1059,52 @@ struct ReplicationConfiguration { } return 0; } + + void from_sync_policy_group(rgw::sal::RGWRadosStore *store, + const rgw_sync_policy_group& group) { + + bool enabled = (group.status == rgw_sync_policy_group::Status::ENABLED); + + for (auto& pipe : group.pipes) { + auto& rule = rules.emplace_back(); + rule.from_sync_policy_pipe(store, pipe, enabled); + } + } }; } +void RGWGetBucketReplication_ObjStore_S3::send_response_data() +{ + if (op_ret) + set_req_state_err(s, op_ret); + dump_errno(s); + end_header(s, this, "application/xml"); + dump_start(s); + + ReplicationConfiguration conf; + + if (s->bucket_info.sync_policy) { + auto policy = s->bucket_info.sync_policy; + + auto iter = policy->groups.find(enabled_group_id); + if (iter != policy->groups.end()) { + conf.from_sync_policy_group(store, iter->second); + } + iter = policy->groups.find(disabled_group_id); + if (iter != policy->groups.end()) { + conf.from_sync_policy_group(store, iter->second); + } + } + + if (!op_ret) { + s->formatter->open_object_section_in_ns("ReplicationConfiguration", XMLNS_AWS_S3); + conf.dump_xml(s->formatter); + s->formatter->close_section(); + rgw_flush_formatter_and_reset(s, s->formatter); + } +} + int RGWPutBucketReplication_ObjStore_S3::get_params() { RGWXMLParser parser; @@ -4087,7 +4276,7 @@ RGWOp *RGWHandler_REST_Bucket_S3::op_get() } else if (is_notification_op()) { return RGWHandler_REST_PSNotifs_S3::create_get_op(); } else if (is_replication_op()) { - return nullptr; // new RGWGetBucketReplication_ObjStore_S3; + return new RGWGetBucketReplication_ObjStore_S3; } return get_obj_op(true); } diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h index a301f24e1fd..513be8a0ebc 100644 --- a/src/rgw/rgw_rest_s3.h +++ b/src/rgw/rgw_rest_s3.h @@ -101,6 +101,12 @@ public: void send_response() override; }; +class RGWGetBucketReplication_ObjStore_S3 : public RGWGetBucketReplication_ObjStore +{ +public: + void send_response_data() override; +}; + class RGWPutBucketReplication_ObjStore_S3 : public RGWPutBucketReplication_ObjStore { public: diff --git a/src/rgw/rgw_xml.h b/src/rgw/rgw_xml.h index 7712572d6f8..b3e1e8ee681 100644 --- a/src/rgw/rgw_xml.h +++ b/src/rgw/rgw_xml.h @@ -353,6 +353,23 @@ static void do_encode_xml(const char *name, const std::list& l, const char *e f->close_section(); } +template +static void encode_xml(const char *name, const std::vector& l, ceph::Formatter *f) +{ + for (typename std::vector::const_iterator iter = l.begin(); iter != l.end(); ++iter) { + encode_xml(name, *iter, f); + } +} + +template +static void encode_xml(const char *name, const std::optional& o, ceph::Formatter *f) +{ + if (!o) { + return; + } + + encode_xml(name, *o, f); +} #endif