Fawkes API  Fawkes Development Version
mongodb_client_config.cpp
1 
2 /***************************************************************************
3  * mongodb_client_config.cpp - MongoDB client configuration
4  *
5  * Created: Wed Jul 12 13:45:03 2017
6  * Copyright 2006-2017 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_client_config.h"
23 
24 #include <config/config.h>
25 #include <logging/logger.h>
26 
27 using namespace fawkes;
28 
29 /** @class MongoDBClientConfig "mongodb_client_config.h"
30  * MongoDB Client Configuration.
31  * Instances of this class represent a single MongoDB client configuration
32  * used to initiate connections.
33  *
34  * @author Tim Niemueller
35  */
36 
37 /** Constructor.
38  * This will read the given configuration.
39  * @param config configuration to query
40  * @param logger logger for info messages
41  * @param cfgname configuration name
42  * @param prefix configuration path prefix
43  */
45  Logger * logger,
46  std::string cfgname,
47  std::string prefix)
48 {
49  logcomp_ = "MongoDBClient|" + cfgname;
50 
51  enabled_ = false;
52  try {
53  enabled_ = config->get_bool((prefix + "enabled").c_str());
54  } catch (Exception &e) {
55  }
56 
57  std::string mode = "connection";
58  try {
59  mode = config->get_string((prefix + "mode").c_str());
60  } catch (Exception &e) {
61  logger->log_info(logcomp_.c_str(),
62  "MongoDB config '%s' specifies no client "
63  "mode, assuming 'connection'.",
64  cfgname.c_str());
65  }
66 
67  if (mode == "replica_set" || mode == "replicaset") {
68  mode_ = REPLICA_SET;
69  replicaset_name_ = config->get_string((prefix + "name").c_str());
70 
71  std::vector<std::string> hosts = config->get_strings(prefix + "hosts");
72  std::transform(hosts.begin(),
73  hosts.end(),
74  std::back_inserter(replicaset_hostports_),
75  [](const std::string &s) -> mongo::HostAndPort {
76  return mongo::HostAndPort(s);
77  });
78 
79  } else if (mode == "sync_cluster" || mode == "synccluster") {
80  throw Exception("sync_cluster connections are no longer supported");
81 
82  } else {
83  mode_ = CONNECTION;
84 
85  conn_hostport_ = mongo::HostAndPort(config->get_string(prefix + "hostport"));
86  }
87 }
88 
89 /** Read authentication info for given configuration.
90  * This will first try to read the fields auth_dbname, auth_username, and
91  * auth_password. If that fails, the auth/ subdirectory is crawled for subtrees
92  * that contain the just named entries.
93  * @param config configuration to query
94  * @param logger logger for info messages
95  * @param cfgname configuration name
96  * @param prefix configuration path prefix
97  */
98 void
99 MongoDBClientConfig::read_authinfo(Configuration *config,
100  Logger * logger,
101  std::string cfgname,
102  std::string prefix)
103 {
104  std::set<std::string> authinfos;
105 
106  try {
107  std::string dbname = config->get_string((prefix + "auth_dbname").c_str());
108  std::string username = config->get_string((prefix + "auth_username").c_str());
109  std::string password = config->get_string((prefix + "auth_password").c_str());
110  auth_infos_.push_back(AuthInfo(dbname, username, password));
111  } catch (Exception &e) {
112  logger->log_info(logcomp_.c_str(),
113  "No default authentication info for "
114  "MongoDB client '%s'",
115  cfgname.c_str());
116  }
117 
118  std::unique_ptr<Configuration::ValueIterator> i(config->search((prefix + "auth/").c_str()));
119  while (i->next()) {
120  std::string auth_name = std::string(i->path()).substr(prefix.length());
121  auth_name = auth_name.substr(0, auth_name.find("/"));
122 
123  if (authinfos.find(auth_name) == authinfos.end()) {
124  try {
125  std::string ap = prefix + auth_name + "/";
126  std::string dbname = config->get_string((ap + "auth_dbname").c_str());
127  std::string username = config->get_string((ap + "auth_username").c_str());
128  std::string password = config->get_string((ap + "auth_password").c_str());
129  auth_infos_.push_back(AuthInfo(dbname, username, password));
130  } catch (Exception &e) {
131  logger->log_info(logcomp_.c_str(),
132  "Incomplete extended auth info '%s' "
133  "for MongoDB client '%s'",
134  auth_name.c_str(),
135  cfgname.c_str());
136  }
137  }
138  }
139 }
140 
141 /** Create MongoDB client for this configuration.
142  * @return MongoDB client
143  */
144 mongo::DBClientBase *
146 {
147  mongo::DBClientBase *client;
148  std::string errmsg;
149 
150  switch (mode_) {
151  case REPLICA_SET: {
152  mongo::DBClientReplicaSet *repset =
153  new mongo::DBClientReplicaSet(replicaset_name_, replicaset_hostports_);
154  client = repset;
155  if (!repset->connect())
156  throw Exception("Cannot connect to replica set %s", replicaset_name_.c_str());
157  std::list<AuthInfo>::iterator ai;
158  for (ai = auth_infos_.begin(); ai != auth_infos_.end(); ++ai) {
159  if (!repset->auth(ai->dbname, ai->username, ai->clearpwd, errmsg, false)) {
160  throw Exception("Authenticating for %s as %s failed: %s",
161  ai->dbname.c_str(),
162  ai->username.c_str(),
163  errmsg.c_str());
164  }
165  }
166  } break;
167 
168  default: {
169  mongo::DBClientConnection *clconn = new mongo::DBClientConnection(/* auto reconnect */ true);
170  client = clconn;
171  std::string errmsg;
172  if (!clconn->connect(conn_hostport_, errmsg)) {
173  throw Exception("Could not connect to MongoDB at %s: %s\n"
174  "You probably forgot to start/enable the mongod service",
175  conn_hostport_.toString().c_str(),
176  errmsg.c_str());
177  }
178  std::list<AuthInfo>::iterator ai;
179  for (ai = auth_infos_.begin(); ai != auth_infos_.end(); ++ai) {
180  if (!clconn->auth(ai->dbname, ai->username, ai->clearpwd, errmsg, false)) {
181  throw Exception("Authenticating for %s as %s failed: %s",
182  ai->dbname.c_str(),
183  ai->username.c_str(),
184  errmsg.c_str());
185  }
186  }
187  } break;
188  }
189 
190  return client;
191 }
192 
193 /** Write client configuration information to log.
194  * @param logger logger to write to
195  * @param component component to pass to logger
196  * @param indent indentation to put before each string
197  */
198 void
199 MongoDBClientConfig::log(Logger *logger, const char *component, const char *indent)
200 {
201  switch (mode_) {
202  case REPLICA_SET: {
203  logger->log_info(component, "%smode: replica set", indent);
204  logger->log_info(component, "%shosts:", indent);
205  std::vector<mongo::HostAndPort>::iterator i;
206  for (i = replicaset_hostports_.begin(); i != replicaset_hostports_.end(); ++i) {
207  logger->log_info(component, "%s - %s:", indent, i->toString().c_str());
208  }
209 
210  if (!auth_infos_.empty()) {
211  logger->log_info(component, "%sauth infos:", indent);
212  std::list<AuthInfo>::iterator a;
213  for (a = auth_infos_.begin(); a != auth_infos_.end(); ++a) {
214  logger->log_info(
215  component, "%s - %s @ %s", indent, a->username.c_str(), a->dbname.c_str());
216  }
217  }
218  } break;
219 
220  default: {
221  logger->log_info(component, "%smode: connection", indent);
222  logger->log_info(component, "%shost: %s", indent, conn_hostport_.toString().c_str());
223  if (!auth_infos_.empty()) {
224  logger->log_info(component, "%sauth infos:", indent);
225  std::list<AuthInfo>::iterator a;
226  for (a = auth_infos_.begin(); a != auth_infos_.end(); ++a) {
227  logger->log_info(
228  component, "%s - %s @ %s", indent, a->username.c_str(), a->dbname.c_str());
229  }
230  }
231  } break;
232  }
233 }
234 
235 /** Get host and port of configuration.
236  * @return string of the form "host:port"
237  */
238 std::string
240 {
241  return conn_hostport_.toString();
242 }
243 
244 /** Get client configuration mode.
245  * @return mode, connection or replica set
246  */
249 {
250  return mode_;
251 }
ConnectionMode mode() const
Get client configuration mode.
std::string hostport() const
Get host and port of configuration.
Fawkes library namespace.
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
virtual ValueIterator * search(const char *path)=0
Iterator with search results.
mongo::DBClientBase * create_client()
Create MongoDB client for this configuration.
Base class for exceptions in Fawkes.
Definition: exception.h:35
MongoDBClientConfig(fawkes::Configuration *config, fawkes::Logger *logger, std::string cfgname, std::string prefix)
Constructor.
virtual void log_info(const char *component, const char *format,...)
Log informational message.
Definition: multi.cpp:195
void log(fawkes::Logger *logger, const char *component, const char *indent)
Write client configuration information to log.
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
ConnectionMode
Connection mode enumeration.
Interface for configuration handling.
Definition: config.h:64
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
Interface for logging.
Definition: logger.h:41