From: Jiffin Tony Thottan Date: Thu, 9 Oct 2025 13:43:52 +0000 (+0530) Subject: rgw/adminops: support for adding restore operation X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=2461b1bef3dd06d954ee44fd04f567dc4b70e75f;p=ceph.git rgw/adminops: support for adding restore operation Adding support for restore cli in admin ops, added existing clis list and statusw Fixes: https://tracker.ceph.com/issues/70931 Signed-off-by: Jiffin Tony Thottan --- diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index 0b6dabe0a71..7017ce5da39 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -144,7 +144,8 @@ set(librgw_common_srcs rgw_realm_watcher.cc rgw_bucket_logging.cc rgw_rest_bucket_logging.cc - rgw_bucket_sync.cc) + rgw_bucket_sync.cc + rgw_rest_restore.cc) list(APPEND librgw_common_srcs driver/immutable_config/store.cc diff --git a/src/rgw/rgw_appmain.cc b/src/rgw/rgw_appmain.cc index df8c0c22cc3..37a1c284ebd 100644 --- a/src/rgw/rgw_appmain.cc +++ b/src/rgw/rgw_appmain.cc @@ -45,6 +45,7 @@ #include "rgw_rest_realm.h" #include "rgw_rest_ratelimit.h" #include "rgw_rest_zero.h" +#include "rgw_rest_restore.h" #include "rgw_swift_auth.h" #include "rgw_log.h" #include "rgw_lib.h" @@ -363,6 +364,7 @@ void rgw::AppMain::cond_init_apis() admin_resource->register_resource("info", new RGWRESTMgr_Info); admin_resource->register_resource("usage", new RGWRESTMgr_Usage); admin_resource->register_resource("account", new RGWRESTMgr_Account); + admin_resource->register_resource("restore", new RGWRESTMgr_Restore); /* Register driver-specific admin APIs */ env.driver->register_admin_apis(admin_resource); rest.register_resource(g_conf()->rgw_admin_entry, admin_resource); diff --git a/src/rgw/rgw_rest_restore.cc b/src/rgw/rgw_rest_restore.cc new file mode 100644 index 00000000000..6327ef4edba --- /dev/null +++ b/src/rgw/rgw_rest_restore.cc @@ -0,0 +1,88 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +/* + * Ceph - scalable distributed file system + * + * Copyright contributors to the Ceph project + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "rgw_rest_restore.h" +#include "rgw_restore.h" + +class RGWOp_Restore_Status : public RGWRESTOp { + +public: + RGWOp_Restore_Status() {} + + int check_caps(const RGWUserCaps& caps) override { + return caps.check_cap("buckets", RGW_CAP_READ); + } + + void execute(optional_yield y) override; + + const char* name() const override { return "get_restore_status"; } +}; + +void RGWOp_Restore_Status::execute(optional_yield y) +{ + std::string bucket_name, tenant, object; + RESTArgs::get_string(s, "bucket", bucket_name, &bucket_name); + RESTArgs::get_string(s, "tenant", tenant, &tenant); + RESTArgs::get_string(s, "object", object, &object); + rgw::restore::RestoreEntry entry; + + entry.bucket = rgw_bucket {tenant, bucket_name}; + entry.obj_key = rgw_obj_key {object}; + + op_ret = driver->get_rgwrestore()->status(this, entry, s->err.message, + flusher, y); +} + +class RGWOp_Restore_List : public RGWRESTOp { + +public: + RGWOp_Restore_List() {} + + int check_caps(const RGWUserCaps& caps) override { + return caps.check_cap("buckets", RGW_CAP_READ); + } + + void execute(optional_yield y) override; + + const char* name() const override { return "get_restore_list"; } +}; + +void RGWOp_Restore_List::execute(optional_yield y) +{ + std::string bucket_name, tenant, restore_status_filter; + std::optional restore_status_filter_optional; + bool exists = false; + RESTArgs::get_string(s, "bucket", bucket_name, &bucket_name); + RESTArgs::get_string(s, "tenant", tenant, &tenant); + RESTArgs::get_string(s, "restore-status-filter", "", &restore_status_filter, &exists); + rgw::restore::RestoreEntry entry; + + entry.bucket = rgw_bucket {tenant, bucket_name}; + + if (exists) { + restore_status_filter_optional = restore_status_filter; + } + + op_ret = driver->get_rgwrestore()->list(this, entry, restore_status_filter_optional, + s->err.message, flusher, y); +} + +RGWOp* RGWHandler_Restore::op_get() +{ + if (s->info.args.sub_resource_exists("object")) + return new RGWOp_Restore_Status; + + return new RGWOp_Restore_List; +} diff --git a/src/rgw/rgw_rest_restore.h b/src/rgw/rgw_rest_restore.h new file mode 100644 index 00000000000..29dc30a2c30 --- /dev/null +++ b/src/rgw/rgw_rest_restore.h @@ -0,0 +1,43 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +/* + * Ceph - scalable distributed file system + * + * Copyright contributors to the Ceph project + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#pragma once + +#include "rgw_rest.h" +#include "rgw_rest_s3.h" + +class RGWHandler_Restore : public RGWHandler_Auth_S3 { + protected: + RGWOp *op_get() override; + public: + using RGWHandler_Auth_S3::RGWHandler_Auth_S3; + ~RGWHandler_Restore() override = default; + + int read_permissions(RGWOp*, optional_yield y) override { + return 0; + } +}; + +class RGWRESTMgr_Restore : public RGWRESTMgr { + public: + RGWRESTMgr_Restore() = default; + ~RGWRESTMgr_Restore() override = default; + + RGWHandler_REST *get_handler(rgw::sal::Driver* driver, struct req_state*, + const rgw::auth::StrategyRegistry& auth_registry, + const std::string&) override { + return new RGWHandler_Restore(auth_registry); + } +}; \ No newline at end of file diff --git a/src/rgw/rgw_restore.cc b/src/rgw/rgw_restore.cc index c3ef799b941..9f4bd97d688 100644 --- a/src/rgw/rgw_restore.cc +++ b/src/rgw/rgw_restore.cc @@ -48,6 +48,7 @@ constexpr int32_t hours_in_a_day = 24; constexpr int32_t secs_in_a_day = hours_in_a_day * 60 * 60; +static constexpr size_t listing_max_entries = 1000; using namespace std; using namespace rgw::sal; @@ -749,4 +750,149 @@ int Restore::restore_obj_from_cloud(rgw::sal::Bucket* pbucket, return ret; } +int Restore::list(const DoutPrefixProvider* dpp, RestoreEntry& entry, + std::optional restore_status_filter, + std::string& err_msg, RGWFormatterFlusher& flusher, optional_yield y) +{ + int ret = 0; + std::unique_ptr bucket; + ret = driver->load_bucket(dpp, entry.bucket, &bucket, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: could not init bucket: " << cpp_strerror(-ret) << dendl; + return ret; + } + rgw::sal::Bucket::ListParams params; + rgw::sal::Bucket::ListResults results; + params.list_versions = bucket->versioned(); + params.allow_unordered = true; + flusher.start(0); + auto f = flusher.get_formatter(); + f->open_object_section("restore_list"); + do { + ret = bucket->list(dpp, params, listing_max_entries, results, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: driver->list_objects(): " << cpp_strerror(-ret) << dendl; + return ret; + } + for (vector::iterator iter = results.objs.begin(); iter != results.objs.end(); ++iter) { + std::unique_ptr obj = bucket->get_object(iter->key.name); + if (obj) { + ret = obj->get_obj_attrs(y, dpp); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to stat object, returned error: " << cpp_strerror(-ret) << dendl; + return -ret; + } + for (map::iterator getattriter = obj->get_attrs().begin(); getattriter != obj->get_attrs().end(); ++getattriter) { + bufferlist& bl = getattriter->second; + if (getattriter->first == RGW_ATTR_RESTORE_STATUS) { + rgw::sal::RGWRestoreStatus rs; + { + using ceph::decode; + try { + decode(rs, bl); + } catch (const JSONDecoder::err& e) { + ldpp_dout(dpp, 0) << "failed to decode JSON input: " << e.what() << dendl; + return EINVAL; + } + } + if (restore_status_filter) { + if (restore_status_filter == rgw::sal::rgw_restore_status_dump(rs)) { + f->dump_string(iter->key.name, rgw::sal::rgw_restore_status_dump(rs)); + } + } else { + f->dump_string(iter->key.name, rgw::sal::rgw_restore_status_dump(rs)); + } + } + } + } + } + } while (results.is_truncated); + f->close_section(); + flusher.flush(); + + return ret; +} + +int Restore::status(const DoutPrefixProvider* dpp, RestoreEntry& entry, + std::string& err_msg, RGWFormatterFlusher& flusher, + optional_yield y) +{ + int ret = 0; + std::unique_ptr bucket; + ret = driver->load_bucket(dpp, entry.bucket, &bucket, y); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: could not init bucket: " << cpp_strerror(-ret) << dendl; + return ret; + } + if (!entry.obj_key.name.empty()) { + flusher.start(0); + auto f = flusher.get_formatter(); + f->open_object_section("object restore status"); + f->dump_string("name", entry.obj_key.name); + std::unique_ptr obj = bucket->get_object(entry.obj_key); + ret = obj->get_obj_attrs(y, dpp); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to stat object, returned error: " << cpp_strerror(-ret) << dendl; + return -ret; + } + map::iterator iter; + for (iter = obj->get_attrs().begin(); iter != obj->get_attrs().end(); ++iter) { + bufferlist& bl = iter->second; + { + using ceph::decode; + if (iter->first == RGW_ATTR_RESTORE_STATUS) { + rgw::sal::RGWRestoreStatus rs; + try { + decode(rs, bl); + } catch (const JSONDecoder::err& e) { + ldpp_dout(dpp, 0) << "failed to decode JSON input: " << e.what() << dendl; + return EINVAL; + } + f->dump_string("RestoreStatus", rgw::sal::rgw_restore_status_dump(rs)); + } else if (iter->first == RGW_ATTR_RESTORE_TYPE) { + rgw::sal::RGWRestoreType rt; + try { + decode(rt, bl); + } catch (const JSONDecoder::err& e) { + ldpp_dout(dpp, 0) << "failed to decode JSON input: " << e.what() << dendl; + return EINVAL; + } + f->dump_string("RestoreType", rgw::sal::rgw_restore_type_dump(rt)); + } else if (iter->first == RGW_ATTR_RESTORE_EXPIRY_DATE) { + ceph::real_time restore_expiry_date; + try { + decode(restore_expiry_date, bl); + } catch (const JSONDecoder::err& e) { + ldpp_dout(dpp, 0) << "failed to decode JSON input: " << e.what() << dendl; + return EINVAL; + } + encode_json("RestoreExpiryDate", restore_expiry_date, f); + } else if (iter->first == RGW_ATTR_RESTORE_TIME) { + ceph::real_time restore_time; + try { + decode(restore_time, bl); + } catch (const JSONDecoder::err& e) { + ldpp_dout(dpp, 0) << "failed to decode JSON input: " << e.what() << dendl; + return EINVAL; + } + encode_json("RestoreTime", restore_time, f); + } else if (iter->first == RGW_ATTR_RESTORE_VERSIONED_EPOCH) { + uint64_t versioned_epoch; + try { + decode(versioned_epoch, bl); + } catch (const JSONDecoder::err& e) { + ldpp_dout(dpp, 0) << "failed to decode JSON input: " << e.what() << dendl; + return EINVAL; + } + f->dump_unsigned("RestoreVersionedEpoch", versioned_epoch); + } + } + } + f->close_section(); + flusher.flush(); + } + + return ret; +} + } // namespace rgw::restore diff --git a/src/rgw/rgw_restore.h b/src/rgw/rgw_restore.h index 7678b5260d4..981eb49360c 100644 --- a/src/rgw/rgw_restore.h +++ b/src/rgw/rgw_restore.h @@ -165,6 +165,15 @@ public: const std::string& version_id, const rgw::notify::EventTypeList& event_types, optional_yield y); + // list restore status of objects in the bucket + int list(const DoutPrefixProvider* dpp, RestoreEntry& entry, + std::optional restore_status_filter, std::string& err_msg, + RGWFormatterFlusher& flusher, optional_yield y); + + // restore status of an object in a bucket + int status(const DoutPrefixProvider* dpp, RestoreEntry& entry, + std::string& err_msg, RGWFormatterFlusher& flusher, + optional_yield y); }; } // namespace rgw::restore