]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/multisite: in a multisite env with bucket sync policies configured,
authorShilpa Jagannath <smanjara@redhat.com>
Wed, 26 Jun 2024 07:04:08 +0000 (03:04 -0400)
committerShilpa Jagannath <smanjara@redhat.com>
Fri, 4 Apr 2025 17:16:53 +0000 (13:16 -0400)
we may end up orphaning objects on remote zones when a delete bucket
is issued on metadata master. to avoid this, list the buckets on remote
zones and delete bucket only when empty. if a zone is unreachable we
drop that zone and continue with bucket deletion. such zones might have
orphaned objects that will have to be cleaned up using radosgw-admin tool

Signed-off-by: Shilpa Jagannath <smanjara@redhat.com>
src/rgw/driver/rados/rgw_cr_rados.cc
src/rgw/driver/rados/rgw_cr_rados.h
src/rgw/driver/rados/rgw_data_sync.cc
src/rgw/driver/rados/rgw_data_sync.h
src/rgw/driver/rados/rgw_rados.cc
src/rgw/driver/rados/rgw_sync_module_aws.h
src/rgw/driver/rados/rgw_sync_module_es.cc

index 332006c02fd45f1706230ab5ba21027da52cab26..1ac07306b09c008ea86a6ee3b498b05411337d7c 100644 (file)
@@ -1204,3 +1204,56 @@ int RGWDataPostNotifyCR::operate(const DoutPrefixProvider* dpp)
   }
   return 0;
 }
+  
+RGWStatRemoteBucketCR::RGWStatRemoteBucketCR(const DoutPrefixProvider *dpp,
+                                   rgw::sal::RadosStore* const store,
+            const rgw_zone_id source_zone,
+            const rgw_bucket& bucket,
+            RGWHTTPManager* http,
+            std::vector<rgw_zone_id> zids,
+            std::vector<bucket_unordered_list_result>& peer_result)
+      : RGWCoroutine(store->ctx()), dpp(dpp), store(store),
+      source_zone(source_zone), bucket(bucket), http(http),
+      zids(zids), peer_result(peer_result) {}
+  
+int RGWStatRemoteBucketCR::operate(const DoutPrefixProvider *dpp) {
+  reenter(this) {
+    yield {
+      auto result = peer_result.begin();
+      for (auto& zid : zids) {
+        auto& zone_conn_map = store->getRados()->svc.zone->get_zone_conn_map();
+        auto ziter = zone_conn_map.find(zid);
+        if (ziter == zone_conn_map.end()) {
+          ldpp_dout(dpp, 0) << "WARNING: no connection to zone " << ziter->first << dendl;
+          continue;
+        }
+        ldpp_dout(dpp, 20) << "query bucket from: " << ziter->first << dendl;
+        RGWRESTConn *conn = ziter->second;
+
+        rgw_http_param_pair pairs[] = { { "versions" , NULL },
+                                       { "format" , "json" },
+                                       { "objs-container" , "true" },
+          { "max-keys", "1" },
+          { "allow-unordered", "true"},
+          { "key-marker" , NULL },
+                                       { "version-id-marker" , NULL },
+                                       { NULL, NULL } };
+        string p = string("/") + bucket.get_key(':', 0);
+        spawn(new RGWReadRESTResourceCR<bucket_unordered_list_result>(store->ctx(), &*conn, &*http, p, pairs, &*result), false);
+        ++result;
+      }
+    }
+
+    while (num_spawned()) {
+      yield wait_for_child();
+      collect(&child_ret, nullptr);
+      if (child_ret < 0) {
+        drain_all();
+        return set_cr_error(child_ret);
+      }
+    }
+
+    return set_cr_done();
+  }
+  return 0;
+}
index 09b364a7e17bfe8eaf00132f25b10f2678c5e2a2..6a5ef9851f3a43f9d8fb99ed77416cb2608336ea 100644 (file)
@@ -16,6 +16,8 @@
 
 #include "services/svc_sys_obj.h"
 #include "services/svc_bucket.h"
