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 dbClusterAddrListEnvVariableName(DB_CLUSTER_ADDR_LIST_ENV_VAR_NAME),
217 dbClusterAddrListEnvVariableValue({}),
218 jsonDatabaseConfiguration(boost::none),
221 auto envStr = system.getenv(dbHostEnvVariableName.c_str());
224 dbHostEnvVariableValue = envStr;
225 sourceForDatabaseConfiguration = dbHostEnvVariableName;
226 auto envStr = system.getenv(dbPortEnvVariableName.c_str());
228 dbPortEnvVariableValue = envStr;
229 envStr = system.getenv(sentinelPortEnvVariableName.c_str());
231 sentinelPortEnvVariableValue = envStr;
232 envStr = system.getenv(sentinelMasterNameEnvVariableName.c_str());
234 sentinelMasterNameEnvVariableValue = envStr;
235 envStr = system.getenv(dbClusterAddrListEnvVariableName.c_str());
237 dbClusterAddrListEnvVariableValue = envStr;
240 readConfigurationFromDirectories(directories);
243 ConfigurationReader::~ConfigurationReader()
247 void ConfigurationReader::readConfigurationFromDirectories(const Directories& directories)
249 for (const auto& i : findConfigurationFiles(directories))
250 readConfiguration(i, i);
253 void ConfigurationReader::readConfigurationFromInputStream(const std::istream& input)
255 jsonNamespaceConfigurations.clear();
256 readConfiguration(const_cast<std::istream&>(input), "<istream>");
260 void ConfigurationReader::readConfiguration(T& input, const std::string& currentSourceName)
262 boost::property_tree::ptree propertyTree;
266 boost::property_tree::read_json(input, propertyTree);
268 catch (const boost::property_tree::json_parser::json_parser_error& e)
270 std::ostringstream os;
271 os << "error in SDL configuration " << currentSourceName << " at line " << e.line() << ": ";
273 logger->error() << os.str();
274 throw Exception(os.str());
277 // Environment variable configuration overrides json configuration
278 if (sourceForDatabaseConfiguration != dbHostEnvVariableName)
280 const auto databaseConfiguration(propertyTree.get_child_optional("database"));
281 if (databaseConfiguration)
283 jsonDatabaseConfiguration = databaseConfiguration;
284 sourceForDatabaseConfiguration = currentSourceName;
288 const auto namespaceConfigurations(propertyTree.get_child_optional("sharedDataLayer"));
289 if (namespaceConfigurations)
291 for(const auto& namespaceConfiguration : *namespaceConfigurations)
293 const auto namespacePrefix = get<std::string>(namespaceConfiguration.second, "namespacePrefix", currentSourceName);
294 jsonNamespaceConfigurations[namespacePrefix] = std::make_pair(namespaceConfiguration.second, currentSourceName);
299 void ConfigurationReader::readDatabaseConfiguration(DatabaseConfiguration& databaseConfiguration)
301 if (!databaseConfiguration.isEmpty())
302 SHAREDDATALAYER_ABORT("Database configuration can be read only to empty container");
306 if (sourceForDatabaseConfiguration == dbHostEnvVariableName)
308 // NOTE: Redis cluster is not currently configurable via environment variables.
309 if (sentinelPortEnvVariableValue.empty())
311 validateAndSetDbType("redis-standalone", databaseConfiguration, sourceForDatabaseConfiguration);
312 if (dbPortEnvVariableValue.empty())
313 parseDatabaseServersConfigurationFromString(databaseConfiguration, dbHostEnvVariableValue, sourceForDatabaseConfiguration);
315 parseDatabaseServersConfigurationFromString(databaseConfiguration, dbHostEnvVariableValue + ":" + dbPortEnvVariableValue, sourceForDatabaseConfiguration);
319 if (dbClusterAddrListEnvVariableValue.empty())
320 validateAndSetDbType("redis-sentinel", databaseConfiguration, sourceForDatabaseConfiguration);
322 validateAndSetDbType("sdl-cluster", databaseConfiguration, sourceForDatabaseConfiguration);
323 parseDatabaseServersConfigurationFromString(databaseConfiguration,
324 dbClusterAddrListEnvVariableValue,
325 dbClusterAddrListEnvVariableName);
327 databaseConfiguration.checkAndApplySentinelAddress(dbHostEnvVariableValue + ":" + sentinelPortEnvVariableValue);
328 databaseConfiguration.checkAndApplySentinelMasterName(sentinelMasterNameEnvVariableValue);
332 parseDatabaseConfigurationTree(databaseConfiguration, jsonDatabaseConfiguration, sourceForDatabaseConfiguration);
334 catch (const std::exception& e)
336 logger->error() << e.what();
341 void ConfigurationReader::readNamespaceConfigurations(NamespaceConfigurations& namespaceConfigurations)
343 if (!namespaceConfigurations.isEmpty())
344 SHAREDDATALAYER_ABORT("Namespace configurations can be read only to empty container");
348 parseNsConfigurationMap(namespaceConfigurations, jsonNamespaceConfigurations);
350 catch(const std::exception& e)
352 logger->error() << e.what();
358 template void ConfigurationReader::readConfiguration(const std::string&, const std::string&);
359 template void ConfigurationReader::readConfiguration(std::istream&, const std::string&);