yanglint FEATURE schema mount support (#1901)
* modify existing context if provided
In ly_ctx_new_yldata(), check if *ctx is NULL. If so, proceed as before
with allocating a new context. Otherwise, load modules into the existing
context.
Signed-off-by: Eric Kinzie <ekinzie@labn.net>
* add parent-reference xpath helper to schema-mount plugin
This new function produces a list of schema nodes from the expanded
parent-reference xpath expressions.
Signed-off-by: Eric Kinzie <ekinzie@labn.net>
* add context lookup to schema-mount plugin
This function allocates a new context for a particular instance of the
yangmnt:mount-point extension.
Signed-off-by: Eric Kinzie <ekinzie@labn.net>
* yanglint: schema-mount extension callback
Add a commandline argument "-x" that accepts a file containing extension
data, used to create a context for the schema-mount extension. This file
has the same format as what is provided to the "-Y" option. A callback
function for the schema-mount extenion is registered if the new option
is specified.
This allows validating instance data for models that include a schema
mount point.
Signed-off-by: Eric Kinzie <ekinzie@labn.net>
* yanglint: print mounted schema trees
Display "mp" flag for mount point and print mounted tree. Also display
the / and @ opts in node names.
Signed-off-by: Eric Kinzie <ekinzie@labn.net>
* add unit test for tree mount-point flag
Test is courtesy of aPiecek <piecek@cesnet.cz>.
Signed-off-by: Eric Kinzie <ekinzie@labn.net>
* add example data files for validation with schema-mount
From tools/lint/examples directory:
% ../../../build/yanglint \
-f json -t config -p ../../../models -p . \
-Y ./sm-context-main.xml -x ./sm-context-extension.xml sm-data.xml
% ../../../build/yanglint \
-f tree -p ../../../models -p . \
-Y ./sm-context-main.xml -x ./sm-context-extension.xml sm-main.yang
Signed-off-by: Eric Kinzie <ekinzie@labn.net>
Signed-off-by: Eric Kinzie <ekinzie@labn.net>
Co-authored-by: Eric Kinzie <ekinzie@labn.net>
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 845d5f1..175daee 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -67,6 +67,12 @@
const struct lysc_node *schema_node;
const char *submodule;
+ /* name of file containing explicit context passed to callback
+ * for schema-mount extension. This also causes a callback to
+ * be registered.
+ */
+ char *schema_context_filename;
+
/* value of --format in case of schema format */
LYS_OUTFORMAT schema_out_format;
ly_bool feature_param_format;
@@ -112,6 +118,10 @@
ly_out_free(c->out, NULL, 0);
ly_ctx_destroy(c->ctx);
+
+ if (c->schema_context_filename) {
+ free(c->schema_context_filename);
+ }
}
static void
@@ -191,6 +201,11 @@
printf(" -s SUBMODULE, --submodule=SUBMODULE\n"
" Print the specific submodule instead of the main module.\n\n");
+ printf(" -x FILE, --ext-data=FILE\n"
+ " File containing the specific data required by an extension. Required by\n"
+ " the schema-mount extension, for example, when the mounted data are\n"
+ " expected in the file. File format is guessed.\n\n");
+
printf(" -n, --not-strict\n"
" Do not require strict data parsing (silently skip unknown data),\n"
" has no effect for schemas.\n\n");
@@ -317,6 +332,22 @@
return NULL;
}
+static LY_ERR
+ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free)
+{
+ struct ly_ctx *ctx;
+ struct lyd_node *data = NULL;
+
+ ctx = ext->module->ctx;
+ if (user_data) {
+ lyd_parse_data_path(ctx, user_data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, &data);
+ }
+
+ *ext_data = data;
+ *ext_data_free = 1;
+ return LY_SUCCESS;
+}
+
static int
fill_context_inputs(int argc, char *argv[], struct context *c)
{
@@ -334,8 +365,13 @@
ly_set_erase(&c->schema_features, free_features);
/* create context from the yang-library file */
+ if (ly_ctx_new(searchdir, c->ctx_options, &c->ctx)) {
+ YLMSG_E("Unable to create libyang context\n");
+ return -1;
+ }
+ ly_ctx_set_ext_data_clb(c->ctx, ext_data_clb, c->schema_context_filename);
if (ly_ctx_new_ylpath(searchdir, c->yang_lib_file, LYD_UNKNOWN, c->ctx_options, &c->ctx)) {
- YLMSG_E("Unable to create libyang context from yang-library data.\n");
+ YLMSG_E("Unable to modify libyang context with yang-library data.\n");
return -1;
}
} else {
@@ -350,6 +386,13 @@
return -1;
}
+ if (c->schema_context_filename) {
+ if (ly_ctx_set_ext_data_clb(c->ctx, ext_data_clb, c->schema_context_filename)) {
+ YLMSG_E("Unable to set extension callback data.\n");
+ return -1;
+ }
+ }
+
/* set the rest of searchdirs */
for (uint32_t i = 1; i < c->searchpaths.count; ++i) {
ly_ctx_set_searchdir(c->ctx, c->searchpaths.objs[i]);
@@ -495,6 +538,7 @@
{"schema-node", required_argument, NULL, 'P'},
{"single-node", no_argument, NULL, 'q'},
{"submodule", required_argument, NULL, 's'},
+ {"ext-data", required_argument, NULL, 'x'},
{"not-strict", no_argument, NULL, 'n'},
{"present", no_argument, NULL, 'e'},
{"type", required_argument, NULL, 't'},
@@ -520,9 +564,9 @@
opterr = 0;
#ifndef NDEBUG
- while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:G:", options, &opt_index)) != -1)
+ while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:x:G:", options, &opt_index)) != -1)
#else
- while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:", options, &opt_index)) != -1)
+ while ((opt = getopt_long(argc, argv, "hvVQf:p:DF:iP:qs:net:d:lL:o:O:R:myY:x:", options, &opt_index)) != -1)
#endif
{
switch (opt) {
@@ -645,6 +689,10 @@
c->submodule = optarg;
break;
+ case 'x': /* --ext-data */
+ c->schema_context_filename = strdup(optarg);
+ break;
+
case 'n': /* --not-strict */
c->data_parse_options &= ~LYD_PARSE_STRICT;
break;