char *client_name;
} rbd_mirror_peer_t;
+#define RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST "mon_host"
+#define RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY "key"
+
typedef enum {
RBD_MIRROR_IMAGE_DISABLING = 0,
RBD_MIRROR_IMAGE_ENABLED = 1,
CEPH_RBD_API int rbd_mirror_peer_set_cluster(rados_ioctx_t io_ctx,
const char *uuid,
const char *cluster_name);
+CEPH_RBD_API int rbd_mirror_peer_get_attributes(
+ rados_ioctx_t p, const char *uuid, char *keys, size_t *max_key_len,
+ char *values, size_t *max_value_len, size_t *key_value_count);
+CEPH_RBD_API int rbd_mirror_peer_set_attributes(
+ rados_ioctx_t p, const char *uuid, const char *keys, const char *values,
+ size_t key_value_count);
+
CEPH_RBD_API int rbd_mirror_image_status_list(rados_ioctx_t io_ctx,
const char *start_id, size_t max,
char **image_ids,
const std::string &client_name);
int mirror_peer_set_cluster(IoCtx& io_ctx, const std::string &uuid,
const std::string &cluster_name);
+ int mirror_peer_get_attributes(
+ IoCtx& io_ctx, const std::string &uuid,
+ std::map<std::string, std::string> *key_vals);
+ int mirror_peer_set_attributes(
+ IoCtx& io_ctx, const std::string &uuid,
+ const std::map<std::string, std::string>& key_vals);
+
int mirror_image_status_list(IoCtx& io_ctx, const std::string &start_id,
size_t max, std::map<std::string, mirror_image_status_t> *images);
int mirror_image_status_summary(IoCtx& io_ctx,
#include "librbd/api/Mirror.h"
#include "include/rados/librados.hpp"
+#include "include/stringify.h"
+#include "common/ceph_json.h"
#include "common/dout.h"
#include "common/errno.h"
#include "cls/rbd/cls_rbd_client.h"
namespace {
+std::string get_peer_config_key_name(int64_t pool_id,
+ const std::string& peer_uuid) {
+ return RBD_MIRROR_PEER_CONFIG_KEY_PREFIX + stringify(pool_id) + "/" +
+ peer_uuid;
+}
+
+int remove_peer_config_key(librados::IoCtx& io_ctx,
+ const std::string& peer_uuid) {
+ int64_t pool_id = io_ctx.get_id();
+ std::string cmd =
+ "{"
+ "\"prefix\": \"config-key rm\", "
+ "\"key\": \"" + get_peer_config_key_name(pool_id, peer_uuid) + "\""
+ "}";
+
+ bufferlist in_bl;
+ bufferlist out_bl;
+ librados::Rados rados(io_ctx);
+ int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr);
+ if (r < 0 && r != -ENOENT && r != -EPERM) {
+ return r;
+ }
+ return 0;
+}
+
template <typename I>
int validate_mirroring_enabled(I *ictx) {
CephContext *cct = ictx->cct;
CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
ldout(cct, 20) << "uuid=" << uuid << dendl;
- int r = cls_client::mirror_peer_remove(&io_ctx, uuid);
+ int r = remove_peer_config_key(io_ctx, uuid);
+ if (r < 0) {
+ lderr(cct) << "failed to remove peer attributes '" << uuid << "': "
+ << cpp_strerror(r) << dendl;
+ return r;
+ }
+
+ r = cls_client::mirror_peer_remove(&io_ctx, uuid);
if (r < 0 && r != -ENOENT) {
lderr(cct) << "failed to remove peer '" << uuid << "': "
<< cpp_strerror(r) << dendl;
return 0;
}
+template <typename I>
+int Mirror<I>::peer_get_attributes(librados::IoCtx& io_ctx,
+ const std::string &uuid,
+ Attributes* attributes) {
+ CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
+ ldout(cct, 20) << "uuid=" << uuid << dendl;
+
+ attributes->clear();
+ std::string cmd =
+ "{"
+ "\"prefix\": \"config-key get\", "
+ "\"key\": \"" + get_peer_config_key_name(io_ctx.get_id(), uuid) + "\""
+ "}";
+
+ bufferlist in_bl;
+ bufferlist out_bl;
+
+ librados::Rados rados(io_ctx);
+ int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr);
+ if (r == -ENOENT || out_bl.length() == 0) {
+ return -ENOENT;
+ } else if (r < 0) {
+ lderr(cct) << "failed to retrieve peer attributes: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ 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_obj();
+ for (auto& pairs : json_obj) {
+ (*attributes)[pairs.first] = pairs.second.get_str();
+ }
+ json_valid = true;
+ } catch (std::runtime_error&) {
+ }
+ }
+
+ if (!json_valid) {
+ lderr(cct) << "invalid peer attributes JSON received" << dendl;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+template <typename I>
+int Mirror<I>::peer_set_attributes(librados::IoCtx& io_ctx,
+ const std::string &uuid,
+ const Attributes& attributes) {
+ CephContext *cct = reinterpret_cast<CephContext *>(io_ctx.cct());
+ ldout(cct, 20) << "uuid=" << uuid << ", "
+ << "attributes=" << attributes << dendl;
+
+ std::vector<mirror_peer_t> mirror_peers;
+ int r = peer_list(io_ctx, &mirror_peers);
+ if (r < 0) {
+ return r;
+ }
+
+ if (std::find_if(mirror_peers.begin(), mirror_peers.end(),
+ [&uuid](const librbd::mirror_peer_t& peer) {
+ return uuid == peer.uuid;
+ }) == mirror_peers.end()) {
+ ldout(cct, 5) << "mirror peer uuid " << uuid << " does not exist" << dendl;
+ return -ENOENT;
+ }
+
+ std::stringstream ss;
+ ss << "{";
+ for (auto& pair : attributes) {
+ ss << "\\\"" << pair.first << "\\\": "
+ << "\\\"" << pair.second << "\\\"";
+ if (&pair != &(*attributes.rbegin())) {
+ ss << ", ";
+ }
+ }
+ ss << "}";
+
+ std::string cmd =
+ "{"
+ "\"prefix\": \"config-key set\", "
+ "\"key\": \"" + get_peer_config_key_name(io_ctx.get_id(), uuid) + "\", "
+ "\"val\": \"" + ss.str() + "\""
+ "}";
+ bufferlist in_bl;
+ bufferlist out_bl;
+
+ librados::Rados rados(io_ctx);
+ r = rados.mon_command(cmd, in_bl, &out_bl, nullptr);
+ if (r < 0) {
+ lderr(cct) << "failed to update peer attributes: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ return 0;
+}
+
template <typename I>
int Mirror<I>::image_status_list(librados::IoCtx& io_ctx,
const std::string &start_id, size_t max,
template <typename ImageCtxT = librbd::ImageCtx>
struct Mirror {
+ typedef std::map<std::string, std::string> Attributes;
typedef std::map<std::string, mirror_image_status_t> IdToMirrorImageStatus;
typedef std::map<mirror_image_status_state_t, int> MirrorImageStatusStates;
const std::string &client_name);
static int peer_set_cluster(librados::IoCtx& io_ctx, const std::string &uuid,
const std::string &cluster_name);
+ static int peer_get_attributes(librados::IoCtx& io_ctx,
+ const std::string &uuid,
+ Attributes* attributes);
+ static int peer_set_attributes(librados::IoCtx& io_ctx,
+ const std::string &uuid,
+ const Attributes& attributes);
static int image_status_list(librados::IoCtx& io_ctx,
const std::string &start_id, size_t max,
return librbd::api::Mirror<>::peer_set_cluster(io_ctx, uuid, cluster_name);
}
+ int RBD::mirror_peer_get_attributes(
+ IoCtx& io_ctx, const std::string &uuid,
+ std::map<std::string, std::string> *key_vals) {
+ return librbd::api::Mirror<>::peer_get_attributes(io_ctx, uuid, key_vals);
+ }
+
+ int RBD::mirror_peer_set_attributes(
+ IoCtx& io_ctx, const std::string &uuid,
+ const std::map<std::string, std::string>& key_vals) {
+ return librbd::api::Mirror<>::peer_set_attributes(io_ctx, uuid, key_vals);
+ }
+
int RBD::mirror_image_status_list(IoCtx& io_ctx, const std::string &start_id,
size_t max, std::map<std::string, mirror_image_status_t> *images) {
return librbd::api::Mirror<>::image_status_list(io_ctx, start_id, max,
return librbd::api::Mirror<>::peer_set_cluster(io_ctx, uuid, cluster_name);
}
+extern "C" int rbd_mirror_peer_get_attributes(
+ rados_ioctx_t p, const char *uuid, char *keys, size_t *max_key_len,
+ char *values, size_t *max_val_len, size_t *key_value_count) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
+
+ std::map<std::string, std::string> attributes;
+ int r = librbd::api::Mirror<>::peer_get_attributes(io_ctx, uuid, &attributes);
+ if (r < 0) {
+ return r;
+ }
+
+ size_t key_total_len = 0, val_total_len = 0;
+ for (auto& it : attributes) {
+ key_total_len += it.first.size() + 1;
+ val_total_len += it.second.length() + 1;
+ }
+
+ bool too_short = ((*max_key_len < key_total_len) ||
+ (*max_val_len < val_total_len));
+
+ *max_key_len = key_total_len;
+ *max_val_len = val_total_len;
+ *key_value_count = attributes.size();
+ if (too_short) {
+ return -ERANGE;
+ }
+
+ char *keys_p = keys;
+ char *values_p = values;
+ for (auto& it : attributes) {
+ strncpy(keys_p, it.first.c_str(), it.first.size() + 1);
+ keys_p += it.first.size() + 1;
+
+ strncpy(values_p, it.second.c_str(), it.second.length() + 1);
+ values_p += it.second.length() + 1;
+ }
+
+ return 0;
+}
+
+extern "C" int rbd_mirror_peer_set_attributes(
+ rados_ioctx_t p, const char *uuid, const char *keys, const char *values,
+ size_t count) {
+ librados::IoCtx io_ctx;
+ librados::IoCtx::from_rados_ioctx_t(p, io_ctx);
+
+ std::map<std::string, std::string> attributes;
+
+ for (size_t i = 0; i < count; ++i) {
+ const char* key = keys;
+ keys += strlen(key) + 1;
+ const char* value = values;
+ values += strlen(value) + 1;
+ attributes[key] = value;
+ }
+
+ return librbd::api::Mirror<>::peer_set_attributes(io_ctx, uuid, attributes);
+}
+
extern "C" int rbd_mirror_image_status_list(rados_ioctx_t p,
const char *start_id, size_t max, char **image_ids,
rbd_mirror_image_status_t *images, size_t *len) {
from collections import Iterable
from datetime import datetime
+from itertools import chain
cimport rados
char *cluster_name
char *client_name
+ cdef char* _RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST "RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST"
+ cdef char* _RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY "RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY"
+
ctypedef enum rbd_mirror_image_state_t:
_RBD_MIRROR_IMAGE_DISABLING "RBD_MIRROR_IMAGE_DISABLING"
_RBD_MIRROR_IMAGE_ENABLED "RBD_MIRROR_IMAGE_ENABLED"
const char *client_name)
int rbd_mirror_peer_set_cluster(rados_ioctx_t io_ctx, const char *uuid,
const char *cluster_name)
+ int rbd_mirror_peer_get_attributes(rados_ioctx_t io_ctx, const char *uuid,
+ char *keys, size_t *max_key_len,
+ char *values, size_t *max_val_length,
+ size_t *key_value_count)
+ int rbd_mirror_peer_set_attributes(rados_ioctx_t io_ctx, const char *uuid,
+ const char *keys, const char *values,
+ size_t count)
+
int rbd_mirror_image_status_list(rados_ioctx_t io, const char *start_id,
size_t max, char **image_ids,
rbd_mirror_image_status_t *images,
RBD_MIRROR_MODE_IMAGE = _RBD_MIRROR_MODE_IMAGE
RBD_MIRROR_MODE_POOL = _RBD_MIRROR_MODE_POOL
+RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST = _RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST
+RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY = _RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY
+
RBD_MIRROR_IMAGE_DISABLING = _RBD_MIRROR_IMAGE_DISABLING
RBD_MIRROR_IMAGE_ENABLED = _RBD_MIRROR_IMAGE_ENABLED
RBD_MIRROR_IMAGE_DISABLED = _RBD_MIRROR_IMAGE_DISABLED
if ret != 0:
raise make_ex(ret, 'error setting mirror peer cluster')
+ def mirror_peer_get_attributes(self, ioctx, uuid):
+ """
+ Get optional mirror peer attributes
+
+ :param ioctx: determines which RADOS pool is written
+ :type ioctx: :class:`rados.Ioctx`
+ :param uuid: uuid of the mirror peer
+ :type uuid: str
+
+ :returns: dict - contains the following keys:
+
+ * ``mon_host`` (str) - monitor addresses
+
+ * ``key`` (str) - CephX key
+ """
+ uuid = cstr(uuid, 'uuid')
+ cdef:
+ rados_ioctx_t _ioctx = convert_ioctx(ioctx)
+ char *_uuid = uuid
+ char *_keys = NULL
+ char *_vals = NULL
+ size_t _keys_size = 512
+ size_t _vals_size = 512
+ size_t _count = 0
+ try:
+ while True:
+ _keys = <char *>realloc_chk(_keys, _keys_size)
+ _vals = <char *>realloc_chk(_vals, _vals_size)
+ with nogil:
+ ret = rbd_mirror_peer_get_attributes(_ioctx, _uuid, _keys,
+ &_keys_size, _vals,
+ &_vals_size, &_count)
+ if ret >= 0:
+ break
+ elif ret != -errno.ERANGE:
+ raise make_ex(ret, 'error getting mirror peer attributes')
+ keys = [decode_cstr(x) for x in _keys[:_keys_size].split(b'\0') if x]
+ vals = [decode_cstr(x) for x in _vals[:_vals_size].split(b'\0') if x]
+ return dict(zip(keys, vals))
+ finally:
+ free(_keys)
+ free(_vals)
+
+ def mirror_peer_set_attributes(self, ioctx, uuid, attributes):
+ """
+ Set optional mirror peer attributes
+
+ :param ioctx: determines which RADOS pool is written
+ :type ioctx: :class:`rados.Ioctx`
+ :param uuid: uuid of the mirror peer
+ :type uuid: str
+ :param attributes: 'mon_host' and 'key' attributes
+ :type attributes: dict
+ """
+ uuid = cstr(uuid, 'uuid')
+ keys_str = '\0'.join([cstr(x[0], 'key') for x in attributes.items()])
+ vals_str = '\0'.join([cstr(x[1], 'val') for x in attributes.items()])
+ cdef:
+ rados_ioctx_t _ioctx = convert_ioctx(ioctx)
+ char *_uuid = uuid
+ char *_keys = keys_str
+ char *_vals = vals_str
+ size_t _count = len(attributes)
+
+ with nogil:
+ ret = rbd_mirror_peer_set_attributes(_ioctx, _uuid, _keys, _vals,
+ _count)
+ if ret != 0:
+ raise make_ex(ret, 'error setting mirror peer attributes')
+
def mirror_image_status_list(self, ioctx):
"""
Iterate over the mirror image statuses of a pool.
return 0;
} else if ((*j_it)->get_data() == "osd tier remove") {
return 0;
+ } else if ((*j_it)->get_data() == "config-key rm") {
+ return 0;
}
}
return -ENOSYS;
ASSERT_EQ(expected_peers, peers);
ASSERT_EQ(-EBUSY, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid1));
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid3));
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
+}
+
+TEST_F(TestLibRBD, MirrorPeerAttributes) {
+ REQUIRE(!is_librados_test_stub(_rados));
+
+ librados::IoCtx ioctx;
+ ASSERT_EQ(0, _rados.ioctx_create(m_pool_name.c_str(), ioctx));
+
+ librbd::RBD rbd;
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE));
+
+ std::string uuid;
+ ASSERT_EQ(0, rbd.mirror_peer_add(ioctx, &uuid, "remote_cluster", "client"));
+
+ std::map<std::string, std::string> attributes;
+ ASSERT_EQ(-ENOENT, rbd.mirror_peer_get_attributes(ioctx, uuid, &attributes));
+ ASSERT_EQ(-ENOENT, rbd.mirror_peer_set_attributes(ioctx, "missing uuid",
+ attributes));
+
+ std::map<std::string, std::string> expected_attributes{
+ {"mon_host", "1.2.3.4"},
+ {"key", "ABC"}};
+ ASSERT_EQ(0, rbd.mirror_peer_set_attributes(ioctx, uuid,
+ expected_attributes));
+
+ ASSERT_EQ(0, rbd.mirror_peer_get_attributes(ioctx, uuid,
+ &attributes));
+ ASSERT_EQ(expected_attributes, attributes);
+
+ ASSERT_EQ(0, rbd.mirror_peer_remove(ioctx, uuid));
+ ASSERT_EQ(0, rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_DISABLED));
}
TEST_F(TestLibRBD, FlushCacheWithCopyupOnExternalSnapshot) {
RBD_LOCK_MODE_EXCLUSIVE, RBD_OPERATION_FEATURE_GROUP,
RBD_SNAP_NAMESPACE_TYPE_TRASH,
RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG,
- RBD_CONFIG_SOURCE_POOL, RBD_CONFIG_SOURCE_IMAGE)
+ RBD_CONFIG_SOURCE_POOL, RBD_CONFIG_SOURCE_IMAGE,
+ RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST,
+ RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY)
rados = None
ioctx = None
'client_name' : client_name,
}
eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
+
+ attribs = {
+ RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST: 'host1',
+ RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY: 'abc'
+ }
+ self.rbd.mirror_peer_set_attributes(ioctx, uuid, attribs)
+ eq(attribs, self.rbd.mirror_peer_get_attributes(ioctx, uuid))
+
self.rbd.mirror_peer_remove(ioctx, uuid)
eq([], list(self.rbd.mirror_peer_list(ioctx)))