blob: cc5e33e2c1fe8adece7f8986d0f05d830c96570e [file] [log] [blame]
Rolf Badorekef2bf512019-08-20 11:17:15 +03001/*
2 Copyright (c) 2018-2019 Nokia.
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15*/
16
Timo Tietavainena0745d22019-11-28 09:55:22 +020017/*
18 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
19 * platform project (RICP).
20*/
21
Rolf Badorekef2bf512019-08-20 11:17:15 +030022#include "private/abort.hpp"
23#include "private/configurationreader.hpp"
24#include <boost/property_tree/json_parser.hpp>
25#include <sdl/exception.hpp>
26#include "private/createlogger.hpp"
27#include "private/databaseconfiguration.hpp"
28#include "private/logger.hpp"
29#include "private/namespaceconfigurations.hpp"
30#include "private/namespacevalidator.hpp"
31#include "private/system.hpp"
32
33using namespace shareddatalayer;
34
35namespace
36{
37 template <typename T>
38 T get(const boost::property_tree::ptree& ptree, const std::string& param, const std::string& sourceName)
39 {
40 try
41 {
42 return ptree.get<T>(param);
43 }
44 catch (const boost::property_tree::ptree_bad_path&)
45 {
46 std::ostringstream os;
47 os << "Configuration error in " << sourceName << ": "
48 << "missing \"" << param << '\"';
49 throw Exception(os.str());
50 }
51 catch (const boost::property_tree::ptree_bad_data& e)
52 {
53 std::ostringstream os;
54 os << "Configuration error in " << sourceName << ": "
55 << "invalid \"" << param << "\": \"" << e.data<boost::property_tree::ptree::data_type>() << '\"';
56 throw Exception(os.str());
57 }
58 }
59
60 void validateAndSetDbType(const std::string& type, DatabaseConfiguration& databaseConfiguration,
61 const std::string& sourceName)
62 {
63 try
64 {
65 databaseConfiguration.checkAndApplyDbType(type);
66 }
67 catch (const std::exception& e)
68 {
69 std::ostringstream os;
70 os << "Configuration error in " << sourceName << ": "
71 << e.what();
72 throw Exception(os.str());
73 }
74 }
75
76 void validateAndSetDbServerAddress(const std::string& address, DatabaseConfiguration& databaseConfiguration,
77 const std::string& sourceName)
78 {
79 try
80 {
81 databaseConfiguration.checkAndApplyServerAddress(address);
82 }
83 catch (const std::exception& e)
84 {
85 std::ostringstream os;
86 os << "Configuration error in " << sourceName << ": "
87 << "invalid \"address\": \"" << address << "\" " << e.what();
88 throw Exception(os.str());
89 }
90 }
91
92 void parseDatabaseServerConfiguration(DatabaseConfiguration& databaseConfiguration,
93 const boost::property_tree::ptree& ptree,
94 const std::string& sourceName)
95 {
96 const auto address(get<std::string>(ptree, "address", sourceName));
97 validateAndSetDbServerAddress(address, databaseConfiguration, sourceName);
98 }
99
100 void parseDatabaseServersConfiguration(DatabaseConfiguration& databaseConfiguration,
101 const boost::property_tree::ptree& ptree,
102 const std::string& sourceName)
103 {
104 const auto servers(ptree.get_child_optional("servers"));
105 if (servers)
106 for(const auto& server : *servers)
107 parseDatabaseServerConfiguration(databaseConfiguration, server.second, sourceName);
108 else
109 {
110 std::ostringstream os;
111 os << "Configuration error in " << sourceName << ": "
112 << "missing \"servers\"";
113 throw Exception(os.str());
114 }
115 }
116
117 void parseDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration,
118 const boost::property_tree::ptree& ptree,
119 const std::string& sourceName)
120 {
121 const auto type(get<std::string>(ptree, "type", sourceName));
122 validateAndSetDbType(type, databaseConfiguration, sourceName);
123
124 parseDatabaseServersConfiguration(databaseConfiguration, ptree, sourceName);
125 }
126
127 void parseDatabaseConfigurationTree(DatabaseConfiguration& databaseConfiguration,
128 const boost::optional<boost::property_tree::ptree>& databaseConfigurationPtree,
129 const std::string& sourceName)
130 {
131 if (databaseConfigurationPtree)
132 parseDatabaseConfiguration(databaseConfiguration, *databaseConfigurationPtree, sourceName);
133 }
134
135 void parseDatabaseServersConfigurationFromString(DatabaseConfiguration& databaseConfiguration,
136 const std::string& serverConfiguration,
137 const std::string& sourceName)
138 {
139 size_t base(0);
140 auto done(false);
141 do
142 {
143 auto split = serverConfiguration.find(',', base);
144 done = std::string::npos == split;
145 validateAndSetDbServerAddress(serverConfiguration.substr(base, done ? std::string::npos : split-base),
146 databaseConfiguration,
147 sourceName);
148 base = split+1;
149 } while (!done);
150 }
151
152 void validateNamespacePrefix(const std::string& prefix,
153 const std::string& sourceName)
154 {
155 if (!isValidNamespaceSyntax(prefix))
156 {
157 std::ostringstream os;
158 os << "Configuration error in " << sourceName << ": "
159 << "\"namespacePrefix\": \"" << prefix << "\""
160 << " contains some of these disallowed characters: "
161 << getDisallowedCharactersInNamespace();
162 throw Exception(os.str());
163 }
164 }
165
166 void validateEnableNotifications(bool enableNotifications, bool useDbBackend,
167 const std::string& sourceName)
168 {
169 if (enableNotifications && !useDbBackend)
170 {
171 std::ostringstream os;
172 os << "Configuration error in " << sourceName << ": "
173 << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
174 throw Exception(os.str());
175 }
176 }
177
178 void parseNsConfiguration(NamespaceConfigurations& namespaceConfigurations,
179 const std::string& namespacePrefix,
180 const boost::property_tree::ptree& ptree,
181 const std::string& sourceName)
182 {
183 const auto useDbBackend(get<bool>(ptree, "useDbBackend", sourceName));
184 const auto enableNotifications(get<bool>(ptree, "enableNotifications", sourceName));
185
186 validateNamespacePrefix(namespacePrefix, sourceName);
187 validateEnableNotifications(enableNotifications, useDbBackend, sourceName);
188
189 namespaceConfigurations.addNamespaceConfiguration({namespacePrefix, useDbBackend, enableNotifications, sourceName});
190 }
191
192 void parseNsConfigurationMap(NamespaceConfigurations& namespaceConfigurations,
193 std::unordered_map<std::string, std::pair<boost::property_tree::ptree, std::string>>& namespaceConfigurationMap)
194 {
195 for (const auto &namespaceConfigurationMapItem : namespaceConfigurationMap )
196 parseNsConfiguration(namespaceConfigurations, namespaceConfigurationMapItem.first, namespaceConfigurationMapItem.second.first, namespaceConfigurationMapItem.second.second);
197 }
198}
199
200ConfigurationReader::ConfigurationReader(std::shared_ptr<Logger> logger):
201 ConfigurationReader(getDefaultConfDirectories(), System::getSystem(), logger)
202{
203}
204
205ConfigurationReader::ConfigurationReader(const Directories& directories,
206 System& system,
207 std::shared_ptr<Logger> logger):
Rolf Badorek2dcf9402019-10-01 18:33:58 +0300208 dbHostEnvVariableName(DB_HOST_ENV_VAR_NAME),
209 dbHostEnvVariableValue({}),
210 dbPortEnvVariableName(DB_PORT_ENV_VAR_NAME),
211 dbPortEnvVariableValue({}),
212 sentinelPortEnvVariableName(SENTINEL_PORT_ENV_VAR_NAME),
213 sentinelPortEnvVariableValue({}),
214 sentinelMasterNameEnvVariableName(SENTINEL_MASTER_NAME_ENV_VAR_NAME),
215 sentinelMasterNameEnvVariableValue({}),
Rolf Badorekef2bf512019-08-20 11:17:15 +0300216 jsonDatabaseConfiguration(boost::none),
217 logger(logger)
218{
219 auto envStr = system.getenv(dbHostEnvVariableName.c_str());
220 if (envStr)
221 {
222 dbHostEnvVariableValue = envStr;
223 sourceForDatabaseConfiguration = dbHostEnvVariableName;
224 auto envStr = system.getenv(dbPortEnvVariableName.c_str());
225 if (envStr)
226 dbPortEnvVariableValue = envStr;
Rolf Badorek2dcf9402019-10-01 18:33:58 +0300227 envStr = system.getenv(sentinelPortEnvVariableName.c_str());
228 if (envStr)
229 sentinelPortEnvVariableValue = envStr;
230 envStr = system.getenv(sentinelMasterNameEnvVariableName.c_str());
231 if (envStr)
232 sentinelMasterNameEnvVariableValue = envStr;
Rolf Badorekef2bf512019-08-20 11:17:15 +0300233 }
234
235 readConfigurationFromDirectories(directories);
236}
237
238ConfigurationReader::~ConfigurationReader()
239{
240}
241
242void ConfigurationReader::readConfigurationFromDirectories(const Directories& directories)
243{
244 for (const auto& i : findConfigurationFiles(directories))
245 readConfiguration(i, i);
246}
247
248void ConfigurationReader::readConfigurationFromInputStream(const std::istream& input)
249{
250 jsonNamespaceConfigurations.clear();
251 readConfiguration(const_cast<std::istream&>(input), "<istream>");
252}
253
254template<typename T>
255void ConfigurationReader::readConfiguration(T& input, const std::string& currentSourceName)
256{
257 boost::property_tree::ptree propertyTree;
258
259 try
260 {
261 boost::property_tree::read_json(input, propertyTree);
262 }
263 catch (const boost::property_tree::json_parser::json_parser_error& e)
264 {
265 std::ostringstream os;
266 os << "error in SDL configuration " << currentSourceName << " at line " << e.line() << ": ";
267 os << e.message();
268 logger->error() << os.str();
269 throw Exception(os.str());
270 }
271
272 // Environment variable configuration overrides json configuration
273 if (sourceForDatabaseConfiguration != dbHostEnvVariableName)
274 {
275 const auto databaseConfiguration(propertyTree.get_child_optional("database"));
276 if (databaseConfiguration)
277 {
278 jsonDatabaseConfiguration = databaseConfiguration;
279 sourceForDatabaseConfiguration = currentSourceName;
280 }
281 }
282
283 const auto namespaceConfigurations(propertyTree.get_child_optional("sharedDataLayer"));
284 if (namespaceConfigurations)
285 {
286 for(const auto& namespaceConfiguration : *namespaceConfigurations)
287 {
288 const auto namespacePrefix = get<std::string>(namespaceConfiguration.second, "namespacePrefix", currentSourceName);
289 jsonNamespaceConfigurations[namespacePrefix] = std::make_pair(namespaceConfiguration.second, currentSourceName);
290 }
291 }
292}
293
294void ConfigurationReader::readDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration)
295{
296 if (!databaseConfiguration.isEmpty())
297 SHAREDDATALAYER_ABORT("Database configuration can be read only to empty container");
298
299 try
300 {
301 if (sourceForDatabaseConfiguration == dbHostEnvVariableName)
302 {
Rolf Badorek2dcf9402019-10-01 18:33:58 +0300303 // NOTE: Redis cluster is not currently configurable via environment variables.
304 if (sentinelPortEnvVariableValue.empty())
305 {
306 validateAndSetDbType("redis-standalone", databaseConfiguration, sourceForDatabaseConfiguration);
307 if (dbPortEnvVariableValue.empty())
308 parseDatabaseServersConfigurationFromString(databaseConfiguration, dbHostEnvVariableValue, sourceForDatabaseConfiguration);
309 else
310 parseDatabaseServersConfigurationFromString(databaseConfiguration, dbHostEnvVariableValue + ":" + dbPortEnvVariableValue, sourceForDatabaseConfiguration);
311 }
312 else
313 {
314 validateAndSetDbType("redis-sentinel", databaseConfiguration, sourceForDatabaseConfiguration);
315 databaseConfiguration.checkAndApplySentinelAddress(dbHostEnvVariableValue + ":" + sentinelPortEnvVariableValue);
316 databaseConfiguration.checkAndApplySentinelMasterName(sentinelMasterNameEnvVariableValue);
317 }
Rolf Badorekef2bf512019-08-20 11:17:15 +0300318 }
319 else
320 parseDatabaseConfigurationTree(databaseConfiguration, jsonDatabaseConfiguration, sourceForDatabaseConfiguration);
321 }
322 catch (const std::exception& e)
323 {
324 logger->error() << e.what();
325 throw;
326 }
327}
328
329void ConfigurationReader::readNamespaceConfigurations(NamespaceConfigurations& namespaceConfigurations)
330{
331 if (!namespaceConfigurations.isEmpty())
332 SHAREDDATALAYER_ABORT("Namespace configurations can be read only to empty container");
333
334 try
335 {
336 parseNsConfigurationMap(namespaceConfigurations, jsonNamespaceConfigurations);
337 }
338 catch(const std::exception& e)
339 {
340 logger->error() << e.what();
341 throw;
342 }
343}
344
345
346template void ConfigurationReader::readConfiguration(const std::string&, const std::string&);
347template void ConfigurationReader::readConfiguration(std::istream&, const std::string&);