]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: add rgw::sal::RadosConfigStore
authorCasey Bodley <cbodley@redhat.com>
Mon, 29 Aug 2022 20:08:10 +0000 (16:08 -0400)
committerCasey Bodley <cbodley@redhat.com>
Wed, 28 Sep 2022 20:24:20 +0000 (16:24 -0400)
Signed-off-by: Casey Bodley <cbodley@redhat.com>
src/rgw/CMakeLists.txt
src/rgw/store/rados/config/impl.cc [new file with mode: 0644]
src/rgw/store/rados/config/impl.h [new file with mode: 0644]
src/rgw/store/rados/config/period.cc [new file with mode: 0644]
src/rgw/store/rados/config/period_config.cc [new file with mode: 0644]
src/rgw/store/rados/config/realm.cc [new file with mode: 0644]
src/rgw/store/rados/config/store.cc [new file with mode: 0644]
src/rgw/store/rados/config/store.h [new file with mode: 0644]
src/rgw/store/rados/config/zone.cc [new file with mode: 0644]
src/rgw/store/rados/config/zonegroup.cc [new file with mode: 0644]

index c1971897a2a3b8497f497f252457801a6988cc2c..6713512d6777b9874b933d8888ad23d37354a01f 100644 (file)
@@ -175,6 +175,15 @@ set(librgw_common_srcs
   rgw_tracer.cc
   rgw_lua_background.cc)
 
+list(APPEND librgw_common_srcs
+  store/rados/config/impl.cc
+  store/rados/config/period.cc
+  store/rados/config/period_config.cc
+  store/rados/config/realm.cc
+  store/rados/config/store.cc
+  store/rados/config/zone.cc
+  store/rados/config/zonegroup.cc)
+
 if(WITH_RADOSGW_AMQP_ENDPOINT)
   list(APPEND librgw_common_srcs rgw_amqp.cc)
 endif()