+#include <string_view>
+using namespace std;
 
 struct rgw_http_param_pair;
 class RGWRESTConn;
@@ -1708,3 +1710,105 @@ public:
   int operate(const DoutPrefixProvider* dpp) override;
 };
 
+struct rgw_bucket_entry_owner {
+  std::string id;
+  std::string display_name;
+
+  rgw_bucket_entry_owner() {}
+  rgw_bucket_entry_owner(const std::string& _id, const std::string& _display_name) : id(_id), display_name(_display_name) {}
+
+  void decode_json(JSONObj *obj);
+};
+
+struct bucket_list_entry {
+  bool delete_marker;
+  rgw_obj_key key;
+  bool is_latest;
+  real_time mtime;
+  string etag;
+  uint64_t size;
+  string storage_class;
+  rgw_bucket_entry_owner owner;
+  uint64_t versioned_epoch;
+  string rgw_tag;
+
+  bucket_list_entry() : delete_marker(false), is_latest(false), size(0), versioned_epoch(0) {}
+
+  void decode_json(JSONObj *obj) {
+    JSONDecoder::decode_json("IsDeleteMarker", delete_marker, obj);
+    JSONDecoder::decode_json("Key", key.name, obj);
+    JSONDecoder::decode_json("VersionId", key.instance, obj);
+    JSONDecoder::decode_json("IsLatest", is_latest, obj);
+    string mtime_str;
+    JSONDecoder::decode_json("RgwxMtime", mtime_str, obj);
+
+    struct tm t;
+    uint32_t nsec;
+    if (parse_iso8601(mtime_str.c_str(), &t, &nsec)) {
+      ceph_timespec ts;
+      ts.tv_sec = (uint64_t)internal_timegm(&t);
+      ts.tv_nsec = nsec;
+      mtime = real_clock::from_ceph_timespec(ts);
+    }
+    JSONDecoder::decode_json("ETag", etag, obj);
+    JSONDecoder::decode_json("Size", size, obj);
+    JSONDecoder::decode_json("StorageClass", storage_class, obj);
+    JSONDecoder::decode_json("Owner", owner, obj);
+    JSONDecoder::decode_json("VersionedEpoch", versioned_epoch, obj);
+    JSONDecoder::decode_json("RgwxTag", rgw_tag, obj);
+    if (key.instance == "null" && !versioned_epoch) {
+      key.instance.clear();
+    }
+  }
+
+  RGWModifyOp get_modify_op() const {
+    if (delete_marker) {
+      return CLS_RGW_OP_LINK_OLH_DM;
+    } else if (!key.instance.empty() && key.instance != "null") {
+      return CLS_RGW_OP_LINK_OLH;
+    } else {
+      return CLS_RGW_OP_ADD;
+    }
+  }
+};
+
+struct bucket_unordered_list_result {
+  string name;
+  string prefix;
+  int max_keys;
+  bool is_truncated;
+  bool allow_unordered;
+  list<bucket_list_entry> entries;
+
+  bucket_unordered_list_result() : max_keys(0), is_truncated(false) {}
+
+  void decode_json(JSONObj *obj) {
+    JSONDecoder::decode_json("Name", name, obj);
+    JSONDecoder::decode_json("Prefix", prefix, obj);
+    JSONDecoder::decode_json("MaxKeys", max_keys, obj);
+    JSONDecoder::decode_json("IsTruncated", is_truncated, obj);
+    JSONDecoder::decode_json("allow-unordered", allow_unordered, obj);
+    JSONDecoder::decode_json("Entries", entries, obj);
+  }
+};
+
+class RGWStatRemoteBucketCR: public RGWCoroutine {
+  int child_ret = 0;
+  const DoutPrefixProvider *dpp;
+  rgw::sal::RadosStore* const store;
+  const rgw_zone_id source_zone;
+  const rgw_bucket& bucket;
+  RGWHTTPManager* http;
+  std::vector<rgw_zone_id> zids;
+  vector<bucket_unordered_list_result>& peer_result;
+
+public:
+  RGWStatRemoteBucketCR(const DoutPrefixProvider *dpp,
+                                   rgw::sal::RadosStore* const store,
+            const rgw_zone_id source_zone,
+            const rgw_bucket& bucket,
+            RGWHTTPManager* http, std::vector<rgw_zone_id> zids,
+            std::vector<bucket_unordered_list_result>& peer_result);
+
+  int operate(const DoutPrefixProvider *dpp) override;
+};
index 7eb8db380889c4263d951085ac0be53629c2ae1c..6b947cf76f521159cc5b2add36f9513a4bcae27d 100644 (file)
@@ -4063,58 +4063,6 @@ void rgw_bucket_entry_owner::decode_json(JSONObj *obj)
   JSONDecoder::decode_json("DisplayName", display_name, obj);
 }
 
