Fawkes API  Fawkes Development Version
mongodb_replicaset_config.cpp
1 
2 /***************************************************************************
3  * mongodb_replicaset_config.cpp - MongoDB replica set configuration
4  *
5  * Created: Thu Jul 13 10:25:19 2017
6  * Copyright 2006-2018 Tim Niemueller [www.niemueller.de]
7  ****************************************************************************/
8 
9 /* This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Library General Public License for more details.
18  *
19  * Read the full text in the LICENSE.GPL file in the doc directory.
20  */
21 
22 #include "mongodb_replicaset_config.h"
23 
24 #include "mongodb_client_config.h"
25 #include "utils.h"
26 
27 #include <config/config.h>
28 #include <logging/logger.h>
29 #include <utils/time/wait.h>
30 
31 #include <bsoncxx/builder/basic/document.hpp>
32 #include <chrono>
33 #include <iterator>
34 #include <mongocxx/exception/operation_exception.hpp>
35 #include <numeric>
36 
37 using namespace fawkes;
38 using namespace bsoncxx::builder;
39 
40 /** @class MongoDBReplicaSetConfig "mongodb_replicaset_config.h"
41  * MongoDB replica set configuration.
42  * Configure a replica set. This only takes care of initializing and
43  * maintaining the configuration of a running replica set. The
44  * mongod instances have to be handled separately, for example using
45  * instance configurations.
46  *
47  * @author Tim Niemueller
48  */
49 
50 /** Constructor.
51  * This will read the given configuration.
52  * @param cfgname configuration name
53  * @param prefix configuration path prefix
54  * @param bootstrap_prefix configuration path prefix for bootstrap configuration
55  */
57  const std::string &prefix,
58  const std::string &bootstrap_prefix)
59 : Thread("MongoDBReplicaSet", Thread::OPMODE_CONTINUOUS),
60  leader_elec_query_(bsoncxx::builder::basic::document()),
61  leader_elec_query_force_(bsoncxx::builder::basic::document()),
62  leader_elec_update_(bsoncxx::builder::basic::document())
63 {
64  set_name("MongoDBReplicaSet|%s", cfgname.c_str());
65  config_name_ = cfgname;
66  prefix_ = prefix;
67  bootstrap_prefix_ = bootstrap_prefix;
68  is_leader_ = false;
69  last_status_ =
70  ReplicaSetStatus{.member_status = MongoDBManagedReplicaSetInterface::ERROR,
71  .primary_status = MongoDBManagedReplicaSetInterface::PRIMARY_UNKNOWN};
72 
73  enabled_ = false;
74 }
75 
76 void
78 {
79  try {
80  enabled_ = config->get_bool(prefix_ + "enabled");
81  } catch (Exception &e) {
82  }
83  if (!enabled_) {
84  throw Exception("Replica set manager '%s' cannot be started while disabled", name());
85  }
86 
88  "Bootstrap Query: %s",
89  bsoncxx::to_json(leader_elec_query_.view()).c_str());
91  "Bootstrap Update: %s",
92  bsoncxx::to_json(leader_elec_update_.view()).c_str());
93 
94  rs_status_if_ =
95  blackboard->open_for_writing<MongoDBManagedReplicaSetInterface>(config_name_.c_str());
96 
97  if (enabled_) {
98  bootstrap_database_ = config->get_string(bootstrap_prefix_ + "database");
99  std::string bootstrap_client_cfg = config->get_string(bootstrap_prefix_ + "client");
100  MongoDBClientConfig bootstrap_client_config(config,
101  logger,
102  bootstrap_client_cfg,
103  "/plugins/mongodb/clients/" + bootstrap_client_cfg
104  + "/");
105  if (!bootstrap_client_config.is_enabled()) {
106  throw Exception("%s: bootstrap client configuration '%s' disabled",
107  name(),
108  bootstrap_client_cfg.c_str());
109  }
110  bootstrap_client_.reset(bootstrap_client_config.create_client());
111  bootstrap_ns_ = bootstrap_database_ + "." + config_name_;
112 
113  local_client_cfg_ = config->get_string(prefix_ + "local-client");
114  loop_interval_ = 5.0;
115  try {
116  loop_interval_ = config->get_float(prefix_ + "loop-interval");
117  } catch (Exception &e) {
118  } // ignored, use default
119 
120  timewait_ = new TimeWait(clock, (int)(loop_interval_ * 1000000.));
121 
122  leader_expiration_ = 10;
123  try {
124  leader_expiration_ = config->get_int(prefix_ + "leader-expiration");
125  } catch (Exception &e) {
126  } // ignored, use default
127 
128  MongoDBClientConfig client_config(config,
129  logger,
130  local_client_cfg_,
131  "/plugins/mongodb/clients/" + local_client_cfg_ + "/");
132  if (!client_config.is_enabled()) {
133  throw Exception("%s: local client configuration '%s' disabled",
134  name(),
135  local_client_cfg_.c_str());
136  }
137  if (client_config.mode() != MongoDBClientConfig::CONNECTION) {
138  throw Exception("%s: local client configuration '%s' mode is not connection",
139  name(),
140  local_client_cfg_.c_str());
141  }
142  local_hostport_ = client_config.hostport();
143  std::vector<std::string> hostv = config->get_strings(prefix_ + "hosts");
144  std::copy(hostv.begin(), hostv.end(), std::inserter(hosts_, hosts_.end()));
145 
146  if (std::find(hosts_.begin(), hosts_.end(), local_hostport_) == hosts_.end()) {
147  throw Exception("%s host list does not include local client", name());
148  }
149 
150  using namespace bsoncxx::builder::basic;
151 
152  auto leader_elec_query_builder = basic::document{};
153  leader_elec_query_builder.append(basic::kvp("host", local_hostport_));
154  leader_elec_query_builder.append(basic::kvp("master", false));
155  leader_elec_query_ = leader_elec_query_builder.extract();
156 
157  auto leader_elec_query_force_builder = basic::document{};
158  leader_elec_query_builder.append(basic::kvp("master", true));
159  leader_elec_query_force_ = leader_elec_query_force_builder.extract();
160 
161  auto leader_elec_update_builder = basic::document{};
162  leader_elec_update_builder.append(basic::kvp("$currentDate", [](basic::sub_document subdoc) {
163  subdoc.append(basic::kvp("last_seen", true));
164  }));
165  leader_elec_update_builder.append(basic::kvp("$set", [this](basic::sub_document subdoc) {
166  subdoc.append(basic::kvp("master", true));
167  subdoc.append(basic::kvp("host", local_hostport_));
168  }));
169  leader_elec_update_ = leader_elec_update_builder.extract();
170 
171  local_client_.reset(client_config.create_client());
172  }
173  bootstrap();
174 }
175 
176 /** Setup replicaset bootstrap client.
177  * @param bootstrap_client MongoDB client to access bootstrap database
178  */
179 void
180 MongoDBReplicaSetConfig::bootstrap()
181 {
182  if (enabled_) {
183  try {
184  auto database = bootstrap_client_->database(bootstrap_database_);
185  auto collection = database[bootstrap_ns_];
186 
187  collection.create_index(basic::make_document(basic::kvp("host", 1)));
188  collection.create_index(basic::make_document(basic::kvp("master", 1)),
189  basic::make_document(basic::kvp("unique", true)));
190  collection.create_index(basic::make_document(basic::kvp("last_seen", 1)),
191  basic::make_document(
192  basic::kvp("expireAfterSeconds", leader_expiration_)));
193  } catch (mongocxx::operation_exception &e) {
194  logger->log_error(name(), "Failed to initialize bootstrap client: %s", e.what());
195  throw;
196  }
197  }
198 }
199 
200 bool
201 MongoDBReplicaSetConfig::leader_elect(bool force)
202 {
203  try {
204  auto write_concern = mongocxx::write_concern();
205  write_concern.majority(std::chrono::milliseconds(0));
206  auto result = bootstrap_client_->database(bootstrap_database_)[bootstrap_ns_].update_one(
207  force ? leader_elec_query_force_.view() : leader_elec_query_.view(),
208  leader_elec_update_.view(),
209  mongocxx::options::update().upsert(true).write_concern(write_concern));
210  if (result) {
211  if (!is_leader_) {
212  is_leader_ = true;
213  logger->log_info(name(), "Became replica set leader");
214  }
215  } else {
216  if (is_leader_) {
217  is_leader_ = false;
218  logger->log_warn(name(), "Lost replica set leadership");
219  }
220  }
221  } catch (mongocxx::operation_exception &e) {
222  if (boost::optional<bsoncxx::document::value> error = e.raw_server_error()) {
223  bsoncxx::array::view writeErrors = error->view()["writeErrors"].get_array();
224  // Do not use writeErrors.length(), which is not the number of errors!
225  auto num_errors = std::distance(writeErrors.begin(), writeErrors.end());
226  int error_code = -1;
227  if (num_errors > 0) {
228  error_code = error->view()["writeErrors"][0]["code"].get_int32();
229  }
230  if (num_errors > 1 || error_code != 11000) {
231  // 11000: Duplicate key exception, occurs if we do not become leader, all fine
232  logger->log_error(name(),
233  "Leader election failed (%i): %s %s",
234  error_code,
235  e.what(),
236  bsoncxx::to_json(error->view()).c_str());
237  is_leader_ = false;
238  } else if (is_leader_) {
239  logger->log_warn(name(), "Lost replica set leadership");
240  is_leader_ = false;
241  }
242  } else {
243  logger->log_error(name(), "Leader election failed; failed to fetch error code: %s", e.what());
244  }
245  }
246  return is_leader_;
247 }
248 
249 void
250 MongoDBReplicaSetConfig::leader_resign()
251 {
252  if (is_leader_) {
253  logger->log_info(name(), "Resigning replica set leadership");
254  auto write_concern = mongocxx::write_concern();
255  write_concern.majority(std::chrono::milliseconds(0));
256  bootstrap_client_->database(bootstrap_database_)[bootstrap_ns_].delete_one(
257  leader_elec_query_.view(), mongocxx::options::delete_options().write_concern(write_concern));
258  }
259 }
260 
261 void
263 {
264  leader_resign();
265  blackboard->close(rs_status_if_);
266 
267  delete timewait_;
268 }
269 
270 void
272 {
273  timewait_->mark_start();
274  bsoncxx::document::value reply{bsoncxx::builder::basic::document()};
275  ReplicaSetStatus status = rs_status(reply);
276 
277  if (status.primary_status == MongoDBManagedReplicaSetInterface::NO_PRIMARY) {
278  logger->log_warn(name(), "No primary, triggering leader election");
279  if (leader_elect(/* force leadership */ false)) {
280  logger->log_info(name(), "No primary, we became leader, managing");
281  rs_monitor(reply);
282  }
283  }
284 
285  switch (status.member_status) {
286  case MongoDBManagedReplicaSetInterface::PRIMARY:
287  if (last_status_.member_status != status.member_status) {
288  logger->log_info(name(), "Became PRIMARY, starting managing");
289  }
290  leader_elect(/* force leaderhsip */ true);
291  rs_monitor(reply);
292  break;
293  case MongoDBManagedReplicaSetInterface::SECONDARY:
294  if (last_status_.member_status != status.member_status) {
295  logger->log_info(name(), "Became SECONDARY");
296  }
297  break;
298  case MongoDBManagedReplicaSetInterface::ARBITER:
299  //logger->log_info(name(), "Arbiter");
300  break;
301  case MongoDBManagedReplicaSetInterface::NOT_INITIALIZED:
302  if (hosts_.size() == 1 || leader_elect()) {
303  // we are alone or leader, initialize replica set
304  if (hosts_.size() == 1) {
305  logger->log_info(name(), "Now initializing RS (alone)");
306  } else {
307  logger->log_info(name(), "Now initializing RS (leader)");
308  }
309  rs_init();
310  }
311  break;
312  case MongoDBManagedReplicaSetInterface::INVALID_CONFIG:
313  // we might later want to cover some typical cases
314  logger->log_error(name(),
315  "Invalid configuration, hands-on required\n%s",
316  bsoncxx::to_json(reply.view()).c_str());
317  break;
318  default: break;
319  }
320 
321  if (last_status_ != status) {
322  rs_status_if_->set_member_status(status.member_status);
323  rs_status_if_->set_primary_status(status.primary_status);
324  rs_status_if_->set_error_msg(status.error_msg.c_str());
325  rs_status_if_->write();
326 
327  last_status_ = status;
328  }
329 
330  timewait_->wait_systime();
331 }
332 
333 MongoDBReplicaSetConfig::ReplicaSetStatus
334 MongoDBReplicaSetConfig::rs_status(bsoncxx::document::value &reply)
335 {
336  ReplicaSetStatus status = {.member_status = MongoDBManagedReplicaSetInterface::ERROR,
337  .primary_status = MongoDBManagedReplicaSetInterface::PRIMARY_UNKNOWN};
338 
339  auto cmd = basic::make_document(basic::kvp("replSetGetStatus", 1));
340  try {
341  reply = local_client_->database("admin").run_command(std::move(cmd));
342  } catch (mongocxx::operation_exception &e) {
343  int error_code = -1;
344  auto error_code_element = e.raw_server_error()->view()["code"];
345  if (error_code_element && error_code_element.type() == bsoncxx::type::k_int32) {
346  error_code = e.raw_server_error()->view()["code"].get_int32();
347  }
348  if (error_code == 94 /* NotYetInitialized */) {
349  logger->log_warn(name(), "Instance has not received replica set configuration, yet");
350  status.member_status = MongoDBManagedReplicaSetInterface::NOT_INITIALIZED;
351  status.error_msg = "Instance has not received replica set configuration, yet";
352  } else if (error_code == 93 /* InvalidReplicaSetConfig */) {
353  logger->log_error(name(),
354  "Invalid replica set configuration: %s",
355  bsoncxx::to_json(reply.view()).c_str());
356  status.member_status = MongoDBManagedReplicaSetInterface::INVALID_CONFIG;
357  status.error_msg = "Invalid replica set configuration: " + bsoncxx::to_json(reply.view());
358  } else {
359  status.error_msg = "Unknown error";
360  }
361  return status;
362  }
363  //logger->log_warn(name(), "rs status reply: %s", bsoncxx::to_json(reply.view()).c_str());
364  try {
365  MongoDBManagedReplicaSetInterface::ReplicaSetMemberStatus self_status =
366  MongoDBManagedReplicaSetInterface::REMOVED;
367  auto members = reply.view()["members"];
368  if (members && members.type() == bsoncxx::type::k_array) {
369  bsoncxx::array::view members_view{members.get_array().value};
370  bool have_primary = false;
371  for (bsoncxx::array::element member : members_view) {
372  int state = member["state"].get_int32();
373  if (state == 1) {
374  have_primary = true;
375  }
376  if (member["self"] && member["self"].get_bool()) {
377  switch (state) {
378  case 1: self_status = MongoDBManagedReplicaSetInterface::PRIMARY; break;
379  case 2: self_status = MongoDBManagedReplicaSetInterface::SECONDARY; break;
380  case 3: // RECOVERING
381  case 5: // STARTUP2
382  case 9: // ROLLBACK
383  self_status = MongoDBManagedReplicaSetInterface::INITIALIZING;
384  break;
385  case 7: self_status = MongoDBManagedReplicaSetInterface::ARBITER; break;
386  default: self_status = MongoDBManagedReplicaSetInterface::ERROR; break;
387  }
388  }
389  }
390  status.primary_status = have_primary ? MongoDBManagedReplicaSetInterface::HAVE_PRIMARY
391  : MongoDBManagedReplicaSetInterface::NO_PRIMARY;
392  status.member_status = self_status;
393  return status;
394  } else {
395  logger->log_error(name(),
396  "Received replica set status reply without members, unknown status");
397  self_status = MongoDBManagedReplicaSetInterface::ERROR;
398  }
399  } catch (mongocxx::operation_exception &e) {
400  logger->log_warn(name(), "Failed to analyze member info: %s", e.what());
401  status.member_status = MongoDBManagedReplicaSetInterface::ERROR;
402  status.error_msg = std::string("Failed to analyze member info: ") + e.what();
403  return status;
404  }
405  return status;
406 }
407 
408 void
409 MongoDBReplicaSetConfig::rs_init()
410 {
411  // using default configuration, this will just add ourself
412  auto cmd = basic::make_document(basic::kvp("replSetInitiate", basic::document{}));
413  bsoncxx::document::value reply{bsoncxx::builder::basic::document()};
414  try {
415  reply = local_client_->database("admin").run_command(std::move(cmd));
416  bool ok = check_mongodb_ok(reply.view());
417  if (!ok) {
418  logger->log_error(name(),
419  "RS initialization failed: %s",
420  reply.view()["errmsg"].get_utf8().value.to_string().c_str());
421  } else {
422  logger->log_debug(name(),
423  "RS initialized successfully: %s",
424  bsoncxx::to_json(reply.view()).c_str());
425  }
426  } catch (mongocxx::operation_exception &e) {
427  logger->log_error(name(), "RS initialization failed: %s", e.what());
428  }
429 }
430 
431 bool
432 MongoDBReplicaSetConfig::rs_get_config(bsoncxx::document::value &rs_config)
433 {
434  auto cmd = basic::make_document(basic::kvp("replSetGetConfig", 1));
435 
436  try {
437  bsoncxx::document::value reply{bsoncxx::builder::basic::document()};
438  reply = local_client_->database("admin").run_command(std::move(cmd));
439  bool ok = check_mongodb_ok(reply.view());
440  if (ok) {
441  rs_config = reply;
442  //logger->log_info(name(), "Config: %s", bsoncxx::to_json(rs_config.view()["config"]).c_str());
443  } else {
444  logger->log_warn(name(),
445  "Failed to get RS config: %s (DB error)",
446  bsoncxx::to_json(reply.view()).c_str());
447  }
448  return ok;
449  } catch (mongocxx::operation_exception &e) {
450  logger->log_warn(name(), "Failed to get RS config: %s", e.what());
451  return false;
452  }
453 }
454 
455 void
456 MongoDBReplicaSetConfig::rs_monitor(const bsoncxx::document::view &status_reply)
457 {
458  using namespace std::chrono_literals;
459 
460  std::set<std::string> in_rs, unresponsive, new_alive, members;
461  int last_member_id{0};
462  bsoncxx::array::view members_view{status_reply["members"].get_array().value};
463  for (bsoncxx::array::element member : members_view) {
464  std::string member_name = member["name"].get_utf8().value.to_string();
465  members.insert(member_name);
466  last_member_id = std::max(int(member["_id"].get_int32()), last_member_id);
467  if (member["self"] && member["self"].get_bool()) {
468  in_rs.insert(member_name);
469  } else {
470  std::chrono::time_point<std::chrono::high_resolution_clock> last_heartbeat_rcvd(
471  std::chrono::milliseconds(member["lastHeartbeatRecv"].get_date()));
472  auto now = std::chrono::high_resolution_clock::now();
473  if ((int(member["health"].get_double()) != 1) || (now - last_heartbeat_rcvd) > 15s) {
474  unresponsive.insert(member_name);
475  } else {
476  in_rs.insert(member_name);
477  }
478  }
479  }
480  std::set<std::string> not_member;
481  std::set_difference(hosts_.begin(),
482  hosts_.end(),
483  in_rs.begin(),
484  in_rs.end(),
485  std::inserter(not_member, not_member.end()));
486 
487  for (const std::string &h : not_member) {
488  // Check if this host became alive, and add if it did
489  if (check_alive(h)) {
490  logger->log_info(name(), "Host %s alive, adding to RS", h.c_str());
491  new_alive.insert(h);
492  //} else {
493  //logger->log_info(name(), "Potential member %s not responding", h.c_str());
494  }
495  }
496 
497  if (!unresponsive.empty() || !new_alive.empty()) {
498  // generate new config
499  bsoncxx::document::value reply{bsoncxx::builder::basic::document()};
500  if (!rs_get_config(reply)) {
501  return;
502  }
503  auto config = reply.view()["config"].get_document().view();
504  using namespace bsoncxx::builder::basic;
505  logger->log_info(name(), "Creating new config");
506  auto new_config = basic::document{};
507  for (auto &&key_it = config.begin(); key_it != config.end(); key_it++) {
508  if (key_it->key() == "version") {
509  new_config.append(basic::kvp("version", config["version"].get_int32() + 1));
510  //new_config = new_config << "version" << config["version"].get_int32() + 1;
511  } else if (key_it->key() == "members") {
512  bsoncxx::array::view members_view{config["members"].get_array().value};
513  new_config.append(basic::kvp("members", [&](basic::sub_array array) {
514  for (bsoncxx::array::element member : members_view) {
515  std::string host = member["host"].get_utf8().value.to_string();
516  if (hosts_.find(host) == hosts_.end()) {
517  logger->log_warn(name(),
518  "Removing '%s', "
519  "not part of the replica set configuration",
520  host.c_str());
521  } else if (unresponsive.find(host) == unresponsive.end()) {
522  // it's not unresponsive, add
523  logger->log_warn(name(), "Keeping RS member '%s'", host.c_str());
524  array.append(basic::make_document(basic::kvp("host", host),
525  basic::kvp("_id", member["_id"].get_value())));
526  } else {
527  logger->log_warn(name(), "Removing RS member '%s'", host.c_str());
528  }
529  }
530  for (const std::string &h : new_alive) {
531  logger->log_info(name(), "Adding new RS member '%s'", h.c_str());
532  array.append(
533  basic::make_document(basic::kvp("host", h), basic::kvp("_id", ++last_member_id)));
534  }
535  }));
536  } else {
537  new_config.append(basic::kvp(key_it->key(), key_it->get_value()));
538  }
539  }
540 
541  //mongo::BSONObj new_config_obj(new_config.obj());
542  //logger->log_info(name(), "Reconfigure: %s", new_config_obj.jsonString(mongo::Strict, true).c_str());
543 
544  auto cmd = basic::document{};
545  cmd.append(basic::kvp("replSetReconfig", new_config));
546  cmd.append(basic::kvp("force", true));
547  try {
548  logger->log_info(name(), "Running command");
549  auto reply = local_client_->database("admin").run_command(cmd.view());
550  logger->log_info(name(), "done");
551  bool ok = check_mongodb_ok(reply.view());
552  if (!ok) {
553  logger->log_error(name(),
554  "RS reconfig failed: %s (DB error)",
555  reply.view()["errmsg"].get_utf8().value.to_string().c_str());
556  }
557  } catch (mongocxx::operation_exception &e) {
558  logger->log_warn(name(), "RS reconfig failed: %s (exception)", e.what());
559  }
560  }
561 }
562 
563 bool
564 MongoDBReplicaSetConfig::check_alive(const std::string &h)
565 {
566  using namespace bsoncxx::builder::basic;
567  try {
568  mongocxx::client client{mongocxx::uri{"mongodb://" + h}};
569  auto cmd = basic::document{};
570  cmd.append(basic::kvp("isMaster", 1));
571  auto reply = client.database("admin").run_command(cmd.view());
572  bool ok = check_mongodb_ok(reply.view());
573  if (!ok) {
574  logger->log_warn(name(), "Failed to connect: %s", bsoncxx::to_json(reply.view()).c_str());
575  }
576  return ok;
577  } catch (mongocxx::operation_exception &e) {
578  logger->log_warn(name(), "Fail: %s", e.what());
579  return false;
580  }
581 }
MongoDBClientConfig
Client configuration.
Definition: mongodb_client_config.h:37
MongoDBClientConfig::hostport
std::string hostport() const
Get host and port of configuration.
Definition: mongodb_client_config.cpp:193
fawkes::Configuration::get_bool
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
MongoDBReplicaSetConfig::finalize
virtual void finalize()
Finalize the thread.
Definition: mongodb_replicaset_config.cpp:262
fawkes::Logger::log_info
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
MongoDBClientConfig::create_client
mongocxx::client * create_client()
Create MongoDB client for this configuration.
Definition: mongodb_client_config.cpp:151
fawkes::Thread::name
const char * name() const
Get name of thread.
Definition: thread.h:100
fawkes::ClockAspect::clock
Clock * clock
By means of this member access to the clock is given.
Definition: clock.h:42
MongoDBClientConfig::mode
ConnectionMode mode() const
Get client configuration mode.
Definition: mongodb_client_config.cpp:208
fawkes::Configuration::get_int
virtual int get_int(const char *path)=0
Get value from configuration which is of type int.
MongoDBClientConfig::CONNECTION
@ CONNECTION
connect to single node
Definition: mongodb_client_config.h:41
fawkes::LoggingAspect::logger
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
fawkes::BlackBoard::close
virtual void close(Interface *interface)=0
Close interface.
fawkes::Logger::log_error
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
fawkes
Fawkes library namespace.
MongoDBReplicaSetConfig::init
virtual void init()
Initialize the thread.
Definition: mongodb_replicaset_config.cpp:77
fawkes::Logger::log_warn
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
fawkes::Configuration::get_strings
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
fawkes::TimeWait::mark_start
void mark_start()
Mark start of loop.
Definition: wait.cpp:68
fawkes::ConfigurableAspect::config
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
MongoDBReplicaSetConfig::loop
virtual void loop()
Code to execute in the thread.
Definition: mongodb_replicaset_config.cpp:271
fawkes::Configuration::get_float
virtual float get_float(const char *path)=0
Get value from configuration which is of type float.
fawkes::Thread
Thread class encapsulation of pthreads.
Definition: thread.h:46
fawkes::TimeWait
Time wait utility.
Definition: wait.h:33
fawkes::BlackBoardAspect::blackboard
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
fawkes::TimeWait::wait_systime
void wait_systime()
Wait until minimum loop time has been reached in real time.
Definition: wait.cpp:96
fawkes::Configuration::get_string
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
MongoDBReplicaSetConfig::MongoDBReplicaSetConfig
MongoDBReplicaSetConfig(const std::string &cfgname, const std::string &prefix, const std::string &bootstrap_prefix)
Constructor.
Definition: mongodb_replicaset_config.cpp:56
fawkes::Logger::log_debug
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
fawkes::BlackBoard::open_for_writing
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
fawkes::Thread::set_name
void set_name(const char *format,...)
Set name of thread.
Definition: thread.cpp:748
MongoDBClientConfig::is_enabled
bool is_enabled() const
Check if configuration is enabled.
Definition: mongodb_client_config.h:55
fawkes::Exception
Base class for exceptions in Fawkes.
Definition: exception.h:36