2 Copyright (c) 2018-2022 Nokia.
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
8 http://www.apache.org/licenses/LICENSE-2.0
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.
18 * This source code is part of the near-RT RIC (RAN Intelligent Controller)
19 * platform project (RICP).
22 #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 #include <boost/algorithm/string.hpp>
34 using namespace shareddatalayer;
39 T get(const boost::property_tree::ptree& ptree, const std::string& param, const std::string& sourceName)
43 return ptree.get<T>(param);
45 catch (const boost::property_tree::ptree_bad_path&)
47 std::ostringstream os;
48 os << "Configuration error in " << sourceName << ": "
49 << "missing \"" << param << '\"';
50 throw Exception(os.str());
52 catch (const boost::property_tree::ptree_bad_data& e)
54 std::ostringstream os;
55 os << "Configuration error in " << sourceName << ": "
56 << "invalid \"" << param << "\": \"" << e.data<boost::property_tree::ptree::data_type>() << '\"';
57 throw Exception(os.str());
61 void validateAndSetDbType(const std::string& type, DatabaseConfiguration& databaseConfiguration,
62 const std::string& sourceName)
66 databaseConfiguration.checkAndApplyDbType(type);
68 catch (const std::exception& e)
70 std::ostringstream os;
71 os << "Configuration error in " << sourceName << ": "
73 throw Exception(os.str());
77 void validateAndSetDbServerAddress(const std::string& address, DatabaseConfiguration& databaseConfiguration,
78 const std::string& sourceName)
82 databaseConfiguration.checkAndApplyServerAddress(address);
84 catch (const std::exception& e)
86 std::ostringstream os;
87 os << "Configuration error in " << sourceName << ": "
88 << "invalid \"address\": \"" << address << "\" " << e.what();
89 throw Exception(os.str());
93 void parseDatabaseServerConfiguration(DatabaseConfiguration& databaseConfiguration,
94 const boost::property_tree::ptree& ptree,
95 const std::string& sourceName)
97 const auto address(get<std::string>(ptree, "address", sourceName));
98 validateAndSetDbServerAddress(address, databaseConfiguration, sourceName);
101 void parseDatabaseServersConfiguration(DatabaseConfiguration& databaseConfiguration,
102 const boost::property_tree::ptree& ptree,
103 const std::string& sourceName)
105 const auto servers(ptree.get_child_optional("servers"));
107 for(const auto& server : *servers)
108 parseDatabaseServerConfiguration(databaseConfiguration, server.second, sourceName);
111 std::ostringstream os;
112 os << "Configuration error in " << sourceName << ": "
113 << "missing \"servers\"";
114 throw Exception(os.str());
118 void parseDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration,
119 const boost::property_tree::ptree& ptree,
120 const std::string& sourceName)
122 const auto type(get<std::string>(ptree, "type", sourceName));
123 validateAndSetDbType(type, databaseConfiguration, sourceName);
125 parseDatabaseServersConfiguration(databaseConfiguration, ptree, sourceName);
128 void parseDatabaseConfigurationTree(DatabaseConfiguration& databaseConfiguration,
129 const boost::optional<boost::property_tree::ptree>& databaseConfigurationPtree,
130 const std::string& sourceName)
132 if (databaseConfigurationPtree)
133 parseDatabaseConfiguration(databaseConfiguration, *databaseConfigurationPtree, sourceName);
136 void parseDatabaseServersConfigurationFromString(DatabaseConfiguration& databaseConfiguration,
137 const std::string& serverConfiguration,
138 const std::string& sourceName)
144 auto split = serverConfiguration.find(',', base);
145 done = std::string::npos == split;
146 validateAndSetDbServerAddress(serverConfiguration.substr(base, done ? std::string::npos : split-base),
147 databaseConfiguration,
153 void validateNamespacePrefix(const std::string& prefix,
154 const std::string& sourceName)
156 if (!isValidNamespaceSyntax(prefix))
158 std::ostringstream os;
159 os << "Configuration error in " << sourceName << ": "
160 << "\"namespacePrefix\": \"" << prefix << "\""
161 << " contains some of these disallowed characters: "
162 << getDisallowedCharactersInNamespace();
163 throw Exception(os.str());
167 void validateEnableNotifications(bool enableNotifications, bool useDbBackend,
168 const std::string& sourceName)
170 if (enableNotifications && !useDbBackend)
172 std::ostringstream os;
173 os << "Configuration error in " << sourceName << ": "
174 << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
175 throw Exception(os.str());
179 void parseNsConfiguration(NamespaceConfigurations& namespaceConfigurations,
180 const std::string& namespacePrefix,
181 const boost::property_tree::ptree& ptree,
182 const std::string& sourceName)
184 const auto useDbBackend(get<bool>(ptree, "useDbBackend", sourceName));
185 const auto enableNotifications(get<bool>(ptree, "enableNotifications", sourceName));
187 validateNamespacePrefix(namespacePrefix, sourceName);
188 validateEnableNotifications(enableNotifications, useDbBackend, sourceName);
190 namespaceConfigurations.addNamespaceConfiguration({namespacePrefix, useDbBackend, enableNotifications, sourceName});
193 void parseNsConfigurationMap(NamespaceConfigurations& namespaceConfigurations,
194 std::unordered_map<std::string, std::pair<boost::property_tree::ptree, std::string>>& namespaceConfigurationMap)
196 for (const auto &namespaceConfigurationMapItem : namespaceConfigurationMap )
197 parseNsConfiguration(namespaceConfigurations, namespaceConfigurationMapItem.first, namespaceConfigurationMapItem.second.first, namespaceConfigurationMapItem.second.second);
200 const std::string DEFAULT_REDIS_PORT("6379");
202 void appendDBPortToAddrList(std::string& addresses, const std::string& port)
205 std::vector<std::string> portList;
206 boost::split(portList, port, boost::is_any_of(","));
209 auto redisPort((portList.size() > 0 && idx < portList.size()) ? portList.at(idx) : DEFAULT_REDIS_PORT);
210 auto pos = addresses.find(',', base);
211 while (std::string::npos != pos)
213 addresses.insert(pos, ":" + redisPort);
214 base = pos + 2 + redisPort.size();
215 pos = addresses.find(',', base);
218 addresses.append(":" + redisPort);
222 ConfigurationReader::ConfigurationReader(std::shared_ptr<Logger> logger):
223 ConfigurationReader(getDefaultConfDirectories(), System::getSystem(), logger)
227 ConfigurationReader::ConfigurationReader(const Directories& directories,
229 std::shared_ptr<Logger> logger):
230 dbHostEnvVariableName(DB_HOST_ENV_VAR_NAME),
231 dbHostEnvVariableValue({}),
232 dbPortEnvVariableName(DB_PORT_ENV_VAR_NAME),
233 dbPortEnvVariableValue({}),
234 sentinelPortEnvVariableName(SENTINEL_PORT_ENV_VAR_NAME),
235 sentinelPortEnvVariableValue({}),
236 sentinelMasterNameEnvVariableName(SENTINEL_MASTER_NAME_ENV_VAR_NAME),
237 sentinelMasterNameEnvVariableValue({}),
238 dbClusterAddrListEnvVariableName(DB_CLUSTER_ADDR_LIST_ENV_VAR_NAME),
239 dbClusterAddrListEnvVariableValue({}),
240 jsonDatabaseConfiguration(boost::none),
243 auto envStr = system.getenv(dbHostEnvVariableName.c_str());
246 dbHostEnvVariableValue = envStr;
247 sourceForDatabaseConfiguration = dbHostEnvVariableName;
248 auto envStr = system.getenv(dbPortEnvVariableName.c_str());
250 dbPortEnvVariableValue = envStr;
251 envStr = system.getenv(sentinelPortEnvVariableName.c_str());
253 sentinelPortEnvVariableValue = envStr;
254 envStr = system.getenv(sentinelMasterNameEnvVariableName.c_str());
256 sentinelMasterNameEnvVariableValue = envStr;
257 envStr = system.getenv(dbClusterAddrListEnvVariableName.c_str());
259 dbClusterAddrListEnvVariableValue = envStr;
262 readConfigurationFromDirectories(directories);
265 ConfigurationReader::~ConfigurationReader()
269 void ConfigurationReader::readConfigurationFromDirectories(const Directories& directories)
271 for (const auto& i : findConfigurationFiles(directories))
272 readConfiguration(i, i);
275 void ConfigurationReader::readConfigurationFromInputStream(const std::istream& input)
277 jsonNamespaceConfigurations.clear();
278 readConfiguration(const_cast<std::istream&>(input), "<istream>");
282 void ConfigurationReader::readConfiguration(T& input, const std::string& currentSourceName)
284 boost::property_tree::ptree propertyTree;
288 boost::property_tree::read_json(input, propertyTree);
290 catch (const boost::property_tree::json_parser::json_parser_error& e)
292 std::ostringstream os;
293 os << "error in SDL configuration " << currentSourceName << " at line " << e.line() << ": ";
295 logger->error() << os.str();
296 throw Exception(os.str());
299 // Environment variable configuration overrides json configuration
300 if (sourceForDatabaseConfiguration != dbHostEnvVariableName)
302 const auto databaseConfiguration(propertyTree.get_child_optional("database"));
303 if (databaseConfiguration)
305 jsonDatabaseConfiguration = databaseConfiguration;
306 sourceForDatabaseConfiguration = currentSourceName;
310 const auto namespaceConfigurations(propertyTree.get_child_optional("sharedDataLayer"));
311 if (namespaceConfigurations)
313 for(const auto& namespaceConfiguration : *namespaceConfigurations)
315 const auto namespacePrefix = get<std::string>(namespaceConfiguration.second, "namespacePrefix", currentSourceName);
316 jsonNamespaceConfigurations[namespacePrefix] = std::make_pair(namespaceConfiguration.second, currentSourceName);
321 void ConfigurationReader::readDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration)
323 if (!databaseConfiguration.isEmpty())
324 SHAREDDATALAYER_ABORT("Database configuration can be read only to empty container");
328 if (sourceForDatabaseConfiguration == dbHostEnvVariableName)
330 // NOTE: Redis cluster is not currently configurable via environment variables.
331 std::string dbHostAddrs;
332 if (!dbHostEnvVariableValue.empty() && sentinelPortEnvVariableValue.empty() && dbClusterAddrListEnvVariableValue.empty())
334 validateAndSetDbType("redis-standalone", databaseConfiguration, sourceForDatabaseConfiguration);
335 dbHostAddrs = dbHostEnvVariableValue;
337 else if (!dbHostEnvVariableValue.empty() && !sentinelPortEnvVariableValue.empty() && dbClusterAddrListEnvVariableValue.empty())
339 validateAndSetDbType("redis-sentinel", databaseConfiguration, sourceForDatabaseConfiguration);
340 dbHostAddrs = dbHostEnvVariableValue;
342 else if (sentinelPortEnvVariableValue.empty() && !dbClusterAddrListEnvVariableValue.empty())
344 validateAndSetDbType("sdl-standalone-cluster", databaseConfiguration, sourceForDatabaseConfiguration);
345 dbHostAddrs = dbClusterAddrListEnvVariableValue;
347 else if (!sentinelPortEnvVariableValue.empty() && !dbClusterAddrListEnvVariableValue.empty())
349 validateAndSetDbType("sdl-sentinel-cluster", databaseConfiguration, sourceForDatabaseConfiguration);
350 dbHostAddrs = dbClusterAddrListEnvVariableValue;
354 std::ostringstream os;
355 os << "Configuration error in " << sourceForDatabaseConfiguration << ": "
356 << "Missing environment variable configuration!";
357 throw Exception(os.str());
360 if (!dbPortEnvVariableValue.empty())
361 appendDBPortToAddrList(dbHostAddrs, dbPortEnvVariableValue);
362 parseDatabaseServersConfigurationFromString(databaseConfiguration,
364 sourceForDatabaseConfiguration);
365 auto dbType = databaseConfiguration.getDbType();
366 if (DatabaseConfiguration::DbType::REDIS_SENTINEL == dbType ||
367 DatabaseConfiguration::DbType::SDL_SENTINEL_CLUSTER == dbType)
369 databaseConfiguration.checkAndApplySentinelPorts(sentinelPortEnvVariableValue);
370 databaseConfiguration.checkAndApplySentinelMasterNames(sentinelMasterNameEnvVariableValue);
374 parseDatabaseConfigurationTree(databaseConfiguration, jsonDatabaseConfiguration, sourceForDatabaseConfiguration);
376 catch (const std::exception& e)
378 logger->error() << e.what();
383 void ConfigurationReader::readNamespaceConfigurations(NamespaceConfigurations& namespaceConfigurations)
385 if (!namespaceConfigurations.isEmpty())
386 SHAREDDATALAYER_ABORT("Namespace configurations can be read only to empty container");
390 parseNsConfigurationMap(namespaceConfigurations, jsonNamespaceConfigurations);
392 catch(const std::exception& e)
394 logger->error() << e.what();
400 template void ConfigurationReader::readConfiguration(const std::string&, const std::string&);
401 template void ConfigurationReader::readConfiguration(std::istream&, const std::string&);