mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
config: introduce new class for loading and parsing configuration
This commit is contained in:
parent
00631f9b07
commit
3260c5a57e
8 changed files with 294 additions and 103 deletions
82
include/villas/config.hpp
Normal file
82
include/villas/config.hpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
/** Configuration file parsing.
|
||||
*
|
||||
* @file
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @license GNU General Public License (version 3)
|
||||
*
|
||||
* VILLASnode
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <jansson.h>
|
||||
|
||||
#include <villas/config.h>
|
||||
#include <villas/advio.h>
|
||||
|
||||
namespace villas {
|
||||
namespace node {
|
||||
|
||||
class Config {
|
||||
|
||||
protected:
|
||||
std::string uri;
|
||||
|
||||
FILE *local_file;
|
||||
AFILE *remote_file;
|
||||
|
||||
Logger logger;
|
||||
|
||||
/** Check if file exists on local system. */
|
||||
static bool isLocalFile(const std::string &uri)
|
||||
{
|
||||
return access(uri.c_str(), F_OK) != -1;
|
||||
}
|
||||
|
||||
/** Decode configuration file. */
|
||||
void decode();
|
||||
|
||||
#ifdef LIBCONFIG_FOUND
|
||||
/** Convert libconfig .conf file to libjansson .json file. */
|
||||
void libconfigDecode();
|
||||
#endif /* LIBCONFIG_FOUND */
|
||||
|
||||
/** Load configuration from standard input (stdim). */
|
||||
void loadFromStdio();
|
||||
|
||||
/** Load configuration from local file. */
|
||||
void loadFromLocalFile(const std::string &u);
|
||||
|
||||
/** Load configuration from a remote URI via advio. */
|
||||
void loadFromRemoteFile(const std::string &u);
|
||||
|
||||
public:
|
||||
json_t *root;
|
||||
|
||||
Config();
|
||||
Config(const std::string &u);
|
||||
|
||||
~Config();
|
||||
|
||||
void load(const std::string &u);
|
||||
|
||||
/** Pretty-print libjansson error. */
|
||||
void prettyPrintError(json_error_t err);
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
} // namespace villas
|
|
@ -27,6 +27,7 @@
|
|||
#include <villas/api.hpp>
|
||||
#include <villas/web.hpp>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/node.h>
|
||||
#include <villas/task.h>
|
||||
#include <villas/common.h>
|
||||
|
@ -42,10 +43,6 @@ protected:
|
|||
|
||||
int idleStop;
|
||||
|
||||
int priority; /**< Process priority (lower is better) */
|
||||
int affinity; /**< Process affinity of the server and all created threads */
|
||||
int hugepages; /**< Number of hugepages to reserve. */
|
||||
|
||||
Logger logger;
|
||||
|
||||
struct vlist nodes;
|
||||
|
@ -60,12 +57,16 @@ protected:
|
|||
Web web;
|
||||
#endif
|
||||
|
||||
int priority; /**< Process priority (lower is better) */
|
||||
int affinity; /**< Process affinity of the server and all created threads */
|
||||
int hugepages; /**< Number of hugepages to reserve. */
|
||||
|
||||
struct task task; /**< Task for periodic stats output */
|
||||
|
||||
std::string name; /**< A name of this super node. Usually the hostname. */
|
||||
std::string uri; /**< URI of configuration */
|
||||
|
||||
json_t *json; /**< JSON representation of the configuration. */
|
||||
Config config; /** The configuration file. */
|
||||
|
||||
public:
|
||||
/** Inititalize configuration object before parsing the configuration. */
|
||||
|
@ -73,14 +74,14 @@ public:
|
|||
|
||||
int init();
|
||||
|
||||
/** Wrapper for parse() */
|
||||
void parseUri(const std::string &name);
|
||||
/** Wrapper for parse() which loads the config first. */
|
||||
void parse(const std::string &name);
|
||||
|
||||
/** Parse super-node configuration.
|
||||
*
|
||||
* @param cfg A libjansson object which contains the configuration.
|
||||
*/
|
||||
void parseJson(json_t *cfg);
|
||||
void parse(json_t *cfg);
|
||||
|
||||
/** Check validity of super node configuration. */
|
||||
void check();
|
||||
|
@ -148,7 +149,7 @@ public:
|
|||
|
||||
json_t * getConfig()
|
||||
{
|
||||
return json;
|
||||
return config.root;
|
||||
}
|
||||
|
||||
std::string getConfigUri()
|
||||
|
|
|
@ -42,6 +42,7 @@ endif()
|
|||
set(LIB_SRC
|
||||
super_node.cpp
|
||||
config_helper.cpp
|
||||
config.cpp
|
||||
memory/heap.c
|
||||
memory/hugepage.c
|
||||
memory/managed.c
|
||||
|
|
190
lib/config.cpp
Normal file
190
lib/config.cpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
|
||||
/** Configuration file parsing.
|
||||
*
|
||||
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
|
||||
* @copyright 2014-2019, Institute for Automation of Complex Power Systems, EONERC
|
||||
* @license GNU General Public License (version 3)
|
||||
*
|
||||
* VILLASnode
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************************/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
#include <villas/utils.h>
|
||||
#include <villas/log.hpp>
|
||||
#include <villas/config.hpp>
|
||||
#include <villas/node/exceptions.hpp>
|
||||
#include <villas/config_helper.hpp>
|
||||
|
||||
using namespace villas;
|
||||
using namespace villas::node;
|
||||
|
||||
Config::Config() :
|
||||
local_file(nullptr),
|
||||
remote_file(nullptr),
|
||||
root(nullptr)
|
||||
{
|
||||
logger = logging.get("config");
|
||||
}
|
||||
|
||||
Config::Config(const std::string &u) :
|
||||
Config()
|
||||
{
|
||||
load(u);
|
||||
}
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
/* Close configuration file */
|
||||
if (remote_file)
|
||||
afclose(remote_file);
|
||||
else if (local_file != stdin)
|
||||
fclose(local_file);
|
||||
|
||||
if (root)
|
||||
json_decref(root);
|
||||
}
|
||||
|
||||
void Config::load(const std::string &u)
|
||||
{
|
||||
if (u == "-")
|
||||
loadFromStdio();
|
||||
else if (isLocalFile(u))
|
||||
loadFromLocalFile(u);
|
||||
else
|
||||
loadFromRemoteFile(u);
|
||||
|
||||
decode();
|
||||
}
|
||||
|
||||
void Config::loadFromStdio()
|
||||
{
|
||||
logger->info("Reading configuration from standard input");
|
||||
|
||||
local_file = stdin;
|
||||
}
|
||||
|
||||
void Config::loadFromLocalFile(const std::string &u)
|
||||
{
|
||||
logger->info("Reading configuration from local file: {}", u);
|
||||
|
||||
local_file = fopen(u.c_str(), "r");
|
||||
if (!local_file)
|
||||
throw RuntimeError("Failed to open configuration from: {}", u);
|
||||
|
||||
uri = u;
|
||||
}
|
||||
|
||||
void Config::loadFromRemoteFile(const std::string &u)
|
||||
{
|
||||
logger->info("Reading configuration from remote URI: {}", u);
|
||||
|
||||
remote_file = afopen(u.c_str(), "r");
|
||||
if (!remote_file)
|
||||
throw RuntimeError("Failed to open configuration from: {}", u);
|
||||
|
||||
local_file = remote_file->file;
|
||||
|
||||
uri = u;
|
||||
}
|
||||
|
||||
void Config::decode()
|
||||
{
|
||||
json_error_t err;
|
||||
|
||||
root = json_loadf(local_file, 0, &err);
|
||||
if (root == nullptr) {
|
||||
#ifdef LIBCONFIG_FOUND
|
||||
/* We try again to parse the config in the legacy format */
|
||||
libconfigDecode();
|
||||
#else
|
||||
throw JanssonParseError(err);
|
||||
#endif /* LIBCONFIG_FOUND */
|
||||
}
|
||||
}
|
||||
|
||||
void Config::libconfigDecode()
|
||||
{
|
||||
int ret;
|
||||
|
||||
config_t cfg;
|
||||
config_setting_t *cfg_root;
|
||||
config_init(&cfg);
|
||||
config_set_auto_convert(&cfg, 1);
|
||||
|
||||
/* Setup libconfig include path.
|
||||
* This is only supported for local files */
|
||||
if (isLocalFile(uri)) {
|
||||
char *cpy = strdup(uri.c_str());
|
||||
|
||||
config_set_include_dir(&cfg, dirname(cpy));
|
||||
|
||||
free(cpy);
|
||||
}
|
||||
|
||||
if (remote_file)
|
||||
arewind(remote_file);
|
||||
else
|
||||
rewind(local_file);
|
||||
|
||||
ret = config_read(&cfg, local_file);
|
||||
if (ret != CONFIG_TRUE)
|
||||
throw LibconfigParseError(&cfg);
|
||||
|
||||
cfg_root = config_root_setting(&cfg);
|
||||
|
||||
root = config_to_json(cfg_root);
|
||||
if (root == nullptr)
|
||||
throw RuntimeError("Failed to convert JSON to configuration file");
|
||||
|
||||
config_destroy(&cfg);
|
||||
}
|
||||
|
||||
void Config::prettyPrintError(json_error_t err)
|
||||
{
|
||||
std::ifstream infile(uri);
|
||||
std::string line;
|
||||
|
||||
int context = 4;
|
||||
int start_line = err.line - context;
|
||||
int end_line = err.line + context;
|
||||
|
||||
for (int line_no = 0; std::getline(infile, line); line_no++) {
|
||||
if (line_no < start_line || line_no > end_line)
|
||||
continue;
|
||||
|
||||
std::cerr << std::setw(4) << std::right << line_no << " " BOX_UD " " << line << std::endl;
|
||||
|
||||
if (line_no == err.line) {
|
||||
for (int col = 0; col < err.column; col++)
|
||||
std::cerr << " ";
|
||||
|
||||
std::cerr << BOX_UD;
|
||||
|
||||
for (int col = 0; col < err.column; col++)
|
||||
std::cerr << " ";
|
||||
|
||||
std::cerr << BOX_UR << err.text << std::endl;
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,8 +22,6 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <villas/super_node.hpp>
|
||||
#include <villas/node.h>
|
||||
|
@ -50,16 +48,15 @@ using namespace villas::node;
|
|||
SuperNode::SuperNode() :
|
||||
state(STATE_INITIALIZED),
|
||||
idleStop(false),
|
||||
priority(0),
|
||||
affinity(0),
|
||||
hugepages(DEFAULT_NR_HUGEPAGES),
|
||||
#ifdef WITH_API
|
||||
api(this),
|
||||
#ifdef WITH_WEB
|
||||
web(&api),
|
||||
#endif
|
||||
priority(0),
|
||||
affinity(0),
|
||||
hugepages(DEFAULT_NR_HUGEPAGES)
|
||||
#endif
|
||||
json(nullptr)
|
||||
{
|
||||
nodes.state = STATE_DESTROYED;
|
||||
paths.state = STATE_DESTROYED;
|
||||
|
@ -81,89 +78,14 @@ SuperNode::SuperNode() :
|
|||
logger = logging.get("super_node");
|
||||
}
|
||||
|
||||
void SuperNode::parseUri(const std::string &u)
|
||||
void SuperNode::parse(const std::string &u)
|
||||
{
|
||||
json_error_t err;
|
||||
config.load(u);
|
||||
|
||||
FILE *f;
|
||||
AFILE *af;
|
||||
|
||||
/* Via stdin */
|
||||
if (u == "-") {
|
||||
logger->info("Reading configuration from standard input");
|
||||
|
||||
af = nullptr;
|
||||
f = stdin;
|
||||
}
|
||||
else {
|
||||
logger->info("Reading configuration from URI: {}", u);
|
||||
|
||||
af = afopen(u.c_str(), "r");
|
||||
if (!af)
|
||||
throw RuntimeError("Failed to open configuration from: {}", u);
|
||||
|
||||
f = af->file;
|
||||
}
|
||||
|
||||
/* Parse config */
|
||||
json = json_loadf(f, 0, &err);
|
||||
if (json == nullptr) {
|
||||
#ifdef LIBCONFIG_FOUND
|
||||
int ret;
|
||||
|
||||
config_t cfg;
|
||||
config_setting_t *json_root = nullptr;
|
||||
|
||||
config_init(&cfg);
|
||||
config_set_auto_convert(&cfg, 1);
|
||||
|
||||
/* Setup libconfig include path.
|
||||
* This is only supported for local files */
|
||||
if (access(u.c_str(), F_OK) != -1) {
|
||||
char *cpy = strdup(u.c_str());
|
||||
|
||||
config_set_include_dir(&cfg, dirname(cpy));
|
||||
|
||||
free(cpy);
|
||||
}
|
||||
|
||||
if (af)
|
||||
arewind(af);
|
||||
else
|
||||
rewind(f);
|
||||
|
||||
ret = config_read(&cfg, f);
|
||||
if (ret != CONFIG_TRUE) {
|
||||
logger->warn("conf: {} in {}:{}", config_error_text(&cfg), u.c_str(), config_error_line(&cfg));
|
||||
logger->warn("json: {} in {}:{}:{}", err.text, err.source, err.line, err.column);
|
||||
logger->error("Failed to parse configuration");
|
||||
killme(SIGABRT);
|
||||
}
|
||||
|
||||
json_root = config_root_setting(&cfg);
|
||||
|
||||
json = config_to_json(json_root);
|
||||
if (json == nullptr)
|
||||
throw RuntimeError("Failed to convert JSON to configuration file");
|
||||
|
||||
config_destroy(&cfg);
|
||||
#else
|
||||
throw JsonError(err, "Failed to parse configuration file");
|
||||
#endif /* LIBCONFIG_FOUND */
|
||||
}
|
||||
|
||||
/* Close configuration file */
|
||||
if (af)
|
||||
afclose(af);
|
||||
else if (f != stdin)
|
||||
fclose(f);
|
||||
|
||||
uri = u;
|
||||
|
||||
parseJson(json);
|
||||
parse(config.root);
|
||||
}
|
||||
|
||||
void SuperNode::parseJson(json_t *j)
|
||||
void SuperNode::parse(json_t *cfg)
|
||||
{
|
||||
int ret;
|
||||
const char *nme = nullptr;
|
||||
|
@ -179,7 +101,7 @@ void SuperNode::parseJson(json_t *j)
|
|||
|
||||
idleStop = true;
|
||||
|
||||
ret = json_unpack_ex(j, &err, JSON_STRICT, "{ s?: o, s?: o, s?: o, s?: o, s?: i, s?: i, s?: i, s?: s, s?: b }",
|
||||
ret = json_unpack_ex(cfg, &err, JSON_STRICT, "{ s?: o, s?: o, s?: o, s?: o, s?: i, s?: i, s?: i, s?: s, s?: b }",
|
||||
"http", &json_web,
|
||||
"logging", &json_logging,
|
||||
"nodes", &json_nodes,
|
||||
|
@ -285,8 +207,6 @@ parse: path *p = (path *) alloc(sizeof(path));
|
|||
}
|
||||
}
|
||||
|
||||
json = j;
|
||||
|
||||
state = STATE_PARSED;
|
||||
}
|
||||
|
||||
|
@ -558,9 +478,6 @@ SuperNode::~SuperNode()
|
|||
vlist_destroy(&paths, (dtor_cb_t) path_destroy, true);
|
||||
vlist_destroy(&nodes, (dtor_cb_t) node_destroy, true);
|
||||
vlist_destroy(&interfaces, (dtor_cb_t) if_destroy, true);
|
||||
|
||||
if (json)
|
||||
json_decref(json);
|
||||
}
|
||||
|
||||
int SuperNode::periodic()
|
||||
|
|
|
@ -172,7 +172,7 @@ int main(int argc, char *argv[])
|
|||
throw RuntimeError("Failed to initialize signal subsystem");
|
||||
|
||||
if (uri)
|
||||
sn.parseUri(uri);
|
||||
sn.parse(uri);
|
||||
else
|
||||
logger->warn("No configuration file specified. Starting unconfigured. Use the API to configure this instance.");
|
||||
|
||||
|
|
|
@ -350,7 +350,7 @@ check: if (optarg == endptr)
|
|||
throw RuntimeError("Failed to initialize signals");
|
||||
|
||||
if (uri)
|
||||
sn.parseUri(uri);
|
||||
sn.parse(uri);
|
||||
else
|
||||
logger->warn("No configuration file specified. Starting unconfigured. Use the API to configure this instance.");
|
||||
|
||||
|
|
|
@ -152,7 +152,7 @@ check: if (optarg == endptr)
|
|||
throw RuntimeError("Failed to initialize signals subsystem");
|
||||
|
||||
if (uri)
|
||||
sn.parseUri(uri);
|
||||
sn.parse(uri);
|
||||
else
|
||||
logger->warn("No configuration file specified. Starting unconfigured. Use the API to configure this instance.");
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue