RESTCONF over mountpoints, extended api_path2xml_mnt with mount-point check

This commit is contained in:
Olof hagsand 2025-02-24 11:55:48 +01:00
parent 9086264b89
commit b0cc1857c0
17 changed files with 299 additions and 213 deletions

View file

@ -930,3 +930,31 @@ restconf_socket_init(const char *netns0,
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
return retval;
}
/*! Callback used by api_path2xml when yang mountpoint is empty
*
* @param[in] h Clixon handle
* @param[in] x XML node
* @param[out] yp YANG
*/
int
restconf_apipath_mount_cb(clixon_handle h,
cxobj *x,
yang_stmt **yp)
{
int retval = -1;
cxobj *xyanglib = NULL;
int ret;
if (clixon_plugin_yang_mount_all(h, x, NULL, NULL, &xyanglib) < 0)
goto done;
if (xyanglib != NULL){
if ((ret = yang_schema_yanglib_mount_parse(h, x, xyanglib, yp)) < 0)
goto done;
}
retval = 0;
done:
if (xyanglib)
xml_free(xyanglib);
return retval;
}

View file

@ -101,6 +101,7 @@ int restconf_drop_privileges(clixon_handle h);
int restconf_authentication_cb(clixon_handle h, void *req, int pretty, restconf_media media_out);
int restconf_config_init(clixon_handle h, cxobj *xrestconf);
int restconf_socket_init(const char *netns0, const char *addrstr, const char *addrtype, uint16_t port, int backlog, int flags, int *ss);
int restconf_apipath_mount_cb(clixon_handle h, cxobj *x, yang_stmt **yp);
#endif /* _RESTCONF_LIB_H_ */

View file

