types FEATURE support identityref data type values
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 3d86181..cf85cf7 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -47,6 +47,24 @@
};
/**
+ * @brief XML-parser's implementation of ly_type_resolve_prefix() callback to provide mapping between prefixes used in the values to the schema
+ * via XML namespaces.
+ */
+static const struct lys_module *
+lydxml_resolve_prefix(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser)
+{
+ const struct lyxml_ns *ns;
+ struct lyxml_context *xmlctx = (struct lyxml_context*)parser;
+
+ ns = lyxml_ns_get(xmlctx, prefix, prefix_len);
+ if (!ns) {
+ return NULL;
+ }
+
+ return ly_ctx_get_module_implemented_ns(ctx, ns->uri);
+}
+
+/**
* @brief Parse XML attributes of the XML element of YANG data.
*
* @param[in] ctx XML YANG data parser context.
@@ -235,7 +253,7 @@
value = "";
value_len = 0;
}
- LY_CHECK_ERR_GOTO(ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic),
+ LY_CHECK_ERR_GOTO(ret = lyd_value_parse((struct lyd_node_term*)cur, value, value_len, dynamic, lydxml_resolve_prefix, ctx),
if (dynamic){free(value);}, cleanup);
} else if (snode->nodetype & LYD_NODE_INNER) {
int dynamic = 0;
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 4e29fb0..4a80a51 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -357,7 +357,7 @@
*/
static LY_ERR
ly_type_validate_int(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **priv)
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
{
LY_ERR ret;
int64_t i;
@@ -445,7 +445,7 @@
*/
static LY_ERR
ly_type_validate_uint(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **priv)
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
{
LY_ERR ret;
uint64_t u;
@@ -515,7 +515,7 @@
*/
static LY_ERR
ly_type_validate_decimal64(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **priv)
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
{
int64_t d;
struct lysc_type_dec* type_dec = (struct lysc_type_dec*)type;
@@ -613,7 +613,7 @@
*/
static LY_ERR
ly_type_validate_binary(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
{
size_t start = 0, stop = 0, count = 0, u, termination = 0;
struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
@@ -707,13 +707,10 @@
*/
static LY_ERR
ly_type_validate_string(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
{
struct lysc_type_str *type_str = (struct lysc_type_str *)type;
- /* initiate */
- *err = NULL;
-
/* length restriction of the string */
if (type_str->length) {
char buf[22];
@@ -747,7 +744,7 @@
*/
static LY_ERR
ly_type_validate_bits(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **priv)
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
{
LY_ERR ret = LY_EVALID;
size_t item_len;
@@ -939,7 +936,7 @@
*/
static LY_ERR
ly_type_validate_enum(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **priv)
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
{
unsigned int u, v;
char *errmsg = NULL;
@@ -1022,7 +1019,7 @@
*/
static LY_ERR
ly_type_validate_boolean(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **priv)
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **priv)
{
int8_t i;
@@ -1091,7 +1088,7 @@
*/
static LY_ERR
ly_type_validate_empty(struct ly_ctx *ctx, struct lysc_type *UNUSED(type), const char *value, size_t value_len, int options,
- const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
+ ly_type_resolve_prefix UNUSED(get_prefix), void *UNUSED(parser), const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
{
if (value_len) {
char *errmsg;
@@ -1106,6 +1103,131 @@
return LY_SUCCESS;
}
+API LY_ERR
+ly_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *der)
+{
+ unsigned int u;
+
+ LY_ARRAY_FOR(base->derived, u) {
+ if (der == base->derived[u]) {
+ return LY_SUCCESS;
+ }
+ if (!ly_type_identity_isderived(base->derived[u], der)) {
+ return LY_SUCCESS;
+ }
+ }
+ return LY_ENOTFOUND;
+}
+
+/**
+ * @brief Validate value of the YANG built-in identiytref type.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_identityref(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ ly_type_resolve_prefix get_prefix, void *parser, const char **canonized, struct ly_err_item **err, void **priv)
+{
+ struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
+ const char *id_name, *prefix = value;
+ size_t id_len, prefix_len;
+ char *errmsg = NULL;
+ const struct lys_module *mod;
+ unsigned int u;
+ struct lysc_ident *ident;
+
+ /* locate prefix if any */
+ for (prefix_len = 0; prefix_len < value_len && value[prefix_len] != ':'; ++prefix_len);
+ if (prefix_len < value_len) {
+ id_name = &value[prefix_len + 1];
+ id_len = value_len - (prefix_len + 1);
+ } else {
+ prefix_len = 0;
+ id_name = value;
+ id_len = value_len;
+ }
+
+ if (!id_len) {
+ errmsg = strdup("Invalid empty identityref value.");
+ goto error;
+ }
+
+ mod = get_prefix(ctx, prefix, prefix_len, parser);
+ if (!mod) {
+ asprintf(&errmsg, "Invalid identityref \"%.*s\" value - unable to map prefix to YANG schema.", (int)value_len, value);
+ goto error;
+ }
+ LY_ARRAY_FOR(mod->compiled->identities, u) {
+ ident = &mod->compiled->identities[u]; /* shortcut */
+ if (!strncmp(ident->name, id_name, id_len) && ident->name[id_len] == '\0') {
+ /* we have match */
+ break;
+ }
+ }
+ if (u == LY_ARRAY_SIZE(mod->compiled->identities)) {
+ /* no match */
+ asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity not found.", (int)value_len, value);
+ goto error;
+ }
+
+ /* check that the identity matches some of the type's base identities */
+ LY_ARRAY_FOR(type_ident->bases, u) {
+ if (!ly_type_identity_isderived(type_ident->bases[u], ident)) {
+ /* we have match */
+ break;
+ }
+ }
+ if (u == LY_ARRAY_SIZE(type_ident->bases)) {
+ /* no match */
+ asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity not accepted by the type specification.", (int)value_len, value);
+ goto error;
+ }
+
+ if (options & LY_TYPE_OPTS_CANONIZE) {
+ if (id_name == value && (options & LY_TYPE_OPTS_DYNAMIC)) {
+ *canonized = lydict_insert_zc(ctx, (char*)value);
+ value = NULL;
+ } else {
+ *canonized = lydict_insert(ctx, id_name, id_len);
+ }
+ }
+
+ if (options & LY_TYPE_OPTS_STORE) {
+ *priv = ident;
+ }
+
+ if (options & LY_TYPE_OPTS_DYNAMIC) {
+ free((char*)value);
+ }
+
+ return LY_SUCCESS;
+
+error:
+ *err = ly_err_new(LY_LLERR, LY_EVALID, LYVE_RESTRICTION, errmsg, NULL, NULL);
+ return LY_EVALID;
+}
+
+/**
+ * @brief Store value of the YANG built-in identityref type.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_identityref(struct ly_ctx *UNUSED(ctx), struct lysc_type *UNUSED(type), int options,
+ struct lyd_value *value, struct ly_err_item **UNUSED(err), void **priv)
+{
+ if (options & LY_TYPE_OPTS_VALIDATE) {
+ /* the value was prepared by ly_type_validate_enum() */
+ value->ident = *priv;
+ } else {
+ /* TODO if there is usecase for store without validate */
+ LOGINT(NULL);
+ return LY_EINT;
+ }
+
+ return LY_SUCCESS;
+}
+
struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
{0}, /* LY_TYPE_UNKNOWN */
{.type = LY_TYPE_BINARY, .validate = ly_type_validate_binary, .store = NULL, .free = NULL},
@@ -1119,7 +1241,7 @@
{.type = LY_TYPE_DEC64, .validate = ly_type_validate_decimal64, .store = ly_type_store_decimal64, .free = NULL},
{.type = LY_TYPE_EMPTY, .validate = ly_type_validate_empty, .store = NULL, .free = NULL},
{.type = LY_TYPE_ENUM, .validate = ly_type_validate_enum, .store = ly_type_store_enum, .free = NULL},
- {0}, /* TODO LY_TYPE_IDENT */
+ {.type = LY_TYPE_IDENT, .validate = ly_type_validate_identityref, .store = ly_type_store_identityref, .free = NULL},
{0}, /* TODO LY_TYPE_INST */
{0}, /* TODO LY_TYPE_LEAFREF */
{0}, /* TODO LY_TYPE_UNION */
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 9c48fd8..a4cbfbb 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -78,6 +78,20 @@
*/
/**
+ * @brief Callback provided by the data parsers to type plugins to resolve (format-specific) mapping between prefixes used in the value strings
+ * to the YANG schemas.
+ *
+ * XML uses XML namespaces, JSON uses schema names as prefixes.
+ *
+ * @param[in] ctx libyang context to find the schema.
+ * @param[in] prefix Prefix found in the value string
+ * @param[in] prefix_len Length of the @p prefix.
+ * @param[in] parser Internal parser's data.
+ * @return Pointer to the YANG schema identified by the provided prefix or NULL if no mapping found.
+ */
+typedef const struct lys_module *(*ly_type_resolve_prefix)(struct ly_ctx *ctx, const char *prefix, size_t prefix_len, void *parser);
+
+/**
* @brief Callback to validate the given @p value according to the given @p type. Optionaly, it can be requested to canonize the value.
*
* Note that the \p value string is not necessarily zero-terminated. The provided \p value_len is always correct.
@@ -87,6 +101,10 @@
* @param[in] value Lexical representation of the value to be validated (and canonized).
* @param[in] value_len Length of the given \p value.
* @param[in] options [Type plugin options ](@ref plugintypeopts).
+ *
+ * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the value strings.
+ * @param[in] parser Parser's data for @p get_prefix
+ *
* @param[out] canonized If LY_TYPE_VALIDATE_CANONIZE option set, the canonized string stored in the @p ctx dictionary is returned via this parameter.
* @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic error message is prepared instead.
* The error structure can be created by ly_err_new().
@@ -95,6 +113,7 @@
* @return LY_ERR value if an error occurred and the value could not be canonized following the type's rules.
*/
typedef LY_ERR (*ly_type_validate_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+ ly_type_resolve_prefix get_prefix, void *parser,
const char **canonized, struct ly_err_item **err, void **priv);
/**
@@ -191,6 +210,16 @@
LY_ERR parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err);
/**
+ * @brief Decide if the @p derived identity is derived from (based on) the @p base identity.
+ *
+ * @param[in] base Expected base identity.
+ * @param[in] derived Expected derived identity.
+ * @return LY_SUCCESS if @p derived IS based on the @p base identity.
+ * @return LY_ENOTFOUND if @p derived IS NOT not based on the @p base identity.
+ */
+LY_ERR ly_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *derived);
+
+/**
* @brief Data type validator for a range/length-restricted values.
*
* @param[in] basetype Base built-in type of the type with the range specified to get know if the @p range structure represents range or length restriction.
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index fb9c3da..36f5ce7 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -84,7 +84,8 @@
}
LY_ERR
-lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic)
+lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic,
+ ly_type_resolve_prefix get_prefix, void *parser)
{
LY_ERR ret = LY_SUCCESS;
struct ly_err_item *err = NULL;
@@ -97,7 +98,7 @@
ctx = node->schema->module->ctx;
type = ((struct lysc_node_leaf*)node->schema)->type;
if (type->plugin->validate) {
- ret = type->plugin->validate(ctx, type, value, value_len, options, &node->value.canonized, &err, &priv);
+ ret = type->plugin->validate(ctx, type, value, value_len, options, get_prefix, parser, &node->value.canonized, &err, &priv);
if (ret) {
ly_err_print(err);
LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index ec11578..d6225dc 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -16,6 +16,7 @@
#define LY_TREE_DATA_INTERNAL_H_
#include "tree_data.h"
+#include "plugins_types.h"
/**
* @brief Get address of a node's child pointer if any.
@@ -44,9 +45,11 @@
* @param[in] value String value to be parsed.
* @param[in] value_len Length of the give @p value (mandatory).
* @param[in] dynamic Flag if @p value is a dynamically allocated memory and should be directly consumed/freed inside the function.
+ * @param[in] get_prefix Parser-specific getter to resolve prefixes used in the value strings.
+ * @param[in] parser Parser's data for @p get_prefix
* @return LY_ERR value.
*/
-LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic);
+LY_ERR lyd_value_parse(struct lyd_node_term *node, const char *value, size_t value_len, int dynamic, ly_type_resolve_prefix get_prefix, void *parser);
/**
* @brief Parse XML string as YANG data tree.
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 2d73a01..160153b 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -710,7 +710,7 @@
/**
* @brief Find and process the referenced base identities from another identity or identityref
*
- * For bases in identity se backlinks to them from the base identities. For identityref, store
+ * For bases in identity set backlinks to them from the base identities. For identityref, store
* the array of pointers to the base identities. So one of the ident or bases parameter must be set
* to distinguish these two use cases.
*
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
index faf6d70..1d0ad79 100644
--- a/tests/features/test_types.c
+++ b/tests/features/test_types.c
@@ -56,7 +56,10 @@
setup(void **state)
{
struct state_s *s;
- const char *schema_a = "module types {namespace urn:tests:types;prefix t;yang-version 1.1; feature f;"
+ const char *schema_a = "module defs {namespace urn:tests:defs;prefix d;yang-version 1.1;"
+ "identity crypto-alg; identity interface-type; identity ethernet {base interface-type;} identity fast-ethernet {base ethernet;}}";
+ const char *schema_b = "module types {namespace urn:tests:types;prefix t;yang-version 1.1; import defs {prefix defs;}"
+ "feature f; identity gigabit-ethernet { base defs:ethernet;}"
"leaf binary {type binary {length 5 {error-message \"This base64 value must be of length 5.\";}}}"
"leaf binary-norestr {type binary;}"
"leaf int8 {type int8 {range 10..20;}}"
@@ -74,7 +77,8 @@
"leaf str {type string {length 8..10; pattern '[a-z ]*';}}"
"leaf str-norestr {type string;}"
"leaf bool {type boolean;}"
- "leaf empty {type empty;}}";
+ "leaf empty {type empty;}"
+ "leaf ident {type identityref {base defs:interface-type;}}}";
s = calloc(1, sizeof *s);
assert_non_null(s);
@@ -85,6 +89,7 @@
assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &s->ctx));
assert_non_null(lys_parse_mem(s->ctx, schema_a, LYS_IN_YANG));
+ assert_non_null(lys_parse_mem(s->ctx, schema_b, LYS_IN_YANG));
*state = s;
@@ -589,6 +594,53 @@
s->func = NULL;
}
+static void
+test_identityref(void **state)
+{
+ struct state_s *s = (struct state_s*)(*state);
+ s->func = test_identityref;
+
+ struct lyd_node *tree;
+ struct lyd_node_term *leaf;
+
+ const char *data = "<ident xmlns=\"urn:tests:types\">gigabit-ethernet</ident>";
+
+ /* valid data */
+ assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+ assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+ assert_string_equal("ident", tree->schema->name);
+ leaf = (struct lyd_node_term*)tree;
+ assert_string_equal("gigabit-ethernet", leaf->value.canonized);
+ lyd_free_all(tree);
+
+ data = "<ident xmlns=\"urn:tests:types\" xmlns:x=\"urn:tests:defs\">x:fast-ethernet</ident>";
+ assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+ assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+ assert_string_equal("ident", tree->schema->name);
+ leaf = (struct lyd_node_term*)tree;
+ assert_string_equal("fast-ethernet", leaf->value.canonized);
+ lyd_free_all(tree);
+
+ /* invalid value */
+ data = "<ident xmlns=\"urn:tests:types\">fast-ethernet</ident>";
+ assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+ logbuf_assert("Invalid identityref \"fast-ethernet\" value - identity not found. /");
+
+ data = "<ident xmlns=\"urn:tests:types\" xmlns:x=\"urn:tests:defs\">x:slow-ethernet</ident>";
+ assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+ logbuf_assert("Invalid identityref \"x:slow-ethernet\" value - identity not found. /");
+
+ data = "<ident xmlns=\"urn:tests:types\" xmlns:x=\"urn:tests:defs\">x:crypto-alg</ident>";
+ assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+ logbuf_assert("Invalid identityref \"x:crypto-alg\" value - identity not accepted by the type specification. /");
+
+ data = "<ident xmlns=\"urn:tests:types\" xmlns:x=\"urn:tests:unknown\">x:fast-ethernet</ident>";
+ assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+ logbuf_assert("Invalid identityref \"x:fast-ethernet\" value - unable to map prefix to YANG schema. /");
+
+ s->func = NULL;
+}
+
int main(void)
{
const struct CMUnitTest tests[] = {
@@ -601,6 +653,7 @@
cmocka_unit_test_setup_teardown(test_binary, setup, teardown),
cmocka_unit_test_setup_teardown(test_boolean, setup, teardown),
cmocka_unit_test_setup_teardown(test_empty, setup, teardown),
+ cmocka_unit_test_setup_teardown(test_identityref, setup, teardown),
};
return cmocka_run_group_tests(tests, NULL, NULL);
diff --git a/tools/re/main.c b/tools/re/main.c
index fa76662..bb6e13a 100644
--- a/tools/re/main.c
+++ b/tools/re/main.c
@@ -272,7 +272,7 @@
}
type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
- match = type->plugin->validate(ctx, type, str, strlen(str), LY_TYPE_OPTS_VALIDATE, NULL, &err, NULL);
+ match = type->plugin->validate(ctx, type, str, strlen(str), LY_TYPE_OPTS_VALIDATE, NULL, NULL, NULL, &err, NULL);
if (verbose) {
for (i = 0; i < patterns_count; i++) {
fprintf(stdout, "pattern %d: %s\n", i + 1, patterns[i]);