RESTCONF over mountpoints, extended api_path2xml_mnt with mount-point check

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

View file

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

View file

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

View file

@ -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)

View file

@ -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

View file

@ -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)

View file

@ -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;