1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2    	// vim: ts=8 sw=2 smarttab
3    	#include "include/rados/librados.hpp"
4    	#include "common/Cond.h"
5    	#include "common/errno.h"
6    	#include "common/ceph_mutex.h"
7    	#include "librbd/internal.h"
8    	#include "librbd/api/Mirror.h"
9    	#include "tools/rbd_mirror/ClusterWatcher.h"
10   	#include "tools/rbd_mirror/ServiceDaemon.h"
11   	#include "tools/rbd_mirror/Types.h"
12   	#include "test/rbd_mirror/test_fixture.h"
13   	#include "test/librados/test_cxx.h"
14   	#include "test/librbd/test_support.h"
15   	#include "gtest/gtest.h"
16   	#include <boost/scope_exit.hpp>
17   	#include <iostream>
18   	#include <map>
19   	#include <memory>
20   	#include <set>
21   	
22   	using rbd::mirror::ClusterWatcher;
23   	using rbd::mirror::PeerSpec;
24   	using rbd::mirror::RadosRef;
25   	using std::map;
26   	using std::set;
27   	using std::string;
28   	
29   	void register_test_cluster_watcher() {
30   	}
31   	
32   	class TestClusterWatcher : public ::rbd::mirror::TestFixture {
33   	public:
34   	
35   	  TestClusterWatcher() {
36   	    m_cluster = std::make_shared<librados::Rados>();
37   	    EXPECT_EQ("", connect_cluster_pp(*m_cluster));
38   	  }
39   	
(1) Event exn_spec_violation: An exception of type "testing::internal::GoogleTestFailureException" is thrown but the throw list "throw()" doesn't allow it to be thrown. This will cause a call to unexpected() which usually calls terminate().
Also see events: [fun_call_w_exception]
40   	  ~TestClusterWatcher() override {
41   	    m_cluster->wait_for_latest_osdmap();
42   	    for (auto& pool : m_pools) {
(2) Event fun_call_w_exception: Called function throws an exception of type "testing::internal::GoogleTestFailureException". [details]
Also see events: [exn_spec_violation]
43   	      EXPECT_EQ(0, m_cluster->pool_delete(pool.c_str()));
44   	    }
45   	  }
46   	
47   	  void SetUp() override {
48   	    TestFixture::SetUp();
49   	    m_service_daemon.reset(new rbd::mirror::ServiceDaemon<>(g_ceph_context,
50   	                                                            m_cluster,
51   	                                                            m_threads));
52   	    m_cluster_watcher.reset(new ClusterWatcher(m_cluster, m_lock,
53   	                                               m_service_daemon.get()));
54   	  }
55   	
56   	  void TearDown() override {
57   	    m_service_daemon.reset();
58   	    m_cluster_watcher.reset();
59   	    TestFixture::TearDown();
60   	  }
61   	
62   	  void create_pool(bool enable_mirroring, const PeerSpec &peer,
63   	                   string *uuid = nullptr, string *name=nullptr) {
64   	    string pool_name = get_temp_pool_name("test-rbd-mirror-");
65   	    ASSERT_EQ(0, m_cluster->pool_create(pool_name.c_str()));
66   	
67   	    int64_t pool_id = m_cluster->pool_lookup(pool_name.c_str());
68   	    ASSERT_GE(pool_id, 0);
69   	
70   	    librados::IoCtx ioctx;
71   	    ASSERT_EQ(0, m_cluster->ioctx_create2(pool_id, ioctx));
72   	    ioctx.application_enable("rbd", true);
73   	
74   	    m_pools.insert(pool_name);
75   	    if (enable_mirroring) {
76   	      ASSERT_EQ(0, librbd::api::Mirror<>::mode_set(ioctx,
77   	                                                   RBD_MIRROR_MODE_POOL));
78   	
79   	      std::string gen_uuid;
80   	      ASSERT_EQ(0, librbd::api::Mirror<>::peer_add(ioctx,
81   	                                                   uuid != nullptr ? uuid :
82   	                                                                     &gen_uuid,
83   						           peer.cluster_name,
84   						           peer.client_name));
85   	      m_pool_peers[pool_id].insert(peer);
86   	    }
87   	    if (name != nullptr) {
88   	      *name = pool_name;
89   	    }
90   	  }
91   	
92   	  void delete_pool(const string &name, const PeerSpec &peer) {
93   	    int64_t pool_id = m_cluster->pool_lookup(name.c_str());
94   	    ASSERT_GE(pool_id, 0);
95   	    if (m_pool_peers.find(pool_id) != m_pool_peers.end()) {
96   	      m_pool_peers[pool_id].erase(peer);
97   	      if (m_pool_peers[pool_id].empty()) {
98   		m_pool_peers.erase(pool_id);
99   	      }
100  	    }
101  	    m_pools.erase(name);
102  	    ASSERT_EQ(0, m_cluster->pool_delete(name.c_str()));
103  	  }
104  	
105  	  void set_peer_config_key(const std::string& pool_name,
106  	                           const PeerSpec &peer) {
107  	    int64_t pool_id = m_cluster->pool_lookup(pool_name.c_str());
108  	    ASSERT_GE(pool_id, 0);
109  	
110  	    std::string json =
111  	      "{"
112  	        "\\\"mon_host\\\": \\\"" + peer.mon_host + "\\\", "
113  	        "\\\"key\\\": \\\"" + peer.key + "\\\""
114  	      "}";
115  	
116  	    bufferlist in_bl;
117  	    ASSERT_EQ(0, m_cluster->mon_command(
118  	      "{"
119  	        "\"prefix\": \"config-key set\","
120  	        "\"key\": \"" RBD_MIRROR_PEER_CONFIG_KEY_PREFIX + stringify(pool_id) +
121  	          "/" + peer.uuid + "\","
122  	        "\"val\": \"" + json + "\"" +
123  	      "}", in_bl, nullptr, nullptr));
124  	  }
125  	
126  	  void create_cache_pool(const string &base_pool, string *cache_pool_name) {
127  	    bufferlist inbl;
128  	    *cache_pool_name = get_temp_pool_name("test-rbd-mirror-");
129  	    ASSERT_EQ(0, m_cluster->pool_create(cache_pool_name->c_str()));
130  	
131  	    ASSERT_EQ(0, m_cluster->mon_command(
132  	      "{\"prefix\": \"osd tier add\", \"pool\": \"" + base_pool +
133  	      "\", \"tierpool\": \"" + *cache_pool_name +
134  	      "\", \"force_nonempty\": \"--force-nonempty\" }",
135  	      inbl, NULL, NULL));
136  	    ASSERT_EQ(0, m_cluster->mon_command(
137  	      "{\"prefix\": \"osd tier set-overlay\", \"pool\": \"" + base_pool +
138  	      "\", \"overlaypool\": \"" + *cache_pool_name + "\"}",
139  	      inbl, NULL, NULL));
140  	    ASSERT_EQ(0, m_cluster->mon_command(
141  	      "{\"prefix\": \"osd tier cache-mode\", \"pool\": \"" + *cache_pool_name +
142  	      "\", \"mode\": \"writeback\"}",
143  	      inbl, NULL, NULL));
144  	    m_cluster->wait_for_latest_osdmap();
145  	  }
146  	
147  	  void remove_cache_pool(const string &base_pool, const string &cache_pool) {
148  	    bufferlist inbl;
149  	    // tear down tiers
150  	    ASSERT_EQ(0, m_cluster->mon_command(
151  	      "{\"prefix\": \"osd tier remove-overlay\", \"pool\": \"" + base_pool +
152  	      "\"}",
153  	      inbl, NULL, NULL));
154  	    ASSERT_EQ(0, m_cluster->mon_command(
155  	      "{\"prefix\": \"osd tier remove\", \"pool\": \"" + base_pool +
156  	      "\", \"tierpool\": \"" + cache_pool + "\"}",
157  	      inbl, NULL, NULL));
158  	    m_cluster->wait_for_latest_osdmap();
159  	    m_cluster->pool_delete(cache_pool.c_str());
160  	  }
161  	
162  	  void check_peers() {
163  	    m_cluster_watcher->refresh_pools();
164  	    std::lock_guard l{m_lock};
165  	    ASSERT_EQ(m_pool_peers, m_cluster_watcher->get_pool_peers());
166  	  }
167  	
168  	  RadosRef m_cluster;
169  	  ceph::mutex m_lock = ceph::make_mutex("TestClusterWatcherLock");
170  	  unique_ptr<rbd::mirror::ServiceDaemon<>> m_service_daemon;
171  	  unique_ptr<ClusterWatcher> m_cluster_watcher;
172  	
173  	  set<string> m_pools;
174  	  ClusterWatcher::PoolPeers m_pool_peers;
175  	};
176  	
177  	TEST_F(TestClusterWatcher, NoPools) {
178  	  check_peers();
179  	}
180  	
181  	TEST_F(TestClusterWatcher, NoMirroredPools) {
182  	  check_peers();
183  	  create_pool(false, PeerSpec());
184  	  check_peers();
185  	  create_pool(false, PeerSpec());
186  	  check_peers();
187  	  create_pool(false, PeerSpec());
188  	  check_peers();
189  	}
190  	
191  	TEST_F(TestClusterWatcher, ReplicatedPools) {
192  	  PeerSpec site1("", "site1", "mirror1");
193  	  PeerSpec site2("", "site2", "mirror2");
194  	  string first_pool, last_pool;
195  	  check_peers();
196  	  create_pool(true, site1, &site1.uuid, &first_pool);
197  	  check_peers();
198  	  create_pool(false, PeerSpec());
199  	  check_peers();
200  	  create_pool(false, PeerSpec());
201  	  check_peers();
202  	  create_pool(false, PeerSpec());
203  	  check_peers();
204  	  create_pool(true, site2, &site2.uuid);
205  	  check_peers();
206  	  create_pool(true, site2, &site2.uuid);
207  	  check_peers();
208  	  create_pool(true, site2, &site2.uuid, &last_pool);
209  	  check_peers();
210  	  delete_pool(first_pool, site1);
211  	  check_peers();
212  	  delete_pool(last_pool, site2);
213  	  check_peers();
214  	}
215  	
216  	TEST_F(TestClusterWatcher, CachePools) {
217  	  PeerSpec site1("", "site1", "mirror1");
218  	  string base1, base2, cache1, cache2;
219  	  create_pool(true, site1, &site1.uuid, &base1);
220  	  check_peers();
221  	
222  	  create_cache_pool(base1, &cache1);
223  	  BOOST_SCOPE_EXIT( base1, cache1, this_ ) {
224  	    this_->remove_cache_pool(base1, cache1);
225  	  } BOOST_SCOPE_EXIT_END;
226  	  check_peers();
227  	
228  	  create_pool(false, PeerSpec(), nullptr, &base2);
229  	  create_cache_pool(base2, &cache2);
230  	  BOOST_SCOPE_EXIT( base2, cache2, this_ ) {
231  	    this_->remove_cache_pool(base2, cache2);
232  	  } BOOST_SCOPE_EXIT_END;
233  	  check_peers();
234  	}
235  	
236  	TEST_F(TestClusterWatcher, ConfigKey) {
237  	  REQUIRE(!is_librados_test_stub(*m_cluster));
238  	
239  	  std::string pool_name;
240  	  check_peers();
241  	
242  	  PeerSpec site1("", "site1", "mirror1");
243  	  create_pool(true, site1, &site1.uuid, &pool_name);
244  	  check_peers();
245  	
246  	  PeerSpec site2("", "site2", "mirror2");
247  	  site2.mon_host = "abc";
248  	  site2.key = "xyz";
249  	  create_pool(false, site2, &site2.uuid);
250  	  set_peer_config_key(pool_name, site2);
251  	
252  	  check_peers();
253  	}
254