1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00

list: use new lookup methods

This commit is contained in:
Steffen Vogel 2020-08-25 20:22:19 +02:00
parent e81e89d5f3
commit 57f570d9b0
31 changed files with 355 additions and 201 deletions

2
common

@ -1 +1 @@
Subproject commit 88ebc1d67c16809fec8975e0cadf002f91c61389
Subproject commit 7f356d91d5fe81e91ec552583fb5b307ccaa723d

View file

@ -16,6 +16,8 @@ externalDocs:
servers:
- url: "https://villas-new.k8s.eonerc.rwth-aachen.de/api/v2"
description: "The production API server in our EONERC OpenStack Kubernetes"
- url: "http://ernie.acs-lab.eonerc.rwth-aachen.de:8080/api/v2"
description: "VILLASnode lab instance in ACS laboratory"
tags:
- name: super-node
description: Global super-node related operations.
@ -71,7 +73,47 @@ paths:
examples:
example1:
value:
{ test: 1 }
build: v0.10.0-4b9e8a6-debug
hooks:
- average
- cast
- decimate
- drop
- dump
- ebm
- fix
- gate
- jitter_calc
- limit_rate
- restart
- scale
- shift_seq
- shift_ts
- skip_first
- stats
- ts
- pps_ts
- print
node-types: []
apis:
- capabilities
- config
- shutdown
- restart
- nodes
- node/start
- node/stop
- node/pause
- node/resume
- node/restart
- stats
- stats/reset
- file
- paths
- path/start
- path/stop
formats: []
"/config":
get:
@ -86,7 +128,42 @@ paths:
examples:
example1:
value:
{ test: 1 }
nodes:
udp_node1:
type: socket
layer: udp
in:
address: '*:12000'
signals:
count: 8
type: float
out:
address: '127.0.0.1:12001'
web_node1:
type: websocket
vectorize: 2
series:
- label: Random walk
unit: V
- label: Sine
unit: A
- label: Rect
unit: Var
- label: Ramp
unit: °C
paths:
- in:
- udp_node1
out:
- web_node1
hooks:
- type: decimate
ratio: 2
- in:
- web_node1
out:
- udp_node1
"/restart":
post:
@ -106,13 +183,14 @@ paths:
description: An optional path to a new configuration file which should be loaded after restarting the node.
responses:
'200':
description: Success
description: Success. The instance has been restarted.
content:
application/json:
examples:
example1:
value:
{ test: 1 }
restarts: 5
config: http://example.com/path/to/config.json
"/shutdown":
post:
@ -121,13 +199,7 @@ paths:
- super-node
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The instance has been shut down.
"/nodes":
get:
@ -142,7 +214,95 @@ paths:
examples:
example1:
value:
{ test: 1 }
- name: udp_node1
uuid: b3df1d73-f483-f16c-5936-4ea48295615c
state: running
affinity: -1
in:
address: '*:12000'
signals:
count: 8
type: float
out:
address: '127.0.0.1:12001'
type: socket
layer: udp
- name: web_node1
uuid: 19c84350-c83a-8a3b-224b-43fa591c8998
state: running
affinity: -1
in:
vectorize: 2
signals:
- type: float
enabled: true
name: signal0
- type: float
enabled: true
name: signal1
- type: float
enabled: true
name: signal2
- type: float
enabled: true
name: signal3
- type: float
enabled: true
name: signal4
- type: float
enabled: true
name: signal5
- type: float
enabled: true
name: signal6
- type: float
enabled: true
name: signal7
- type: float
enabled: true
name: signal8
- type: float
enabled: true
name: signal9
- type: float
enabled: true
name: signal10
- type: float
enabled: true
name: signal11
- type: float
enabled: true
name: signal12
- type: float
enabled: true
name: signal13
- type: float
enabled: true
name: signal14
out:
vectorize: 2
signals:
- type: float
enabled: true
name: signal0
- type: float
enabled: true
name: signal1
- type: float
enabled: true
name: signal2
type: websocket
vectorize: 2
series:
- label: Random walk
unit: V
- label: Sine
unit: A
- label: Rect
unit: Var
- label: Ramp
unit: °C
"/node/{uuid}/stats":
get:
@ -160,6 +320,8 @@ paths:
example1:
value:
{ test: 1 }
'404':
description: Error. There is no node with the given UUID or the node does not collect statistics.
"/node/{uuid}/stats/reset":
post:
@ -170,13 +332,9 @@ paths:
- $ref: "#/components/parameters/node-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The statistics of the node have been reset.
'404':
description: Error. There is no node with the given UUID.
"/node/{uuid}/start":
post:
@ -187,14 +345,10 @@ paths:
- $ref: "#/components/parameters/node-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The node has been started.
'404':
description: Error. There is no node with the given UUID.
"/node/{uuid}/stop":
post:
summary: Stop a node.
@ -204,14 +358,10 @@ paths:
- $ref: "#/components/parameters/node-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The node has been stopped.
'404':
description: Error. There is no node with the given UUID.
"/node/{uuid}/pause":
post:
summary: Pause a node.
@ -221,13 +371,9 @@ paths:
- $ref: "#/components/parameters/node-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The node has been paused.
'404':
description: Error. There is no node with the given UUID.
"/node/{uuid}/resume":
post:
@ -238,14 +384,10 @@ paths:
- $ref: "#/components/parameters/node-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The node has been resumed.
'404':
description: Error. There is no node with the given UUID.
"/node/{uuid}/restart":
post:
summary: Retart a node.
@ -255,14 +397,10 @@ paths:
- $ref: "#/components/parameters/node-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The node has been restarted.
'404':
description: Error. There is no node with the given UUID.
"/node/{uuid}/file/rewind":
post:
summary: Rewind the playback file to the beginning.
@ -272,13 +410,9 @@ paths:
- $ref: "#/components/parameters/node-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The file has been rewound.
'404':
description: Error. There is no node with the given UUID.
"/node/{uuid}/file/seek":
post:
@ -302,17 +436,13 @@ paths:
example: 123
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The read-pointer of the file has been changed.
'404':
description: Error. There is no node with the given UUID.
"/paths":
get:
summary: Get a list of all configure paths.
summary: Get a list of all paths.
tags:
- paths
responses:
@ -323,7 +453,38 @@ paths:
examples:
example1:
value:
{ test: 1 }
- uuid: 251c99af-4b05-9de4-367e-2bb550412e56
state: running
mode: any
enabled: true
builtin: true
reverse: false
original_sequence_no: true
last_sequence: false
poll: false
queuelen: 1024
signals: []
hooks: []
in:
- udp_node1
out:
- web_node1
- uuid: 61b5674b-95fa-b35f-bff8-c877acf21e3b
state: running
mode: any
enabled: true
builtin: true
reverse: false
original_sequence_no: true
last_sequence: false
poll: false
queuelen: 1024
signals: []
hooks: []
in:
- web_node1
out:
- udp_node1
"/path/{uuid}/start":
post:
@ -334,14 +495,10 @@ paths:
- $ref: "#/components/parameters/path-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The path has been started.
'404':
description: Error. There is no path with the given UUID.
"/path/{uuid}/stop":
post:
summary: Start a path.
@ -351,45 +508,6 @@ paths:
- $ref: "#/components/parameters/path-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
"/path/{uuid}/stats":
get:
summary: Get the statistics of a path.
tags:
- paths
parameters:
- $ref: "#/components/parameters/path-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
"/path/{uuid}/stats/reset":
post:
summary: Reset the statistics counters for a specific path.
tags:
- paths
parameters:
- $ref: "#/components/parameters/path-uuid"
responses:
'200':
description: Success
content:
application/json:
examples:
example1:
value:
{ test: 1 }
description: Success. The path has been stopped.
'404':
description: Error. There is no path with the given UUID.

