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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue