Skip to content

Commit 8cb8545

Browse files
committed
yanglint UPDATE extension validation
1 parent 280b7b7 commit 8cb8545

File tree

4 files changed

+59
-35
lines changed

4 files changed

+59
-35
lines changed

tests/yanglint/interactive/data_operational.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/interactive/ly.tcl" : "ly.tcl"}]
22

33
set ddir "$::env(TESTS_DIR)/data"
4-
set err1 "Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types"
4+
set err1 "Operational datastore takes effect only with RPCs/Actions/Replies/Notification/Extensions input data types"
55

66
test data_operational_twice {it is not allowed to specify more than one --operational parameter} {
77
-setup $ly_setup -cleanup $ly_cleanup -body {

tests/yanglint/non-interactive/data_operational.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ source [expr {[info exists ::env(TESTS_DIR)] ? "$env(TESTS_DIR)/non-interactive/
22

33
set mdir "$::env(YANG_MODULES_DIR)"
44
set ddir "$::env(TESTS_DIR)/data"
5-
set err1 "Operational datastore takes effect only with RPCs/Actions/Replies/Notification input data types"
5+
set err1 "Operational datastore takes effect only with RPCs/Actions/Replies/Notification/Extensions input data types"
66

77
test data_operational_twice {it is not allowed to specify more than one --operational parameter} {
88
ly_cmd_err "-t notif -O $ddir/modconfig.xml -O $ddir/modleaf.xml" "cannot be set multiple times"

tools/lint/cmd_data.c

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ cmd_data_dep(struct yl_opt *yo, int posc)
322322
}
323323

324324
if (yo->data_operational.path && (!yo->data_type && !yo->data_ext)) {
325-
YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notification/extensions input data types.");
325+
YLMSG_W("Operational datastore takes effect only with RPCs/Actions/Replies/Notification/Extensions input data types.");
326326
yo->data_operational.path = NULL;
327327
}
328328

@@ -536,7 +536,6 @@ check_operation_parent(struct lyd_node *op, struct lyd_node *oper_tree, struct c
536536
*
537537
* @param[in] ctx libyang context with schema.
538538
* @param[in] yo context for yanglint.
539-
*
540539
* @return 0 on success.
541540
*/
542541
static int
@@ -568,13 +567,26 @@ find_extension(struct ly_ctx *ctx, struct yl_opt *yo)
568567
return 1;
569568
}
570569

570+
/**
571+
* @brief Parses input data based on its type and returns the corresponding data tree.
572+
* @param[in] ctx libyang context with schema.
573+
* @param[in] type The type of data in the input files.
574+
* @param[in] input_f Data input file.
575+
* @param[in] parse_options Parser options.
576+
* @param[in] validate_options Validation options.
577+
* @param[out] tree Pointer to the top-level data tree parsed from the input.
578+
* @param[out] op Pointer to the specific operation node.
579+
* @param[in] reply_rpc Source RPC operation file information for parsing NETCONF rpc-reply.
580+
* @return LY_ERR value.
581+
*/
571582
static LY_ERR
572583
parse_input_by_type(struct ly_ctx *ctx, enum lyd_type type, struct cmdline_file *input_f,
573-
uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree, struct lyd_node **op,
574-
struct lyd_node **envp, struct cmdline_file *reply_rpc)
584+
uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree,
585+
struct lyd_node **op, struct cmdline_file *reply_rpc)
575586
{
576587

577588
LY_ERR ret = LY_SUCCESS;
589+
struct lyd_node *envp = NULL;
578590

579591
switch (type) {
580592
case LYD_TYPE_DATA_YANG:
@@ -587,18 +599,18 @@ parse_input_by_type(struct ly_ctx *ctx, enum lyd_type type, struct cmdline_file
587599
break;
588600
case LYD_TYPE_RPC_NETCONF:
589601
case LYD_TYPE_NOTIF_NETCONF:
590-
ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, type, envp, op);
602+
ret = lyd_parse_op(ctx, NULL, input_f->in, input_f->format, type, &envp, op);
591603

592604
/* adjust pointers */
593605
for (*tree = *op; lyd_parent(*tree); *tree = lyd_parent(*tree)) {}
594606
break;
595607
case LYD_TYPE_REPLY_NETCONF:
596608
/* parse source RPC operation */
597609
assert(reply_rpc && reply_rpc->in);
598-
ret = lyd_parse_op(ctx, NULL, reply_rpc->in, reply_rpc->format, LYD_TYPE_RPC_NETCONF, envp, op);
610+
ret = lyd_parse_op(ctx, NULL, reply_rpc->in, reply_rpc->format, LYD_TYPE_RPC_NETCONF, &envp, op);
599611
if (ret) {
600612
YLMSG_E("Failed to parse source NETCONF RPC operation file \"%s\".", reply_rpc->path);
601-
return ret;
613+
goto cleanup;
602614
}
603615

604616
/* adjust pointers */
@@ -608,38 +620,59 @@ parse_input_by_type(struct ly_ctx *ctx, enum lyd_type type, struct cmdline_file
608620
lyd_free_siblings(lyd_child(*op));
609621

610622
/* we do not care */
611-
lyd_free_all(*envp);
612-
*envp = NULL;
623+
lyd_free_all(envp);
624+
envp = NULL;
613625

614-
ret = lyd_parse_op(ctx, *op, input_f->in, input_f->format, type, envp, NULL);
626+
ret = lyd_parse_op(ctx, *op, input_f->in, input_f->format, type, &envp, NULL);
615627
break;
616628
default:
617629
YLMSG_E("Internal error (%s:%d).", __FILE__, __LINE__);
618-
return ret;
630+
goto cleanup;
619631
}
620632

633+
cleanup:
634+
lyd_free_all(envp);
635+
envp = NULL;
621636
return ret;
622637
}
623638

639+
/**
640+
* @brief Parses and validates data for a specific YANG extension instance.
641+
*
642+
* @param[in] ctx libyang context with schema.
643+
* @param[in] yo context for yanglint.
644+
* @param[in] input_f Data input file.
645+
* @param[in] oper_tree operational data tree.
646+
* @return LY_ERR value.
647+
*/
624648
static LY_ERR
625-
parse_extension_instance(struct ly_ctx *ctx, struct yl_opt *yo, struct cmdline_file *input_f, struct lyd_node **tree, struct lyd_node **oper_tree)
649+
parse_extension_instance(struct ly_ctx *ctx, struct yl_opt *yo, struct cmdline_file *input_f, struct lyd_node *oper_tree)
626650
{
627651

628652
LY_ERR ret = LY_SUCCESS;
653+
struct lyd_node *tree;
629654

630655
if (find_extension(ctx, yo)) {
631656
YLMSG_E("Extension '%s:%s:%s' not found in module.", yo->mod_name, yo->name, yo->argument);
632-
return 1;
657+
return LY_ENOTFOUND;
633658
}
634659

635-
// lyd_insert_sibling
636-
if (lyd_parse_ext_data(yo->ext, NULL, input_f->in, input_f->format, LYD_PARSE_ONLY, 0, tree)) {
660+
if ((ret = lyd_parse_ext_data(yo->ext, NULL, input_f->in, input_f->format, LYD_PARSE_ONLY, 0, &tree))) {
637661
YLMSG_E("Parsing of extension data failed.")
638-
return 1;
662+
return ret;
663+
}
664+
if (oper_tree) {
665+
lyd_insert_sibling(tree, oper_tree, &tree);
639666
}
640667

668+
ret = lyd_validate_all(&tree, ctx, yo->data_validate_options, NULL);
669+
641670
if (yo->data_out_format) {
642-
lyd_print_all(yo->out, *tree, yo->data_out_format, yo->data_print_options);
671+
lyd_print_all(yo->out, tree, yo->data_out_format, yo->data_print_options);
672+
}
673+
674+
if (!yo->data_operational.in) {
675+
lyd_free_all(tree);
643676
}
644677

645678
yo->data_ext = 0;
@@ -650,7 +683,7 @@ int
650683
cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo)
651684
{
652685
LY_ERR ret = LY_SUCCESS;
653-
struct lyd_node *tree = NULL, *op = NULL, *envp = NULL, *merged_tree = NULL, *oper_tree = NULL;
686+
struct lyd_node *tree = NULL, *op = NULL, *merged_tree = NULL, *oper_tree = NULL;
654687
const char *xpath;
655688
struct ly_set *set = NULL;
656689

@@ -666,10 +699,10 @@ cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo)
666699
for (uint32_t u = 0; u < yo->data_inputs.count; ++u) {
667700
struct cmdline_file *input_f = (struct cmdline_file *)yo->data_inputs.objs[u];
668701

669-
if (yo->data_ext && strcmp(input_f->path, yo->data_operational.path)) {
670-
ret = parse_extension_instance(ctx, yo, input_f, &tree, &oper_tree);
702+
if (yo->data_ext) {
703+
ret = parse_extension_instance(ctx, yo, input_f, oper_tree);
671704
} else {
672-
ret = parse_input_by_type(ctx, yo->data_type, input_f, yo->data_parse_options, yo->data_validate_options, &tree, &op, &envp, &yo->reply_rpc);
705+
ret = parse_input_by_type(ctx, yo->data_type, input_f, yo->data_parse_options, yo->data_validate_options, &tree, &op, &yo->reply_rpc);
673706
}
674707

675708
if (ret) {
@@ -748,8 +781,6 @@ cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo)
748781
/* next iter */
749782
lyd_free_all(tree);
750783
tree = NULL;
751-
lyd_free_all(envp);
752-
envp = NULL;
753784
}
754785

755786
if (yo->data_merge) {
@@ -782,7 +813,6 @@ cmd_data_process(struct ly_ctx *ctx, struct yl_opt *yo)
782813

783814
cleanup:
784815
lyd_free_all(tree);
785-
lyd_free_all(envp);
786816
lyd_free_all(merged_tree);
787817
lyd_free_all(oper_tree);
788818
ly_set_free(set, NULL);

tools/lint/examples/README.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -538,25 +538,19 @@ $ yanglint -f json -t config -p . -Y sm-context-main.xml -x sm-context-extension
538538

539539
## Validating extension data
540540

541-
The input data should be parsed and validated strictly according to the schema tree defined by the extension instance.
542-
543-
The tool parses and validates the input file (ext-data.xml) as a standalone extension data tree, not as part of a complete configuration. It does not allow mixing extension data with normal module data.
541+
This command sequence uses `yanglint` to load YANG modules and validate a file (`ext-data.xml`) that contains YANG extension, an instance of a `yang-errors` structure defined by `ietf-restconf`. However, because this extension data references operational data, the validation process also requires that corresponding operational data.
544542

545543
Preparation:
546544

547545
```
548546
> clear
549547
550548
> add example-jukebox.yang
551-
> data -t ext --ext-instance rc:yang-data:yang-errors ext-data.xml
549+
> data -t ext --ext-inst ietf-restconf:yang-data:yang-errors -O operational-data.xml ext-data.xml
552550
```
553551

554552
Output:
555553

556-
```
557-
libyang[0]: Invalid instance-identifier "/example-jukebox:jukebox/library" value - required instance not found. (data path: /ietf-restconf:errors/error[1]/error-path)
558-
YANGLINT[E]: Parsing of extension data failed.
554+
No output is printed, it means that the data is valid.
559555

560-
```
561-
This error indicates that the input file refers to data (/example-jukebox:jukebox/library) that do not exist in the extension's schema tree — which violates the rules.
562556

0 commit comments

Comments
 (0)