Assigned meta-data for the ietf-netconf-with-defaults:default attribute for JSON (RFC8040 Sec 5.3.2)

This commit is contained in:
Olof hagsand 2022-09-16 14:46:41 +02:00
parent a6f67755b3
commit 3bf8a7dca3
9 changed files with 140 additions and 106 deletions

View file

@ -41,9 +41,21 @@ Expected: September 2022
### New features ### New features
* With-defaults RFC6243 * With-defaults capability
* see [Netconf With-defaults Capability](https://github.com/clicon/clixon/issues/262) * Standards
* RESTCONF call home according to RFC 8071 * 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 * clixon-restconf.yang extended with callhome inspired by ietf-restconf-server.yang
* See e.g., draft-ietf-netconf-restconf-client-server-26.txt * See e.g., draft-ietf-netconf-restconf-client-server-26.txt
* The `<socket>` list has been extended with a `call-home` presence container including: * The `<socket>` list has been extended with a `call-home` presence container including:

View file

@ -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 Clixon interaction is best done posting issues, pull requests, or joining the
[slack channel](https://clixondev.slack.com). [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/) Clixon is sponsored by [Rubicon Communications LLC(Netgate)](https://www.netgate.com/)

View file

@ -503,13 +503,12 @@ restconf_insert_attributes(cxobj *xdata,
* @retval 0 OK, all callbacks executed OK * @retval 0 OK, all callbacks executed OK
* @retval -1 Error in one callback * @retval -1 Error in one callback
* @note This extension adds semantics to YANG according to RFC8040 as follows: * @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 list-stmt is not required to have a key-stmt defined.(NB!!)
* - The if-feature-stmt is ignored if present. * - The if-feature-stmt is ignored if present.
* - The config-stmt is ignored if present. * - The config-stmt is ignored if present.
* - The available identity values for any 'identityref' * - The available identity values for any 'identityref'
* leaf or leaf-list nodes are limited to the module * leaf or leaf-list nodes are limited to the module containing this extension statement and
* containing this extension statement and the modules * the modules imported into that module.
* imported into that module.
*/ */
int int
restconf_main_extension_cb(clicon_handle h, restconf_main_extension_cb(clicon_handle h,

View file

@ -315,7 +315,6 @@ main(int argc,
char *inline_config = NULL; char *inline_config = NULL;
size_t sz; size_t sz;
/* In the startup, logs to stderr & debug flag set later */ /* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, logdst); clicon_log_init(__PROGRAM__, LOG_INFO, logdst);

View file

@ -141,8 +141,8 @@ typedef int (plgexit_t)(clicon_handle); /* Plugin exit */
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @param[in] yext Yang node of extension * @param[in] yext Yang node of extension
* @param[in] ys Yang node of (unknown) statement belonging to extension * @param[in] ys Yang node of (unknown) statement belonging to extension
* @retval 0 OK, all callbacks executed OK * @retval 0 OK, all callbacks executed OK
* @retval -1 Error in one callback * @retval -1 Error in one callback
*/ */
typedef int (plgextension_t)(clicon_handle h, yang_stmt *yext, yang_stmt *ys); typedef int (plgextension_t)(clicon_handle h, yang_stmt *yext, yang_stmt *ys);

View file

@ -697,48 +697,126 @@ nullchild(cbuf *cb,
return retval; return retval;
} }
/*! /*! Encode json metadata
{ * This function could be more general, code based on two examples:
"example-social:uint8-numbers": [17], * 1) ietf-list-pagination:remaining
"@example-social:uint8-numbers": [ * {
{ * "example-social:uint8-numbers": [17],
"ietf-list-pagination:remaining": 5 * "@example-social:uint8-numbers": [
} * {
] * "ietf-list-pagination:remaining": 5
} * }
* ]
* }
* 2) ietf-netconf-with-defaults:default:
* "@mtu" : {
* "ietf-netconf-with-defaults:default" : true
* },
*/ */
static int static int
json_metadata_encoding(cbuf *cb, json_metadata_encoding(cbuf *cb,
cxobj *x, cxobj *x,
int level, int level,
int pretty, int pretty,
char *modname, char *prefix,
char *name, char *name,
char *modname2, char *modname2,
char *name2, char *name2,
char *val) char *val,
int list)
{ {
int retval = -1; cprintf(cb, ",\"@");
if (prefix)
cprintf(cb, ",\"@%s:%s\":[", modname, name); cprintf(cb, "%s:", prefix);
cprintf(cb, "%s\":", name);
if (list)
cprintf(cb, "[");
cprintf(cb, "%*s", pretty?((level+1)*JSON_INDENT):0, "{"); cprintf(cb, "%*s", pretty?((level+1)*JSON_INDENT):0, "{");
cprintf(cb, "\"%s:%s\":%s", modname2, name2, val); cprintf(cb, "\"%s:%s\":%s", modname2, name2, val);
cprintf(cb, "%*s", pretty?((level+1)*JSON_INDENT):0, "}"); 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; retval = 0;
// done: done:
return retval; return retval;
} }
/*! Do the actual work of translating XML to JSON /*! Do the actual work of translating XML to JSON
* @param[out] cb Cligen text buffer containing json on exit * @param[out] cb Cligen text buffer containing json on exit
* @param[in] x XML tree structure containing XML to translate * @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] arraytype Does x occur in a array (of its parent) and how?
* @param[in] level Indentation level * @param[in] level Indentation level
* @param[in] pretty Pretty-print output (2 means debug) * @param[in] pretty Pretty-print output (2 means debug)
* @param[in] flat Dont print NO_ARRAY object name (for _vec call) * @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 * @note Does not work with XML attributes
* The following matrix explains how the mapping is done. * The following matrix explains how the mapping is done.
@ -776,7 +854,7 @@ xml2json1_cbuf(cbuf *cb,
int pretty, int pretty,
int flat, int flat,
char *modname0, char *modname0,
cbuf **metacbp) cbuf *metacbp)
{ {
int retval = -1; int retval = -1;
int i; int i;
@ -883,6 +961,10 @@ xml2json1_cbuf(cbuf *cb,
default: default:
break; break;
} }
if ((metacbc = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
/* Check for typed sub-body if: /* Check for typed sub-body if:
* arraytype=* but child-type is BODY_CHILD * arraytype=* but child-type is BODY_CHILD
* This is code for writing <a>42</a> as "a":42 and not "a":"42" * This is code for writing <a>42</a> as "a":42 and not "a":"42"
@ -891,38 +973,10 @@ xml2json1_cbuf(cbuf *cb,
for (i=0; i<xml_child_nr(x); i++){ for (i=0; i<xml_child_nr(x); i++){
xc = xml_child_i(x, i); xc = xml_child_i(x, i);
if (xml_type(xc) == CX_ATTR){ if (xml_type(xc) == CX_ATTR){
int ismeta = 0; if (metacbp &&
char *namespace = NULL; xml2json_encode_attr(xc, x, ys, level, pretty, modname, metacbp) < 0)
yang_stmt *ymod;
cbuf *metacb = NULL;
if (xml2ns(xc, xml_prefix(xc), &namespace) < 0)
goto done; goto done;
if (namespace == NULL) continue;
continue;
if ((ymod = yang_find_module_by_namespace(ys_spec(ys), namespace)) == NULL)
continue;
if (xml2ns(xc, xml_prefix(xc), &namespace) < 0)
goto done;
if (yang_metadata_annotation_check(xc, ymod, &ismeta) < 0)
goto done;
if (!ismeta)
continue;
if ((metacb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
if (json_metadata_encoding(metacb, x, level, pretty,
modname, xml_name(x),
yang_argument_get(ymod),
xml_name(xc),
xml_value(xc)) < 0)
goto done;
if (metacbp)
*metacbp = metacb;
else
cbuf_free(metacb);
continue; /* XXX Only xmlns attributes mapped */
} }
xc_arraytype = array_eval(i?xml_child_i(x,i-1):NULL, xc_arraytype = array_eval(i?xml_child_i(x,i-1):NULL,
xc, xc,
@ -931,46 +985,17 @@ xml2json1_cbuf(cbuf *cb,
xc, xc,
xc_arraytype, xc_arraytype,
level+1, pretty, 0, modname0, level+1, pretty, 0, modname0,
&metacbc) < 0) metacbc) < 0)
goto done; goto done;
if (commas > 0) { if (commas > 0) {
cprintf(cb, ",%s", pretty?"\n":""); cprintf(cb, ",%s", pretty?"\n":"");
--commas; --commas;
} }
} }
if (metacbc){ if (cbuf_len(metacbc)){
cprintf(cb, "%s", cbuf_get(metacbc)); cprintf(cb, "%s", cbuf_get(metacbc));
} }
#if 0 /* identify md:annotations as RFC 7952 Sec 5.2.1*/
for (i=0; i<xml_child_nr(x); i++){
xc = xml_child_i(x, i);
if (xml_type(xc) == CX_ATTR){
int ismeta = 0;
char *namespace = NULL;
yang_stmt *ymod;
if (xml2ns(xc, xml_prefix(xc), &namespace) < 0)
goto done;
if (namespace == NULL)
continue;
if ((ymod = yang_find_module_by_namespace(ys_spec(ys), namespace)) == NULL)
continue;
if (xml2ns(xc, xml_prefix(xc), &namespace) < 0)
goto done;
if (yang_metadata_annotation_check(xc, ymod, &ismeta) < 0)
goto done;
if (!ismeta)
continue;
if (json_metadata_encoding(cb, x, level, pretty,
modname, xml_name(x),
yang_argument_get(ymod),
xml_name(xc),
xml_value(xc)) < 0)
goto done;
}
}
#endif
switch (arraytype){ switch (arraytype){
case BODY_ARRAY: case BODY_ARRAY:
break; break;

View file

@ -790,7 +790,6 @@ yang_metadata_init(clicon_handle h)
int retval = -1; int retval = -1;
clixon_plugin_t *cp = NULL; clixon_plugin_t *cp = NULL;
/* Create a pseudo-plugin to create extension callback to set the ietf-yang-meta /* Create a pseudo-plugin to create extension callback to set the ietf-yang-meta
* yang-data extension for api-root top-level restconf function. * yang-data extension for api-root top-level restconf function.
*/ */

View file

@ -557,7 +557,7 @@ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+json' $RCP
"HTTP/$HVER 200" \ "HTTP/$HVER 200" \
"Content-Type: application/yang-data+json" \ "Content-Type: application/yang-data+json" \
"Cache-Control: no-cache" \ "Cache-Control: no-cache" \
'{"example:interface":\[{"name":"eth1","mtu":1500,"status":"ok"}\]}' '{"example:interface":\[{"name":"eth1","mtu":1500,"status":"ok","@mtu":{"ietf-netconf-with-defaults:default":true},"@status":{"ietf-netconf-with-defaults:default":true}}\]}'
new "rfc8040 B.3.9. RESTCONF with-defaults parameter = report-all-tagged xml" new "rfc8040 B.3.9. RESTCONF with-defaults parameter = report-all-tagged xml"
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:interfaces/interface=eth1?with-defaults=report-all-tagged)" \ expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/example:interfaces/interface=eth1?with-defaults=report-all-tagged)" \

View file

@ -224,7 +224,7 @@ tls_server_reply_cb(int s,
SSL_shutdown(ssl); SSL_shutdown(ssl);
SSL_free(ssl); SSL_free(ssl);
clixon_event_unreg_fd(s, tls_server_reply_cb); clixon_event_unreg_fd(s, tls_server_reply_cb);
fprintf(stdout, "Close %d remote %lu\n", seq, td.tv_sec); fprintf(stdout, "Close %d remote\n", seq);
close(s); close(s);
free(sd); free(sd);
if (_connects == 0) if (_connects == 0)
@ -365,7 +365,7 @@ tls_timeout_cb(int fd,
{ {
tls_accept_handle *ta = (tls_accept_handle *)arg; tls_accept_handle *ta = (tls_accept_handle *)arg;
fprintf(stderr, "Timeout on socket:%d\n", ta->ta_ss); fprintf(stderr, "Timeout(%ds) after accept on socket:%d\n", _timeout_s, ta->ta_ss);
exit(200); exit(200);
} }