op.mp_expiration = rule->get_mp_expiration().get_days();
}
op.dm_expiration = rule->get_dm_expiration();
- auto ret = prefix_map.insert(pair<string, lc_op>(rule->get_prefix(), op));
+
+ std::string prefix;
+ if (rule->get_filter().has_prefix()){
+ prefix = rule->get_filter().get_prefix();
+ } else {
+ prefix = rule->get_prefix();
+ }
+ auto ret = prefix_map.emplace(std::move(prefix), std::move(op));
return ret.second;
}
};
WRITE_CLASS_ENCODER(LCExpiration)
+class LCFilter
+{
+ protected:
+ std::string prefix;
+ // TODO add support for tagging
+ public:
+ const std::string& get_prefix() const{
+ return prefix;
+ }
+
+ void set_prefix(const string& _prefix){
+ prefix = _prefix;
+ }
+
+ void set_prefix(std::string&& _prefix){
+ prefix = std::move(_prefix);
+ }
+
+ bool empty() const {
+ return prefix.empty();
+ }
+
+ bool has_prefix() const {
+ return !prefix.empty();
+ }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(prefix, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(1, bl);
+ ::decode(prefix, bl);
+ DECODE_FINISH(bl);
+ }
+};
+WRITE_CLASS_ENCODER(LCFilter);
+
+
+
class LCRule
{
protected:
LCExpiration expiration;
LCExpiration noncur_expiration;
LCExpiration mp_expiration;
+ LCFilter filter;
bool dm_expiration = false;
public:
string& get_status() {
return status;
}
-
+
string& get_prefix() {
return prefix;
}
+ LCFilter& get_filter() {
+ return filter;
+ }
+
LCExpiration& get_expiration() {
return expiration;
}
bool valid();
void encode(bufferlist& bl) const {
- ENCODE_START(4, 1, bl);
+ ENCODE_START(5, 1, bl);
::encode(id, bl);
::encode(prefix, bl);
::encode(status, bl);
::encode(noncur_expiration, bl);
::encode(mp_expiration, bl);
::encode(dm_expiration, bl);
+ ::encode(filter, bl);
ENCODE_FINISH(bl);
}
void decode(bufferlist::iterator& bl) {
- DECODE_START_LEGACY_COMPAT_LEN(4, 1, 1, bl);
+ DECODE_START_LEGACY_COMPAT_LEN(5, 1, 1, bl);
::decode(id, bl);
::decode(prefix, bl);
::decode(status, bl);
if (struct_v >= 4) {
::decode(dm_expiration, bl);
}
+ if (struct_v >= 5) {
+ ::decode(filter, bl);
+ }
DECODE_FINISH(bl);
}
status.clear();
dm_expiration = false;
+ // S3 generates a 48 bit random ID, maybe we could generate shorter IDs
+ static constexpr auto LC_ID_LENGTH = 48;
+
lc_id = static_cast<LCID_S3 *>(find_first("ID"));
- if (!lc_id)
- return false;
- id = lc_id->get_data();
+ if (lc_id){
+ id = lc_id->get_data();
+ } else {
+ gen_rand_alphanumeric_lower(nullptr, &id, LC_ID_LENGTH);
+ }
+
+
+ XMLObj *obj = find_first("Filter");
+
+ if (obj){
+ string _prefix;
+ RGWXMLDecoder::decode_xml("Prefix", _prefix, obj);
+ filter.set_prefix(std::move(_prefix));
+ } else {
+ // Ideally the following code should be deprecated and we should return
+ // False here, The new S3 LC configuration xml spec. makes Filter mandatory
+ // and Prefix optional. However older clients including boto2 still generate
+ // xml according to the older spec, where Prefix existed outside of Filter
+ // and S3 itself seems to be sloppy on enforcing the mandatory Filter
+ // argument. A day will come when S3 enforces their own xml-spec, but it is
+ // not this day
+
+ lc_prefix = static_cast<LCPrefix_S3 *>(find_first("Prefix"));
+
+ if (!lc_prefix){
+ return false;
+ }
+
+ prefix = lc_prefix->get_data();
+ }
- lc_prefix = static_cast<LCPrefix_S3 *>(find_first("Prefix"));
- if (!lc_prefix)
- return false;
- prefix = lc_prefix->get_data();
lc_status = static_cast<LCStatus_S3 *>(find_first("Status"));
if (!lc_status)
void LCRule_S3::to_xml(CephContext *cct, ostream& out) {
out << "<Rule>" ;
out << "<ID>" << id << "</ID>";
- out << "<Prefix>" << prefix << "</Prefix>";
+ if (!filter.empty()) {
+ LCFilter_S3& lc_filter = static_cast<LCFilter_S3&>(filter);
+ lc_filter.to_xml(out);
+ } else {
+ out << "<Prefix>" << prefix << "</Prefix>";
+ }
out << "<Status>" << status << "</Status>";
if (!expiration.empty() || dm_expiration) {
LCExpiration_S3 expir(expiration.get_days_str(), expiration.get_date(), dm_expiration);
string& to_str() { return data; }
};
+class LCFilter_S3 : public LCFilter, public XMLObj
+{
+ public:
+ ~LCFilter_S3() override {}
+ string& to_str() { return data; }
+ void to_xml(ostream& out){
+ out << "<Filter>";
+ if (!prefix.empty())
+ out << "<Prefix>" << prefix << "<Prefix>";
+ out << "</Filter>";
+ }
+ void dump_xml(Formatter *f) const {
+ f->open_object_section("Filter");
+ if (!prefix.empty())
+ encode_xml("Prefix", prefix, f);
+ f->close_section(); // Filter
+ }
+};
+
class LCStatus_S3 : public XMLObj
{
public:
void dump_xml(Formatter *f) const {
f->open_object_section("Rule");
encode_xml("ID", id, f);
- encode_xml("Prefix", prefix, f);
+ // In case of an empty filter and an empty Prefix, we defer to Prefix.
+ if (!filter.empty()) {
+ const LCFilter_S3& lc_filter = static_cast<const LCFilter_S3&>(filter);
+ lc_filter.dump_xml(f);
+ } else {
+ encode_xml("Prefix", prefix, f);
+ }
encode_xml("Status", status, f);
if (!expiration.empty() || dm_expiration) {
LCExpiration_S3 expir(expiration.get_days_str(), expiration.get_date(), dm_expiration);
const LCMPExpiration_S3& mp_expir = static_cast<const LCMPExpiration_S3&>(mp_expiration);
mp_expir.dump_xml(f);
}
+
f->close_section(); // Rule
}
};