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
* 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 `<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
[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/)

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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 <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++){
xc = xml_child_i(x, i);
if (xml_type(xc) == CX_ATTR){
int ismeta = 0;
char *namespace = NULL;
yang_stmt *ymod;
cbuf *metacb = NULL;
if (xml2ns(xc, xml_prefix(xc), &namespace) < 0)
if (metacbp &&
xml2json_encode_attr(xc, x, ys, level, pretty, modname, metacbp) < 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 ((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 */
continue;
}
xc_arraytype = array_eval(i?xml_child_i(x,i-1):NULL,
xc,
@ -931,46 +985,17 @@ xml2json1_cbuf(cbuf *cb,
xc,
xc_arraytype,
level+1, pretty, 0, modname0,
&metacbc) < 0)
metacbc) < 0)
goto done;
if (commas > 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; 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){
case BODY_ARRAY:
break;

View file

@ -790,7 +790,6 @@ yang_metadata_init(clicon_handle h)
int retval = -1;
clixon_plugin_t *cp = NULL;
/* Create a pseudo-plugin to create extension callback to set the ietf-yang-meta
* 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" \
"Content-Type: application/yang-data+json" \
"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"
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_free(ssl);
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);
free(sd);
if (_connects == 0)
@ -365,7 +365,7 @@ tls_timeout_cb(int fd,
{
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);
}