]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/cloud-restore: Add new tier-type & options related to S3 Glacier
authorSoumya Koduri <skoduri@redhat.com>
Sat, 8 Feb 2025 18:34:01 +0000 (00:04 +0530)
committerSoumya Koduri <skoduri@redhat.com>
Sat, 15 Mar 2025 16:13:12 +0000 (21:43 +0530)
Unlike regular S3 cloud services, restoring objects from S3/Tape
or AWS Glacier services would require special handling. We need to
first restore the object using Glacier `RestoreObject` API and then
download it using `GET`.
https://docs.aws.amazon.com/cli/latest/reference/s3api/restore-object.html

A new cloud tier-type `s3-glacier` is added to handle S3 Glacier
endpoints along with below tier-config options -

`glacier_restore_days` - lifetime of the restored copy on the Glacier
 endpoint ; default: 1 day
`glacier_restore_tier_type` - Retrieval tier at which the restore will be processed.
      Only "Standard" (default) and "Expedited" options
      are supported.

In addition, a new option `restore_storage_class` is added to configure
the storage class the objects need to be restored to. Default value:
STANDARD

Design doc: https://docs.google.com/document/d/1rzLJAzHK6cLuzJswgoplgugNOFCKi8NrjPOGfgVrIFg/edit?tab=t.0#heading=h.sgrmb31roboc

Signed-off-by: Soumya Koduri <skoduri@redhat.com>
19 files changed:
src/rgw/driver/daos/rgw_sal_daos.h
src/rgw/driver/motr/rgw_sal_motr.h
src/rgw/driver/rados/.rgw_lc_tier.cc.swm [new file with mode: 0644]
src/rgw/driver/rados/rgw_lc_tier.h
src/rgw/driver/rados/rgw_obj_manifest.cc
src/rgw/driver/rados/rgw_obj_manifest.h
src/rgw/driver/rados/rgw_putobj_processor.cc
src/rgw/driver/rados/rgw_rados.cc
src/rgw/driver/rados/rgw_sal_rados.cc
src/rgw/driver/rados/rgw_sal_rados.h
src/rgw/driver/rados/rgw_zone.cc
src/rgw/radosgw-admin/radosgw-admin.cc
src/rgw/rgw_lc.cc
src/rgw/rgw_op.cc
src/rgw/rgw_sal.h
src/rgw/rgw_sal_dbstore.h
src/rgw/rgw_sal_filter.h
src/rgw/rgw_zone.cc
src/rgw/rgw_zone_types.h

index fac26b548811c7c8d86cb5c7953440f34b082564..19cff1d579802e0c5b8ed39807f5037fc3c54726 100644 (file)
@@ -377,8 +377,11 @@ class DaosPlacementTier : public StorePlacementTier {
   virtual ~DaosPlacementTier() = default;
 
   virtual const std::string& get_tier_type() { return tier.tier_type; }
+  virtual bool is_tier_type_s3() { return (tier.is_tier_type_s3()); }
   virtual const std::string& get_storage_class() { return tier.storage_class; }
   virtual bool retain_head_object() { return tier.retain_head_object; }
+  virtual bool allow_read_through() { return tier.allow_read_through; }
+  virtual uint64_t get_read_through_restore_days() { return tier.read_through_restore_days; }
   RGWZoneGroupPlacementTier& get_rt() { return tier; }
 };
 
index 2071d827e950869cc18cfcddc83db62712c83572..e95ab3252d6df3c5420a36562e735447eb5a1d38 100644 (file)
@@ -419,8 +419,11 @@ public:
   virtual ~MotrPlacementTier() = default;
 
   virtual const std::string& get_tier_type() { return tier.tier_type; }
+  virtual bool is_tier_type_s3() { return (tier.is_tier_type_s3()); }
   virtual const std::string& get_storage_class() { return tier.storage_class; }
   virtual bool retain_head_object() { return tier.retain_head_object; }
+  virtual bool allow_read_through() { return tier.allow_read_through; }
+  virtual uint64_t get_read_through_restore_days() { return tier.read_through_restore_days; }
   RGWZoneGroupPlacementTier& get_rt() { return tier; }
 };
 