@ -246,6 +246,25 @@ api_data_write(clixon_handle h,
/* strip /... from start */
for (i=0; i<pi; i++)
api_path = index(api_path+1, '/');
/* Create config top-of-tree */
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
goto done;
/* Translate api_path to xml in the form of xtop/xbot */
xbot = xtop;
if (api_path){ /* If URI, otherwise top data/config object */
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
restconf_apipath_mount_cb, h, &xbot, &ybot, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
if (ybot){
if (ys_real_module(ybot, &ymodapi) < 0)
goto done;
}
}
if (api_path){
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
@ -260,24 +279,6 @@ api_data_write(clixon_handle h,
op = OP_MERGE; /* bad request if it does not exist */
else
op = OP_REPLACE; /* OP_CREATE if it does not exist */
/* Create config top-of-tree */
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
goto done;
/* Translate api_path to xml in the form of xtop/xbot */
xbot = xtop;
if (api_path){ /* If URI, otherwise top data/config object */
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
if (ybot){
if (ys_real_module(ybot, &ymodapi) < 0)
goto done;
}
}
/* 4.4.1: The message-body MUST contain exactly one instance of the
* expected data resource. (tested again below)
*/
@ -341,6 +342,7 @@ api_data_write(clixon_handle h,
}
break;
case YANG_DATA_JSON:
// XXX yspec is top-level, but data may be mounted yspec
if ((ret = clixon_json_parse_string(data, 1, yb, yspec, &xdata0, &xerr)) < 0){
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
goto done;
@ -749,7 +751,8 @@ api_data_delete(clixon_handle h,
goto done;
xbot = xtop;
if (api_path){
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
restconf_apipath_mount_cb, h, &xbot, &y, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)

View file

@ -67,6 +67,7 @@
static int api_data_pagination(clixon_handle h, void *req, char *api_path, int pi, cvec *qvec, int pretty, restconf_media media_out);
/*! Generic GET (both HEAD and GET)
*
* According to restconf
* @param[in] h Clixon handle
* @param[in] req Generic Www handle
@ -117,16 +118,15 @@ api_data_get2(clixon_handle h,
size_t xlen;
int i;
cxobj *x;
int ret;
cvec *nsc = NULL;
char *attr; /* attribute value string */
netconf_content content = CONTENT_ALL;
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
cxobj *xtop = NULL;
cxobj *xbot = NULL;
yang_stmt *y = NULL;
char *defaults = NULL;
cvec *nscd = NULL;
int ret;
clixon_debug(CLIXON_DBG_RESTCONF, "");
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -142,21 +142,28 @@ api_data_get2(clixon_handle h,
/* Translate api-path to xml, but to validate the api-path, note: strict=1
* xtop and xbot unnecessary for this function but needed by function
*/
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
goto done;
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
if (ret != 0 &&
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
restconf_apipath_mount_cb, h, NULL, &y, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
goto done;
goto ok;
}
/* Ad-hoc method to determine json pagination request:
* address list and one of "item/offset/.." is defined
*/
if (!head &&
if (!head && y &&
(yang_keyword_get(y) == Y_LIST || yang_keyword_get(y) == Y_LEAF_LIST) &&
(cvec_find(qvec, "where") || cvec_find(qvec, "sort-by") ||
cvec_find(qvec, "direction") || cvec_find(qvec, "offset") ||
@ -203,9 +210,7 @@ api_data_get2(clixon_handle h,
}
clixon_debug(CLIXON_DBG_RESTCONF, "path:%s", xpath);
ret = clicon_rpc_get(h, xpath, nsc, content, depth, defaults, &xret);
if (ret < 0){
if ((ret = clicon_rpc_get(h, xpath, nsc, content, depth, defaults, &xret)) < 0){
if (netconf_operation_failed_xml(&xerr, "protocol", clixon_err_reason()) < 0)
goto done;
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
@ -323,9 +328,9 @@ api_data_get2(clixon_handle h,
return retval;
}
/*! GET Collection
/*! GET pagination
*
* According to restconf collection draft. Lists, work in progress
* According to restconf pagination draft. Lists, work in progress
* @param[in] h Clixon handle
* @param[in] req Generic Www handle
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
@ -339,10 +344,6 @@ api_data_get2(clixon_handle h,
* @code
* curl -X GET http://localhost/restconf/data/interfaces
* @endcode
* A collection resource contains a set of data resources. It is used
* to represent a all instances or a subset of all instances in a YANG
* list or leaf-list.
* @see draft-ietf-netconf-restconf-collection-00.txt
*/
static int
api_data_pagination(clixon_handle h,
@ -395,10 +396,10 @@ api_data_pagination(clixon_handle h,
* xtop and xbot unnecessary for this function but needed by function
* Set strict=0 to accept list uri:s with =keys syntax
*/
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0)
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 0,
restconf_apipath_mount_cb, h, &xbot, &y, &xerr)) < 0)
goto done;
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc)
* XXX: xpath not used in collection?
*/
if (ret != 0 &&
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
@ -448,7 +449,7 @@ api_data_pagination(clixon_handle h,
clixon_err(OE_XML, EINVAL, "Invalid content attribute %d", content);
goto done;
}
/* Clixon extensions and collection attributes */
/* Clixon extensions and pagination attributes */
/* Check for depth attribute */
if ((attr = cvec_find_str(qvec, "depth")) != NULL){
clixon_debug(CLIXON_DBG_RESTCONF, "depth=%s", attr);
@ -635,7 +636,7 @@ api_data_head(clixon_handle h,
* @endcode
* Request may contain
* Accept: application/yang.data+json,application/yang.data+xml
* Response contains one of:
* Response contains one
* Content-Type: application/yang-data+xml
* Content-Type: application/yang-data+json
* NOTE: If a retrieval request for a data resource representing a YANG leaf-

View file

@ -483,7 +483,6 @@ yang_patch_do_merge(clixon_handle h,
}
if (key_xn != NULL)
xml_addsub(x_simple_patch, key_xn);
// Loop through the XML, create JSON from each one, and submit a simple patch
for (int k = 0; k < value_vec_len; k++) {
if (value_vec[k] != NULL) {
@ -683,7 +682,7 @@ yang_patch_do_edit(clixon_handle h,
// Get key field
/* Translate api_path to xml in the form of xtop/xbot */
xbot = xtop;
if ((ret = api_path2xml(cbuf_get(api_path_target), yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
if ((ret = api_path2xml_mnt(h, cbuf_get(api_path_target), yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)

View file

@ -203,7 +203,8 @@ api_data_post(clixon_handle h,
xbot = xtop;
if (api_path){
/* Translate api-path to xml, side-effect: validate the api-path, note: strict=1 */
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
restconf_apipath_mount_cb, h, &xbot, &ybot, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
@ -777,7 +778,8 @@ api_operations_post(clixon_handle h,
if (xml_rootchild(xtop, 0, &xtop) < 0)
goto done;
xbot = xtop;
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0)
if ((ret = api_path2xml_mnt(oppath, yspec, xtop, YC_SCHEMANODE, 1,
restconf_apipath_mount_cb, h, &xbot, &y, &xerr)) < 0)
goto done;
if (ret == 0){ /* validation failed */
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)

View file

@ -373,7 +373,7 @@ example_restconf_start(clixon_handle h)
* @see RFC 8528
*/
int
restconf_yang_mount(clixon_handle h,
example_restconf_yang_mount(clixon_handle h,
cxobj *xt,
int *config,
validate_level *vl,
@ -407,7 +407,6 @@ restconf_yang_mount(clixon_handle h,
if (xml_rootchild(*yanglib, 0, yanglib) < 0)
goto done;
}
retval = 0;
done:
if (cb)
@ -423,7 +422,7 @@ static clixon_plugin_api api = {
example_restconf_start,/* start */
NULL, /* exit */
.ca_auth=example_restconf_credentials, /* auth */
.ca_yang_mount=restconf_yang_mount, /* RFC 8528 schema mount */
.ca_yang_mount=example_restconf_yang_mount, /* RFC 8528 schema mount */
};
/*! Restconf plugin initialization

View file

@ -72,6 +72,9 @@ typedef struct {
yang_stmt *cp_yang; /* Corresponding yang spec (after XML match - ie resolved) */
} clixon_path;
/*! Callback if empty mount-point is encountered */
typedef int (api_path_mnt_cb_t)(clixon_handle h, cxobj *x, yang_stmt **yp);
/*
* Prototypes
*/
@ -84,6 +87,10 @@ int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, cvec **nsc, c
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
yang_class nodeclass, int strict,
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);
int api_path2xml_mnt(char *api_path, yang_stmt *yspec, cxobj *xtop,
yang_class nodeclass, int strict,
api_path_mnt_cb_t mnt_cb, void *arg,
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);
int xml2api_path_1(cxobj *x, cbuf *cb);
int clixon_xml_find_api_path(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format,
...) __attribute__ ((format (printf, 5, 6)));;

View file

@ -625,7 +625,8 @@ xml2json_encode_leafs(cxobj *xb,
return retval;
}
/* X has no XML child - no body.
/*! Check if X has no XML child - no body.
*
* If x is a container, use {} instead of null
* if leaf or leaf-list then assume EMPTY type, then [null]
* else null
@ -1052,7 +1053,6 @@ xml2json1_cbuf(cbuf *cb,
*
* XML-style namespace notation in tree, but RFC7951 in output assume yang
* populated
*
* @param[in,out] cb Cligen buffer to write to
* @param[in] x XML tree to translate from
* @param[in] pretty Set if output is pretty-printed
@ -1123,7 +1123,6 @@ xml2json_cbuf1(cbuf *cb,
*
* XML-style namespace notation in tree, but RFC7951 in output assume yang
* populated
*
* @param[in,out] cb Cligen buffer to write to
* @param[in] xt Top-level xml object
* @param[in] pretty Set if output is pretty-printed
@ -1449,11 +1448,12 @@ _json_parse(char *str,
{
int retval = -1;
clixon_json_yacc jy = {0,};
int ret;
cxobj *x;
cbuf *cberr = NULL;
int i;
int failed = 0; /* yang assignment */
yang_stmt *yspec1;
int ret;
if (clixon_debug_get() & CLIXON_DBG_DETAIL)
clixon_debug(CLIXON_DBG_PARSE|CLIXON_DBG_DETAIL, "%s", str);
@ -1473,6 +1473,10 @@ _json_parse(char *str,
clixon_err(OE_JSON, 0, "JSON parser error with no error code (should not happen)");
goto done;
}
if (xml_spec(xt))
yspec1 = ys_spec(xml_spec(xt));
else
yspec1 = yspec;
/* Traverse new objects */
for (i = 0; i < jy.jy_xlen; i++) {
x = jy.jy_xvec[i];
@ -1493,7 +1497,8 @@ _json_parse(char *str,
}
}
/* Names are split into name/prefix, but now add namespace info */
if ((ret = json_xmlns_translate(yspec, x, xerr)) < 0)
// XXX yspec is top-level but x may be across mtpoint
if ((ret = json_xmlns_translate(yspec1, x, xerr)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -1501,7 +1506,9 @@ _json_parse(char *str,
* XXX should be xml_bind_yang0_parent() sometimes.
*/
switch (yb){
case YB_PARENT:
case YB_NONE:
break;
case YB_MODULE:
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
goto done;
if (ret == 0)
@ -1513,14 +1520,12 @@ _json_parse(char *str,
if (ret == 0)
failed++;
break;
case YB_MODULE:
case YB_PARENT:
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
goto done;
if (ret == 0)
failed++;
break;
case YB_NONE:
break;
case YB_RPC:
if ((ret = xml_bind_yang_rpc(NULL, x, yspec, xerr)) < 0)
goto done;

View file

@ -1035,6 +1035,8 @@ api_path2xpath(char *api_path,
* @param[in] y0 Yang spec for x0
* @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
* @param[in] mnt_cb Callback to mount new yang if mount-point is empty
* @param[in] arg Argument to callback
* @param[out] xbotp Resulting xml tree
* @param[out] ybotp Yang spec matching xpathp
* @param[out] xerr Netconf error message (if retval=0)
@ -1053,6 +1055,8 @@ api_path2xml_vec(char **vec,
yang_stmt *y0,
yang_class nodeclass,
int strict,
api_path_mnt_cb_t mnt_cb,
void *arg,
cxobj **xbotp,
yang_stmt **ybotp,
cxobj **xerr)
@ -1081,6 +1085,7 @@ api_path2xml_vec(char **vec,
char *xpath = NULL;
cvec *nsc = NULL;
int ymtpoint; /* y is mountpoint */
int ret;
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
if (xbotp)
@ -1112,9 +1117,12 @@ api_path2xml_vec(char **vec,
goto fail;
}
if (ymtpoint){
/* XXX: Ignore return value: if none are mounted, no change of yspec is made here */
if (yang_mount_get_yspec_any(y0, &y0) < 0)
if ((ret = yang_mount_get_yspec_any(y0, &y0)) < 0)
goto done;
if (ret == 0 && mnt_cb != NULL){
if (mnt_cb(arg, x0, &y0) < 0)
goto done;
}
}
if ((ymod = yang_find_module_by_name(y0, prefix)) == NULL){
cprintf(cberr, "No such yang module prefix");
@ -1277,6 +1285,7 @@ api_path2xml_vec(char **vec,
if ((retval = api_path2xml_vec(vec+1, nvec-1,
x, y,
nodeclass, strict,
mnt_cb, arg,
xbotp, ybotp, xerr)) < 1)
goto done;
ok:
@ -1344,6 +1353,44 @@ api_path2xml(char *api_path,
cxobj **xbotp,
yang_stmt **ybotp,
cxobj **xerr)
{
return api_path2xml_mnt(api_path, yspec, xtop, nodeclass, strict, NULL, NULL, xbotp, ybotp, xerr);
}
/*! Create xml tree from api-path as vector and as a side-effect mount yangs
*
* Specialized version of api_path2xml_vec
* @param[in] h Clixon handle
* @param[in] vec APIpath as char* vector
* @param[in] nvec Length of vec
* @param[in] x0 XML tree so far
* @param[in] y0 Yang spec for x0
* @param[in] nodeclass Set to schema nodes, data nodes, etc
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
* @param[in] mnt_cb Callback if mount-point is empty
* @param[in] arg Argument to callback
* @param[out] xbotp Resulting xml tree
* @param[out] ybotp Yang spec matching xpathp
* @param[out] xerr Netconf error message (if retval=0)
* @retval 1 OK
* @retval 0 Invalid api_path or associated XML, netconf error
* @retval -1 Fatal error
*
* @note both retval -1 set clixon_err, retval 0 set xerr netconf xml
* @see api_path2xpath For api-path to xml xpath translation
* @see api_path2xml
*/
int
api_path2xml_mnt(char *api_path,
yang_stmt *yspec,
cxobj *xtop,
yang_class nodeclass,
int strict,
api_path_mnt_cb_t mnt_cb,
void *arg,
cxobj **xbotp,
yang_stmt **ybotp,
cxobj **xerr)
{
int retval = -1;
char **vec = NULL;
@ -1376,6 +1423,7 @@ api_path2xml(char *api_path,
nvec--; /* NULL-terminated */
if ((retval = api_path2xml_vec(vec+1, nvec,
xtop, yspec, nodeclass, strict,
mnt_cb, arg,
xbotp, ybotp, xerr)) < 1)
goto done;
/* Fix namespace */
@ -1471,14 +1519,6 @@ xml2api_path_1(cxobj *x,
default:
break;
}
#if 0
{ /* Just for testing */
cxobj *xc;
if ((xc = xml_child_i_type(x, 0, CX_ELMNT)) != NULL)
if (xml2api_path_1(xc, cb) < 0)
goto done;
}
#endif
ok:
retval = 0;
done:

View file

@ -932,7 +932,7 @@ clixon_plugin_yang_mount_all(clixon_handle h,
return retval;
}
/*! Call single backend statedata callback
/*! Call single backend system-only callback
*
* Create an xml tree (xret) for one callback only on the form:
* <config>...</config>,

View file

@ -890,7 +890,6 @@ _xml_parse(const char *str,
}
xy.xy_xtop = xt;
xy.xy_xparent = xt;
xy.xy_yspec = yspec;
if (clixon_xml_parsel_init(&xy) < 0)
goto done;
if (clixon_xml_parseparse(&xy) != 0) /* yacc returns 1 on error */

View file

@ -51,7 +51,6 @@ struct clixon_xml_parse_yacc {
cxobj *xy_xtop; /* cxobj top element (fixed) */
cxobj *xy_xelement; /* cxobj active element (changes with parse context) */
cxobj *xy_xparent; /* cxobj parent element (changes with parse context) */
yang_stmt *xy_yspec; /* If set, top-level yang-spec */
int xy_lex_state; /* lex return state */
cxobj **xy_xvec; /* Vector of created top-level nodes (to know which are created) */
int xy_xlen; /* Length of xy_xvec */

View file

@ -2445,7 +2445,7 @@ yang_spec_print(FILE *f,
}
/*! Print yang top-level specs
*
*a
* @param[in] f File to print to.
* @param[in] ymounts Yang mounts to print
*/

View file

@ -775,6 +775,7 @@ yang_schema_find_share(clixon_handle h,
* @param[in] h Clixon handle
* @param[in] xt XML tree node
* @param[in] xyanglib XML yang-lib
* @param[out] yspecp Resulting mounted yang spec
* @retval 1 OK
* @retval 0 No yanglib or problem when parsing yanglib
* @retval -1 Error

View file

@ -38,7 +38,6 @@ if [ -d ${TOP_SRCDIR}/yang/clixon ]; then
else
cp /usr/local/share/clixon/$y $dir/
fi
if [ "${WITH_RESTCONF}" = "native" ]; then
# Create server certs
certdir=$dir/certs

View file

@ -241,6 +241,9 @@ expectpart "$($clixon_cli -1 -f $cfg show config xml -- -m clixon-mount0 -M urn:
new "restconf get config mntpoint"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/clixon-example:top/mylist=x/root)" 0 "HTTP/$HVER 200" '<root xmlns="urn:example:clixon"><mount1 xmlns="urn:example:mount1"><mylist1><name1>x1</name1><options xmlns="urn:example:mount2"><option2>bar</option2></options></mylist1></mount1><yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module-set><name>mylabel</name><module><name>clixon-mount0</name><namespace>urn:example:mount0</namespace></module></module-set></yang-library></root>'
new "restconf get across mountpoint"
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/clixon-example:top/mylist=x/root/clixon-mount1:mount1)" 0 "HTTP/$HVER 200" '<mount1 xmlns="urn:example:mount1"><mylist1><name1>x1</name1><options xmlns="urn:example:mount2"><option2>bar</option2></options></mylist1></mount1>' --not-- '<root' '<yang-library'
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
stop_restconf