diff --git a/doc/openapi.yaml b/doc/openapi.yaml index 71f256c10..c6f158fc4 100644 --- a/doc/openapi.yaml +++ b/doc/openapi.yaml @@ -192,7 +192,6 @@ paths: formats: - csv - gtnet - - gtnet.fake - iotagent_ul - json - json.kafka diff --git a/etc/examples/nodes/ethernet.conf b/etc/examples/nodes/ethernet.conf deleted file mode 100644 index e4e4e26bf..000000000 --- a/etc/examples/nodes/ethernet.conf +++ /dev/null @@ -1,26 +0,0 @@ -nodes = { - ethernet_node = { - type = "socket", # See above. - - ### The following settings are specific to the socket node-type!! ### - - layer = "eth", - in = { - address = "12:34:56:78:90:AB%em1:12002" - }, - out = { - address = "12:34:56:78:90:AB%em1:12002" - } - }, - unix_domain_node = { - type = "socket", - layer = "unix", # Datagram UNIX domain sockets require two endpoints - - in = { - address = "/var/run/villas-node/node.sock" - }, - out = { - address = "/var/run/villas-node/client.sock" - } - } -} diff --git a/etc/examples/nodes/example.conf b/etc/examples/nodes/example.conf new file mode 100644 index 000000000..763a3de1a --- /dev/null +++ b/etc/examples/nodes/example.conf @@ -0,0 +1,8 @@ +nodes = { + example_node = { + type = "example" + + setting1 = 1 + setting2 = "abc" + } +} diff --git a/etc/examples/nodes/sampled_values.conf b/etc/examples/nodes/iec61850-9-2.conf similarity index 100% rename from etc/examples/nodes/sampled_values.conf rename to etc/examples/nodes/iec61850-9-2.conf diff --git a/etc/examples/nodes/multicast.conf b/etc/examples/nodes/multicast.conf deleted file mode 100644 index 8ac01b256..000000000 --- a/etc/examples/nodes/multicast.conf +++ /dev/null @@ -1,23 +0,0 @@ -nodes = { - udp_node = { # The dictionary is indexed by the name of the node. - type = "socket", # For a list of available node-types run: 'villas-node -h' - - ### The following settings are specific to the socket node-type!! ### - - in = { - address = "127.0.0.1:12001" # This node only received messages on this IP:Port pair - - multicast = { # IGMP multicast is only support for layer = (ip|udp) - enabled = true, - - group = "224.1.2.3", # The multicast group. Must be within 224.0.0.0/4 - interface = "1.2.3.4", # The IP address of the interface which should receive multicast packets. - ttl = 128, # The time to live for outgoing multicast packets. - loop = false, # Whether or not to loopback outgoing multicast packets to the local host. - } - }, - out = { - address = "127.0.0.1:12000", # This node sents outgoing messages to this IP:Port pair - } - } -} diff --git a/etc/examples/nodes/netem.conf b/etc/examples/nodes/netem.conf index c42386188..53752745e 100644 --- a/etc/examples/nodes/netem.conf +++ b/etc/examples/nodes/netem.conf @@ -4,7 +4,7 @@ nodes = { ### The following settings are specific to the socket node-type!! ### - format = "gtnet.fake", # For a list of available node-types run: 'villas-node -h' + format = "gtnet", # For a list of available node-types run: 'villas-node -h' in = { address = "127.0.0.1:12001" # This node only received messages on this IP:Port pair diff --git a/etc/examples/nodes/signal_generator.conf b/etc/examples/nodes/signal.conf similarity index 100% rename from etc/examples/nodes/signal_generator.conf rename to etc/examples/nodes/signal.conf diff --git a/etc/examples/nodes/socket.conf b/etc/examples/nodes/socket.conf new file mode 100644 index 000000000..11dbcbef1 --- /dev/null +++ b/etc/examples/nodes/socket.conf @@ -0,0 +1,76 @@ +nodes = { + udp_node = { # The dictionary is indexed by the name of the node. + type = "socket", # For a list of available node-types run: 'villas-node -h' + vectorize = 30, # Receive and sent 30 samples per message (combining). + samplelen = 10 # The maximum number of samples this node can receive + + builtin = false, # By default, all nodes will have a few builtin hooks attached to them. + # When collecting statistics or measurements these are undesired. + + ### The following settings are specific to the socket node-type!! ### + + layer = "udp", # Layer can be one of: + # - udp Send / receive L4 UDP packets + # - ip Send / receive L3 IP packets + # - eth Send / receive L2 Ethernet frames (IEEE802.3) + + format = "gtnet", # For a list of available node-types run: 'villas-node -h' + + in = { + address = "127.0.0.1:12001" # This node only received messages on this IP:Port pair + + verify_source = true # Check if source address of incoming packets matches the remote address. + }, + out = { + address = "127.0.0.1:12000", # This node sents outgoing messages to this IP:Port pair + } + } + + ethernet_node = { + type = "socket", # See above. + + ### The following settings are specific to the socket node-type!! ### + + layer = "eth", + in = { + address = "12:34:56:78:90:AB%em1:12002" + }, + out = { + address = "12:34:56:78:90:AB%em1:12002" + } + }, + + unix_domain_node = { + type = "socket", + layer = "unix", # Datagram UNIX domain sockets require two endpoints + + in = { + address = "/var/run/villas-node/node.sock" + }, + out = { + address = "/var/run/villas-node/client.sock" + } + } + + udp_multicast_node = { # The dictionary is indexed by the name of the node. + type = "socket", # For a list of available node-types run: 'villas-node -h' + + ### The following settings are specific to the socket node-type!! ### + + in = { + address = "127.0.0.1:12001" # This node only received messages on this IP:Port pair + + multicast = { # IGMP multicast is only support for layer = (ip|udp) + enabled = true, + + group = "224.1.2.3", # The multicast group. Must be within 224.0.0.0/4 + interface = "1.2.3.4", # The IP address of the interface which should receive multicast packets. + ttl = 128, # The time to live for outgoing multicast packets. + loop = false, # Whether or not to loopback outgoing multicast packets to the local host. + } + }, + out = { + address = "127.0.0.1:12000", # This node sents outgoing messages to this IP:Port pair + } + } +} diff --git a/etc/examples/nodes/temper.conf b/etc/examples/nodes/temper.conf new file mode 100644 index 000000000..0de395ac3 --- /dev/null +++ b/etc/examples/nodes/temper.conf @@ -0,0 +1,13 @@ +nodes = { + temper_node = { + type = "temper" + + calibration = { + scale = 1.0 + offset = 0.0 + } + + bus = 0x1 + port = 0x1 + } +} diff --git a/etc/examples/nodes/udp.conf b/etc/examples/nodes/udp.conf deleted file mode 100644 index 4eadd13e3..000000000 --- a/etc/examples/nodes/udp.conf +++ /dev/null @@ -1,28 +0,0 @@ -nodes = { - udp_node = { # The dictionary is indexed by the name of the node. - type = "socket", # For a list of available node-types run: 'villas-node -h' - vectorize = 30, # Receive and sent 30 samples per message (combining). - samplelen = 10 # The maximum number of samples this node can receive - - builtin = false, # By default, all nodes will have a few builtin hooks attached to them. - # When collecting statistics or measurements these are undesired. - - ### The following settings are specific to the socket node-type!! ### - - layer = "udp", # Layer can be one of: - # - udp Send / receive L4 UDP packets - # - ip Send / receive L3 IP packets - # - eth Send / receive L2 Ethernet frames (IEEE802.3) - - format = "gtnet.fake", # For a list of available node-types run: 'villas-node -h' - - in = { - address = "127.0.0.1:12001" # This node only received messages on this IP:Port pair - - verify_source = true # Check if source address of incoming packets matches the remote address. - }, - out = { - address = "127.0.0.1:12000", # This node sents outgoing messages to this IP:Port pair - } - } -} diff --git a/etc/gtnet-skt/test5.conf b/etc/gtnet-skt/test5.conf index fdf1c8787..b28ba4bc1 100644 --- a/etc/gtnet-skt/test5.conf +++ b/etc/gtnet-skt/test5.conf @@ -32,7 +32,10 @@ nodes = { node1 = { type = "socket" - format = "gtnet.fake" + format = { + type = "gtnet" + fake = true + } in = { address = "134.130.169.31:12002" # Local ip:port, use '*' for random port diff --git a/etc/gtnet-skt/test6_gtsync_compare.conf b/etc/gtnet-skt/test6_gtsync_compare.conf index 69bd1a233..de83d76e3 100644 --- a/etc/gtnet-skt/test6_gtsync_compare.conf +++ b/etc/gtnet-skt/test6_gtsync_compare.conf @@ -32,7 +32,10 @@ logging = { nodes = { node1 = { type = "socket", - format = "gtnet.fake" + format = { + type = "gtnet" + fake = true + } in = { address = "134.130.169.31:12002" # Local ip:port, use '*' for random port @@ -50,7 +53,10 @@ nodes = { }, node2 = { type = "socket", - format = "gtnet.fake" + format = { + type = "gtnet" + fake = true + } in = { address = "134.130.169.31:12004", # Local ip:port, use '*' for random port diff --git a/etc/labs/lab17.conf b/etc/labs/lab17.conf index e7c143fdb..b57dcba47 100644 --- a/etc/labs/lab17.conf +++ b/etc/labs/lab17.conf @@ -3,7 +3,10 @@ nodes = { rtds_ss1 = { type = "socket", layer = "udp", - format = "gtnet.fake" + format = { + type = "gtnet" + fake = true + } in = { # Local address, i.e. address of villas instance address = "134.130.169.31:12000" @@ -45,7 +48,10 @@ nodes = { rtds_ss2 = { type = "socket", layer = "udp", - format = "gtnet.fake" + format = { + type = "gtnet" + fake = true + } in = { # Local address, i.e. address of villas instance @@ -90,7 +96,10 @@ nodes = { rtds_ss1_monitoring = { type = "socket" layer = "udp" - format = "gtnet.fake" + format = { + type = "gtnet" + fake = true + } in = { # Local address, i.e. address of villas instance address = "134.130.169.31:12002" diff --git a/etc/Shmem_mqtt.conf b/etc/shmem_mqtt.conf similarity index 98% rename from etc/Shmem_mqtt.conf rename to etc/shmem_mqtt.conf index 859ff026e..02060fc83 100644 --- a/etc/Shmem_mqtt.conf +++ b/etc/shmem_mqtt.conf @@ -1,12 +1,10 @@ nodes = { sig = { - - type = "signal1" + type = "signal" signal = "sine" } - dpsim = { enabled = false, type = "shmem", diff --git a/include/villas/capabilities.hpp b/include/villas/capabilities.hpp new file mode 100644 index 000000000..b939f29f7 --- /dev/null +++ b/include/villas/capabilities.hpp @@ -0,0 +1,33 @@ +/** Capabilities + * + * @author Steffen Vogel + * @copyright 2014-2020, 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 . + *********************************************************************************/ + +#pragma once + +#include + +namespace villas { +namespace node { + +json_t * getCapabilities(); + +} /* namespace node */ +} /* namepace vilals */ diff --git a/include/villas/nodes/kafka.hpp b/include/villas/nodes/kafka.hpp index 44ee8f5d9..894e36e22 100644 --- a/include/villas/nodes/kafka.hpp +++ b/include/villas/nodes/kafka.hpp @@ -64,7 +64,7 @@ struct kafka { } ssl; struct { - char *mechanism; /**< SASL mechanism. */ + char *mechanisms; /**< SASL mechanisms. */ char *username; /**< SSL CA path. */ char *password; /**< SSL certificate. */ } sasl; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index dd58441af..5f722e574 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -41,6 +41,7 @@ if(UNIX AND NOT APPLE) endif() set(LIB_SRC + capabilities.cpp config_helper.cpp config.cpp dumper.cpp diff --git a/lib/api/requests/capabiltities.cpp b/lib/api/requests/capabiltities.cpp index 0901032ef..b7cec57ae 100644 --- a/lib/api/requests/capabiltities.cpp +++ b/lib/api/requests/capabiltities.cpp @@ -20,11 +20,9 @@ * along with this program. If not, see . *********************************************************************************/ -#include -#include -#include #include #include +#include namespace villas { namespace node { @@ -37,52 +35,13 @@ public: virtual Response * execute() { - json_t *json_hooks = json_array(); - json_t *json_apis = json_array(); - json_t *json_nodes = json_array(); - json_t *json_formats = json_array(); - json_t *json_name; - if (method != Session::Method::GET) throw InvalidMethod(this); if (body != nullptr) throw BadRequest("Capabilities endpoint does not accept any body data"); - for (auto p : plugin::Registry::lookup()) { - json_name = json_string(p->getName().c_str()); - - json_array_append_new(json_apis, json_name); - } - - for (auto p : plugin::Registry::lookup()) { - json_name = json_string(p->getName().c_str()); - - json_array_append_new(json_hooks, json_name); - } - - for (auto p : plugin::Registry::lookup()) { - json_name = json_string(p->getName().c_str()); - - json_array_append_new(json_formats, json_name); - } - -#if 0 /* @todo Port to C++ */ - for (auto f : NodeFactory::lookup()) { - json_name = json_string(f->getName().c_str()); - - json_array_append_new(json_nodes, json_name); - } -#else - for (auto *vt : *node_types) - json_array_append_new(json_nodes, json_string(vt->name)); -#endif - - auto *json_capabilities = json_pack("{ s: o, s: o, s: o, s: o }", - "hooks", json_hooks, - "node-types", json_nodes, - "apis", json_apis, - "formats", json_formats); + auto *json_capabilities = getCapabilities(); return new JsonResponse(session, HTTP_STATUS_OK, json_capabilities); } diff --git a/lib/capabilities.cpp b/lib/capabilities.cpp new file mode 100644 index 000000000..fe2f92e4e --- /dev/null +++ b/lib/capabilities.cpp @@ -0,0 +1,75 @@ +/** Capabilities + * + * @author Steffen Vogel + * @copyright 2014-2020, 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 . + *********************************************************************************/ + +#include + +#include +#include +#include +#include + +using namespace villas; +using namespace villas::node; + +json_t * villas::node::getCapabilities() +{ + json_t *json_hooks = json_array(); + json_t *json_apis = json_array(); + json_t *json_nodes = json_array(); + json_t *json_formats = json_array(); + json_t *json_name; + + for (auto p : plugin::Registry::lookup()) { + json_name = json_string(p->getName().c_str()); + + json_array_append_new(json_apis, json_name); + } + + for (auto p : plugin::Registry::lookup()) { + json_name = json_string(p->getName().c_str()); + + json_array_append_new(json_hooks, json_name); + } + + for (auto p : plugin::Registry::lookup()) { + json_name = json_string(p->getName().c_str()); + + json_array_append_new(json_formats, json_name); + } + +#if 0 /* @todo Port to C++ */ + for (auto f : NodeFactory::lookup()) { + json_name = json_string(f->getName().c_str()); + + json_array_append_new(json_nodes, json_name); + } +#else + for (auto *vt : *node_types) + json_array_append_new(json_nodes, json_string(vt->name)); +#endif + + return json_pack("{ s: o, s: o, s: o, s: o }", + "hooks", json_hooks, + "nodes", json_nodes, + "apis", json_apis, + "formats", json_formats); +} diff --git a/lib/hooks/pmu_dft.cpp b/lib/hooks/pmu_dft.cpp index de1ab7094..162e9530b 100644 --- a/lib/hooks/pmu_dft.cpp +++ b/lib/hooks/pmu_dft.cpp @@ -263,7 +263,7 @@ public: "window_size_factor", &windowSizeFactor, "window_type", &windowTypeC, "padding_type", &paddingTypeC, - "freq_estimate_type", &freqEstimateTypeC, + "frequency_estimate_type", &freqEstimateTypeC, "sync", &sync, "pps_index", &ppsIndex, "angle_unit", &angleUnitC diff --git a/lib/nodes/kafka.cpp b/lib/nodes/kafka.cpp index f1e6b68be..1edadc42c 100644 --- a/lib/nodes/kafka.cpp +++ b/lib/nodes/kafka.cpp @@ -153,7 +153,7 @@ int kafka_init(struct vnode *n) k->producer.client = nullptr; k->producer.topic = nullptr; - k->sasl.mechanism = nullptr; + k->sasl.mechanisms = nullptr; k->sasl.username = nullptr; k->sasl.password = nullptr; @@ -227,19 +227,19 @@ int kafka_parse(struct vnode *n, json_t *json) } if (json_sasl) { - const char *mechanism; + const char *mechanisms; const char *username; const char *password; - ret = json_unpack_ex(json_ssl, &err, 0, "{ s: s, s: s, s: s }", - "mechanism", &mechanism, + ret = json_unpack_ex(json_sasl, &err, 0, "{ s: s, s: s, s: s }", + "mechanisms", &mechanisms, "username", &username, "password", &password ); if (ret) - throw ConfigError(json_sasl, err, "node-config-node-kafka-sasl", "Failed to parse SASL configuration of node {}", *n); + throw ConfigError(json_sasl, err, "node-config-node-kafka-sasl", "Failed to parse SASL configuration"); - k->sasl.mechanism = strdup(mechanism); + k->sasl.mechanisms = strdup(mechanisms); k->sasl.username = strdup(username); k->sasl.password = strdup(password); } @@ -362,7 +362,7 @@ int kafka_start(struct vnode *n) } if (!strcmp(k->protocol, "SASL_PLAINTEXT") || !strcmp(k->protocol, "SASL_SSL")) { - ret = rd_kafka_conf_set(rdkconf, "sasl.mechanisms", k->sasl.mechanism, errstr, sizeof(errstr)); + ret = rd_kafka_conf_set(rdkconf, "sasl.mechanisms", k->sasl.mechanisms, errstr, sizeof(errstr)); if (ret != RD_KAFKA_CONF_OK) goto kafka_config_error; diff --git a/lib/path.cpp b/lib/path.cpp index eef01e2fd..eb0c310a5 100644 --- a/lib/path.cpp +++ b/lib/path.cpp @@ -346,16 +346,6 @@ int path_prepare(struct vpath *p, NodeList &nodes) if (p->original_sequence_no == -1) p->original_sequence_no = vlist_length(&p->sources) == 1; - /* Autodetect whether to use poll() for this path or not */ - if (p->poll == -1) { - if (p->rate > 0) - p->poll = 1; - else if (vlist_length(&p->sources) > 1) - p->poll = 1; - else - p->poll = 0; - } - /* Prepare poll() */ if (p->poll) { ret = path_prepare_poll(p); @@ -487,6 +477,16 @@ int path_parse(struct vpath *p, json_t *json, NodeList &nodes, const uuid_t sn_u if (ret) return ret; + /* Autodetect whether to use poll() for this path or not */ + if (p->poll == -1) { + if (p->rate > 0) + p->poll = 1; + else if (vlist_length(&p->sources) > 1) + p->poll = 1; + else + p->poll = 0; + } + p->config = json; p->state = State::PARSED; diff --git a/src/villas-node.cpp b/src/villas-node.cpp index bee8f8b53..b7eaac9e9 100644 --- a/src/villas-node.cpp +++ b/src/villas-node.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #ifdef WITH_NODE_OPAL #include @@ -71,6 +72,7 @@ protected: SuperNode sn; std::string uri; + bool showCapabilities = false; void handler(int signal, siginfo_t *sinfo, void *ctx) { @@ -92,6 +94,7 @@ protected: << " OPTIONS is one or more of the following options:" << std::endl << " -h show this usage information" << std::endl << " -d LVL set logging level" << std::endl + << " -C show capabilities in JSON format" << std::endl << " -V show the version of the tool" << std::endl << std::endl << " CONFIG is the path to an optional configuration file" << std::endl << " if omitted, VILLASnode will start without a configuration" << std::endl @@ -145,7 +148,7 @@ protected: /* Parse optional command line arguments */ int c; - while ((c = getopt(argc, argv, "hVd:")) != -1) { + while ((c = getopt(argc, argv, "hCVd:")) != -1) { switch (c) { case 'V': printVersion(); @@ -155,6 +158,10 @@ protected: logging.setLevel(optarg); break; + case 'C': + showCapabilities = true; + break; + case 'h': case '?': usage(); @@ -175,6 +182,20 @@ protected: int main() { + return showCapabilities + ? capabilities() + : daemon(); + } + + int capabilities() { + auto *json_caps = getCapabilities(); + + json_dumpf(json_caps, stdout, JSON_INDENT(4)); + + return 0; + } + + int daemon() { if (!uri.empty()) sn.parse(uri); else diff --git a/tests/integration/missing-example-configs.sh b/tests/integration/missing-example-configs.sh new file mode 100755 index 000000000..14a87772e --- /dev/null +++ b/tests/integration/missing-example-configs.sh @@ -0,0 +1,51 @@ + +#!/bin/bash +# +# Test example configurations +# +# @author Steffen Vogel +# @copyright 2014-2020, 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 . +################################################################################## + +NODE_TYPES=$(villas-node -C | jq -r '.nodes | join(" ")') +HOOKS_TYPES=$(villas-node -C | jq -r '.["hooks"] | join(" ")') + +CONFIGS=$(find -name '*.conf' -o -name '*.json') + +MISSING=0 + +for NODE in ${NODE_TYPES}; do + [ ${NODE} == "loopback_internal" ] && continue + + if [ ! -f "${SRCDIR}/etc/examples/nodes/${NODE}.conf" ]; then + echo "Missing example config for node-type: ${NODE}" + ((MISSING++)) + fi +done + +for HOOK in ${HOOK_TYPES}; do + [ ${NODE} == "loopback_internal" ] && continue + + if [ ! -f "${SRCDIR}/etc/examples/hooks/${HOOK}.conf" ]; then + echo "Missing example config for hook-type: ${HOOK}" + ((MISSING++)) + fi +done + +(( ${MISSING} == 0 )) diff --git a/tests/integration/test-config.sh b/tests/integration/test-config.sh index ff30400cf..a0cedad3e 100755 --- a/tests/integration/test-config.sh +++ b/tests/integration/test-config.sh @@ -30,8 +30,10 @@ CONFIGS=$(find ${SRCDIR}/etc/ -name '*.conf' -o -name '*.json') for CONFIG in ${CONFIGS}; do if [ "$(basename ${CONFIG})" == "opal.conf" ] || [ "$(basename ${CONFIG})" == "paths.conf" ] || + [ "$(basename ${CONFIG})" == "tricks.json" ] || + [ "$(basename ${CONFIG})" == "tricks.conf" ] || [ "$(basename ${CONFIG})" == "vc707_ips.conf" ] || - [ "$(basename ${CONFIG})" == "uldaq.conf" ] || + [ "$(basename ${CONFIG})" == "infiniband.conf" ] || [ "$(basename ${CONFIG})" == "global.conf" ]; then echo "=== Skipping config: ${CONFIG}" continue