1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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()) {
96 // S3 generates a 48 bit random ID, maybe we could generate shorter IDs
97 static constexpr auto LC_ID_LENGTH
= 48;
99 gen_rand_alphanumeric_lower(cct
, &id
, LC_ID_LENGTH
);
106 if (cct
->_conf
->rgw_lc_max_rules
< rule_map
.size()) {
108 ss
<< "Warn: The lifecycle config has too many rules, rule number is:"
109 << rule_map
.size() << ", max number is:" << cct
->_conf
->rgw_lc_max_rules
;
110 throw RGWXMLDecoder::err(ss
.str());
114 void LCFilter_S3::dump_xml(Formatter
*f
) const
117 encode_xml("Prefix", prefix
, f
);
119 bool multi
= has_multi_condition();
121 f
->open_array_section("And");
124 const auto& tagset_s3
= static_cast<const RGWObjTagSet_S3
&>(obj_tags
);
125 tagset_s3
.dump_xml(f
);
132 void LCFilter_S3::decode_xml(XMLObj
*obj
)
134 XMLObj
*o
= obj
->find_first("And");
135 bool single_cond
= false;
136 int num_conditions
= 0;
137 // If there is an AND condition, every tag is a child of and
138 // else we only support single conditions and return false if we see multiple
145 RGWXMLDecoder::decode_xml("Prefix", prefix
, o
);
148 auto tags_iter
= o
->find("Tag");
150 while (auto tag_xml
=tags_iter
.get_next()){
151 std::string _key
,_val
;
152 RGWXMLDecoder::decode_xml("Key", _key
, tag_xml
);
153 RGWXMLDecoder::decode_xml("Value", _val
, tag_xml
);
154 obj_tags
.emplace_tag(std::move(_key
), std::move(_val
));
158 if (single_cond
&& num_conditions
> 1) {
159 throw RGWXMLDecoder::err("Bad filter: badly formed multiple conditions");
163 void LCTransition_S3::decode_xml(XMLObj
*obj
)
165 bool has_days
= RGWXMLDecoder::decode_xml("Days", days
, obj
);
166 bool has_date
= RGWXMLDecoder::decode_xml("Date", date
, obj
);
167 if ((has_days
&& has_date
) || (!has_days
&& !has_date
)) {
168 throw RGWXMLDecoder::err("bad Transition section");
171 if (has_date
&& !check_date(date
)) {
172 //We need return xml error according to S3
173 throw RGWXMLDecoder::err("bad Date in Transition section");
176 if (!RGWXMLDecoder::decode_xml("StorageClass", storage_class
, obj
)) {
177 throw RGWXMLDecoder::err("missing StorageClass in Transition section");
181 void LCTransition_S3::dump_xml(Formatter
*f
) const {
183 encode_xml("Days", days
, f
);
185 encode_xml("Date", date
, f
);
187 encode_xml("StorageClass", storage_class
, f
);
190 void LCNoncurTransition_S3::decode_xml(XMLObj
*obj
)
192 if (!RGWXMLDecoder::decode_xml("NoncurrentDays", days
, obj
)) {
193 throw RGWXMLDecoder::err("missing NoncurrentDays in NoncurrentVersionTransition section");
195 if (!RGWXMLDecoder::decode_xml("StorageClass", storage_class
, obj
)) {
196 throw RGWXMLDecoder::err("missing StorageClass in NoncurrentVersionTransition section");
200 void LCNoncurTransition_S3::dump_xml(Formatter
*f
) const
202 encode_xml("NoncurrentDays", days
, f
);
203 encode_xml("StorageClass", storage_class
, f
);
206 void LCRule_S3::decode_xml(XMLObj
*obj
)
211 dm_expiration
= false;
213 RGWXMLDecoder::decode_xml("ID", id
, obj
);
215 LCFilter_S3 filter_s3
;
216 if (!RGWXMLDecoder::decode_xml("Filter", filter_s3
, obj
)) {
217 // Ideally the following code should be deprecated and we should return
218 // False here, The new S3 LC configuration xml spec. makes Filter mandatory
219 // and Prefix optional. However older clients including boto2 still generate
220 // xml according to the older spec, where Prefix existed outside of Filter
221 // and S3 itself seems to be sloppy on enforcing the mandatory Filter
222 // argument. A day will come when S3 enforces their own xml-spec, but it is
225 if (!RGWXMLDecoder::decode_xml("Prefix", prefix
, obj
)) {
226 throw RGWXMLDecoder::err("missing Prefix in Filter");
229 filter
= (LCFilter
)filter_s3
;
231 if (!RGWXMLDecoder::decode_xml("Status", status
, obj
)) {
232 throw RGWXMLDecoder::err("missing Status in Filter");
234 if (status
.compare("Enabled") != 0 && status
.compare("Disabled") != 0) {
235 throw RGWXMLDecoder::err("bad Status in Filter");
238 LCExpiration_S3 s3_expiration
;
239 LCNoncurExpiration_S3 s3_noncur_expiration
;
240 LCMPExpiration_S3 s3_mp_expiration
;
241 LCFilter_S3 s3_filter
;
243 bool has_expiration
= RGWXMLDecoder::decode_xml("Expiration", s3_expiration
, obj
);
244 bool has_noncur_expiration
= RGWXMLDecoder::decode_xml("NoncurrentVersionExpiration", s3_noncur_expiration
, obj
);
245 bool has_mp_expiration
= RGWXMLDecoder::decode_xml("AbortIncompleteMultipartUpload", s3_mp_expiration
, obj
);
247 vector
<LCTransition_S3
> transitions
;
248 vector
<LCNoncurTransition_S3
> noncur_transitions
;
250 bool has_transition
= RGWXMLDecoder::decode_xml("Transition", transitions
, obj
);
251 bool has_noncur_transition
= RGWXMLDecoder::decode_xml("NoncurrentVersionTransition", noncur_transitions
, obj
);
253 if (!has_expiration
&&
254 !has_noncur_expiration
&&
255 !has_mp_expiration
&&
257 !has_noncur_transition
) {
258 throw RGWXMLDecoder::err("bad Rule");
261 if (has_expiration
) {
262 if (s3_expiration
.has_days() ||
263 s3_expiration
.has_date()) {
264 expiration
= s3_expiration
;
266 dm_expiration
= s3_expiration
.get_dm_expiration();
269 if (has_noncur_expiration
) {
270 noncur_expiration
= s3_noncur_expiration
;
272 if (has_mp_expiration
) {
273 mp_expiration
= s3_mp_expiration
;
275 for (auto& t
: transitions
) {
276 if (!add_transition(t
)) {
277 throw RGWXMLDecoder::err("Failed to add transition");
280 for (auto& t
: noncur_transitions
) {
281 if (!add_noncur_transition(t
)) {
282 throw RGWXMLDecoder::err("Failed to add non-current version transition");
287 void LCRule_S3::dump_xml(Formatter
*f
) const {
288 encode_xml("ID", id
, f
);
289 // In case of an empty filter and an empty Prefix, we defer to Prefix.
290 if (!filter
.empty()) {
291 const LCFilter_S3
& lc_filter
= static_cast<const LCFilter_S3
&>(filter
);
292 encode_xml("Filter", lc_filter
, f
);
294 encode_xml("Prefix", prefix
, f
);
296 encode_xml("Status", status
, f
);
297 if (!expiration
.empty() || dm_expiration
) {
298 LCExpiration_S3
expir(expiration
.get_days_str(), expiration
.get_date(), dm_expiration
);
299 encode_xml("Expiration", expir
, f
);
301 if (!noncur_expiration
.empty()) {
302 const LCNoncurExpiration_S3
& noncur_expir
= static_cast<const LCNoncurExpiration_S3
&>(noncur_expiration
);
303 encode_xml("NoncurrentVersionExpiration", noncur_expir
, f
);
305 if (!mp_expiration
.empty()) {
306 const LCMPExpiration_S3
& mp_expir
= static_cast<const LCMPExpiration_S3
&>(mp_expiration
);
307 encode_xml("AbortIncompleteMultipartUpload", mp_expir
, f
);
309 if (!transitions
.empty()) {
310 for (auto &elem
: transitions
) {
311 const LCTransition_S3
& tran
= static_cast<const LCTransition_S3
&>(elem
.second
);
312 encode_xml("Transition", tran
, f
);
315 if (!noncur_transitions
.empty()) {
316 for (auto &elem
: noncur_transitions
) {
317 const LCNoncurTransition_S3
& noncur_tran
= static_cast<const LCNoncurTransition_S3
&>(elem
.second
);
318 encode_xml("NoncurrentVersionTransition", noncur_tran
, f
);
323 int RGWLifecycleConfiguration_S3::rebuild(RGWRados
*store
, RGWLifecycleConfiguration
& dest
)
326 multimap
<string
, LCRule
>::iterator iter
;
327 for (iter
= rule_map
.begin(); iter
!= rule_map
.end(); ++iter
) {
328 LCRule
& src_rule
= iter
->second
;
329 ret
= dest
.check_and_add_rule(src_rule
);
334 ret
= -ERR_INVALID_REQUEST
;
340 void RGWLifecycleConfiguration_S3::dump_xml(Formatter
*f
) const
342 for (auto iter
= rule_map
.begin(); iter
!= rule_map
.end(); ++iter
) {
343 const LCRule_S3
& rule
= static_cast<const LCRule_S3
&>(iter
->second
);
344 encode_xml("Rule", rule
, f
);