-struct bucket_list_entry {
-  bool delete_marker;
-  rgw_obj_key key;
-  bool is_latest;
-  real_time mtime;
-  string etag;
-  uint64_t size;
-  string storage_class;
-  rgw_bucket_entry_owner owner;
-  uint64_t versioned_epoch;
-  string rgw_tag;
-
-  bucket_list_entry() : delete_marker(false), is_latest(false), size(0), versioned_epoch(0) {}
-
-  void decode_json(JSONObj *obj) {
-    JSONDecoder::decode_json("IsDeleteMarker", delete_marker, obj);
-    JSONDecoder::decode_json("Key", key.name, obj);
-    JSONDecoder::decode_json("VersionId", key.instance, obj);
-    JSONDecoder::decode_json("IsLatest", is_latest, obj);
-    string mtime_str;
-    JSONDecoder::decode_json("RgwxMtime", mtime_str, obj);
-
-    struct tm t;
-    uint32_t nsec;
-    if (parse_iso8601(mtime_str.c_str(), &t, &nsec)) {
-      ceph_timespec ts;
-      ts.tv_sec = (uint64_t)internal_timegm(&t);
-      ts.tv_nsec = nsec;
-      mtime = real_clock::from_ceph_timespec(ts);
-    }
-    JSONDecoder::decode_json("ETag", etag, obj);
-    JSONDecoder::decode_json("Size", size, obj);
-    JSONDecoder::decode_json("StorageClass", storage_class, obj);
-    JSONDecoder::decode_json("Owner", owner, obj);
-    JSONDecoder::decode_json("VersionedEpoch", versioned_epoch, obj);
-    JSONDecoder::decode_json("RgwxTag", rgw_tag, obj);
-    if (key.instance == "null" && !versioned_epoch) {
-      key.instance.clear();
-    }
-  }
-
-  RGWModifyOp get_modify_op() const {
-    if (delete_marker) {
-      return CLS_RGW_OP_LINK_OLH_DM;
-    } else if (!key.instance.empty() && key.instance != "null") {
-      return CLS_RGW_OP_LINK_OLH;
-    } else {
-      return CLS_RGW_OP_ADD;
-    }
-  }
-};
-
 struct bucket_list_result {
   string name;
   string prefix;
index 4e160f14cc486221c2baa55699923096f6abc42c..9daf2b739bde3bf8a6aac17ecf80df5d06393dfb 100644 (file)
@@ -285,17 +285,6 @@ struct rgw_datalog_shard_data {
 
 class RGWAsyncRadosProcessor;
 class RGWDataSyncControlCR;
-
-struct rgw_bucket_entry_owner {
-  std::string id;
-  std::string display_name;
-
-  rgw_bucket_entry_owner() {}
-  rgw_bucket_entry_owner(const std::string& _id, const std::string& _display_name) : id(_id), display_name(_display_name) {}
-
-  void decode_json(JSONObj *obj);
-};
-
 class RGWSyncErrorLogger;
 class RGWRESTConn;
 class RGWServices;
index e50305d991f6bf1452e8a0fbf274fd74918e896b..3fb9a4eb09e501494ef921db615365d9e28bbafe 100644 (file)
@@ -5612,6 +5612,64 @@ int RGWRados::delete_bucket(RGWBucketInfo& bucket_info, std::map<std::string, bu
     }
   }
 
+  // delete_bucket checks for objects in the bucket on other zones,
+  // if there is bucket sync policy configured, by doing unordered
+  // listing with max_key=1. we don't prevent users from
+  // deleting a bucket if there are any errors in this block
+  // except if we found objects in another zone.
+  if (svc.zone->is_syncing_bucket_meta(bucket)) {
+    auto bs_policy = bucket_info.sync_policy;
+    if (bs_policy) {
+      ldpp_dout(dpp, 10) << "bucket policy exists" << dendl;
+      const rgw_zone_id source_zone = svc.zone->get_zone_params().get_id();
+      RGWBucketSyncPolicyHandlerRef source_handler;
+      int ret = driver->get_sync_policy_handler(dpp, source_zone, bucket, &source_handler, y);
+      if (ret < 0) {
+        ldpp_dout(dpp, 10) << "could not get bucket sync policy handler (r=" << ret << ")" << dendl;
+      }
+
+      auto all_dests = source_handler->get_all_dests();
+
+      std::vector<rgw_zone_id> zids;
+      rgw_zone_id last_zid;
+      for (auto& diter : all_dests) {
+        const auto& zid = diter.first;
+        if (zid == last_zid) {
+          continue;
+        }
+        last_zid = zid;
+        zids.push_back(zid);
+      }
+
+      std::vector<bucket_unordered_list_result> peer_status;
+      peer_status.resize(zids.size());
+
+      RGWCoroutinesManager crs(driver->ctx(), driver->getRados()->get_cr_registry());
+      RGWHTTPManager http(driver->ctx(), crs.get_completion_mgr());
+      ret = http.start();
+      if (ret < 0) {
+        ldpp_dout(dpp, 0) << "failed in http_manager.start() ret=" << ret << dendl;
+      }
+      ret = crs.run(dpp, new RGWStatRemoteBucketCR(dpp, driver, source_zone, bucket, &http, zids, peer_status));
+      if (ret < 0) {
+        ldpp_dout(dpp, 0) << "failed to fetch remote bucket stats " << cpp_strerror(ret) << dendl;
+      }
+
+      for (const auto& list_result: peer_status) {
+        auto entries_iter = list_result.entries.begin();
+        for (; entries_iter != list_result.entries.end(); ++entries_iter) {
+          std::string ns;
+          rgw_obj_key obj;
+
+          if (rgw_obj_key::oid_to_key_in_ns(entries_iter->key.name, &obj, ns)) {
+            ldpp_dout(dpp, 0) << "cannot delete bucket. objects exist in the bucket in another zone " << dendl;
+            return -ENOTEMPTY;
+          }
+        }
+      }
+    }
+  }
+
   bool remove_ep = true;
 
   if (objv_tracker.read_version.empty()) {
index 92532ff00e7faa39351d5a41825bbdbeff4e9096..ce6e1d27ab0180186152b329bcb7211c5bb5a576 100644 (file)
@@ -5,6 +5,8 @@
 
 #include "rgw_sync_module.h"
 
+struct rgw_bucket_entry_owner;
+
 struct rgw_sync_aws_multipart_part_info {
   int part_num{0};
   uint64_t ofs{0};
index 414fbeac4c990ee8697615a55d486007a9dd97e9..5fb4b11d2fbf2de849db5270e81034525af4669d 100644 (file)
@@ -9,6 +9,7 @@
 #include "rgw_sync_module_es.h"
 #include "rgw_sync_module_es_rest.h"
 #include "rgw_rest_conn.h"
+#include "rgw_cr_rados.h"
 #include "rgw_cr_rest.h"
 #include "rgw_op.h"
 #include "rgw_es_query.h"