View file

@ -0,0 +1,32 @@
/** API Request.
*
* @file
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @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 <http://www.gnu.org/licenses/>.
*********************************************************************************/
#pragma once
#include <villas/api/request.hpp>
class NodeRequest : public Request {
public:
};

View file

@ -58,7 +58,7 @@ public:
RequestFactory *factory;
enum Method {
UNKNOWN_METHOD,
UNKNOWN,
GET,
POST,
DELETE,

View file

@ -45,7 +45,6 @@
#include <villas/log.hpp>
/* Forward declarations */
struct stats;
struct node;
/** The register mode determines under which condition the path is triggered. */

View file

@ -132,7 +132,7 @@ public:
struct node * getNode(const std::string &name)
{
return (struct node *) vlist_lookup(&nodes, name.c_str());
return vlist_lookup_name<struct node>(&nodes, name);
}
struct vlist * getNodes()

View file

@ -26,16 +26,18 @@ set(API_SRC
request.cpp
response.cpp
requests/capabiltities.cpp
requests/shutdown.cpp
requests/status.cpp
requests/capabiltities.cpp
requests/config.cpp
requests/nodes.cpp
requests/paths.cpp
requests/shutdown.cpp
requests/restart.cpp
requests/nodes.cpp
requests/node_action.cpp
requests/file.cpp
requests/stats_reset.cpp
requests/node_stats.cpp
requests/node_stats_reset.cpp
requests/node_file.cpp
requests/paths.cpp
requests/path_action.cpp
)
add_library(api STATIC ${API_SRC})

View file

@ -65,29 +65,29 @@ public:
};
/* Register API requests */
char n1[] = "node/start";
char r1[] = "/node/([^/]+)/start";
char d1[] = "start a node";
static char n1[] = "node/start";
static char r1[] = "/node/([^/]+)/start";
static char d1[] = "start a node";
static RequestPlugin<NodeActionRequest<node_start>, n1, r1, d1> p1;
char n2[] = "node/stop";
char r2[] = "/node/([^/]+)/stop";
char d2[] = "stop a node";
static char n2[] = "node/stop";
static char r2[] = "/node/([^/]+)/stop";
static char d2[] = "stop a node";
static RequestPlugin<NodeActionRequest<node_stop>, n2, r2, d2> p2;
char n3[] = "node/pause";
char r3[] = "/node/([^/]+)/pause";
char d3[] = "pause a node";
static char n3[] = "node/pause";
static char r3[] = "/node/([^/]+)/pause";
static char d3[] = "pause a node";
static RequestPlugin<NodeActionRequest<node_pause>, n3, r3, d3> p3;
char n4[] = "node/resume";
char r4[] = "/node/([^/]+)/resume";
char d4[] = "resume a node";
static char n4[] = "node/resume";
static char r4[] = "/node/([^/]+)/resume";
static char d4[] = "resume a node";
static RequestPlugin<NodeActionRequest<node_resume>, n4, r4, d4> p4;
char n5[] = "node/restart";
char r5[] = "/node/([^/]+)/restart";
char d5[] = "restart a node";
static char n5[] = "node/restart";
static char r5[] = "/node/([^/]+)/restart";
static char d5[] = "restart a node";
static RequestPlugin<NodeActionRequest<node_restart>, n5, r5, d5> p5;

