From af04ec9e9d91ecaaf614d0cff6152ab356a73dad Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Tue, 4 May 2021 11:38:39 +0200 Subject: [PATCH] Completed: Yang deviation [deviation statement not yet support #211](https://github.com/clicon/clixon/issues/211) --- CHANGELOG.md | 4 +- lib/src/clixon_proto.c | 20 ++++-- lib/src/clixon_yang.c | 47 ++++++++++++- lib/src/clixon_yang_parse.y | 85 +++++++++++------------ test/test_yang_deviation.sh | 131 ++++++++++++++++++++++++++++-------- 5 files changed, 201 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7e8cd225..7ae99e375 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,10 +34,8 @@ Expected: June 2021 ### New features -* Yang Deviation/deviate [deviation statement not yet support #211](https://github.com/clicon/clixon/issues/211) +* Yang deviation [deviation statement not yet support #211](https://github.com/clicon/clixon/issues/211) * See RFC7950 Sec 5.6.3 - * Implemented: "not-supported" and "add" - * Not yet: "replace" and "delete" ### Minor features diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 3b17eecca..d40f408af 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -770,11 +770,21 @@ send_msg_notify_xml(clicon_handle h, } /*! Look for a text pattern in an input string, one char at a time - * @param[in] tag What to look for - * @param[in] ch New input character - * @param[in,out] state A state integer holding how far we have parsed. - * @retval 0 No, we havent detected end tag - * @retval 1 Yes, we have detected end tag! + * @param[in] tag What to look for + * @param[in] ch New input character + * @param[in,out] state A state integer holding how far we have parsed. + * @retval 0 No, we havent detected end tag + * @retval 1 Yes, we have detected end tag! + * @code + * int state = 0; + * char ch; + * while (1) { + * // read ch + * if (detect_endtag("mypattern", ch, &state)) { + * // mypattern is matched + * } + * } + * @endcode */ int detect_endtag(char *tag, diff --git a/lib/src/clixon_yang.c b/lib/src/clixon_yang.c index e9f8e620f..55a3fd422 100644 --- a/lib/src/clixon_yang.c +++ b/lib/src/clixon_yang.c @@ -517,7 +517,8 @@ ys_prune(yang_stmt *yp, * @param[in] ys Yang node to remove * @retval 0 OK * @retval -1 Error - * @see ys_prune if parent and position is known + * @see ys_prune if parent and position is know + * @see ys_free Deallocate yang node * @note Do not call this in a loop of yang children (unless you know what you are doing) */ static int @@ -1608,6 +1609,7 @@ yang_deviation(yang_stmt *ys, yang_stmt *yd; yang_stmt *yc; yang_stmt *yc1; + yang_stmt *ytc; char *devop; clicon_handle h = (clicon_handle)arg; enum rfc_6020 kw; @@ -1648,8 +1650,7 @@ yang_deviation(yang_stmt *ys, else if (strcmp(devop, "add") == 0){ yc = NULL; while ((yc = yn_each(yd, yc)) != NULL) { - /* If a property can only appear once, the property MUST NOT - exist in the target node. */ + /* If a property can only appear once, the property MUST NOT exist in the target node. */ kw = yang_keyword_get(yc); if (yang_find(ytarget, kw, NULL) != NULL){ if (yang_cardinality_interval(h, @@ -1674,8 +1675,48 @@ yang_deviation(yang_stmt *ys, } } else if (strcmp(devop, "replace") == 0){ + yc = NULL; + while ((yc = yn_each(yd, yc)) != NULL) { + /* The properties to replace MUST exist in the target node.*/ + kw = yang_keyword_get(yc); + if ((ytc = yang_find(ytarget, kw, NULL)) == NULL){ + clicon_err(OE_YANG, 0, "deviation %s: \"%s %s\" replaced but node does not exist in target %s", + nodeid, + yang_key2str(kw), yang_argument_get(yc), + yang_argument_get(ytarget)); + goto done; + } + /* Remove old */ + if (ys_prune_self(ytc) < 0) + goto done; + if (ys_free(ytc) < 0) + goto done; + /* Make a copy of deviate child and insert. */ + if ((yc1 = ys_dup(yc)) == NULL) + goto done; + if (yn_insert(ytarget, yc1) < 0) + goto done; + } } else if (strcmp(devop, "delete") == 0){ + yc = NULL; + while ((yc = yn_each(yd, yc)) != NULL) { + /* The substatement's keyword MUST match a corresponding keyword in the target node, and the + * argument's string MUST be equal to the corresponding keyword's argument string in the + * target node. */ + kw = yang_keyword_get(yc); + if ((ytc = yang_find(ytarget, kw, NULL)) == NULL){ + clicon_err(OE_YANG, 0, "deviation %s: \"%s %s\" replaced but node does not exist in target %s", + nodeid, + yang_key2str(kw), yang_argument_get(yc), + yang_argument_get(ytarget)); + goto done; + } + if (ys_prune_self(ytc) < 0) + goto done; + if (ys_free(ytc) < 0) + goto done; + } } else{ /* Shouldnt happen, lex/yacc takes it */ clicon_err(OE_YANG, EINVAL, "%s: invalid deviate operator", devop); diff --git a/lib/src/clixon_yang_parse.y b/lib/src/clixon_yang_parse.y index a6b979de7..fd939aa06 100644 --- a/lib/src/clixon_yang_parse.y +++ b/lib/src/clixon_yang_parse.y @@ -1508,12 +1508,6 @@ notification_substmt : if_feature_stmt { _PARSE_DEBUG("notification-substmt -> | { _PARSE_DEBUG("notification-substmt -> "); } ; -/* deviation /oc-sys:system/oc-sys:config/oc-sys:hostname { - deviate not-supported; - } - * XXX abs-schema-nodeid-str is too difficult, it needs the + semantics - -*/ deviation_stmt : K_DEVIATION string { if (ysp_add_push(_yy, Y_DEVIATION, $2, NULL) == NULL) _YYERROR("deviation_stmt"); } '{' deviation_substmts '}' @@ -1569,13 +1563,13 @@ deviate_add_substmt : units_stmt { _PARSE_DEBUG("deviate-add-substmt -> units deviate_delete_stmt : K_DEVIATE D_DELETE ';' - { if (ysp_add(_yy, Y_DEVIATE, strdup("add") /* D_NOT_SUPPORTED*/, NULL) == NULL) _YYERROR("notification_stmt"); - _PARSE_DEBUG("deviate-delete-stmt -> DEVIATE add ;"); } + { if (ysp_add(_yy, Y_DEVIATE, strdup("delete"), NULL) == NULL) _YYERROR("notification_stmt"); + _PARSE_DEBUG("deviate-delete-stmt -> DEVIATE delete ;"); } | K_DEVIATE D_DELETE - { if (ysp_add_push(_yy, Y_DEVIATE, strdup("add"), NULL) == NULL) _YYERROR("deviate_stmt"); } + { if (ysp_add_push(_yy, Y_DEVIATE, strdup("delete"), NULL) == NULL) _YYERROR("deviate_stmt"); } '{' deviate_delete_substmts '}' { if (ystack_pop(_yy) < 0) _YYERROR("deviate_stmt"); - _PARSE_DEBUG("deviate-delete-stmt -> DEVIATE add { deviate-substmts }"); } + _PARSE_DEBUG("deviate-delete-stmt -> DEVIATE delete { deviate-delete-substmts }"); } ; deviate_delete_substmts : deviate_delete_substmts deviate_delete_substmt @@ -1590,15 +1584,14 @@ deviate_delete_substmt : units_stmt { _PARSE_DEBUG("deviate-delete-substmt -> un | { _PARSE_DEBUG("deviate-delete-substmt -> "); } ; - deviate_replace_stmt : K_DEVIATE D_REPLACE ';' - { if (ysp_add(_yy, Y_DEVIATE, strdup("add") /* D_NOT_SUPPORTED*/, NULL) == NULL) _YYERROR("notification_stmt"); - _PARSE_DEBUG("deviate-replace-stmt -> DEVIATE add ;"); } + { if (ysp_add(_yy, Y_DEVIATE, strdup("replace"), NULL) == NULL) _YYERROR("notification_stmt"); + _PARSE_DEBUG("deviate-replace-stmt -> DEVIATE replace ;"); } | K_DEVIATE D_REPLACE - { if (ysp_add_push(_yy, Y_DEVIATE, strdup("add"), NULL) == NULL) _YYERROR("deviate_stmt"); } + { if (ysp_add_push(_yy, Y_DEVIATE, strdup("replace"), NULL) == NULL) _YYERROR("deviate_stmt"); } '{' deviate_replace_substmts '}' { if (ystack_pop(_yy) < 0) _YYERROR("deviate_stmt"); - _PARSE_DEBUG("deviate-replace-stmt -> DEVIATE add { deviate-substmts }"); } + _PARSE_DEBUG("deviate-replace-stmt -> DEVIATE replace { deviate-replace-substmts }"); } ; deviate_replace_substmts : deviate_replace_substmts deviate_replace_substmt @@ -1688,40 +1681,40 @@ yang_stmt : action_stmt { _PARSE_DEBUG("yang-stmt -> action-stmt"); | leaf_stmt { _PARSE_DEBUG("yang-stmt -> leaf-stmt");} | length_stmt { _PARSE_DEBUG("yang-stmt -> length-stmt");} | list_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | mandatory_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | max_elements_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | min_elements_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | modifier_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | module_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | must_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | namespace_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} + | mandatory_stmt { _PARSE_DEBUG("yang-stmt -> mandatory-stmt");} + | max_elements_stmt { _PARSE_DEBUG("yang-stmt -> max-elements-stmt");} + | min_elements_stmt { _PARSE_DEBUG("yang-stmt -> min-elements-stmt");} + | modifier_stmt { _PARSE_DEBUG("yang-stmt -> modifier-stmt");} + | module_stmt { _PARSE_DEBUG("yang-stmt -> module-stmt");} + | must_stmt { _PARSE_DEBUG("yang-stmt -> must-stmt");} + | namespace_stmt { _PARSE_DEBUG("yang-stmt -> namespace-stmt");} | notification_stmt { _PARSE_DEBUG("yang-stmt -> notification-stmt");} - | ordered_by_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | organization_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | output_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | path_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | pattern_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | position_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | prefix_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | presence_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | range_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | reference_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | refine_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | require_instance_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | revision_date_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | revision_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} + | ordered_by_stmt { _PARSE_DEBUG("yang-stmt -> ordered-by-stmt");} + | organization_stmt { _PARSE_DEBUG("yang-stmt -> organization-stmt");} + | output_stmt { _PARSE_DEBUG("yang-stmt -> output-stmt");} + | path_stmt { _PARSE_DEBUG("yang-stmt -> path-stmt");} + | pattern_stmt { _PARSE_DEBUG("yang-stmt -> pattern-stmt");} + | position_stmt { _PARSE_DEBUG("yang-stmt -> position-stmt");} + | prefix_stmt { _PARSE_DEBUG("yang-stmt -> prefix-stmt");} + | presence_stmt { _PARSE_DEBUG("yang-stmt -> presence-stmt");} + | range_stmt { _PARSE_DEBUG("yang-stmt -> range-stmt");} + | reference_stmt { _PARSE_DEBUG("yang-stmt -> reference-stmt");} + | refine_stmt { _PARSE_DEBUG("yang-stmt -> refine-stmt");} + | require_instance_stmt { _PARSE_DEBUG("yang-stmt -> require-instance-stmt");} + | revision_date_stmt { _PARSE_DEBUG("yang-stmt -> revision-date-stmt");} + | revision_stmt { _PARSE_DEBUG("yang-stmt -> revision-stmt");} | rpc_stmt { _PARSE_DEBUG("yang-stmt -> rpc-stmt");} - | status_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | submodule_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} + | status_stmt { _PARSE_DEBUG("yang-stmt -> status-stmt");} + | submodule_stmt { _PARSE_DEBUG("yang-stmt -> submodule-stmt");} | typedef_stmt { _PARSE_DEBUG("yang-stmt -> typedef-stmt");} - | type_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | unique_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | units_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | uses_augment_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | uses_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | value_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | when_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} - | yang_version_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} + | type_stmt { _PARSE_DEBUG("yang-stmt -> type-stmt");} + | unique_stmt { _PARSE_DEBUG("yang-stmt -> unique-stmt");} + | units_stmt { _PARSE_DEBUG("yang-stmt -> units-stmt");} + | uses_augment_stmt { _PARSE_DEBUG("yang-stmt -> uses-augment-stmt");} + | uses_stmt { _PARSE_DEBUG("yang-stmt -> uses-stmt");} + | value_stmt { _PARSE_DEBUG("yang-stmt -> value-stmt");} + | when_stmt { _PARSE_DEBUG("yang-stmt -> when-stmt");} + | yang_version_stmt { _PARSE_DEBUG("yang-stmt -> yang-version-stmt");} /* | yin_element_stmt { _PARSE_DEBUG("yang-stmt -> list-stmt");} */ ; diff --git a/test/test_yang_deviation.sh b/test/test_yang_deviation.sh index cf00cb7c1..654a63855 100755 --- a/test/test_yang_deviation.sh +++ b/test/test_yang_deviation.sh @@ -35,11 +35,15 @@ module example-base{ prefix base; namespace "urn:example:base"; container system { - must "daytime or time"; - leaf daytime{ /* not supported removes this */ + must "daytime or time"; /* deviate delete removes this */ + leaf daytime{ /* deviate not-supported removes this */ + type string; + } + leaf time{ type string; } list name-server { + max-elements 1; /* deviate replace replaces to "max.elements 3" here */ key name; leaf name { type string; @@ -52,7 +56,7 @@ module example-base{ } leaf type { type string; - /* add rule adds default here */ + /* deviate add adds "default admin" here */ } } } @@ -60,13 +64,17 @@ module example-base{ EOF # Args: -# 0: daytime implemented: true/false -# 1: admin type default: true/false +# 1: daytime implemented: true/false +# 2: admin type default: true/false +# 3: mustdate default: true/false +# 4: maxelement of name-server is 1: true/false (if false the # is 3) function testrun() { daytime=$1 admindefault=$2 - + mustdate=$3 + maxel1=$4 + new "test params: -f $cfg" if [ "$BE" -ne 0 ]; then @@ -82,29 +90,55 @@ function testrun() new "wait backend" wait_backend - if ! $daytime; then # Not supported - dont continue - new "Add example-base daytime - should not be supported" - expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOSept17]]>]]>" "applicationunknown-elementdaytimeerrorFailed to find YANG spec of XML node: daytime with parent: system in namespace: urn:example:base]]>]]" + new "Add user bob" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLObob]]>]]>" "]]>]]" + + if $mustdate; then # fail since there is neither date or daytime (delete rule) + new "netconf validate expect error" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^applicationoperation-failederrormust xpath validation failed]]>]]>$" else + new "netconf validate ok" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "]]>]]" + fi + + new "Add time" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "]]>]]" + + new "netconf validate ok" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "]]>]]" + + if $daytime; then # not-supported rule new "Add example-base daytime - supported" expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOSept17]]>]]>" "]]>]]" + else # Not supported + new "Add example-base daytime - expect error not supported" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOSept17]]>]]>" "applicationunknown-elementdaytimeerrorFailed to find YANG spec of XML node: daytime with parent: system in namespace: urn:example:base]]>]]" + fi # daytime supported - new "Add user bob" - expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLObob]]>]]>" "]]>]]" - - new "netconf commit" - expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$" + new "netconf commit" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$" - if $admindefault; then - new "Get type admin expected" - expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^bobadmin]]>]]>$" + if $admindefault; then # add rule + new "Get type admin expected" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^bobadmin]]>]]>$" # XXX Cannot select a default value?? -# expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" foo - else - new "Get type none expected" - expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$" - fi + # expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" foo + else + new "Get type none expected" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^]]>]]>$" + fi + + # Add 2 name-servers + new "Add two name-servers" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLOaabb]]>]]>" "]]>]]" + if $maxel1; then # add two and check if it fails + new "netconf validate 2 element fail" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "^protocoloperation-failedtoo-many-elementserror/system/name-server]]>]]>$" + else + new "netconf validate 2 elements ok" + expecteof "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO]]>]]>" "]]>]]" fi + if [ "$BE" -ne 0 ]; then new "Kill backend" # Check if premature kill @@ -128,8 +162,8 @@ module example-deviations{ } } EOF -new "daytime supported" -testrun true false +new "1. daytime supported" +testrun true false true true # Example from RFC 7950 Sec 7.20.3.3 cat < $fyangdev @@ -145,10 +179,10 @@ module example-deviations{ } } EOF -new "daytime not supported" -testrun false false +new "2. daytime not supported" +testrun false false true true -# Example from RFC 7950 Sec 7.20.3.3 +# Add example from RFC 7950 Sec 7.20.3.3 cat < $fyangdev module example-deviations{ yang-version 1.1; @@ -164,8 +198,47 @@ module example-deviations{ } } EOF -new "deviate add, check admin default" -testrun true true +new "3. deviate add, check admin default" +testrun true true true true + +# Delete example from RFC 7950 Sec 7.20.3.3 +cat < $fyangdev +module example-deviations{ + yang-version 1.1; + prefix md; + namespace "urn:example:deviations"; + import example-base { + prefix base; + } + deviation /base:system/base:name-server { + deviate replace { + max-elements 3; + } + } +} +EOF +new "4. deviate replace" +testrun true false true false + +# Replace example from RFC 7950 Sec 7.20.3.3 +cat < $fyangdev +module example-deviations{ + yang-version 1.1; + prefix md; + namespace "urn:example:deviations"; + import example-base { + prefix base; + } + deviation /base:system { + deviate delete { + must "daytime or time"; + } + } +} +EOF + +new "5. deviate delete" +testrun true false false true rm -rf "$dir"