config: creating templates for a specific context

Templates for new peers must preserve the existing values
of shared properties, otherwise writing back the template
might unintentionally overwrite those.

Because this depends on getting the right context, some utility
functions from SyncConfig were made public. Instead of "" as normal
form for the default context, "@default" is now used. This is more
readable in error messages and while debugging.

This patch does this only for the
syncevo-dbus-server. SyncConfig::createServerTemplate() knows nothing
about contexts at the moment. The command line has not been updated
yet (MB #8048). When doing that it might turn out that changing the
semantic of SyncConfig::createServerTemplate() and moving some
syncevo-dbus-server.cpp code into SyncConfig.cpp is the better
solution.
This commit is contained in:
Patrick Ohly 2009-11-21 18:25:19 +01:00
parent 50e8a7ca9d
commit 6207e80bec
4 changed files with 102 additions and 25 deletions

View File

@ -189,6 +189,10 @@ public:
/** Session.GetDatabases() */
void getDatabases(const string &sourceName, SourceDatabases_t &databases);
private:
/** utility method which constructs a SyncConfig which references a local configuration (never a template) */
boost::shared_ptr<SyncConfig> getLocalConfig(const std::string &configName);
};
/**
@ -1146,6 +1150,28 @@ void ReadOperations::getConfigs(bool getTemplates, std::vector<std::string> &con
}
}
boost::shared_ptr<SyncConfig> ReadOperations::getLocalConfig(const string &configName)
{
string peer, context;
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(configName),
peer, context);
boost::shared_ptr<SyncConfig> syncConfig(new SyncConfig(configName));
// the default configuration can always be opened for reading,
// everything else must exist
if (context != "default" &&
!syncConfig->exists()) {
SE_THROW_EXCEPTION(NoSuchConfig, "No configuration '" + configName + "' found");
}
// TODO: handle temporary configs (MB #8116)
// - if config was set temporarily, it doesn't have to exist on disk =>
// the check above is too strict
// - set temporary properties as filters
return syncConfig;
}
void ReadOperations::getConfig(bool getTemplate,
Config_t &config)
{
@ -1153,18 +1179,30 @@ void ReadOperations::getConfig(bool getTemplate,
boost::shared_ptr<SyncConfig> syncConfig;
/** get server template */
if(getTemplate) {
syncConfig = SyncConfig::createServerTemplate(m_configName);
string peer, context;
SyncConfig::splitConfigString(SyncConfig::normalizeConfigString(m_configName),
peer, context);
syncConfig = SyncConfig::createServerTemplate(peer);
if(!syncConfig.get()) {
SE_THROW_EXCEPTION(NoSuchConfig, "No template '" + m_configName + "' found");
}
} else { ///< get a matching server configuration
boost::shared_ptr<SyncConfig> from;
syncConfig.reset(new SyncConfig(m_configName));
// the default configuration can always be opened for reading,
// everything else must exist
if (!m_configName.empty() && !syncConfig->exists()) {
SE_THROW_EXCEPTION(NoSuchConfig, "No configuration '" + m_configName + "' found");
// use the shared properties from the right context as filter
// so that the returned template preserves existing properties
boost::shared_ptr<SyncConfig> shared = getLocalConfig(string("@") + context);
ConfigProps props;
shared->getProperties()->readProperties(props);
syncConfig->setConfigFilter(true, "", props);
BOOST_FOREACH(std::string source, shared->getSyncSources()) {
SyncSourceNodes nodes = shared->getSyncSourceNodes(source, "");
props.clear();
nodes.getProperties()->readProperties(props);
syncConfig->setConfigFilter(false, source, props);
}
} else {
syncConfig = getLocalConfig(m_configName);
}
/** get sync properties and their values */

View File

@ -67,7 +67,7 @@ void ConfigProperty::throwValueError(const ConfigNode &node, const string &name,
SyncContext::throwError(node.getName() + ": " + name + " = " + value + ": " + error);
}
string SyncConfig::normalizePeerString(const string &peer)
string SyncConfig::normalizeConfigString(const string &peer)
{
string normal = peer;
boost::to_lower(normal);
@ -76,9 +76,24 @@ string SyncConfig::normalizePeerString(const string &peer)
} else if (boost::ends_with(normal, "@")) {
normal.resize(normal.size() - 1);
}
if (normal.empty()) {
normal = "@default";
}
return normal;
}
void SyncConfig::splitConfigString(const string &config, string &peer, string &context)
{
string::size_type at = config.rfind('@');
if (at != config.npos) {
peer = config.substr(0, at);
context = config.substr(at + 1);
} else {
peer = config;
context = "default";
}
}
SyncConfig::SyncConfig() :
m_layout(HTTP_SERVER_LAYOUT) // use more compact layout with shorter paths and less source nodes
{
@ -113,7 +128,7 @@ SyncConfig::SyncConfig(const string &peer,
string root;
m_peer = normalizePeerString(peer);
m_peer = normalizeConfigString(peer);
// except for SHARED_LAYOUT (set below),
// everything is below the directory called like
@ -139,13 +154,7 @@ SyncConfig::SyncConfig(const string &peer,
} else {
// check whether config name specifies a context,
// otherwise use "default"
string::size_type at = m_peerPath.rfind('@');
if (at != m_peerPath.npos) {
m_contextPath = m_peerPath.substr(at + 1);
m_peerPath.resize(at);
} else {
m_contextPath = "default";
}
splitConfigString(m_peer, m_peerPath, m_contextPath);
if (!m_peerPath.empty()) {
m_peerPath = m_contextPath + "/peers/" + m_peerPath;
}
@ -264,7 +273,7 @@ void SyncConfig::addPeers(const string &root,
if (!access((root + "/" + peerPath).c_str(), F_OK)) {
// not a real HTTP server, search for peers
BOOST_FOREACH(const string &peer, tree.getChildren(peerPath)) {
res.push_back(pair<string, string>(normalizePeerString(peer + "@" + server),
res.push_back(pair<string, string>(normalizeConfigString(peer + "@" + server),
root + "/" + peerPath + "/" + peer));
}
} else if (!access((root + "/" + server + "/" + configname).c_str(), F_OK)) {

View File

@ -890,6 +890,21 @@ class SyncConfig {
*/
static ConfigPropertyRegistry &getRegistry();
/**
* Normalize a config string:
* - lower case
* - @default stripped
* - empty string replaced with "@default"
*/
static string normalizeConfigString(const string &peer);
/**
* Split a config string (normalized or not) into the peer part
* (before final @) and the context (after that @, not including
* it), return "default" as context if not specified otherwise.
*/
static void splitConfigString(const string &config, string &peer, string &context);
/**
* Replaces the property filter of either the sync properties or
* all sources. This can be used to e.g. temporarily override
@ -1184,13 +1199,6 @@ private:
SyncEvolution >= 1.0 */
};
/**
* Normalize a peer string:
* - lower case
* - @default stripped
*/
static string normalizePeerString(const string &peer);
/**
* scans for peer configurations
* @param root absolute directory path

View File

@ -1463,5 +1463,27 @@ class TestMultipleConfigs(unittest.TestCase, DBusUtil):
self.failUnlessEqual(peers, ['foo@other_context'])
self.session.Detach()
def testTemplates(self):
'''templates reuse common properties'''
self.setupConfigs()
# deviceID must be shared and thus be reused in templates
self.setUpSession("")
config = self.session.GetConfig(False, utf8_strings=True)
config[""]["DEVICEID"] = "shared-device-identifier"
self.session.SetConfig(True, False, config, utf8_strings=True)
config = self.server.GetConfig("", False, utf8_strings=True)
self.failUnlessEqual(config[""]["deviceId"], "shared-device-identifier")
# get template for default context
config = self.server.GetConfig("scheduleworld", True, utf8_strings=True)
self.failUnlessEqual(config[""]["defaultPeer"], "foobar_peer")
self.failUnlessEqual(config[""]["deviceId"], "shared-device-identifier")
# now for @other_context - different device ID!
config = self.server.GetConfig("scheduleworld@other_context", True, utf8_strings=True)
self.failUnlessEqual(config[""]["defaultPeer"], "foobar_peer")
self.failIfEqual(config[""]["deviceId"], "shared-device-identifier")
if __name__ == '__main__':
unittest.main()