diff --git a/src/rgw/store/rados/config/impl.cc b/src/rgw/store/rados/config/impl.cc
new file mode 100644 (file)
index 0000000..f1b2bef
--- /dev/null
@@ -0,0 +1,129 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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 "impl.h"
+
+#include "common/async/yield_context.h"
+#include "common/errno.h"
+#include "rgw_string.h"
+#include "rgw_zone.h"
+
+namespace rgw::rados {
+
+// default pool names
+constexpr std::string_view default_zone_root_pool = "rgw.root";
+constexpr std::string_view default_zonegroup_root_pool = "rgw.root";
+constexpr std::string_view default_realm_root_pool = "rgw.root";
+constexpr std::string_view default_period_root_pool = "rgw.root";
+
+static rgw_pool default_pool(std::string_view name,
+                             std::string_view default_name)
+{
+  return std::string{name_or_default(name, default_name)};
+}
+
+ConfigImpl::ConfigImpl(const ceph::common::ConfigProxy& conf)
+  : realm_pool(default_pool(conf->rgw_realm_root_pool,
+                            default_realm_root_pool)),
+    period_pool(default_pool(conf->rgw_period_root_pool,
+                             default_period_root_pool)),
+    zonegroup_pool(default_pool(conf->rgw_zonegroup_root_pool,
+                                default_zonegroup_root_pool)),
+    zone_pool(default_pool(conf->rgw_zone_root_pool,
+                           default_zone_root_pool))
+{
+}
+
+int ConfigImpl::read(const DoutPrefixProvider* dpp, optional_yield y,
+                     const rgw_pool& pool, const std::string& oid,
+                     bufferlist& bl, RGWObjVersionTracker* objv)
+{
+  librados::IoCtx ioctx;
+  int r = rgw_init_ioctx(dpp, &rados, pool, ioctx, true, false);
+  if (r < 0) {
+    return r;
+  }
+  librados::ObjectReadOperation op;
+  if (objv) {
+    objv->prepare_op_for_read(&op);
+  }
+  op.read(0, 0, &bl, nullptr);
+  return rgw_rados_operate(dpp, ioctx, oid, &op, nullptr, y);
+}
+
+int ConfigImpl::write(const DoutPrefixProvider* dpp, optional_yield y,
+                      const rgw_pool& pool, const std::string& oid,
+                      Create create, const bufferlist& bl,
+                      RGWObjVersionTracker* objv)
+{
+  librados::IoCtx ioctx;
+  int r = rgw_init_ioctx(dpp, &rados, pool, ioctx, true, false);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::ObjectWriteOperation op;
+  switch (create) {
+    case Create::MustNotExist: op.create(true); break;
+    case Create::MayExist: op.create(false); break;
+    case Create::MustExist: op.assert_exists(); break;
+  }
+  if (objv) {
+    objv->prepare_op_for_write(&op);
+  }
+  op.write_full(bl);
+
+  r = rgw_rados_operate(dpp, ioctx, oid, &op, y);
+  if (r >= 0 && objv) {
+    objv->apply_write();
+  }
+  return r;
+}
+
+int ConfigImpl::remove(const DoutPrefixProvider* dpp, optional_yield y,
+                       const rgw_pool& pool, const std::string& oid,
+                       RGWObjVersionTracker* objv)
+{
+  librados::IoCtx ioctx;
+  int r = rgw_init_ioctx(dpp, &rados, pool, ioctx, true, false);
+  if (r < 0) {
+    return r;
+  }
+
+  librados::ObjectWriteOperation op;
+  if (objv) {
+    objv->prepare_op_for_write(&op);
+  }
+  op.remove();
+
+  r = rgw_rados_operate(dpp, ioctx, oid, &op, y);
+  if (r >= 0 && objv) {
+    objv->apply_write();
+  }
+  return r;
+}
+
+int ConfigImpl::notify(const DoutPrefixProvider* dpp, optional_yield y,
+                       const rgw_pool& pool, const std::string& oid,
+                       bufferlist& bl, uint64_t timeout_ms)
+{
+  librados::IoCtx ioctx;
+  int r = rgw_init_ioctx(dpp, &rados, pool, ioctx, true, false);
+  if (r < 0) {
+    return r;
+  }
+  return rgw_rados_notify(dpp, ioctx, oid, bl, timeout_ms, nullptr, y);
+}
+
+} // namespace rgw::rados
diff --git a/src/rgw/store/rados/config/impl.h b/src/rgw/store/rados/config/impl.h
new file mode 100644 (file)
index 0000000..3aed451
--- /dev/null
@@ -0,0 +1,139 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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 "include/rados/librados.hpp"
+#include "common/dout.h"
+#include "rgw_basic_types.h"
+#include "rgw_tools.h"
+#include "rgw_sal_config.h"
+
+namespace rgw::rados {
+
+// write options that control object creation
+enum class Create {
+  MustNotExist, // fail with EEXIST if the object already exists
+  MayExist, // create if the object didn't exist, overwrite if it did
+  MustExist, // fail with ENOENT if the object doesn't exist
+};
+
+struct ConfigImpl {
+  librados::Rados rados;
+
+  const rgw_pool realm_pool;
+  const rgw_pool period_pool;
+  const rgw_pool zonegroup_pool;
+  const rgw_pool zone_pool;
+
+  ConfigImpl(const ceph::common::ConfigProxy& conf);
+
+  int read(const DoutPrefixProvider* dpp, optional_yield y,
+           const rgw_pool& pool, const std::string& oid,
+           bufferlist& bl, RGWObjVersionTracker* objv);
+
+  template <typename T>
+  int read(const DoutPrefixProvider* dpp, optional_yield y,
+           const rgw_pool& pool, const std::string& oid,
+           T& data, RGWObjVersionTracker* objv)
+  {
+    bufferlist bl;
+    int r = read(dpp, y, pool, oid, bl, objv);
+    if (r < 0) {
+      return r;
+    }
+    try {
+      auto p = bl.cbegin();
+      decode(data, p);
+    } catch (const buffer::error& err) {
+      ldpp_dout(dpp, 0) << "ERROR: failed to decode obj from "
+          << pool << ":" << oid << dendl;
+      return -EIO;
+    }
+    return 0;
+  }
+
+  int write(const DoutPrefixProvider* dpp, optional_yield y,
+            const rgw_pool& pool, const std::string& oid, Create create,
+            const bufferlist& bl, RGWObjVersionTracker* objv);
+
+  template <typename T>
+  int write(const DoutPrefixProvider* dpp, optional_yield y,
+            const rgw_pool& pool, const std::string& oid, Create create,
+            const T& data, RGWObjVersionTracker* objv)
+  {
+    bufferlist bl;
+    encode(data, bl);
+
+    return write(dpp, y, pool, oid, create, bl, objv);
+  }
+
+  int remove(const DoutPrefixProvider* dpp, optional_yield y,
+             const rgw_pool& pool, const std::string& oid,
+             RGWObjVersionTracker* objv);
+
+  int list(const DoutPrefixProvider* dpp, optional_yield y,
+           const rgw_pool& pool, const std::string& marker,
+           std::regular_invocable<std::string> auto filter,
+           std::span<std::string> entries,
+           sal::ListResult<std::string>& result)
+  {
+    librados::IoCtx ioctx;
+    int r = rgw_init_ioctx(dpp, &rados, pool, ioctx, true, false);
+    if (r < 0) {
+      return r;
+    }
+    librados::ObjectCursor oc;
+    if (!oc.from_str(marker)) {
+      ldpp_dout(dpp, 10) << "failed to parse cursor: " << marker << dendl;
+      return -EINVAL;
+    }
+    std::size_t count = 0;
+    try {
+      auto iter = ioctx.nobjects_begin(oc);
+      const auto end = ioctx.nobjects_end();
+      for (; count < entries.size() && iter != end; ++iter) {
+        std::string entry = filter(iter->get_oid());
+        if (!entry.empty()) {
+          entries[count++] = std::move(entry);
+        }
+      }
+      if (iter == end) {
+        result.next.clear();
+      } else {
+        result.next = iter.get_cursor().to_str();
+      }
+    } catch (const std::exception& e) {
+      ldpp_dout(dpp, 10) << "NObjectIterator exception " << e.what() << dendl;
+      return -EIO;
+    }
+    result.entries = entries.first(count);
+    return 0;
+  }
+
+  int notify(const DoutPrefixProvider* dpp, optional_yield y,
+             const rgw_pool& pool, const std::string& oid,
+             bufferlist& bl, uint64_t timeout_ms);
+};
+
+inline std::string_view name_or_default(std::string_view name,
+                                        std::string_view default_name)
+{
+  if (!name.empty()) {
+    return name;
+  }
+  return default_name;
+}
+
+} // namespace rgw::rados
diff --git a/src/rgw/store/rados/config/period.cc b/src/rgw/store/rados/config/period.cc
new file mode 100644 (file)
index 0000000..04650ce
--- /dev/null
@@ -0,0 +1,230 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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 "common/dout.h"
+#include "common/errno.h"
+#include "rgw_zone.h"
+#include "store/rados/config/store.h"
+
+#include "impl.h"
+
+namespace rgw::rados {
+
+// period oids
+constexpr std::string_view period_info_oid_prefix = "periods.";
+constexpr std::string_view period_latest_epoch_info_oid = ".latest_epoch";
+constexpr std::string_view period_staging_suffix = ":staging";
+
+static std::string period_oid(std::string_view period_id, uint32_t epoch)
+{
+  // omit the epoch for the staging period
+  if (period_id.ends_with(period_staging_suffix)) {
+    return string_cat_reserve(period_info_oid_prefix, period_id);
+  }
+  return fmt::format("{}{}.{}", period_info_oid_prefix, period_id, epoch);
+}
+
+static std::string latest_epoch_oid(const ceph::common::ConfigProxy& conf,
+                                    std::string_view period_id)
+{
+  return string_cat_reserve(
+      period_info_oid_prefix, period_id,
+      name_or_default(conf->rgw_period_latest_epoch_info_oid,
+                      period_latest_epoch_info_oid));
+}
+
+static int read_latest_epoch(const DoutPrefixProvider* dpp, optional_yield y,
+                             ConfigImpl* impl, std::string_view period_id,
+                             uint32_t& epoch, RGWObjVersionTracker* objv)
+{
+  const auto& pool = impl->period_pool;
+  const auto latest_oid = latest_epoch_oid(dpp->get_cct()->_conf, period_id);
+  RGWPeriodLatestEpochInfo latest;
+  int r = impl->read(dpp, y, pool, latest_oid, latest, objv);
+  if (r >= 0) {
+    epoch = latest.epoch;
+  }
+  return r;
+}
+
+static int write_latest_epoch(const DoutPrefixProvider* dpp, optional_yield y,
+                              ConfigImpl* impl, bool exclusive,
+                              std::string_view period_id, uint32_t epoch,
+                              RGWObjVersionTracker* objv)
+{
+  const auto& pool = impl->period_pool;
+  const auto latest_oid = latest_epoch_oid(dpp->get_cct()->_conf, period_id);
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+  RGWPeriodLatestEpochInfo latest{epoch};
+  return impl->write(dpp, y, pool, latest_oid, create, latest, objv);
+}
+
+static int delete_latest_epoch(const DoutPrefixProvider* dpp, optional_yield y,
+                               ConfigImpl* impl, std::string_view period_id,
+                               RGWObjVersionTracker* objv)
+{
+  const auto& pool = impl->period_pool;
+  const auto latest_oid = latest_epoch_oid(dpp->get_cct()->_conf, period_id);
+  return impl->remove(dpp, y, pool, latest_oid, objv);
+}
+
+static int update_latest_epoch(const DoutPrefixProvider* dpp, optional_yield y,
+                               ConfigImpl* impl, std::string_view period_id,
+                               uint32_t epoch)
+{
+  static constexpr int MAX_RETRIES = 20;
+
+  for (int i = 0; i < MAX_RETRIES; i++) {
+    uint32_t existing_epoch = 0;
+    RGWObjVersionTracker objv;
+    bool exclusive = false;
+
+    // read existing epoch
+    int r = read_latest_epoch(dpp, y, impl, period_id, existing_epoch, &objv);
+    if (r == -ENOENT) {
+      // use an exclusive create to set the epoch atomically
+      exclusive = true;
+      objv.generate_new_write_ver(dpp->get_cct());
+      ldpp_dout(dpp, 20) << "creating initial latest_epoch=" << epoch
+          << " for period=" << period_id << dendl;
+    } else if (r < 0) {
+      ldpp_dout(dpp, 0) << "ERROR: failed to read latest_epoch" << dendl;
+      return r;
+    } else if (epoch <= existing_epoch) {
+      r = -EEXIST; // fail with EEXIST if epoch is not newer
+      ldpp_dout(dpp, 10) << "found existing latest_epoch " << existing_epoch
+          << " >= given epoch " << epoch << ", returning r=" << r << dendl;
+      return r;
+    } else {
+      ldpp_dout(dpp, 20) << "updating latest_epoch from " << existing_epoch
+          << " -> " << epoch << " on period=" << period_id << dendl;
+    }
+
+    r = write_latest_epoch(dpp, y, impl, exclusive, period_id, epoch, &objv);
+    if (r == -EEXIST) {
+      continue; // exclusive create raced with another update, retry
+    } else if (r == -ECANCELED) {
+      continue; // write raced with a conflicting version, retry
+    }
+    if (r < 0) {
+      ldpp_dout(dpp, 0) << "ERROR: failed to write latest_epoch" << dendl;
+      return r;
+    }
+    return 0; // return success
+  }
+
+  return -ECANCELED; // fail after max retries
+}
+
+int RadosConfigStore::create_period(const DoutPrefixProvider* dpp,
+                                    optional_yield y, bool exclusive,
+                                    const RGWPeriod& info)
+{
+  if (info.get_id().empty()) {
+    ldpp_dout(dpp, 0) << "period cannot have an empty id" << dendl;
+    return -EINVAL;
+  }
+  if (info.get_epoch() == 0) {
+    ldpp_dout(dpp, 0) << "period cannot have an empty epoch" << dendl;
+    return -EINVAL;
+  }
+  const auto& pool = impl->period_pool;
+  const auto info_oid = period_oid(info.get_id(), info.get_epoch());
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+  RGWObjVersionTracker objv;
+  objv.generate_new_write_ver(dpp->get_cct());
+  int r = impl->write(dpp, y, pool, info_oid, create, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  (void) update_latest_epoch(dpp, y, impl.get(), info.get_id(), info.get_epoch());
+  return 0;
+}
+
+int RadosConfigStore::read_period(const DoutPrefixProvider* dpp,
+                                  optional_yield y,
+                                  std::string_view period_id,
+                                  std::optional<uint32_t> epoch,
+                                  RGWPeriod& info)
+{
+  int r = 0;
+  if (!epoch) {
+    epoch = 0;
+    r = read_latest_epoch(dpp, y, impl.get(), period_id, *epoch, nullptr);
+    if (r < 0) {
+      return r;
+    }
+  }
+
+  const auto& pool = impl->period_pool;
+  const auto info_oid = period_oid(period_id, *epoch);
+  return impl->read(dpp, y, pool, info_oid, info, nullptr);
+}
+
+int RadosConfigStore::delete_period(const DoutPrefixProvider* dpp,
+                                    optional_yield y,
+                                    std::string_view period_id)
+{
+  const auto& pool = impl->period_pool;
+
+  // read the latest_epoch
+  uint32_t latest_epoch = 0;
+  RGWObjVersionTracker latest_objv;
+  int r = read_latest_epoch(dpp, y, impl.get(), period_id,
+                            latest_epoch, &latest_objv);
+  if (r < 0 && r != -ENOENT) { // just delete epoch=0 on ENOENT
+    ldpp_dout(dpp, 0) << "failed to read latest epoch for period "
+        << period_id << ": " << cpp_strerror(r) << dendl;
+    return r;
+  }
+
+  for (uint32_t epoch = 0; epoch <= latest_epoch; epoch++) {
+    const auto info_oid = period_oid(period_id, epoch);
+    r = impl->remove(dpp, y, pool, info_oid, nullptr);
+    if (r < 0 && r != -ENOENT) { // ignore ENOENT
+      ldpp_dout(dpp, 0) << "failed to delete period " << info_oid
+          << ": " << cpp_strerror(r) << dendl;
+      return r;
+    }
+  }
+
+  return delete_latest_epoch(dpp, y, impl.get(), period_id, &latest_objv);
+}
+
+int RadosConfigStore::list_period_ids(const DoutPrefixProvider* dpp,
+                                      optional_yield y,
+                                      const std::string& marker,
+                                      std::span<std::string> entries,
+                                      sal::ListResult<std::string>& result)
+{
+  const auto& pool = impl->period_pool;
+  constexpr auto prefix = [] (std::string oid) -> std::string {
+      if (!oid.starts_with(period_info_oid_prefix)) {
+        return {};
+      }
+      if (!oid.ends_with(period_latest_epoch_info_oid)) {
+        return {};
+      }
+      // trim the prefix and suffix
+      const std::size_t count = oid.size() -
+          period_info_oid_prefix.size() -
+          period_latest_epoch_info_oid.size();
+      return oid.substr(period_info_oid_prefix.size(), count);
+    };
+
+  return impl->list(dpp, y, pool, marker, prefix, entries, result);
+}
+
+} // namespace rgw::rados
diff --git a/src/rgw/store/rados/config/period_config.cc b/src/rgw/store/rados/config/period_config.cc
new file mode 100644 (file)
index 0000000..b17a48a
--- /dev/null
@@ -0,0 +1,55 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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_zone.h"
+#include "store/rados/config/store.h"
+
+#include "impl.h"
+
+namespace rgw::rados {
+
+// period config oids
+constexpr std::string_view period_config_prefix = "period_config.";
+constexpr std::string_view period_config_realm_default = "default";
+
+std::string period_config_oid(std::string_view realm_id)
+{
+  if (realm_id.empty()) {
+    realm_id = period_config_realm_default;
+  }
+  return string_cat_reserve(period_config_prefix, realm_id);
+}
+
+int RadosConfigStore::read_period_config(const DoutPrefixProvider* dpp,
+                                         optional_yield y,
+                                         std::string_view realm_id,
+                                         RGWPeriodConfig& info)
+{
+  const auto& pool = impl->period_pool;
+  const auto oid = period_config_oid(realm_id);
+  return impl->read(dpp, y, pool, oid, info, nullptr);
+}
+
+int RadosConfigStore::write_period_config(const DoutPrefixProvider* dpp,
+                                          optional_yield y, bool exclusive,
+                                          std::string_view realm_id,
+                                          const RGWPeriodConfig& info)
+{
+  const auto& pool = impl->period_pool;
+  const auto oid = period_config_oid(realm_id);
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+  return impl->write(dpp, y, pool, oid, create, info, nullptr);
+}
+
+} // namespace rgw::rados
diff --git a/src/rgw/store/rados/config/realm.cc b/src/rgw/store/rados/config/realm.cc
new file mode 100644 (file)
index 0000000..f62cb7a
--- /dev/null
@@ -0,0 +1,364 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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 "common/dout.h"
+#include "common/errno.h"
+#include "rgw_realm_watcher.h"
+#include "rgw_zone.h"
+#include "store/rados/config/store.h"
+
+#include "impl.h"
+
+namespace rgw::rados {
+
+// realm oids
+constexpr std::string_view realm_names_oid_prefix = "realms_names.";
+constexpr std::string_view realm_info_oid_prefix = "realms.";
+constexpr std::string_view realm_control_oid_suffix = ".control";
+constexpr std::string_view default_realm_info_oid = "default.realm";
+
+static std::string realm_info_oid(std::string_view realm_id)
+{
+  return string_cat_reserve(realm_info_oid_prefix, realm_id);
+}
+static std::string realm_name_oid(std::string_view realm_id)
+{
+  return string_cat_reserve(realm_names_oid_prefix, realm_id);
+}
+static std::string realm_control_oid(std::string_view realm_id)
+{
+  return string_cat_reserve(realm_info_oid_prefix, realm_id,
+                            realm_control_oid_suffix);
+}
+static std::string default_realm_oid(const ceph::common::ConfigProxy& conf)
+{
+  return std::string{name_or_default(conf->rgw_default_realm_info_oid,
+                                     default_realm_info_oid)};
+}
+
+
+int RadosConfigStore::write_default_realm_id(const DoutPrefixProvider* dpp,
+                                             optional_yield y, bool exclusive,
+                                             std::string_view realm_id)
+{
+  const auto& pool = impl->realm_pool;
+  const auto oid = default_realm_oid(dpp->get_cct()->_conf);
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+
+  RGWDefaultSystemMetaObjInfo default_info;
+  default_info.default_id = realm_id;
+
+  return impl->write(dpp, y, pool, oid, create, default_info, nullptr);
+}
+
+int RadosConfigStore::read_default_realm_id(const DoutPrefixProvider* dpp,
+                                            optional_yield y,
+                                            std::string& realm_id)
+{
+  const auto& pool = impl->realm_pool;
+  const auto oid = default_realm_oid(dpp->get_cct()->_conf);
+
+  RGWDefaultSystemMetaObjInfo default_info;
+  int r = impl->read(dpp, y, pool, oid, default_info, nullptr);
+  if (r >= 0) {
+    realm_id = default_info.default_id;
+  }
+  return r;
+}
+
+int RadosConfigStore::delete_default_realm_id(const DoutPrefixProvider* dpp,
+                                              optional_yield y)
+{
+  const auto& pool = impl->realm_pool;
+  const auto oid = default_realm_oid(dpp->get_cct()->_conf);
+
+  return impl->remove(dpp, y, pool, oid, nullptr);
+}
+
+
+class RadosRealmWriter : public sal::RealmWriter {
+  ConfigImpl* impl;
+  RGWObjVersionTracker objv;
+  std::string realm_id;
+  std::string realm_name;
+ public:
+  RadosRealmWriter(ConfigImpl* impl, RGWObjVersionTracker objv,
+                   std::string_view realm_id, std::string_view realm_name)
+    : impl(impl), objv(std::move(objv)),
+      realm_id(realm_id), realm_name(realm_name)
+  {
+  }
+
+  int write(const DoutPrefixProvider* dpp, optional_yield y,
+            const RGWRealm& info) override
+  {
+    if (realm_id != info.get_id() || realm_name != info.get_name()) {
+      return -EINVAL; // can't modify realm id or name directly
+    }
+
+    const auto& pool = impl->realm_pool;
+    const auto info_oid = realm_info_oid(info.get_id());
+    return impl->write(dpp, y, pool, info_oid, Create::MustExist, info, &objv);
+  }
+
+  int rename(const DoutPrefixProvider* dpp, optional_yield y,
+             RGWRealm& info, std::string_view new_name) override
+  {
+    if (realm_id != info.get_id() || realm_name != info.get_name()) {
+      return -EINVAL; // can't modify realm id or name directly
+    }
+    if (new_name.empty()) {
+      ldpp_dout(dpp, 0) << "realm cannot have an empty name" << dendl;
+      return -EINVAL;
+    }
+
+    const auto& pool = impl->realm_pool;
+    const auto name = RGWNameToId{info.get_id()};
+    const auto info_oid = realm_info_oid(info.get_id());
+    const auto old_oid = realm_name_oid(info.get_name());
+    const auto new_oid = realm_name_oid(new_name);
+
+    // link the new name
+    RGWObjVersionTracker new_objv;
+    new_objv.generate_new_write_ver(dpp->get_cct());
+    int r = impl->write(dpp, y, pool, new_oid, Create::MustNotExist,
+                        name, &new_objv);
+    if (r < 0) {
+      return r;
+    }
+
+    // write the info with updated name
+    info.set_name(std::string{new_name});
+    r = impl->write(dpp, y, pool, info_oid, Create::MustExist, info, &objv);
+    if (r < 0) {
+      // on failure, unlink the new name
+      (void) impl->remove(dpp, y, pool, new_oid, &new_objv);
+      return r;
+    }
+
+    // unlink the old name
+    (void) impl->remove(dpp, y, pool, old_oid, nullptr);
+
+    realm_name = new_name;
+    return 0;
+  }
+
+  int remove(const DoutPrefixProvider* dpp, optional_yield y) override
+  {
+    const auto& pool = impl->realm_pool;
+    const auto info_oid = realm_info_oid(realm_id);
+    int r = impl->remove(dpp, y, pool, info_oid, &objv);
+    if (r < 0) {
+      return r;
+    }
+    const auto name_oid = realm_name_oid(realm_name);
+    (void) impl->remove(dpp, y, pool, name_oid, nullptr);
+    const auto control_oid = realm_control_oid(realm_id);
+    (void) impl->remove(dpp, y, pool, control_oid, nullptr);
+    return 0;
+  }
+}; // RadosRealmWriter
+
+
+int RadosConfigStore::create_realm(const DoutPrefixProvider* dpp,
+                                   optional_yield y, bool exclusive,
+                                   const RGWRealm& info,
+                                   std::unique_ptr<sal::RealmWriter>* writer)
+{
+  if (info.get_id().empty()) {
+    ldpp_dout(dpp, 0) << "realm cannot have an empty id" << dendl;
+    return -EINVAL;
+  }
+  if (info.get_name().empty()) {
+    ldpp_dout(dpp, 0) << "realm cannot have an empty name" << dendl;
+    return -EINVAL;
+  }
+
+  const auto& pool = impl->realm_pool;
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+
+  // write the realm info
+  const auto info_oid = realm_info_oid(info.get_id());
+  RGWObjVersionTracker objv;
+  objv.generate_new_write_ver(dpp->get_cct());
+
+  int r = impl->write(dpp, y, pool, info_oid, create, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  // write the realm name
+  const auto name_oid = realm_name_oid(info.get_name());
+  const auto name = RGWNameToId{info.get_id()};
+  RGWObjVersionTracker name_objv;
+  name_objv.generate_new_write_ver(dpp->get_cct());
+
+  r = impl->write(dpp, y, pool, name_oid, create, name, &name_objv);
+  if (r < 0) {
+    (void) impl->remove(dpp, y, pool, info_oid, &objv);
+    return r;
+  }
+
+  // create control object for watch/notify
+  const auto control_oid = realm_control_oid(info.get_id());
+  bufferlist empty_bl;
+  r = impl->write(dpp, y, pool, control_oid, Create::MayExist,
+                  empty_bl, nullptr);
+  if (r < 0) {
+    (void) impl->remove(dpp, y, pool, name_oid, &name_objv);
+    (void) impl->remove(dpp, y, pool, info_oid, &objv);
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosRealmWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_realm_by_id(const DoutPrefixProvider* dpp,
+                                       optional_yield y,
+                                       std::string_view realm_id,
+                                       RGWRealm& info,
+                                       std::unique_ptr<sal::RealmWriter>* writer)
+{
+  const auto& pool = impl->realm_pool;
+  const auto info_oid = realm_info_oid(realm_id);
+  RGWObjVersionTracker objv;
+  int r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosRealmWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_realm_by_name(const DoutPrefixProvider* dpp,
+                                         optional_yield y,
+                                         std::string_view realm_name,
+                                         RGWRealm& info,
+                                         std::unique_ptr<sal::RealmWriter>* writer)
+{
+  const auto& pool = impl->realm_pool;
+
+  // look up realm id by name
+  RGWNameToId name;
+  const auto name_oid = realm_name_oid(realm_name);
+  int r = impl->read(dpp, y, pool, name_oid, name, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  const auto info_oid = realm_info_oid(name.obj_id);
+  RGWObjVersionTracker objv;
+  r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosRealmWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_default_realm(const DoutPrefixProvider* dpp,
+                                         optional_yield y,
+                                         RGWRealm& info,
+                                         std::unique_ptr<sal::RealmWriter>* writer)
+{
+  const auto& pool = impl->realm_pool;
+
+  // read default realm id
+  RGWDefaultSystemMetaObjInfo default_info;
+  const auto default_oid = default_realm_oid(dpp->get_cct()->_conf);
+  int r = impl->read(dpp, y, pool, default_oid, default_info, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  const auto info_oid = realm_info_oid(default_info.default_id);
+  RGWObjVersionTracker objv;
+  r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosRealmWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_realm_id(const DoutPrefixProvider* dpp,
+                                    optional_yield y,
+                                    std::string_view realm_name,
+                                    std::string& realm_id)
+{
+  const auto& pool = impl->realm_pool;
+  RGWNameToId name;
+
+  // look up realm id by name
+  const auto name_oid = realm_name_oid(realm_name);
+  int r = impl->read(dpp, y, pool, name_oid, name, nullptr);
+  if (r < 0) {
+    return r;
+  }
+  realm_id = std::move(name.obj_id);
+  return 0;
+}
+
+int RadosConfigStore::realm_notify_new_period(const DoutPrefixProvider* dpp,
+                                              optional_yield y,
+                                              const RGWPeriod& period)
+{
+  const auto& pool = impl->realm_pool;
+  const auto control_oid = realm_control_oid(period.get_realm());
+
+  bufferlist bl;
+  using ceph::encode;
+  // push the period to dependent zonegroups/zones
+  encode(RGWRealmNotify::ZonesNeedPeriod, bl);
+  encode(period, bl);
+  // reload the gateway with the new period
+  encode(RGWRealmNotify::Reload, bl);
+
+  constexpr uint64_t timeout_ms = 0;
+  return impl->notify(dpp, y, pool, control_oid, bl, timeout_ms);
+}
+
+int RadosConfigStore::list_realm_names(const DoutPrefixProvider* dpp,
+                                       optional_yield y,
+                                       const std::string& marker,
+                                       std::span<std::string> entries,
+                                       sal::ListResult<std::string>& result)
+{
+  const auto& pool = impl->realm_pool;
+  constexpr auto prefix = [] (std::string oid) -> std::string {
+      if (!oid.starts_with(realm_names_oid_prefix)) {
+        return {};
+      }
+      return oid.substr(realm_names_oid_prefix.size());
+    };
+  return impl->list(dpp, y, pool, marker, prefix, entries, result);
+}
+
+} // namespace rgw::rados
diff --git a/src/rgw/store/rados/config/store.cc b/src/rgw/store/rados/config/store.cc
new file mode 100644 (file)
index 0000000..ec2b034
--- /dev/null
@@ -0,0 +1,52 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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 "include/rados/librados.hpp"
+#include "common/errno.h"
+#include "impl.h"
+#include "store.h"
+
+namespace rgw::rados {
+
+RadosConfigStore::RadosConfigStore(std::unique_ptr<ConfigImpl> impl)
+  : impl(std::move(impl))
+{
+}
+
+RadosConfigStore::~RadosConfigStore() = default;
+
+
+auto create_config_store(const DoutPrefixProvider* dpp)
+    -> std::unique_ptr<RadosConfigStore>
+{
+  auto impl = std::make_unique<ConfigImpl>(dpp->get_cct()->_conf);
+
+  // initialize a Rados client
+  int r = impl->rados.init_with_context(dpp->get_cct());
+  if (r < 0) {
+    ldpp_dout(dpp, -1) << "Rados client initialization failed with "
+        << cpp_strerror(-r) << dendl;
+    return nullptr;
+  }
+  r = impl->rados.connect();
+  if (r < 0) {
+    ldpp_dout(dpp, -1) << "Rados client connection failed with "
+        << cpp_strerror(-r) << dendl;
+    return nullptr;
+  }
+
+  return std::make_unique<RadosConfigStore>(std::move(impl));
+}
+
+} // namespace rgw::rados
diff --git a/src/rgw/store/rados/config/store.h b/src/rgw/store/rados/config/store.h
new file mode 100644 (file)
index 0000000..1b93a80
--- /dev/null
@@ -0,0 +1,182 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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 <list>
+#include <memory>
+#include <string>
+#include "rgw_common.h"
+#include "rgw_sal_config.h"
+
+class DoutPrefixProvider;
+class optional_yield;
+
+namespace rgw::rados {
+
+struct ConfigImpl;
+
+class RadosConfigStore : public sal::ConfigStore {
+ public:
+  explicit RadosConfigStore(std::unique_ptr<ConfigImpl> impl);
+  virtual ~RadosConfigStore() override;
+
+  // Realm
+  virtual int write_default_realm_id(const DoutPrefixProvider* dpp,
+                                     optional_yield y, bool exclusive,
+                                     std::string_view realm_id) override;
+  virtual int read_default_realm_id(const DoutPrefixProvider* dpp,
+                                    optional_yield y,
+                                    std::string& realm_id) override;
+  virtual int delete_default_realm_id(const DoutPrefixProvider* dpp,
+                                      optional_yield y) override;
+
+  virtual int create_realm(const DoutPrefixProvider* dpp,
+                           optional_yield y, bool exclusive,
+                           const RGWRealm& info,
+                           std::unique_ptr<sal::RealmWriter>* writer) override;
+  virtual int read_realm_by_id(const DoutPrefixProvider* dpp,
+                               optional_yield y,
+                               std::string_view realm_id,
+                               RGWRealm& info,
+                               std::unique_ptr<sal::RealmWriter>* writer) override;
+  virtual int read_realm_by_name(const DoutPrefixProvider* dpp,
+                                 optional_yield y,
+                                 std::string_view realm_name,
+                                 RGWRealm& info,
+                                 std::unique_ptr<sal::RealmWriter>* writer) override;
+  virtual int read_default_realm(const DoutPrefixProvider* dpp,
+                                 optional_yield y,
+                                 RGWRealm& info,
+                                 std::unique_ptr<sal::RealmWriter>* writer) override;
+  virtual int read_realm_id(const DoutPrefixProvider* dpp,
+                            optional_yield y, std::string_view realm_name,
+                            std::string& realm_id) override;
+  virtual int realm_notify_new_period(const DoutPrefixProvider* dpp,
+                                      optional_yield y,
+                                      const RGWPeriod& period) override;
+  virtual int list_realm_names(const DoutPrefixProvider* dpp,
+                               optional_yield y, const std::string& marker,
+                               std::span<std::string> entries,
+                               sal::ListResult<std::string>& result) override;
+
+  // Period
+  virtual int create_period(const DoutPrefixProvider* dpp,
+                            optional_yield y, bool exclusive,
+                            const RGWPeriod& info) override;
+  virtual int read_period(const DoutPrefixProvider* dpp,
+                          optional_yield y, std::string_view period_id,
+                          std::optional<uint32_t> epoch, RGWPeriod& info) override;
+  virtual int delete_period(const DoutPrefixProvider* dpp,
+                            optional_yield y,
+                            std::string_view period_id) override;
+  virtual int list_period_ids(const DoutPrefixProvider* dpp,
+                              optional_yield y, const std::string& marker,
+                              std::span<std::string> entries,
+                              sal::ListResult<std::string>& result) override;
+
+  // ZoneGroup
+  virtual int write_default_zonegroup_id(const DoutPrefixProvider* dpp,
+                                         optional_yield y, bool exclusive,
+                                         std::string_view realm_id,
+                                         std::string_view zonegroup_id) override;
+  virtual int read_default_zonegroup_id(const DoutPrefixProvider* dpp,
+                                        optional_yield y,
+                                        std::string_view realm_id,
+                                        std::string& zonegroup_id) override;
+  virtual int delete_default_zonegroup_id(const DoutPrefixProvider* dpp,
+                                          optional_yield y,
+                                          std::string_view realm_id) override;
+
+  virtual int create_zonegroup(const DoutPrefixProvider* dpp,
+                               optional_yield y, bool exclusive,
+                               const RGWZoneGroup& info,
+                               std::unique_ptr<sal::ZoneGroupWriter>* writer) override;
+  virtual int read_zonegroup_by_id(const DoutPrefixProvider* dpp,
+                                   optional_yield y,
+                                   std::string_view zonegroup_id,
+                                   RGWZoneGroup& info,
+                                   std::unique_ptr<sal::ZoneGroupWriter>* writer) override;
+  virtual int read_zonegroup_by_name(const DoutPrefixProvider* dpp,
+                                     optional_yield y,
+                                     std::string_view zonegroup_name,
+                                     RGWZoneGroup& info,
+                                     std::unique_ptr<sal::ZoneGroupWriter>* writer) override;
+  virtual int read_default_zonegroup(const DoutPrefixProvider* dpp,
+                                     optional_yield y,
+                                     std::string_view realm_id,
+                                     RGWZoneGroup& info,
+                                     std::unique_ptr<sal::ZoneGroupWriter>* writer) override;
+  virtual int list_zonegroup_names(const DoutPrefixProvider* dpp,
+                                   optional_yield y, const std::string& marker,
+                                   std::span<std::string> entries,
+                                   sal::ListResult<std::string>& result) override;
+
+  // Zone
+  virtual int write_default_zone_id(const DoutPrefixProvider* dpp,
+                                    optional_yield y, bool exclusive,
+                                    std::string_view realm_id,
+                                    std::string_view zone_id) override;
+  virtual int read_default_zone_id(const DoutPrefixProvider* dpp,
+                                   optional_yield y,
+                                   std::string_view realm_id,
+                                   std::string& zone_id) override;
+  virtual int delete_default_zone_id(const DoutPrefixProvider* dpp,
+                                     optional_yield y,
+                                     std::string_view realm_id) override;
+
+  virtual int create_zone(const DoutPrefixProvider* dpp,
+                          optional_yield y, bool exclusive,
+                          const RGWZoneParams& info,
+                          std::unique_ptr<sal::ZoneWriter>* writer) override;
+  virtual int read_zone_by_id(const DoutPrefixProvider* dpp,
+                              optional_yield y,
+                              std::string_view zone_id,
+                              RGWZoneParams& info,
+                              std::unique_ptr<sal::ZoneWriter>* writer) override;
+  virtual int read_zone_by_name(const DoutPrefixProvider* dpp,
+                                optional_yield y,
+                                std::string_view zone_name,
+                                RGWZoneParams& info,
+                                std::unique_ptr<sal::ZoneWriter>* writer) override;
+  virtual int read_default_zone(const DoutPrefixProvider* dpp,
+                                optional_yield y,
+                                std::string_view realm_id,
+                                RGWZoneParams& info,
+                                std::unique_ptr<sal::ZoneWriter>* writer) override;
+  virtual int list_zone_names(const DoutPrefixProvider* dpp,
+                              optional_yield y, const std::string& marker,
+                              std::span<std::string> entries,
+                              sal::ListResult<std::string>& result) override;
+
+  // PeriodConfig
+  virtual int read_period_config(const DoutPrefixProvider* dpp,
+                                 optional_yield y,
+                                 std::string_view realm_id,
+                                 RGWPeriodConfig& info) override;
+  virtual int write_period_config(const DoutPrefixProvider* dpp,
+                                  optional_yield y, bool exclusive,
+                                  std::string_view realm_id,
+                                  const RGWPeriodConfig& info) override;
+
+ private:
+  std::unique_ptr<ConfigImpl> impl;
+}; // RadosConfigStore
+
+
+/// RadosConfigStore factory function
+auto create_config_store(const DoutPrefixProvider* dpp)
+    -> std::unique_ptr<RadosConfigStore>;
+
+} // namespace rgw::rados
diff --git a/src/rgw/store/rados/config/zone.cc b/src/rgw/store/rados/config/zone.cc
new file mode 100644 (file)
index 0000000..c1151f5
--- /dev/null
@@ -0,0 +1,312 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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 "common/dout.h"
+#include "common/errno.h"
+#include "rgw_zone.h"
+#include "store/rados/config/store.h"
+
+#include "impl.h"
+
+namespace rgw::rados {
+
+// zone oids
+constexpr std::string_view zone_info_oid_prefix = "zone_info.";
+constexpr std::string_view zone_names_oid_prefix = "zone_names.";
+
+std::string zone_info_oid(std::string_view zone_id)
+{
+  return string_cat_reserve(zone_info_oid_prefix, zone_id);
+}
+std::string zone_name_oid(std::string_view zone_id)
+{
+  return string_cat_reserve(zone_names_oid_prefix, zone_id);
+}
+std::string default_zone_oid(const ceph::common::ConfigProxy& conf,
+                             std::string_view realm_id)
+{
+  return fmt::format("{}.{}", conf->rgw_default_zone_info_oid, realm_id);
+}
+
+
+int RadosConfigStore::write_default_zone_id(const DoutPrefixProvider* dpp,
+                                            optional_yield y,
+                                            bool exclusive,
+                                            std::string_view realm_id,
+                                            std::string_view zone_id)
+{
+  const auto& pool = impl->zone_pool;
+  const auto default_oid = default_zone_oid(dpp->get_cct()->_conf, realm_id);
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+
+  RGWDefaultSystemMetaObjInfo default_info;
+  default_info.default_id = zone_id;
+
+  return impl->write(dpp, y, pool, default_oid, create, default_info, nullptr);
+}
+
+int RadosConfigStore::read_default_zone_id(const DoutPrefixProvider* dpp,
+                                           optional_yield y,
+                                           std::string_view realm_id,
+                                           std::string& zone_id)
+{
+  const auto& pool = impl->zone_pool;
+  const auto default_oid = default_zone_oid(dpp->get_cct()->_conf, realm_id);
+
+  RGWDefaultSystemMetaObjInfo default_info;
+  int r = impl->read(dpp, y, pool, default_oid, default_info, nullptr);
+  if (r >= 0) {
+    zone_id = default_info.default_id;
+  }
+  return r;
+}
+
+int RadosConfigStore::delete_default_zone_id(const DoutPrefixProvider* dpp,
+                                             optional_yield y,
+                                             std::string_view realm_id)
+{
+  const auto& pool = impl->zone_pool;
+  const auto default_oid = default_zone_oid(dpp->get_cct()->_conf, realm_id);
+
+  return impl->remove(dpp, y, pool, default_oid, nullptr);
+}
+
+
+class RadosZoneWriter : public sal::ZoneWriter {
+  ConfigImpl* impl;
+  RGWObjVersionTracker objv;
+  std::string zone_id;
+  std::string zone_name;
+ public:
+  RadosZoneWriter(ConfigImpl* impl, RGWObjVersionTracker objv,
+                  std::string_view zone_id, std::string_view zone_name)
+      : impl(impl), objv(std::move(objv)),
+        zone_id(zone_id), zone_name(zone_name)
+  {
+  }
+
+  int write(const DoutPrefixProvider* dpp, optional_yield y,
+            const RGWZoneParams& info) override
+  {
+    if (zone_id != info.get_id() || zone_name != info.get_name()) {
+      return -EINVAL; // can't modify zone id or name directly
+    }
+
+    const auto& pool = impl->zone_pool;
+    const auto info_oid = zone_info_oid(info.get_id());
+    return impl->write(dpp, y, pool, info_oid, Create::MustExist, info, &objv);
+  }
+
+  int rename(const DoutPrefixProvider* dpp, optional_yield y,
+             RGWZoneParams& info, std::string_view new_name) override
+  {
+    if (zone_id != info.get_id() || zone_name != info.get_name()) {
+      return -EINVAL; // can't modify zone id or name directly
+    }
+    if (new_name.empty()) {
+      ldpp_dout(dpp, 0) << "zone cannot have an empty name" << dendl;
+      return -EINVAL;
+    }
+
+    const auto& pool = impl->zone_pool;
+    const auto name = RGWNameToId{info.get_id()};
+    const auto info_oid = zone_info_oid(info.get_id());
+    const auto old_oid = zone_name_oid(info.get_name());
+    const auto new_oid = zone_name_oid(new_name);
+
+    // link the new name
+    RGWObjVersionTracker new_objv;
+    new_objv.generate_new_write_ver(dpp->get_cct());
+    int r = impl->write(dpp, y, pool, new_oid, Create::MustNotExist,
+                        name, &new_objv);
+    if (r < 0) {
+      return r;
+    }
+
+    // write the info with updated name
+    info.set_name(std::string{new_name});
+    r = impl->write(dpp, y, pool, info_oid, Create::MustExist, info, &objv);
+    if (r < 0) {
+      // on failure, unlink the new name
+      (void) impl->remove(dpp, y, pool, new_oid, &new_objv);
+      return r;
+    }
+
+    // unlink the old name
+    (void) impl->remove(dpp, y, pool, old_oid, nullptr);
+
+    zone_name = new_name;
+    return 0;
+  }
+
+  int remove(const DoutPrefixProvider* dpp, optional_yield y) override
+  {
+    const auto& pool = impl->zone_pool;
+    const auto info_oid = zone_info_oid(zone_id);
+    int r = impl->remove(dpp, y, pool, info_oid, &objv);
+    if (r < 0) {
+      return r;
+    }
+    const auto name_oid = zone_name_oid(zone_name);
+    (void) impl->remove(dpp, y, pool, name_oid, nullptr);
+    return 0;
+  }
+}; // RadosZoneWriter
+
+
+int RadosConfigStore::create_zone(const DoutPrefixProvider* dpp,
+                                  optional_yield y, bool exclusive,
+                                  const RGWZoneParams& info,
+                                  std::unique_ptr<sal::ZoneWriter>* writer)
+{
+  if (info.get_id().empty()) {
+    ldpp_dout(dpp, 0) << "zone cannot have an empty id" << dendl;
+    return -EINVAL;
+  }
+  if (info.get_name().empty()) {
+    ldpp_dout(dpp, 0) << "zone cannot have an empty name" << dendl;
+    return -EINVAL;
+  }
+
+  const auto& pool = impl->zone_pool;
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+
+  // write the zone info
+  const auto info_oid = zone_info_oid(info.get_id());
+  RGWObjVersionTracker objv;
+  objv.generate_new_write_ver(dpp->get_cct());
+
+  int r = impl->write(dpp, y, pool, info_oid, create, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  // write the zone name
+  const auto name_oid = zone_name_oid(info.get_name());
+  const auto name = RGWNameToId{info.get_id()};
+  RGWObjVersionTracker name_objv;
+  name_objv.generate_new_write_ver(dpp->get_cct());
+
+  r = impl->write(dpp, y, pool, name_oid, create, name, &name_objv);
+  if (r < 0) {
+    (void) impl->remove(dpp, y, pool, info_oid, &objv);
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosZoneWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_zone_by_id(const DoutPrefixProvider* dpp,
+                                      optional_yield y,
+                                      std::string_view zone_id,
+                                      RGWZoneParams& info,
+                                      std::unique_ptr<sal::ZoneWriter>* writer)
+{
+  const auto& pool = impl->zone_pool;
+  const auto info_oid = zone_info_oid(zone_id);
+  RGWObjVersionTracker objv;
+
+  int r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosZoneWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_zone_by_name(const DoutPrefixProvider* dpp,
+                                        optional_yield y,
+                                        std::string_view zone_name,
+                                        RGWZoneParams& info,
+                                        std::unique_ptr<sal::ZoneWriter>* writer)
+{
+  const auto& pool = impl->zone_pool;
+
+  // look up zone id by name
+  const auto name_oid = zone_name_oid(zone_name);
+  RGWNameToId name;
+  int r = impl->read(dpp, y, pool, name_oid, name, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  const auto info_oid = zone_info_oid(name.obj_id);
+  RGWObjVersionTracker objv;
+  r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosZoneWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_default_zone(const DoutPrefixProvider* dpp,
+                                        optional_yield y,
+                                        std::string_view realm_id,
+                                        RGWZoneParams& info,
+                                        std::unique_ptr<sal::ZoneWriter>* writer)
+{
+  const auto& pool = impl->zone_pool;
+
+  // read default zone id
+  const auto default_oid = default_zone_oid(dpp->get_cct()->_conf, realm_id);
+  RGWDefaultSystemMetaObjInfo default_info;
+  int r = impl->read(dpp, y, pool, default_oid, default_info, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  const auto info_oid = zone_info_oid(default_info.default_id);
+  RGWObjVersionTracker objv;
+  r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosZoneWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::list_zone_names(const DoutPrefixProvider* dpp,
+                                      optional_yield y,
+                                      const std::string& marker,
+                                      std::span<std::string> entries,
+                                      sal::ListResult<std::string>& result)
+{
+  const auto& pool = impl->zone_pool;
+  constexpr auto prefix = [] (std::string oid) -> std::string {
+      if (!oid.starts_with(zone_names_oid_prefix)) {
+        return {};
+      }
+      return oid.substr(zone_names_oid_prefix.size());
+    };
+  return impl->list(dpp, y, pool, marker, prefix, entries, result);
+}
+
+} // namespace rgw::rados
diff --git a/src/rgw/store/rados/config/zonegroup.cc b/src/rgw/store/rados/config/zonegroup.cc
new file mode 100644 (file)
index 0000000..984fda1
--- /dev/null
@@ -0,0 +1,315 @@
+// vim: ts=8 sw=2 smarttab ft=cpp
+
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * 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 "common/dout.h"
+#include "common/errno.h"
+#include "rgw_zone.h"
+#include "store/rados/config/store.h"
+
+#include "impl.h"
+
+namespace rgw::rados {
+
+// zonegroup oids
+constexpr std::string_view zonegroup_names_oid_prefix = "zonegroups_names.";
+constexpr std::string_view zonegroup_info_oid_prefix = "zonegroup_info.";
+constexpr std::string_view default_zonegroup_info_oid = "default.zonegroup";
+
+static std::string zonegroup_info_oid(std::string_view zonegroup_id)
+{
+  return string_cat_reserve(zonegroup_info_oid_prefix, zonegroup_id);
+}
+static std::string zonegroup_name_oid(std::string_view zonegroup_id)
+{
+  return string_cat_reserve(zonegroup_names_oid_prefix, zonegroup_id);
+}
+static std::string default_zonegroup_oid(const ceph::common::ConfigProxy& conf,
+                                         std::string_view realm_id)
+{
+  const auto prefix = name_or_default(conf->rgw_default_zonegroup_info_oid,
+                                      default_zonegroup_info_oid);
+  return fmt::format("{}.{}", prefix, realm_id);
+}
+
+
+int RadosConfigStore::write_default_zonegroup_id(const DoutPrefixProvider* dpp,
+                                                 optional_yield y,
+                                                 bool exclusive,
+                                                 std::string_view realm_id,
+                                                 std::string_view zonegroup_id)
+{
+  const auto& pool = impl->zonegroup_pool;
+  const auto oid = default_zonegroup_oid(dpp->get_cct()->_conf, realm_id);
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+
+  RGWDefaultSystemMetaObjInfo default_info;
+  default_info.default_id = zonegroup_id;
+
+  return impl->write(dpp, y, pool, oid, create, default_info, nullptr);
+}
+
+int RadosConfigStore::read_default_zonegroup_id(const DoutPrefixProvider* dpp,
+                                                optional_yield y,
+                                                std::string_view realm_id,
+                                                std::string& zonegroup_id)
+{
+  const auto& pool = impl->zonegroup_pool;
+  const auto oid = default_zonegroup_oid(dpp->get_cct()->_conf, realm_id);
+
+  RGWDefaultSystemMetaObjInfo default_info;
+  int r = impl->read(dpp, y, pool, oid, default_info, nullptr);
+  if (r >= 0) {
+    zonegroup_id = default_info.default_id;
+  }
+  return r;
+}
+
+int RadosConfigStore::delete_default_zonegroup_id(const DoutPrefixProvider* dpp,
+                                                  optional_yield y,
+                                                  std::string_view realm_id)
+{
+  const auto& pool = impl->zonegroup_pool;
+  const auto oid = default_zonegroup_oid(dpp->get_cct()->_conf, realm_id);
+  return impl->remove(dpp, y, pool, oid, nullptr);
+}
+
+
+class RadosZoneGroupWriter : public sal::ZoneGroupWriter {
+  ConfigImpl* impl;
+  RGWObjVersionTracker objv;
+  std::string zonegroup_id;
+  std::string zonegroup_name;
+ public:
+  RadosZoneGroupWriter(ConfigImpl* impl, RGWObjVersionTracker objv,
+                       std::string_view zonegroup_id,
+                       std::string_view zonegroup_name)
+    : impl(impl), objv(std::move(objv)),
+      zonegroup_id(zonegroup_id), zonegroup_name(zonegroup_name)
+  {
+  }
+
+  int write(const DoutPrefixProvider* dpp, optional_yield y,
+            const RGWZoneGroup& info) override
+  {
+    if (zonegroup_id != info.get_id() || zonegroup_name != info.get_name()) {
+      return -EINVAL; // can't modify zonegroup id or name directly
+    }
+
+    const auto& pool = impl->zonegroup_pool;
+    const auto info_oid = zonegroup_info_oid(info.get_id());
+    return impl->write(dpp, y, pool, info_oid, Create::MustExist, info, &objv);
+  }
+
+  int rename(const DoutPrefixProvider* dpp, optional_yield y,
+             RGWZoneGroup& info, std::string_view new_name) override
+  {
+    if (zonegroup_id != info.get_id() || zonegroup_name != info.get_name()) {
+      return -EINVAL; // can't modify zonegroup id or name directly
+    }
+    if (new_name.empty()) {
+      ldpp_dout(dpp, 0) << "zonegroup cannot have an empty name" << dendl;
+      return -EINVAL;
+    }
+
+    const auto& pool = impl->zonegroup_pool;
+    const auto name = RGWNameToId{info.get_id()};
+    const auto info_oid = zonegroup_info_oid(info.get_id());
+    const auto old_oid = zonegroup_name_oid(info.get_name());
+    const auto new_oid = zonegroup_name_oid(new_name);
+
+    // link the new name
+    RGWObjVersionTracker new_objv;
+    new_objv.generate_new_write_ver(dpp->get_cct());
+    int r = impl->write(dpp, y, pool, new_oid, Create::MustNotExist,
+                        name, &new_objv);
+    if (r < 0) {
+      return r;
+    }
+
+    // write the info with updated name
+    info.set_name(std::string{new_name});
+    r = impl->write(dpp, y, pool, info_oid, Create::MustExist, info, &objv);
+    if (r < 0) {
+      // on failure, unlink the new name
+      (void) impl->remove(dpp, y, pool, new_oid, &new_objv);
+      return r;
+    }
+
+    // unlink the old name
+    (void) impl->remove(dpp, y, pool, old_oid, nullptr);
+
+    zonegroup_name = new_name;
+    return 0;
+  }
+
+  int remove(const DoutPrefixProvider* dpp, optional_yield y) override
+  {
+    const auto& pool = impl->zonegroup_pool;
+    const auto info_oid = zonegroup_info_oid(zonegroup_id);
+    int r = impl->remove(dpp, y, pool, info_oid, &objv);
+    if (r < 0) {
+      return r;
+    }
+    const auto name_oid = zonegroup_name_oid(zonegroup_name);
+    (void) impl->remove(dpp, y, pool, name_oid, nullptr);
+    return 0;
+  }
+}; // RadosZoneGroupWriter
+
+
+int RadosConfigStore::create_zonegroup(const DoutPrefixProvider* dpp,
+                                       optional_yield y, bool exclusive,
+                                       const RGWZoneGroup& info,
+                                       std::unique_ptr<sal::ZoneGroupWriter>* writer)
+{
+  if (info.get_id().empty()) {
+    ldpp_dout(dpp, 0) << "zonegroup cannot have an empty id" << dendl;
+    return -EINVAL;
+  }
+  if (info.get_name().empty()) {
+    ldpp_dout(dpp, 0) << "zonegroup cannot have an empty name" << dendl;
+    return -EINVAL;
+  }
+
+  const auto& pool = impl->zonegroup_pool;
+  const auto create = exclusive ? Create::MustNotExist : Create::MayExist;
+
+  // write the zonegroup info
+  const auto info_oid = zonegroup_info_oid(info.get_id());
+  RGWObjVersionTracker objv;
+  objv.generate_new_write_ver(dpp->get_cct());
+
+  int r = impl->write(dpp, y, pool, info_oid, create, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  // write the zonegroup name
+  const auto name_oid = zonegroup_name_oid(info.get_name());
+  const auto name = RGWNameToId{info.get_id()};
+  RGWObjVersionTracker name_objv;
+  name_objv.generate_new_write_ver(dpp->get_cct());
+
+  r = impl->write(dpp, y, pool, name_oid, create, name, &name_objv);
+  if (r < 0) {
+    (void) impl->remove(dpp, y, pool, info_oid, &objv);
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosZoneGroupWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_zonegroup_by_id(const DoutPrefixProvider* dpp,
+                                           optional_yield y,
+                                           std::string_view zonegroup_id,
+                                           RGWZoneGroup& info,
+                                           std::unique_ptr<sal::ZoneGroupWriter>* writer)
+{
+  const auto& pool = impl->zonegroup_pool;
+  const auto info_oid = zonegroup_info_oid(zonegroup_id);
+  RGWObjVersionTracker objv;
+
+  int r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosZoneGroupWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_zonegroup_by_name(const DoutPrefixProvider* dpp,
+                                             optional_yield y,
+                                             std::string_view zonegroup_name,
+                                             RGWZoneGroup& info,
+                                             std::unique_ptr<sal::ZoneGroupWriter>* writer)
+{
+  const auto& pool = impl->zonegroup_pool;
+
+  // look up zonegroup id by name
+  RGWNameToId name;
+  const auto name_oid = zonegroup_name_oid(zonegroup_name);
+  int r = impl->read(dpp, y, pool, name_oid, name, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  const auto info_oid = zonegroup_info_oid(name.obj_id);
+  RGWObjVersionTracker objv;
+  r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosZoneGroupWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::read_default_zonegroup(const DoutPrefixProvider* dpp,
+                                             optional_yield y,
+                                             std::string_view realm_id,
+                                             RGWZoneGroup& info,
+                                             std::unique_ptr<sal::ZoneGroupWriter>* writer)
+{
+  const auto& pool = impl->zonegroup_pool;
+
+  // read default zonegroup id
+  RGWDefaultSystemMetaObjInfo default_info;
+  const auto default_oid = default_zonegroup_oid(dpp->get_cct()->_conf, realm_id);
+  int r = impl->read(dpp, y, pool, default_oid, default_info, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  const auto info_oid = zonegroup_info_oid(default_info.default_id);
+  RGWObjVersionTracker objv;
+  r = impl->read(dpp, y, pool, info_oid, info, &objv);
+  if (r < 0) {
+    return r;
+  }
+
+  if (writer) {
+    *writer = std::make_unique<RadosZoneGroupWriter>(
+        impl.get(), std::move(objv), info.get_id(), info.get_name());
+  }
+  return 0;
+}
+
+int RadosConfigStore::list_zonegroup_names(const DoutPrefixProvider* dpp,
+                                           optional_yield y,
+                                           const std::string& marker,
+                                           std::span<std::string> entries,
+                                           sal::ListResult<std::string>& result)
+{
+  const auto& pool = impl->zonegroup_pool;
+  constexpr auto prefix = [] (std::string oid) -> std::string {
+      if (!oid.starts_with(zonegroup_names_oid_prefix)) {
+        return {};
+      }
+      return oid.substr(zonegroup_names_oid_prefix.size());
+    };
+  return impl->list(dpp, y, pool, marker, prefix, entries, result);
+}
+
+} // namespace rgw::rados