Skip to content

Commit 6332769

Browse files
authored
Merge pull request ceph#61745 from soumyakoduri/wip-skoduri-glacier-cli
rgw/cloud-restore [PART1] : Add Restore support from Glacier/Tape cloud endpoints
2 parents 003d52b + 300f643 commit 6332769

22 files changed

+431
-48
lines changed

src/rgw/driver/daos/rgw_sal_daos.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,11 @@ class DaosPlacementTier : public StorePlacementTier {
377377
virtual ~DaosPlacementTier() = default;
378378

379379
virtual const std::string& get_tier_type() { return tier.tier_type; }
380+
virtual bool is_tier_type_s3() { return (tier.is_tier_type_s3()); }
380381
virtual const std::string& get_storage_class() { return tier.storage_class; }
381382
virtual bool retain_head_object() { return tier.retain_head_object; }
383+
virtual bool allow_read_through() { return tier.allow_read_through; }
384+
virtual uint64_t get_read_through_restore_days() { return tier.read_through_restore_days; }
382385
RGWZoneGroupPlacementTier& get_rt() { return tier; }
383386
};
384387

src/rgw/driver/motr/rgw_sal_motr.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,8 +419,11 @@ class MotrPlacementTier: public StorePlacementTier {
419419
virtual ~MotrPlacementTier() = default;
420420

421421
virtual const std::string& get_tier_type() { return tier.tier_type; }
422+
virtual bool is_tier_type_s3() { return (tier.is_tier_type_s3()); }
422423
virtual const std::string& get_storage_class() { return tier.storage_class; }
423424
virtual bool retain_head_object() { return tier.retain_head_object; }
425+
virtual bool allow_read_through() { return tier.allow_read_through; }
426+
virtual uint64_t get_read_through_restore_days() { return tier.read_through_restore_days; }
424427
RGWZoneGroupPlacementTier& get_rt() { return tier; }
425428
};
426429

