1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
9 #include "include/types.h"
12 #include "rgw_lc_s3.h"
15 #define dout_subsys ceph_subsys_rgw
17 static bool check_date(const string
& _date
)
19 boost::optional
<ceph::real_time
> date
= ceph::from_iso_8601(_date
);
20 if (boost::none
== date
) {
23 struct timespec time
= ceph::real_clock::to_timespec(*date
);
24 if (time
.tv_sec
% (24*60*60) || time
.tv_nsec
) {
30 void LCExpiration_S3::dump_xml(Formatter
*f
) const {
32 encode_xml("ExpiredObjectDeleteMarker", "true", f
);
33 } else if (!days
.empty()) {
34 encode_xml("Days", days
, f
);
36 encode_xml("Date", date
, f
);
40 void LCExpiration_S3::decode_xml(XMLObj
*obj
)
42 bool has_days
= RGWXMLDecoder::decode_xml("Days", days
, obj
);
43 bool has_date
= RGWXMLDecoder::decode_xml("Date", date
, obj
);
45 bool has_dm
= RGWXMLDecoder::decode_xml("ExpiredObjectDeleteMarker", dm
, obj
);
47 int num
= !!has_days
+ !!has_date
+ !!has_dm
;
50 throw RGWXMLDecoder::err("bad Expiration section");
53 if (has_date
&& !check_date(date
)) {
54 //We need return xml error according to S3
55 throw RGWXMLDecoder::err("bad date in Date section");
59 dm_expiration
= (dm
== "true");
63 void LCNoncurExpiration_S3::decode_xml(XMLObj
*obj
)
65 RGWXMLDecoder::decode_xml("NoncurrentDays", days
, obj
, true);
68 void LCNoncurExpiration_S3::dump_xml(Formatter
*f
) const
70 encode_xml("NoncurrentDays", days
, f
);
73 void LCMPExpiration_S3::decode_xml(XMLObj
*obj
)
75 RGWXMLDecoder::decode_xml("DaysAfterInitiation", days
, obj
, true);
78 void LCMPExpiration_S3::dump_xml(Formatter
*f
) const
80 encode_xml("DaysAfterInitiation", days
, f
);
83 void RGWLifecycleConfiguration_S3::decode_xml(XMLObj
*obj
)
86 throw RGWXMLDecoder::err("ERROR: RGWLifecycleConfiguration_S3 can't be decoded without cct initialized");
88 vector
<LCRule_S3
> rules
;
90 RGWXMLDecoder::decode_xml("Rule", rules
, obj
, true);
92 for (auto& rule
: rules
) {
93 if (rule
.get_id().empty()) {
94 // S3 generates a 48 bit random ID, maybe we could generate shorter IDs
95 static constexpr auto LC_ID_LENGTH
= 48;
96 string id
= gen_rand_alphanumeric_lower(cct
, LC_ID_LENGTH
);
103 if (cct
->_conf
->rgw_lc_max_rules
< rule_map
.size()) {
105 ss
<< "Warn: The lifecycle config has too many rules, rule number is:"
106 << rule_map
.size() << ", max number is:" << cct
->_conf
->rgw_lc_max_rules
;
107 throw RGWXMLDecoder::err(ss
.str());
111 void LCFilter_S3::dump_xml(Formatter
*f
) const
114 encode_xml("Prefix", prefix
, f
);
116 bool multi
= has_multi_condition();
118 f
->open_array_section("And");
121 const auto& tagset_s3
= static_cast<const RGWObjTagSet_S3
&>(obj_tags
);
122 tagset_s3
.dump_xml(f
);
129 void LCFilter_S3::decode_xml(XMLObj
*obj
)
131 XMLObj
*o
= obj
->find_first("And");
132 bool single_cond
= false;
133 int num_conditions
= 0;
134 // If there is an AND condition, every tag is a child of and
135 // else we only support single conditions and return false if we see multiple
142 RGWXMLDecoder::decode_xml("Prefix", prefix
, o
);
145 auto tags_iter
= o
->find("Tag");
147 while (auto tag_xml
=tags_iter
.get_next()){
148 std::string _key
,_val
;
149 RGWXMLDecoder::decode_xml("Key", _key
, tag_xml
);
150 RGWXMLDecoder::decode_xml("Value", _val
, tag_xml
);
151 obj_tags
.emplace_tag(std::move(_key
), std::move(_val
));
155 if (single_cond
&& num_conditions
> 1) {
156 throw RGWXMLDecoder::err("Bad filter: badly formed multiple conditions");
160 void LCTransition_S3::decode_xml(XMLObj
*obj
)
162 bool has_days
= RGWXMLDecoder::decode_xml("Days", days
, obj
);
163 bool has_date
= RGWXMLDecoder::decode_xml("Date", date
, obj
);
164 if ((has_days
&& has_date
) || (!has_days
&& !has_date
)) {
165 throw RGWXMLDecoder::err("bad Transition section");
168 if (has_date
&& !check_date(date
)) {
169 //We need return xml error according to S3
170 throw RGWXMLDecoder::err("bad Date in Transition section");
173 if (!RGWXMLDecoder::decode_xml("StorageClass", storage_class
, obj
)) {
174 throw RGWXMLDecoder::err("missing StorageClass in Transition section");
178 void LCTransition_S3::dump_xml(Formatter
*f
) const {
180 encode_xml("Days", days
, f
);
182 encode_xml("Date", date
, f
);
184 encode_xml("StorageClass", storage_class
, f
);
187 void LCNoncurTransition_S3::decode_xml(XMLObj
*obj
)
189 if (!RGWXMLDecoder::decode_xml("NoncurrentDays", days
, obj
)) {
190 throw RGWXMLDecoder::err("missing NoncurrentDays in NoncurrentVersionTransition section");
192 if (!RGWXMLDecoder::decode_xml("StorageClass", storage_class
, obj
)) {
193 throw RGWXMLDecoder::err("missing StorageClass in NoncurrentVersionTransition section");
197 void LCNoncurTransition_S3::dump_xml(Formatter
*f
) const
199 encode_xml("NoncurrentDays", days
, f
);
200 encode_xml("StorageClass", storage_class
, f
);
203 void LCRule_S3::decode_xml(XMLObj
*obj
)
208 dm_expiration
= false;
210 RGWXMLDecoder::decode_xml("ID", id
, obj
);
212 LCFilter_S3 filter_s3
;
213 if (!RGWXMLDecoder::decode_xml("Filter", filter_s3
, obj
)) {
214 // Ideally the following code should be deprecated and we should return
215 // False here, The new S3 LC configuration xml spec. makes Filter mandatory
216 // and Prefix optional. However older clients including boto2 still generate
217 // xml according to the older spec, where Prefix existed outside of Filter
218 // and S3 itself seems to be sloppy on enforcing the mandatory Filter
219 // argument. A day will come when S3 enforces their own xml-spec, but it is
222 if (!RGWXMLDecoder::decode_xml("Prefix", prefix
, obj
)) {
223 throw RGWXMLDecoder::err("missing Prefix in Filter");
226 filter
= (LCFilter
)filter_s3
;
228 if (!RGWXMLDecoder::decode_xml("Status", status
, obj
)) {
229 throw RGWXMLDecoder::err("missing Status in Filter");
231 if (status
.compare("Enabled") != 0 && status
.compare("Disabled") != 0) {
232 throw RGWXMLDecoder::err("bad Status in Filter");
235 LCExpiration_S3 s3_expiration
;
236 LCNoncurExpiration_S3 s3_noncur_expiration
;
237 LCMPExpiration_S3 s3_mp_expiration
;
238 LCFilter_S3 s3_filter
;
240 bool has_expiration
= RGWXMLDecoder::decode_xml("Expiration", s3_expiration
, obj
);
241 bool has_noncur_expiration
= RGWXMLDecoder::decode_xml("NoncurrentVersionExpiration", s3_noncur_expiration
, obj
);
242 bool has_mp_expiration
= RGWXMLDecoder::decode_xml("AbortIncompleteMultipartUpload", s3_mp_expiration
, obj
);
244 vector
<LCTransition_S3
> transitions
;
245 vector
<LCNoncurTransition_S3
> noncur_transitions
;
247 bool has_transition
= RGWXMLDecoder::decode_xml("Transition", transitions
, obj
);
248 bool has_noncur_transition
= RGWXMLDecoder::decode_xml("NoncurrentVersionTransition", noncur_transitions
, obj
);
250 if (!has_expiration
&&
251 !has_noncur_expiration
&&
252 !has_mp_expiration
&&
254 !has_noncur_transition
) {
255 throw RGWXMLDecoder::err("bad Rule");
258 if (has_expiration
) {
259 if (s3_expiration
.has_days() ||
260 s3_expiration
.has_date()) {
261 expiration
= s3_expiration
;
263 dm_expiration
= s3_expiration
.get_dm_expiration();
266 if (has_noncur_expiration
) {
267 noncur_expiration
= s3_noncur_expiration
;
269 if (has_mp_expiration
) {
270 mp_expiration
= s3_mp_expiration
;
272 for (auto& t
: transitions
) {
273 if (!add_transition(t
)) {
274 throw RGWXMLDecoder::err("Failed to add transition");
277 for (auto& t
: noncur_transitions
) {
278 if (!add_noncur_transition(t
)) {
279 throw RGWXMLDecoder::err("Failed to add non-current version transition");
284 void LCRule_S3::dump_xml(Formatter
*f
) const {
285 encode_xml("ID", id
, f
);
286 // In case of an empty filter and an empty Prefix, we defer to Prefix.
287 if (!filter
.empty()) {
288 const LCFilter_S3
& lc_filter
= static_cast<const LCFilter_S3
&>(filter
);
289 encode_xml("Filter", lc_filter
, f
);
291 encode_xml("Prefix", prefix
, f
);
293 encode_xml("Status", status
, f
);
294 if (!expiration
.empty() || dm_expiration
) {
295 LCExpiration_S3
expir(expiration
.get_days_str(), expiration
.get_date(), dm_expiration
);
296 encode_xml("Expiration", expir
, f
);
298 if (!noncur_expiration
.empty()) {
299 const LCNoncurExpiration_S3
& noncur_expir
= static_cast<const LCNoncurExpiration_S3
&>(noncur_expiration
);
300 encode_xml("NoncurrentVersionExpiration", noncur_expir
, f
);
302 if (!mp_expiration
.empty()) {
303 const LCMPExpiration_S3
& mp_expir
= static_cast<const LCMPExpiration_S3
&>(mp_expiration
);
304 encode_xml("AbortIncompleteMultipartUpload", mp_expir
, f
);
306 if (!transitions
.empty()) {
307 for (auto &elem
: transitions
) {
308 const LCTransition_S3
& tran
= static_cast<const LCTransition_S3
&>(elem
.second
);
309 encode_xml("Transition", tran
, f
);
312 if (!noncur_transitions
.empty()) {
313 for (auto &elem
: noncur_transitions
) {
314 const LCNoncurTransition_S3
& noncur_tran
= static_cast<const LCNoncurTransition_S3
&>(elem
.second
);
315 encode_xml("NoncurrentVersionTransition", noncur_tran
, f
);
320 int RGWLifecycleConfiguration_S3::rebuild(RGWRados
*store
, RGWLifecycleConfiguration
& dest
)
323 multimap
<string
, LCRule
>::iterator iter
;
324 for (iter
= rule_map
.begin(); iter
!= rule_map
.end(); ++iter
) {
325 LCRule
& src_rule
= iter
->second
;
326 ret
= dest
.check_and_add_rule(src_rule
);
331 ret
= -ERR_INVALID_REQUEST
;
337 void RGWLifecycleConfiguration_S3::dump_xml(Formatter
*f
) const
339 for (auto iter
= rule_map
.begin(); iter
!= rule_map
.end(); ++iter
) {
340 const LCRule_S3
& rule
= static_cast<const LCRule_S3
&>(iter
->second
);
341 encode_xml("Rule", rule
, f
);