From: Jason Dillaman Date: Fri, 13 Sep 2019 17:12:02 +0000 (-0400) Subject: librbd: new RBD mirroring peer bootstrap API methods X-Git-Tag: v15.1.0~1462^2~4 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=41b75a04964cc5e1cf784a2e0cb92cacc0306b43;p=ceph-ci.git librbd: new RBD mirroring peer bootstrap API methods These new methods will help facilitate the creation of keys and the transfer of cluster connection metadata between Ceph clusters. Signed-off-by: Jason Dillaman --- diff --git a/src/include/rbd/librbd.h b/src/include/rbd/librbd.h index 78d358ddc86..55668b06327 100644 --- a/src/include/rbd/librbd.h +++ b/src/include/rbd/librbd.h @@ -139,6 +139,12 @@ typedef enum { RBD_MIRROR_MODE_POOL /* mirroring enabled on all journaled images */ } rbd_mirror_mode_t; +typedef enum { + RBD_MIRROR_PEER_DIRECTION_RX = 0, + RBD_MIRROR_PEER_DIRECTION_TX = 1, + RBD_MIRROR_PEER_DIRECTION_RX_TX = 2 +} rbd_mirror_peer_direction_t; + typedef struct { char *uuid; char *cluster_name; @@ -441,6 +447,12 @@ CEPH_RBD_API int rbd_mirror_mode_get(rados_ioctx_t io_ctx, CEPH_RBD_API int rbd_mirror_mode_set(rados_ioctx_t io_ctx, rbd_mirror_mode_t mirror_mode); +CEPH_RBD_API int rbd_mirror_peer_bootstrap_create(rados_ioctx_t io_ctx, + char *token, size_t *max_len); +CEPH_RBD_API int rbd_mirror_peer_bootstrap_import( + rados_ioctx_t io_ctx, rbd_mirror_peer_direction_t direction, + const char *token); + CEPH_RBD_API int rbd_mirror_peer_add(rados_ioctx_t io_ctx, char *uuid, size_t uuid_max_length, const char *cluster_name, diff --git a/src/include/rbd/librbd.hpp b/src/include/rbd/librbd.hpp index d1d2667bd10..15eb9f56a31 100644 --- a/src/include/rbd/librbd.hpp +++ b/src/include/rbd/librbd.hpp @@ -74,6 +74,8 @@ namespace librbd { std::string address; } locker_t; + typedef rbd_mirror_peer_direction_t mirror_peer_direction_t; + typedef struct { std::string uuid; std::string cluster_name; @@ -270,6 +272,11 @@ public: int mirror_mode_get(IoCtx& io_ctx, rbd_mirror_mode_t *mirror_mode); int mirror_mode_set(IoCtx& io_ctx, rbd_mirror_mode_t mirror_mode); + int mirror_peer_bootstrap_create(IoCtx& io_ctx, std::string* token); + int mirror_peer_bootstrap_import(IoCtx& io_ctx, + mirror_peer_direction_t direction, + const std::string &token); + int mirror_peer_add(IoCtx& io_ctx, std::string *uuid, const std::string &cluster_name, const std::string &client_name); diff --git a/src/include/rbd_types.h b/src/include/rbd_types.h index 2b6243af9e1..35a1a8bc3c3 100644 --- a/src/include/rbd_types.h +++ b/src/include/rbd_types.h @@ -116,9 +116,10 @@ * MON config-key prefix for storing optional remote cluster connectivity * parameters */ -#define RBD_MIRROR_CONFIG_KEY_PREFIX "rbd/mirror/" -#define RBD_MIRROR_SITE_NAME_CONFIG_KEY RBD_MIRROR_CONFIG_KEY_PREFIX "site_name" -#define RBD_MIRROR_PEER_CONFIG_KEY_PREFIX RBD_MIRROR_CONFIG_KEY_PREFIX "peer/" +#define RBD_MIRROR_CONFIG_KEY_PREFIX "rbd/mirror/" +#define RBD_MIRROR_SITE_NAME_CONFIG_KEY RBD_MIRROR_CONFIG_KEY_PREFIX "site_name" +#define RBD_MIRROR_PEER_CLIENT_ID_CONFIG_KEY RBD_MIRROR_CONFIG_KEY_PREFIX "peer_client_id" +#define RBD_MIRROR_PEER_CONFIG_KEY_PREFIX RBD_MIRROR_CONFIG_KEY_PREFIX "peer/" struct rbd_info { ceph_le64 max_id; diff --git a/src/librbd/api/Mirror.cc b/src/librbd/api/Mirror.cc index 985c591b39a..4e2b3704570 100644 --- a/src/librbd/api/Mirror.cc +++ b/src/librbd/api/Mirror.cc @@ -24,6 +24,7 @@ #include "librbd/mirror/Types.h" #include "librbd/MirroringWatcher.h" #include +#include #include #include "json_spirit/json_spirit.h" @@ -105,6 +106,141 @@ int remove_peer_config_key(librados::IoCtx& io_ctx, return 0; } +int create_bootstrap_user(CephContext* cct, librados::Rados& rados, + std::string* peer_client_id, std::string* cephx_key) { + ldout(cct, 20) << dendl; + + // retrieve peer CephX user from config-key + int r = get_config_key(rados, RBD_MIRROR_PEER_CLIENT_ID_CONFIG_KEY, + peer_client_id); + if (r == -EACCES) { + ldout(cct, 5) << "insufficient permissions to get peer-client-id " + << "config-key" << dendl; + return r; + } else if (r < 0 && r != -ENOENT) { + lderr(cct) << "failed to retrieve peer client id key: " + << cpp_strerror(r) << dendl; + return r; + } else if (r == -ENOENT || peer_client_id->empty()) { + ldout(cct, 20) << "creating new peer-client-id config-key" << dendl; + + *peer_client_id = "rbd-mirror-peer"; + r = set_config_key(rados, RBD_MIRROR_PEER_CLIENT_ID_CONFIG_KEY, + *peer_client_id); + if (r == -EACCES) { + ldout(cct, 5) << "insufficient permissions to update peer-client-id " + << "config-key" << dendl; + return r; + } else if (r < 0) { + lderr(cct) << "failed to update peer client id key: " + << cpp_strerror(r) << dendl; + return r; + } + } + ldout(cct, 20) << "peer_client_id=" << *peer_client_id << dendl; + + // create peer client user + std::string cmd = + R"({)" \ + R"( "prefix": "auth get-or-create",)" \ + R"( "entity": "client.)" + *peer_client_id + R"(",)" \ + R"( "caps": [)" \ + R"( "mon", "profile rbd-mirror-peer",)" \ + R"( "osd", "profile rbd"],)" \ + R"( "format": "json")" \ + R"(})"; + + bufferlist in_bl; + bufferlist out_bl; + + r = rados.mon_command(cmd, in_bl, &out_bl, nullptr); + if (r == -EINVAL) { + ldout(cct, 5) << "caps mismatch for existing user" << dendl; + return -EEXIST; + } else if (r == -EACCES) { + ldout(cct, 5) << "insufficient permissions to create user" << dendl; + return r; + } else if (r < 0) { + lderr(cct) << "failed to create or update RBD mirroring bootstrap user: " + << cpp_strerror(r) << dendl; + return r; + } + + // extract key from response + bool json_valid = false; + json_spirit::mValue json_root; + if(json_spirit::read(out_bl.to_str(), json_root)) { + try { + auto& json_obj = json_root.get_array()[0].get_obj(); + *cephx_key = json_obj["key"].get_str(); + json_valid = true; + } catch (std::runtime_error&) { + } + } + + if (!json_valid) { + lderr(cct) << "invalid auth keyring JSON received" << dendl; + return -EBADMSG; + } + + return 0; +} + +int create_bootstrap_peer(CephContext* cct, librados::IoCtx& io_ctx, + const std::string& site_name, const std::string& fsid, + const std::string& client_id, const std::string& key, + const std::string& mon_host, + const std::string& cluster1, + const std::string& cluster2) { + ldout(cct, 20) << dendl; + + std::string peer_uuid; + std::vector peers; + int r = Mirror<>::peer_list(io_ctx, &peers); + if (r < 0 && r != -ENOENT) { + lderr(cct) << "failed to list mirror peers: " << cpp_strerror(r) << dendl; + return r; + } + + if (peers.empty()) { + r = Mirror<>::peer_add(io_ctx, &peer_uuid, site_name, + "client." + client_id); + if (r < 0) { + lderr(cct) << "failed to add " << cluster1 << " peer to " + << cluster2 << " " << "cluster: " << cpp_strerror(r) << dendl; + return r; + } + } else if (peers[0].cluster_name != site_name && + peers[0].cluster_name != fsid) { + // only support a single peer + lderr(cct) << "multiple peers are not currently supported" << dendl; + return -EINVAL; + } else { + peer_uuid = peers[0].uuid; + + if (peers[0].cluster_name != site_name) { + r = Mirror<>::peer_set_cluster(io_ctx, peer_uuid, site_name); + if (r < 0) { + // non-fatal attempt to update site name + lderr(cct) << "failed to update peer site name" << dendl; + } + } + } + + Mirror<>::Attributes attributes { + {"mon_host", mon_host}, + {"key", key}}; + r = Mirror<>::peer_set_attributes(io_ctx, peer_uuid, attributes); + if (r < 0) { + lderr(cct) << "failed to update " << cluster1 << " cluster connection " + << "attributes in " << cluster2 << " cluster: " + << cpp_strerror(r) << dendl; + return r; + } + + return 0; +} + template int validate_mirroring_enabled(I *ictx) { CephContext *cct = ictx->cct; @@ -475,11 +611,6 @@ int Mirror::image_resync(I *ictx) { return r; } - r = validate_mirroring_enabled(ictx); - if (r < 0) { - return r; - } - C_SaferCond tag_owner_ctx; bool is_tag_owner; Journal::is_tag_owner(ictx, &is_tag_owner, &tag_owner_ctx); @@ -853,6 +984,234 @@ int Mirror::mode_set(librados::IoCtx& io_ctx, return 0; } +template +int Mirror::peer_bootstrap_create(librados::IoCtx& io_ctx, + std::string* token) { + CephContext *cct = reinterpret_cast(io_ctx.cct()); + ldout(cct, 20) << dendl; + + auto mirror_mode = cls::rbd::MIRROR_MODE_DISABLED; + int r = cls_client::mirror_mode_get(&io_ctx, &mirror_mode); + if (r < 0 && r != -ENOENT) { + lderr(cct) << "failed to retrieve mirroring mode: " << cpp_strerror(r) + << dendl; + return r; + } else if (mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) { + return -EINVAL; + } + + // retrieve the cluster fsid + std::string fsid; + librados::Rados rados(io_ctx); + r = rados.cluster_fsid(&fsid); + if (r < 0) { + lderr(cct) << "failed to retrieve cluster fsid: " << cpp_strerror(r) + << dendl; + return r; + } + + std::string peer_client_id; + std::string cephx_key; + r = create_bootstrap_user(cct, rados, &peer_client_id, &cephx_key); + if (r < 0) { + return r; + } + + std::string mon_host = cct->_conf.get_val("mon_host"); + ldout(cct, 20) << "mon_host=" << mon_host << dendl; + + // format the token response + bufferlist token_bl; + token_bl.append( + R"({)" \ + R"("fsid":")" + fsid + R"(",)" + \ + R"("client_id":")" + peer_client_id + R"(",)" + \ + R"("key":")" + cephx_key + R"(",)" + \ + R"("mon_host":")" + \ + boost::replace_all_copy(mon_host, "\"", "\\\"") + R"(")" + \ + R"(})"); + ldout(cct, 20) << "token=" << token_bl.to_str() << dendl; + + bufferlist base64_bl; + token_bl.encode_base64(base64_bl); + *token = base64_bl.to_str(); + + return 0; +} + +template +int Mirror::peer_bootstrap_import(librados::IoCtx& io_ctx, + rbd_mirror_peer_direction_t direction, + const std::string& token) { + CephContext *cct = reinterpret_cast(io_ctx.cct()); + ldout(cct, 20) << dendl; + + if (direction != RBD_MIRROR_PEER_DIRECTION_RX && + direction != RBD_MIRROR_PEER_DIRECTION_RX_TX) { + lderr(cct) << "invalid mirror peer direction" << dendl; + return -EINVAL; + } + + bufferlist token_bl; + try { + bufferlist base64_bl; + base64_bl.append(token); + token_bl.decode_base64(base64_bl); + } catch (buffer::error& err) { + lderr(cct) << "failed to decode base64" << dendl; + return -EINVAL; + } + + ldout(cct, 20) << "token=" << token_bl.to_str() << dendl; + + bool json_valid = false; + std::string expected_remote_fsid; + std::string remote_client_id; + std::string remote_key; + std::string remote_mon_host; + + json_spirit::mValue json_root; + if(json_spirit::read(token_bl.to_str(), json_root)) { + try { + auto& json_obj = json_root.get_obj(); + expected_remote_fsid = json_obj["fsid"].get_str(); + remote_client_id = json_obj["client_id"].get_str(); + remote_key = json_obj["key"].get_str(); + remote_mon_host = json_obj["mon_host"].get_str(); + json_valid = true; + } catch (std::runtime_error&) { + } + } + + if (!json_valid) { + lderr(cct) << "invalid bootstrap token JSON received" << dendl; + return -EINVAL; + } + + // sanity check import process + std::string local_fsid; + librados::Rados rados(io_ctx); + int r = rados.cluster_fsid(&local_fsid); + if (r < 0) { + lderr(cct) << "failed to retrieve cluster fsid: " << cpp_strerror(r) + << dendl; + return r; + } + + std::string local_site_name; + r = site_name_get(rados, &local_site_name); + if (r < 0) { + lderr(cct) << "failed to retrieve cluster site name: " << cpp_strerror(r) + << dendl; + return r; + } + + // attempt to connect to remote cluster + librados::Rados remote_rados; + remote_rados.init(remote_client_id.c_str()); + + auto remote_cct = reinterpret_cast(remote_rados.cct()); + remote_cct->_conf.set_val("mon_host", remote_mon_host); + remote_cct->_conf.set_val("key", remote_key); + + r = remote_rados.connect(); + if (r < 0) { + lderr(cct) << "failed to connect to peer cluster: " << cpp_strerror(r) + << dendl; + return r; + } + + std::string remote_fsid; + r = remote_rados.cluster_fsid(&remote_fsid); + if (r < 0) { + lderr(cct) << "failed to retrieve remote cluster fsid: " + << cpp_strerror(r) << dendl; + return r; + } else if (local_fsid == remote_fsid) { + lderr(cct) << "cannot import token for local cluster" << dendl; + return -EINVAL; + } else if (expected_remote_fsid != remote_fsid) { + lderr(cct) << "unexpected remote cluster fsid" << dendl; + return -EINVAL; + } + + std::string remote_site_name; + r = site_name_get(remote_rados, &remote_site_name); + if (r < 0) { + lderr(cct) << "failed to retrieve remote cluster site name: " + << cpp_strerror(r) << dendl; + return r; + } else if (local_site_name == remote_site_name) { + lderr(cct) << "cannot import token for duplicate site name" << dendl; + return -EINVAL; + } + + librados::IoCtx remote_io_ctx; + r = remote_rados.ioctx_create(io_ctx.get_pool_name().c_str(), remote_io_ctx); + if (r == -ENOENT) { + ldout(cct, 10) << "remote pool does not exist" << dendl; + return r; + } else if (r < 0) { + lderr(cct) << "failed to open remote pool '" << io_ctx.get_pool_name() + << "': " << cpp_strerror(r) << dendl; + return r; + } + + auto remote_mirror_mode = cls::rbd::MIRROR_MODE_DISABLED; + r = cls_client::mirror_mode_get(&remote_io_ctx, &remote_mirror_mode); + if (r < 0 && r != -ENOENT) { + lderr(cct) << "failed to retrieve remote mirroring mode: " + << cpp_strerror(r) << dendl; + return r; + } else if (remote_mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) { + return -ENOSYS; + } + + auto local_mirror_mode = cls::rbd::MIRROR_MODE_DISABLED; + r = cls_client::mirror_mode_get(&io_ctx, &local_mirror_mode); + if (r < 0 && r != -ENOENT) { + lderr(cct) << "failed to retrieve local mirroring mode: " << cpp_strerror(r) + << dendl; + return r; + } else if (local_mirror_mode == cls::rbd::MIRROR_MODE_DISABLED) { + // copy mirror mode from remote peer + r = mode_set(io_ctx, static_cast(remote_mirror_mode)); + if (r < 0) { + return r; + } + } + + if (direction == RBD_MIRROR_PEER_DIRECTION_RX_TX) { + // create a local mirror peer user and export it to the remote cluster + std::string local_client_id; + std::string local_key; + r = create_bootstrap_user(cct, rados, &local_client_id, &local_key); + if (r < 0) { + return r; + } + + std::string local_mon_host = cct->_conf.get_val("mon_host"); + + // create local cluster peer in remote cluster + r = create_bootstrap_peer(cct, remote_io_ctx, local_site_name, local_fsid, + local_client_id, local_key, local_mon_host, + "local", "remote"); + if (r < 0) { + return r; + } + } + + // create remote cluster peer in local cluster + r = create_bootstrap_peer(cct, io_ctx, remote_site_name, remote_fsid, + remote_client_id, remote_key, remote_mon_host, + "remote", "local"); + if (r < 0) { + return r; + } + + return 0; +} + template int Mirror::peer_add(librados::IoCtx& io_ctx, std::string *uuid, const std::string &cluster_name, diff --git a/src/librbd/api/Mirror.h b/src/librbd/api/Mirror.h index 0544a0547c5..94768acdbbd 100644 --- a/src/librbd/api/Mirror.h +++ b/src/librbd/api/Mirror.h @@ -29,6 +29,11 @@ struct Mirror { static int mode_get(librados::IoCtx& io_ctx, rbd_mirror_mode_t *mirror_mode); static int mode_set(librados::IoCtx& io_ctx, rbd_mirror_mode_t mirror_mode); + static int peer_bootstrap_create(librados::IoCtx& io_ctx, std::string* token); + static int peer_bootstrap_import(librados::IoCtx& io_ctx, + rbd_mirror_peer_direction_t direction, + const std::string& token); + static int peer_add(librados::IoCtx& io_ctx, std::string *uuid, const std::string &cluster_name, const std::string &client_name); diff --git a/src/librbd/librbd.cc b/src/librbd/librbd.cc index 84d0984ef1b..bd8a58e86c1 100644 --- a/src/librbd/librbd.cc +++ b/src/librbd/librbd.cc @@ -882,6 +882,17 @@ namespace librbd { return librbd::api::Mirror<>::mode_set(io_ctx, mirror_mode); } + int RBD::mirror_peer_bootstrap_create(IoCtx& io_ctx, std::string* token) { + return librbd::api::Mirror<>::peer_bootstrap_create(io_ctx, token); + } + + int RBD::mirror_peer_bootstrap_import(IoCtx& io_ctx, + rbd_mirror_peer_direction_t direction, + const std::string& token) { + return librbd::api::Mirror<>::peer_bootstrap_import(io_ctx, direction, + token); + } + int RBD::mirror_peer_add(IoCtx& io_ctx, std::string *uuid, const std::string &cluster_name, const std::string &client_name) { @@ -2770,6 +2781,37 @@ extern "C" int rbd_mirror_mode_set(rados_ioctx_t p, return librbd::api::Mirror<>::mode_set(io_ctx, mirror_mode); } +extern "C" int rbd_mirror_peer_bootstrap_create(rados_ioctx_t p, char *token, + size_t *max_len) { + librados::IoCtx io_ctx; + librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + + std::string token_str; + int r = librbd::api::Mirror<>::peer_bootstrap_create(io_ctx, &token_str); + if (r < 0) { + return r; + } + + auto total_len = token_str.size() + 1; + if (*max_len < total_len) { + *max_len = total_len; + return -ERANGE; + } + *max_len = total_len; + + strcpy(token, token_str.c_str()); + return 0; +} + +extern "C" int rbd_mirror_peer_bootstrap_import( + rados_ioctx_t p, rbd_mirror_peer_direction_t direction, + const char *token) { + librados::IoCtx io_ctx; + librados::IoCtx::from_rados_ioctx_t(p, io_ctx); + + return librbd::api::Mirror<>::peer_bootstrap_import(io_ctx, direction, token); +} + extern "C" int rbd_mirror_peer_add(rados_ioctx_t p, char *uuid, size_t uuid_max_length, const char *cluster_name, diff --git a/src/pybind/rbd/rbd.pyx b/src/pybind/rbd/rbd.pyx index 02fa9d6a2c9..9aae566c623 100644 --- a/src/pybind/rbd/rbd.pyx +++ b/src/pybind/rbd/rbd.pyx @@ -154,6 +154,11 @@ cdef extern from "rbd/librbd.h" nogil: _RBD_MIRROR_MODE_IMAGE "RBD_MIRROR_MODE_IMAGE" _RBD_MIRROR_MODE_POOL "RBD_MIRROR_MODE_POOL" + ctypedef enum rbd_mirror_peer_direction_t: + _RBD_MIRROR_PEER_DIRECTION_RX "RBD_MIRROR_PEER_DIRECTION_RX" + _RBD_MIRROR_PEER_DIRECTION_TX "RBD_MIRROR_PEER_DIRECTION_TX" + _RBD_MIRROR_PEER_DIRECTION_RX_TX "RBD_MIRROR_PEER_DIRECTION_RX_TX" + ctypedef struct rbd_mirror_peer_t: char *uuid char *cluster_name @@ -347,6 +352,12 @@ cdef extern from "rbd/librbd.h" nogil: int rbd_mirror_mode_get(rados_ioctx_t io, rbd_mirror_mode_t *mirror_mode) int rbd_mirror_mode_set(rados_ioctx_t io, rbd_mirror_mode_t mirror_mode) + int rbd_mirror_peer_bootstrap_create(rados_ioctx_t io_ctx, char *token, + size_t *max_len) + int rbd_mirror_peer_bootstrap_import( + rados_ioctx_t io_ctx, rbd_mirror_peer_direction_t direction, + const char *token) + int rbd_mirror_peer_add(rados_ioctx_t io, char *uuid, size_t uuid_max_length, const char *cluster_name, const char *client_name) @@ -653,6 +664,10 @@ RBD_MIRROR_MODE_DISABLED = _RBD_MIRROR_MODE_DISABLED RBD_MIRROR_MODE_IMAGE = _RBD_MIRROR_MODE_IMAGE RBD_MIRROR_MODE_POOL = _RBD_MIRROR_MODE_POOL +RBD_MIRROR_PEER_DIRECTION_RX = _RBD_MIRROR_PEER_DIRECTION_RX +RBD_MIRROR_PEER_DIRECTION_TX = _RBD_MIRROR_PEER_DIRECTION_TX +RBD_MIRROR_PEER_DIRECTION_RX_TX = _RBD_MIRROR_PEER_DIRECTION_RX_TX + RBD_MIRROR_IMAGE_DISABLING = _RBD_MIRROR_IMAGE_DISABLING RBD_MIRROR_IMAGE_ENABLED = _RBD_MIRROR_IMAGE_ENABLED RBD_MIRROR_IMAGE_DISABLED = _RBD_MIRROR_IMAGE_DISABLED @@ -1770,6 +1785,55 @@ class RBD(object): if ret != 0: raise make_ex(ret, 'error setting mirror mode') + def mirror_peer_bootstrap_create(self, ioctx): + """ + Creates a new RBD mirroring bootstrap token for an + external cluster. + + :param ioctx: determines which RADOS pool is written + :type ioctx: :class:`rados.Ioctx` + :returns: str - bootstrap token + """ + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + char *_token = NULL + size_t _max_size = 512 + try: + while True: + _token = realloc_chk(_token, _max_size) + with nogil: + ret = rbd_mirror_peer_bootstrap_create(_ioctx, _token, + &_max_size) + if ret >= 0: + break + elif ret != -errno.ERANGE: + raise make_ex(ret, 'error creating bootstrap token') + return decode_cstr(_token) + finally: + free(_token) + + def mirror_peer_bootstrap_import(self, ioctx, direction, token): + """ + Import a bootstrap token from an external cluster to + auto-configure the mirror peer. + + :param ioctx: determines which RADOS pool is written + :type ioctx: :class:`rados.Ioctx` + :param direction: mirror peer direction + :type direction: int + :param token: bootstrap token + :type token: str + """ + token = cstr(token, 'token') + cdef: + rados_ioctx_t _ioctx = convert_ioctx(ioctx) + rbd_mirror_peer_direction_t _direction = direction + char *_token = token + with nogil: + ret = rbd_mirror_peer_bootstrap_import(_ioctx, _direction, _token) + if ret != 0: + raise make_ex(ret, 'error importing bootstrap token') + def mirror_peer_add(self, ioctx, cluster_name, client_name): """ Add mirror peer. diff --git a/src/test/librbd/test_mirroring.cc b/src/test/librbd/test_mirroring.cc index a31b11e00b0..204a38ff724 100644 --- a/src/test/librbd/test_mirroring.cc +++ b/src/test/librbd/test_mirroring.cc @@ -968,3 +968,25 @@ TEST_F(TestMirroring, SiteName) { ASSERT_EQ(0, m_rbd.mirror_site_name_get(_rados, &site_name)); ASSERT_EQ(fsid, site_name); } + +TEST_F(TestMirroring, Bootstrap) { + REQUIRE(!is_librados_test_stub(_rados)); + + std::string token_b64; + ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_DISABLED)); + ASSERT_EQ(-EINVAL, m_rbd.mirror_peer_bootstrap_create(m_ioctx, &token_b64)); + + ASSERT_EQ(0, m_rbd.mirror_mode_set(m_ioctx, RBD_MIRROR_MODE_POOL)); + ASSERT_EQ(0, m_rbd.mirror_peer_bootstrap_create(m_ioctx, &token_b64)); + + bufferlist token_b64_bl; + token_b64_bl.append(token_b64); + + bufferlist token_bl; + token_bl.decode_base64(token_b64_bl); + + // cannot import token into same cluster + ASSERT_EQ(-EINVAL, + m_rbd.mirror_peer_bootstrap_import( + m_ioctx, RBD_MIRROR_PEER_DIRECTION_RX, token_b64)); +} diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index c5f3ed109c9..1f2a5b8ddd8 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -1,6 +1,8 @@ # vim: expandtab smarttab shiftwidth=4 softtabstop=4 +import base64 import errno import functools +import json import socket import os import time @@ -28,7 +30,8 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists, RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG, RBD_CONFIG_SOURCE_POOL, RBD_CONFIG_SOURCE_IMAGE, RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST, - RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY) + RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY, + RBD_MIRROR_PEER_DIRECTION_RX) rados = None ioctx = None @@ -1796,6 +1799,24 @@ class TestMirroring(object): self.rbd.mirror_site_name_set(rados, "") eq(rados.get_fsid(), self.rbd.mirror_site_name_get(rados)) + def test_mirror_peer_bootstrap(self): + eq([], list(self.rbd.mirror_peer_list(ioctx))) + + self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED) + assert_raises(InvalidArgument, self.rbd.mirror_peer_bootstrap_create, + ioctx); + + self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_POOL) + token_b64 = self.rbd.mirror_peer_bootstrap_create(ioctx) + token = base64.b64decode(token_b64) + token_dict = json.loads(token) + eq(sorted(['fsid', 'client_id', 'key', 'mon_host']), + sorted(list(token_dict.keys()))) + + # requires different cluster + assert_raises(InvalidArgument, self.rbd.mirror_peer_bootstrap_import, + ioctx, RBD_MIRROR_PEER_DIRECTION_RX, token_b64) + def test_mirror_peer(self): eq([], list(self.rbd.mirror_peer_list(ioctx))) cluster_name = "test_cluster"