Assigned meta-data for the ietf-netconf-with-defaults:default attribute for JSON (RFC8040 Sec 5.3.2)
This commit is contained in:
parent
a6f67755b3
commit
3bf8a7dca3
9 changed files with 140 additions and 106 deletions
18
CHANGELOG.md
18
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 `<socket>` list has been extended with a `call-home` presence container including:
|
||||
|
|
|
|||
|
|
@ -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/)
|
||||
|
|
|
|||
|
|
@ -507,9 +507,8 @@ restconf_insert_attributes(cxobj *xdata,
|
|||
* - 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.
|
||||
* 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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 *prefix,
|
||||
char *name,
|
||||
char *modname2,
|
||||
char *name2,
|
||||
char *val)
|
||||
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, "}");
|
||||
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 */
|
||||
}
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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)" \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue