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);
200 ConfigurationReader::ConfigurationReader(std::shared_ptr<Logger> logger):
201 ConfigurationReader(getDefaultConfDirectories(), System::getSystem(), logger)
205 ConfigurationReader::ConfigurationReader(const Directories& directories,
207 std::shared_ptr<Logger> logger):
208 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({}),
216 jsonDatabaseConfiguration(boost::none),
219 auto envStr = system.getenv(dbHostEnvVariableName.c_str());
222 dbHostEnvVariableValue = envStr;
223 sourceForDatabaseConfiguration = dbHostEnvVariableName;
224 auto envStr = system.getenv(dbPortEnvVariableName.c_str());
226 dbPortEnvVariableValue = envStr;
227 envStr = system.getenv(sentinelPortEnvVariableName.c_str());
229 sentinelPortEnvVariableValue = envStr;
230 envStr = system.getenv(sentinelMasterNameEnvVariableName.c_str());
232 sentinelMasterNameEnvVariableValue = envStr;
235 readConfigurationFromDirectories(directories);
238 ConfigurationReader::~ConfigurationReader()
242 void ConfigurationReader::readConfigurationFromDirectories(const Directories& directories)
244 for (const auto& i : findConfigurationFiles(directories))
245 readConfiguration(i, i);
248 void ConfigurationReader::readConfigurationFromInputStream(const std::istream& input)
250 jsonNamespaceConfigurations.clear();
251 readConfiguration(const_cast<std::istream&>(input), "<istream>");
255 void ConfigurationReader::readConfiguration(T& input, const std::string& currentSourceName)
257 boost::property_tree::ptree propertyTree;
261 boost::property_tree::read_json(input, propertyTree);
263 catch (const boost::property_tree::json_parser::json_parser_error& e)
265 std::ostringstream os;
266 os << "error in SDL configuration " << currentSourceName << " at line " << e.line() << ": ";
268 logger->error() << os.str();
269 throw Exception(os.str());
272 // Environment variable configuration overrides json configuration
273 if (sourceForDatabaseConfiguration != dbHostEnvVariableName)
275 const auto databaseConfiguration(propertyTree.get_child_optional("database"));
276 if (databaseConfiguration)
278 jsonDatabaseConfiguration = databaseConfiguration;
279 sourceForDatabaseConfiguration = currentSourceName;
283 const auto namespaceConfigurations(propertyTree.get_child_optional("sharedDataLayer"));
284 if (namespaceConfigurations)
286 for(const auto& namespaceConfiguration : *namespaceConfigurations)
288 const auto namespacePrefix = get<std::string>(namespaceConfiguration.second, "namespacePrefix", currentSourceName);
289 jsonNamespaceConfigurations[namespacePrefix] = std::make_pair(namespaceConfiguration.second, currentSourceName);
294 void ConfigurationReader::readDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration)
296 if (!databaseConfiguration.isEmpty())
297 SHAREDDATALAYER_ABORT("Database configuration can be read only to empty container");
301 if (sourceForDatabaseConfiguration == dbHostEnvVariableName)
303 // NOTE: Redis cluster is not currently configurable via environment variables.
304 if (sentinelPortEnvVariableValue.empty())
306 validateAndSetDbType("redis-standalone", databaseConfiguration, sourceForDatabaseConfiguration);
307 if (dbPortEnvVariableValue.empty())
308 parseDatabaseServersConfigurationFromString(databaseConfiguration, dbHostEnvVariableValue, sourceForDatabaseConfiguration);
310 parseDatabaseServersConfigurationFromString(databaseConfiguration, dbHostEnvVariableValue + ":" + dbPortEnvVariableValue, sourceForDatabaseConfiguration);
314 validateAndSetDbType("redis-sentinel", databaseConfiguration, sourceForDatabaseConfiguration);
315 databaseConfiguration.checkAndApplySentinelAddress(dbHostEnvVariableValue + ":" + sentinelPortEnvVariableValue);
316 databaseConfiguration.checkAndApplySentinelMasterName(sentinelMasterNameEnvVariableValue);
320 parseDatabaseConfigurationTree(databaseConfiguration, jsonDatabaseConfiguration, sourceForDatabaseConfiguration);
322 catch (const std::exception& e)
324 logger->error() << e.what();
329 void ConfigurationReader::readNamespaceConfigurations(NamespaceConfigurations& namespaceConfigurations)
331 if (!namespaceConfigurations.isEmpty())
332 SHAREDDATALAYER_ABORT("Namespace configurations can be read only to empty container");
336 parseNsConfigurationMap(namespaceConfigurations, jsonNamespaceConfigurations);
338 catch(const std::exception& e)
340 logger->error() << e.what();
346 template void ConfigurationReader::readConfiguration(const std::string&, const std::string&);
347 template void ConfigurationReader::readConfiguration(std::istream&, const std::string&);