RESTCONF over mountpoints, extended api_path2xml_mnt with mount-point check
This commit is contained in:
parent
9086264b89
commit
b0cc1857c0
17 changed files with 299 additions and 213 deletions
|
|
@ -930,3 +930,31 @@ restconf_socket_init(const char *netns0,
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
|
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
|
||||||
return 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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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_authentication_cb(clixon_handle h, void *req, int pretty, restconf_media media_out);
|
||||||
int restconf_config_init(clixon_handle h, cxobj *xrestconf);
|
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_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_ */
|
#endif /* _RESTCONF_LIB_H_ */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -246,6 +246,25 @@ api_data_write(clixon_handle h,
|
||||||
/* strip /... from start */
|
/* strip /... from start */
|
||||||
for (i=0; i<pi; i++)
|
for (i=0; i<pi; i++)
|
||||||
api_path = index(api_path+1, '/');
|
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){
|
if (api_path){
|
||||||
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
|
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
|
||||||
if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
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 */
|
op = OP_MERGE; /* bad request if it does not exist */
|
||||||
else
|
else
|
||||||
op = OP_REPLACE; /* OP_CREATE if it does not exist */
|
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
|
/* 4.4.1: The message-body MUST contain exactly one instance of the
|
||||||
* expected data resource. (tested again below)
|
* expected data resource. (tested again below)
|
||||||
*/
|
*/
|
||||||
|
|
@ -341,6 +342,7 @@ api_data_write(clixon_handle h,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case YANG_DATA_JSON:
|
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 ((ret = clixon_json_parse_string(data, 1, yb, yspec, &xdata0, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -749,7 +751,8 @@ api_data_delete(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path){
|
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;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
|
|
||||||
|
|
@ -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);
|
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)
|
/*! Generic GET (both HEAD and GET)
|
||||||
|
*
|
||||||
* According to restconf
|
* According to restconf
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
|
|
@ -117,16 +118,15 @@ api_data_get2(clixon_handle h,
|
||||||
size_t xlen;
|
size_t xlen;
|
||||||
int i;
|
int i;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
int ret;
|
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
char *attr; /* attribute value string */
|
char *attr; /* attribute value string */
|
||||||
netconf_content content = CONTENT_ALL;
|
netconf_content content = CONTENT_ALL;
|
||||||
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
||||||
cxobj *xtop = NULL;
|
cxobj *xtop = NULL;
|
||||||
cxobj *xbot = NULL;
|
|
||||||
yang_stmt *y = NULL;
|
yang_stmt *y = NULL;
|
||||||
char *defaults = NULL;
|
char *defaults = NULL;
|
||||||
cvec *nscd = NULL;
|
cvec *nscd = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
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
|
/* 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
|
* 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)
|
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
|
||||||
goto done;
|
restconf_apipath_mount_cb, h, NULL, &y, &xerr)) < 0)
|
||||||
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
|
|
||||||
if (ret != 0 &&
|
|
||||||
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
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:
|
/* Ad-hoc method to determine json pagination request:
|
||||||
* address list and one of "item/offset/.." is defined
|
* 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) &&
|
(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, "where") || cvec_find(qvec, "sort-by") ||
|
||||||
cvec_find(qvec, "direction") || cvec_find(qvec, "offset") ||
|
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);
|
clixon_debug(CLIXON_DBG_RESTCONF, "path:%s", xpath);
|
||||||
ret = clicon_rpc_get(h, xpath, nsc, content, depth, defaults, &xret);
|
if ((ret = clicon_rpc_get(h, xpath, nsc, content, depth, defaults, &xret)) < 0){
|
||||||
|
|
||||||
if (ret < 0){
|
|
||||||
if (netconf_operation_failed_xml(&xerr, "protocol", clixon_err_reason()) < 0)
|
if (netconf_operation_failed_xml(&xerr, "protocol", clixon_err_reason()) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
|
@ -323,9 +328,9 @@ api_data_get2(clixon_handle h,
|
||||||
return retval;
|
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] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
* @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
|
* @code
|
||||||
* curl -X GET http://localhost/restconf/data/interfaces
|
* curl -X GET http://localhost/restconf/data/interfaces
|
||||||
* @endcode
|
* @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
|
static int
|
||||||
api_data_pagination(clixon_handle h,
|
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
|
* xtop and xbot unnecessary for this function but needed by function
|
||||||
* Set strict=0 to accept list uri:s with =keys syntax
|
* 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;
|
goto done;
|
||||||
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc)
|
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc)
|
||||||
* XXX: xpath not used in collection?
|
|
||||||
*/
|
*/
|
||||||
if (ret != 0 &&
|
if (ret != 0 &&
|
||||||
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 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);
|
clixon_err(OE_XML, EINVAL, "Invalid content attribute %d", content);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Clixon extensions and collection attributes */
|
/* Clixon extensions and pagination attributes */
|
||||||
/* Check for depth attribute */
|
/* Check for depth attribute */
|
||||||
if ((attr = cvec_find_str(qvec, "depth")) != NULL){
|
if ((attr = cvec_find_str(qvec, "depth")) != NULL){
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "depth=%s", attr);
|
clixon_debug(CLIXON_DBG_RESTCONF, "depth=%s", attr);
|
||||||
|
|
@ -635,7 +636,7 @@ api_data_head(clixon_handle h,
|
||||||
* @endcode
|
* @endcode
|
||||||
* Request may contain
|
* Request may contain
|
||||||
* Accept: application/yang.data+json,application/yang.data+xml
|
* 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+xml
|
||||||
* Content-Type: application/yang-data+json
|
* Content-Type: application/yang-data+json
|
||||||
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
||||||
|
|
|
||||||
|
|
@ -483,7 +483,6 @@ yang_patch_do_merge(clixon_handle h,
|
||||||
}
|
}
|
||||||
if (key_xn != NULL)
|
if (key_xn != NULL)
|
||||||
xml_addsub(x_simple_patch, key_xn);
|
xml_addsub(x_simple_patch, key_xn);
|
||||||
|
|
||||||
// Loop through the XML, create JSON from each one, and submit a simple patch
|
// Loop through the XML, create JSON from each one, and submit a simple patch
|
||||||
for (int k = 0; k < value_vec_len; k++) {
|
for (int k = 0; k < value_vec_len; k++) {
|
||||||
if (value_vec[k] != NULL) {
|
if (value_vec[k] != NULL) {
|
||||||
|
|
@ -683,7 +682,7 @@ yang_patch_do_edit(clixon_handle h,
|
||||||
// Get key field
|
// Get key field
|
||||||
/* Translate api_path to xml in the form of xtop/xbot */
|
/* Translate api_path to xml in the form of xtop/xbot */
|
||||||
xbot = xtop;
|
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;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,8 @@ api_data_post(clixon_handle h,
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path){
|
if (api_path){
|
||||||
/* Translate api-path to xml, side-effect: validate the api-path, note: strict=1 */
|
/* 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;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
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)
|
if (xml_rootchild(xtop, 0, &xtop) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
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;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
|
|
||||||
|
|
@ -373,11 +373,11 @@ example_restconf_start(clixon_handle h)
|
||||||
* @see RFC 8528
|
* @see RFC 8528
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
restconf_yang_mount(clixon_handle h,
|
example_restconf_yang_mount(clixon_handle h,
|
||||||
cxobj *xt,
|
cxobj *xt,
|
||||||
int *config,
|
int *config,
|
||||||
validate_level *vl,
|
validate_level *vl,
|
||||||
cxobj **yanglib)
|
cxobj **yanglib)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
|
|
@ -407,7 +407,6 @@ restconf_yang_mount(clixon_handle h,
|
||||||
if (xml_rootchild(*yanglib, 0, yanglib) < 0)
|
if (xml_rootchild(*yanglib, 0, yanglib) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cb)
|
if (cb)
|
||||||
|
|
@ -423,7 +422,7 @@ static clixon_plugin_api api = {
|
||||||
example_restconf_start,/* start */
|
example_restconf_start,/* start */
|
||||||
NULL, /* exit */
|
NULL, /* exit */
|
||||||
.ca_auth=example_restconf_credentials, /* auth */
|
.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
|
/*! Restconf plugin initialization
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,9 @@ typedef struct {
|
||||||
yang_stmt *cp_yang; /* Corresponding yang spec (after XML match - ie resolved) */
|
yang_stmt *cp_yang; /* Corresponding yang spec (after XML match - ie resolved) */
|
||||||
} clixon_path;
|
} 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
|
* 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,
|
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
||||||
yang_class nodeclass, int strict,
|
yang_class nodeclass, int strict,
|
||||||
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);
|
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 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,
|
int clixon_xml_find_api_path(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format,
|
||||||
...) __attribute__ ((format (printf, 5, 6)));;
|
...) __attribute__ ((format (printf, 5, 6)));;
|
||||||
|
|
|
||||||
|
|
@ -625,7 +625,8 @@ xml2json_encode_leafs(cxobj *xb,
|
||||||
return retval;
|
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 x is a container, use {} instead of null
|
||||||
* if leaf or leaf-list then assume EMPTY type, then [null]
|
* if leaf or leaf-list then assume EMPTY type, then [null]
|
||||||
* else null
|
* else null
|
||||||
|
|
@ -1052,7 +1053,6 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
*
|
*
|
||||||
* XML-style namespace notation in tree, but RFC7951 in output assume yang
|
* XML-style namespace notation in tree, but RFC7951 in output assume yang
|
||||||
* populated
|
* populated
|
||||||
*
|
|
||||||
* @param[in,out] cb Cligen buffer to write to
|
* @param[in,out] cb Cligen buffer to write to
|
||||||
* @param[in] x XML tree to translate from
|
* @param[in] x XML tree to translate from
|
||||||
* @param[in] pretty Set if output is pretty-printed
|
* @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
|
* XML-style namespace notation in tree, but RFC7951 in output assume yang
|
||||||
* populated
|
* populated
|
||||||
*
|
|
||||||
* @param[in,out] cb Cligen buffer to write to
|
* @param[in,out] cb Cligen buffer to write to
|
||||||
* @param[in] xt Top-level xml object
|
* @param[in] xt Top-level xml object
|
||||||
* @param[in] pretty Set if output is pretty-printed
|
* @param[in] pretty Set if output is pretty-printed
|
||||||
|
|
@ -1449,11 +1448,12 @@ _json_parse(char *str,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
clixon_json_yacc jy = {0,};
|
clixon_json_yacc jy = {0,};
|
||||||
int ret;
|
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cbuf *cberr = NULL;
|
cbuf *cberr = NULL;
|
||||||
int i;
|
int i;
|
||||||
int failed = 0; /* yang assignment */
|
int failed = 0; /* yang assignment */
|
||||||
|
yang_stmt *yspec1;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (clixon_debug_get() & CLIXON_DBG_DETAIL)
|
if (clixon_debug_get() & CLIXON_DBG_DETAIL)
|
||||||
clixon_debug(CLIXON_DBG_PARSE|CLIXON_DBG_DETAIL, "%s", str);
|
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)");
|
clixon_err(OE_JSON, 0, "JSON parser error with no error code (should not happen)");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
if (xml_spec(xt))
|
||||||
|
yspec1 = ys_spec(xml_spec(xt));
|
||||||
|
else
|
||||||
|
yspec1 = yspec;
|
||||||
/* Traverse new objects */
|
/* Traverse new objects */
|
||||||
for (i = 0; i < jy.jy_xlen; i++) {
|
for (i = 0; i < jy.jy_xlen; i++) {
|
||||||
x = jy.jy_xvec[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 */
|
/* 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;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -1501,7 +1506,9 @@ _json_parse(char *str,
|
||||||
* XXX should be xml_bind_yang0_parent() sometimes.
|
* XXX should be xml_bind_yang0_parent() sometimes.
|
||||||
*/
|
*/
|
||||||
switch (yb){
|
switch (yb){
|
||||||
case YB_PARENT:
|
case YB_NONE:
|
||||||
|
break;
|
||||||
|
case YB_MODULE:
|
||||||
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
|
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
|
@ -1513,14 +1520,12 @@ _json_parse(char *str,
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
failed++;
|
failed++;
|
||||||
break;
|
break;
|
||||||
case YB_MODULE:
|
case YB_PARENT:
|
||||||
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
|
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
failed++;
|
failed++;
|
||||||
break;
|
break;
|
||||||
case YB_NONE:
|
|
||||||
break;
|
|
||||||
case YB_RPC:
|
case YB_RPC:
|
||||||
if ((ret = xml_bind_yang_rpc(NULL, x, yspec, xerr)) < 0)
|
if ((ret = xml_bind_yang_rpc(NULL, x, yspec, xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -1035,6 +1035,8 @@ api_path2xpath(char *api_path,
|
||||||
* @param[in] y0 Yang spec for x0
|
* @param[in] y0 Yang spec for x0
|
||||||
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
* @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] 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] xbotp Resulting xml tree
|
||||||
* @param[out] ybotp Yang spec matching xpathp
|
* @param[out] ybotp Yang spec matching xpathp
|
||||||
* @param[out] xerr Netconf error message (if retval=0)
|
* @param[out] xerr Netconf error message (if retval=0)
|
||||||
|
|
@ -1047,15 +1049,17 @@ api_path2xpath(char *api_path,
|
||||||
* @see api_path2xml
|
* @see api_path2xml
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_path2xml_vec(char **vec,
|
api_path2xml_vec(char **vec,
|
||||||
int nvec,
|
int nvec,
|
||||||
cxobj *x0,
|
cxobj *x0,
|
||||||
yang_stmt *y0,
|
yang_stmt *y0,
|
||||||
yang_class nodeclass,
|
yang_class nodeclass,
|
||||||
int strict,
|
int strict,
|
||||||
cxobj **xbotp,
|
api_path_mnt_cb_t mnt_cb,
|
||||||
yang_stmt **ybotp,
|
void *arg,
|
||||||
cxobj **xerr)
|
cxobj **xbotp,
|
||||||
|
yang_stmt **ybotp,
|
||||||
|
cxobj **xerr)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *nodeid;
|
char *nodeid;
|
||||||
|
|
@ -1081,6 +1085,7 @@ api_path2xml_vec(char **vec,
|
||||||
char *xpath = NULL;
|
char *xpath = NULL;
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
int ymtpoint; /* y is mountpoint */
|
int ymtpoint; /* y is mountpoint */
|
||||||
|
int ret;
|
||||||
|
|
||||||
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
|
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
|
||||||
if (xbotp)
|
if (xbotp)
|
||||||
|
|
@ -1112,9 +1117,12 @@ api_path2xml_vec(char **vec,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (ymtpoint){
|
if (ymtpoint){
|
||||||
/* XXX: Ignore return value: if none are mounted, no change of yspec is made here */
|
if ((ret = yang_mount_get_yspec_any(y0, &y0)) < 0)
|
||||||
if (yang_mount_get_yspec_any(y0, &y0) < 0)
|
|
||||||
goto done;
|
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){
|
if ((ymod = yang_find_module_by_name(y0, prefix)) == NULL){
|
||||||
cprintf(cberr, "No such yang module prefix");
|
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,
|
if ((retval = api_path2xml_vec(vec+1, nvec-1,
|
||||||
x, y,
|
x, y,
|
||||||
nodeclass, strict,
|
nodeclass, strict,
|
||||||
|
mnt_cb, arg,
|
||||||
xbotp, ybotp, xerr)) < 1)
|
xbotp, ybotp, xerr)) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
ok:
|
ok:
|
||||||
|
|
@ -1344,6 +1353,44 @@ api_path2xml(char *api_path,
|
||||||
cxobj **xbotp,
|
cxobj **xbotp,
|
||||||
yang_stmt **ybotp,
|
yang_stmt **ybotp,
|
||||||
cxobj **xerr)
|
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;
|
int retval = -1;
|
||||||
char **vec = NULL;
|
char **vec = NULL;
|
||||||
|
|
@ -1376,6 +1423,7 @@ api_path2xml(char *api_path,
|
||||||
nvec--; /* NULL-terminated */
|
nvec--; /* NULL-terminated */
|
||||||
if ((retval = api_path2xml_vec(vec+1, nvec,
|
if ((retval = api_path2xml_vec(vec+1, nvec,
|
||||||
xtop, yspec, nodeclass, strict,
|
xtop, yspec, nodeclass, strict,
|
||||||
|
mnt_cb, arg,
|
||||||
xbotp, ybotp, xerr)) < 1)
|
xbotp, ybotp, xerr)) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
/* Fix namespace */
|
/* Fix namespace */
|
||||||
|
|
@ -1471,14 +1519,6 @@ xml2api_path_1(cxobj *x,
|
||||||
default:
|
default:
|
||||||
break;
|
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:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -932,7 +932,7 @@ clixon_plugin_yang_mount_all(clixon_handle h,
|
||||||
return retval;
|
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:
|
* Create an xml tree (xret) for one callback only on the form:
|
||||||
* <config>...</config>,
|
* <config>...</config>,
|
||||||
|
|
|
||||||
|
|
@ -890,7 +890,6 @@ _xml_parse(const char *str,
|
||||||
}
|
}
|
||||||
xy.xy_xtop = xt;
|
xy.xy_xtop = xt;
|
||||||
xy.xy_xparent = xt;
|
xy.xy_xparent = xt;
|
||||||
xy.xy_yspec = yspec;
|
|
||||||
if (clixon_xml_parsel_init(&xy) < 0)
|
if (clixon_xml_parsel_init(&xy) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clixon_xml_parseparse(&xy) != 0) /* yacc returns 1 on error */
|
if (clixon_xml_parseparse(&xy) != 0) /* yacc returns 1 on error */
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ struct clixon_xml_parse_yacc {
|
||||||
cxobj *xy_xtop; /* cxobj top element (fixed) */
|
cxobj *xy_xtop; /* cxobj top element (fixed) */
|
||||||
cxobj *xy_xelement; /* cxobj active element (changes with parse context) */
|
cxobj *xy_xelement; /* cxobj active element (changes with parse context) */
|
||||||
cxobj *xy_xparent; /* cxobj parent 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 */
|
int xy_lex_state; /* lex return state */
|
||||||
cxobj **xy_xvec; /* Vector of created top-level nodes (to know which are created) */
|
cxobj **xy_xvec; /* Vector of created top-level nodes (to know which are created) */
|
||||||
int xy_xlen; /* Length of xy_xvec */
|
int xy_xlen; /* Length of xy_xvec */
|
||||||
|
|
|
||||||
|
|
@ -2445,9 +2445,9 @@ yang_spec_print(FILE *f,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Print yang top-level specs
|
/*! Print yang top-level specs
|
||||||
*
|
*a
|
||||||
* @param[in] f File to print to.
|
* @param[in] f File to print to.
|
||||||
* @param[in] ymounts Yang mounts to print
|
* @param[in] ymounts Yang mounts to print
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
yang_mounts_print(FILE *f,
|
yang_mounts_print(FILE *f,
|
||||||
|
|
|
||||||
|
|
@ -775,6 +775,7 @@ yang_schema_find_share(clixon_handle h,
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] xt XML tree node
|
* @param[in] xt XML tree node
|
||||||
* @param[in] xyanglib XML yang-lib
|
* @param[in] xyanglib XML yang-lib
|
||||||
|
* @param[out] yspecp Resulting mounted yang spec
|
||||||
* @retval 1 OK
|
* @retval 1 OK
|
||||||
* @retval 0 No yanglib or problem when parsing yanglib
|
* @retval 0 No yanglib or problem when parsing yanglib
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ if [ -d ${TOP_SRCDIR}/yang/clixon ]; then
|
||||||
else
|
else
|
||||||
cp /usr/local/share/clixon/$y $dir/
|
cp /usr/local/share/clixon/$y $dir/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${WITH_RESTCONF}" = "native" ]; then
|
if [ "${WITH_RESTCONF}" = "native" ]; then
|
||||||
# Create server certs
|
# Create server certs
|
||||||
certdir=$dir/certs
|
certdir=$dir/certs
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,9 @@ expectpart "$($clixon_cli -1 -f $cfg show config xml -- -m clixon-mount0 -M urn:
|
||||||
new "restconf get config mntpoint"
|
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>'
|
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
|
if [ $RC -ne 0 ]; then
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
stop_restconf
|
stop_restconf
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue