context FETAURE yang-library data generation
Also added list of deriving modules for each module.
diff --git a/src/context.c b/src/context.c
index a908e7f..1b0a7c0 100644
--- a/src/context.c
+++ b/src/context.c
@@ -11,6 +11,7 @@
*
* https://opensource.org/licenses/BSD-3-Clause
*/
+#define _GNU_SOUCRE /* asprintf */
#include "common.h"
@@ -533,6 +534,253 @@
}
}
+static LY_ERR
+ylib_feature(struct lyd_node *parent, const struct lys_module *cur_mod)
+{
+ LY_ARRAY_SIZE_TYPE i;
+ struct lyd_node *node;
+
+ if (!cur_mod->implemented) {
+ /* no features can be enabled */
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_FOR(cur_mod->compiled->features, i) {
+ if (!(cur_mod->compiled->features[i].flags & LYS_FENABLED)) {
+ continue;
+ }
+
+ node = lyd_new_term(parent, NULL, "feature", cur_mod->compiled->features[i].name);
+ LY_CHECK_RET(!node, LY_EOTHER);
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+ylib_deviation(struct lyd_node *parent, const struct lys_module *cur_mod, int bis)
+{
+ LY_ARRAY_SIZE_TYPE i;
+ struct lyd_node *node;
+ struct lys_module *mod;
+
+ if (!cur_mod->implemented) {
+ /* no deviations of the module for certain */
+ return LY_SUCCESS;
+ }
+
+ LY_ARRAY_FOR(cur_mod->compiled->deviated_by, i) {
+ mod = cur_mod->compiled->deviated_by[i];
+
+ if (bis) {
+ node = lyd_new_term(parent, NULL, "deviation", mod->name);
+ LY_CHECK_RET(!node, LY_EOTHER);
+ } else {
+ node = lyd_new_list(parent, NULL, "deviation", mod->name, (mod->parsed->revs ? mod->parsed->revs[0].date : ""));
+ LY_CHECK_RET(!node, LY_EOTHER);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+static LY_ERR
+ylib_submodules(struct lyd_node *parent, const struct lys_module *cur_mod, int bis)
+{
+ LY_ARRAY_SIZE_TYPE i;
+ struct lyd_node *node, *cont;
+ struct lysp_submodule *submod;
+ int ret;
+ char *str;
+
+ LY_ARRAY_FOR(cur_mod->parsed->includes, i) {
+ submod = cur_mod->parsed->includes[i].submodule;
+
+ if (bis) {
+ cont = lyd_new_list(parent, NULL, "submodule", submod->name);
+ LY_CHECK_RET(!cont, LY_EOTHER);
+
+ if (submod->revs) {
+ node = lyd_new_term(cont, NULL, "revision", submod->revs[0].date);
+ LY_CHECK_RET(!node, LY_EOTHER);
+ }
+ } else {
+ cont = lyd_new_list(parent, NULL, "submodule", submod->name, (submod->revs ? submod->revs[0].date : ""));
+ LY_CHECK_RET(!cont, LY_EOTHER);
+ }
+
+ if (submod->filepath) {
+ ret = asprintf(&str, "file://%s", submod->filepath);
+ LY_CHECK_ERR_RET(ret == -1, LOGMEM(cur_mod->ctx), LY_EMEM);
+
+ node = lyd_new_term(cont, NULL, bis ? "location" : "schema", str);
+ LY_CHECK_RET(!node, LY_EOTHER);
+ free(str);
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+API uint16_t
+ly_ctx_get_yanglib_id(const struct ly_ctx *ctx)
+{
+ return ctx->module_set_id;
+}
+
+API struct lyd_node *
+ly_ctx_get_yanglib_data(const struct ly_ctx *ctx)
+{
+ uint32_t i;
+ int bis = 0, ret;
+ char id[8], *str;
+ const struct lys_module *mod;
+ struct lyd_node *root = NULL, *root_bis = NULL, *cont, *set_bis, *node;
+
+ LY_CHECK_ARG_RET(ctx, ctx, NULL);
+
+ mod = ly_ctx_get_module_implemented(ctx, "ietf-yang-library");
+ LY_CHECK_ERR_RET(!mod, LOGERR(ctx, LY_EINVAL, "Module \"ietf-yang-library\" is not implemented."), NULL);
+
+ if (mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, "2016-06-21")) {
+ bis = 0;
+ } else if (mod->parsed->revs && !strcmp(mod->parsed->revs[0].date, IETF_YANG_LIB_REV)) {
+ bis = 1;
+ } else {
+ LOGERR(ctx, LY_EINVAL, "Incompatible ietf-yang-library version in context.");
+ return NULL;
+ }
+
+ root = lyd_new_inner(NULL, mod, "modules-state");
+ LY_CHECK_GOTO(!root, error);
+
+ if (bis) {
+ root_bis = lyd_new_inner(NULL, mod, "yang-library");
+ LY_CHECK_GOTO(!root_bis, error);
+
+ set_bis = lyd_new_list(root_bis, NULL, "module-set", "complete");
+ LY_CHECK_GOTO(!set_bis, error);
+ }
+
+ for (i = 0; i < ctx->list.count; ++i) {
+ mod = ctx->list.objs[i];
+
+ /*
+ * deprecated legacy
+ */
+ cont = lyd_new_list(root, NULL, "module", mod->name, (mod->parsed->revs ? mod->parsed->revs[0].date : ""));
+ LY_CHECK_GOTO(!cont, error);
+
+ /* schema */
+ if (mod->filepath) {
+ ret = asprintf(&str, "file://%s", mod->filepath);
+ LY_CHECK_ERR_GOTO(ret == -1, LOGMEM(ctx), error);
+
+ node = lyd_new_term(cont, NULL, "schema", str);
+ free(str);
+ LY_CHECK_GOTO(!node, error);
+ }
+
+ /* namespace */
+ node = lyd_new_term(cont, NULL, "namespace", mod->ns);
+ LY_CHECK_GOTO(!node, error);
+
+ /* feature leaf-list */
+ LY_CHECK_GOTO(ylib_feature(cont, mod), error);
+
+ /* deviation list */
+ LY_CHECK_GOTO(ylib_deviation(cont, mod, 0), error);
+
+ /* conformance-type */
+ node = lyd_new_term(cont, NULL, "conformance-type", (mod->implemented ? "implement" : "import"));
+ LY_CHECK_GOTO(!node, error);
+
+ /* submodule list */
+ LY_CHECK_GOTO(ylib_submodules(cont, mod, 0), error);
+
+ /*
+ * current revision
+ */
+ if (bis) {
+ /* name and revision */
+ if (mod->implemented) {
+ cont = lyd_new_list(set_bis, NULL, "module", mod->name);
+ LY_CHECK_GOTO(!cont, error);
+
+ if (mod->parsed->revs) {
+ node = lyd_new_term(cont, NULL, "revision", mod->parsed->revs[0].date);
+ LY_CHECK_GOTO(!node, error);
+ }
+ } else {
+ cont = lyd_new_list(set_bis, NULL, "import-only-module", mod->name,
+ (mod->parsed->revs ? mod->parsed->revs[0].date : ""));
+ LY_CHECK_GOTO(!cont, error);
+ }
+
+ /* namespace */
+ node = lyd_new_term(cont, NULL, "namespace", mod->ns);
+ LY_CHECK_GOTO(!node, error);
+
+ /* location */
+ if (mod->filepath) {
+ ret = asprintf(&str, "file://%s", mod->filepath);
+ LY_CHECK_ERR_GOTO(ret == -1, LOGMEM(ctx), error);
+
+ node = lyd_new_term(cont, NULL, "schema", str);
+ free(str);
+ LY_CHECK_GOTO(!node, error);
+ }
+
+ /* submodule list */
+ LY_CHECK_GOTO(ylib_submodules(cont, mod, 1), error);
+
+ /* feature list */
+ LY_CHECK_GOTO(ylib_feature(cont, mod), error);
+
+ /* deviation */
+ LY_CHECK_GOTO(ylib_deviation(cont, mod, 1), error);
+ }
+ }
+
+ /* IDs */
+ sprintf(id, "%u", ctx->module_set_id);
+ node = lyd_new_term(root, NULL, "module-set-id", id);
+ LY_CHECK_GOTO(!node, error);
+
+ if (bis) {
+ /* create one complete schema */
+ cont = lyd_new_list(root_bis, NULL, "schema", "complete");
+ LY_CHECK_GOTO(!cont, error);
+
+ node = lyd_new_term(cont, NULL, "module-set", "complete");
+ LY_CHECK_GOTO(!node, error);
+
+ /* content-id */
+ node = lyd_new_term(root_bis, NULL, "content-id", id);
+ LY_CHECK_GOTO(!node, error);
+ }
+
+ if (root_bis) {
+ if (lyd_insert_sibling(root_bis, root)) {
+ goto error;
+ }
+ root = root_bis;
+ root_bis = 0;
+ }
+
+ /* TODO uncomment once lefref validation works
+ if (lyd_validate(&root, NULL, LYD_VALOPT_DATA_ONLY)) {
+ goto error;
+ }*/
+
+ return root;
+
+error:
+ lyd_free_all(root);
+ lyd_free_all(root_bis);
+ return NULL;
+}
+
API void
ly_ctx_destroy(struct ly_ctx *ctx, void (*private_destructor)(const struct lysc_node *node, void *priv))
{
diff --git a/src/context.h b/src/context.h
index 53634cd..a1b771b 100644
--- a/src/context.h
+++ b/src/context.h
@@ -431,6 +431,25 @@
const struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision);
/**
+ * @brief Get current ID of the modules set. The value is available also
+ * as module-set-id in ::ly_ctx_get_yanglib_data() result.
+ *
+ * @param[in] ctx Context to be examined.
+ * @return Numeric identifier of the current context's modules set.
+ */
+uint16_t ly_ctx_get_yanglib_id(const struct ly_ctx *ctx);
+
+/**
+ * @brief Get data of the internal ietf-yang-library module with information about all the loaded modules.
+ * ietf-yang-library module must be loaded.
+ *
+ * @param[in] ctx Context with the modules.
+ * @return Generated data, must be freed,
+ * @return NULL on error.
+ */
+struct lyd_node *ly_ctx_get_yanglib_data(const struct ly_ctx *ctx);
+
+/**
* @brief Free all internal structures of the specified context.
*
* The function should be used before terminating the application to destroy
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 9917b5d..3742a06 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1652,6 +1652,7 @@
struct lysc_action *rpcs; /**< list of RPCs ([sized array](@ref sizedarrays)) */
struct lysc_notif *notifs; /**< list of notifications ([sized array](@ref sizedarrays)) */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lys_module **deviated_by; /**< List of modules that deviate this module ([sized array](@ref sizedarrays)) */
};
/**
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index a49a5de..2f0f278 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -5869,7 +5869,7 @@
int i, changed_type;
size_t prefix_len, name_len;
const char *prefix, *name, *nodeid, *dflt;
- struct lys_module *mod;
+ struct lys_module *mod, **dev_mod;
uint32_t min, max;
uint16_t flags;
@@ -6783,6 +6783,10 @@
goto cleanup;
}
+ /* add this module into the target module deviated_by */
+ LY_ARRAY_NEW_GOTO(ctx->ctx, devs[u]->target->module->compiled->deviated_by, dev_mod, ret, cleanup);
+ *dev_mod = mod_p->mod;
+
lysc_update_path(ctx, NULL, NULL);
}
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index c6406bf..957082c 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -844,6 +844,7 @@
FREE_ARRAY(ctx, module->rpcs, lysc_action_free);
FREE_ARRAY(ctx, module->notifs, lysc_notif_free);
FREE_ARRAY(ctx, module->exts, lysc_ext_instance_free);
+ LY_ARRAY_FREE(module->deviated_by);
free(module);
}
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
index ca5c5b5..c236413 100644
--- a/tests/utests/CMakeLists.txt
+++ b/tests/utests/CMakeLists.txt
@@ -5,6 +5,7 @@
utest:test_context
utest:test_xml
utest:test_xpath
+ utest:test_yanglib
utest:schema/test_schema
utest:schema/test_parser_yang
utest:schema/test_parser_yin
@@ -37,6 +38,7 @@
" "
" "
" "
+ " "
" ")
set(tests ${tests} ${local_tests} PARENT_SCOPE)
set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/utests/test_yanglib.c b/tests/utests/test_yanglib.c
new file mode 100644
index 0000000..eb35cff
--- /dev/null
+++ b/tests/utests/test_yanglib.c
@@ -0,0 +1,214 @@
+/**
+ * @file test_yanglib.c
+ * @author: Michal Vasko <mvasko@cesnet.cz>
+ * @brief unit tests for ietf-yang-library data
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "tests/config.h"
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../../src/context.h"
+#include "../../src/tree_schema.h"
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+struct ly_ctx *ctx; /* context for tests */
+
+/* set to 0 to printing error messages to stderr instead of checking them in code */
+#define ENABLE_LOGGER_CHECKING 1
+
+#if ENABLE_LOGGER_CHECKING
+static void
+logger(LY_LOG_LEVEL level, const char *msg, const char *path)
+{
+ (void) level; /* unused */
+ if (store) {
+ if (path && path[0]) {
+ snprintf(logbuf, BUFSIZE - 1, "%s %s", msg, path);
+ } else {
+ strncpy(logbuf, msg, BUFSIZE - 1);
+ }
+ if (store > 0) {
+ --store;
+ }
+ }
+}
+#endif
+
+static LY_ERR
+test_imp_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data,
+ LYS_INFORMAT *format, const char **module_data, void (**free_module_data)(void *model_data, void *user_data))
+{
+ const char *schema_a_sub =
+ "submodule a_sub {"
+ "belongs-to a {"
+ "prefix a;"
+ "}"
+ "yang-version 1.1;"
+
+ "feature feat1;"
+
+ "list l3 {"
+ "key \"a\";"
+ "leaf a {"
+ "type uint16;"
+ "}"
+ "leaf b {"
+ "type uint16;"
+ "}"
+ "}"
+ "}";
+
+ assert_string_equal(mod_name, "a");
+ assert_null(mod_rev);
+ if (!submod_name) {
+ return LY_ENOTFOUND;
+ }
+ assert_string_equal(submod_name, "a_sub");
+ assert_null(sub_rev);
+ assert_null(user_data);
+
+ *format = LYS_IN_YANG;
+ *module_data = schema_a_sub;
+ *free_module_data = NULL;
+ return LY_SUCCESS;
+}
+
+static int
+setup(void **state)
+{
+ (void) state; /* unused */
+
+ const char *schema_a =
+ "module a {"
+ "namespace urn:tests:a;"
+ "prefix a;"
+ "yang-version 1.1;"
+
+ "include a_sub;"
+
+ "list l2 {"
+ "key \"a\";"
+ "leaf a {"
+ "type uint16;"
+ "}"
+ "leaf b {"
+ "type uint16;"
+ "}"
+ "}"
+ "}";
+ const char *schema_b =
+ "module b {"
+ "namespace urn:tests:b;"
+ "prefix b;"
+ "yang-version 1.1;"
+
+ "import a {"
+ "prefix a;"
+ "}"
+
+ "deviation /a:l2 {"
+ "deviate add {"
+ "max-elements 40;"
+ "}"
+ "}"
+
+ "leaf foo {"
+ "type string;"
+ "}"
+ "}";
+ const struct lys_module *mod;
+
+#if ENABLE_LOGGER_CHECKING
+ ly_set_log_clb(logger, 1);
+#endif
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
+ ly_ctx_set_module_imp_clb(ctx, test_imp_clb, NULL);
+
+ mod = lys_parse_mem(ctx, schema_a, LYS_IN_YANG);
+ assert_non_null(mod);
+ assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "feat1"));
+ assert_non_null(lys_parse_mem(ctx, schema_b, LYS_IN_YANG));
+
+ return 0;
+}
+
+static int
+teardown(void **state)
+{
+#if ENABLE_LOGGER_CHECKING
+ if (*state) {
+ fprintf(stderr, "%s\n", logbuf);
+ }
+#else
+ (void) state; /* unused */
+#endif
+
+ ly_ctx_destroy(ctx, NULL);
+ ctx = NULL;
+
+ return 0;
+}
+
+void
+logbuf_clean(void)
+{
+ logbuf[0] = '\0';
+}
+
+#if ENABLE_LOGGER_CHECKING
+# define logbuf_assert(str) assert_string_equal(logbuf, str)
+#else
+# define logbuf_assert(str)
+#endif
+
+static void
+test_yanglib(void **state)
+{
+ *state = test_yanglib;
+
+ struct lyd_node *tree;
+ struct ly_set *set;
+ LY_ERR ret;
+
+ tree = ly_ctx_get_yanglib_data(ctx);
+ assert_non_null(tree);
+
+ /* make sure there is "a" with a submodule and deviation */
+ ret = lyd_find_xpath(tree, "/ietf-yang-library:yang-library/module-set/module[name='a'][submodule/name='a_sub']"
+ "[feature='feat1'][deviation='b']", &set);
+ assert_int_equal(ret, LY_SUCCESS);
+
+ assert_int_equal(set->count, 1);
+ ly_set_free(set, NULL);
+
+ lyd_free_all(tree);
+ *state = NULL;
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_yanglib, setup, teardown),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}