@@ -252,6 +252,70 @@ static const struct generic_attr generic_attrs[] = {
252252 { " ETAG" , RGW_ATTR_ETAG },
253253};
254254
255+ /* Restore object from remote endpoint.
256+ */
257+ int rgw_cloud_tier_restore_object (RGWLCCloudTierCtx& tier_ctx,
258+ std::map<std::string, std::string>& headers,
259+ real_time* pset_mtime, std::string& etag,
260+ uint64_t & accounted_size, rgw::sal::Attrs& attrs,
261+ std::optional<uint64_t > days,
262+ RGWZoneGroupTierS3Glacier& glacier_params,
263+ void * cb) {
264+ RGWRESTConn::get_obj_params req_params;
265+ std::string target_obj_name;
266+ int ret = 0 ;
267+ rgw_lc_obj_properties obj_properties (tier_ctx.o .meta .mtime , tier_ctx.o .meta .etag ,
268+ tier_ctx.o .versioned_epoch , tier_ctx.acl_mappings ,
269+ tier_ctx.target_storage_class );
270+
271+ rgw_bucket dest_bucket;
272+ dest_bucket.name = tier_ctx.target_bucket_name ;
273+ target_obj_name = tier_ctx.bucket_info .bucket .name + " /" +
274+ tier_ctx.obj ->get_name ();
275+ if (!tier_ctx.o .is_current ()) {
276+ target_obj_name += get_key_instance (tier_ctx.obj ->get_key ());
277+ }
278+
279+ if (glacier_params.glacier_restore_tier_type != GlacierRestoreTierType::Expedited) {
280+ // XXX: Supporting STANDARD tier type is still in WIP
281+ ldpp_dout (tier_ctx.dpp , -1 ) << __func__ << " ERROR: Only Expedited tier_type is supported " << dendl;
282+ return -1 ;
283+ }
284+
285+ rgw_obj dest_obj (dest_bucket, rgw_obj_key (target_obj_name));
286+
287+ ret = cloud_tier_restore (tier_ctx.dpp , tier_ctx.conn , dest_obj, days, glacier_params);
288+
289+ ldpp_dout (tier_ctx.dpp , 20 ) << __func__ << " Restoring object=" << dest_obj << " returned ret = " << ret << dendl;
290+
291+ if (ret < 0 ) {
292+ ldpp_dout (tier_ctx.dpp , -1 ) << __func__ << " ERROR: failed to restore object=" << dest_obj << " ; ret = " << ret << dendl;
293+ return ret;
294+ }
295+
296+ // now send HEAD request and verify if restore is complete on glacier/tape endpoint
297+ bool restore_in_progress = false ;
298+ do {
299+ ret = rgw_cloud_tier_get_object (tier_ctx, true , headers, nullptr , etag,
300+ accounted_size, attrs, nullptr );
301+
302+ if (ret < 0 ) {
303+ ldpp_dout (tier_ctx.dpp , 0 ) << __func__ << " ERROR: failed to fetch HEAD from cloud for obj=" << tier_ctx.obj << " , ret = " << ret << dendl;
304+ return ret;
305+ }
306+
307+ restore_in_progress = is_restore_in_progress (tier_ctx.dpp , headers);
308+ } while (restore_in_progress);
309+
310+ // now do the actual GET
311+ ret = rgw_cloud_tier_get_object (tier_ctx, false , headers, pset_mtime, etag,
312+ accounted_size, attrs, cb);
313+
314+ ldpp_dout (tier_ctx.dpp , 20 ) << __func__ << " (): fetching object from cloud bucket:" << dest_bucket << " , object: " << target_obj_name << " returned ret:" << ret << dendl;
315+
316+ return ret;
317+ }
318+
255319/* Read object or just head from remote endpoint.
256320 */
257321int rgw_cloud_tier_get_object (RGWLCCloudTierCtx& tier_ctx, bool head,
@@ -367,6 +431,30 @@ static bool is_already_tiered(const DoutPrefixProvider *dpp,
367431 return 0 ;
368432}
369433
434+ bool is_restore_in_progress (const DoutPrefixProvider *dpp,
435+ std::map<std::string, std::string>& headers) {
436+ map<string, string> attrs = headers;
437+
438+ for (const auto & a : attrs) {
439+ ldpp_dout (dpp, 20 ) << " GetCrf attr[" << a.first << " ] = " << a.second <<dendl;
440+ }
441+ string s = attrs[" X_AMZ_RESTORE" ];
442+
443+ if (s.empty ())
444+ s = attrs[" x_amz_restore" ];
445+
446+ ldpp_dout (dpp, 0 ) << " is_already_tiered attrs[X_AMZ_RESTORE] = " << s <<dendl;
447+
448+ if (!s.empty ()){
449+ const char *r_str = " ongoing-request=\" true\" " ;
450+ const char *found = std::strstr (s.c_str (), r_str);
451+ if (found) {
452+ return true ;
453+ }
454+ }
455+ return false ;;
456+ }
457+
370458/* Read object locally & also initialize dest rest obj based on read attrs */
371459class RGWLCStreamRead
372460{
@@ -914,6 +1002,95 @@ static int cloud_tier_send_multipart_part(RGWLCCloudTierCtx& tier_ctx,
9141002 return 0 ;
9151003}
9161004
1005+ int cloud_tier_restore (const DoutPrefixProvider *dpp, RGWRESTConn& dest_conn,
1006+ const rgw_obj& dest_obj, std::optional<uint64_t > days,
1007+ RGWZoneGroupTierS3Glacier& glacier_params) {
1008+ rgw_http_param_pair params[] = {{" restore" , nullptr }, {nullptr , nullptr }};
1009+ // XXX: include versionId=VersionId in the params above
1010+
1011+ stringstream ss;
1012+ XMLFormatter formatter;
1013+ int ret;
1014+
1015+ bufferlist bl, out_bl;
1016+ string resource = obj_to_aws_path (dest_obj);
1017+
1018+ const std::string tier_v = (glacier_params.glacier_restore_tier_type == GlacierRestoreTierType::Expedited) ? " Expedited" : " Standard" ;
1019+
1020+ struct RestoreRequest {
1021+ std::optional<uint64_t > days;
1022+ std::optional<std::string> tier;
1023+
1024+ explicit RestoreRequest (std::optional<uint64_t > _days, std::optional<std::string> _tier) : days(_days), tier(_tier) {}
1025+
1026+ void dump_xml (Formatter *f) const {
1027+ encode_xml (" Days" , days, f);
1028+ if (tier) {
1029+ f->open_object_section (" GlacierJobParameters" );
1030+ encode_xml (" Tier" , tier, f);
1031+ f->close_section ();
1032+ };
1033+ }
1034+ } req_enc (days, tier_v);
1035+
1036+ struct RestoreResult {
1037+ std::string code;
1038+
1039+ void decode_xml (XMLObj *obj) {
1040+ RGWXMLDecoder::decode_xml (" Code" , code, obj);
1041+ }
1042+ } result;
1043+
1044+ req_enc.days = glacier_params.glacier_restore_days ;
1045+ req_enc.tier = tier_v;
1046+ encode_xml (" RestoreRequest" , req_enc, &formatter);
1047+
1048+ formatter.flush (ss);
1049+ bl.append (ss.str ());
1050+
1051+ ret = dest_conn.send_resource (dpp, " POST" , resource, params, nullptr ,
1052+ out_bl, &bl, nullptr , null_yield);
1053+
1054+ if (ret < 0 ) {
1055+ ldpp_dout (dpp, 0 ) << __func__ << " ERROR: failed to send Restore request to cloud for obj=" << dest_obj << " , ret = " << ret << dendl;
1056+ } else {
1057+ ldpp_dout (dpp, 0 ) << __func__ << " Sent Restore request to cloud for obj=" << dest_obj << " , ret = " << ret << dendl;
1058+ }
1059+
1060+ if (out_bl.length () > 0 ) {
1061+ RGWXMLDecoder::XMLParser parser;
1062+ if (!parser.init ()) {
1063+ ldpp_dout (dpp, 0 ) << " ERROR: failed to initialize xml parser for parsing restore request response from server" << dendl;
1064+ return -EIO;
1065+ }
1066+
1067+ if (!parser.parse (out_bl.c_str (), out_bl.length (), 1 )) {
1068+ string str (out_bl.c_str (), out_bl.length ());
1069+ ldpp_dout (dpp, 5 ) << " ERROR: failed to parse xml restore: " << str << dendl;
1070+ return -EIO;
1071+ }
1072+
1073+ try {
1074+ RGWXMLDecoder::decode_xml (" Error" , result, &parser, true );
1075+ } catch (RGWXMLDecoder::err& err) {
1076+ string str (out_bl.c_str (), out_bl.length ());
1077+ ldpp_dout (dpp, 5 ) << " ERROR: unexpected xml: " << str << dendl;
1078+ return -EIO;
1079+ }
1080+
1081+ ldpp_dout (dpp, 0 ) << " ERROR: Restore request received result : " << result.code << dendl;
1082+ if (result.code != " RestoreAlreadyInProgress" ) {
1083+ return -EIO;
1084+ } else { // treat as success
1085+ return 0 ;
1086+ }
1087+
1088+ ldpp_dout (dpp, 0 ) << " ERROR: restore req failed with error: " << result.code << dendl;
1089+ }
1090+
1091+ return ret;
1092+ }
1093+
9171094static int cloud_tier_abort_multipart (const DoutPrefixProvider *dpp,
9181095 RGWRESTConn& dest_conn, const rgw_obj& dest_obj,
9191096 const std::string& upload_id) {
0 commit comments