diff --git a/src/rgw/driver/rados/.rgw_lc_tier.cc.swm b/src/rgw/driver/rados/.rgw_lc_tier.cc.swm
new file mode 100644 (file)
index 0000000..990e136
Binary files /dev/null and b/src/rgw/driver/rados/.rgw_lc_tier.cc.swm differ
index fd8013eb000937891d2cec105109a46f41c89b95..44ebaddfd9c86f1ae1ca86a6fdd6fcbaef7e313d 100644 (file)
@@ -22,6 +22,8 @@ struct RGWLCCloudTierCtx {
   rgw::sal::Driver *driver;
   RGWBucketInfo& bucket_info;
   std::string storage_class;
+  std::string restore_storage_class;
+  std::string tier_type;
 
   rgw::sal::Object *obj;
 
index 2e9f94350089687bd4cf5c96085a9bf6ca8a6c7a..b75411424590a7243ce52446153565dad72f5277 100644 (file)
@@ -347,7 +347,7 @@ void RGWObjManifest::dump(Formatter *f) const
   ::encode_json("tail_placement", tail_placement, f);
   ::encode_json("tier_type", tier_type, f);
 
-  if (tier_type == "cloud-s3") {
+  if (tier_type == "cloud-s3" || tier_type == "cloud-s3-glacier") {
     ::encode_json("tier_config", tier_config, f);
   }
 
index af0ce9ce0fa37c3ad7c5319c44d1b57a8c5a3d0d..1d66a1e437b519e5113deace67f2ca59da8ed61f 100644 (file)
@@ -471,16 +471,24 @@ public:
       return tier_type;
   }
 
+  bool is_tier_type_s3() {
+      return (tier_type == "cloud-s3" || tier_type == "cloud-s3-glacier");
+  }
+
+  bool is_tier_type_s3_glacier() {
+      return (tier_type == "cloud-s3-glacier");
+  }
+
   inline void set_tier_type(std::string value) {
-      /* Only "cloud-s3" tier-type is supported for now */
-      if (value == "cloud-s3") {
+      /* Only "cloud-s3" & "cloud-s3-glacier" tier-type is supported for now */
+      if (value == "cloud-s3" || value == "cloud-s3-glacier") {
         tier_type = value;
       }
   }
 
   inline void set_tier_config(RGWObjTier t) {
-      /* Set only if tier_type set to "cloud-s3" */
-      if (tier_type != "cloud-s3")
+      /* Set only if tier_type set to "cloud-s3" or "cloud-s3-glacier" */
+      if (!is_tier_type_s3())
         return;
 
       tier_config.name = t.name;
@@ -489,7 +497,7 @@ public:
   }
 
   inline const void get_tier_config(RGWObjTier* t) {
-      if (tier_type != "cloud-s3")
+      if (!is_tier_type_s3())
         return;
 
       t->name = tier_config.name;
index d0f5a45d9bd2e76ac5d07c6af1b33e10e914b94a..95ca28290e74a3e3e912199ba9cea1d4475770ac 100644 (file)
@@ -45,9 +45,9 @@ int read_cloudtier_info_from_attrs(rgw::sal::Attrs& attrs, RGWObjCategory& categ
     auto i = attr_iter->second;
     string m = i.to_str();
 
-    if (m == "cloud-s3") {
+    if (m == "cloud-s3" || m == "cloud-s3-glacier") {
       category = RGWObjCategory::CloudTiered;
-      manifest.set_tier_type("cloud-s3");
+      manifest.set_tier_type(m);
 
       auto config_iter = attrs.find(RGW_ATTR_CLOUD_TIER_CONFIG);
       if (config_iter != attrs.end()) {
index a83abe07460c71fcf2b07bb5e12159238f8067a6..94861390c3d6944d7168f8304462c7b95411dbdd 100644 (file)
@@ -5287,15 +5287,16 @@ int RGWRados::restore_obj_from_cloud(RGWLCCloudTierCtx& tier_ctx,
   }
   boost::optional<RGWPutObj_Compress> compressor;
   CompressorRef plugin;
+  dest_placement.storage_class = tier_ctx.restore_storage_class;
   RGWRadosPutObj cb(dpp, cct, plugin, compressor, &processor, progress_cb, progress_data,
                     [&](map<string, bufferlist> obj_attrs) {
-                      // XXX: do we need filter() like in fetch_remote_obj() cb
+                      // XXX: do we need filter() lke in fetch_remote_obj() cb
                       dest_placement.inherit_from(dest_bucket_info.placement_rule);
                       /* For now we always restore to STANDARD storage-class.
                        * Later we will add support to take restore-target-storage-class
                        * for permanent restore
                        */
-                      dest_placement.storage_class = RGW_STORAGE_CLASS_STANDARD;
+  //                    dest_placement.storage_class = RGW_STORAGE_CLASS_STANDARD;
 
                       processor.set_tail_placement(dest_placement);
 
@@ -5420,7 +5421,7 @@ int RGWRados::restore_obj_from_cloud(RGWLCCloudTierCtx& tier_ctx,
     // set tier-config only for temp restored objects, as
     // permanent copies will be treated as regular objects
     {
-      t.append("cloud-s3");
+      t.append(tier_ctx.tier_type);
       encode(tier_config, t_tier);
       attrs[RGW_ATTR_CLOUD_TIER_TYPE] = t;
       attrs[RGW_ATTR_CLOUD_TIER_CONFIG] = t_tier;
index f27e4c895a0a175bbc5cc0289396bfdd8ff912ed..0578d2e59581f31cb1ae212a6735270a8acd28a4 100644 (file)
@@ -2916,6 +2916,8 @@ int RadosObject::restore_obj_from_cloud(Bucket* bucket,
   const rgw::sal::ZoneGroup& zonegroup = store->get_zone()->get_zonegroup();
   int ret = 0;
   string src_storage_class = o.meta.storage_class; // or take src_placement also as input
+  // update tier_config in case tier params are updated
+  tier_config.tier_placement = rtier->get_rt();
 
   if (bucket_name.empty()) {
     bucket_name = "rgwx-" + zonegroup.get_name() + "-" + tier->get_storage_class() +
@@ -2933,6 +2935,8 @@ int RadosObject::restore_obj_from_cloud(Bucket* bucket,
   tier_ctx.multipart_min_part_size = rtier->get_rt().t.s3.multipart_min_part_size;
   tier_ctx.multipart_sync_threshold = rtier->get_rt().t.s3.multipart_sync_threshold;
   tier_ctx.storage_class = tier->get_storage_class();
+  tier_ctx.restore_storage_class = rtier->get_rt().restore_storage_class;
+  tier_ctx.tier_type = rtier->get_rt().tier_type;
 
   ldpp_dout(dpp, 20) << "Restoring object(" << o.key << ") from the cloud endpoint(" << endpoint << ")" << dendl;
 
@@ -3242,7 +3246,7 @@ int RadosObject::write_cloud_tier(const DoutPrefixProvider* dpp,
   tier_config.tier_placement = rtier->get_rt();
   tier_config.is_multipart_upload = is_multipart_upload;
 
-  pmanifest->set_tier_type("cloud-s3");
+  pmanifest->set_tier_type(rtier->get_rt().tier_type);
   pmanifest->set_tier_config(tier_config);
 
   /* check if its necessary */
index 5072e1e5753ae614afdf3d4c15b952d112aff398..cbb51872decd012c239e0f20beeb046934cb2a78 100644 (file)
@@ -43,8 +43,11 @@ public:
   virtual ~RadosPlacementTier() = default;
 
   virtual const std::string& get_tier_type() { return tier.tier_type; }
+  virtual bool is_tier_type_s3() { return (tier.is_tier_type_s3()); }
   virtual const std::string& get_storage_class() { return tier.storage_class; }
   virtual bool retain_head_object() { return tier.retain_head_object; }
+  virtual bool allow_read_through() { return tier.allow_read_through; }
+  virtual uint64_t get_read_through_restore_days() { return tier.read_through_restore_days; }
   RGWZoneGroupPlacementTier& get_rt() { return tier; }
 };
 
index 7d5fe3bcb21b96dbc9591f313bebd8e7a9e167cd..c71656838df2c601e4bb13706123e3683963f6b4 100644 (file)
@@ -1370,10 +1370,17 @@ int RGWZoneGroupPlacementTier::update_params(const JSONFormattable& config)
     }
   }
 
-  if (tier_type == "cloud-s3") {
+  if (is_tier_type_s3()) {
     r = t.s3.update_params(config);
   }
 
+  if (config.exists("restore_storage_class")) {
+    restore_storage_class = config["restore_storage_class"];
+  }
+
+  if (is_tier_type_s3_glacier()) {
+    r = s3_glacier.update_params(config);
+  }
   return r;
 }
 
@@ -1389,10 +1396,18 @@ int RGWZoneGroupPlacementTier::clear_params(const JSONFormattable& config)
     read_through_restore_days = DEFAULT_READ_THROUGH_RESTORE_DAYS;
   }
 
-  if (tier_type == "cloud-s3") {
+  if (is_tier_type_s3()) {
     t.s3.clear_params(config);
   }
 
+  if (config.exists("restore_storage_class")) {
+    restore_storage_class = RGW_STORAGE_CLASS_STANDARD;
+  }
+
+  if (is_tier_type_s3_glacier()) {
+    s3_glacier.clear_params(config);
+  }
+
   return 0;
 }
 
@@ -1509,6 +1524,40 @@ int RGWZoneGroupPlacementTierS3::clear_params(const JSONFormattable& config)
   return 0;
 }
 
+int RGWZoneGroupTierS3Glacier::update_params(const JSONFormattable& config)
+{
+  int r = -1;
+
+  if (config.exists("glacier_restore_days")) {
+    r = conf_to_uint64(config, "glacier_restore_days", &glacier_restore_days);
+    if (r < 0) {
+      glacier_restore_days = DEFAULT_GLACIER_RESTORE_DAYS;
+    }
+  }
+  if (config.exists("glacier_restore_tier_type")) {
+    string s;
+    s = config["glacier_restore_tier_type"];
+    if (s != "Expedited") {
+      glacier_restore_tier_type = Standard;
+    } else {
+      glacier_restore_tier_type = Expedited;
+    }
+  }
+  return 0;
+}
+
+int RGWZoneGroupTierS3Glacier::clear_params(const JSONFormattable& config)
+{
+  if (config.exists("glacier_restore_days")) {
+    glacier_restore_days = DEFAULT_GLACIER_RESTORE_DAYS;
+  }
+  if (config.exists("glacier_restore_tier_type")) {
+    /* default */
+    glacier_restore_tier_type = Standard;
+  }
+  return 0;
+}
+
 void rgw_meta_sync_info::generate_test_instances(list<rgw_meta_sync_info*>& o)
 {
   auto info = new rgw_meta_sync_info;
index 5f541fb1ff55a6f3b034302ec2493a803a4d8759..bd6087020363b7e096ede3a50eca80a3feffe7c2 100644 (file)
@@ -5929,7 +5929,7 @@ int main(int argc, const char **argv)
       /* Tier options */
       bool tier_class = false;
       std::string storage_class = rule.get_storage_class();
-      RGWZoneGroupPlacementTier t{storage_class};
+      RGWZoneGroupPlacementTier t;
       RGWZoneGroupPlacementTier *pt = &t;
 
          auto ptiter = target.tier_targets.find(storage_class);
@@ -5937,8 +5937,8 @@ int main(int argc, const char **argv)
         pt = &ptiter->second;
         tier_class = true;
       } else if (tier_type_specified) {
-        if (tier_type == "cloud-s3") {
-          /* we support only cloud-s3 tier-type for now.
+        if (tier_type == "cloud-s3" || tier_type == "cloud-s3-glacier") {
+          /* we support only cloud-s3 & cloud-s3-glacier tier-type for now.
            * Once set cant be reset. */
           tier_class = true;
           pt->tier_type = tier_type;
index 1a175f4e279c5c16a8521479a9b4d9c96b214893..de9dd1bfa2bc23614f55e7c9a8e1a8c531bbe73b 100644 (file)
@@ -1517,7 +1517,7 @@ public:
 
     r = zonegroup.get_placement_tier(target_placement, &oc.tier);
 
-    if (!r && oc.tier->get_tier_type() == "cloud-s3") {
+    if (!r && oc.tier->is_tier_type_s3()) {
       ldpp_dout(oc.dpp, 30) << "Found cloud s3 tier: " << target_placement.storage_class << dendl;
       if (!oc.o.is_current() &&
           !pass_object_lock_check(oc.driver, oc.obj.get(), oc.dpp)) {
index a96eb3ef6024fdcfd976a73f939a6daae619b509..3ca67ac10f10673bda2c393b9e389d03407ed6a5 100644 (file)
@@ -969,7 +969,7 @@ int handle_cloudtier_obj(req_state* s, const DoutPrefixProvider *dpp, rgw::sal::
   RGWObjManifest m;
   try { 
     decode(m, attr_iter->second);
-    if (m.get_tier_type() != "cloud-s3") {
+    if (!m.is_tier_type_s3()) {
       ldpp_dout(dpp, 20) << "not a cloud tier object " <<  s->object->get_key().name << dendl;
       if (restore_op) {
         op_ret = -ERR_INVALID_OBJECT_STATE;
@@ -983,7 +983,7 @@ int handle_cloudtier_obj(req_state* s, const DoutPrefixProvider *dpp, rgw::sal::
     m.get_tier_config(&tier_config);
     if (sync_cloudtiered) {
       bufferlist t, t_tier;
-      t.append("cloud-s3");
+      t.append(m.get_tier_type());
       attrs[RGW_ATTR_CLOUD_TIER_TYPE] = t;
       encode(tier_config, t_tier);
       attrs[RGW_ATTR_CLOUD_TIER_CONFIG] = t_tier;
@@ -1015,11 +1015,9 @@ int handle_cloudtier_obj(req_state* s, const DoutPrefixProvider *dpp, rgw::sal::
         s->err.message = "failed to restore object";
         return op_ret;
       }
-      rgw::sal::RadosPlacementTier* rtier = static_cast<rgw::sal::RadosPlacementTier*>(tier.get());
-      tier_config.tier_placement = rtier->get_rt();
       if (!restore_op) {
-        if (tier_config.tier_placement.allow_read_through) {
-          days = tier_config.tier_placement.read_through_restore_days;
+        if (tier->allow_read_through()) {
+          days = tier->get_read_through_restore_days();
         } else { //read-through is not enabled
           op_ret = -ERR_INVALID_OBJECT_STATE;
           s->err.message = "Read through is not enabled for this config";
@@ -4413,7 +4411,7 @@ void RGWPutObj::execute(optional_yield y)
       RGWObjManifest m;
       try{
         decode(m, bl);
-        if (m.get_tier_type() == "cloud-s3") {
+        if (m.is_tier_type_s3()) {
           op_ret = -ERR_INVALID_OBJECT_STATE;
           s->err.message = "This object was transitioned to cloud-s3";
           ldpp_dout(this, 4) << "Cannot copy cloud tiered object. Failing with "
@@ -5869,7 +5867,7 @@ void RGWCopyObj::execute(optional_yield y)
       RGWObjManifest m;
       try{
         decode(m, bl);
-        if (m.get_tier_type() == "cloud-s3") {
+        if (m.is_tier_type_s3()) {
           op_ret = -ERR_INVALID_OBJECT_STATE;
           s->err.message = "This object was transitioned to cloud-s3";
           ldpp_dout(this, 4) << "Cannot copy cloud tiered object. Failing with "
index 8af0d863342566fa939865e9d8d6d68b2486fe1e..68442e741d983ab78bae02ab7fe58b00033dbd24 100644 (file)
@@ -1719,10 +1719,16 @@ public:
 
   /** Get the type of this tier */
   virtual const std::string& get_tier_type() = 0;
+  /** Is the type of this tier cloud-s3/clous-s3-glacier */
+  virtual bool is_tier_type_s3() = 0;
   /** Get the storage class of this tier */
   virtual const std::string& get_storage_class() = 0;
   /** Should we retain the head object when transitioning */
   virtual bool retain_head_object() = 0;
+  /** Is read_through allowed */
+  virtual bool allow_read_through() = 0;
+  /** Get read_through restore_days */
+  virtual uint64_t get_read_through_restore_days() = 0;
   /** Get the placement rule associated with this tier */
 };
 
index 66fa83b813d6fab3ac48837ff5971dc425c887e8..3fabc6d5ec18684cce6e96e0561d3437e89f0858 100644 (file)
@@ -207,7 +207,10 @@ protected:
     virtual ~DBPlacementTier() = default;
 
     virtual const std::string& get_tier_type() { return tier.tier_type; }
+    virtual bool is_tier_type_s3() { return (tier.is_tier_type_s3()); }
     virtual const std::string& get_storage_class() { return tier.storage_class; }
+    virtual bool allow_read_through() { return tier.allow_read_through; }
+    virtual uint64_t get_read_through_restore_days() { return tier.read_through_restore_days; }
     virtual bool retain_head_object() { return tier.retain_head_object; }
     RGWZoneGroupPlacementTier& get_rt() { return tier; }
   };
index 38f7b3413c848c98012596601aff493cf7fc85b3..b3e3a031d74751a7106a4c72ed5ec94ed9689c59 100644 (file)
@@ -29,8 +29,11 @@ public:
   virtual ~FilterPlacementTier() = default;
 
   virtual const std::string& get_tier_type() override { return next->get_tier_type(); }
+  virtual bool is_tier_type_s3() { return next->is_tier_type_s3(); }
   virtual const std::string& get_storage_class() override { return next->get_storage_class(); }
   virtual bool retain_head_object() override { return next->retain_head_object(); }
+  virtual bool allow_read_through() { return next->allow_read_through(); }
+  virtual uint64_t get_read_through_restore_days() { return next->get_read_through_restore_days(); }
 
   /* Internal to Filters */
   PlacementTier* get_next() { return next.get(); }
index 1763ef225369334f2372c609fe3c629651d24c68..44338c0fea8130179785ed4361108b837c87647d 100644 (file)
@@ -862,11 +862,14 @@ void RGWZoneGroupPlacementTier::decode_json(JSONObj *obj)
   JSONDecoder::decode_json("tier_type", tier_type, obj);
   JSONDecoder::decode_json("storage_class", storage_class, obj);
   JSONDecoder::decode_json("retain_head_object", retain_head_object, obj);
+  if (is_tier_type_s3()) {
+    JSONDecoder::decode_json("s3", t.s3, obj);
+  }
   JSONDecoder::decode_json("allow_read_through", allow_read_through, obj);
   JSONDecoder::decode_json("read_through_restore_days", read_through_restore_days, obj);
-
-  if (tier_type == "cloud-s3") {
-    JSONDecoder::decode_json("s3", t.s3, obj);
+  JSONDecoder::decode_json("restore_storage_class", restore_storage_class, obj);
+  if (is_tier_type_s3_glacier()) {
+    JSONDecoder::decode_json("s3-glacier", s3_glacier, obj);
   }
 }
 
@@ -896,16 +899,39 @@ void RGWZoneStorageClasses::decode_json(JSONObj *obj)
   standard_class = &m[RGW_STORAGE_CLASS_STANDARD];
 }
 
+void RGWZoneGroupTierS3Glacier::dump(Formatter *f) const
+{
+  encode_json("glacier_restore_days", glacier_restore_days, f);
+  string s = (glacier_restore_tier_type == Standard ? "Standard" : "Expedited");
+  encode_json("glacier_restore_tier_type", s, f);
+}
+
+void RGWZoneGroupTierS3Glacier::decode_json(JSONObj *obj)
+{
+  JSONDecoder::decode_json("glacier_restore_days", glacier_restore_days, obj);
+  string s;
+  JSONDecoder::decode_json("glacier_restore_tier_type", s, obj);
+  if (s != "Expedited") {
+    glacier_restore_tier_type = Standard;
+  } else {
+    glacier_restore_tier_type = Expedited;
+  }
+}
+
 void RGWZoneGroupPlacementTier::dump(Formatter *f) const
 {
   encode_json("tier_type", tier_type, f);
   encode_json("storage_class", storage_class, f);
   encode_json("retain_head_object", retain_head_object, f);
+  if (is_tier_type_s3()) {
+    encode_json("s3", t.s3, f);
+  }
   encode_json("allow_read_through", allow_read_through, f);
   encode_json("read_through_restore_days", read_through_restore_days, f);
+  encode_json("restore_storage_class", restore_storage_class, f);
 
-  if (tier_type == "cloud-s3") {
-    encode_json("s3", t.s3, f);
+  if (is_tier_type_s3_glacier()) {
+    encode_json("s3-glacier", s3_glacier, f);
   }
 }
 
index 41c015692a90c532646ed569e7ac0caca7e3960f..197e23812d3c2fe75c30fc730f5afb7e4d685d61 100644 (file)
@@ -542,6 +542,42 @@ struct RGWZoneGroupPlacementTierS3 {
 };
 WRITE_CLASS_ENCODER(RGWZoneGroupPlacementTierS3)
 
+enum GlacierRestoreTierType : uint8_t {
+  Standard = 0,
+  Expedited = 1,
+};
+
+struct RGWZoneGroupTierS3Glacier {
+#define DEFAULT_GLACIER_RESTORE_DAYS 1
+  uint64_t glacier_restore_days = DEFAULT_GLACIER_RESTORE_DAYS;
+  GlacierRestoreTierType glacier_restore_tier_type{Standard};
+
+  int update_params(const JSONFormattable& config);
+  int clear_params(const JSONFormattable& config);
+
+  void encode(bufferlist& bl) const {
+    ENCODE_START(1, 1, bl);
+    encode(glacier_restore_days, bl);
+    encode(glacier_restore_tier_type, bl);
+    ENCODE_FINISH(bl);
+  }
+
+  void decode(bufferlist::const_iterator& bl) {
+    DECODE_START(1, bl);
+    decode(glacier_restore_days, bl);
+    decode(glacier_restore_tier_type, bl);
+    DECODE_FINISH(bl);
+  }
+  void dump(Formatter *f) const;
+  void decode_json(JSONObj *obj);
+  static void generate_test_instances(std::list<RGWZoneGroupTierS3Glacier*>& o) {
+    o.push_back(new RGWZoneGroupTierS3Glacier);
+    o.back()->glacier_restore_days = 2;
+    o.back()->glacier_restore_tier_type = GlacierRestoreTierType::Expedited;
+  }
+};
+WRITE_CLASS_ENCODER(RGWZoneGroupTierS3Glacier)
+
 struct RGWZoneGroupPlacementTier {
 #define DEFAULT_READ_THROUGH_RESTORE_DAYS 1
 
@@ -555,20 +591,27 @@ struct RGWZoneGroupPlacementTier {
 
   bool allow_read_through = false;
   uint64_t read_through_restore_days = 1;
+  std::string restore_storage_class = RGW_STORAGE_CLASS_STANDARD;
+
+  RGWZoneGroupTierS3Glacier s3_glacier;
 
   int update_params(const JSONFormattable& config);
   int clear_params(const JSONFormattable& config);
 
   void encode(bufferlist& bl) const {
-    ENCODE_START(3, 1, bl);
+    ENCODE_START(4, 1, bl);
     encode(tier_type, bl);
     encode(storage_class, bl);
     encode(retain_head_object, bl);
-    if (tier_type == "cloud-s3") {
+    if (is_tier_type_s3()) {
       encode(t.s3, bl);
     }
     encode(allow_read_through, bl);
     encode(read_through_restore_days, bl);
+    encode(restore_storage_class, bl);
+    if (is_tier_type_s3_glacier()) {
+      encode(s3_glacier, bl);
+    }
     ENCODE_FINISH(bl);
   }
 
@@ -588,15 +631,29 @@ struct RGWZoneGroupPlacementTier {
         decode(t.s3, bl);
       }
     } else if (struct_v >= 3) {
-      if (tier_type == "cloud-s3") {
+      if (is_tier_type_s3()) {
         decode(t.s3, bl);
       }
       decode(allow_read_through, bl);
       decode(read_through_restore_days, bl);
     }
+    if (struct_v >= 4) {
+      decode(restore_storage_class, bl);
+      if (is_tier_type_s3_glacier()) {
+        decode(s3_glacier, bl);
+      }
+    }
     DECODE_FINISH(bl);
   }
 
+  bool is_tier_type_s3() const {
+    return (tier_type == "cloud-s3" || tier_type == "cloud-s3-glacier");
+  }
+
+  bool is_tier_type_s3_glacier() const {
+    return (tier_type == "cloud-s3-glacier");
+  }
+
   void dump(Formatter *f) const;
   void decode_json(JSONObj *obj);
   static void generate_test_instances(std::list<RGWZoneGroupPlacementTier*>& o) {
@@ -604,6 +661,10 @@ struct RGWZoneGroupPlacementTier {
     o.push_back(new RGWZoneGroupPlacementTier);
     o.back()->tier_type = "cloud-s3";
     o.back()->storage_class = "STANDARD";
+    o.back()->allow_read_through = false;
+    o.back()->restore_storage_class = "STANDARD";
+    o.back()->s3_glacier.glacier_restore_days = 2;
+    o.back()->s3_glacier.glacier_restore_tier_type = GlacierRestoreTierType::Expedited;
   }
 };
 WRITE_CLASS_ENCODER(RGWZoneGroupPlacementTier)