syncing commonlibs with Many thanks to Michael Iedema for these patches, makes config a lot better.
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@5655 19bc5d8c-e614-43d4-8b26-e1612bc8e597
diff --git a/CommonLibs/Configuration.cpp b/CommonLibs/Configuration.cpp
index 5dcc277..bda6865 100644
--- a/CommonLibs/Configuration.cpp
+++ b/CommonLibs/Configuration.cpp
@@ -32,6 +32,12 @@
#include <iostream>
#include <string.h>
+#ifdef DEBUG_CONFIG
+#define debugLogEarly gLogEarly
+#else
+#define debugLogEarly
+#endif
+
using namespace std;
@@ -57,7 +63,7 @@
}
-ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdName)
+ConfigurationTable::ConfigurationTable(const char* filename, const char *wCmdName, ConfigurationKeyMap wSchema)
{
gLogEarly(LOG_INFO, "opening configuration table from path %s", filename);
// Connect to the database.
@@ -78,35 +84,440 @@
if (!sqlite3_command(mDB,createConfigTable)) {
gLogEarly(LOG_EMERG, "cannot create configuration table in database at %s, error message: %s", filename, sqlite3_errmsg(mDB));
}
+
+ // Build CommonLibs schema
+ ConfigurationKey *tmp;
+ tmp = new ConfigurationKey("Log.Alarms.Max","20",
+ "alarms",
+ ConfigurationKey::CUSTOMER,
+ ConfigurationKey::VALRANGE,
+ "10:20",// educated guess
+ false,
+ "Maximum number of alarms to remember inside the application."
+ );
+ mSchema[tmp->getName()] = *tmp;
+ free(tmp);
+
+ tmp = new ConfigurationKey("Log.File","",
+ "",
+ ConfigurationKey::DEVELOPER,
+ ConfigurationKey::FILEPATH_OPT,// audited
+ "",
+ false,
+ "Path to use for textfile based logging. "
+ "By default, this feature is disabled. "
+ "To enable, specify an absolute path to the file you wish to use, eg: /tmp/my-debug.log. "
+ "To disable again, execute \"unconfig Log.File\"."
+ );
+ mSchema[tmp->getName()] = *tmp;
+ free(tmp);
+
+ tmp = new ConfigurationKey("Log.Level","NOTICE",
+ "",
+ ConfigurationKey::CUSTOMER,
+ ConfigurationKey::CHOICE,
+ "EMERG|EMERGENCY - report serious faults associated with service failure or hardware damage,"
+ "ALERT|ALERT - report likely service disruption caused by misconfiguration or poor connectivity,"
+ "CRIT|CRITICAL - report anomalous events that are likely to degrade service,"
+ "ERR|ERROR - report internal errors of the software that may result in degradation of service in unusual circumstances,"
+ "WARNING|WARNING - report anomalous events that may indicate a degradation of normal service,"
+ "NOTICE|NOTICE - report anomalous events that probably do not affect service but may be of interest to network operators,"
+ "INFO|INFORMATION - report normal events,"
+ "DEBUG|DEBUG - only for use by developers and will degrade system performance",
+ false,
+ "Default logging level when no other level is defined for a file."
+ );
+ mSchema[tmp->getName()] = *tmp;
+ free(tmp);
+
+ // Add application specific schema
+ mSchema.insert(wSchema.begin(), wSchema.end());
+
+ // Init the cross checking callback to something predictable
+ mCrossCheck = NULL;
}
+string ConfigurationTable::getDefaultSQL(const std::string& program, const std::string& version)
+{
+ stringstream ss;
+ ConfigurationKeyMap::iterator mp;
+ ss << "--" << endl;
+ ss << "-- This file was generated using: " << program << " --gensql" << endl;
+ ss << "-- binary version: " << version << endl;
+ ss << "--" << endl;
+ ss << "-- Future changes should not be put in this file directly but" << endl;
+ ss << "-- rather in the program's ConfigurationKey schema." << endl;
+ ss << "--" << endl;
+ ss << "PRAGMA foreign_keys=OFF;" << endl;
+ ss << "BEGIN TRANSACTION;" << endl;
+ ss << "CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');" << endl;
+
+ mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ ss << "INSERT INTO \"CONFIG\" VALUES(";
+ // name
+ ss << "'" << mp->first << "',";
+ // default
+ ss << "'" << mp->second.getDefaultValue() << "',";
+ // static
+ if (mp->second.isStatic()) {
+ ss << "1";
+ } else {
+ ss << "0";
+ }
+ ss << ",";
+ // optional
+ ss << "0,";
+ // description
+ ss << "'";
+ if (mp->second.getType() == ConfigurationKey::BOOLEAN) {
+ ss << "1=enabled, 0=disabled - ";
+ }
+ ss << mp->second.getDescription();
+ if (mp->second.isStatic()) {
+ ss << " Static.";
+ }
+ ss << "'";
+ ss << ");" << endl;
+ mp++;
+ }
+
+ ss << "COMMIT;" << endl;
+ ss << endl;
+
+ return ss.str();
+}
+
+string ConfigurationTable::getTeX(const std::string& program, const std::string& version)
+{
+ stringstream ss;
+ ConfigurationKeyMap::iterator mp;
+
+ ss << "% START AUTO-GENERATED CONTENT" << endl;
+ ss << "% -- these sections were generated using: " << program << " --gentex" << endl;
+ ss << "% -- binary version: " << version << endl;
+
+ ss << "\\subsection{Customer Site Parameters}" << endl;
+ ss << "These parameters must be changed to fit your site." << endl;
+ ss << "\\begin{itemize}" << endl;
+ mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ if (mp->second.getVisibility() == ConfigurationKey::CUSTOMERSITE) {
+ ss << " \\item ";
+ // name
+ ss << mp->first << " -- ";
+ // description
+ ss << mp->second.getDescription();
+ ss << endl;
+ }
+ mp++;
+ }
+ ss << "\\end{itemize}" << endl;
+ ss << endl;
+
+ ss << "\\subsection{Customer Tuneable Parameters}" << endl;
+ ss << "These parameters can be changed to optimize your site." << endl;
+ ss << "\\begin{itemize}" << endl;
+ mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ if (mp->second.getVisibility() != ConfigurationKey::CUSTOMERSITE &&
+ (
+ mp->second.getVisibility() == ConfigurationKey::CUSTOMER ||
+ mp->second.getVisibility() == ConfigurationKey::CUSTOMERTUNE ||
+ mp->second.getVisibility() == ConfigurationKey::CUSTOMERWARN
+ )) {
+ ss << " \\item ";
+ // name
+ ss << mp->first << " -- ";
+ // description
+ ss << mp->second.getDescription();
+ ss << endl;
+ }
+ mp++;
+ }
+ ss << "\\end{itemize}" << endl;
+ ss << endl;
+
+ ss << "\\subsection{Developer/Factory Parameters}" << endl;
+ ss << "These parameters should only be changed by when developing new code." << endl;
+ ss << "\\begin{itemize}" << endl;
+ mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ if (mp->second.getVisibility() == ConfigurationKey::FACTORY ||
+ mp->second.getVisibility() == ConfigurationKey::DEVELOPER) {
+ ss << " \\item ";
+ // name
+ ss << mp->first << " -- ";
+ // description
+ ss << mp->second.getDescription();
+ ss << endl;
+ }
+ mp++;
+ }
+ ss << "\\end{itemize}" << endl;
+ ss << "% END AUTO-GENERATED CONTENT" << endl;
+ ss << endl;
+
+ string tmp = Utils::replaceAll(ss.str(), "^", "\\^");
+ return Utils::replaceAll(tmp, "_", "\\_");
+}
bool ConfigurationTable::defines(const string& key)
{
- assert(mDB);
- ScopedLock lock(mLock);
-
- // Check the cache.
- checkCacheAge();
- ConfigurationMap::const_iterator where = mCache.find(key);
- if (where!=mCache.end()) return where->second.defined();
-
- // Check the database.
- char *value = NULL;
- sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"VALUESTRING",value);
-
- // Cache the result.
- if (value) {
- mCache[key] = ConfigurationRecord(value);
- free(value);
- return true;
+ try {
+ ScopedLock lock(mLock);
+ return lookup(key).defined();
+ } catch (ConfigurationTableKeyNotFound) {
+ debugLogEarly(LOG_ALERT, "configuration parameter %s not found", key.c_str());
+ return false;
}
-
- mCache[key] = ConfigurationRecord(false);
- return false;
}
+bool ConfigurationTable::keyDefinedInSchema(const std::string& name)
+{
+ return mSchema.find(name) == mSchema.end() ? false : true;
+}
+
+bool ConfigurationTable::isValidValue(const std::string& name, const std::string& val) {
+ bool ret = false;
+
+ ConfigurationKey key = mSchema[name];
+
+ switch (key.getType()) {
+ case ConfigurationKey::BOOLEAN: {
+ if (val == "1" || val == "0") {
+ ret = true;
+ }
+ break;
+ }
+
+ case ConfigurationKey::CHOICE_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::CHOICE: {
+ int startPos = -1;
+ uint endPos = 0;
+
+ std::string tmp = key.getValidValues();
+
+ do {
+ startPos++;
+ if ((endPos = tmp.find('|', startPos)) != std::string::npos) {
+ if (val == tmp.substr(startPos, endPos-startPos)) {
+ ret = true;
+ break;
+ }
+ } else {
+ if (val == tmp.substr(startPos, tmp.find(',', startPos)-startPos)) {
+ ret = true;
+ break;
+ }
+ }
+
+ } while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
+ break;
+ }
+
+ case ConfigurationKey::CIDR_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::CIDR: {
+ uint delimiter;
+ std::string ip;
+ int cidr = -1;
+
+ delimiter = val.find('/');
+ if (delimiter != std::string::npos) {
+ ip = val.substr(0, delimiter);
+ std::stringstream(val.substr(delimiter+1)) >> cidr;
+ if (ConfigurationKey::isValidIP(ip) && 0 <= cidr && cidr <= 32) {
+ ret = true;
+ }
+ }
+ break;
+ }
+
+ case ConfigurationKey::FILEPATH_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::FILEPATH: {
+ regex_t r;
+ const char* expression = "^[a-zA-Z0-9/_.-]+$";
+ int result = regcomp(&r, expression, REG_EXTENDED);
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ break;//abort();
+ }
+ if (regexec(&r, val.c_str(), 0, NULL, 0)==0) {
+ ret = true;
+ }
+ regfree(&r);
+ break;
+ }
+
+ case ConfigurationKey::IPADDRESS_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::IPADDRESS: {
+ ret = ConfigurationKey::isValidIP(val);
+ break;
+ }
+
+ case ConfigurationKey::IPANDPORT: {
+ uint delimiter;
+ std::string ip;
+ int port = -1;
+
+ delimiter = val.find(':');
+ if (delimiter != std::string::npos) {
+ ip = val.substr(0, delimiter);
+ std::stringstream(val.substr(delimiter+1)) >> port;
+ if (ConfigurationKey::isValidIP(ip) && 1 <= port && port <= 65535) {
+ ret = true;
+ }
+ }
+ break;
+ }
+
+ case ConfigurationKey::MIPADDRESS_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::MIPADDRESS: {
+ int startPos = -1;
+ uint endPos = 0;
+
+ do {
+ startPos++;
+ endPos = val.find(' ', startPos);
+ if (ConfigurationKey::isValidIP(val.substr(startPos, endPos-startPos))) {
+ ret = true;
+ } else {
+ ret = false;
+ break;
+ }
+
+ } while ((startPos = endPos) != (int)std::string::npos);
+ break;
+ }
+
+ case ConfigurationKey::PORT_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::PORT: {
+ int intVal;
+
+ std::stringstream(val) >> intVal;
+
+ if (1 <= intVal && intVal <= 65535) {
+ ret = true;
+ }
+ break;
+ }
+
+ case ConfigurationKey::REGEX_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::REGEX: {
+ regex_t r;
+ const char* expression = val.c_str();
+ int result = regcomp(&r, expression, REG_EXTENDED);
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ } else {
+ ret = true;
+ }
+ regfree(&r);
+ break;
+ }
+
+ case ConfigurationKey::STRING_OPT: {
+ if (val.length() == 0) {
+ ret = true;
+ break;
+ }
+ }
+ case ConfigurationKey::STRING: {
+ regex_t r;
+ const char* expression = key.getValidValues().c_str();
+ int result = regcomp(&r, expression, REG_EXTENDED);
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ break;//abort();
+ }
+ if (regexec(&r, val.c_str(), 0, NULL, 0)==0) {
+ ret = true;
+ }
+ regfree(&r);
+ break;
+ }
+
+ case ConfigurationKey::VALRANGE: {
+ regex_t r;
+ int result;
+ if (key.getValidValues().find('.') != std::string::npos) {
+ result = regcomp(&r, "^[0-9.-]+$", REG_EXTENDED);
+ } else {
+ result = regcomp(&r, "^[0-9-]+$", REG_EXTENDED);
+ }
+ if (result) {
+ char msg[256];
+ regerror(result,&r,msg,255);
+ break;//abort();
+ }
+ if (regexec(&r, val.c_str(), 0, NULL, 0)!=0) {
+ ret = false;
+ } else if (key.getValidValues().find('.') != std::string::npos) {
+ ret = ConfigurationKey::isInValRange<float>(key, val, false);
+ } else {
+ ret = ConfigurationKey::isInValRange<int>(key, val, true);
+ }
+
+ regfree(&r);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+ConfigurationKeyMap ConfigurationTable::getSimilarKeys(const std::string& snippet) {
+ ConfigurationKeyMap tmp;
+
+ ConfigurationKeyMap::const_iterator mp = mSchema.begin();
+ while (mp != mSchema.end()) {
+ if (mp->first.find(snippet) != std::string::npos) {
+ tmp[mp->first] = mp->second;
+ }
+ mp++;
+ }
+
+ return tmp;
+}
const ConfigurationRecord& ConfigurationTable::lookup(const string& key)
{
@@ -129,15 +540,18 @@
sqlite3_single_lookup(mDB,"CONFIG",
"KEYSTRING",key.c_str(),"VALUESTRING",value);
- // Nothing defined?
- if (!value) {
- // Cache the failure.
+ // value found, cache the result
+ if (value) {
+ mCache[key] = ConfigurationRecord(value);
+ // key definition found, cache the default
+ } else if (keyDefinedInSchema(key)) {
+ mCache[key] = ConfigurationRecord(mSchema[key].getDefaultValue());
+ // total miss, cache the error
+ } else {
mCache[key] = ConfigurationRecord(false);
throw ConfigurationTableKeyNotFound(key);
}
- // Cache the result.
- mCache[key] = ConfigurationRecord(value);
free(value);
// Leave mLock locked. The caller holds it still.
@@ -146,22 +560,13 @@
-bool ConfigurationTable::isStatic(const string& key) const
+bool ConfigurationTable::isStatic(const string& key)
{
- assert(mDB);
- unsigned stat;
- bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"STATIC",stat);
- if (success) return (bool)stat;
- return false;
-}
-
-bool ConfigurationTable::isRequired(const string& key) const
-{
- assert(mDB);
- unsigned optional;
- bool success = sqlite3_single_lookup(mDB,"CONFIG","KEYSTRING",key.c_str(),"OPTIONAL",optional);
- if (success) return !((bool)optional);
- return false;
+ if (keyDefinedInSchema(key)) {
+ return mSchema[key].isStatic();
+ } else {
+ return false;
+ }
}
@@ -175,30 +580,20 @@
return lookup(key).value();
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- gLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
}
-string ConfigurationTable::getStr(const string& key, const char* defaultValue)
-{
- try {
- ScopedLock lock(mLock);
- return lookup(key).value();
- } catch (ConfigurationTableKeyNotFound) {
- gLogEarly(LOG_NOTICE, "deinfing missing parameter %s with value %s", key.c_str(),defaultValue);
- set(key,defaultValue);
- return string(defaultValue);
- }
-}
-
bool ConfigurationTable::getBool(const string& key)
{
try {
return getNum(key) != 0;
} catch (ConfigurationTableKeyNotFound) {
- return false;
+ // Raise an alert and re-throw the exception.
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ throw ConfigurationTableKeyNotFound(key);
}
}
@@ -211,30 +606,22 @@
return lookup(key).number();
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- gLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
}
-long ConfigurationTable::getNum(const string& key, long defaultValue)
-{
- try {
- ScopedLock lock(mLock);
- return lookup(key).number();
- } catch (ConfigurationTableKeyNotFound) {
- gLogEarly(LOG_NOTICE, "deinfing missing parameter %s with value %ld", key.c_str(),defaultValue);
- set(key,defaultValue);
- return defaultValue;
- }
-}
-
-
float ConfigurationTable::getFloat(const string& key)
{
- // We need the lock because rec is a reference into the cache.
- ScopedLock lock(mLock);
- return lookup(key).floatNumber();
+ try {
+ ScopedLock lock(mLock);
+ return lookup(key).floatNumber();
+ } catch (ConfigurationTableKeyNotFound) {
+ // Raise an alert and re-throw the exception.
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ throw ConfigurationTableKeyNotFound(key);
+ }
}
std::vector<string> ConfigurationTable::getVectorOfStrings(const string& key)
@@ -247,7 +634,7 @@
line = strdup(rec.value().c_str());
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- gLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
@@ -268,17 +655,6 @@
}
-std::vector<string> ConfigurationTable::getVectorOfStrings(const string& key, const char* defaultValue){
- try {
- return getVectorOfStrings(key);
- } catch (ConfigurationTableKeyNotFound) {
- set(key,defaultValue);
- return getVectorOfStrings(key);
- }
-}
-
-
-
std::vector<unsigned> ConfigurationTable::getVector(const string& key)
{
// Look up the string.
@@ -289,7 +665,7 @@
line = strdup(rec.value().c_str());
} catch (ConfigurationTableKeyNotFound) {
// Raise an alert and re-throw the exception.
- gLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
+ debugLogEarly(LOG_ALERT, "configuration parameter %s has no defined value", key.c_str());
throw ConfigurationTableKeyNotFound(key);
}
@@ -310,25 +686,9 @@
}
-bool ConfigurationTable::unset(const string& key)
-{
- assert(mDB);
- if (!defines(key)) return true;
- if (isRequired(key)) return false;
-
- ScopedLock lock(mLock);
- // Clear the cache entry and the database.
- ConfigurationMap::iterator where = mCache.find(key);
- if (where!=mCache.end()) mCache.erase(where);
- // Don't delete it; just set VALUESTRING to NULL.
- string cmd = "UPDATE CONFIG SET VALUESTRING=NULL WHERE KEYSTRING=='"+key+"'";
- return sqlite3_command(mDB,cmd.c_str());
-}
-
bool ConfigurationTable::remove(const string& key)
{
assert(mDB);
- if (isRequired(key)) return false;
ScopedLock lock(mLock);
// Clear the cache entry and the database.
@@ -352,14 +712,43 @@
while (src==SQLITE_ROW) {
const char* value = (const char*)sqlite3_column_text(stmt,1);
os << sqlite3_column_text(stmt,0) << " ";
- if (value) os << value << endl;
- else os << "(null)" << endl;
+ int len = 0;
+ if (value) {
+ len = strlen(value);
+ }
+ if (len && value) os << value << endl;
+ else os << "(disabled)" << endl;
src = sqlite3_run_query(mDB,stmt);
}
sqlite3_finalize(stmt);
}
+ConfigurationRecordMap ConfigurationTable::getAllPairs() const
+{
+ ConfigurationRecordMap tmp;
+
+ // Prepare the statement.
+ string cmd = "SELECT KEYSTRING,VALUESTRING FROM CONFIG";
+ sqlite3_stmt *stmt;
+ if (sqlite3_prepare_statement(mDB,&stmt,cmd.c_str())) return tmp;
+ // Read the result.
+ int src = sqlite3_run_query(mDB,stmt);
+ while (src==SQLITE_ROW) {
+ const char* key = (const char*)sqlite3_column_text(stmt,0);
+ const char* value = (const char*)sqlite3_column_text(stmt,1);
+ if (key && value) {
+ tmp[string(key)] = ConfigurationRecord(value);
+ } else if (key && !value) {
+ tmp[string(key)] = ConfigurationRecord(false);
+ }
+ src = sqlite3_run_query(mDB,stmt);
+ }
+ sqlite3_finalize(stmt);
+
+ return tmp;
+}
+
bool ConfigurationTable::set(const string& key, const string& value)
{
assert(mDB);
@@ -428,6 +817,21 @@
}
+void ConfigurationTable::setCrossCheckHook(vector<string> (*wCrossCheck)(const string&))
+{
+ mCrossCheck = wCrossCheck;
+}
+
+
+vector<string> ConfigurationTable::crossCheck(const string& key) {
+ vector<string> ret;
+
+ if (mCrossCheck != NULL) {
+ ret = mCrossCheck(key);
+ }
+
+ return ret;
+}
void HashString::computeHash()
{
@@ -474,5 +878,277 @@
}
+bool ConfigurationKey::isValidIP(const std::string& ip) {
+ struct sockaddr_in sa;
+ return inet_pton(AF_INET, ip.c_str(), &(sa.sin_addr)) != 0;
+}
+
+
+void ConfigurationKey::getMinMaxStepping(const ConfigurationKey &key, std::string &min, std::string &max, std::string &stepping) {
+ uint delimiter;
+ int startPos;
+ uint endPos;
+
+ std::string tmp = key.getValidValues();
+ stepping = "1";
+
+ // grab steps if they're defined
+ startPos = tmp.find('(');
+ if (startPos != (int)std::string::npos) {
+ endPos = tmp.find(')');
+ stepping = tmp.substr(startPos+1, endPos-startPos-1);
+ tmp = tmp.substr(0, startPos);
+ }
+ startPos = 0;
+
+ delimiter = tmp.find(':', startPos);
+ min = tmp.substr(startPos, delimiter-startPos);
+ max = tmp.substr(delimiter+1, tmp.find(',', delimiter)-delimiter-1);
+}
+
+
+template<class T> bool ConfigurationKey::isInValRange(const ConfigurationKey &key, const std::string& val, const bool isInteger) {
+ bool ret = false;
+
+ T convVal;
+ T min;
+ T max;
+ T steps;
+ std::string strMin;
+ std::string strMax;
+ std::string strSteps;
+
+ std::stringstream(val) >> convVal;
+
+ ConfigurationKey::getMinMaxStepping(key, strMin, strMax, strSteps);
+ std::stringstream(strMin) >> min;
+ std::stringstream(strMax) >> max;
+ std::stringstream(strSteps) >> steps;
+
+ // TODO : only ranges checked, steps not enforced
+ if (isInteger) {
+ if (val.find('.') == std::string::npos && min <= convVal && convVal <= max) {
+ ret = true;
+ }
+ } else {
+ if (min <= convVal && convVal <= max) {
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+const std::string ConfigurationKey::getARFCNsString() {
+ stringstream ss;
+ int i;
+ float downlink;
+ float uplink;
+
+ // 128:251 GSM850
+ downlink = 869.2;
+ uplink = 824.2;
+ for (i = 128; i <= 251; i++) {
+ ss << i << "|GSM850 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
+ downlink += 0.2;
+ uplink += 0.2;
+ }
+
+ // 1:124 PGSM900
+ downlink = 935.2;
+ uplink = 890.2;
+ for (i = 1; i <= 124; i++) {
+ ss << i << "|PGSM900 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
+ downlink += 0.2;
+ uplink += 0.2;
+ }
+
+ // 512:885 DCS1800
+ downlink = 1805.2;
+ uplink = 1710.2;
+ for (i = 512; i <= 885; i++) {
+ ss << i << "|DCS1800 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
+ downlink += 0.2;
+ uplink += 0.2;
+ }
+
+ // 512:810 PCS1900
+ downlink = 1930.2;
+ uplink = 1850.2;
+ for (i = 512; i <= 810; i++) {
+ ss << i << "|PCS1900 #" << i << " : " << downlink << " MHz downlink / " << uplink << " MHz uplink,";
+ downlink += 0.2;
+ uplink += 0.2;
+ }
+
+ ss << endl;
+
+ return ss.str();
+}
+
+const std::string ConfigurationKey::visibilityLevelToString(const ConfigurationKey::VisibilityLevel& visibility) {
+ std::string ret = "UNKNOWN ERROR";
+
+ switch (visibility) {
+ case ConfigurationKey::CUSTOMER:
+ ret = "customer - can be freely changed by the customer without any detriment to their system";
+ break;
+ case ConfigurationKey::CUSTOMERSITE:
+ ret = "customer site - these values are different for each BTS and should not be left default";
+ break;
+ case ConfigurationKey::CUSTOMERTUNE:
+ ret = "customer tune - should only be changed to tune an installation to better suit the physical environment or MS usage pattern";
+ break;
+ case ConfigurationKey::CUSTOMERWARN:
+ ret = "customer warn - a warning will be presented and confirmation required before changing this sensitive setting";
+ break;
+ case ConfigurationKey::DEVELOPER:
+ ret = "developer - should only be changed by developers to debug/optimize the implementation";
+ break;
+ case ConfigurationKey::FACTORY:
+ ret = "factory - set once at the factory, should never be changed";
+ break;
+ }
+
+ return ret;
+}
+
+const std::string ConfigurationKey::typeToString(const ConfigurationKey::Type& type) {
+ std::string ret = "UNKNOWN ERROR";
+
+ switch (type) {
+ case BOOLEAN:
+ ret = "boolean";
+ break;
+ case CHOICE_OPT:
+ ret = "multiple choice (optional)";
+ break;
+ case CHOICE:
+ ret = "multiple choice";
+ break;
+ case CIDR_OPT:
+ ret = "CIDR notation (optional)";
+ break;
+ case CIDR:
+ ret = "CIDR notation";
+ break;
+ case FILEPATH_OPT:
+ ret = "file path (optional)";
+ break;
+ case FILEPATH:
+ ret = "file path";
+ break;
+ case IPADDRESS_OPT:
+ ret = "IP address (optional)";
+ break;
+ case IPADDRESS:
+ ret = "IP address";
+ break;
+ case IPANDPORT:
+ ret = "IP address and port";
+ break;
+ case MIPADDRESS_OPT:
+ ret = "space-separated list of IP addresses (optional)";
+ break;
+ case MIPADDRESS:
+ ret = "space-separated list of IP addresses";
+ break;
+ case PORT_OPT:
+ ret = "IP port (optional)";
+ break;
+ case PORT:
+ ret = "IP port";
+ break;
+ case REGEX_OPT:
+ ret = "regular expression (optional)";
+ break;
+ case REGEX:
+ ret = "regular expression";
+ break;
+ case STRING_OPT:
+ ret = "string (optional)";
+ break;
+ case STRING:
+ ret = "string";
+ break;
+ case VALRANGE:
+ ret = "value range";
+ break;
+ }
+
+ return ret;
+}
+
+void ConfigurationKey::printKey(const ConfigurationKey &key, const std::string& currentValue, ostream& os) {
+ os << key.getName() << " ";
+ if (!currentValue.length()) {
+ os << "(disabled)";
+ } else {
+ os << currentValue;
+ }
+ if (currentValue.compare(key.getDefaultValue()) == 0) {
+ os << " [default]";
+ }
+ os << endl;
+}
+
+void ConfigurationKey::printDescription(const ConfigurationKey &key, ostream& os) {
+ std::string tmp;
+
+ os << " - description: " << key.getDescription() << std::endl;
+ if (key.getUnits().length()) {
+ os << " - units: " << key.getUnits() << std::endl;
+ }
+ os << " - type: " << ConfigurationKey::typeToString(key.getType()) << std::endl;
+ if (key.getDefaultValue().length()) {
+ os << " - default value: " << key.getDefaultValue() << std::endl;
+ }
+ os << " - visibility level: " << ConfigurationKey::visibilityLevelToString(key.getVisibility()) << std::endl;
+ os << " - static: " << key.isStatic() << std::endl;
+
+ tmp = key.getValidValues();
+ if (key.getType() == ConfigurationKey::VALRANGE) {
+ int startPos = tmp.find('(');
+ uint delimiter = 0;
+ if (startPos != (int)std::string::npos) {
+ tmp = tmp.substr(0, startPos);
+ }
+ startPos = -1;
+
+ do {
+ startPos++;
+ delimiter = tmp.find(':', startPos);
+ os << " - valid values: " << "from " << tmp.substr(startPos, delimiter-startPos) << " to "
+ << tmp.substr(delimiter+1, tmp.find(',', delimiter)-delimiter-1) << std::endl;
+
+ } while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
+
+ } else if (key.getType() == ConfigurationKey::CHOICE) {
+ int startPos = -1;
+ uint endPos = 0;
+
+ do {
+ startPos++;
+ if ((endPos = tmp.find('|', startPos)) != std::string::npos) {
+ os << " - valid values: " << tmp.substr(startPos, endPos-startPos);
+ os << " = " << tmp.substr(endPos+1, tmp.find(',', endPos)-endPos-1) << std::endl;
+ } else {
+ os << " - valid values: " << tmp.substr(startPos, tmp.find(',', startPos)-startPos) << std::endl;
+ }
+
+ } while ((startPos = tmp.find(',', startPos)) != (int)std::string::npos);
+
+ } else if (key.getType() == ConfigurationKey::BOOLEAN) {
+ os << " - valid values: 0 = disabled" << std::endl;
+ os << " - valid values: 1 = enabled" << std::endl;
+
+ } else if (key.getType() == ConfigurationKey::STRING) {
+ os << " - valid val regex: " << tmp << std::endl;
+
+ } else if (key.getValidValues().length()) {
+ os << " - raw valid values: " << tmp << std::endl;
+ }
+}
+
// vim: ts=4 sw=4