]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/adminops: support for adding restore operation
authorJiffin Tony Thottan <thottanjiffin@gmail.com>
Thu, 9 Oct 2025 13:43:52 +0000 (19:13 +0530)
committerJiffin Tony Thottan <thottanjiffin@gmail.com>
Tue, 9 Dec 2025 07:57:54 +0000 (13:27 +0530)
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 <thottanjiffin@gmail.com>
src/rgw/CMakeLists.txt
src/rgw/rgw_appmain.cc
src/rgw/rgw_rest_restore.cc [new file with mode: 0644]
src/rgw/rgw_rest_restore.h [new file with mode: 0644]
src/rgw/rgw_restore.cc
src/rgw/rgw_restore.h

index 0b6dabe0a7187570db68d764b18f1e9fda1a9306..7017ce5da39b6eceacf4989a703762e4627b85e6 100644 (file)
@@ -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
index df8c0c22cc327750059baa0c22214ed72d3559bf..37a1c284ebd834317a1c63600a3ab70438342c70 100644 (file)
@@ -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 (file)
index 0000000..6327ef4
--- /dev/null
@@ -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<std::string> 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 (file)
index 0000000..29dc30a
--- /dev/null
@@ -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
index c3ef799b941a0e8e39fa72d146edd895e55e2943..9f4bd97d6882fc1a8fd5eaa07ae321373ddc1605 100644 (file)
@@ -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<string> restore_status_filter,
+                  std::string& err_msg, RGWFormatterFlusher& flusher, optional_yield y)
+{
+  int ret = 0;
+  std::unique_ptr<rgw::sal::Bucket> 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<rgw_bucket_dir_entry>::iterator iter = results.objs.begin(); iter != results.objs.end(); ++iter) {
+      std::unique_ptr<rgw::sal::Object> 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<string, bufferlist>::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<rgw::sal::Bucket> 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<rgw::sal::Object> 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<string, bufferlist>::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
index 7678b5260d43c93ce5ca18c460550818eb9ac1c2..981eb49360c5d01144ea2474ae22befb49558722 100644 (file)
@@ -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<std::string> 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