#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
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,
<< 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;
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,
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,
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;
}
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,
}
}
+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) {
finish(r);
return;
}
-
auto src_image_ctx = *m_src_image_ctx;
src_image_ctx->child = m_dst_image_ctx;
" 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