src/rgw/driver/rados/rgw_lc_tier.cc

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/
257321
int 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 */
371459
class 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+
9171094
static int cloud_tier_abort_multipart(const DoutPrefixProvider *dpp,
9181095
RGWRESTConn& dest_conn, const rgw_obj& dest_obj,
9191096
const std::string& upload_id) {

src/rgw/driver/rados/rgw_lc_tier.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct RGWLCCloudTierCtx {
2222
rgw::sal::Driver *driver;
2323
RGWBucketInfo& bucket_info;
2424
std::string storage_class;
25+
std::string restore_storage_class;
26+
std::string tier_type;
2527

2628
rgw::sal::Object *obj;
2729

@@ -55,3 +57,18 @@ int rgw_cloud_tier_get_object(RGWLCCloudTierCtx& tier_ctx, bool head,
5557
real_time* pset_mtime, std::string& etag,
5658
uint64_t& accounted_size, rgw::sal::Attrs& attrs,
5759
void* cb);
60+
int rgw_cloud_tier_restore_object(RGWLCCloudTierCtx& tier_ctx,
61+
std::map<std::string, std::string>& headers,
62+
real_time* pset_mtime, std::string& etag,
63+
uint64_t& accounted_size, rgw::sal::Attrs& attrs,
64+
std::optional<uint64_t> days,
65+
RGWZoneGroupTierS3Glacier& glacier_params,
66+
void* cb);
67+
68+
int cloud_tier_restore(const DoutPrefixProvider *dpp,
69+
RGWRESTConn& dest_conn, const rgw_obj& dest_obj,
70+
std::optional<uint64_t> days,
71+
RGWZoneGroupTierS3Glacier& glacier_params);
72+
73+
bool is_restore_in_progress(const DoutPrefixProvider *dpp,
74+
std::map<std::string, std::string>& headers);

src/rgw/driver/rados/rgw_obj_manifest.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ void RGWObjManifest::dump(Formatter *f) const
347347
::encode_json("tail_placement", tail_placement, f);
348348
::encode_json("tier_type", tier_type, f);
349349

350-
if (tier_type == "cloud-s3") {
350+
if (RGWTierType::is_tier_type_supported(tier_type)) {
351351
::encode_json("tier_config", tier_config, f);
352352
}
353353

src/rgw/driver/rados/rgw_obj_manifest.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -471,16 +471,27 @@ class RGWObjManifest {
471471
return tier_type;
472472
}
473473

474+
bool is_tier_type_s3() {
475+
return (tier_type == RGWTierType::CLOUD_S3 ||
476+
tier_type == RGWTierType::CLOUD_S3_GLACIER);
477+
}
478+
479+
bool is_tier_type_s3_glacier() {
480+
return (tier_type == RGWTierType::CLOUD_S3_GLACIER);
481+
}
482+
474483
inline void set_tier_type(std::string value) {
475-
/* Only "cloud-s3" tier-type is supported for now */
476-
if (value == "cloud-s3") {
484+
/* Only RGWTierType::CLOUD_S3 & RGWTierType::CLOUD_S3_GLACIER
485+
* tier-type are supported for now */
486+
if (RGWTierType::is_tier_type_supported(value)) {
477487
tier_type = value;
478488
}
479489
}
480490

481491
inline void set_tier_config(RGWObjTier t) {
482-
/* Set only if tier_type set to "cloud-s3" */
483-
if (tier_type != "cloud-s3")
492+
/* Set only if tier_type set to RGWTierType::CLOUD_S3 or
493+
* RGWTierType::CLOUD_S3_GLACIER */
494+
if (!is_tier_type_s3())
484495
return;
485496

486497
tier_config.name = t.name;
@@ -489,7 +500,7 @@ class RGWObjManifest {
489500
}
490501

491502
inline const void get_tier_config(RGWObjTier* t) {
492-
if (tier_type != "cloud-s3")
503+
if (!is_tier_type_s3())
493504
return;
494505

495506
t->name = tier_config.name;

src/rgw/driver/rados/rgw_putobj_processor.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ int read_cloudtier_info_from_attrs(rgw::sal::Attrs& attrs, RGWObjCategory& categ
4545
auto i = attr_iter->second;
4646
string m = i.to_str();
4747

48-
if (m == "cloud-s3") {
48+
if (RGWTierType::is_tier_type_supported(m)) {
4949
category = RGWObjCategory::CloudTiered;
50-
manifest.set_tier_type("cloud-s3");
50+
manifest.set_tier_type(m);
5151

5252
auto config_iter = attrs.find(RGW_ATTR_CLOUD_TIER_CONFIG);
5353
if (config_iter != attrs.end()) {

src/rgw/driver/rados/rgw_rados.cc

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5287,16 +5287,11 @@ int RGWRados::restore_obj_from_cloud(RGWLCCloudTierCtx& tier_ctx,
52875287
}
52885288
boost::optional<RGWPutObj_Compress> compressor;
52895289
CompressorRef plugin;
5290+
dest_placement.storage_class = tier_ctx.restore_storage_class;
52905291
RGWRadosPutObj cb(dpp, cct, plugin, compressor, &processor, progress_cb, progress_data,
52915292
[&](map<string, bufferlist> obj_attrs) {
52925293
// XXX: do we need filter() like in fetch_remote_obj() cb
52935294
dest_placement.inherit_from(dest_bucket_info.placement_rule);
5294-
/* For now we always restore to STANDARD storage-class.
5295-
* Later we will add support to take restore-target-storage-class
5296-
* for permanent restore
5297-
*/
5298-
dest_placement.storage_class = RGW_STORAGE_CLASS_STANDARD;
5299-
53005295
processor.set_tail_placement(dest_placement);
53015296

53025297
ret = processor.prepare(rctx.y);
@@ -5325,9 +5320,18 @@ int RGWRados::restore_obj_from_cloud(RGWLCCloudTierCtx& tier_ctx,
53255320
real_time set_mtime;
53265321
std::map<std::string, std::string> headers;
53275322
ldpp_dout(dpp, 20) << "Fetching from cloud, object:" << dest_obj << dendl;
5328-
ret = rgw_cloud_tier_get_object(tier_ctx, false, headers,
5323+
if (tier_config.tier_placement.tier_type == "cloud-s3-glacier") {
5324+
ldpp_dout(dpp, 20) << "Restoring object:" << dest_obj << " from the cloud" << dendl;
5325+
RGWZoneGroupTierS3Glacier& glacier_params = tier_config.tier_placement.s3_glacier;
5326+
ret = rgw_cloud_tier_restore_object(tier_ctx, headers,
5327+
&set_mtime, etag, accounted_size,
5328+
attrs, days, glacier_params, &cb);
5329+
} else {
5330+
ldpp_dout(dpp, 20) << "Fetching object:" << dest_obj << "from the cloud" << dendl;
5331+
ret = rgw_cloud_tier_get_object(tier_ctx, false, headers,
53295332
&set_mtime, etag, accounted_size,
53305333
attrs, &cb);
5334+
}
53315335

53325336
if (ret < 0) {
53335337
ldpp_dout(dpp, 20) << "Fetching from cloud failed, object:" << dest_obj << dendl;
@@ -5420,7 +5424,7 @@ int RGWRados::restore_obj_from_cloud(RGWLCCloudTierCtx& tier_ctx,
54205424
// set tier-config only for temp restored objects, as
54215425
// permanent copies will be treated as regular objects
54225426
{
5423-
t.append("cloud-s3");
5427+
t.append(tier_ctx.tier_type);
54245428
encode(tier_config, t_tier);
54255429
attrs[RGW_ATTR_CLOUD_TIER_TYPE] = t;
54265430
attrs[RGW_ATTR_CLOUD_TIER_CONFIG] = t_tier;

src/rgw/driver/rados/rgw_sal_rados.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2916,6 +2916,8 @@ int RadosObject::restore_obj_from_cloud(Bucket* bucket,
29162916
const rgw::sal::ZoneGroup& zonegroup = store->get_zone()->get_zonegroup();
29172917
int ret = 0;
29182918
string src_storage_class = o.meta.storage_class; // or take src_placement also as input
2919+
// update tier_config in case tier params are updated
2920+
tier_config.tier_placement = rtier->get_rt();
29192921

29202922
if (bucket_name.empty()) {
29212923
bucket_name = "rgwx-" + zonegroup.get_name() + "-" + tier->get_storage_class() +
@@ -2933,6 +2935,8 @@ int RadosObject::restore_obj_from_cloud(Bucket* bucket,
29332935
tier_ctx.multipart_min_part_size = rtier->get_rt().t.s3.multipart_min_part_size;
29342936
tier_ctx.multipart_sync_threshold = rtier->get_rt().t.s3.multipart_sync_threshold;
29352937
tier_ctx.storage_class = tier->get_storage_class();
2938+
tier_ctx.restore_storage_class = rtier->get_rt().restore_storage_class;
2939+
tier_ctx.tier_type = rtier->get_rt().tier_type;
29362940

29372941
ldpp_dout(dpp, 20) << "Restoring object(" << o.key << ") from the cloud endpoint(" << endpoint << ")" << dendl;
29382942

@@ -3242,7 +3246,7 @@ int RadosObject::write_cloud_tier(const DoutPrefixProvider* dpp,
32423246
tier_config.tier_placement = rtier->get_rt();
32433247
tier_config.is_multipart_upload = is_multipart_upload;
32443248

3245-
pmanifest->set_tier_type("cloud-s3");
3249+
pmanifest->set_tier_type(rtier->get_rt().tier_type);
32463250
pmanifest->set_tier_config(tier_config);
32473251

32483252
/* check if its necessary */

src/rgw/driver/rados/rgw_sal_rados.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ class RadosPlacementTier: public StorePlacementTier {
4343
virtual ~RadosPlacementTier() = default;
4444

4545
virtual const std::string& get_tier_type() { return tier.tier_type; }
46+
virtual bool is_tier_type_s3() { return (tier.is_tier_type_s3()); }
4647
virtual const std::string& get_storage_class() { return tier.storage_class; }
4748
virtual bool retain_head_object() { return tier.retain_head_object; }
49+
virtual bool allow_read_through() { return tier.allow_read_through; }
50+
virtual uint64_t get_read_through_restore_days() { return tier.read_through_restore_days; }
4851
RGWZoneGroupPlacementTier& get_rt() { return tier; }
4952
};
5053

0 commit comments

Comments
 (0)