]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
POC for change in RBD import migration
authorLeonid Chernin <leonidc@il.ibm.com>
Mon, 2 Feb 2026 06:01:14 +0000 (08:01 +0200)
committerLeonid Chernin <leonidc@il.ibm.com>
Thu, 19 Feb 2026 14:19:18 +0000 (16:19 +0200)
version = 2

Signed-off-by: Leonid Chernin <leonidc@il.ibm.com>
src/librbd/api/Migration.cc
src/librbd/api/Migration.h
src/librbd/migration/OpenSourceImageRequest.cc
src/librbd/migration/OpenSourceImageRequest.h
src/mon/MonCommands.h

index 34cd0f1336903cc23ce323c4b8802843fbe4408c..7ceefd2282bf39189eff2df1585a562f074ab2d0 100644 (file)
@@ -39,7 +39,7 @@
 #include "librbd/migration/NativeFormat.h"
 #include "librbd/mirror/DisableRequest.h"
 #include "librbd/mirror/EnableRequest.h"
-
+//#include <rados/librados.hpp>
 #include <boost/scope_exit.hpp>
 
 #include <shared_mutex> // for std::shared_lock
@@ -515,6 +515,104 @@ int Migration<I>::prepare(librados::IoCtx& io_ctx,
   return r;
 }
 
