2 Copyright (c) 2018-2019 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"
33 using namespace shareddatalayer;
38 T get(const boost::property_tree::ptree& ptree, const std::string& param, const std::string& sourceName)
42 return ptree.get<T>(param);
44 catch (const boost::property_tree::ptree_bad_path&)
46 std::ostringstream os;
47 os << "Configuration error in " << sourceName << ": "
48 << "missing \"" << param << '\"';
49 throw Exception(os.str());
51 catch (const boost::property_tree::ptree_bad_data& e)
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());
60 void validateAndSetDbType(const std::string& type, DatabaseConfiguration& databaseConfiguration,
61 const std::string& sourceName)
65 databaseConfiguration.checkAndApplyDbType(type);
67 catch (const std::exception& e)
69 std::ostringstream os;
70 os << "Configuration error in " << sourceName << ": "
72 throw Exception(os.str());
76 void validateAndSetDbServerAddress(const std::string& address, DatabaseConfiguration& databaseConfiguration,
77 const std::string& sourceName)
81 databaseConfiguration.checkAndApplyServerAddress(address);
83 catch (const std::exception& e)
85 std::ostringstream os;
86 os << "Configuration error in " << sourceName << ": "
87 << "invalid \"address\": \"" << address << "\" " << e.what();
88 throw Exception(os.str());
92 void parseDatabaseServerConfiguration(DatabaseConfiguration& databaseConfiguration,
93 const boost::property_tree::ptree& ptree,
94 const std::string& sourceName)
96 const auto address(get<std::string>(ptree, "address", sourceName));
97 validateAndSetDbServerAddress(address, databaseConfiguration, sourceName);
100 void parseDatabaseServersConfiguration(DatabaseConfiguration& databaseConfiguration,
101 const boost::property_tree::ptree& ptree,
102 const std::string& sourceName)
104 const auto servers(ptree.get_child_optional("servers"));
106 for(const auto& server : *servers)
107 parseDatabaseServerConfiguration(databaseConfiguration, server.second, sourceName);
110 std::ostringstream os;
111 os << "Configuration error in " << sourceName << ": "
112 << "missing \"servers\"";
113 throw Exception(os.str());
117 void parseDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration,
118 const boost::property_tree::ptree& ptree,
119 const std::string& sourceName)
121 const auto type(get<std::string>(ptree, "type", sourceName));
122 validateAndSetDbType(type, databaseConfiguration, sourceName);
124 parseDatabaseServersConfiguration(databaseConfiguration, ptree, sourceName);
127 void parseDatabaseConfigurationTree(DatabaseConfiguration& databaseConfiguration,
128 const boost::optional<boost::property_tree::ptree>& databaseConfigurationPtree,
129 const std::string& sourceName)
131 if (databaseConfigurationPtree)
132 parseDatabaseConfiguration(databaseConfiguration, *databaseConfigurationPtree, sourceName);
135 void parseDatabaseServersConfigurationFromString(DatabaseConfiguration& databaseConfiguration,
136 const std::string& serverConfiguration,
137 const std::string& sourceName)
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,
152 void validateNamespacePrefix(const std::string& prefix,
153 const std::string& sourceName)
155 if (!isValidNamespaceSyntax(prefix))
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());
166 void validateEnableNotifications(bool enableNotifications, bool useDbBackend,
167 const std::string& sourceName)
169 if (enableNotifications && !useDbBackend)
171 std::ostringstream os;
172 os << "Configuration error in " << sourceName << ": "
173 << "\"enableNotifications\" cannot be true, when \"useDbBackend\" is false";
174 throw Exception(os.str());
178 void parseNsConfiguration(NamespaceConfigurations& namespaceConfigurations,
179 const std::string& namespacePrefix,
180 const boost::property_tree::ptree& ptree,
181 const std::string& sourceName)
183 const auto useDbBackend(get<bool>(ptree, "useDbBackend", sourceName));
184 const auto enableNotifications(get<bool>(ptree, "enableNotifications", sourceName));
186 validateNamespacePrefix(namespacePrefix, sourceName);
187 validateEnableNotifications(enableNotifications, useDbBackend, sourceName);
189 namespaceConfigurations.addNamespaceConfiguration({namespacePrefix, useDbBackend, enableNotifications, sourceName});
192 void parseNsConfigurationMap(NamespaceConfigurations& namespaceConfigurations,
193 std::unordered_map<std::string, std::pair<boost::property_tree::ptree, std::string>>& namespaceConfigurationMap)
195 for (const auto &namespaceConfigurationMapItem : namespaceConfigurationMap )
196 parseNsConfiguration(namespaceConfigurations, namespaceConfigurationMapItem.first, namespaceConfigurationMapItem.second.first, namespaceConfigurationMapItem.second.second);
199 void appendDBPortToAddrList(std::string& addresses, const std::string& port)
202 auto pos = addresses.find(',', base);
203 while (std::string::npos != pos)
205 addresses.insert(pos, ":" + port);
206 base = pos + 2 + port.size();
207 pos = addresses.find(',', base);
209 addresses.append(":" + port);
213 ConfigurationReader::ConfigurationReader(std::shared_ptr<Logger> logger):
214 ConfigurationReader(getDefaultConfDirectories(), System::getSystem(), logger)
218 ConfigurationReader::ConfigurationReader(const Directories& directories,
220 std::shared_ptr<Logger> logger):
221 dbHostEnvVariableName(DB_HOST_ENV_VAR_NAME),
222 dbHostEnvVariableValue({}),
223 dbPortEnvVariableName(DB_PORT_ENV_VAR_NAME),
224 dbPortEnvVariableValue({}),
225 sentinelPortEnvVariableName(SENTINEL_PORT_ENV_VAR_NAME),
226 sentinelPortEnvVariableValue({}),
227 sentinelMasterNameEnvVariableName(SENTINEL_MASTER_NAME_ENV_VAR_NAME),
228 sentinelMasterNameEnvVariableValue({}),
229 dbClusterAddrListEnvVariableName(DB_CLUSTER_ADDR_LIST_ENV_VAR_NAME),
230 dbClusterAddrListEnvVariableValue({}),
231 jsonDatabaseConfiguration(boost::none),
234 auto envStr = system.getenv(dbHostEnvVariableName.c_str());
237 dbHostEnvVariableValue = envStr;
238 sourceForDatabaseConfiguration = dbHostEnvVariableName;
239 auto envStr = system.getenv(dbPortEnvVariableName.c_str());
241 dbPortEnvVariableValue = envStr;
242 envStr = system.getenv(sentinelPortEnvVariableName.c_str());
244 sentinelPortEnvVariableValue = envStr;
245 envStr = system.getenv(sentinelMasterNameEnvVariableName.c_str());
247 sentinelMasterNameEnvVariableValue = envStr;
248 envStr = system.getenv(dbClusterAddrListEnvVariableName.c_str());
250 dbClusterAddrListEnvVariableValue = envStr;
253 readConfigurationFromDirectories(directories);
256 ConfigurationReader::~ConfigurationReader()
260 void ConfigurationReader::readConfigurationFromDirectories(const Directories& directories)
262 for (const auto& i : findConfigurationFiles(directories))
263 readConfiguration(i, i);
266 void ConfigurationReader::readConfigurationFromInputStream(const std::istream& input)
268 jsonNamespaceConfigurations.clear();
269 readConfiguration(const_cast<std::istream&>(input), "<istream>");
273 void ConfigurationReader::readConfiguration(T& input, const std::string& currentSourceName)
275 boost::property_tree::ptree propertyTree;
279 boost::property_tree::read_json(input, propertyTree);
281 catch (const boost::property_tree::json_parser::json_parser_error& e)
283 std::ostringstream os;
284 os << "error in SDL configuration " << currentSourceName << " at line " << e.line() << ": ";
286 logger->error() << os.str();
287 throw Exception(os.str());
290 // Environment variable configuration overrides json configuration
291 if (sourceForDatabaseConfiguration != dbHostEnvVariableName)
293 const auto databaseConfiguration(propertyTree.get_child_optional("database"));
294 if (databaseConfiguration)
296 jsonDatabaseConfiguration = databaseConfiguration;
297 sourceForDatabaseConfiguration = currentSourceName;
301 const auto namespaceConfigurations(propertyTree.get_child_optional("sharedDataLayer"));
302 if (namespaceConfigurations)
304 for(const auto& namespaceConfiguration : *namespaceConfigurations)
306 const auto namespacePrefix = get<std::string>(namespaceConfiguration.second, "namespacePrefix", currentSourceName);
307 jsonNamespaceConfigurations[namespacePrefix] = std::make_pair(namespaceConfiguration.second, currentSourceName);
312 void ConfigurationReader::readDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration)
314 if (!databaseConfiguration.isEmpty())
315 SHAREDDATALAYER_ABORT("Database configuration can be read only to empty container");
319 if (sourceForDatabaseConfiguration == dbHostEnvVariableName)
321 // NOTE: Redis cluster is not currently configurable via environment variables.
322 std::string dbHostAddrs;
323 if (!dbHostEnvVariableValue.empty() && sentinelPortEnvVariableValue.empty() && dbClusterAddrListEnvVariableValue.empty())
325 validateAndSetDbType("redis-standalone", databaseConfiguration, sourceForDatabaseConfiguration);
326 dbHostAddrs = dbHostEnvVariableValue;
328 else if (!dbHostEnvVariableValue.empty() && !sentinelPortEnvVariableValue.empty() && dbClusterAddrListEnvVariableValue.empty())
330 validateAndSetDbType("redis-sentinel", databaseConfiguration, sourceForDatabaseConfiguration);
331 dbHostAddrs = dbHostEnvVariableValue;
333 else if (sentinelPortEnvVariableValue.empty() && !dbClusterAddrListEnvVariableValue.empty())
335 validateAndSetDbType("sdl-standalone-cluster", databaseConfiguration, sourceForDatabaseConfiguration);
336 dbHostAddrs = dbClusterAddrListEnvVariableValue;
338 else if (!sentinelPortEnvVariableValue.empty() && !dbClusterAddrListEnvVariableValue.empty())
340 validateAndSetDbType("sdl-sentinel-cluster", databaseConfiguration, sourceForDatabaseConfiguration);
341 dbHostAddrs = dbClusterAddrListEnvVariableValue;
345 std::ostringstream os;
346 os << "Configuration error in " << sourceForDatabaseConfiguration << ": "
347 << "Missing environment variable configuration!";
348 throw Exception(os.str());
351 if (!dbPortEnvVariableValue.empty())
352 appendDBPortToAddrList(dbHostAddrs, dbPortEnvVariableValue);
353 parseDatabaseServersConfigurationFromString(databaseConfiguration,
355 sourceForDatabaseConfiguration);
356 auto dbType = databaseConfiguration.getDbType();
357 if (DatabaseConfiguration::DbType::REDIS_SENTINEL == dbType ||
358 DatabaseConfiguration::DbType::SDL_SENTINEL_CLUSTER == dbType)
360 databaseConfiguration.checkAndApplySentinelAddress(dbHostEnvVariableValue + ":" + sentinelPortEnvVariableValue);
361 databaseConfiguration.checkAndApplySentinelMasterName(sentinelMasterNameEnvVariableValue);
365 parseDatabaseConfigurationTree(databaseConfiguration, jsonDatabaseConfiguration, sourceForDatabaseConfiguration);
367 catch (const std::exception& e)
369 logger->error() << e.what();
374 void ConfigurationReader::readNamespaceConfigurations(NamespaceConfigurations& namespaceConfigurations)
376 if (!namespaceConfigurations.isEmpty())
377 SHAREDDATALAYER_ABORT("Namespace configurations can be read only to empty container");
381 parseNsConfigurationMap(namespaceConfigurations, jsonNamespaceConfigurations);
383 catch(const std::exception& e)
385 logger->error() << e.what();
391 template void ConfigurationReader::readConfiguration(const std::string&, const std::string&);
392 template void ConfigurationReader::readConfiguration(std::istream&, const std::string&);