diff --git a/CHANGELOG.md b/CHANGELOG.md index 1605ed1f..dd5694ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,6 +85,12 @@ * Any config option from file can be overrided by giving them on command-line. ### API changes on existing features (you may need to change your code) +* Date-and-time type now properly uses ISO 8601 UTC timezone designators. + * Eg 2008-09-21T18:57:21.003456 is changed to 2008-09-21T18:57:21.003456Z +* Renamed yang file `ietf-netconf-notification@2008-07-01.yang` to `clixon-rfc5277`. + * Fixed validation problems, see [https://github.com/clicon/clixon/issues/62] + * Name confusion, the file is manually constructed from the rfc. + * Changed prefix to `ncevent` * Stricter YANG choice validation leads to enforcement of structures like: `choice c{ mandatory true; leaf x` statements. `x` was not previously enforced. * Many hand-crafted validation messages have been removed and replaced with generic validations, which may lead to changed rpc-error messages. * CLICON_XML_SORT option (in clixon-config.yang) has been removed and set to true permanently. Unsorted XML lists leads to slower performance and old obsolete code can be removed. @@ -101,6 +107,7 @@ * For backward compatibility, define CLICON_CLI_MODEL_TREENAME_PATCH in clixon_custom.h ### Minor changes +* Cligen uses posix regex while yang uses XSD. It differs in some aspects. A translator function has been added for `\d` -> `[0-9]` translation, there may be more. * Added new clixon-lib yang module for internal netconf protocol. Currently only extends the standard with a debug RPC. * Added three-valued return values for several validate functions where -1 is fatal error, 0 is validation failed and 1 is validation OK. * This includes: `xmldb_put`, `xml_yang_validate_all`, `xml_yang_validate_add`, `xml_yang_validate_rpc`, `api_path2xml`, `api_path2xpath` @@ -118,6 +125,7 @@ * "); if (helptext) cprintf(cb, "(\"%s\")", helptext); @@ -329,7 +335,7 @@ yang2cli_var_union_one(clicon_handle h, goto done; } else { - if (clicon_type2cv(origtype, restype, &cvtype) < 0) + if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) goto done; if ((retval = yang2cli_var_sub(h, ys, ytype, cb, helptext, cvtype, options, cvv, pattern, fraction_digits)) < 0) @@ -417,7 +423,7 @@ yang2cli_var(clicon_handle h, retval = 0; goto done; } - if (clicon_type2cv(origtype, restype, &cvtype) < 0) + if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) goto done; /* Note restype can be NULL here for example with unresolved hardcoded uuid */ if (restype && strcmp(restype, "union") == 0){ diff --git a/apps/restconf/restconf_main.c b/apps/restconf/restconf_main.c index 0e33e5b1..c92a2ff1 100644 --- a/apps/restconf/restconf_main.c +++ b/apps/restconf/restconf_main.c @@ -666,7 +666,7 @@ main(int argc, yang_spec_parse_module(h, "ietf-restconf-monitoring", NULL, yspec)< 0) goto done; if (clicon_option_bool(h, "CLICON_STREAM_DISCOVERY_RFC5277") && - yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0) + yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0) goto done; /* Call start function in all plugins before we go interactive Pass all args after the standard options to plugin_start diff --git a/lib/clixon/clixon_string.h b/lib/clixon/clixon_string.h index 5895401d..f78ddbc9 100644 --- a/lib/clixon/clixon_string.h +++ b/lib/clixon/clixon_string.h @@ -88,6 +88,7 @@ const char *clicon_int2str(const map_str2int *mstab, int i); int clicon_str2int(const map_str2int *mstab, char *str); int nodeid_split(char *nodeid, char **prefix, char **id); char *clixon_trim(char *str); +int regexp_xsd2posix(char *xsd, char **posix); #ifndef HAVE_STRNDUP char *clicon_strndup (const char *, size_t); #endif /* ! HAVE_STRNDUP */ diff --git a/lib/clixon/clixon_yang_type.h b/lib/clixon/clixon_yang_type.h index c023f374..2e859e47 100644 --- a/lib/clixon/clixon_yang_type.h +++ b/lib/clixon/clixon_yang_type.h @@ -67,7 +67,7 @@ int yang2cv_type(char *ytype, enum cv_type *cv_type); char *cv2yang_type(enum cv_type cv_type); yang_stmt *yang_find_identity(yang_stmt *ys, char *identity); int ys_cv_validate(cg_var *cv, yang_stmt *ys, char **reason); -int clicon_type2cv(char *type, char *rtype, enum cv_type *cvtype); +int clicon_type2cv(char *type, char *rtype, yang_stmt *ys, enum cv_type *cvtype); int yang_type_get(yang_stmt *ys, char **otype, yang_stmt **restype, int *options, cvec **cvv, char **pattern, uint8_t *fraction_digits); diff --git a/lib/src/clixon_netconf_lib.c b/lib/src/clixon_netconf_lib.c index cfa93882..427e99fc 100644 --- a/lib/src/clixon_netconf_lib.c +++ b/lib/src/clixon_netconf_lib.c @@ -1032,7 +1032,7 @@ netconf_module_load(clicon_handle h) /* Load yang spec */ if (yang_spec_parse_module(h, "ietf-netconf", NULL, yspec)< 0) goto done; - if (yang_spec_parse_module(h, "ietf-netconf-notification", NULL, yspec)< 0) + if (yang_spec_parse_module(h, "clixon-rfc5277", NULL, yspec)< 0) goto done; retval = 0; done: diff --git a/lib/src/clixon_stream.c b/lib/src/clixon_stream.c index 6bad4671..dbc5467a 100644 --- a/lib/src/clixon_stream.c +++ b/lib/src/clixon_stream.c @@ -548,7 +548,7 @@ stream_notify(clicon_handle h, yang_spec *yspec = NULL; char *str = NULL; cbuf *cb = NULL; - char timestr[27]; + char timestr[28]; struct timeval tv; event_stream_t *es; @@ -622,7 +622,7 @@ stream_notify_xml(clicon_handle h, yang_spec *yspec = NULL; char *str = NULL; cbuf *cb = NULL; - char timestr[27]; + char timestr[28]; struct timeval tv; event_stream_t *es; diff --git a/lib/src/clixon_string.c b/lib/src/clixon_string.c index 733a1ca4..f0dc1c24 100644 --- a/lib/src/clixon_string.c +++ b/lib/src/clixon_string.c @@ -634,6 +634,51 @@ clixon_trim(char *str) return s; } +/*! Transform from XSD regex to posix ERE + * The usecase is that Yang (RFC7950) supports XSD regexpressions but CLIgen supports + * Current translations: + * \d --> [0-9] + * POSIX ERE regexps according to man regex(3). + * @param[in] xsd Input regex string according XSD + * @param[out] posix Output (malloced) string according to POSIX ERE + * @see http://www.w3.org/TR/2004/REC-xmlschema-2-20041028 + * @note that the translation is ad-hoc, may need more translations + */ +int +regexp_xsd2posix(char *xsd, + char **posix) +{ + int retval = -1; + char *x; + char *p = NULL; + int i; + int len; + + len = strlen(xsd); + x = xsd; + while ((x = strstr(x, "\\d")) != NULL){ + len += 3; /* \d --> [0-9] */ + x += 2; + } + if ((p = malloc(len+1)) == NULL){ + clicon_err(OE_UNIX, errno, "malloc"); + goto done; + } + memset(p, 0, len+1); + *posix = p; + for (i=0; iys_argument:NULL; - if (clicon_type2cv(type, restype, &cvtype) < 0) /* This handles non-resolved also */ + if (clicon_type2cv(type, restype, ys, &cvtype) < 0) /* This handles non-resolved also */ goto done; /* 2. Create the CV using cvtype and name it */ if ((cv = cv_new(cvtype)) == NULL){ @@ -1230,7 +1230,7 @@ ys_populate_range(yang_stmt *ys, restype = yrestype?yrestype->ys_argument:NULL; origtype = yarg_id((yang_stmt*)yparent); /* This handles non-resolved also */ - if (clicon_type2cv(origtype, restype, &cvtype) < 0) + if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) goto done; if ((vec = clicon_strsep(ys->ys_argument, "|", &nvec)) == NULL) goto done; diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 56b16842..ce43aec3 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -254,35 +254,6 @@ yang2cv_type(char *ytype, *cv_type = ret; return 0; } - /* special derived types */ - if (strcmp("ipv4-address", ytype) == 0){ /* RFC6991 */ - *cv_type = CGV_IPV4ADDR; - return 0; - } - if (strcmp("ipv6-address", ytype) == 0){ /* RFC6991 */ - *cv_type = CGV_IPV6ADDR; - return 0; - } - if (strcmp("ipv4-prefix", ytype) == 0){ /* RFC6991 */ - *cv_type = CGV_IPV4PFX; - return 0; - } - if (strcmp("ipv6-prefix", ytype) == 0){ /* RFC6991 */ - *cv_type = CGV_IPV6PFX; - return 0; - } - if (strcmp("date-and-time", ytype) == 0){ /* RFC6991 */ - *cv_type = CGV_TIME; - return 0; - } - if (strcmp("mac-address", ytype) == 0){ /* RFC6991 */ - *cv_type = CGV_MACADDR; - return 0; - } - if (strcmp("uuid", ytype) == 0){ /* RFC6991 */ - *cv_type = CGV_UUID; - return 0; - } return 0; } @@ -330,12 +301,14 @@ cv2yang_type(enum cv_type cv_type) * not true yang types * @param[in] origtype Name of original type * @param[in] restype Resolved type. May be null, in that case origtype is used + * @param[in] ys Yang stmt of original resolving node * @param[out] cvtype Translation from resolved type * @note Thereis a kludge for handling direct translations of native cligen types */ int clicon_type2cv(char *origtype, char *restype, + yang_stmt *ys, enum cv_type *cvtype) { int retval = -1; @@ -344,7 +317,8 @@ clicon_type2cv(char *origtype, if (restype != NULL){ yang2cv_type(restype, cvtype); if (*cvtype == CGV_ERR){ - clicon_err(OE_YANG, EINVAL, "\"%s\" type not translated", restype); + clicon_err(OE_YANG, 0, "%s: \"%s\" type not translated", + ys_module(ys)->ys_argument, restype); goto done; } } @@ -355,7 +329,8 @@ clicon_type2cv(char *origtype, */ yang2cv_type(origtype, cvtype); if (*cvtype == CGV_ERR){ - clicon_err(OE_YANG, EINVAL, "\"%s\": type not resolved", origtype); + clicon_err(OE_YANG, 0, "%s:\"%s\": type not resolved", + ys_module(ys)->ys_argument, origtype); goto done; } } @@ -543,10 +518,15 @@ cv_validate1(cg_var *cv, } } if ((options & YANG_OPTIONS_PATTERN) != 0){ - if ((retval2 = match_regexp(str, pattern)) < 0){ + char *posix = NULL; + if (regexp_xsd2posix(pattern, &posix) < 0) + goto done; + if ((retval2 = match_regexp(str, posix)) < 0){ clicon_err(OE_DB, 0, "match_regexp: %s", pattern); return -1; } + if (posix) + free(posix); if (retval2 == 0){ if (reason) *reason = cligen_reason("regexp match fail: \"%s\" does not match %s", @@ -611,7 +591,7 @@ ys_cv_validate_union_one(yang_stmt *ys, goto done; } else { - if (clicon_type2cv(type, restype, &cvtype) < 0) + if (clicon_type2cv(type, restype, ys, &cvtype) < 0) goto done; /* reparse value with the new type */ if ((cvt = cv_new(cvtype)) == NULL){ @@ -718,7 +698,7 @@ ys_cv_validate(cg_var *cv, &options, &cvv, &pattern, &fraction) < 0) goto done; restype = yrestype?yrestype->ys_argument:NULL; - if (clicon_type2cv(type, restype, &cvtype) < 0) + if (clicon_type2cv(type, restype, ys, &cvtype) < 0) goto done; if (cv_type_get(ycv) != cvtype){ @@ -978,7 +958,7 @@ yang_type_resolve(yang_stmt *yorig, break; /* Did not find a matching typedef there, proceed to next level */ yn = ys->ys_parent; - if (yn && yn->yn_keyword == Y_SPEC) + if (yn && (yn->yn_keyword == Y_SPEC)) yn = NULL; ys = (yang_stmt*)yn; } diff --git a/test/test_stream.sh b/test/test_stream.sh index 2673b832..75c7e613 100755 --- a/test/test_stream.sh +++ b/test/test_stream.sh @@ -153,7 +153,7 @@ new "netconf NONEXIST subscription" expectwait "$clixon_netconf -qf $cfg -y $fyang" 'NONEXIST]]>]]>' '^applicationinvalid-valueerrorNo such stream]]>]]>$' $NCWAIT new "netconf EXAMPLE subscription with wrong date" -expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLEkallekaka]]>]]>' '^applicationbad-elementstartTimeerrorInvalid time: kallekaka]]>]]>$' 0 +expectwait "$clixon_netconf -qf $cfg -y $fyang" 'EXAMPLEkallekaka]]>]]>' '^applicationbad-elementstartTimeerrorregexp match fail: "kallekaka" does not match' 0 #new "netconf EXAMPLE subscription with replay" #NOW=$(date +"%Y-%m-%dT%H:%M:%S") @@ -181,7 +181,7 @@ expectwait 'curl -s -X GET -H "Accept: text/event-stream" -H "Cache-Control: no- # 2a) start subscription 8s - expect 1-2 notifications new "2a) start subscriptions 8s - expect 1-2 notifications" ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 8) -expect="data: ${DATE}T[0-9:.]*faultEthernet0major" +expect="data: ${DATE}T[0-9:.]*ZfaultEthernet0major" match=$(echo "$ret" | grep -Eo "$expect") if [ -z "$match" ]; then @@ -193,10 +193,11 @@ if [ $nr -lt 1 -o $nr -gt 2 ]; then fi sleep 2 + # 2b) start subscription 8s - stoptime after 5s - expect 1-2 notifications new "2b) start subscriptions 8s - stoptime after 5s - expect 1-2 notifications" ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 8 -e +10) -expect="data: ${DATE}T[0-9:.]*faultEthernet0major" +expect="data: ${DATE}T[0-9:.]*ZfaultEthernet0major" match=$(echo "$ret" | grep -Eo "$expect") if [ -z "$match" ]; then err "$expect" "$ret" @@ -209,7 +210,7 @@ fi # 2c new "2c) start sub 8s - replay from start -8s - expect 3-4 notifications" ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 10 -s -8) -expect="data: ${DATE}T[0-9:.]*faultEthernet0major" +expect="data: ${DATE}T[0-9:.]*ZfaultEthernet0major" match=$(echo "$ret" | grep -Eo "$expect") if [ -z "$match" ]; then err "$expect" "$ret" @@ -222,7 +223,7 @@ fi # 2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications new "2d) start sub 8s - replay from start -8s to stop +4s - expect 3 notifications" ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 10 -s -30 -e +4) -expect="data: ${DATE}T[0-9:.]*faultEthernet0major" +expect="data: ${DATE}T[0-9:.]*ZfaultEthernet0major" match=$(echo "$ret" | grep -Eo "$expect") if [ -z "$match" ]; then err "$expect" "$ret" @@ -235,7 +236,7 @@ fi # 2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications new "2e) start sub 8s - replay from -90s w retention 60s - expect 10 notifications" ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 10 -s -90 -e +0) -expect="data: ${DATE}T[0-9:.]*faultEthernet0major" +expect="data: ${DATE}T[0-9:.]*ZfaultEthernet0major" match=$(echo "$ret" | grep -Eo "$expect") if [ -z "$match" ]; then err "$expect" "$ret" @@ -253,7 +254,7 @@ PID=$! new "Start subscriptions in parallell" ret=$($UTIL -u http://localhost/streams/EXAMPLE -t 8) -expect="data: ${DATE}T[0-9:.]*faultEthernet0major" +expect="data: ${DATE}T[0-9:.]*ZfaultEthernet0major" match=$(echo "$ret" | grep -Eo "$expect") if [ -z "$match" ]; then diff --git a/util/clixon_util_stream.c b/util/clixon_util_stream.c index 3530514f..6b52fb3e 100644 --- a/util/clixon_util_stream.c +++ b/util/clixon_util_stream.c @@ -216,8 +216,8 @@ main(int argc, char **argv) char *url = NULL; char *getdata = NULL; int timeout = 10; - char start[27] = {0,}; /* strlen = 0 */ - char stop[27] = {0,}; + char start[28] = {0,}; /* strlen = 0 */ + char stop[28] = {0,}; int c; char *argv0 = argv[0]; struct timeval now; diff --git a/yang/Makefile.in b/yang/Makefile.in index a1d0be57..3d8e38dd 100644 --- a/yang/Makefile.in +++ b/yang/Makefile.in @@ -48,7 +48,7 @@ YANGSPECS += ietf-inet-types@2013-07-15.yang YANGSPECS += ietf-yang-types@2013-07-15.yang YANGSPECS += ietf-restconf@2017-01-26.yang YANGSPECS += ietf-restconf-monitoring@2017-01-26.yang -YANGSPECS += ietf-netconf-notification@2008-07-01.yang +YANGSPECS += clixon-rfc5277@2008-07-01.yang YANGSPECS += ietf-yang-library@2016-06-21.yang APPNAME = clixon # subdir ehere these files are installed diff --git a/yang/ietf-netconf-notification@2008-07-01.yang b/yang/clixon-rfc5277@2008-07-01.yang similarity index 95% rename from yang/ietf-netconf-notification@2008-07-01.yang rename to yang/clixon-rfc5277@2008-07-01.yang index 1db3ee3e..aaa45ed2 100644 --- a/yang/ietf-netconf-notification@2008-07-01.yang +++ b/yang/clixon-rfc5277@2008-07-01.yang @@ -1,7 +1,7 @@ -module ietf-netconf-notification { +module clixon-rfc5277 { /* namespace "urn:ietf:params:xml:ns:netconf:notification:1.0";*/ namespace "urn:ietf:params:xml:ns:netmod:notification"; - prefix "rcmon"; + prefix "ncevent"; import ietf-yang-types { prefix yang; } import ietf-inet-types { prefix inet; } @@ -10,7 +10,8 @@ module ietf-netconf-notification { "IETF NETCONF (Network Configuration) Working Group"; description - "Note this is a translation from RFC 5277 schema in section 4 to Yang. + "Note this is a translation from RFC 5277 schema in section 4 to Yang + made by Olof Hagsand manually for the Clixon project. RFC 5277 is Copyright (C) The IETF Trust (2008)."; revision 2008-07-01 { @@ -120,14 +121,14 @@ module ietf-netconf-notification { for more information on filters"; } leaf startTime { - type date-and-time; + type yang:date-and-time; description "used to trigger the replay feature and indicate that the replay should start at the time specified. If is not present, this is not a replay subscription."; } leaf stopTime { - type date-and-time; + type yang:date-and-time; description "used with the optional replay feature to indicate the newest notifications of interest. If is not present, the notifications will