+constexpr const char* SPEC_SECRET_KEY = "secret_key";
+
+static int get_config_key(librados::Rados& rados, const std::string& key,
+                   std::string* value) {
+  std::string cmd =
+    "{"
+      "\"prefix\": \"config-key get\", "
+      "\"key\": \"" + key + "\""
+    "}";
+
+  bufferlist out_bl;
+
+  int r = rados.mon_command(std::move(cmd), {}, &out_bl, nullptr);
+  if (r == -EINVAL) {
+    return -EOPNOTSUPP;
+  } else if (r < 0 && r != -ENOENT) {
+    return r;
+  }
+
+  *value = out_bl.to_str();
+  return 0;
+}
+
+static int set_config_key(librados::Rados& rados, const std::string& key,
+                   const std::string& value) {
+  std::string cmd;
+  if (value.empty()) {
+    cmd = "{"
+            "\"prefix\": \"config-key rm\", "
+            "\"key\": \"" + key + "\""
+          "}";
+  } else {
+    cmd = "{"
+            "\"prefix\": \"config-key set\", "
+            "\"key\": \"" + key + "\", "
+            "\"val\": \"" + value + "\""
+          "}";
+  }
+  bufferlist out_bl;
+
+  int r = rados.mon_command(std::move(cmd), {}, &out_bl, nullptr);
+  if (r == -EINVAL) {
+    return -EOPNOTSUPP;
+  } else if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+static int try_get_fsid_secret_key(json_spirit::mObject& source_spec_object,
+                                  librados::IoCtx& dest_io_ctx) {
+    std::string secret_key;
+    auto cct = reinterpret_cast<CephContext *>(dest_io_ctx.cct());
+    auto it = source_spec_object.find("secret_key");
+    if (it != source_spec_object.end()) {
+      try {
+        secret_key = it->second.get_str();
+        ldout(cct, 5) << "found secret_key in source spec " << secret_key << dendl;
+      } catch (std::runtime_error&) {
+        lderr(cct) << "secret_key must be a string" << dendl;
+        return -EINVAL;
+      }
+
+      std::string fsid;
+      auto it_fsid = source_spec_object.find("source_cluster_fsid");
+      if (it_fsid != source_spec_object.end()) {
+        fsid = it_fsid->second.get_str();
+        ldout(cct, 5) << "found fsid in source spec " << fsid << dendl;
+      } else {
+        lderr(cct) << "source_cluster_fsid missing in spec" << dendl;
+        return -EINVAL;
+      }
+      source_spec_object.erase("secret_key"); // need to remove the key from the spec
+
+      librados::Rados rados(dest_io_ctx);
+      int r = set_config_key(rados,  "migration/fsid/" + fsid, secret_key);
+      if (r < 0) {
+       lderr(reinterpret_cast<CephContext*>(rados.cct()))
+           << "failed to store secret key in monitor: "
+           << cpp_strerror(r) << dendl;
+       return r;
+      }
+      ldout(cct, 5) << "key set " <<  fsid << dendl;
+      std::string value;
+      r = get_config_key(rados, "migration/fsid/" + fsid, &value);
+      if (r < 0) {
+        lderr(reinterpret_cast<CephContext*>(rados.cct()))
+           << "failed to fetch secret key from the monitor: "
+           << cpp_strerror(r) << dendl;
+        return r;
+      }
+      ldout(cct, 5) << " get value by key " <<  fsid <<" got "<< value << dendl;
+    }
+    return 0;
+}
+
+
 template <typename I>
 int Migration<I>::prepare_import(
     const std::string& source_spec, librados::IoCtx& dest_io_ctx,
@@ -529,16 +627,35 @@ int Migration<I>::prepare_import(
                  << dest_io_ctx.get_pool_name() << "/"
                  << dest_image_name << ", opts=" << opts << dendl;
 
+  std::string sanitized_source_spec = source_spec;
+  // use json-spirit to clean-up json formatting
+  json_spirit::mObject source_spec_object;
+  json_spirit::mValue json_root;
+  if(json_spirit::read(source_spec, json_root)) {
+    try {
+      source_spec_object = json_root.get_obj();
+    } catch (std::runtime_error&) {
+      lderr(cct) << "failed to clean source spec" << dendl;
+      return -EINVAL;
+    }
+  }
+
+  int r = try_get_fsid_secret_key(source_spec_object,  dest_io_ctx);
+  if (r == 0) {
+   sanitized_source_spec = json_spirit::write(source_spec_object);
+   ldout(cct, 5) << "sanitized source spec: "
+                  << sanitized_source_spec << dendl;
+  }
   I* src_image_ctx;
   librados::Rados* src_rados;
   C_SaferCond open_ctx;
   auto req = migration::OpenSourceImageRequest<I>::create(
     dest_io_ctx, nullptr, CEPH_NOSNAP,
-    {-1, "", "", "", source_spec, {}, 0, false}, &src_image_ctx, &src_rados,
+    {-1, "", "", "", sanitized_source_spec, {}, 0, false}, &src_image_ctx, &src_rados,
     &open_ctx);
   req->send();
 
-  int r = open_ctx.wait();
+  r = open_ctx.wait();
   if (r < 0) {
     lderr(cct) << "failed to open source image: " << cpp_strerror(r) << dendl;
     return r;
@@ -561,17 +678,6 @@ int Migration<I>::prepare_import(
 
   ldout(cct, 20) << "updated opts=" << opts << dendl;
 
-  // use json-spirit to clean-up json formatting
-  json_spirit::mObject source_spec_object;
-  json_spirit::mValue json_root;
-  if(json_spirit::read(source_spec, json_root)) {
-    try {
-      source_spec_object = json_root.get_obj();
-    } catch (std::runtime_error&) {
-      lderr(cct) << "failed to clean source spec" << dendl;
-      return -EINVAL;
-    }
-  }
 
   auto dst_image_ctx = I::create(
     dest_image_name, util::generate_image_id(dest_io_ctx), nullptr,
@@ -587,6 +693,48 @@ int Migration<I>::prepare_import(
   return migration.prepare_import();
 }
 
+template <typename I>
+int Migration<I>::cleanup_secret_from_kv(const std::string& source_spec) {
+  json_spirit::mObject obj;
+  json_spirit::mValue root;
+  CephContext* cct = m_dst_image_ctx->cct;
+  if (!json_spirit::read(source_spec, root)) {
+    return 0; // spec already sanitized or legacy path
+  }
+
+  try {
+    obj = root.get_obj();
+  } catch (...) {
+    return 0;
+  }
+
+  auto it = obj.find("source_cluster_fsid");
+  if (it == obj.end()) {
+    return 0;
+  }
+
+  std::string fsid = it->second.get_str();
+  std::string key = "migration/fsid/" + fsid;
+
+  librados::bufferlist in, out;
+  json_spirit::mObject in_obj;
+  in_obj["key"] = key;
+  in.append(json_spirit::write(in_obj));
+
+  //int r = m_dst_image_ctx->md_ctx.get_cluster()->mon_command(
+ //     "config-key del", std::move(in), &out, nullptr);
+  librados::Rados rados(m_dst_image_ctx->md_ctx);
+  int r = set_config_key(rados, key, "");
+  if (r !=0) {
+      lderr(cct) << "failed to remove the key  " << key
+                 << dendl;
+  } else {
+      ldout(cct, 5) << "successfully removed key" << key << dendl;
+  }
+  return r;
+}
+
+
 template <typename I>
 int Migration<I>::execute(librados::IoCtx& io_ctx,
                           const std::string &image_name,
@@ -643,9 +791,10 @@ int Migration<I>::execute(librados::IoCtx& io_ctx,
                       opts, &prog_ctx);
   r = migration.execute();
   if (r < 0) {
-    return r;
+      migration.cleanup_secret_from_kv(dst_migration_spec.source_spec);
+      return r;
   }
-
+  migration.cleanup_secret_from_kv(dst_migration_spec.source_spec);
   return 0;
 }
 
index f5a361cab98977d29057bbedb0e400add3edbf34..b1f2832b7bce7fdc2ad7cdbe36e16454bd4f8a7c 100644 (file)
@@ -37,6 +37,7 @@ public:
                     image_migration_status_t *status);
 
   static int get_source_spec(ImageCtxT* image_ctx, std::string* source_spec);
+  int cleanup_secret_from_kv(const std::string& source_spec);
 
 private:
   CephContext* m_cct;
index 3288a813e9f7bd6bf7f70a1c5ed324e679d351ef..e9f8c08a5ba3e36bbd413908c9fbedc3960225ad 100644 (file)
 namespace librbd {
 namespace migration {
 
+  static int get_config_key(librados::Rados& rados, const std::string& key,
+                     std::string* value) {
+  std::string cmd =
+    "{"
+      "\"prefix\": \"config-key get\", "
+      "\"key\": \"" + key + "\""
+    "}";
+
+  bufferlist out_bl;
+
+  int r = rados.mon_command(std::move(cmd), {}, &out_bl, nullptr);
+  if (r == -EINVAL) {
+    return -EOPNOTSUPP;
+  } else if (r < 0 && r != -ENOENT) {
+    return r;
+  }
+
+  *value = out_bl.to_str();
+  return 0;
+}
+
 template <typename I>
 OpenSourceImageRequest<I>::OpenSourceImageRequest(
     librados::IoCtx& dst_io_ctx, I* dst_image_ctx, uint64_t src_snap_id,
@@ -70,12 +91,58 @@ void OpenSourceImageRequest<I>::send() {
   }
 }
 
+template <typename I>
+int OpenSourceImageRequest<I>::inject_remote_cluster_creds(
+     CephContext* src_cct, const std::string& mon_host,
+     const std::string& fsid, const std::string& secret_key) {
+  return 0;
+}
+
 template <typename I>
 void OpenSourceImageRequest<I>::open_native(
     const json_spirit::mObject& source_spec_object, bool import_only) {
   ldout(m_cct, 10) << dendl;
-
-  int r = NativeFormat<I>::create_image_ctx(m_dst_io_ctx, source_spec_object,
+  std::string fsid;
+  std::string mon_host;
+  int r;
+  auto it_fsid = source_spec_object.find("source_cluster_fsid");
+  if (it_fsid != source_spec_object.end()) {
+    fsid = it_fsid->second.get_str();
+    mon_host = source_spec_object.at("mon_host").get_str();
+    ldout(m_cct, 5) << "open_native: found fsid in source spec " << fsid << dendl;
+    librados::Rados dest_rados(m_dst_io_ctx);
+    std::string value;
+
+    r = get_config_key(dest_rados, "migration/fsid/" + fsid, &value);
+    if (r < 0) {
+       lderr(m_cct)  << "failed to fetch secret key from the monitor: "  << dendl;
+     } else {
+       ldout(m_cct, 5) << " get value by key " <<  fsid <<" got "<< value << dendl;
+       CephContext* cct = (CephContext*)m_dst_io_ctx.cct();
+       //CephContext* cct = m_dst_io_ctx.cct();
+       ldout(m_cct, 5) << " here " <<  dendl;
+       r = cct->_conf.set_val("mon_host", mon_host);
+       ldout(m_cct, 5) << " here1 " << r << dendl;
+       lderr(m_cct) << "set_val returned: " << r << " (" << cpp_strerror(r) << ")" << dendl;
+
+       r = cct->_conf.set_val("key", value);
+       ldout(m_cct, 5) << " here2 "<< r  << dendl;
+       lderr(m_cct) << "set_val returned: " << r << " (" << cpp_strerror(r) << ")" << dendl;
+
+       r = cct->_conf.set_val("fsid", fsid);
+       ldout(m_cct, 5) << " here3 "<< r  << dendl;
+       lderr(m_cct) << "set_val returned: " << r << " (" << cpp_strerror(r) << ")" << dendl;
+
+return;
+       ldout(m_cct, 5) << "Verifying overridden config:"
+                      << " mon_host=" << cct->_conf.get_val<std::string>("mon_host")
+                      << " key=" << cct->_conf.get_val<std::string>("key")
+                      << " fsid=" << cct->_conf.get_val<std::string>("fsid")
+                      << dendl;
+     }
+  }
+  ldout(m_cct, 5) << " here4 " << dendl;
+  r = NativeFormat<I>::create_image_ctx(m_dst_io_ctx, source_spec_object,
                                             import_only, m_src_snap_id,
                                             m_src_image_ctx, m_src_rados);
   if (r < 0) {
@@ -84,7 +151,6 @@ void OpenSourceImageRequest<I>::open_native(
     finish(r);
     return;
   }
-
   auto src_image_ctx = *m_src_image_ctx;
   src_image_ctx->child = m_dst_image_ctx;
 
index ec23a59d9f726a4e418fe73d34f9202f922205fb..012cdedd381ac017187297960b7fa66edb3c820c 100644 (file)
@@ -92,7 +92,8 @@ private:
   void open_native(const json_spirit::mObject& source_spec_object,
                    bool import_only);
   void handle_open_native(int r);
-
+  int inject_remote_cluster_creds( CephContext* src_cct, const std::string& mon_host,
+                          const std::string& fsid, const std::string& secret_key);
   void open_format(const json_spirit::mObject& source_spec_object);
   void handle_open_format(int r);
 
index ca9907c51e6d1f068c31cb2b2a22350f372dbcf5..81ffb7010c4e425a702aa871562555f8de45408d 100644 (file)
@@ -1459,6 +1459,28 @@ COMMAND("nvme-gw listeners"
        " show all nvmeof gateways listeners within (pool, group)",
        "mon", "r")
 
+COMMAND("import-migration put-metadata"
+   " name=pool,type=CephString"
+   " name=image,type=CephString"
+   " name=migration_id,type=CephString"
+   " name=key_ref,type=CephString",
+   "Store migration metadata in monitor DB",
+   "mon", "rw")
+
+COMMAND("import-migration get-metadata"
+   " name=pool,type=CephString"
+   " name=image,type=CephString"
+   " name=migration_id,type=CephString",
+   "Retrieve migration metadata from monitor DB",
+   "mon", "r")
+
+COMMAND("import-migration erase-metadata"
+   " name=pool,type=CephString"
+   " name=image,type=CephString"
+   " name=migration_id,type=CephString",
+   "Erase migration metadata from monitor DB",
+   "mon", "rw")
+
 // these are tell commands that were implemented as CLI commands in
 // the broken pre-octopus way that we want to allow to work when a
 // monitor has upgraded to octopus+ but the monmap min_mon_release is