Merge branch 'libyang2' of https://github.com/CESNET/libyang into libyang2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 149a3e4..9b325cc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -224,6 +224,7 @@
src/printer_schema.c
src/printer_yang.c
src/plugins_types.c
+ src/plugins_exts.c
src/xml.c
src/xpath.c)
@@ -246,7 +247,7 @@
src/printer_data.h
src/tree_schema.h
src/printer_schema.h
- src/extensions.h
+ src/plugins_exts.h
src/plugins_types.h
src/dict.h
src/log.h
diff --git a/src/common.c b/src/common.c
index 4ce64fd..a59d5af 100644
--- a/src/common.c
+++ b/src/common.c
@@ -24,7 +24,7 @@
#include <sys/stat.h>
#include <unistd.h>
-#include "extensions.h"
+#include "plugins_exts.h"
#include "tree_schema.h"
#include "tree_schema_internal.h"
diff --git a/src/extensions.h b/src/extensions.h
deleted file mode 100644
index 588f63a..0000000
--- a/src/extensions.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/**
- * @file extesnions.h
- * @author Radek Krejci <rkrejci@cesnet.cz>
- * @brief libyang support for YANG extensions implementation.
- *
- * Copyright (c) 2015 - 2018 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
- */
-
-#ifndef LY_EXTENSIONS_H_
-#define LY_EXTENSIONS_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @defgroup extensions YANG Extensions
- *
- * @{
- */
-
-/**
- * @brief Extension instance structure parent enumeration
- */
-typedef enum {
- LYEXT_PAR_MODULE, /**< ::lys_module or ::lys_submodule */
- LYEXT_PAR_NODE, /**< ::lys_node (and the derived structures) */
- LYEXT_PAR_TPDF, /**< ::lys_tpdf */
- LYEXT_PAR_TYPE, /**< ::lys_type */
- LYEXT_PAR_TYPE_BIT, /**< ::lys_type_bit */
- LYEXT_PAR_TYPE_ENUM, /**< ::lys_type_enum */
- LYEXT_PAR_FEATURE, /**< ::lys_feature */
- LYEXT_PAR_RESTR, /**< ::lys_restr - YANG's must, range, length and pattern statements */
- LYEXT_PAR_WHEN, /**< ::lys_when */
- LYEXT_PAR_IDENT, /**< ::lys_ident */
- LYEXT_PAR_EXT, /**< ::lys_ext */
- LYEXT_PAR_EXTINST, /**< ::lys_ext_instance */
- LYEXT_PAR_REFINE, /**< ::lys_refine */
- LYEXT_PAR_DEVIATION, /**< ::lys_deviation */
- LYEXT_PAR_DEVIATE, /**< ::lys_deviate */
- LYEXT_PAR_IMPORT, /**< ::lys_import */
- LYEXT_PAR_INCLUDE, /**< ::lysp_include */
- LYEXT_PAR_REVISION, /**< ::lysc_revision */
-} LYEXT_PARENT;
-
-/**
- * @brief Enum of substatements in which extension instances can appear.
- */
-typedef enum {
- LYEXT_SUBSTMT_SELF = 0, /**< extension of the structure itself, not substatement's */
- LYEXT_SUBSTMT_ARGUMENT, /**< extension of the argument statement, can appear in lys_ext */
- LYEXT_SUBSTMT_BASE, /**< extension of the base statement, can appear (repeatedly) in lys_type and lys_ident */
- LYEXT_SUBSTMT_BELONGSTO, /**< extension of the belongs-to statement, can appear in lys_submodule */
- LYEXT_SUBSTMT_CONTACT, /**< extension of the contact statement, can appear in lys_module */
- LYEXT_SUBSTMT_DEFAULT, /**< extension of the default statement, can appear in lys_node_leaf, lys_node_leaflist,
- lys_node_choice and lys_deviate */
- LYEXT_SUBSTMT_DESCRIPTION, /**< extension of the description statement, can appear in lys_module, lys_submodule,
- lys_node, lys_import, lys_include, lys_ext, lys_feature, lys_tpdf, lys_restr,
- lys_ident, lys_deviation, lys_type_enum, lys_type_bit, lys_when and lys_revision */
- LYEXT_SUBSTMT_ERRTAG, /**< extension of the error-app-tag statement, can appear in lys_restr */
- LYEXT_SUBSTMT_ERRMSG, /**< extension of the error-message statement, can appear in lys_restr */
- LYEXT_SUBSTMT_KEY, /**< extension of the key statement, can appear in lys_node_list */
- LYEXT_SUBSTMT_NAMESPACE, /**< extension of the namespace statement, can appear in lys_module */
- LYEXT_SUBSTMT_ORGANIZATION, /**< extension of the organization statement, can appear in lys_module and lys_submodule */
- LYEXT_SUBSTMT_PATH, /**< extension of the path statement, can appear in lys_type */
- LYEXT_SUBSTMT_PREFIX, /**< extension of the prefix statement, can appear in lys_module, lys_submodule (for
- belongs-to's prefix) and lys_import */
- LYEXT_SUBSTMT_PRESENCE, /**< extension of the presence statement, can appear in lys_node_container */
- LYEXT_SUBSTMT_REFERENCE, /**< extension of the reference statement, can appear in lys_module, lys_submodule,
- lys_node, lys_import, lys_include, lys_revision, lys_tpdf, lys_restr, lys_ident,
- lys_ext, lys_feature, lys_deviation, lys_type_enum, lys_type_bit and lys_when */
- LYEXT_SUBSTMT_REVISIONDATE, /**< extension of the revision-date statement, can appear in lys_import and lys_include */
- LYEXT_SUBSTMT_UNITS, /**< extension of the units statement, can appear in lys_tpdf, lys_node_leaf,
- lys_node_leaflist and lys_deviate */
- LYEXT_SUBSTMT_VALUE, /**< extension of the value statement, can appear in lys_type_enum */
- LYEXT_SUBSTMT_VERSION, /**< extension of the yang-version statement, can appear in lys_module and lys_submodule */
- LYEXT_SUBSTMT_MODIFIER, /**< extension of the modifier statement, can appear in lys_restr */
- LYEXT_SUBSTMT_REQINSTANCE, /**< extension of the require-instance statement, can appear in lys_type */
- LYEXT_SUBSTMT_YINELEM, /**< extension of the yin-element statement, can appear in lys_ext */
- LYEXT_SUBSTMT_CONFIG, /**< extension of the config statement, can appear in lys_node and lys_deviate */
- LYEXT_SUBSTMT_MANDATORY, /**< extension of the mandatory statement, can appear in lys_node_leaf, lys_node_choice,
- lys_node_anydata and lys_deviate */
- LYEXT_SUBSTMT_ORDEREDBY, /**< extension of the ordered-by statement, can appear in lys_node_list and lys_node_leaflist */
- LYEXT_SUBSTMT_STATUS, /**< extension of the status statement, can appear in lys_tpdf, lys_node, lys_ident,
- lys_ext, lys_feature, lys_type_enum and lys_type_bit */
- LYEXT_SUBSTMT_FRACDIGITS, /**< extension of the fraction-digits statement, can appear in lys_type */
- LYEXT_SUBSTMT_MAX, /**< extension of the max-elements statement, can appear in lys_node_list,
- lys_node_leaflist and lys_deviate */
- LYEXT_SUBSTMT_MIN, /**< extension of the min-elements statement, can appear in lys_node_list,
- lys_node_leaflist and lys_deviate */
- LYEXT_SUBSTMT_POSITION, /**< extension of the position statement, can appear in lys_type_bit */
- LYEXT_SUBSTMT_UNIQUE, /**< extension of the unique statement, can appear in lys_node_list and lys_deviate */
- LYEXT_SUBSTMT_IFFEATURE, /**< extension of the if-feature statement */
-} LYEXT_SUBSTMT;
-
-/** @} */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LY_TREE_SCHEMA_H_ */
diff --git a/src/log.c b/src/log.c
index b600b62..8efac1a 100644
--- a/src/log.c
+++ b/src/log.c
@@ -24,6 +24,7 @@
#include <string.h>
#include "log.h"
+#include "plugins_exts.h"
THREAD_LOCAL enum int_log_opts log_opt;
volatile uint8_t ly_log_level = LY_LLWRN;
@@ -464,6 +465,29 @@
}
API void
+lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...)
+{
+ va_list ap;
+ char *plugin_msg;
+ int ret;
+
+ if (ly_log_level < level) {
+ return;
+ }
+ ret = asprintf(&plugin_msg, "Extension plugin \"%s\": %s)", ext->def->plugin->id, format);
+ if (ret == -1) {
+ LOGMEM(ext->def->module->ctx);
+ return;
+ }
+
+ va_start(ap, format);
+ log_vprintf(ext->def->module->ctx, level, (level == LY_LLERR ? LY_EPLUGIN : 0), err_no, path ? strdup(path) : NULL, plugin_msg, ap);
+ va_end(ap);
+
+ free(plugin_msg);
+}
+
+API void
ly_err_print(struct ly_err_item *eitem)
{
if (ly_log_opts & LY_LOLOG) {
diff --git a/src/parser_yang.c b/src/parser_yang.c
index e5dde2f..3949b4a 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -24,8 +24,8 @@
#include "context.h"
#include "dict.h"
-#include "extensions.h"
#include "log.h"
+#include "plugins_exts.h"
#include "set.h"
#include "tree.h"
#include "tree_schema.h"
diff --git a/src/plugins_exts.c b/src/plugins_exts.c
new file mode 100644
index 0000000..720f828
--- /dev/null
+++ b/src/plugins_exts.c
@@ -0,0 +1,106 @@
+/**
+ * @file plugins_exts.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Internally implemented YANG extensions.
+ *
+ * Copyright (c) 2019 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 "common.h"
+
+#include "plugins_exts.h"
+
+#include "plugins_exts_nacm.c"
+
+/**
+ * @brief list of all extension plugins implemented internally
+ */
+struct lyext_plugins_list lyext_plugins_internal[5] = {
+ {"ietf-netconf-acm", "2012-02-22", "default-deny-write", &nacm_plugin},
+ {"ietf-netconf-acm", "2018-02-14", "default-deny-write", &nacm_plugin},
+ {"ietf-netconf-acm", "2012-02-22", "default-deny-all", &nacm_plugin},
+ {"ietf-netconf-acm", "2018-02-14", "default-deny-all", &nacm_plugin},
+ {NULL, NULL, NULL, NULL} /* terminating item */
+};
+
+/* TODO support for external extension plugins */
+
+struct lyext_plugin *
+lyext_get_plugin(struct lysc_ext *ext)
+{
+ unsigned int u;
+
+ for (u = 0; lyext_plugins_internal[u].module; ++u) {
+ if (!strcmp(ext->name, lyext_plugins_internal[u].name) &&
+ !strcmp(ext->module->name, lyext_plugins_internal[u].module) &&
+ (!lyext_plugins_internal[u].revision || !strcmp(ext->module->revision, lyext_plugins_internal[u].revision))) {
+ /* we have the match */
+ return lyext_plugins_internal[u].plugin;
+ }
+ }
+
+ return NULL;
+}
+
+API const char *
+lyext_parent2str(LYEXT_PARENT type)
+{
+ switch(type) {
+ case LYEXT_PAR_MODULE:
+ return "module";
+ case LYEXT_PAR_NODE:
+ return "data node";
+ case LYEXT_PAR_INPUT:
+ return "input";
+ case LYEXT_PAR_OUTPUT:
+ return "output";
+ case LYEXT_PAR_TYPE:
+ return "type";
+ case LYEXT_PAR_TYPE_BIT:
+ return "bit";
+ case LYEXT_PAR_TYPE_ENUM:
+ return "enum";
+ case LYEXT_PAR_FEATURE:
+ return "feature";
+ case LYEXT_PAR_MUST:
+ return "must";
+ case LYEXT_PAR_PATTERN:
+ return "pattern";
+ case LYEXT_PAR_LENGTH:
+ return "length";
+ case LYEXT_PAR_RANGE:
+ return "range";
+ case LYEXT_PAR_WHEN:
+ return "when";
+ case LYEXT_PAR_IDENT:
+ return "identity";
+ case LYEXT_PAR_EXT:
+ return "extension instance";
+ case LYEXT_PAR_IMPORT:
+ return "import";
+/* YANG allows extension instances inside the following statements,
+ * but they do not have any meaning in current libyang
+ case LYEXT_PAR_TPDF:
+ return "typedef";
+ case LYEXT_PAR_EXTINST:
+ return "extension";
+ case LYEXT_PAR_REFINE:
+ return "refine";
+ case LYEXT_PAR_DEVIATION:
+ return "deviation";
+ case LYEXT_PAR_DEVIATE:
+ return "deviate";
+ case LYEXT_PAR_INCLUDE:
+ return "include";
+ case LYEXT_PAR_REVISION:
+ return "revision";
+ */
+ default:
+ return "unknown";
+ }
+}
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
new file mode 100644
index 0000000..6684d6a
--- /dev/null
+++ b/src/plugins_exts.h
@@ -0,0 +1,167 @@
+/**
+ * @file plugins_exts.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang support for YANG extensions implementation.
+ *
+ * Copyright (c) 2015 - 2019 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
+ */
+
+#ifndef LY_PLUGINS_EXTS_H_
+#define LY_PLUGINS_EXTS_H_
+
+#include "set.h"
+#include "tree_schema.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * @defgroup extensions YANG Extensions
+ *
+ * @{
+ */
+
+/**
+ * @brief Extensions API version
+ */
+#define LYEXT_API_VERSION 1
+
+/**
+ * @brief Macro to store version of extension plugins API in the plugins.
+ * It is matched when the plugin is being loaded by libyang.
+ */
+#define LYEXT_VERSION_CHECK int lyext_api_version = LYEXT_API_VERSION;
+
+/**
+ * @defgroup extensionscompile YANG Extensions - Compilation Helpers
+ * @ingroup extensions
+ * @brief Helper functions to compile (via lyext_clb_compile callback) statements inside the extension instance.
+ *
+ * NOTE: There is a lot of useful static functions in the tree_schema_compile.c which could be provided here. Since we don't want
+ * to have a large API with functions which will be never used, we provide here just the functions which are evidently needed.
+ * If you, as an extension plugin author, need to make some of the compile functions available, please contact libyang maintainers
+ * via the GITHUB issue tracker.
+ *
+ * @{
+ */
+
+/**
+ * @brief internal context for compilation
+ */
+struct lysc_ctx {
+ struct ly_ctx *ctx;
+ struct lys_module *mod;
+ struct lys_module *mod_def; /**< context module for the definitions of the nodes being currently
+ processed - groupings are supposed to be evaluated in place where
+ defined, but its content instances are supposed to be placed into
+ the target module (mod) */
+ struct ly_set groupings; /**< stack for groupings circular check */
+ struct ly_set unres; /**< to validate leafref's target and xpath of when/must */
+ struct ly_set dflts; /**< set of incomplete default values */
+ struct ly_set tpdf_chain;
+ uint16_t path_len;
+ int options; /**< various @ref scflags. */
+#define LYSC_CTX_BUFSIZE 4078
+ char path[LYSC_CTX_BUFSIZE];
+};
+
+/**
+ * @brief Update path in the compile context, which is used for logging where the compilation failed.
+ *
+ * @param[in] ctx Compile context with the path.
+ * @param[in] parent Parent of the current node to check difference of the node's module. The current module is taken from lysc_ctx::mod.
+ * @param[in] name Name of the node to update path with. If NULL, the last segment is removed. If the format is `{keyword}`, the following
+ * call updates the segment to the form `{keyword='name'}` (to remove this compound segment, 2 calls with NULL @p name must be used).
+ */
+void lysc_update_path(struct lysc_ctx *ctx, struct lysc_node *parent, const char *name);
+
+/** @} extensionscompile */
+
+/**
+ * @brief Callback to compile extension from the lysp_ext_instance to the lysc_ext_instance. The later structure is generally prepared
+ * and only the extension specific data are supposed to be added (if any).
+ *
+ * @param[in] cctx Current compile context.
+ * @param[in] p_ext Parsed extension instance data.
+ * @param[in,out] c_ext Prepared compiled extension instance structure where an addition, extension-specific, data are supposed to be placed
+ * for later use (data validation or use of external tool).
+ * @return LY_SUCCESS in case of success.
+ * @return LY_EVALID in case of non-conforming parsed data.
+ */
+typedef LY_ERR (*lyext_clb_compile)(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext);
+
+/**
+ * @brief Callback to free the extension specific data created by the lyext_clb_compile callback of the same extension plugin.
+ *
+ * @param[in,out] ext Compiled extension structure where the data to free are placed.
+ */
+typedef void (*lyext_clb_free)(struct lysc_ext_instance *ext);
+
+/**
+ * @brief Callback to decide if data instance is valid according to the schema.
+ *
+ * The callback is used only for the extension instances placed in the following parent statements
+ * (which is specified as lysc_ext_instance::parent_type):
+ * - LYEXT_PAR_NODE - @p node is instance of the schema node where the extension instance was specified.
+ * - LYEXT_PAR_TPDF - @p node is instance of the schema node with the value of the typedef's type where the extension instance was specified.
+ * - LYEXT_PAR_TYPE - @p node is instance of the schema node with the value of the type where the extension instance was specified.
+ * - LYEXT_PAR_TYPE_BIT - @p node is instance of the schema node with the value of the bit where the extension instance was specified.
+ * - LYEXT_PAR_TYPE_ENUM - @p node is instance of the schema node with the value of the enum where the extension instance was specified.
+ *
+ * @param[in] ext Extension instance to be checked.
+ * @param[in] node Data node, where the extension data are supposed to be placed.
+ *
+ * @return LY_SUCCESS on data validation success.
+ * @return LY_EVALID in case the validation fails.
+ */
+typedef LY_ERR (*lyext_clb_data_validation)(struct lysc_ext_instance *ext, struct lyd_node *node);
+
+/**
+ * @brief Extension plugin implementing various aspects of a YANG extension
+ */
+struct lyext_plugin {
+ const char *id; /**< Plugin identification (mainly for distinguish incompatible versions of the plugins for external tools) */
+ lyext_clb_compile compile; /**< Callback to compile extension instance from the parsed data */
+ lyext_clb_data_validation validate; /**< Callback to decide if data instance is valid according to the schema. */
+ /* TODO printers? (schema/data) */
+ lyext_clb_free free; /**< Free the extension instance specific data created by lyext_plugin::compile callback */
+};
+
+struct lyext_plugins_list {
+ const char *module; /**< name of the module where the extension is defined */
+ const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
+ which is not an optimal approach due to a possible future revisions of the module.
+ Instead, there should be defined multiple items in the plugins list, each with the
+ different revision, but all with the same pointer to the plugin extension. The
+ only valid use case for the NULL revision is the case the module has no revision. */
+ const char *name; /**< name of the extension */
+ struct lyext_plugin *plugin; /**< plugin for the extension */
+};
+
+
+/**
+ * @brief Provide a log message from an extension plugin.
+ *
+ * @param[in] ext Compiled extension structure providing generic information about the extension/plugin causing the message.
+ * @param[in] level Log message level (error, warning, etc.)
+ * @param[in] err_no Error type code.
+ * @param[in] path Path relevant to the message.
+ * @param[in] format Format string to print.
+ */
+void lyext_log(const struct lysc_ext_instance *ext, LY_LOG_LEVEL level, LY_ERR err_no, const char *path, const char *format, ...);
+
+/** @} extensions */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PLUGINS_EXTS_H_ */
diff --git a/src/plugins_exts_internal.h b/src/plugins_exts_internal.h
new file mode 100644
index 0000000..4f0eb5f
--- /dev/null
+++ b/src/plugins_exts_internal.h
@@ -0,0 +1,27 @@
+/**
+ * @file plugins_exts_internal.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief internal functions to support extension plugins.
+ *
+ * Copyright (c) 2019 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
+ */
+
+#ifndef LY_PLUGINS_EXTS_INTERNAL_H_
+#define LY_PLUGINS_EXTS_INTERNAL_H_
+
+#include "tree_schema.h"
+
+/**
+ * @brief Find the extension plugin for the specified extension instance.
+ *
+ * @param[in] mod YANG module where the
+ */
+struct lyext_plugin *lyext_get_plugin(struct lysc_ext *ext);
+
+#endif /* LY_PLUGINS_EXTS_INTERNAL_H_ */
diff --git a/src/plugins_exts_nacm.c b/src/plugins_exts_nacm.c
new file mode 100644
index 0000000..992aa04
--- /dev/null
+++ b/src/plugins_exts_nacm.c
@@ -0,0 +1,126 @@
+/**
+ * @file plugins_exts_nacm.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief libyang extension plugin - NACM (RFC 6536)
+ *
+ * Copyright (c) 2019 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 "common.h"
+
+#include <stdlib.h>
+
+#include "plugins_exts.h"
+#include "tree_schema.h"
+
+/**
+ * @brief Storage for ID used to check plugin API version compatibility.
+ * Ignored here in the internal plugin.
+LYEXT_VERSION_CHECK
+ */
+
+/**
+ * @brief Compile NAMC's extension instances.
+ *
+ * Implementation of lyext_clb_compile callback set as lyext_plugin::compile.
+ */
+LY_ERR
+nacm_compile(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext)
+{
+ struct lysc_node *parent = NULL, *iter;
+ struct lysc_ext_instance *inherited;
+ unsigned int u;
+
+ static const uint8_t nacm_deny_all = 1;
+ static const uint8_t nacm_deny_write = 2;
+
+ /* store the NACM flag */
+ if (!strcmp(c_ext->def->name, "default-deny-write")) {
+ c_ext->data = (void*)&nacm_deny_write;
+ } else if (!strcmp(c_ext->def->name, "default-deny-all")) {
+ c_ext->data = (void*)&nacm_deny_all;
+ } else {
+ return LY_EINT;
+ }
+
+ /* check that the extension is instantiated at an allowed place - data node */
+ if (c_ext->parent_type != LYEXT_PAR_NODE) {
+ lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path, "Extension %s is allowed only in a data nodes, but it is placed in \"%s\" statement.",
+ p_ext->name, lyext_parent2str(c_ext->parent_type));
+ return LY_EVALID;
+ } else {
+ parent = (struct lysc_node*)c_ext->parent;
+ if (!(parent->nodetype & (LYS_CONTAINER | LYS_LEAF | LYS_LEAFLIST | LYS_LIST | LYS_CHOICE | LYS_ANYDATA | LYS_CASE | LYS_ACTION | LYS_NOTIF))) {
+ /* note LYS_AUGMENT and LYS_USES is not in the list since they are not present in the compiled tree. Instead, libyang
+ * passes all their extensions to their children nodes */
+invalid_parent:
+ lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path,
+ "Extension %s is not allowed in %s statement.", p_ext->name, lys_nodetype2str(parent->nodetype));
+ return LY_EVALID;
+ }
+ if (c_ext->data == (void*)&nacm_deny_write && (parent->nodetype & (LYS_ACTION | LYS_NOTIF))) {
+ goto invalid_parent;
+ }
+ }
+
+ /* check for duplication */
+ LY_ARRAY_FOR(parent->exts, u) {
+ if (&parent->exts[u] != c_ext && parent->exts[u].def->plugin == c_ext->def->plugin) {
+ /* duplication of a NACM extension on a single node
+ * We check plugin since we want to catch even the situation that there is default-deny-all
+ * AND default-deny-write */
+ if (parent->exts[u].def == c_ext->def) {
+ lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path, "Extension %s is instantiated multiple times.", p_ext->name);
+ } else {
+ lyext_log(c_ext, LY_LLERR, LY_EVALID, cctx->path, "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.");
+ }
+ return LY_EVALID;
+ }
+ }
+
+ /* inherit the extension instance to all the children nodes */
+ LYSC_TREE_DFS_BEGIN(parent, iter) {
+ if (iter != parent) { /* ignore the parent from which we inherit */
+ /* check that the node does not have its own NACM extension instance */
+ LY_ARRAY_FOR(iter->exts, u) {
+ if (iter->exts[u].def == c_ext->def) {
+ /* the child already have its own NACM flag, so skip the subtree */
+ LYSC_TREE_DFS_continue = 1;
+ break;
+ }
+ }
+ if (!LYSC_TREE_DFS_continue) {
+ /* duplicate this one to inherit it to the child */
+ LY_ARRAY_NEW_RET(cctx->ctx, iter->exts, inherited, LY_EMEM);
+
+ inherited->def = c_ext->def;
+ inherited->parent = iter;
+ inherited->parent_type = LYEXT_PAR_NODE;
+ if (c_ext->argument) {
+ inherited->argument = lydict_insert(cctx->ctx, c_ext->argument, strlen(c_ext->argument));
+ }
+ /* TODO duplicate extension instances */
+ inherited->data = c_ext->data;
+ }
+ }
+ LYSC_TREE_DFS_END(parent, iter)
+ }
+
+ return LY_SUCCESS;
+}
+
+
+/**
+ * @brief Plugin for the NACM's default-deny-write and default-deny-all extensions
+ */
+struct lyext_plugin nacm_plugin = {
+ .id = "libyang 2 - NACM, version 1",
+ .compile = &nacm_compile,
+ .validate = NULL,
+ .free = NULL
+};
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 1be7af2..9d6a339 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -1,5 +1,5 @@
/**
- * @file plugin_types.c
+ * @file plugins_types.c
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief Built-in types plugins and interface for user types plugins.
*
@@ -2601,41 +2601,41 @@
struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
{0}, /* LY_TYPE_UNKNOWN */
{.type = LY_TYPE_BINARY, .store = ly_type_store_binary, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_canonical, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_canonical, .free = ly_type_free_canonical, .id = "libyang 2 - binary, version 1"},
{.type = LY_TYPE_UINT8, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_uint, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_uint, .free = ly_type_free_canonical, .id = "libyang 2 - unsigned integer, version 1"},
{.type = LY_TYPE_UINT16, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_uint, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_uint, .free = ly_type_free_canonical, .id = "libyang 2 - unsigned integer, version 1"},
{.type = LY_TYPE_UINT32, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_uint, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_uint, .free = ly_type_free_canonical, .id = "libyang 2 - unsigned integer, version 1"},
{.type = LY_TYPE_UINT64, .store = ly_type_store_uint, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_uint, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_uint, .free = ly_type_free_canonical, .id = "libyang 2 - unsigned integer, version 1"},
{.type = LY_TYPE_STRING, .store = ly_type_store_string, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_canonical, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_canonical, .free = ly_type_free_canonical, .id = "libyang 2 - string, version 1"},
{.type = LY_TYPE_BITS, .store = ly_type_store_bits, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_bits, .free = ly_type_free_bits},
+ .duplicate = ly_type_dup_bits, .free = ly_type_free_bits, .id = "libyang 2 - bits, version 1"},
{.type = LY_TYPE_BOOL, .store = ly_type_store_boolean, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_int, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_int, .free = ly_type_free_canonical, .id = "libyang 2 - boolean, version 1"},
{.type = LY_TYPE_DEC64, .store = ly_type_store_decimal64, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_decimal64, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_decimal64, .free = ly_type_free_canonical, .id = "libyang 2 - decimal64, version 1"},
{.type = LY_TYPE_EMPTY, .store = ly_type_store_empty, .compare = ly_type_compare_empty, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_canonical, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_canonical, .free = ly_type_free_canonical, .id = "libyang 2 - empty, version 1"},
{.type = LY_TYPE_ENUM, .store = ly_type_store_enum, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_enum, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_enum, .free = ly_type_free_canonical, .id = "libyang 2 - enumeration, version 1"},
{.type = LY_TYPE_IDENT, .store = ly_type_store_identityref, .compare = ly_type_compare_identityref, .print = ly_type_print_identityref,
- .duplicate = ly_type_dup_identityref, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_identityref, .free = ly_type_free_canonical, .id = "libyang 2 - identityref, version 1"},
{.type = LY_TYPE_INST, .store = ly_type_store_instanceid, .compare = ly_type_compare_instanceid, .print = ly_type_print_instanceid,
- .duplicate = ly_type_dup_instanceid, .free = ly_type_free_instanceid},
+ .duplicate = ly_type_dup_instanceid, .free = ly_type_free_instanceid, .id = "libyang 2 - instance-identifier, version 1"},
{.type = LY_TYPE_LEAFREF, .store = ly_type_store_leafref, .compare = ly_type_compare_leafref, .print = ly_type_print_leafref,
- .duplicate = ly_type_dup_leafref, .free = ly_type_free_leafref},
+ .duplicate = ly_type_dup_leafref, .free = ly_type_free_leafref, .id = "libyang 2 - leafref, version 1"},
{.type = LY_TYPE_UNION, .store = ly_type_store_union, .compare = ly_type_compare_union, .print = ly_type_print_union,
- .duplicate = ly_type_dup_union, .free = ly_type_free_union},
+ .duplicate = ly_type_dup_union, .free = ly_type_free_union, .id = "libyang 2 - union,version 1"},
{.type = LY_TYPE_INT8, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_int, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_int, .free = ly_type_free_canonical, .id = "libyang 2 - integer, version 1"},
{.type = LY_TYPE_INT16, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_int, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_int, .free = ly_type_free_canonical, .id = "libyang 2 - integer, version 1"},
{.type = LY_TYPE_INT32, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_int, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_int, .free = ly_type_free_canonical, .id = "libyang 2 - integer, version 1"},
{.type = LY_TYPE_INT64, .store = ly_type_store_int, .compare = ly_type_compare_canonical, .print = ly_type_print_canonical,
- .duplicate = ly_type_dup_int, .free = ly_type_free_canonical},
+ .duplicate = ly_type_dup_int, .free = ly_type_free_canonical, .id = "libyang 2 - integer, version 1"},
};
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 0f1ff77..a071d38 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -184,6 +184,7 @@
ly_type_print_clb print; /**< printer callback to get string representing the value */
ly_type_dup_clb duplicate; /**< data duplication callback */
ly_type_free_clb free; /**< optional function to free the type-spceific way stored value */
+ const char *id; /**< Plugin identification (mainly for distinguish incompatible versions when used by external tools) */
uint32_t flags; /**< [type flags ](@ref plugintypeflags). */
};
diff --git a/src/printer_yang.c b/src/printer_yang.c
index 5156935..65ce5b5 100755
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -20,8 +20,8 @@
#include <stdlib.h>
#include <string.h>
-#include "extensions.h"
#include "log.h"
+#include "plugins_exts.h"
#include "printer_internal.h"
#include "tree.h"
#include "tree_schema.h"
diff --git a/src/tree_data.h b/src/tree_data.h
index e9b1c32..22e9d3c 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -48,7 +48,7 @@
* 3 5 6
* </pre>
*
- * Use the same parameters for #LY_TREE_DFS_BEGIN and #LY_TREE_DFS_END. While
+ * Use the same parameters for #LYD_TREE_DFS_BEGIN and #LYD_TREE_DFS_END. While
* START can be any of the lyd_node* types, NEXT and ELEM variables are expected
* to be pointers to a generic struct lyd_node.
*
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 6f45540..3fc8b08 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -752,8 +752,9 @@
#endif
if (!mod->implemented) {
- /* pre-compile features of the module */
+ /* pre-compile features and extension definitions of the module */
LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, mod->parsed->features, &mod->off_features), error);
+ LY_CHECK_GOTO(lys_extension_precompile(NULL, ctx, mod, mod->parsed->extensions, &mod->off_extensions), error);
}
/* decide the latest revision */
@@ -798,8 +799,9 @@
goto error_ctx;
}
if (!mod->implemented) {
- /* pre-compile features of the module */
+ /* pre-compile features and extension definitions of the module */
LY_CHECK_GOTO(lys_feature_precompile(NULL, ctx, mod, inc->submodule->features, &mod->off_features), error);
+ LY_CHECK_GOTO(lys_extension_precompile(NULL, ctx, mod, mod->parsed->extensions, &mod->off_extensions), error);
}
}
mod->parsed->parsing = 0;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index e3b00c5..4a3dbec 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -23,7 +23,6 @@
#include "log.h"
#include "tree.h"
-#include "extensions.h"
#include "tree_data.h"
struct ly_ctx;
@@ -33,6 +32,90 @@
#endif
/**
+ * @brief Macro to iterate via all elements in a schema tree which can be instantiated in data tree
+ * (skips cases, input, output). This is the opening part to the #LYS_TREE_DFS_END - they always have to be used together.
+ *
+ * The function follows deep-first search algorithm:
+ * <pre>
+ * 1
+ * / \
+ * 2 4
+ * / / \
+ * 3 5 6
+ * </pre>
+ *
+ * Use the same parameters for #LYSC_TREE_DFS_BEGIN and #LYSC_TREE_DFS_END. While
+ * START can be any of the lysc_node* types (including lysc_action and lysc_notif),
+ * ELEM variable must be of the struct lysc_node* type.
+ *
+ * To skip a particular subtree, instead of the continue statement, set LYSC_TREE_DFS_continue
+ * variable to non-zero value.
+ *
+ * Use with opening curly bracket '{' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param ELEM Iterator intended for use in the block.
+ */
+#define LYSC_TREE_DFS_BEGIN(START, ELEM) \
+ { int LYSC_TREE_DFS_continue = 0; struct lysc_node *LYSC_TREE_DFS_next; \
+ for ((ELEM) = (LYSC_TREE_DFS_next) = (START); \
+ (ELEM); \
+ (ELEM) = (LYSC_TREE_DFS_next), LYSC_TREE_DFS_continue = 0)
+
+/**
+ * @brief Macro to iterate via all elements in a (sub)tree. This is the closing part
+ * to the #LYSC_TREE_DFS_BEGIN - they always have to be used together.
+ *
+ * Use the same parameters for #LYSC_TREE_DFS_BEGIN and #LYSC_TREE_DFS_END. While
+ * START can be a pointer to any of the lysc_node* types (including lysc_action and lysc_notif),
+ * ELEM variable must be pointer to the lysc_node type.
+ *
+ * Use with closing curly bracket '}' after the macro.
+ *
+ * @param START Pointer to the starting element processed first.
+ * @param ELEM Iterator intended for use in the block.
+ */
+
+#define LYSC_TREE_DFS_END(START, ELEM) \
+ /* select element for the next run - children first */ \
+ if (LYSC_TREE_DFS_continue) { \
+ (LYSC_TREE_DFS_next) = NULL; \
+ } else { \
+ (LYSC_TREE_DFS_next) = (struct lysc_node*)lysc_node_children(ELEM, 0); \
+ }\
+ if (!(LYSC_TREE_DFS_next)) { \
+ /* in case of RPC/action, get also the output children */ \
+ if (!LYSC_TREE_DFS_continue && (ELEM)->nodetype == LYS_ACTION) { \
+ (LYSC_TREE_DFS_next) = (struct lysc_node*)lysc_node_children(ELEM, LYS_CONFIG_R); \
+ } \
+ if (!(LYSC_TREE_DFS_next)) { \
+ /* no children */ \
+ if ((ELEM) == (struct lysc_node*)(START)) { \
+ /* we are done, (START) has no children */ \
+ break; \
+ } \
+ /* try siblings */ \
+ (LYSC_TREE_DFS_next) = (ELEM)->next; \
+ } \
+ } \
+ while (!(LYSC_TREE_DFS_next)) { \
+ /* parent is already processed, go to its sibling */ \
+ (ELEM) = (ELEM)->parent; \
+ /* no siblings, go back through parents */ \
+ if ((ELEM) == (struct lysc_node*)(START)) { \
+ /* we are done, no next element to process */ \
+ break; \
+ } \
+ if ((ELEM)->nodetype == LYS_ACTION) { \
+ /* there is actually next node as a child of action's output */ \
+ (LYSC_TREE_DFS_next) = (struct lysc_node*)lysc_node_children(ELEM, LYS_CONFIG_R); \
+ } \
+ if (!(LYSC_TREE_DFS_next)) { \
+ (LYSC_TREE_DFS_next) = (ELEM)->next; \
+ } \
+ } } \
+
+/**
* @brief Schema input formats accepted by libyang [parser functions](@ref howtoschemasparsers).
*/
typedef enum {
@@ -79,6 +162,92 @@
#define LYS_AUGMENT 0x2000
/**
+ * @brief Extension instance structure parent enumeration
+ */
+typedef enum {
+ LYEXT_PAR_MODULE, /**< ::lysc_module */
+ LYEXT_PAR_NODE, /**< ::lysc_node (and the derived structures including ::lysc_action and ::lysc_notif) */
+ LYEXT_PAR_INPUT, /**< ::lysc_action_inout */
+ LYEXT_PAR_OUTPUT, /**< ::lysc_action_inout */
+ LYEXT_PAR_TYPE, /**< ::lysc_type */
+ LYEXT_PAR_TYPE_BIT, /**< ::lysc_type_bitenum_item */
+ LYEXT_PAR_TYPE_ENUM, /**< ::lysc_type_bitenum_item */
+ LYEXT_PAR_FEATURE, /**< ::lysc_feature */
+ LYEXT_PAR_MUST, /**< ::lysc_must */
+ LYEXT_PAR_PATTERN, /**< ::lysc_pattern */
+ LYEXT_PAR_LENGTH, /**< ::lysc_range */
+ LYEXT_PAR_RANGE, /**< ::lysc_range */
+ LYEXT_PAR_WHEN, /**< ::lysc_when */
+ LYEXT_PAR_IDENT, /**< ::lysc_ident */
+ LYEXT_PAR_EXT, /**< ::lysc_ext */
+ LYEXT_PAR_IMPORT, /**< ::lysc_import */
+// LYEXT_PAR_TPDF, /**< ::lysp_tpdf */
+// LYEXT_PAR_EXTINST, /**< ::lysp_ext_instance */
+// LYEXT_PAR_REFINE, /**< ::lysp_refine */
+// LYEXT_PAR_DEVIATION, /**< ::lysp_deviation */
+// LYEXT_PAR_DEVIATE, /**< ::lysp_deviate */
+// LYEXT_PAR_INCLUDE, /**< ::lysp_include */
+// LYEXT_PAR_REVISION, /**< ::lysp_revision */
+} LYEXT_PARENT;
+
+/**
+ * @brief Stringify extension instance parent type.
+ * @param[in] type Parent type to stringify.
+ * @return Constant string with the name of the parent statement.
+ */
+const char *lyext_parent2str(LYEXT_PARENT type);
+
+/**
+ * @brief Enum of substatements in which extension instances can appear.
+ */
+typedef enum {
+ LYEXT_SUBSTMT_SELF = 0, /**< extension of the structure itself, not substatement's */
+ LYEXT_SUBSTMT_ARGUMENT, /**< extension of the argument statement, can appear in lys_ext */
+ LYEXT_SUBSTMT_BASE, /**< extension of the base statement, can appear (repeatedly) in lys_type and lys_ident */
+ LYEXT_SUBSTMT_BELONGSTO, /**< extension of the belongs-to statement, can appear in lys_submodule */
+ LYEXT_SUBSTMT_CONTACT, /**< extension of the contact statement, can appear in lys_module */
+ LYEXT_SUBSTMT_DEFAULT, /**< extension of the default statement, can appear in lys_node_leaf, lys_node_leaflist,
+ lys_node_choice and lys_deviate */
+ LYEXT_SUBSTMT_DESCRIPTION, /**< extension of the description statement, can appear in lys_module, lys_submodule,
+ lys_node, lys_import, lys_include, lys_ext, lys_feature, lys_tpdf, lys_restr,
+ lys_ident, lys_deviation, lys_type_enum, lys_type_bit, lys_when and lys_revision */
+ LYEXT_SUBSTMT_ERRTAG, /**< extension of the error-app-tag statement, can appear in lys_restr */
+ LYEXT_SUBSTMT_ERRMSG, /**< extension of the error-message statement, can appear in lys_restr */
+ LYEXT_SUBSTMT_KEY, /**< extension of the key statement, can appear in lys_node_list */
+ LYEXT_SUBSTMT_NAMESPACE, /**< extension of the namespace statement, can appear in lys_module */
+ LYEXT_SUBSTMT_ORGANIZATION, /**< extension of the organization statement, can appear in lys_module and lys_submodule */
+ LYEXT_SUBSTMT_PATH, /**< extension of the path statement, can appear in lys_type */
+ LYEXT_SUBSTMT_PREFIX, /**< extension of the prefix statement, can appear in lys_module, lys_submodule (for
+ belongs-to's prefix) and lys_import */
+ LYEXT_SUBSTMT_PRESENCE, /**< extension of the presence statement, can appear in lys_node_container */
+ LYEXT_SUBSTMT_REFERENCE, /**< extension of the reference statement, can appear in lys_module, lys_submodule,
+ lys_node, lys_import, lys_include, lys_revision, lys_tpdf, lys_restr, lys_ident,
+ lys_ext, lys_feature, lys_deviation, lys_type_enum, lys_type_bit and lys_when */
+ LYEXT_SUBSTMT_REVISIONDATE, /**< extension of the revision-date statement, can appear in lys_import and lys_include */
+ LYEXT_SUBSTMT_UNITS, /**< extension of the units statement, can appear in lys_tpdf, lys_node_leaf,
+ lys_node_leaflist and lys_deviate */
+ LYEXT_SUBSTMT_VALUE, /**< extension of the value statement, can appear in lys_type_enum */
+ LYEXT_SUBSTMT_VERSION, /**< extension of the yang-version statement, can appear in lys_module and lys_submodule */
+ LYEXT_SUBSTMT_MODIFIER, /**< extension of the modifier statement, can appear in lys_restr */
+ LYEXT_SUBSTMT_REQINSTANCE, /**< extension of the require-instance statement, can appear in lys_type */
+ LYEXT_SUBSTMT_YINELEM, /**< extension of the yin-element statement, can appear in lys_ext */
+ LYEXT_SUBSTMT_CONFIG, /**< extension of the config statement, can appear in lys_node and lys_deviate */
+ LYEXT_SUBSTMT_MANDATORY, /**< extension of the mandatory statement, can appear in lys_node_leaf, lys_node_choice,
+ lys_node_anydata and lys_deviate */
+ LYEXT_SUBSTMT_ORDEREDBY, /**< extension of the ordered-by statement, can appear in lys_node_list and lys_node_leaflist */
+ LYEXT_SUBSTMT_STATUS, /**< extension of the status statement, can appear in lys_tpdf, lys_node, lys_ident,
+ lys_ext, lys_feature, lys_type_enum and lys_type_bit */
+ LYEXT_SUBSTMT_FRACDIGITS, /**< extension of the fraction-digits statement, can appear in lys_type */
+ LYEXT_SUBSTMT_MAX, /**< extension of the max-elements statement, can appear in lys_node_list,
+ lys_node_leaflist and lys_deviate */
+ LYEXT_SUBSTMT_MIN, /**< extension of the min-elements statement, can appear in lys_node_list,
+ lys_node_leaflist and lys_deviate */
+ LYEXT_SUBSTMT_POSITION, /**< extension of the position statement, can appear in lys_type_bit */
+ LYEXT_SUBSTMT_UNIQUE, /**< extension of the unique statement, can appear in lys_node_list and lys_deviate */
+ LYEXT_SUBSTMT_IFFEATURE, /**< extension of the if-feature statement */
+} LYEXT_SUBSTMT;
+
+/**
* @brief YANG import-stmt
*/
struct lysp_import {
@@ -842,10 +1011,22 @@
void lysp_module_free(struct lysp_module *module);
/**
+ * @brief Compiled YANG extension-stmt
+ */
+struct lysc_ext {
+ const char *name; /**< extension name */
+ const char *argument; /**< argument name, NULL if not specified */
+ struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+ struct lyext_plugin *plugin; /**< Plugin implementing the specific extension */
+ struct lys_module *module; /**< module structure */
+ uint16_t flags; /**< LYS_STATUS_* value (@ref snodeflags) */
+};
+
+/**
* @brief YANG extension instance
*/
struct lysc_ext_instance {
- struct lyext_plugin *plugin; /**< pointer to the plugin implementing the extension (if present) */
+ struct lysc_ext *def; /**< pointer to the extension definition */
void *parent; /**< pointer to the parent element holding the extension instance(s), use
::lysc_ext_instance#parent_type to access the schema element */
const char *argument; /**< optional value of the extension's argument */
@@ -853,14 +1034,8 @@
uint32_t insubstmt_index; /**< in case the instance is in a substatement that can appear multiple times,
this identifies the index of the substatement for this extension instance */
LYEXT_PARENT parent_type; /**< type of the parent structure */
-#if 0
- uint8_t ext_type; /**< extension type (#LYEXT_TYPE) */
- uint8_t padding; /**< 32b padding */
- struct lys_module *module; /**< pointer to the extension instance's module (mandatory) */
- LYS_NODE nodetype; /**< LYS_EXT */
-#endif
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
- void *priv; /**< private caller's data, not used by libyang */
+ void *data; /**< private plugins's data, not used by libyang */
};
/**
@@ -1370,6 +1545,7 @@
struct lysc_node *data; /**< list of module's top-level data nodes (linked list) */
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 *extensions; /**< list of the extension definitions ([sized array](@ref sizedarrays)) */
struct lysc_ext_instance *exts; /**< list of the extension instances ([sized array](@ref sizedarrays)) */
};
@@ -1494,6 +1670,11 @@
from if-feature statements of the compiled schemas and their proper use in case
the module became implemented in future (no matter if implicitly via augment/deviate
or explicitly via ly_ctx_module_implement()). */
+ struct lysc_ext *off_extensions; /**< List of pre-compiled extension definitions of the module in non-implemented modules
+ ([sized array](@ref sizedarrays)). These extensions are prepared to be linked with the extension instances,
+ but they are not implemented (connected with any extension plugin). In case the module become
+ implemented, the list is moved into the compiled module structure and available extension plugins
+ are connected with the appropriate extension definision. */
uint8_t implemented; /**< flag if the module is implemented, not just imported. The module is implemented if
the flag has non-zero value. Specific values are used internally:
@@ -1684,6 +1865,13 @@
LY_ERR lys_value_validate(struct ly_ctx *ctx, const struct lysc_node *node, const char *value, size_t value_len,
ly_clb_resolve_prefix get_prefix, void *get_prefix_data, LYD_FORMAT format);
+/**
+ * @brief Stringify schema nodetype.
+ * @param[in] nodetype Nodetype to stringify.
+ * @return Constant string with the name of the node's type.
+ */
+const char *lys_nodetype2str(uint16_t nodetype);
+
/** @} */
#ifdef __cplusplus
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index bea32c1..61019e2 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -24,8 +24,10 @@
#include "dict.h"
#include "log.h"
+#include "plugins_exts.h"
#include "set.h"
#include "plugins_types.h"
+#include "plugins_exts_internal.h"
#include "tree.h"
#include "tree_schema.h"
#include "tree_schema_internal.h"
@@ -61,6 +63,16 @@
} \
}
+#define COMPILE_EXTS_GOTO(CTX, EXTS_P, EXT_C, PARENT, PARENT_TYPE, RET, GOTO) \
+ if (EXTS_P) { \
+ LY_ARRAY_CREATE_GOTO((CTX)->ctx, EXT_C, LY_ARRAY_SIZE(EXTS_P), RET, GOTO); \
+ for (uint32_t __exts_iter = 0, __array_offset = LY_ARRAY_SIZE(EXT_C); __exts_iter < LY_ARRAY_SIZE(EXTS_P); ++__exts_iter) { \
+ LY_ARRAY_INCREMENT(EXT_C); \
+ RET = lys_compile_ext(CTX, &(EXTS_P)[__exts_iter], &(EXT_C)[__exts_iter + __array_offset], PARENT, PARENT_TYPE); \
+ LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+ } \
+ }
+
#define COMPILE_ARRAY_UNIQUE_GOTO(CTX, ARRAY_P, ARRAY_C, ITER, FUNC, RET, GOTO) \
if (ARRAY_P) { \
LY_ARRAY_CREATE_GOTO((CTX)->ctx, ARRAY_C, LY_ARRAY_SIZE(ARRAY_P), RET, GOTO); \
@@ -153,15 +165,7 @@
}
}
-/**
- * @brief Update path in the compile context.
- *
- * @param[in] ctx Compile context with the path.
- * @param[in] parent Parent of the current node to check difference of the node's module. The current module is taken from lysc_ctx::mod.
- * @param[in] name Name of the node to update path with. If NULL, the last segment is removed. If the format is `{keyword}`, the following
- * call updates the segment to the form `{keyword='name'}` (to remove this compound segment, 2 calls with NULL @p name must be used).
- */
-static void
+void
lysc_update_path(struct lysc_ctx *ctx, struct lysc_node *parent, const char *name)
{
int len;
@@ -421,16 +425,18 @@
}
static LY_ERR
-lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext)
+lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext, void *parent, LYEXT_PARENT parent_type)
{
const char *name;
unsigned int u;
const struct lys_module *mod;
- struct lysp_ext *edef = NULL;
+ struct lysc_ext *elist = NULL;
DUP_STRING(ctx->ctx, ext_p->argument, ext->argument);
ext->insubstmt = ext_p->insubstmt;
ext->insubstmt_index = ext_p->insubstmt_index;
+ ext->parent = parent;
+ ext->parent_type = parent_type;
/* get module where the extension definition should be placed */
for (u = 0; ext_p->name[u] != ':'; ++u);
@@ -444,17 +450,100 @@
ext_p->name, mod->name),
LY_EVALID);
name = &ext_p->name[u + 1];
+
/* find the extension definition there */
- for (ext = NULL, u = 0; u < LY_ARRAY_SIZE(mod->parsed->extensions); ++u) {
- if (!strcmp(name, mod->parsed->extensions[u].name)) {
- edef = &mod->parsed->extensions[u];
+ if (mod->off_extensions) {
+ elist = mod->off_extensions;
+ } else {
+ elist = mod->compiled->extensions;
+ }
+ LY_ARRAY_FOR(elist, u) {
+ if (!strcmp(name, elist[u].name)) {
+ ext->def = &elist[u];
break;
}
}
- LY_CHECK_ERR_RET(!edef, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
- "Extension definition of extension instance \"%s\" not found.", ext_p->name),
+ LY_CHECK_ERR_RET(!ext->def,
+ LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+ "Extension definition of extension instance \"%s\" not found.", ext_p->name),
LY_EVALID);
- /* TODO extension plugins */
+
+ if (ext->def->plugin && ext->def->plugin->compile) {
+ LY_CHECK_RET(ext->def->plugin->compile(ctx, ext_p, ext),LY_EVALID);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Fill in the prepared compiled extensions definition structure according to the parsed extension definition.
+ */
+static LY_ERR
+lys_compile_extension(struct lysc_ctx *ctx, struct lysp_ext *ext_p, struct lysc_ext *ext)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ DUP_STRING(ctx->ctx, ext_p->name, ext->name);
+ DUP_STRING(ctx->ctx, ext_p->argument, ext->argument);
+ ext->module = ctx->mod_def;
+ COMPILE_EXTS_GOTO(ctx, ext_p->exts, ext->exts, ext, LYEXT_PAR_EXT, ret, done);
+
+done:
+ return ret;
+}
+
+/**
+ * @brief Link the extensions definitions with the available extension plugins.
+ *
+ * This is done only in the compiled (implemented) module. Extensions of a non-implemented modules
+ * are not connected with even available extension plugins.
+ *
+ * @param[in] extensions List of extensions to be processed ([sized array](@ref sizedarrays)).
+ */
+static void
+lys_compile_extension_plugins(struct lysc_ext *extensions)
+{
+ unsigned int u;
+
+ LY_ARRAY_FOR(extensions, u) {
+ extensions[u].plugin = lyext_get_plugin(&extensions[u]);
+ }
+}
+
+LY_ERR
+lys_extension_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_ext *extensions_p, struct lysc_ext **extensions)
+{
+ unsigned int offset = 0, u;
+ struct lysc_ctx context = {0};
+
+ assert(ctx_sc || ctx);
+
+ if (!ctx_sc) {
+ context.ctx = ctx;
+ context.mod = module;
+ context.path_len = 1;
+ context.path[0] = '/';
+ ctx_sc = &context;
+ }
+
+ if (!extensions_p) {
+ return LY_SUCCESS;
+ }
+ if (*extensions) {
+ offset = LY_ARRAY_SIZE(*extensions);
+ }
+
+ lysc_update_path(ctx_sc, NULL, "{extension}");
+ LY_ARRAY_CREATE_RET(ctx_sc->ctx, *extensions, LY_ARRAY_SIZE(extensions_p), LY_EMEM);
+ LY_ARRAY_FOR(extensions_p, u) {
+ lysc_update_path(ctx_sc, NULL, extensions_p[u].name);
+ LY_ARRAY_INCREMENT(*extensions);
+ COMPILE_CHECK_UNIQUENESS(ctx_sc, *extensions, name, &(*extensions)[offset + u], "extension", extensions_p[u].name);
+ LY_CHECK_RET(lys_compile_extension(ctx_sc, &extensions_p[u], &(*extensions)[offset + u]));
+ lysc_update_path(ctx_sc, NULL, NULL);
+ }
+ lysc_update_path(ctx_sc, NULL, NULL);
return LY_SUCCESS;
}
@@ -662,7 +751,6 @@
static LY_ERR
lys_compile_when(struct lysc_ctx *ctx, struct lysp_when *when_p, struct lysc_when **when)
{
- unsigned int u;
LY_ERR ret = LY_SUCCESS;
*when = calloc(1, sizeof **when);
@@ -671,7 +759,7 @@
DUP_STRING(ctx->ctx, when_p->dsc, (*when)->dsc);
DUP_STRING(ctx->ctx, when_p->ref, (*when)->ref);
LY_CHECK_ERR_GOTO(!(*when)->cond, ret = ly_errcode(ctx->ctx), done);
- COMPILE_ARRAY_GOTO(ctx, when_p->exts, (*when)->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, when_p->exts, (*when)->exts, (*when), LYEXT_PAR_WHEN, ret, done);
done:
return ret;
@@ -687,7 +775,6 @@
static LY_ERR
lys_compile_must(struct lysc_ctx *ctx, struct lysp_restr *must_p, struct lysc_must *must)
{
- unsigned int u;
LY_ERR ret = LY_SUCCESS;
must->cond = lyxp_expr_parse(ctx->ctx, must_p->arg);
@@ -697,7 +784,7 @@
DUP_STRING(ctx->ctx, must_p->emsg, must->emsg);
DUP_STRING(ctx->ctx, must_p->dsc, must->dsc);
DUP_STRING(ctx->ctx, must_p->ref, must->ref);
- COMPILE_ARRAY_GOTO(ctx, must_p->exts, must->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, must_p->exts, must->exts, must, LYEXT_PAR_MUST, ret, done);
done:
return ret;
@@ -713,12 +800,11 @@
static LY_ERR
lys_compile_import(struct lysc_ctx *ctx, struct lysp_import *imp_p, struct lysc_import *imp)
{
- unsigned int u;
struct lys_module *mod = NULL;
LY_ERR ret = LY_SUCCESS;
DUP_STRING(ctx->ctx, imp_p->prefix, imp->prefix);
- COMPILE_ARRAY_GOTO(ctx, imp_p->exts, imp->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, imp_p->exts, imp->exts, imp, LYEXT_PAR_IMPORT, ret, done);
imp->module = imp_p->module;
/* make sure that we have the parsed version (lysp_) of the imported module to import groupings or typedefs.
@@ -774,7 +860,7 @@
ident->module = ctx->mod;
COMPILE_ARRAY_GOTO(ctx, ident_p->iffeatures, ident->iffeatures, u, lys_compile_iffeature, ret, done);
/* backlings (derived) can be added no sooner than when all the identities in the current module are present */
- COMPILE_ARRAY_GOTO(ctx, ident_p->exts, ident->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, ident_p->exts, ident->exts, ident, LYEXT_PAR_IDENT, ret, done);
ident->flags = ident_p->flags;
lysc_update_path(ctx, NULL, NULL);
@@ -1069,7 +1155,7 @@
lysc_update_path(ctx, NULL, feature_p->name);
/* finish compilation started in lys_feature_precompile() */
- COMPILE_ARRAY_GOTO(ctx, feature_p->exts, feature->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, feature_p->exts, feature->exts, feature, LYEXT_PAR_FEATURE, ret, done);
COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, u, lys_compile_iffeature, ret, done);
if (feature->iffeatures) {
for (u = 0; u < LY_ARRAY_SIZE(feature->iffeatures); ++u) {
@@ -1866,7 +1952,7 @@
struct lysc_pattern **base_patterns, struct lysc_pattern ***patterns)
{
struct lysc_pattern **pattern;
- unsigned int u, v;
+ unsigned int u;
LY_ERR ret = LY_SUCCESS;
/* first, copy the patterns from the base type */
@@ -1891,7 +1977,7 @@
DUP_STRING(ctx->ctx, patterns_p[u].emsg, (*pattern)->emsg);
DUP_STRING(ctx->ctx, patterns_p[u].dsc, (*pattern)->dsc);
DUP_STRING(ctx->ctx, patterns_p[u].ref, (*pattern)->ref);
- COMPILE_ARRAY_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts, v, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, patterns_p[u].exts, (*pattern)->exts, (*pattern), LYEXT_PAR_PATTERN, ret, done);
}
done:
return ret;
@@ -2062,7 +2148,7 @@
}
COMPILE_ARRAY_GOTO(ctx, enums_p[u].iffeatures, e->iffeatures, v, lys_compile_iffeature, ret, done);
- COMPILE_ARRAY_GOTO(ctx, enums_p[u].exts, e->exts, v, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, enums_p[u].exts, e->exts, e, basetype == LY_TYPE_ENUM ? LYEXT_PAR_TYPE_ENUM : LYEXT_PAR_TYPE_BIT, ret, done);
if (basetype == LY_TYPE_BITS) {
/* keep bits ordered by position */
@@ -2618,7 +2704,7 @@
LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length));
if (!tpdfname) {
- COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, bin->length->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, bin->length->exts, bin->length, LYEXT_PAR_LENGTH, ret, done);
}
}
@@ -2689,7 +2775,7 @@
LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
base ? ((struct lysc_type_dec*)base)->range : NULL, &dec->range));
if (!tpdfname) {
- COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, dec->range->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, dec->range->exts, dec->range, LYEXT_PAR_RANGE, ret, done);
}
}
@@ -2706,7 +2792,7 @@
LY_CHECK_RET(lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
base ? ((struct lysc_type_str*)base)->length : NULL, &str->length));
if (!tpdfname) {
- COMPILE_ARRAY_GOTO(ctx, type_p->length->exts, str->length->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, type_p->length->exts, str->length->exts, str->length, LYEXT_PAR_LENGTH, ret, done);
}
} else if (base && ((struct lysc_type_str*)base)->length) {
str->length = lysc_range_dup(ctx->ctx, ((struct lysc_type_str*)base)->length);
@@ -2766,7 +2852,7 @@
LY_CHECK_RET(lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
base ? ((struct lysc_type_num*)base)->range : NULL, &num->range));
if (!tpdfname) {
- COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, num->range->exts, u, lys_compile_ext, ret, done);
+ COMPILE_EXTS_GOTO(ctx, type_p->range->exts, num->range->exts, num->range, LYEXT_PAR_RANGE, ret, done);
}
}
@@ -3213,7 +3299,7 @@
LY_CHECK_GOTO(ret, cleanup);
}
- COMPILE_ARRAY_GOTO(ctx, type_p->exts, (*type)->exts, u, lys_compile_ext, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, type_p->exts, (*type)->exts, (*type), LYEXT_PAR_TYPE, ret, cleanup);
cleanup:
ly_set_erase(&tpdf_chain, free);
@@ -3364,12 +3450,12 @@
DUP_STRING(ctx->ctx, action_p->dsc, action->dsc);
DUP_STRING(ctx->ctx, action_p->ref, action->ref);
COMPILE_ARRAY_GOTO(ctx, action_p->iffeatures, action->iffeatures, u, lys_compile_iffeature, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, action_p->exts, action->exts, u, lys_compile_ext, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, action_p->exts, action->exts, action, LYEXT_PAR_NODE, ret, cleanup);
/* input */
lysc_update_path(ctx, (struct lysc_node*)action, "input");
COMPILE_ARRAY_GOTO(ctx, action_p->input.musts, action->input.musts, u, lys_compile_must, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, action_p->input.exts, action->input_exts, u, lys_compile_ext, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, action_p->input.exts, action->input_exts, &action->input, LYEXT_PAR_INPUT, ret, cleanup);
ctx->options |= LYSC_OPT_RPC_INPUT;
LY_LIST_FOR(action_p->input.data, child_p) {
LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)action, uses_status));
@@ -3380,7 +3466,7 @@
/* output */
lysc_update_path(ctx, (struct lysc_node*)action, "output");
COMPILE_ARRAY_GOTO(ctx, action_p->output.musts, action->output.musts, u, lys_compile_must, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, action_p->output.exts, action->output_exts, u, lys_compile_ext, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, action_p->output.exts, action->output_exts, &action->output, LYEXT_PAR_OUTPUT, ret, cleanup);
ctx->options |= LYSC_OPT_RPC_OUTPUT;
LY_LIST_FOR(action_p->output.data, child_p) {
LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node*)action, uses_status));
@@ -3444,8 +3530,8 @@
DUP_STRING(ctx->ctx, notif_p->dsc, notif->dsc);
DUP_STRING(ctx->ctx, notif_p->ref, notif->ref);
COMPILE_ARRAY_GOTO(ctx, notif_p->iffeatures, notif->iffeatures, u, lys_compile_iffeature, ret, cleanup);
- COMPILE_ARRAY_GOTO(ctx, notif_p->exts, notif->exts, u, lys_compile_ext, ret, cleanup);
COMPILE_ARRAY_GOTO(ctx, notif_p->musts, notif->musts, u, lys_compile_must, ret, cleanup);
+ COMPILE_EXTS_GOTO(ctx, notif_p->exts, notif->exts, notif, LYEXT_PAR_NODE, ret, cleanup);
ctx->options |= LYSC_OPT_NOTIFICATION;
LY_LIST_FOR(notif_p->data, child_p) {
@@ -5383,11 +5469,12 @@
(*when)->context = lysc_xpath_context(node);
}
COMPILE_ARRAY_GOTO(ctx, node_p->iffeatures, node->iffeatures, u, lys_compile_iffeature, ret, error);
- COMPILE_ARRAY_GOTO(ctx, node_p->exts, node->exts, u, lys_compile_ext, ret, error);
/* nodetype-specific part */
LY_CHECK_GOTO(node_compile_spec(ctx, node_p, node), error);
+ COMPILE_EXTS_GOTO(ctx, node_p->exts, node->exts, node, LYEXT_PAR_NODE, ret, error);
+
/* inherit LYS_MAND_TRUE in parent containers */
if (node->flags & LYS_MAND_TRUE) {
lys_compile_mandatory_parents(parent, 1);
@@ -6520,8 +6607,12 @@
/* features are compiled directly into the compiled module structure,
* but it must be done in two steps to allow forward references (via if-feature) between the features themselves.
* The features compilation is finished in the main module (lys_compile()). */
- ret = lys_feature_precompile(ctx, NULL, NULL, submod->features,
- mainmod->mod->off_features ? &mainmod->mod->off_features : &mainmod->features);
+ ret = lys_feature_precompile(ctx, NULL, NULL, submod->features, &mainmod->features);
+ LY_CHECK_GOTO(ret, error);
+ }
+ if (!mainmod->mod->off_extensions) {
+ /* extensions are compiled directly into the compiled module structure, compilation is finished in the main module (lys_compile()). */
+ ret = lys_extension_precompile(ctx, NULL, NULL, submod->extensions, &mainmod->extensions);
LY_CHECK_GOTO(ret, error);
}
@@ -6581,6 +6672,8 @@
ret = lys_compile_submodule(&ctx, &sp->includes[u]);
LY_CHECK_GOTO(ret != LY_SUCCESS, error);
}
+
+ /* features */
if (mod->off_features) {
/* there is already precompiled array of features */
mod_c->features = mod->off_features;
@@ -6609,6 +6702,20 @@
}
lysc_update_path(&ctx, NULL, NULL);
+ /* extensions */
+ /* 2-steps: a) prepare compiled structures and ... */
+ if (mod->off_extensions) {
+ /* there is already precompiled array of extension definitions */
+ mod_c->extensions = mod->off_extensions;
+ mod->off_extensions = NULL;
+ } else {
+ /* extension definitions are compiled directly into the compiled module structure */
+ ret = lys_extension_precompile(&ctx, NULL, NULL, sp->extensions, &mod_c->extensions);
+ }
+ /* ... b) connect the extension definitions with the appropriate extension plugins */
+ lys_compile_extension_plugins(mod_c->extensions);
+
+ /* identities */
lysc_update_path(&ctx, NULL, "{identity}");
COMPILE_ARRAY_UNIQUE_GOTO(&ctx, sp->identities, mod_c->identities, u, lys_compile_identity, ret, error);
if (sp->identities) {
@@ -6637,7 +6744,8 @@
ret = lys_compile_deviations(&ctx, sp);
LY_CHECK_GOTO(ret, error);
- COMPILE_ARRAY_GOTO(&ctx, sp->exts, mod_c->exts, u, lys_compile_ext, ret, error);
+ /* extension instances TODO cover extension instances from submodules */
+ COMPILE_EXTS_GOTO(&ctx, sp->exts, mod_c->exts, mod_c, LYEXT_PAR_MODULE, ret, error);
/* validate leafref's paths and when/must xpaths */
/* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 20112ca..68c8252 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -472,6 +472,14 @@
}
void
+lysc_extension_free(struct ly_ctx *ctx, struct lysc_ext *ext)
+{
+ FREE_STRING(ctx, ext->name);
+ FREE_STRING(ctx, ext->argument);
+ FREE_ARRAY(ctx, ext->exts, lysc_ext_instance_free);
+}
+
+void
lysc_iffeature_free(struct ly_ctx *UNUSED(ctx), struct lysc_iffeature *iff)
{
LY_ARRAY_FREE(iff->features);
@@ -822,6 +830,7 @@
FREE_ARRAY(ctx, module->rpcs, lysc_action_free);
FREE_ARRAY(ctx, module->notifs, lysc_notif_free);
+ FREE_ARRAY(ctx, module->extensions, lysc_extension_free);
FREE_ARRAY(ctx, module->exts, lysc_ext_instance_free);
free(module);
@@ -847,6 +856,7 @@
lysc_module_free(module->compiled, private_destructor);
FREE_ARRAY(module->ctx, module->off_features, lysc_feature_free);
+ FREE_ARRAY(module->ctx, module->off_extensions, lysc_extension_free);
lysp_module_free(module->parsed);
FREE_STRING(module->ctx, module->name);
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 5e931a7..46d7801 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -26,9 +26,9 @@
#include "context.h"
#include "dict.h"
-#include "extensions.h"
#include "hash_table.h"
#include "log.h"
+#include "plugins_exts.h"
#include "set.h"
#include "tree.h"
#include "tree_schema.h"
@@ -1074,7 +1074,7 @@
return NULL;
}
-const char *
+API const char *
lys_nodetype2str(uint16_t nodetype)
{
switch(nodetype) {
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 33f5c7c..1ff883d 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -17,6 +17,7 @@
#include <stdint.h>
+#include "plugins_exts.h"
#include "set.h"
#include "tree_schema.h"
#include "xml.h"
@@ -127,26 +128,6 @@
};
/**
- * @brief internal context for compilation
- */
-struct lysc_ctx {
- struct ly_ctx *ctx;
- struct lys_module *mod;
- struct lys_module *mod_def; /**< context module for the definitions of the nodes being currently
- processed - groupings are supposed to be evaluated in place where
- defined, but its content instances are supposed to be placed into
- the target module (mod) */
- struct ly_set groupings; /**< stack for groupings circular check */
- struct ly_set unres; /**< to validate leafref's target and xpath of when/must */
- struct ly_set dflts; /**< set of incomplete default values */
- struct ly_set tpdf_chain;
- uint16_t path_len;
- int options; /**< various @ref scflags. */
-#define LYSC_CTX_BUFSIZE 4078
- char path[LYSC_CTX_BUFSIZE];
-};
-
-/**
* @brief Check that \p c is valid UTF8 code point for YANG string.
*
* @param[in] ctx yang parser context for logging.
@@ -494,13 +475,6 @@
const char *lys_prefix_find_module(const struct lys_module *mod, const struct lys_module *import);
/**
- * @brief Stringify schema nodetype.
- * @param[in] nodetype Nodetype to stringify.
- * @return Constant string with the name of the node's type.
- */
-const char *lys_nodetype2str(uint16_t nodetype);
-
-/**
* @brief Stringify YANG built-in type.
* @param[in] basetype Built-in tyep ID to stringify.
* @return Constant string with the name of the built-in type.
@@ -658,13 +632,14 @@
* @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p module.
* @param[in] ctx libyang context.
* @param[in] module Module of the features.
- * @param[in] features_p Array if the parsed features definitions to precompile.
+ * @param[in] features_p Array of the parsed features definitions to precompile.
* @param[in,out] features Pointer to the storage of the (pre)compiled features array where the new features are
* supposed to be added. The storage is supposed to be initiated to NULL when the first parsed features are going
* to be processed.
* @return LY_ERR value.
*/
-LY_ERR lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module, struct lysp_feature *features_p, struct lysc_feature **features);
+LY_ERR lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_feature *features_p, struct lysc_feature **features);
/**
* @brief Get the @ref ifftokens from the given position in the 2bits array
@@ -675,6 +650,21 @@
uint8_t lysc_iff_getop(uint8_t *list, int pos);
/**
+ * @brief Internal wrapper around lys_compile_extension() to be able to prepare list of compiled extension definition
+ * even for the parsed (not-implemented) module - see lys_module::off_extensions.
+ *
+ * @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p module.
+ * @param[in] ctx libyang context.
+ * @param[in] module Module of the extensions.
+ * @param[in] extensions_p Array of the parsed extension definitions to precompile.
+ * @param[in,out] extensions Pointer to the storage of the (pre)compiled extensions array where the new extensions are
+ * supposed to be added. The storage is supposed to be initiated to NULL when the first parsed extensions are going
+ * to be processed.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_extension_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lys_module *module,
+ struct lysp_ext *extensions_p, struct lysc_ext **extensions);
+/**
* @brief Macro to free [sized array](@ref sizedarrays) of items using the provided free function. The ARRAY itself is also freed,
* but the memory is not sanitized.
*/
diff --git a/tests/config.h.in b/tests/config.h.in
index a7a5b7e..5943c07 100644
--- a/tests/config.h.in
+++ b/tests/config.h.in
@@ -3,7 +3,7 @@
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief cmocka tests configuration header.
*
- * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2098 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.
@@ -19,4 +19,6 @@
#define TESTS_SRC "@CMAKE_CURRENT_SOURCE_DIR@"
#define TESTS_BIN "@CMAKE_CURRENT_BINARY_DIR@"
+#define TESTS_DIR_MODULES_YANG TESTS_SRC"/modules/yang"
+
#endif /* LYTEST_CONFIG_H_ */
diff --git a/tests/features/CMakeLists.txt b/tests/features/CMakeLists.txt
index 00f3423..20c5d94 100644
--- a/tests/features/CMakeLists.txt
+++ b/tests/features/CMakeLists.txt
@@ -1,6 +1,8 @@
set(local_tests
- features_types)
+ features_types
+ features_nacm)
set(local_tests_wraps
+ " "
" ")
set(tests ${tests} ${local_tests} PARENT_SCOPE)
set(tests_wraps ${tests_wraps} ${local_tests_wraps} PARENT_SCOPE)
diff --git a/tests/features/test_nacm.c b/tests/features/test_nacm.c
new file mode 100644
index 0000000..87882da
--- /dev/null
+++ b/tests/features/test_nacm.c
@@ -0,0 +1,204 @@
+/*
+ * @file test_nacm.c
+ * @author: Radek Krejci <rkrejci@cesnet.cz>
+ * @brief unit tests for NACM extensions support
+ *
+ * Copyright (c) 2019 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/libyang.h"
+
+#define BUFSIZE 1024
+char logbuf[BUFSIZE] = {0};
+int store = -1; /* negative for infinite logging, positive for limited logging */
+
+struct state_s {
+ void *func;
+ struct ly_ctx *ctx;
+};
+
+/* 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 int
+setup(void **state)
+{
+ struct state_s *s;
+
+ s = calloc(1, sizeof *s);
+ assert_non_null(s);
+
+#if ENABLE_LOGGER_CHECKING
+ ly_set_log_clb(logger, 1);
+#endif
+
+ assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &s->ctx));
+ assert_non_null(ly_ctx_load_module(s->ctx, "ietf-netconf-acm", "2018-02-14"));
+
+ *state = s;
+
+ return 0;
+}
+
+static int
+teardown(void **state)
+{
+ struct state_s *s = (struct state_s*)(*state);
+
+#if ENABLE_LOGGER_CHECKING
+ if (s->func) {
+ fprintf(stderr, "%s\n", logbuf);
+ }
+#endif
+
+ ly_ctx_destroy(s->ctx, NULL);
+ free(s);
+
+ 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_deny_all(void **state)
+{
+ struct state_s *s = (struct state_s*)(*state);
+ s->func = test_deny_all;
+
+ struct lys_module *mod;
+ struct lysc_node_container *cont;
+ struct lysc_node_leaf *leaf;
+ struct lysc_ext_instance *e;
+
+ const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:nacm:a; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "container a { nacm:default-deny-all; leaf aa {type string;}}"
+ "leaf b {type string;}}";
+
+ /* valid data */
+ assert_non_null(mod = lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+ assert_non_null(cont = (struct lysc_node_container*)mod->compiled->data);
+ assert_non_null(leaf = (struct lysc_node_leaf*)cont->child);
+ assert_non_null(e = &cont->exts[0]);
+ assert_int_equal(LY_ARRAY_SIZE(cont->exts), 1);
+ assert_int_equal(LY_ARRAY_SIZE(leaf->exts), 1); /* NACM extensions inherit */
+ assert_ptr_equal(e->def, leaf->exts[0].def);
+ assert_int_equal(1, *((uint8_t*)e->data)); /* plugin's value for default-deny-all */
+ assert_null(cont->next->exts);
+
+ /* invalid */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "nacm:default-deny-all;}";
+ assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+ logbuf_assert("Extension plugin \"libyang 2 - NACM, version 1\": "
+ "Extension nacm:default-deny-all is allowed only in a data nodes, but it is placed in \"module\" statement.) /");
+
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "leaf l { type string; nacm:default-deny-all; nacm:default-deny-write;}}";
+ assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+ logbuf_assert("Extension plugin \"libyang 2 - NACM, version 1\": "
+ "Extension nacm:default-deny-write is mixed with nacm:default-deny-all.) /aa:l");
+
+ s->func = NULL;
+}
+
+static void
+test_deny_write(void **state)
+{
+ struct state_s *s = (struct state_s*)(*state);
+ s->func = test_deny_write;
+
+ struct lys_module *mod;
+ struct lysc_node_container *cont;
+ struct lysc_node_leaf *leaf;
+ struct lysc_ext_instance *e;
+
+ const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:nacm:a; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "container a { nacm:default-deny-write; leaf aa {type string;}}"
+ "leaf b {type string;}}";
+
+ /* valid data */
+ assert_non_null(mod = lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+ assert_non_null(cont = (struct lysc_node_container*)mod->compiled->data);
+ assert_non_null(leaf = (struct lysc_node_leaf*)cont->child);
+ assert_non_null(e = &cont->exts[0]);
+ assert_int_equal(LY_ARRAY_SIZE(cont->exts), 1);
+ assert_int_equal(LY_ARRAY_SIZE(leaf->exts), 1); /* NACM extensions inherit */
+ assert_ptr_equal(e->def, leaf->exts[0].def);
+ assert_int_equal(2, *((uint8_t*)e->data)); /* plugin's value for default-deny-write */
+ assert_null(cont->next->exts);
+
+ /* invalid */
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "notification notif {nacm:default-deny-write;}}";
+ assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+ logbuf_assert("Extension plugin \"libyang 2 - NACM, version 1\": "
+ "Extension nacm:default-deny-write is not allowed in Notification statement.) /aa:notif");
+
+ data = "module aa {yang-version 1.1; namespace urn:tests:extensions:nacm:aa; prefix en;"
+ "import ietf-netconf-acm {revision-date 2018-02-14; prefix nacm;}"
+ "leaf l { type string; nacm:default-deny-write; nacm:default-deny-write;}}";
+ assert_null(lys_parse_mem(s->ctx, data, LYS_IN_YANG));
+ logbuf_assert("Extension plugin \"libyang 2 - NACM, version 1\": "
+ "Extension nacm:default-deny-write is instantiated multiple times.) /aa:l");
+
+ s->func = NULL;
+}
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(test_deny_all, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_deny_write, setup, teardown),
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
index b60b4eb..f4dd506 100644
--- a/tests/features/test_types.c
+++ b/tests/features/test_types.c
@@ -1,7 +1,7 @@
/*
- * @file test_parser_xml.c
+ * @file test_types.c
* @author: Radek Krejci <rkrejci@cesnet.cz>
- * @brief unit tests for functions from parser_xml.c
+ * @brief unit tests for support of YANG data types
*
* Copyright (c) 2019 CESNET, z.s.p.o.
*
diff --git a/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang b/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang
new file mode 100644
index 0000000..bf4855f
--- /dev/null
+++ b/tests/modules/yang/ietf-netconf-acm@2018-02-14.yang
@@ -0,0 +1,464 @@
+module ietf-netconf-acm {
+
+ namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
+
+ prefix nacm;
+
+ import ietf-yang-types {
+ prefix yang;
+ }
+
+ organization
+ "IETF NETCONF (Network Configuration) Working Group";
+
+ contact
+ "WG Web: <https://datatracker.ietf.org/wg/netconf/>
+ WG List: <mailto:netconf@ietf.org>
+
+ Author: Andy Bierman
+ <mailto:andy@yumaworks.com>
+
+ Author: Martin Bjorklund
+ <mailto:mbj@tail-f.com>";
+
+ description
+ "Network Configuration Access Control Model.
+
+ Copyright (c) 2012 - 2018 IETF Trust and the persons
+ identified as authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, is permitted pursuant to, and subject
+ to the license terms contained in, the Simplified BSD
+ License set forth in Section 4.c of the IETF Trust's
+ Legal Provisions Relating to IETF Documents
+ (https://trustee.ietf.org/license-info).
+
+ This version of this YANG module is part of RFC 8341; see
+ the RFC itself for full legal notices.";
+
+ revision "2018-02-14" {
+ description
+ "Added support for YANG 1.1 actions and notifications tied to
+ data nodes. Clarified how NACM extensions can be used by
+ other data models.";
+ reference
+ "RFC 8341: Network Configuration Access Control Model";
+ }
+
+ revision "2012-02-22" {
+ description
+ "Initial version.";
+ reference
+ "RFC 6536: Network Configuration Protocol (NETCONF)
+ Access Control Model";
+ }
+
+ /*
+ * Extension statements
+ */
+
+ extension default-deny-write {
+ description
+ "Used to indicate that the data model node
+ represents a sensitive security system parameter.
+
+ If present, the NETCONF server will only allow the designated
+ 'recovery session' to have write access to the node. An
+ explicit access control rule is required for all other users.
+
+ If the NACM module is used, then it must be enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), or this extension
+ is ignored.
+
+ The 'default-deny-write' extension MAY appear within a data
+ definition statement. It is ignored otherwise.";
+ }
+
+ extension default-deny-all {
+ description
+ "Used to indicate that the data model node
+ controls a very sensitive security system parameter.
+
+ If present, the NETCONF server will only allow the designated
+ 'recovery session' to have read, write, or execute access to
+ the node. An explicit access control rule is required for all
+ other users.
+
+ If the NACM module is used, then it must be enabled (i.e.,
+ /nacm/enable-nacm object equals 'true'), or this extension
+ is ignored.
+
+ The 'default-deny-all' extension MAY appear within a data
+ definition statement, 'rpc' statement, or 'notification'
+ statement. It is ignored otherwise.";
+ }
+
+ /*
+ * Derived types
+ */
+
+ typedef user-name-type {
+ type string {
+ length "1..max";
+ }
+ description
+ "General-purpose username string.";
+ }
+
+ typedef matchall-string-type {
+ type string {
+ pattern '\*';
+ }
+ description
+ "The string containing a single asterisk '*' is used
+ to conceptually represent all possible values
+ for the particular leaf using this data type.";
+ }
+
+ typedef access-operations-type {
+ type bits {
+ bit create {
+ description
+ "Any protocol operation that creates a
+ new data node.";
+ }
+ bit read {
+ description
+ "Any protocol operation or notification that
+ returns the value of a data node.";
+ }
+ bit update {
+ description
+ "Any protocol operation that alters an existing
+ data node.";
+ }
+ bit delete {
+ description
+ "Any protocol operation that removes a data node.";
+ }
+ bit exec {
+ description
+ "Execution access to the specified protocol operation.";
+ }
+ }
+ description
+ "Access operation.";
+ }
+
+ typedef group-name-type {
+ type string {
+ length "1..max";
+ pattern '[^\*].*';
+ }
+ description
+ "Name of administrative group to which
+ users can be assigned.";
+ }
+
+ typedef action-type {
+ type enumeration {
+ enum permit {
+ description
+ "Requested action is permitted.";
+ }
+ enum deny {
+ description
+ "Requested action is denied.";
+ }
+ }
+ description
+ "Action taken by the server when a particular
+ rule matches.";
+ }
+
+ typedef node-instance-identifier {
+ type yang:xpath1.0;
+ description
+ "Path expression used to represent a special
+ data node, action, or notification instance-identifier
+ string.
+
+ A node-instance-identifier value is an
+ unrestricted YANG instance-identifier expression.
+ All the same rules as an instance-identifier apply,
+ except that predicates for keys are optional. If a key
+ predicate is missing, then the node-instance-identifier
+ represents all possible server instances for that key.
+
+ This XML Path Language (XPath) expression is evaluated in the
+ following context:
+
+ o The set of namespace declarations are those in scope on
+ the leaf element where this type is used.
+
+ o The set of variable bindings contains one variable,
+ 'USER', which contains the name of the user of the
+ current session.
+
+ o The function library is the core function library, but
+ note that due to the syntax restrictions of an
+ instance-identifier, no functions are allowed.
+
+ o The context node is the root node in the data tree.
+
+ The accessible tree includes actions and notifications tied
+ to data nodes.";
+ }
+
+ /*
+ * Data definition statements
+ */
+
+ container nacm {
+ nacm:default-deny-all;
+
+ description
+ "Parameters for NETCONF access control model.";
+
+ leaf enable-nacm {
+ type boolean;
+ default "true";
+ description
+ "Enables or disables all NETCONF access control
+ enforcement. If 'true', then enforcement
+ is enabled. If 'false', then enforcement
+ is disabled.";
+ }
+
+ leaf read-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether read access is granted if
+ no appropriate rule is found for a
+ particular read request.";
+ }
+
+ leaf write-default {
+ type action-type;
+ default "deny";
+ description
+ "Controls whether create, update, or delete access
+ is granted if no appropriate rule is found for a
+ particular write request.";
+ }
+
+ leaf exec-default {
+ type action-type;
+ default "permit";
+ description
+ "Controls whether exec access is granted if no appropriate
+ rule is found for a particular protocol operation request.";
+ }
+
+ leaf enable-external-groups {
+ type boolean;
+ default "true";
+ description
+ "Controls whether the server uses the groups reported by the
+ NETCONF transport layer when it assigns the user to a set of
+ NACM groups. If this leaf has the value 'false', any group
+ names reported by the transport layer are ignored by the
+ server.";
+ }
+
+ leaf denied-operations {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request was denied.";
+ }
+
+ leaf denied-data-writes {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that a
+ protocol operation request to alter
+ a configuration datastore was denied.";
+ }
+
+ leaf denied-notifications {
+ type yang:zero-based-counter32;
+ config false;
+ mandatory true;
+ description
+ "Number of times since the server last restarted that
+ a notification was dropped for a subscription because
+ access to the event type was denied.";
+ }
+
+ container groups {
+ description
+ "NETCONF access control groups.";
+
+ list group {
+ key name;
+
+ description
+ "One NACM group entry. This list will only contain
+ configured entries, not any entries learned from
+ any transport protocols.";
+
+ leaf name {
+ type group-name-type;
+ description
+ "Group name associated with this entry.";
+ }
+
+ leaf-list user-name {
+ type user-name-type;
+ description
+ "Each entry identifies the username of
+ a member of the group associated with
+ this entry.";
+ }
+ }
+ }
+
+ list rule-list {
+ key name;
+ ordered-by user;
+ description
+ "An ordered collection of access control rules.";
+
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule-list.";
+ }
+ leaf-list group {
+ type union {
+ type matchall-string-type;
+ type group-name-type;
+ }
+ description
+ "List of administrative groups that will be
+ assigned the associated access rights
+ defined by the 'rule' list.
+
+ The string '*' indicates that all groups apply to the
+ entry.";
+ }
+
+ list rule {
+ key name;
+ ordered-by user;
+ description
+ "One access control rule.
+
+ Rules are processed in user-defined order until a match is
+ found. A rule matches if 'module-name', 'rule-type', and
+ 'access-operations' match the request. If a rule
+ matches, the 'action' leaf determines whether or not
+ access is granted.";
+
+ leaf name {
+ type string {
+ length "1..max";
+ }
+ description
+ "Arbitrary name assigned to the rule.";
+ }
+
+ leaf module-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ default "*";
+ description
+ "Name of the module associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ object being accessed is defined in the module with the
+ specified module name.";
+ }
+ choice rule-type {
+ description
+ "This choice matches if all leafs present in the rule
+ match the request. If no leafs are present, the
+ choice matches all requests.";
+ case protocol-operation {
+ leaf rpc-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if
+ its value equals the requested protocol operation
+ name.";
+ }
+ }
+ case notification {
+ leaf notification-name {
+ type union {
+ type matchall-string-type;
+ type string;
+ }
+ description
+ "This leaf matches if it has the value '*' or if its
+ value equals the requested notification name.";
+ }
+ }
+
+ case data-node {
+ leaf path {
+ type node-instance-identifier;
+ mandatory true;
+ description
+ "Data node instance-identifier associated with the
+ data node, action, or notification controlled by
+ this rule.
+
+ Configuration data or state data
+ instance-identifiers start with a top-level
+ data node. A complete instance-identifier is
+ required for this type of path value.
+
+ The special value '/' refers to all possible
+ datastore contents.";
+ }
+ }
+ }
+
+ leaf access-operations {
+ type union {
+ type matchall-string-type;
+ type access-operations-type;
+ }
+ default "*";
+ description
+ "Access operations associated with this rule.
+
+ This leaf matches if it has the value '*' or if the
+ bit corresponding to the requested operation is set.";
+ }
+
+ leaf action {
+ type action-type;
+ mandatory true;
+ description
+ "The access control action associated with the
+ rule. If a rule has been determined to match a
+ particular request, then this object is used
+ to determine whether to permit or deny the
+ request.";
+ }
+
+ leaf comment {
+ type string;
+ description
+ "A textual description of the access rule.";
+ }
+ }
+ }
+ }
+}