Kea  1.5.0
dbaccess_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2018 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
10 #include <database/db_exceptions.h>
13 
14 #include <boost/lexical_cast.hpp>
15 
16 #include <map>
17 #include <string>
18 #include <utility>
19 
20 using namespace std;
21 using namespace isc::data;
22 
23 namespace isc {
24 namespace db {
25 
26 
27 // Factory function to build the parser
28 DbAccessParser::DbAccessParser()
29  : values_() {
30 }
31 
32 // Parse the configuration and check that the various keywords are consistent.
33 void
34 DbAccessParser::parse(std::string& access_string,
35  ConstElementPtr database_config) {
36 
37  // To cope with incremental updates, the strategy is:
38  // 1. Take a copy of the stored keyword/value pairs.
39  // 2. Update the copy with the passed keywords.
40  // 3. Perform validation checks on the updated keyword/value pairs.
41  // 4. If all is OK, update the stored keyword/value pairs.
42  // 5. Save resulting database access string in the Configuration
43  // Manager.
44 
45  // Note only range checks can fail with a database_config from
46  // a flex/bison parser.
47 
48  // 1. Take a copy of the stored keyword/value pairs.
49  DatabaseConnection::ParameterMap values_copy = values_;
50 
51  int64_t lfc_interval = 0;
52  int64_t timeout = 0;
53  int64_t port = 0;
54  int64_t max_reconnect_tries = 0;
55  int64_t reconnect_wait_time = 0;
56  int64_t request_timeout = 0;
57  int64_t tcp_keepalive = 0;
58 
59  // 2. Update the copy with the passed keywords.
60  for (std::pair<std::string, ConstElementPtr> param : database_config->mapValue()) {
61  try {
62  if ((param.first == "persist") || (param.first == "readonly") ||
63  (param.first == "tcp-nodelay")) {
64  values_copy[param.first] = (param.second->boolValue() ?
65  "true" : "false");
66 
67  } else if (param.first == "lfc-interval") {
68  lfc_interval = param.second->intValue();
69  values_copy[param.first] =
70  boost::lexical_cast<std::string>(lfc_interval);
71 
72  } else if (param.first == "connect-timeout") {
73  timeout = param.second->intValue();
74  values_copy[param.first] =
75  boost::lexical_cast<std::string>(timeout);
76 
77  } else if (param.first == "max-reconnect-tries") {
78  max_reconnect_tries = param.second->intValue();
79  values_copy[param.first] =
80  boost::lexical_cast<std::string>(max_reconnect_tries);
81 
82  } else if (param.first == "reconnect-wait-time") {
83  reconnect_wait_time = param.second->intValue();
84  values_copy[param.first] =
85  boost::lexical_cast<std::string>(reconnect_wait_time);
86 
87  } else if (param.first == "request-timeout") {
88  request_timeout = param.second->intValue();
89  values_copy[param.first] =
90  boost::lexical_cast<std::string>(request_timeout);
91 
92  } else if (param.first == "tcp-keepalive") {
93  tcp_keepalive = param.second->intValue();
94  values_copy[param.first] =
95  boost::lexical_cast<std::string>(tcp_keepalive);
96 
97  } else if (param.first == "port") {
98  port = param.second->intValue();
99  values_copy[param.first] =
100  boost::lexical_cast<std::string>(port);
101 
102  } else {
103  values_copy[param.first] = param.second->stringValue();
104  }
105  } catch (const isc::data::TypeError& ex) {
106  // Append position of the element.
107  isc_throw(DbConfigError, "invalid value type specified for "
108  "parameter '" << param.first << "' ("
109  << param.second->getPosition() << ")");
110  }
111  }
112 
113  // 3. Perform validation checks on the updated set of keyword/values.
114  //
115  // a. Check if the "type" keyword exists and thrown an exception if not.
116  auto type_ptr = values_copy.find("type");
117  if (type_ptr == values_copy.end()) {
119  "database access parameters must "
120  "include the keyword 'type' to determine type of database "
121  "to be accessed (" << database_config->getPosition() << ")");
122  }
123 
124  // b. Check if the 'type' keyword known and throw an exception if not.
125  //
126  // Please note when you add a new database backend you have to add
127  // the new type here and in server grammars.
128  string dbtype = type_ptr->second;
129  if ((dbtype != "memfile") &&
130  (dbtype != "mysql") &&
131  (dbtype != "postgresql") &&
132  (dbtype != "cql")) {
133  ConstElementPtr value = database_config->get("type");
134  isc_throw(DbConfigError, "unknown backend database type: " << dbtype
135  << " (" << value->getPosition() << ")");
136  }
137 
138  // c. Check that the lfc-interval is within a reasonable range.
139  if ((lfc_interval < 0) ||
140  (lfc_interval > std::numeric_limits<uint32_t>::max())) {
141  ConstElementPtr value = database_config->get("lfc-interval");
142  isc_throw(DbConfigError, "lfc-interval value: " << lfc_interval
143  << " is out of range, expected value: 0.."
144  << std::numeric_limits<uint32_t>::max()
145  << " (" << value->getPosition() << ")");
146  }
147 
148  // d. Check that the timeout is within a reasonable range.
149  if ((timeout < 0) ||
150  (timeout > std::numeric_limits<uint32_t>::max())) {
151  ConstElementPtr value = database_config->get("connect-timeout");
152  isc_throw(DbConfigError, "connect-timeout value: " << timeout
153  << " is out of range, expected value: 0.."
154  << std::numeric_limits<uint32_t>::max()
155  << " (" << value->getPosition() << ")");
156  }
157 
158  // e. Check that the port is within a reasonable range.
159  if ((port < 0) ||
160  (port > std::numeric_limits<uint16_t>::max())) {
161  ConstElementPtr value = database_config->get("port");
162  isc_throw(DbConfigError, "port value: " << port
163  << " is out of range, expected value: 0.."
164  << std::numeric_limits<uint16_t>::max()
165  << " (" << value->getPosition() << ")");
166  }
167 
168  // Check that the max-reconnect-retries reasonable.
169  if (max_reconnect_tries < 0) {
170  ConstElementPtr value = database_config->get("max-reconnect-tries");
171  isc_throw(DbConfigError, "max-reconnect-tries cannot be less than zero: "
172  << " (" << value->getPosition() << ")");
173  }
174 
175  // Check that the reconnect-wait-time reasonable.
176  if ((reconnect_wait_time < 0) ||
177  (reconnect_wait_time > std::numeric_limits<uint32_t>::max())) {
178  ConstElementPtr value = database_config->get("reconnect-wait-time");
179  isc_throw(DbConfigError, "reconnect-wait-time " << reconnect_wait_time
180  << " must be in range 0...MAX_UINT32 (4294967295) "
181  << " (" << value->getPosition() << ")");
182  }
183 
184  // Check that request_timeout value makes sense.
185  if ((reconnect_wait_time < 0) ||
186  (reconnect_wait_time > std::numeric_limits<uint32_t>::max())) {
187  ConstElementPtr value = database_config->get("reconnect-wait-time");
188  isc_throw(DbConfigError, "reconnect-wait-time " << reconnect_wait_time
189  << " must be in range 0...MAX_UINT32 (4294967295) "
190  << " (" << value->getPosition() << ")");
191  }
192  // Check that tcp_keepalive value makes sense.
193  if ((tcp_keepalive < 0) ||
194  (tcp_keepalive > std::numeric_limits<uint32_t>::max())) {
195  ConstElementPtr value = database_config->get("reconnect-wait-time");
196  isc_throw(DbConfigError, "tcp-keepalive " << tcp_keepalive
197  << " must be in range 0...MAX_UINT32 (4294967295) "
198  << " (" << value->getPosition() << ")");
199  }
200 
201  // 4. If all is OK, update the stored keyword/value pairs. We do this by
202  // swapping contents - values_copy is destroyed immediately after the
203  // operation (when the method exits), so we are not interested in its new
204  // value.
205  values_.swap(values_copy);
206 
207  // 5. Save the database access string in the Configuration Manager.
208  access_string = getDbAccessString();
209 }
210 
211 // Create the database access string
212 std::string
214 
215  // Construct the database access string from all keywords and values in the
216  // parameter map where the value is not null.
217  string dbaccess;
218  for (auto keyval : values_) {
219  if (!keyval.second.empty()) {
220 
221  // Separate keyword/value pair from predecessor (if there is one).
222  if (!dbaccess.empty()) {
223  dbaccess += std::string(" ");
224  }
225 
226  // Add the keyword/value pair to the access string.
227  dbaccess += (keyval.first + std::string("=") + keyval.second);
228  }
229  }
230 
231  return (dbaccess);
232 }
233 
234 } // namespace db
235 } // namespace isc
dbaccess_parser.h
isc::db::DbConfigError
Error detected in the database configuration.
Definition: db_exceptions.h:71
db_exceptions.h
dhcp_parsers.h
isc::data
Definition: cfg_to_element.h:25
isc::data::TypeError
A standard Data module exception that is thrown if a function is called for an Element that has a wro...
Definition: data.h:30
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: exceptions/exceptions.h:192
database_connection.h
isc::db::DbAccessParser::getDbAccessString
std::string getDbAccessString() const
Construct database access string.
Definition: dbaccess_parser.cc:213
isc::db::DbAccessParser::parse
void parse(std::string &access_string, isc::data::ConstElementPtr database_config)
Parse configuration value.
Definition: dbaccess_parser.cc:34
isc::data::ConstElementPtr
boost::shared_ptr< const Element > ConstElementPtr
Definition: data.h:23
isc::db::DatabaseConnection::ParameterMap
std::map< std::string, std::string > ParameterMap
Database configuration parameter map.
Definition: database_connection.h:152