#define RBD_TRASH "rbd_trash"
+/**
+ * MON config-key prefix for storing optional remote cluster connectivity
+ * parameters
+ */
+#define RBD_MIRROR_PEER_CONFIG_KEY_PREFIX "rbd/mirror/peer/"
+
struct rbd_info {
__le64 max_id;
} __attribute__ ((packed));
+set(librbd_test_support_srcs
+ test_support.cc
+ )
+add_library(rbd_test_support STATIC ${librbd_test_support_srcs})
+target_link_libraries(rbd_test_support PRIVATE
+ GTest::GTest)
+
set(librbd_test
test_fixture.cc
- test_support.cc
test_librbd.cc
test_ImageWatcher.cc
test_internal.cc
journal/test_Replay.cc)
add_library(rbd_test STATIC ${librbd_test})
target_link_libraries(rbd_test PRIVATE
+ rbd_test_support
Boost::thread
GMock::GMock
GTest::GTest)
// vim: ts=8 sw=2 smarttab
#include "test/librbd/test_support.h"
#include "include/rbd_types.h"
+#include "gtest/gtest.h"
#include <sstream>
bool get_features(uint64_t *features) {
return r;
}
+
+bool is_librados_test_stub(librados::Rados &rados) {
+ std::string fsid;
+ EXPECT_EQ(0, rados.cluster_fsid(&fsid));
+ return fsid == "00000000-1111-2222-3333-444444444444";
+}
+
int get_image_id(librbd::Image &image, std::string *image_id);
int create_image_data_pool(librados::Rados &rados, std::string &data_pool, bool *created);
+bool is_librados_test_stub(librados::Rados &rados);
+
#define REQUIRE(x) { \
if (!(x)) { \
std::cout << "SKIPPING" << std::endl; \
)
add_library(rbd_mirror_test STATIC ${rbd_mirror_test_srcs})
target_link_libraries(rbd_mirror_test
+ rbd_test_support
GTest::GTest)
add_executable(unittest_rbd_mirror
#include "tools/rbd_mirror/Types.h"
#include "test/rbd_mirror/test_fixture.h"
#include "test/librados/test.h"
+#include "test/librbd/test_support.h"
#include "gtest/gtest.h"
#include <boost/scope_exit.hpp>
#include <iostream>
ASSERT_EQ(0, m_cluster->pool_delete(name.c_str()));
}
+ void set_peer_config_key(const std::string& pool_name,
+ const PeerSpec &peer) {
+ int64_t pool_id = m_cluster->pool_lookup(pool_name.c_str());
+ ASSERT_GE(pool_id, 0);
+
+ std::string json =
+ "{"
+ "\\\"mon_host\\\": \\\"" + peer.mon_host + "\\\", "
+ "\\\"key\\\": \\\"" + peer.key + "\\\""
+ "}";
+
+ bufferlist in_bl;
+ ASSERT_EQ(0, m_cluster->mon_command(
+ "{"
+ "\"prefix\": \"config-key set\","
+ "\"key\": \"" RBD_MIRROR_PEER_CONFIG_KEY_PREFIX + stringify(pool_id) +
+ "/" + peer.uuid + "\","
+ "\"val\": \"" + json + "\"" +
+ "}", in_bl, nullptr, nullptr));
+ }
+
void create_cache_pool(const string &base_pool, string *cache_pool_name) {
bufferlist inbl;
*cache_pool_name = get_temp_pool_name("test-rbd-mirror-");
} BOOST_SCOPE_EXIT_END;
check_peers();
}
+
+TEST_F(TestClusterWatcher, ConfigKey) {
+ REQUIRE(!is_librados_test_stub(*m_cluster));
+
+ std::string pool_name;
+ check_peers();
+
+ PeerSpec site1("", "site1", "mirror1");
+ create_pool(true, site1, &site1.uuid, &pool_name);
+ check_peers();
+
+ PeerSpec site2("", "site2", "mirror2");
+ site2.mon_host = "abc";
+ site2.key = "xyz";
+ create_pool(false, site2, &site2.uuid);
+ set_peer_config_key(pool_name, site2);
+
+ check_peers();
+}
EXPECT_EQ(0, librbd::api::Mirror<>::mode_set(m_local_io_ctx,
RBD_MIRROR_MODE_POOL));
- if (is_librados_test_stub()) {
+ if (is_librados_test_stub(*_rados)) {
// speed testing up a little
EXPECT_EQ(0, _rados->conf_set("rbd_mirror_leader_heartbeat_interval",
"1"));
}
}
- bool is_librados_test_stub() {
- std::string fsid;
- EXPECT_EQ(0, _rados->cluster_fsid(&fsid));
- return fsid == "00000000-1111-2222-3333-444444444444";
- }
-
librados::IoCtx &create_connection(bool no_heartbeats = false) {
m_connections.push_back(std::unique_ptr<Connection>(new Connection()));
Connection *c = m_connections.back().get();
if (no_heartbeats) {
EXPECT_EQ(0, c->cluster.conf_set("rbd_mirror_leader_heartbeat_interval",
"3600"));
- } else if (is_librados_test_stub()) {
+ } else if (is_librados_test_stub(*_rados)) {
EXPECT_EQ(0, c->cluster.conf_set("rbd_mirror_leader_heartbeat_interval",
"1"));
}
// vim: ts=8 sw=2 smarttab
#include "ClusterWatcher.h"
+#include "include/stringify.h"
+#include "common/ceph_json.h"
#include "common/debug.h"
#include "common/errno.h"
#include "cls/rbd/cls_rbd_client.h"
continue;
}
+ std::vector<PeerSpec> peers{configs.begin(), configs.end()};
+ for (auto& peer : peers) {
+ r = resolve_peer_config_keys(pool_id, pool_name, &peer);
+ if (r < 0) {
+ break;
+ }
+ }
+
if (m_service_pools[pool_id] != service_daemon::CALLOUT_ID_NONE) {
m_service_daemon->remove_callout(pool_id, m_service_pools[pool_id]);
m_service_pools[pool_id] = service_daemon::CALLOUT_ID_NONE;
}
- pool_peers->insert({pool_id, Peers{configs.begin(), configs.end()}});
+ pool_peers->emplace(pool_id, Peers{peers.begin(), peers.end()});
}
for (auto it = m_service_pools.begin(); it != m_service_pools.end(); ) {
}
}
+int ClusterWatcher::resolve_peer_config_keys(int64_t pool_id,
+ const std::string& pool_name,
+ PeerSpec* peer) {
+ dout(10) << "retrieving config-key: pool_id=" << pool_id << ", "
+ << "pool_name=" << pool_name << ", "
+ << "peer_uuid=" << peer->uuid << dendl;
+
+ std::string cmd =
+ "{"
+ "\"prefix\": \"config-key get\", "
+ "\"key\": \"" RBD_MIRROR_PEER_CONFIG_KEY_PREFIX + stringify(pool_id) +
+ "/" + peer->uuid + "\""
+ "}";
+
+ bufferlist in_bl;
+ bufferlist out_bl;
+ int r = m_cluster->mon_command(cmd, in_bl, &out_bl, nullptr);
+ if (r == -ENOENT || out_bl.length() == 0) {
+ return 0;
+ } else if (r < 0) {
+ derr << "error reading mirroring peer config for pool " << pool_name << ": "
+ << cpp_strerror(r) << dendl;
+ m_service_pools[pool_id] = m_service_daemon->add_or_update_callout(
+ pool_id, m_service_pools[pool_id],
+ service_daemon::CALLOUT_LEVEL_WARNING,
+ "mirroring peer config-key query failed");
+ 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();
+ if (json_obj.count("mon_host")) {
+ peer->mon_host = json_obj["mon_host"].get_str();
+ }
+ if (json_obj.count("key")) {
+ peer->key = json_obj["key"].get_str();
+ }
+ json_valid = true;
+ } catch (std::runtime_error&) {
+ }
+ }
+
+ if (!json_valid) {
+ derr << "error parsing mirroring peer config for pool " << pool_name << ", "
+ << "peer " << peer->uuid << dendl;
+ m_service_pools[pool_id] = m_service_daemon->add_or_update_callout(
+ pool_id, m_service_pools[pool_id],
+ service_daemon::CALLOUT_LEVEL_WARNING,
+ "mirroring peer config-key decode failed");
+ }
+
+ return 0;
+}
+
} // namespace mirror
} // namespace rbd
*/
class ClusterWatcher {
public:
- typedef std::set<PeerSpec> Peers;
+ struct PeerSpecCompare {
+ bool operator()(const PeerSpec& lhs, const PeerSpec& rhs) const {
+ return (lhs.uuid < rhs.uuid);
+ }
+ };
+ typedef std::set<PeerSpec, PeerSpecCompare> Peers;
typedef std::map<int64_t, Peers> PoolPeers;
ClusterWatcher(RadosRef cluster, Mutex &lock,
PoolPeers m_pool_peers;
void read_pool_peers(PoolPeers *pool_peers);
+
+ int resolve_peer_config_keys(int64_t pool_id, const std::string& pool_name,
+ PeerSpec* peer);
};
} // namespace mirror
std::string cluster_name;
std::string client_name;
- bool operator<(const PeerSpec &rhs) const {
- return uuid < rhs.uuid;
+ /// optional config properties
+ std::string mon_host;
+ std::string key;
+
+ bool operator==(const PeerSpec& rhs) const {
+ return (uuid == rhs.uuid &&
+ cluster_name == rhs.cluster_name &&
+ client_name == rhs.client_name &&
+ mon_host == rhs.mon_host &&
+ key == rhs.key);
}
- bool operator==(const PeerSpec &rhs) const {
- return uuid == rhs.uuid;
+ bool operator<(const PeerSpec& rhs) const {
+ if (uuid != rhs.uuid) {
+ return uuid < rhs.uuid;
+ } else if (cluster_name != rhs.cluster_name) {
+ return cluster_name < rhs.cluster_name;
+ } else if (client_name != rhs.client_name) {
+ return client_name < rhs.client_name;
+ } else if (mon_host < rhs.mon_host) {
+ return mon_host < rhs.mon_host;
+ } else {
+ return key < rhs.key;
+ }
}
};