Skip to content

Commit f3df157

Browse files
committed
symlinks in configuration
1 parent 0c165e6 commit f3df157

File tree

3 files changed

+104
-0
lines changed

3 files changed

+104
-0
lines changed

doc/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@ Equipments should be prefixed as `[equipment-...]`.
140140
Consumers should be prefixed as `[consumer-...]`.
141141
General settings are defined in section `[readout]`.
142142

143+
Section names ending with `-*` can be used to define default parameters. They are applied to all section with similar names. Existing key-value pairs are not overwritten, but are defined according to defaults if they don't exist. For example, it is possible to define the TFperiod for all equipments by adding a section named `[equipment-*]` with `TFperiod=32`.
144+
145+
Values can be symbolic links to a value stored in another configuration file. The syntax is: @LINK,URI,entryPoint,path
146+
For example: @LINK,file:/local/readout-test-config-link1.cfg,,bank.size
147+
Parameters are similar to the command-line arguments of o2-readout-exe, see ```Usage``` below.
148+
Files are cached, i.e. the corresponding configuration tree is loaded only once if several values use a link to the same URI/entryPoint.
149+
Links substitutions are done at the end of the configuration tree aggregation (sections merging, etc).
150+
It is done recursively (up to 5 iterations, after which it fails to avoid circular dependencies).
151+
143152
Comments can be added by starting a line with the # sign. Inline comments (# later in the line) are not accepted.
144153
Documented example files are provided with the source code and distribution.
145154

doc/releaseNotes.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,3 +628,7 @@ This file describes the main feature changes for each readout.exe released versi
628628
## v2.24.0 - 18/06/2024
629629
- Updated configuration parameters:
630630
- equipment-rorc-*: added parameters firmwareVersionsDenied and firmwareVersionsAllowed, to enforce the check of firmware for specific versions. By default, v3.10.0 (e4a5a46e) is denied and all other allowed: it is the default version in CRU flash and should be updated to a more recent one at boot time.
631+
632+
## next version
633+
- Updated configuration parameters:
634+
- Values can be a link to a value stored in another file.

src/mainReadout.cxx

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,97 @@ int Readout::_configure(const boost::property_tree::ptree& properties)
932932
pos = cfg.get().erase(pos);
933933
}
934934

935+
// resolve "symlinks"
936+
int cfgLinksErrors = 0;
937+
struct ConfigCache {
938+
std::string URI;
939+
std::string EntryPoint;
940+
std::unique_ptr<ConfigFile> cfg;
941+
};
942+
std::vector<ConfigCache> cfgCache;
943+
int nSubstitutions = 0;
944+
std::function<void(boost::property_tree::ptree &, const std::string &)> resolveLinks;
945+
resolveLinks = [&resolveLinks, &cfgLinksErrors, &cfgCache, &loadConfig, &nSubstitutions](boost::property_tree::ptree &pt, const std::string &key) -> void {
946+
if (pt.empty()) {
947+
std::string value = pt.data();
948+
const std::string keywordLink = "@LINK";
949+
if (value.compare(0, keywordLink.length(), keywordLink) == 0) {
950+
// this is a symlink
951+
// extract reference
952+
// syntax: @LINK,URI,EntryPoint,Path
953+
std::vector<std::string> linkArgs;
954+
getListFromString(value, linkArgs, ',');
955+
if (linkArgs.size() != 4) {
956+
theLog.log(LogErrorSupport_(3102), "Failed to parse link: %s = %s", key.c_str(), value.c_str());
957+
cfgLinksErrors++;
958+
return;
959+
}
960+
const char* cfgLinkUri = linkArgs[1].c_str();
961+
const char* cfgLinkEntryPoint = linkArgs[2].c_str();
962+
const char* cfgLinkPath = linkArgs[3].c_str();
963+
// search for file in cache
964+
unsigned int ix = 0;
965+
for(;ix < cfgCache.size(); ix++) {
966+
if ((cfgCache[ix].URI == cfgLinkUri) && (cfgCache[ix].EntryPoint == cfgLinkEntryPoint)) {
967+
break;
968+
}
969+
}
970+
if (ix == cfgCache.size()) {
971+
// no match in cache, add it
972+
try {
973+
auto cfg = std::make_unique<ConfigFile>();
974+
if (cfg==nullptr) {
975+
throw __LINE__;
976+
}
977+
if (loadConfig(cfgLinkUri, cfgLinkEntryPoint, *cfg)) {
978+
throw __LINE__;
979+
}
980+
cfgCache.push_back({cfgLinkUri, cfgLinkEntryPoint, std::move(cfg)});
981+
}
982+
catch(...) {
983+
theLog.log(LogErrorSupport_(3102), "Failed to load linked configuration %s %s", cfgLinkUri, cfgLinkEntryPoint);
984+
cfgLinksErrors++;
985+
return;
986+
}
987+
theLog.log(LogInfoSupport, "Reading linked configuration from %s %s", cfgLinkUri, cfgLinkEntryPoint);
988+
}
989+
// at this stage we now have a valid config in cache at index ix
990+
// get value from linked config
991+
std::string linkValue;
992+
if (cfgCache[ix].cfg->getOptionalValue<std::string>(cfgLinkPath, linkValue)) {
993+
theLog.log(LogErrorSupport_(3102), "Failed to get link value: %s = %s", key.c_str(), value.c_str());
994+
cfgLinksErrors++;
995+
return;
996+
}
997+
theLog.log(LogInfoDevel_(3002), "Link substituted %s : %s -> %s", key.c_str(), value.c_str(), linkValue.c_str());
998+
pt.data() = linkValue;
999+
nSubstitutions++;
1000+
}
1001+
return;
1002+
}
1003+
for (boost::property_tree::ptree::iterator pos = pt.begin(); pos != pt.end();++pos) {
1004+
//printf("%s\n", pos->first.c_str());
1005+
resolveLinks(pos->second, pos->first.c_str());
1006+
}
1007+
};
1008+
int maxLoops = 5;
1009+
for (int i = 0; i <= maxLoops; i++) {
1010+
if (i == maxLoops) {
1011+
theLog.log(LogErrorSupport_(3100), "Links not fully resolved after %d iterations, there might be some circular dependencies in the configuration", maxLoops);
1012+
break;
1013+
}
1014+
nSubstitutions = 0;
1015+
resolveLinks(cfg.get(),"");
1016+
if (nSubstitutions == 0) {
1017+
break;
1018+
}
1019+
}
1020+
if (cfgLinksErrors) {
1021+
theLog.log(LogErrorSupport_(3100), "Some links in the configuration could not be resolved");
1022+
return -1;
1023+
}
1024+
1025+
9351026
// extract optional configuration parameters
9361027
// configuration parameter: | readout | customCommands | string | | List of key=value pairs defining some custom shell commands to be executed at before/after state change commands. |
9371028
if (customCommandsShellPid) {

0 commit comments

Comments
 (0)