diff --git a/CHANGELOG.md b/CHANGELOG.md index 128b63bb..c8ba6d08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,9 +41,21 @@ Expected: September 2022 ### New features -* With-defaults RFC6243 - * see [Netconf With-defaults Capability](https://github.com/clicon/clixon/issues/262) -* RESTCONF call home according to RFC 8071 +* With-defaults capability + * Standards + * RFC 6243 "With-defaults Capability for NETCONF" + * RFC 8040 "RESTCONF Protocol": Section 4.8.9 + * Features: + * `:with-defaults` capability + * `explicit` basic mode + * Full retrieval modes: `explicit`, `trim`, `report-all`, `report-all-tagged`. + * RESTCONF with-defaults query for XML and JSON + * Assigned meta-data for the `ietf-netconf-with-defaults:default` attribute for JSON (RFC8040 Sec 5.3.2) + * Added `withdefault` option to cli show commands + * See `CLISPEC changes of cli show functions` below + * See [Netconf With-defaults Capability](https://github.com/clicon/clixon/issues/262) +* RESTCONF call home + * Standard: RFC 8071 "NETCONF Call Home and RESTCONF Call Home" * clixon-restconf.yang extended with callhome inspired by ietf-restconf-server.yang * See e.g., draft-ietf-netconf-restconf-client-server-26.txt * The `` list has been extended with a `call-home` presence container including: diff --git a/README.md b/README.md index a63a94c9..429ea0ed 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,6 @@ Clixon has a master branch continuously tested with CI, but releases are made ca Clixon interaction is best done posting issues, pull requests, or joining the [slack channel](https://clixondev.slack.com). -[Slack invite](https://join.slack.com/t/clixondev/shared_invite/zt-1da9fdr51-G~W5CES01_TRVdg5oXo43w)(updated 28/7 2022, valid 30d) +[Slack invite](https://join.slack.com/t/clixondev/shared_invite/zt-1fz8at20n-KGNmKD7KH2j2jIFGU_EhMQ)(updated 15/9 2022, valid 30d) Clixon is sponsored by [Rubicon Communications LLC(Netgate)](https://www.netgate.com/) diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index e561fe72..5f2fa131 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -503,13 +503,12 @@ restconf_insert_attributes(cxobj *xdata, * @retval 0 OK, all callbacks executed OK * @retval -1 Error in one callback * @note This extension adds semantics to YANG according to RFC8040 as follows: - * - The list-stmt is not required to have a key-stmt defined.(NB!!) - * - The if-feature-stmt is ignored if present. - * - The config-stmt is ignored if present. - * - The available identity values for any 'identityref' - * leaf or leaf-list nodes are limited to the module - * containing this extension statement and the modules - * imported into that module. + * - The list-stmt is not required to have a key-stmt defined.(NB!!) + * - The if-feature-stmt is ignored if present. + * - The config-stmt is ignored if present. + * - The available identity values for any 'identityref' + * leaf or leaf-list nodes are limited to the module containing this extension statement and + * the modules imported into that module. */ int restconf_main_extension_cb(clicon_handle h, diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c index ee8cf1be..a5d0faf0 100644 --- a/apps/restconf/restconf_main_fcgi.c +++ b/apps/restconf/restconf_main_fcgi.c @@ -315,7 +315,6 @@ main(int argc, char *inline_config = NULL; size_t sz; - /* In the startup, logs to stderr & debug flag set later */ clicon_log_init(__PROGRAM__, LOG_INFO, logdst); diff --git a/lib/clixon/clixon_plugin.h b/lib/clixon/clixon_plugin.h index 0c4b447d..43b84345 100644 --- a/lib/clixon/clixon_plugin.h +++ b/lib/clixon/clixon_plugin.h @@ -141,8 +141,8 @@ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */ * @param[in] h Clixon handle * @param[in] yext Yang node of extension * @param[in] ys Yang node of (unknown) statement belonging to extension - * @retval 0 OK, all callbacks executed OK - * @retval -1 Error in one callback + * @retval 0 OK, all callbacks executed OK + * @retval -1 Error in one callback */ typedef int (plgextension_t)(clicon_handle h, yang_stmt *yext, yang_stmt *ys); diff --git a/lib/src/clixon_json.c b/lib/src/clixon_json.c index 21afce16..010ab2b3 100644 --- a/lib/src/clixon_json.c +++ b/lib/src/clixon_json.c @@ -697,48 +697,126 @@ nullchild(cbuf *cb, return retval; } -/*! -{ - "example-social:uint8-numbers": [17], - "@example-social:uint8-numbers": [ - { - "ietf-list-pagination:remaining": 5 - } - ] - } +/*! Encode json metadata + * This function could be more general, code based on two examples: + * 1) ietf-list-pagination:remaining + * { + * "example-social:uint8-numbers": [17], + * "@example-social:uint8-numbers": [ + * { + * "ietf-list-pagination:remaining": 5 + * } + * ] + * } + * 2) ietf-netconf-with-defaults:default: + * "@mtu" : { + * "ietf-netconf-with-defaults:default" : true + * }, */ static int -json_metadata_encoding(cbuf *cb, - cxobj *x, - int level, - int pretty, - char *modname, - char *name, - char *modname2, - char *name2, - char *val) +json_metadata_encoding(cbuf *cb, + cxobj *x, + int level, + int pretty, + char *prefix, + char *name, + char *modname2, + char *name2, + char *val, + int list) { - int retval = -1; - - cprintf(cb, ",\"@%s:%s\":[", modname, name); + cprintf(cb, ",\"@"); + if (prefix) + cprintf(cb, "%s:", prefix); + cprintf(cb, "%s\":", name); + if (list) + cprintf(cb, "["); cprintf(cb, "%*s", pretty?((level+1)*JSON_INDENT):0, "{"); cprintf(cb, "\"%s:%s\":%s", modname2, name2, val); cprintf(cb, "%*s", pretty?((level+1)*JSON_INDENT):0, "}"); - cprintf(cb, "%*s", pretty?(level*JSON_INDENT):0, "]"); + if (list) + cprintf(cb, "%*s", pretty?(level*JSON_INDENT):0, "]"); + return 0; +} + +/*! Encode XML attributes as JSON meta-data + * + * There are two methods for this: + * 1) Registered meta-data according to RFC 7952 using md:annotate. + * This is derived from a YANG module + * Examples: ietf-origin:origin, ietf-list-pagination: remaining + * 2) Assigned, if someother mechanism, eg XSD is used + * Example: ietf-restconf: default + * If neither matches, the attribute is skipped + * @param[in] xc XML attribute + * @param[in] xp XML node, parent of xa + * @param[in] yp Yang spec of xp + * @param[in] level Indentation level + * @param[in] level Indentation level + * @param[in] pretty Pretty-print output (2 means debug) + * @param[in] modname Name of yang module + * @param[in,out] metacb Encode into cbuf + * @see RFC7952 + */ +static int +xml2json_encode_attr(cxobj *xa, + cxobj *xp, + yang_stmt *yp, + int level, + int pretty, + char *modname, + cbuf *metacb) +{ + int retval = -1; + int ismeta = 0; + char *namespace = NULL; + yang_stmt *ymod; + enum rfc_6020 ykeyw; + + if (xml2ns(xa, xml_prefix(xa), &namespace) < 0) + goto done; + /* Check for (1) registered meta-data */ + if (namespace != NULL && yp){ + ykeyw = yang_keyword_get(yp); + if ((ymod = yang_find_module_by_namespace(ys_spec(yp), namespace)) != NULL){ + if (yang_metadata_annotation_check(xa, ymod, &ismeta) < 0) + goto done; + if (ismeta) + if (json_metadata_encoding(metacb, xp, level, pretty, + modname, xml_name(xp), + yang_argument_get(ymod), + xml_name(xa), + xml_value(xa), + ykeyw == Y_LEAF_LIST || ykeyw == Y_LIST) < 0) + goto done; + } + /* Check for (2) assigned - hardcoded for now */ + else if (strcmp(namespace, "urn:ietf:params:xml:ns:netconf:default:1.0") == 0 && + strcmp(xml_name(xa), "default") == 0){ + /* RFC 7952 / RFC 8040 defaults attribute */ + if (json_metadata_encoding(metacb, xp, level, pretty, + modname, xml_name(xp), + "ietf-netconf-with-defaults", + xml_name(xa), + xml_value(xa), + ykeyw == Y_LEAF_LIST || ykeyw == Y_LIST) < 0) + goto done; + } + } retval = 0; - // done: + done: return retval; } /*! Do the actual work of translating XML to JSON * @param[out] cb Cligen text buffer containing json on exit * @param[in] x XML tree structure containing XML to translate - * @param[in] yp Parent yang spec needed for body * @param[in] arraytype Does x occur in a array (of its parent) and how? * @param[in] level Indentation level * @param[in] pretty Pretty-print output (2 means debug) * @param[in] flat Dont print NO_ARRAY object name (for _vec call) - * @param[in] bodystr Set if value is string, 0 otherwise. Only if body + * @param[in] modname0 + * @param[out] metacbp Meta encoding of attribute * * @note Does not work with XML attributes * The following matrix explains how the mapping is done. @@ -776,7 +854,7 @@ xml2json1_cbuf(cbuf *cb, int pretty, int flat, char *modname0, - cbuf **metacbp) + cbuf *metacbp) { int retval = -1; int i; @@ -883,6 +961,10 @@ xml2json1_cbuf(cbuf *cb, default: break; } + if ((metacbc = cbuf_new()) == NULL){ + clicon_err(OE_UNIX, errno, "cbuf_new"); + goto done; + } /* Check for typed sub-body if: * arraytype=* but child-type is BODY_CHILD * This is code for writing 42 as "a":42 and not "a":"42" @@ -891,38 +973,10 @@ xml2json1_cbuf(cbuf *cb, for (i=0; i 0) { cprintf(cb, ",%s", pretty?"\n":""); --commas; } } - if (metacbc){ + if (cbuf_len(metacbc)){ cprintf(cb, "%s", cbuf_get(metacbc)); } -#if 0 /* identify md:annotations as RFC 7952 Sec 5.2.1*/ - for (i=0; ita_ss); + fprintf(stderr, "Timeout(%ds) after accept on socket:%d\n", _timeout_s, ta->ta_ss); exit(200); }