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);
|
||||
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_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_ */
|
||||
|
||||
|
|
|
|||
|
|
@ -86,10 +86,10 @@
|
|||
* @retval -1 Error
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
* Minimal support:
|
||||
* @endcode
|
||||
* Minimal support:
|
||||
* 200 OK
|
||||
* Allow: HEAD,GET,PUT,DELETE,OPTIONS
|
||||
* Allow: HEAD,GET,PUT,DELETE,OPTIONS
|
||||
* @see RFC5789 PATCH Method for HTTP Section 3.2
|
||||
*/
|
||||
int
|
||||
|
|
@ -113,7 +113,7 @@ api_data_options(clixon_handle h,
|
|||
/*! Check matching keys
|
||||
*
|
||||
* Check that x1 and x2 are of type list/leaf-list and share the same key statements
|
||||
* I.e that if x1=<list><key>b</key></list> then x2 = <list><key>b</key></list> as
|
||||
* I.e that if x1=<list><key>b</key></list> then x2 = <list><key>b</key></list> as
|
||||
* well. Otherwise return -1.
|
||||
* @param[in] y Yang statement, should be list or leaf-list
|
||||
* @param[in] x1 First XML tree (eg data)
|
||||
|
|
@ -180,7 +180,7 @@ match_list_keys(yang_stmt *y,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Common PUT plain PATCH method
|
||||
/*! Common PUT plain PATCH method
|
||||
*
|
||||
* Code checks if object exists.
|
||||
* PUT: If it does not, set op to create, otherwise replace
|
||||
|
|
@ -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)
|
||||
*/
|
||||
|
|
@ -319,7 +320,7 @@ api_data_write(clixon_handle h,
|
|||
else
|
||||
yb = YB_PARENT;
|
||||
|
||||
/* Parse input data as json or xml into xml
|
||||
/* Parse input data as json or xml into xml
|
||||
* Note that in POST (api_data_post) the new object is grafted on xbot, since it is a new
|
||||
* object. In that case all yang bindings can be made since xbot is available.
|
||||
* Here the new object replaces xbot and is therefore more complicated to make when parsing.
|
||||
|
|
@ -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;
|
||||
|
|
@ -361,7 +363,7 @@ api_data_write(clixon_handle h,
|
|||
} /* switch media_in */
|
||||
|
||||
/* The message-body MUST contain exactly one instance of the
|
||||
* expected data resource.
|
||||
* expected data resource.
|
||||
*/
|
||||
if (xml_child_nr_type(xdata0, CX_ELMNT) != 1){
|
||||
if (netconf_malformed_message_xml(&xerr, "The message-body MUST contain exactly one instance of the expected data resource") < 0)
|
||||
|
|
@ -387,7 +389,7 @@ api_data_write(clixon_handle h,
|
|||
}
|
||||
}
|
||||
|
||||
/* Add operation create as attribute. If that fails with Conflict, then
|
||||
/* Add operation create as attribute. If that fails with Conflict, then
|
||||
* try "replace" (see comment in function header)
|
||||
*/
|
||||
if (xml_add_attr(xdata, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) == NULL)
|
||||
|
|
@ -397,7 +399,7 @@ api_data_write(clixon_handle h,
|
|||
CLIXON_LIB_PREFIX, CLIXON_LIB_NS) == NULL)
|
||||
goto done;
|
||||
/* Top-of tree, no api-path
|
||||
* Replace xparent with x, ie bottom of api-path with data
|
||||
* Replace xparent with x, ie bottom of api-path with data
|
||||
*/
|
||||
dname = xml_name(xdata);
|
||||
if (api_path==NULL) {
|
||||
|
|
@ -438,7 +440,7 @@ api_data_write(clixon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* If list or leaf-list, api-path keys must match data keys
|
||||
/* If list or leaf-list, api-path keys must match data keys
|
||||
* There are two cases, either the object is the list element itself,
|
||||
* eg xpath:obj=a data:<obj><key>b</key></obj>
|
||||
* or the object is the key element:
|
||||
|
|
@ -578,11 +580,11 @@ api_data_write(clixon_handle h,
|
|||
if (xdata0)
|
||||
xml_free(xdata0);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
} /* api_data_write */
|
||||
|
||||
/*! Generic REST PUT method
|
||||
/*! Generic REST PUT method
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
|
|
@ -595,7 +597,7 @@ api_data_write(clixon_handle h,
|
|||
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note restconf PUT is mapped to edit-config replace.
|
||||
* @note restconf PUT is mapped to edit-config replace.
|
||||
* @see RFC8040 Sec 4.5 PUT
|
||||
* @see api_data_post
|
||||
* @example
|
||||
|
|
@ -605,7 +607,7 @@ api_data_write(clixon_handle h,
|
|||
A request message-body MUST be present, representing the new data resource, or the server
|
||||
MUST return a "400 Bad Request" status-line.
|
||||
|
||||
...if the PUT request creates a new resource, a "201 Created" status-line is returned.
|
||||
...if the PUT request creates a new resource, a "201 Created" status-line is returned.
|
||||
If an existing resource is modified, a "204 No Content" status-line is returned.
|
||||
|
||||
* Netconf: <edit-config> (nc:operation="create/replace")
|
||||
|
|
@ -637,7 +639,7 @@ api_data_put(clixon_handle h,
|
|||
media_in, media_out, 0, ds);
|
||||
}
|
||||
|
||||
/*! Generic REST PATCH method for plain patch
|
||||
/*! Generic REST PATCH method for plain patch
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
|
|
@ -650,7 +652,7 @@ api_data_put(clixon_handle h,
|
|||
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* Netconf: <edit-config> (nc:operation="merge")
|
||||
* Netconf: <edit-config> (nc:operation="merge")
|
||||
* See RFC8040 Sec 4.6.1
|
||||
* Plain patch can be used to create or update, but not delete, a child
|
||||
* resource within the target resource.
|
||||
|
|
@ -709,7 +711,7 @@ api_data_patch(clixon_handle h,
|
|||
* See RFC 8040 Sec 4.7
|
||||
* Example:
|
||||
* curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0
|
||||
* Netconf: <edit-config> (nc:operation="delete")
|
||||
* Netconf: <edit-config> (nc:operation="delete")
|
||||
*/
|
||||
int
|
||||
api_data_delete(clixon_handle h,
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||
|
||||
|
|
@ -24,14 +24,14 @@
|
|||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
|
||||
* Restconf method implementation for operations get and data get and head
|
||||
*/
|
||||
|
||||
|
|
@ -67,11 +67,12 @@
|
|||
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
|
||||
*
|
||||
* According to restconf
|
||||
* @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)
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
|
|
@ -80,20 +81,20 @@ static int api_data_pagination(clixon_handle h, void *req, char *api_path, int p
|
|||
* @retval -1 Error
|
||||
* @code
|
||||
* curl -X GET http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
* @endcode
|
||||
* See RFC8040 Sec 4.2 and 4.3
|
||||
* XXX: cant find a way to use Accept request field to choose Content-Type
|
||||
* I would like to support both xml and json.
|
||||
* Request may contain
|
||||
* Accept: application/yang.data+json,application/yang.data+xml
|
||||
* Response contains one of:
|
||||
* Content-Type: application/yang-data+xml
|
||||
* Content-Type: application/yang-data+json
|
||||
* XXX: cant find a way to use Accept request field to choose Content-Type
|
||||
* I would like to support both xml and json.
|
||||
* Request may contain
|
||||
* Accept: application/yang.data+json,application/yang.data+xml
|
||||
* Response contains one of:
|
||||
* 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-
|
||||
* list or list object identifies more than one instance, and XML
|
||||
* encoding is used in the response, then an error response containing a
|
||||
* "400 Bad Request" status-line MUST be returned by the server.
|
||||
* Netconf: <get-config>, <get>
|
||||
* Netconf: <get-config>, <get>
|
||||
* @note there is an ad-hoc method to determine json pagination request instead of regular GET
|
||||
*/
|
||||
static int
|
||||
|
|
@ -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,16 +210,14 @@ 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)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* We get return via netconf which is complete tree from root
|
||||
/* We get return via netconf which is complete tree from root
|
||||
* We need to cut that tree to only the object.
|
||||
*/
|
||||
#if 0 /* DEBUG */
|
||||
|
|
@ -254,9 +259,9 @@ api_data_get2(clixon_handle h,
|
|||
}
|
||||
/* Check if not exists */
|
||||
if (xlen == 0){
|
||||
/* 4.3: If a retrieval request for a data resource represents an
|
||||
instance that does not exist, then an error response containing
|
||||
a "404 Not Found" status-line MUST be returned by the server.
|
||||
/* 4.3: If a retrieval request for a data resource represents an
|
||||
instance that does not exist, then an error response containing
|
||||
a "404 Not Found" status-line MUST be returned by the server.
|
||||
The error-tag value "invalid-value" is used in this case. */
|
||||
if (netconf_invalid_value_xml(&xerr, "application", "Instance does not exist") < 0)
|
||||
goto done;
|
||||
|
|
@ -323,13 +328,13 @@ 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)
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
|
|
@ -338,11 +343,7 @@ api_data_get2(clixon_handle h,
|
|||
* @retval -1 Error
|
||||
* @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
|
||||
* @endcode
|
||||
*/
|
||||
static int
|
||||
api_data_pagination(clixon_handle h,
|
||||
|
|
@ -391,14 +392,14 @@ api_data_pagination(clixon_handle h,
|
|||
if (api_path){
|
||||
if ((xtop = xml_new("top", NULL, CX_ELMNT)) == NULL)
|
||||
goto done;
|
||||
/* 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
|
||||
* 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?
|
||||
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc)
|
||||
*/
|
||||
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);
|
||||
|
|
@ -502,7 +503,7 @@ api_data_pagination(clixon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* We get return via netconf which is complete tree from root
|
||||
/* We get return via netconf which is complete tree from root
|
||||
* We need to cut that tree to only the object.
|
||||
*/
|
||||
#if 0 /* DEBUG */
|
||||
|
|
@ -588,14 +589,14 @@ api_data_pagination(clixon_handle h,
|
|||
|
||||
/*! REST HEAD method
|
||||
*
|
||||
* The HEAD method is sent by the client to retrieve just the header fields
|
||||
* that would be returned for the comparable GET method, without the
|
||||
* response message-body.
|
||||
* Relation to netconf: none
|
||||
* The HEAD method is sent by the client to retrieve just the header fields
|
||||
* that would be returned for the comparable GET method, without the
|
||||
* response message-body.
|
||||
* Relation to netconf: none
|
||||
* @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)
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
|
|
@ -618,12 +619,12 @@ api_data_head(clixon_handle h,
|
|||
|
||||
/*! REST GET method
|
||||
*
|
||||
* According to restconf
|
||||
* According to restconf
|
||||
* @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)
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
|
|
@ -632,17 +633,17 @@ api_data_head(clixon_handle h,
|
|||
* @retval -1 Error
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||
* @endcode
|
||||
* Request may contain
|
||||
* Accept: application/yang.data+json,application/yang.data+xml
|
||||
* Response contains one of:
|
||||
* Content-Type: application/yang-data+xml
|
||||
* Content-Type: application/yang-data+json
|
||||
* @endcode
|
||||
* Request may contain
|
||||
* Accept: application/yang.data+json,application/yang.data+xml
|
||||
* 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-
|
||||
* list or list object identifies more than one instance, and XML
|
||||
* encoding is used in the response, then an error response containing a
|
||||
* "400 Bad Request" status-line MUST be returned by the server.
|
||||
* Netconf: <get-config>, <get>
|
||||
* Netconf: <get-config>, <get>
|
||||
*/
|
||||
int
|
||||
api_data_get(clixon_handle h,
|
||||
|
|
@ -678,7 +679,7 @@ api_data_get(clixon_handle h,
|
|||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] pi Offset, where path starts
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
|
|
@ -687,7 +688,7 @@ api_data_get(clixon_handle h,
|
|||
* @retval -1 Error
|
||||
* @code
|
||||
* curl -G http://localhost/restconf/operations
|
||||
* @endcode
|
||||
* @endcode
|
||||
* @see RFC8040 Sec 3.3.2:
|
||||
* This optional resource is a container that provides access to the
|
||||
* data-model-specific RPC operations supported by the server. The
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ yang_patch_xml2json_modified_cbuf(cxobj *x_simple_patch)
|
|||
char *json_simple_patch_tmp;
|
||||
int brace_count = 0;
|
||||
size_t len;
|
||||
|
||||
|
||||
json_simple_patch = cbuf_new();
|
||||
if (json_simple_patch == NULL)
|
||||
return NULL;
|
||||
|
|
@ -150,7 +150,7 @@ yang_patch_xml2json_modified_cbuf(cxobj *x_simple_patch)
|
|||
return json_simple_patch;
|
||||
}
|
||||
|
||||
/*! yang_patch_strip_after_last_slash
|
||||
/*! yang_patch_strip_after_last_slash
|
||||
*
|
||||
* Strip /... from end of val
|
||||
* so that e.g. "/interface=eth2" becomes "/"
|
||||
|
|
@ -166,7 +166,7 @@ yang_patch_strip_after_last_slash(char* val)
|
|||
cbuf *cb;
|
||||
cbuf *val_tmp;
|
||||
int idx;
|
||||
|
||||
|
||||
cb = cbuf_new();
|
||||
val_tmp = cbuf_new();
|
||||
cbuf_append_str(val_tmp, val);
|
||||
|
|
@ -212,7 +212,7 @@ yang_patch_do_replace(clixon_handle h,
|
|||
restconf_media media_out,
|
||||
ietf_ds_t ds,
|
||||
cbuf *simple_patch_request_uri,
|
||||
char *target_val,
|
||||
char *target_val,
|
||||
int value_vec_len,
|
||||
cxobj **value_vec,
|
||||
cxobj *x_simple_patch
|
||||
|
|
@ -223,7 +223,7 @@ yang_patch_do_replace(clixon_handle h,
|
|||
cbuf *delete_req_uri = NULL;
|
||||
cbuf *post_req_uri = NULL;
|
||||
cbuf *json_simple_patch = NULL;
|
||||
|
||||
|
||||
if ((delete_req_uri = cbuf_new()) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
|
|
@ -322,7 +322,7 @@ yang_patch_do_create(clixon_handle h,
|
|||
int retval = -1;
|
||||
cxobj *value_vec_tmp = NULL;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
|
||||
// Send the POST request
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||
|
|
@ -361,7 +361,7 @@ yang_patch_do_create(clixon_handle h,
|
|||
* @param[in] value_vec pointer to the "value" array of an edit in YANG patch
|
||||
* @param[in] x_simple_patch pointer to XML containing module name, e.g. <ietf-interfaces:interface/>
|
||||
* @param[in] where_val value in "where" field of edit in YANG patch
|
||||
* @param[in] api_path full API path, e.g. "/restconf/data/example-jukebox:jukebox/playlist=Foo-One"
|
||||
* @param[in] api_path full API path, e.g. "/restconf/data/example-jukebox:jukebox/playlist=Foo-One"
|
||||
* @param[in] point_val value in "point" field of edit in YANG patch
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
|
|
@ -388,7 +388,7 @@ yang_patch_do_insert(clixon_handle h,
|
|||
cg_var *cv;
|
||||
cbuf *point_str = NULL;
|
||||
cvec *qvec_tmp = NULL;
|
||||
|
||||
|
||||
if ((point_str = cbuf_new()) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
|
|
@ -476,14 +476,13 @@ yang_patch_do_merge(clixon_handle h,
|
|||
cxobj *value_vec_tmp = NULL;
|
||||
cbuf *cb = NULL;
|
||||
cbuf *json_simple_patch = NULL;
|
||||
|
||||
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
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) {
|
||||
|
|
@ -618,7 +617,7 @@ yang_patch_do_edit(clixon_handle h,
|
|||
size_t veclen = 0;
|
||||
int ret;
|
||||
cxobj *xerr = NULL; /* malloced must be freed */
|
||||
cxobj *xtop;
|
||||
cxobj *xtop;
|
||||
cxobj *xbot = NULL;
|
||||
yang_patch_op_t operation;
|
||||
char *where_val = 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)
|
||||
|
|
|
|||
|
|
@ -64,13 +64,13 @@
|
|||
#include "restconf_err.h"
|
||||
#include "restconf_methods_post.h"
|
||||
|
||||
/*! Print location header from
|
||||
/*! Print location header from
|
||||
*
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] xobj If set (eg POST) add to api-path
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* $https “on” if connection operates in SSL mode, or an empty string otherwise
|
||||
* $https “on” if connection operates in SSL mode, or an empty string otherwise
|
||||
* @note ports are ignored
|
||||
*/
|
||||
static int
|
||||
|
|
@ -117,7 +117,7 @@ http_location_header(clixon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Generic REST POST method
|
||||
/*! Generic REST POST method
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
|
|
@ -131,7 +131,7 @@ http_location_header(clixon_handle h,
|
|||
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* restconf POST is mapped to edit-config create.
|
||||
* restconf POST is mapped to edit-config create.
|
||||
* @see RFC8040 Sec 4.4.1
|
||||
|
||||
POST:
|
||||
|
|
@ -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)
|
||||
|
|
@ -233,8 +234,8 @@ api_data_post(clixon_handle h,
|
|||
yb = YB_MODULE;
|
||||
else
|
||||
yb = YB_PARENT;
|
||||
/* Parse input data as json or xml into xml
|
||||
* If xbot is top-level (api_path=null) it does not have a spec therefore look for
|
||||
/* Parse input data as json or xml into xml
|
||||
* If xbot is top-level (api_path=null) it does not have a spec therefore look for
|
||||
* top-level (yspec) otherwise assume parent (xbot) is populated.
|
||||
*/
|
||||
switch (media_in){
|
||||
|
|
@ -273,7 +274,7 @@ api_data_post(clixon_handle h,
|
|||
} /* switch media_in */
|
||||
|
||||
/* RFC 8040 4.4.1: The message-body MUST contain exactly one instance of the
|
||||
* expected data resource.
|
||||
* expected data resource.
|
||||
*/
|
||||
clixon_debug(CLIXON_DBG_RESTCONF, "nrchildren0: %d", nrchildren0);
|
||||
if (xml_child_nr_type(xbot, CX_ELMNT) - nrchildren0 != 1){
|
||||
|
|
@ -315,7 +316,7 @@ api_data_post(clixon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* If URI points out an object, then data's parent should be that object
|
||||
/* If URI points out an object, then data's parent should be that object
|
||||
*/
|
||||
if (ybot && yang_parent_get(ydata) != ybot){
|
||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
||||
|
|
@ -399,12 +400,12 @@ api_data_post(clixon_handle h,
|
|||
return retval;
|
||||
} /* api_data_post */
|
||||
|
||||
/*! Handle input data to api_operations_post
|
||||
/*! Handle input data to api_operations_post
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] yspec Yang top-level specification
|
||||
* @param[in] yspec Yang top-level specification
|
||||
* @param[in] yrpc Yang rpc spec
|
||||
* @param[in] xrpc XML pointer to rpc method
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
|
|
@ -449,7 +450,7 @@ api_operations_post_input(clixon_handle h,
|
|||
media_in = restconf_content_type(h);
|
||||
switch (media_in){
|
||||
case YANG_DATA_XML:
|
||||
/* XXX: Here data is on the form: <input xmlns="urn:example:clixon"/> and has no proper yang binding
|
||||
/* XXX: Here data is on the form: <input xmlns="urn:example:clixon"/> and has no proper yang binding
|
||||
* support */
|
||||
if ((ret = clixon_xml_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
||||
|
|
@ -465,7 +466,7 @@ api_operations_post_input(clixon_handle h,
|
|||
}
|
||||
break;
|
||||
case YANG_DATA_JSON:
|
||||
/* XXX: Here data is on the form: {"clixon-example:input":null} and has no proper yang binding
|
||||
/* XXX: Here data is on the form: {"clixon-example:input":null} and has no proper yang binding
|
||||
* support */
|
||||
if ((ret = clixon_json_parse_string(data, 1, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
||||
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
||||
|
|
@ -486,7 +487,7 @@ api_operations_post_input(clixon_handle h,
|
|||
break;
|
||||
} /* switch media_in */
|
||||
xml_name_set(xdata, NETCONF_OUTPUT_DATA);
|
||||
/* Here xdata is:
|
||||
/* Here xdata is:
|
||||
* <data><input xmlns="urn:example:clixon">...</input></data>
|
||||
*/
|
||||
#if 1
|
||||
|
|
@ -532,12 +533,12 @@ api_operations_post_input(clixon_handle h,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! Handle output data to api_operations_post
|
||||
/*! Handle output data to api_operations_post
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
* @param[in] xret XML reply messages from backend/handler
|
||||
* @param[in] yspec Yang top-level specification
|
||||
* @param[in] yspec Yang top-level specification
|
||||
* @param[in] youtput Yang rpc output specification
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] media_out Output media
|
||||
|
|
@ -570,7 +571,7 @@ api_operations_post_output(clixon_handle h,
|
|||
/* Validate that exactly only <rpc-reply> tag with exactly one element child */
|
||||
if ((xoutput = xml_child_i_type(xret, 0, CX_ELMNT)) == NULL ||
|
||||
strcmp(xml_name(xoutput),"rpc-reply") != 0
|
||||
/* See https://github.com/clicon/clixon/issues/158
|
||||
/* See https://github.com/clicon/clixon/issues/158
|
||||
* This is an internal error, they should not be double but the error should not be detected
|
||||
* here, it should be detected in the backend plugin caller.
|
||||
|| xml_child_nr_type(xrpc, CX_ELMNT) != 1 XXX backend can have multiple callbacks
|
||||
|
|
@ -594,7 +595,7 @@ api_operations_post_output(clixon_handle h,
|
|||
if (xml_purge(xa) < 0)
|
||||
goto done;
|
||||
|
||||
/* Sanity check of outgoing XML
|
||||
/* Sanity check of outgoing XML
|
||||
* For now, skip outgoing checks.
|
||||
* (1) Does not handle <ok/> properly
|
||||
* (2) Uncertain how validation errors should be logged/handled
|
||||
|
|
@ -657,7 +658,7 @@ api_operations_post_output(clixon_handle h,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/*! REST operation POST method
|
||||
/*! REST operation POST method
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
* @param[in] req Generic Www handle
|
||||
|
|
@ -669,7 +670,7 @@ api_operations_post_output(clixon_handle h,
|
|||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* See RFC 8040 Sec 3.6 / 4.4.2
|
||||
* @note We map post to edit-config create.
|
||||
* @note We map post to edit-config create.
|
||||
* POST {+restconf}/operations/<operation>
|
||||
* 1. Initialize
|
||||
* 2. Get rpc module and name from uri (oppath) and find yang spec
|
||||
|
|
@ -740,7 +741,7 @@ api_operations_post(clixon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* 2. Get rpc module and name from uri (oppath) and find yang spec
|
||||
/* 2. Get rpc module and name from uri (oppath) and find yang spec
|
||||
* POST {+restconf}/operations/<operation>
|
||||
*
|
||||
* The <operation> field identifies the module name and rpc identifier
|
||||
|
|
@ -762,7 +763,7 @@ api_operations_post(clixon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* 3. Build xml tree with user and rpc:
|
||||
/* 3. Build xml tree with user and rpc:
|
||||
* <rpc username="foo"><myfn xmlns="uri"/>
|
||||
*/
|
||||
if ((username = clicon_username_get(h)) != NULL){
|
||||
|
|
@ -777,14 +778,15 @@ 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)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* Here xtop is: <rpc username="foo"><myfn xmlns="uri"/></rpc>
|
||||
/* Here xtop is: <rpc username="foo"><myfn xmlns="uri"/></rpc>
|
||||
* xbot is <myfn xmlns="uri"/>
|
||||
* 4. Parse input data (arguments):
|
||||
* JSON: {"example:input":{"x":0}}
|
||||
|
|
@ -799,7 +801,7 @@ api_operations_post(clixon_handle h,
|
|||
if (ret == 0)
|
||||
goto ok;
|
||||
}
|
||||
/* Here xtop is:
|
||||
/* Here xtop is:
|
||||
<rpc username="foo"><myfn xmlns="uri"><x>42</x></myfn></rpc> */
|
||||
#if 1
|
||||
clixon_debug_xml(CLIXON_DBG_RESTCONF, xtop, ". Translate input args:");
|
||||
|
|
@ -829,8 +831,8 @@ api_operations_post(clixon_handle h,
|
|||
* Note (1) xtop is <rpc><method> xbot is <method>
|
||||
* (2) local handler wants <method> and backend wants <rpc><method>
|
||||
*/
|
||||
/* Look for local (client-side) restconf plugins.
|
||||
* -1:Error, 0:OK local, 1:OK backend
|
||||
/* Look for local (client-side) restconf plugins.
|
||||
* -1:Error, 0:OK local, 1:OK backend
|
||||
*/
|
||||
if ((ret = rpc_callback_call(h, xbot, req, &nr, cbret)) < 0)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -373,11 +373,11 @@ example_restconf_start(clixon_handle h)
|
|||
* @see RFC 8528
|
||||
*/
|
||||
int
|
||||
restconf_yang_mount(clixon_handle h,
|
||||
cxobj *xt,
|
||||
int *config,
|
||||
validate_level *vl,
|
||||
cxobj **yanglib)
|
||||
example_restconf_yang_mount(clixon_handle h,
|
||||
cxobj *xt,
|
||||
int *config,
|
||||
validate_level *vl,
|
||||
cxobj **yanglib)
|
||||
{
|
||||
int retval = -1;
|
||||
cbuf *cb = NULL;
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)));;
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ enum array_element_type{
|
|||
};
|
||||
|
||||
enum childtype{
|
||||
NULL_CHILD=0, /* eg <a/> no children. Translated to null if in
|
||||
NULL_CHILD=0, /* eg <a/> no children. Translated to null if in
|
||||
* array or leaf terminal, and to {} if proper object, ie container.
|
||||
* anyxml/anydata?
|
||||
*/
|
||||
|
|
@ -107,7 +107,7 @@ enum childtype{
|
|||
ANY_CHILD, /* eg <a><b/></a> or <a><b/><c/></a> */
|
||||
};
|
||||
|
||||
/*! x is element and has exactly one child which in turn has none
|
||||
/*! x is element and has exactly one child which in turn has none
|
||||
*
|
||||
* remove attributes from x
|
||||
* @see tleaf in clixon_xml_map.c
|
||||
|
|
@ -182,14 +182,14 @@ arraytype2str(enum array_element_type lt)
|
|||
}
|
||||
|
||||
/*! Check typeof x in array
|
||||
*
|
||||
*
|
||||
* Check if element is in an array, and if so, if it is in the start "[x,", in the middle: "[..,x,..]"
|
||||
* in the end: ",x]", or a single element: "[x]"
|
||||
* Some complexity when x is in different namespaces
|
||||
* @param[in] xprev The previous element (if any)
|
||||
* @param[in] x The element itself
|
||||
* @param[in] xnext The next element (if any)
|
||||
* @retval arraytype Type of array
|
||||
* @retval arraytype Type of array
|
||||
*/
|
||||
static enum array_element_type
|
||||
array_eval(cxobj *xprev,
|
||||
|
|
@ -296,7 +296,7 @@ json_str_escape_cdata(cbuf *cb,
|
|||
* @param[in] x XML tree. Must be yang populated.
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[out] xerr Reason for invalid tree returned as netconf err msg or NULL
|
||||
* @retval 1 OK
|
||||
* @retval 1 OK
|
||||
* @retval 0 Invalid, wrt namespace. xerr set
|
||||
* @retval -1 Error
|
||||
* @see RFC7951 Sec 4 and 6.8
|
||||
|
|
@ -340,7 +340,7 @@ json2xml_decode_identityref(cxobj *x,
|
|||
prefix, body, ns);
|
||||
if (!xml_nsctx_get_prefix(nsc, ns, &prefix2)){
|
||||
/* (no) insert a xmlns:<prefix> statement
|
||||
* Get yang prefix from import statement of my mod
|
||||
* Get yang prefix from import statement of my mod
|
||||
* I am not sure this is correct
|
||||
*/
|
||||
if (yang_find_prefix_by_namespace(y, ns, &prefix2) < 0)
|
||||
|
|
@ -396,11 +396,11 @@ json2xml_decode_identityref(cxobj *x,
|
|||
*
|
||||
* Assume an xml tree where prefix:name have been split into "module":"name"
|
||||
* In other words, from JSON RFC7951 to XML namespace trees
|
||||
*
|
||||
*
|
||||
* @param[in] x XML tree. Must be yang populated. After json parsing
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[out] xerr Reason for invalid tree returned as netconf err msg or NULL
|
||||
* @retval 1 OK
|
||||
* @retval 1 OK
|
||||
* @retval 0 Invalid, wrt namespace. xerr set
|
||||
* @retval -1 Error
|
||||
* @see RFC7951 Sec 4 and 6.8
|
||||
|
|
@ -606,7 +606,7 @@ xml2json_encode_leafs(cxobj *xb,
|
|||
}
|
||||
ok:
|
||||
/* write into original cb0
|
||||
* includign quoting and encoding
|
||||
* includign quoting and encoding
|
||||
*/
|
||||
if (quote){
|
||||
cprintf(cb0, "\"");
|
||||
|
|
@ -625,8 +625,9 @@ xml2json_encode_leafs(cxobj *xb,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* X has no XML child - no body.
|
||||
* If x is a container, use {} instead of null
|
||||
/*! 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
|
||||
*/
|
||||
|
|
@ -714,7 +715,7 @@ json_metadata_encoding(cbuf *cb,
|
|||
/*! Encode XML attributes as JSON meta-data
|
||||
*
|
||||
* There are two methods for this:
|
||||
* 1) Registered meta-data according to RFC 7952 using md:annotate.
|
||||
* 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
|
||||
|
|
@ -781,7 +782,7 @@ xml2json_encode_attr(cxobj *xa,
|
|||
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[in] x XML tree structure containing XML to translate
|
||||
|
|
@ -852,7 +853,7 @@ xml2json1_cbuf(cbuf *cb,
|
|||
if (ys_real_module(ys, &ymod) < 0)
|
||||
goto done;
|
||||
modname = yang_argument_get(ymod);
|
||||
/* Special case for ietf-netconf -> ietf-restconf translation
|
||||
/* Special case for ietf-netconf -> ietf-restconf translation
|
||||
* A special case is for return data on the form {"data":...}
|
||||
* See also json_xmlns_translate()
|
||||
*/
|
||||
|
|
@ -946,7 +947,7 @@ xml2json1_cbuf(cbuf *cb,
|
|||
goto done;
|
||||
}
|
||||
/* 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"
|
||||
*/
|
||||
commas = xml_child_nr_notype(x, CX_ATTR) - 1;
|
||||
|
|
@ -1050,9 +1051,8 @@ xml2json1_cbuf(cbuf *cb,
|
|||
|
||||
/*! Translate an XML tree to JSON in a CLIgen buffer
|
||||
*
|
||||
* XML-style namespace notation in tree, but RFC7951 in output assume yang
|
||||
* populated
|
||||
*
|
||||
* 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
|
||||
|
|
@ -1121,9 +1121,8 @@ xml2json_cbuf1(cbuf *cb,
|
|||
|
||||
/*! Translate an XML tree to JSON in a CLIgen buffer skip top-level object
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* @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
|
||||
|
|
@ -1178,7 +1177,7 @@ clixon_json2cbuf(cbuf *cb,
|
|||
* @param[in] vec Vector of xml objecst
|
||||
* @param[in] veclen Length of vector
|
||||
* @param[in] pretty Set if output is pretty-printed (2 for debug)
|
||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note This only works if the vector is uniform, ie same object name.
|
||||
|
|
@ -1320,8 +1319,8 @@ json_print(FILE *f,
|
|||
* @param[in] vec Vector of xml objecst
|
||||
* @param[in] veclen Length of vector
|
||||
* @param[in] pretty Set if output is pretty-printed (2 for debug)
|
||||
* @param[in] fn File print function
|
||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||
* @param[in] fn File print function
|
||||
* @param[in] skiptop 0: Include top object 1: Skip top-object, only children,
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @note This only works if the vector is uniform, ie same object name.
|
||||
|
|
@ -1357,11 +1356,11 @@ xml2json_vec(FILE *f,
|
|||
*
|
||||
* Assume an xml tree where prefix:name have been split into "module":"name"
|
||||
* In other words, from JSON to XML namespace trees
|
||||
*
|
||||
*
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in,out] x XML tree. Translate it in-line
|
||||
* @param[out] xerr Reason for invalid tree returned as netconf err msg or NULL
|
||||
* @retval 1 OK
|
||||
* @retval 1 OK
|
||||
* @retval 0 Invalid, wrt namespace. xerr set
|
||||
* @retval -1 Error
|
||||
* @note the opposite - xml2ns is made inline in xml2json1_cbuf
|
||||
|
|
@ -1430,11 +1429,11 @@ json_xmlns_translate(yang_stmt *yspec,
|
|||
* @param[in] yb How to bind yang to XML top-level when parsing (if rfc7951)
|
||||
* @param[in] yspec Yang specification (if rfc 7951)
|
||||
* @param[out] xt XML top of tree typically w/o children on entry (but created)
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
* @retval 1 OK and valid
|
||||
* @retval 0 Invalid (only if yang spec)
|
||||
* @retval -1 Error
|
||||
*
|
||||
*
|
||||
* @see _xml_parse for XML variant
|
||||
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||
* @see RFC 7951
|
||||
|
|
@ -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,11 +1473,15 @@ _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];
|
||||
/* RFC 7951 Section 4: A namespace-qualified member name MUST be used for all
|
||||
* members of a top-level JSON object
|
||||
/* RFC 7951 Section 4: A namespace-qualified member name MUST be used for all
|
||||
* members of a top-level JSON object
|
||||
*/
|
||||
if (rfc7951 && xml_prefix(x) == NULL){
|
||||
/* XXX: For top-level config file: */
|
||||
|
|
@ -1493,15 +1497,18 @@ _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;
|
||||
/* Now assign yang stmts to each XML node
|
||||
/* Now assign yang stmts to each XML node
|
||||
* 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;
|
||||
|
|
@ -1528,7 +1533,7 @@ _json_parse(char *str,
|
|||
failed++;
|
||||
break;
|
||||
}
|
||||
/* Now find leafs with identityrefs (+transitive) and translate
|
||||
/* Now find leafs with identityrefs (+transitive) and translate
|
||||
* prefixes in values to XML namespaces */
|
||||
if ((ret = json2xml_decode(x, xerr)) < 0)
|
||||
goto done;
|
||||
|
|
@ -1537,7 +1542,7 @@ _json_parse(char *str,
|
|||
}
|
||||
if (failed)
|
||||
goto fail;
|
||||
/* Sort the complete tree after parsing. Sorting is not really meaningful if Yang
|
||||
/* Sort the complete tree after parsing. Sorting is not really meaningful if Yang
|
||||
not bound */
|
||||
if (yb != YB_NONE)
|
||||
if (xml_sort_recurse(xt) < 0)
|
||||
|
|
@ -1564,7 +1569,7 @@ _json_parse(char *str,
|
|||
* @param[in] yb How to bind yang to XML top-level when parsing
|
||||
* @param[in] yspec Yang specification, mandatory to make module->xmlns translation
|
||||
* @param[in,out] xt Top object, if not exists, on success it is created with name 'top'
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
* @retval 1 OK and valid
|
||||
* @retval 0 Invalid (only if yang spec) w xerr set
|
||||
* @retval -1 Error
|
||||
|
|
@ -1599,24 +1604,24 @@ clixon_json_parse_string(char *str,
|
|||
return _json_parse(str, rfc7951, yb, yspec, *xt, xerr);
|
||||
}
|
||||
|
||||
/*! Read a JSON definition from file and parse it into a parse-tree.
|
||||
/*! Read a JSON definition from file and parse it into a parse-tree.
|
||||
*
|
||||
* File will be parsed as follows:
|
||||
* (1) parsed according to JSON; # Only this check if yspec is NULL
|
||||
* (2) sanity checked wrt yang
|
||||
* (2) sanity checked wrt yang
|
||||
* (3) namespaces check (using <ns>:<name> notation
|
||||
* (4) an xml parse tree will be returned
|
||||
* Note, only (1) and (4) will be done if yspec is NULL.
|
||||
* Part of (3) is to split json names if they contain colon,
|
||||
* Part of (3) is to split json names if they contain colon,
|
||||
* eg: name="a:b" -> prefix="a", name="b"
|
||||
* But this is not done if yspec=NULL, and is not part of the JSON spec
|
||||
*
|
||||
*
|
||||
* @param[in] fp File descriptor to the JSON file (ASCII string)
|
||||
* @param[in] rfc7951 Do sanity checks according to RFC 7951 JSON Encoding of Data Modeled with YANG
|
||||
* @param[in] yb How to bind yang to XML top-level when parsing
|
||||
* @param[in] yspec Yang specification, or NULL
|
||||
* @param[in,out] xt Pointer to (XML) parse tree. If empty, create.
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
* @param[out] xerr Reason for invalid returned as netconf err msg
|
||||
* @retval 1 OK and valid
|
||||
* @retval 0 Invalid (only if yang spec) w xerr set
|
||||
* @retval -1 Error
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -1047,15 +1049,17 @@ api_path2xpath(char *api_path,
|
|||
* @see api_path2xml
|
||||
*/
|
||||
static int
|
||||
api_path2xml_vec(char **vec,
|
||||
int nvec,
|
||||
cxobj *x0,
|
||||
yang_stmt *y0,
|
||||
yang_class nodeclass,
|
||||
int strict,
|
||||
cxobj **xbotp,
|
||||
yang_stmt **ybotp,
|
||||
cxobj **xerr)
|
||||
api_path2xml_vec(char **vec,
|
||||
int nvec,
|
||||
cxobj *x0,
|
||||
yang_stmt *y0,
|
||||
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 *nodeid;
|
||||
|
|
@ -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");
|
||||
|
|
@ -1179,7 +1187,7 @@ api_path2xml_vec(char **vec,
|
|||
}
|
||||
}
|
||||
else{
|
||||
/* Transform restval "a,b,c" to "a" "b" "c" (nvalvec=3)
|
||||
/* Transform restval "a,b,c" to "a" "b" "c" (nvalvec=3)
|
||||
* Note that vnr can be < length of cvk, due to empty or unset values
|
||||
* Note also that valvec entries are encoded
|
||||
*/
|
||||
|
|
@ -1269,7 +1277,7 @@ api_path2xml_vec(char **vec,
|
|||
goto done;
|
||||
}
|
||||
/* cf xml_bind_yang0_opt/xml_yang_mount_get */
|
||||
if (yang_mount_get(y, xpath, &y1) < 0)
|
||||
if (yang_mount_get(y, xpath, &y1) < 0)
|
||||
goto done;
|
||||
if (y1 != NULL)
|
||||
y = y1;
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -2445,9 +2445,9 @@ 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
|
||||
* @param[in] ymounts Yang mounts to print
|
||||
*/
|
||||
int
|
||||
yang_mounts_print(FILE *f,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue