1    	// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2    	// vim: ts=8 sw=2 smarttab
3    	
4    	#include "tools/rbd_mirror/ServiceDaemon.h"
5    	#include "include/Context.h"
6    	#include "include/stringify.h"
7    	#include "common/ceph_context.h"
8    	#include "common/config.h"
9    	#include "common/debug.h"
10   	#include "common/errno.h"
11   	#include "common/Formatter.h"
12   	#include "common/Timer.h"
13   	#include "tools/rbd_mirror/Threads.h"
14   	#include <sstream>
15   	
16   	#define dout_context g_ceph_context
17   	#define dout_subsys ceph_subsys_rbd_mirror
18   	#undef dout_prefix
19   	#define dout_prefix *_dout << "rbd::mirror::ServiceDaemon: " << this << " " \
20   	                           << __func__ << ": "
21   	
22   	namespace rbd {
23   	namespace mirror {
24   	
25   	namespace {
26   	
27   	const std::string RBD_MIRROR_AUTH_ID_PREFIX("rbd-mirror.");
28   	
29   	struct AttributeDumpVisitor : public boost::static_visitor<void> {
30   	  ceph::Formatter *f;
31   	  const std::string& name;
32   	
33   	  AttributeDumpVisitor(ceph::Formatter *f, const std::string& name)
34   	    : f(f), name(name) {
35   	  }
36   	
37   	  void operator()(bool val) const {
38   	    f->dump_bool(name.c_str(), val);
39   	  }
40   	  void operator()(uint64_t val) const {
41   	    f->dump_unsigned(name.c_str(), val);
42   	  }
43   	  void operator()(const std::string& val) const {
44   	    f->dump_string(name.c_str(), val);
45   	  }
46   	};
47   	
48   	} // anonymous namespace
49   	
50   	using namespace service_daemon;
51   	
52   	template <typename I>
53   	ServiceDaemon<I>::ServiceDaemon(CephContext *cct, RadosRef rados,
54   	                                Threads<I>* threads)
55   	  : m_cct(cct), m_rados(rados), m_threads(threads) {
56   	  dout(20) << dendl;
57   	}
58   	
59   	template <typename I>
(1) Event exn_spec_violation: An exception of type "std::length_error" 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]
60   	ServiceDaemon<I>::~ServiceDaemon() {
61   	  dout(20) << dendl;
62   	  std::lock_guard timer_locker{m_threads->timer_lock};
63   	  if (m_timer_ctx != nullptr) {
64   	    m_threads->timer->cancel_event(m_timer_ctx);
(2) Event fun_call_w_exception: Called function throws an exception of type "std::length_error". [details]
Also see events: [exn_spec_violation]
65   	    update_status();
66   	  }
67   	}
68   	
69   	template <typename I>
70   	int ServiceDaemon<I>::init() {
71   	  dout(20) << dendl;
72   	
73   	  std::string id = m_cct->_conf->name.get_id();
74   	  if (id.find(RBD_MIRROR_AUTH_ID_PREFIX) == 0) {
75   	    id = id.substr(RBD_MIRROR_AUTH_ID_PREFIX.size());
76   	  }
77   	
78   	  std::string instance_id = stringify(m_rados->get_instance_id());
79   	  std::map<std::string, std::string> service_metadata = {
80   	    {"id", id}, {"instance_id", instance_id}};
81   	  int r = m_rados->service_daemon_register("rbd-mirror", instance_id,
82   	                                           service_metadata);
83   	  if (r < 0) {
84   	    return r;
85   	  }
86   	
87   	  return 0;
88   	}
89   	
90   	template <typename I>
91   	void ServiceDaemon<I>::add_pool(int64_t pool_id, const std::string& pool_name) {
92   	  dout(20) << "pool_id=" << pool_id << ", pool_name=" << pool_name << dendl;
93   	
94   	  {
95   	    std::lock_guard locker{m_lock};
96   	    m_pools.insert({pool_id, {pool_name}});
97   	  }
98   	  schedule_update_status();
99   	}
100  	
101  	template <typename I>
102  	void ServiceDaemon<I>::remove_pool(int64_t pool_id) {
103  	  dout(20) << "pool_id=" << pool_id << dendl;
104  	  {
105  	    std::lock_guard locker{m_lock};
106  	    m_pools.erase(pool_id);
107  	  }
108  	  schedule_update_status();
109  	}
110  	
111  	template <typename I>
112  	uint64_t ServiceDaemon<I>::add_or_update_callout(int64_t pool_id,
113  	                                                 uint64_t callout_id,
114  	                                                 CalloutLevel callout_level,
115  	                                                 const std::string& text) {
116  	  dout(20) << "pool_id=" << pool_id << ", "
117  	           << "callout_id=" << callout_id << ", "
118  	           << "callout_level=" << callout_level << ", "
119  	           << "text=" << text << dendl;
120  	
121  	  {
122  	    std::lock_guard locker{m_lock};
123  	    auto pool_it = m_pools.find(pool_id);
124  	    if (pool_it == m_pools.end()) {
125  	      return CALLOUT_ID_NONE;
126  	    }
127  	
128  	    if (callout_id == CALLOUT_ID_NONE) {
129  	      callout_id = ++m_callout_id;
130  	    }
131  	    pool_it->second.callouts[callout_id] = {callout_level, text};
132  	  }
133  	
134  	  schedule_update_status();
135  	  return callout_id;
136  	}
137  	
138  	template <typename I>
139  	void ServiceDaemon<I>::remove_callout(int64_t pool_id, uint64_t callout_id) {
140  	  dout(20) << "pool_id=" << pool_id << ", "
141  	           << "callout_id=" << callout_id << dendl;
142  	
143  	  {
144  	    std::lock_guard locker{m_lock};
145  	    auto pool_it = m_pools.find(pool_id);
146  	    if (pool_it == m_pools.end()) {
147  	      return;
148  	    }
149  	    pool_it->second.callouts.erase(callout_id);
150  	  }
151  	
152  	  schedule_update_status();
153  	}
154  	
155  	template <typename I>
156  	void ServiceDaemon<I>::add_or_update_attribute(int64_t pool_id,
157  	                                               const std::string& key,
158  	                                               const AttributeValue& value) {
159  	  dout(20) << "pool_id=" << pool_id << ", "
160  	           << "key=" << key << ", "
161  	           << "value=" << value << dendl;
162  	
163  	  {
164  	    std::lock_guard locker{m_lock};
165  	    auto pool_it = m_pools.find(pool_id);
166  	    if (pool_it == m_pools.end()) {
167  	      return;
168  	    }
169  	    pool_it->second.attributes[key] = value;
170  	  }
171  	
172  	  schedule_update_status();
173  	}
174  	
175  	template <typename I>
176  	void ServiceDaemon<I>::remove_attribute(int64_t pool_id,
177  	                                        const std::string& key) {
178  	  dout(20) << "pool_id=" << pool_id << ", "
179  	           << "key=" << key << dendl;
180  	
181  	  {
182  	    std::lock_guard locker{m_lock};
183  	    auto pool_it = m_pools.find(pool_id);
184  	    if (pool_it == m_pools.end()) {
185  	      return;
186  	    }
187  	    pool_it->second.attributes.erase(key);
188  	  }
189  	
190  	  schedule_update_status();
191  	}
192  	
193  	template <typename I>
194  	void ServiceDaemon<I>::schedule_update_status() {
195  	  std::lock_guard timer_locker{m_threads->timer_lock};
196  	  if (m_timer_ctx != nullptr) {
197  	    return;
198  	  }
199  	
200  	  m_timer_ctx = new LambdaContext([this](int) {
201  	      m_timer_ctx = nullptr;
202  	      update_status();
203  	    });
204  	  m_threads->timer->add_event_after(1, m_timer_ctx);
205  	}
206  	
207  	template <typename I>
208  	void ServiceDaemon<I>::update_status() {
(1) Event fun_call_w_exception: Called function throws an exception of type "std::length_error". [details]
209  	  dout(20) << dendl;
210  	  ceph_assert(ceph_mutex_is_locked(m_threads->timer_lock));
211  	
212  	  ceph::JSONFormatter f;
213  	  {
214  	    std::lock_guard locker{m_lock};
215  	    f.open_object_section("pools");
216  	    for (auto& pool_pair : m_pools) {
217  	      f.open_object_section(stringify(pool_pair.first).c_str());
218  	      f.dump_string("name", pool_pair.second.name);
219  	      f.open_object_section("callouts");
220  	      for (auto& callout : pool_pair.second.callouts) {
221  	        f.open_object_section(stringify(callout.first).c_str());
222  	        f.dump_string("level", stringify(callout.second.level).c_str());
223  	        f.dump_string("text", callout.second.text.c_str());
224  	        f.close_section();
225  	      }
226  	      f.close_section(); // callouts
227  	
228  	      for (auto& attribute : pool_pair.second.attributes) {
229  	        AttributeDumpVisitor attribute_dump_visitor(&f, attribute.first);
230  	        boost::apply_visitor(attribute_dump_visitor, attribute.second);
231  	      }
232  	      f.close_section(); // pool
233  	    }
234  	    f.close_section(); // pools
235  	  }
236  	
237  	  std::stringstream ss;
238  	  f.flush(ss);
239  	
240  	  int r = m_rados->service_daemon_update_status({{"json", ss.str()}});
241  	  if (r < 0) {
242  	    derr << "failed to update service daemon status: " << cpp_strerror(r)
243  	         << dendl;
244  	  }
245  	}
246  	
247  	} // namespace mirror
248  	} // namespace rbd
249  	
250  	template class rbd::mirror::ServiceDaemon<librbd::ImageCtx>;
251