View file

@ -43,29 +43,24 @@ public:
virtual Response * execute()
{
int ret;
json_error_t err;
if (method != Method::GET)
throw InvalidMethod(this);
if (body != nullptr)
throw BadRequest("Stats endpoint does not accept any body data");
json_t *json_stats = json_array();
const auto &nodeName = matches[1].str();
struct vlist *nodes = session->getSuperNode()->getNodes();
for (size_t i = 0; i < vlist_length(nodes); i++) {
struct node *n = (struct node *) vlist_at(nodes, i);
struct node *node = (struct node *) vlist_lookup(nodes, nodeName.c_str());
if (node && strcmp(node, node_name(n)))
continue;
if (!node)
throw BadRequest("Unknown node");
if (n->stats)
json_array_append(json_stats, n->stats->toJson());
}
if (!node->stats)
throw BadRequest("The statistics collection for this node is not enabled");
return new Response(session, json_stats);
return new Response(session, node->stats->toJson());
}
};

View file

@ -46,6 +46,13 @@ public:
int ret;
json_error_t err;
if (method != Method::POST)
throw InvalidMethod(this);
if (body != nullptr)
throw BadRequest("Stats endpoint does not accept any body data");
char *node = nullptr;
ret = json_unpack_ex(body, &err, 0, "{ s?: s }",
@ -74,7 +81,7 @@ public:
/* Register API requests */
static char n[] = "stats/reset";
static char r[] = "/stats/reset";
static char r[] = "/node/([^/]+)/stats/reset";
static char d[] = "reset internal statistics counters";
static RequestPlugin<StatsRequest, n, r, d> p;

View file

@ -23,7 +23,7 @@
#include <jansson.h>
#include <villas/plugin.h>
#include <villas/node.h>
#include <villas/path.h>
#include <villas/super_node.hpp>
#include <villas/utils.hpp>
#include <villas/api.hpp>
@ -35,7 +35,7 @@ namespace villas {
namespace node {
namespace api {
template<int (*A)(struct node *)>
template<int (*A)(struct vpath *)>
class PathActionRequest : public Request {
public:
@ -53,16 +53,16 @@ public:
const auto &pathIndexStr = matches[1].str();
try {
pathIndex = std::atoul(pathIndexStr);
pathIndex = std::stoul(pathIndexStr);
} catch (const std::invalid_argument &e) {
throw BadRequest("Invalid argument");
}
struct vlist *paths = session->getSuperNode()->getPaths();
struct vpath *p = (struct path *) vlist_at_safe(pathIndex);
struct vpath *p = (struct vpath *) vlist_at_safe(paths, pathIndex);
if (!p)
throw Error(HTTP_STATUS_NOT_FOUND, "Node not found");
throw Error(HTTP_STATUS_NOT_FOUND, "Path not found");
A(p);
@ -72,14 +72,14 @@ public:
};
/* Register API requests */
char n1[] = "path/start";
char r1[] = "/path/([^/]+)/start";
char d1[] = "start a path";
static char n1[] = "path/start";
static char r1[] = "/path/([^/]+)/start";
static char d1[] = "start a path";
static RequestPlugin<PathActionRequest<path_start>, n1, r1, d1> p1;
char n2[] = "path/stop";
char r2[] = "/path/([^/]+)/stop";
char d2[] = "stop a path";
static char n2[] = "path/stop";
static char r2[] = "/path/([^/]+)/stop";
static char d2[] = "stop a path";
static RequestPlugin<PathActionRequest<path_stop>, n2, r2, d2> p2;

View file

@ -79,7 +79,7 @@ public:
? cfg
: session->getSuperNode()->getConfigUri();
logger->info("Restarting to %s", configUri.c_str());
logger->info("Restarting to {}", configUri.c_str());
/* Increment API restart counter */
char *scnt = getenv("VILLAS_API_RESTART_COUNT");

View file

@ -37,7 +37,7 @@ public:
virtual Response * execute()
{
if (method != Method::GET)
if (method != Method::POST)
throw InvalidMethod(this);
if (body != nullptr)

View file

@ -131,7 +131,7 @@ void Session::open(void *in, size_t len)
try {
meth = getRequestMethod(wsi);
if (meth == Request::Method::UNKNOWN_METHOD)
if (meth == Request::Method::UNKNOWN)
throw RuntimeError("Invalid request method");
request = RequestFactory::make(this, uri, meth);
@ -332,7 +332,7 @@ int Session::getRequestMethod(struct lws *wsi)
return Request::Method::OPTIONS;
#endif
else
return Request::Method::UNKNOWN_METHOD;
return Request::Method::UNKNOWN;
}
std::string Session::methodToString(int method)

View file

@ -185,7 +185,7 @@ static int json_reserve_unpack_sample(struct io *io, json_t *json_smp, struct sa
struct signal *sig;
sig = (struct signal *) vlist_lookup(io->signals, name);
sig = vlist_lookup_name<struct signal>(io->signals, name);
if (sig) {
if (!sig->enabled)
continue;

View file

@ -73,7 +73,7 @@ public:
for (size_t i = 0; i < vlist_length(&signal_names); i++) {
char *signal_name = (char *) vlist_at_safe(&signal_names, i);
int index = vlist_lookup_index(&signals, signal_name);
int index = vlist_lookup_index<struct signal>(&signals, signal_name);
if (index < 0)
throw RuntimeError("Failed to find signal {}", signal_name);

View file

@ -72,7 +72,7 @@ public:
assert(state == State::CHECKED);
if (signal_name) {
signal_index = vlist_lookup_index(&signals, signal_name);
signal_index = vlist_lookup_index<struct signal>(&signals, signal_name);
if (signal_index < 0)
throw RuntimeError("Failed to find signal: {}", signal_name);
}

View file

@ -221,7 +221,7 @@ public:
assert(state != State::STARTED);
if (signal_name) {
signal_index = vlist_lookup_index(&signals, signal_name);
signal_index = vlist_lookup_index<struct signal>(&signals, signal_name);
if (signal_index < 0)
throw RuntimeError("Failed to find signal: {}", signal_name);
}

View file

@ -122,7 +122,7 @@ public:
assert(state == State::CHECKED);
if (!signalName.empty()) {
signalIndex = vlist_lookup_index(&signals, signalName.c_str());
signalIndex = vlist_lookup_index<struct signal>(&signals, signalName);
if (signalIndex < 0)
throw RuntimeError("Failed to find signal: {}", signalName);
}

View file

@ -75,7 +75,7 @@ public:
for (size_t i = 0; i < vlist_length(&signal_names); i++) {
char *signal_name = (char *) vlist_at_safe(&signal_names, i);
int index = vlist_lookup_index(&signals, signal_name);
int index = vlist_lookup_index<struct signal>(&signals, signal_name);
if (index < 0)
throw RuntimeError("Failed to find signal {}", signal_name);

View file

@ -53,7 +53,7 @@ public:
assert(state != State::STARTED);
if (signal_name) {
signal_index = vlist_lookup_index(&signals, signal_name);
signal_index = vlist_lookup_index<struct signal>(&signals, signal_name);
if (signal_index < 0)
throw RuntimeError("Failed to find signal: {}", signal_name);
}

View file

@ -48,7 +48,7 @@ int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *n
goto invalid_format;
}
me->node = (struct node *) vlist_lookup(nodes, node);
me->node = vlist_lookup_name<struct node>(nodes, node);
if (!me->node) {
warning("Unknown node %s", node);
goto invalid_format;
@ -128,7 +128,7 @@ int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *n
first_str = strtok_r(nullptr, "-]", &lasts);
if (first_str) {
if (me->node)
first = vlist_lookup_index(&me->node->in.signals, first_str);
first = vlist_lookup_index<struct signal>(&me->node->in.signals, first_str);
if (first < 0) {
char *endptr;
@ -149,7 +149,7 @@ int mapping_parse_str(struct mapping_entry *me, const char *str, struct vlist *n
last_str = strtok_r(nullptr, "]", &lasts);
if (last_str) {
if (me->node)
last = vlist_lookup_index(&me->node->in.signals, last_str);
last = vlist_lookup_index<struct signal>(&me->node->in.signals, last_str);
if (last < 0) {
char *endptr;

View file

@ -600,7 +600,7 @@ int node_list_parse(struct vlist *list, json_t *cfg, struct vlist *all)
switch (json_typeof(cfg)) {
case JSON_STRING:
str = json_string_value(cfg);
node = (struct node *) vlist_lookup(all, str);
node = vlist_lookup_name<struct node>(all, str);
if (!node)
goto invalid2;
@ -612,7 +612,7 @@ int node_list_parse(struct vlist *list, json_t *cfg, struct vlist *all)
if (!json_is_string(elm))
goto invalid;
node = (struct node *) vlist_lookup(all, json_string_value(elm));
node = vlist_lookup_name<struct node>(all, json_string_value(elm));
if (!node)
goto invalid;

View file

@ -328,6 +328,7 @@ int file_start(struct node *n)
struct sample *smp = sample_alloc_mem(vlist_length(&n->in.signals));
for (unsigned i = 0; i < f->skip_lines; i++)
io_scan(&f->io, &smp, 1);
sample_free(smp);
return 0;

View file

@ -315,7 +315,7 @@ static int ngsi_parse_entity(struct node *n, json_t *json_entity, struct sample
return -3;
/* Check attribute name and type */
attr = (NgsiAttribute *) vlist_lookup(&i->in.signals, name);
attr = vlist_lookup_name<NgsiAttribute>(&i->in.signals, name);
if (!attr || attr->type != type)
continue; /* skip unknown attributes */

View file

@ -100,7 +100,7 @@ int stats_node_start(struct node *n)
for (size_t i = 0; i < vlist_length(&s->signals); i++) {
struct stats_node_signal *stats_sig = (struct stats_node_signal *) vlist_at(&s->signals, i);
stats_sig->node = (struct node *) vlist_lookup(nodes, stats_sig->node_str);
stats_sig->node = vlist_lookup_name<struct node>(nodes, stats_sig->node_str);
if (!stats_sig->node)
error("Invalid reference node %s for setting 'node' of node %s", stats_sig->node_str, node_name(n));
}

View file

@ -218,7 +218,7 @@ int websocket_protocol_cb(struct lws *wsi, enum lws_callback_reasons reason, voi
format = (char *) "villas.web";
/* Search for node whose name matches the URI. */
c->node = (struct node *) vlist_lookup(&p.node.instances, node);
c->node = vlist_lookup_name<struct node>(&p.node.instances, node);
if (!c->node) {
websocket_connection_close(c, wsi, LWS_CLOSE_STATUS_POLICY_VIOLATION, "Unknown node");
warning("Failed to find node: node=%s", node);

View file

@ -464,7 +464,7 @@ int path_parse(struct vpath *p, json_t *cfg, struct vlist *nodes)
return -1;
}
node = (struct node *) vlist_lookup(nodes, name);
node = vlist_lookup_name<struct node>(nodes, name);
if (!node) {
p->logger->error("The 'mask' entry '{}' is not a valid node name", name);
return -1;

View file

@ -102,7 +102,7 @@ if ! pkg-config "libwebsockets >= 2.3.0"; then
mkdir -p libwebsockets/build
pushd libwebsockets/build
git checkout v4.0-stable
cmake -DLWS_WITHOUT_TESTAPPS=ON -DLWS_WITHOUT_EXTENSIONS=OFF ${CMAKE_OPTS} ..
cmake -DLWS_WITHOUT_TESTAPPS=ON -DLWS_WITHOUT_EXTENSIONS=OFF -DLWS_WITH_SERVER_STATUS=ON ${CMAKE_OPTS} ..
make -j$(nproc) ${TARGET}
popd
fi

View file

@ -67,41 +67,41 @@ Test(mapping, parse_nodes)
ret = mapping_parse_str(&m, "apple.ts.origin", &nodes);
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup(&nodes, "apple"));
cr_assert_eq(m.node, vlist_lookup_name<struct node>(&nodes, "apple"));
cr_assert_eq(m.type, MappingType::TIMESTAMP);
cr_assert_eq(m.timestamp.type, MappingTimestampType::ORIGIN);
ret = mapping_parse_str(&m, "cherry.stats.owd.mean", &nodes);
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup(&nodes, "cherry"));
cr_assert_eq(m.node, vlist_lookup_name<struct node>(&nodes, "cherry"));
cr_assert_eq(m.type, MappingType::STATS);
cr_assert_eq(m.stats.metric, Stats::Metric::OWD);
cr_assert_eq(m.stats.type, Stats::Type::MEAN);
ret = mapping_parse_str(&m, "carrot.data[1-2]", &nodes);
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup(&nodes, "carrot"));
cr_assert_eq(m.node, vlist_lookup_name<struct node>(&nodes, "carrot"));
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 1);
cr_assert_eq(m.length, 2);
ret = mapping_parse_str(&m, "carrot", &nodes);
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup(&nodes, "carrot"));
cr_assert_eq(m.node, vlist_lookup_name<struct node>(&nodes, "carrot"));
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 0);
cr_assert_eq(m.length, -1);
ret = mapping_parse_str(&m, "carrot.data[sole]", &nodes);
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup(&nodes, "carrot"));
cr_assert_eq(m.node, vlist_lookup_name<struct node>(&nodes, "carrot"));
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 1);
cr_assert_eq(m.length, 1);
ret = mapping_parse_str(&m, "carrot.data[sole-mio]", &nodes);
cr_assert_eq(ret, 0);
cr_assert_eq(m.node, vlist_lookup(&nodes, "carrot"));
cr_assert_eq(m.node, vlist_lookup_name<struct node>(&nodes, "carrot"));
cr_assert_eq(m.type, MappingType::DATA);
cr_assert_eq(m.data.offset, 1);
cr_assert_eq(m.length, 2);