RESTCONF over mountpoints, extended api_path2xml_mnt with mount-point check
This commit is contained in:
parent
9086264b89
commit
b0cc1857c0
17 changed files with 299 additions and 213 deletions
|
|
@ -930,3 +930,31 @@ restconf_socket_init(const char *netns0,
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
|
clixon_debug(CLIXON_DBG_RESTCONF, "retval:%d", retval);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Callback used by api_path2xml when yang mountpoint is empty
|
||||||
|
*
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] x XML node
|
||||||
|
* @param[out] yp YANG
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
restconf_apipath_mount_cb(clixon_handle h,
|
||||||
|
cxobj *x,
|
||||||
|
yang_stmt **yp)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xyanglib = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (clixon_plugin_yang_mount_all(h, x, NULL, NULL, &xyanglib) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xyanglib != NULL){
|
||||||
|
if ((ret = yang_schema_yanglib_mount_parse(h, x, xyanglib, yp)) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xyanglib)
|
||||||
|
xml_free(xyanglib);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ int restconf_drop_privileges(clixon_handle h);
|
||||||
int restconf_authentication_cb(clixon_handle h, void *req, int pretty, restconf_media media_out);
|
int restconf_authentication_cb(clixon_handle h, void *req, int pretty, restconf_media media_out);
|
||||||
int restconf_config_init(clixon_handle h, cxobj *xrestconf);
|
int restconf_config_init(clixon_handle h, cxobj *xrestconf);
|
||||||
int restconf_socket_init(const char *netns0, const char *addrstr, const char *addrtype, uint16_t port, int backlog, int flags, int *ss);
|
int restconf_socket_init(const char *netns0, const char *addrstr, const char *addrtype, uint16_t port, int backlog, int flags, int *ss);
|
||||||
|
int restconf_apipath_mount_cb(clixon_handle h, cxobj *x, yang_stmt **yp);
|
||||||
|
|
||||||
#endif /* _RESTCONF_LIB_H_ */
|
#endif /* _RESTCONF_LIB_H_ */
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,10 +86,10 @@
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||||
* @endcode
|
* @endcode
|
||||||
* Minimal support:
|
* Minimal support:
|
||||||
* 200 OK
|
* 200 OK
|
||||||
* Allow: HEAD,GET,PUT,DELETE,OPTIONS
|
* Allow: HEAD,GET,PUT,DELETE,OPTIONS
|
||||||
* @see RFC5789 PATCH Method for HTTP Section 3.2
|
* @see RFC5789 PATCH Method for HTTP Section 3.2
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -113,7 +113,7 @@ api_data_options(clixon_handle h,
|
||||||
/*! Check matching keys
|
/*! Check matching keys
|
||||||
*
|
*
|
||||||
* Check that x1 and x2 are of type list/leaf-list and share the same key statements
|
* 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.
|
* well. Otherwise return -1.
|
||||||
* @param[in] y Yang statement, should be list or leaf-list
|
* @param[in] y Yang statement, should be list or leaf-list
|
||||||
* @param[in] x1 First XML tree (eg data)
|
* @param[in] x1 First XML tree (eg data)
|
||||||
|
|
@ -180,7 +180,7 @@ match_list_keys(yang_stmt *y,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Common PUT plain PATCH method
|
/*! Common PUT plain PATCH method
|
||||||
*
|
*
|
||||||
* Code checks if object exists.
|
* Code checks if object exists.
|
||||||
* PUT: If it does not, set op to create, otherwise replace
|
* PUT: If it does not, set op to create, otherwise replace
|
||||||
|
|
@ -246,6 +246,25 @@ api_data_write(clixon_handle h,
|
||||||
/* strip /... from start */
|
/* strip /... from start */
|
||||||
for (i=0; i<pi; i++)
|
for (i=0; i<pi; i++)
|
||||||
api_path = index(api_path+1, '/');
|
api_path = index(api_path+1, '/');
|
||||||
|
/* Create config top-of-tree */
|
||||||
|
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
|
||||||
|
goto done;
|
||||||
|
/* Translate api_path to xml in the form of xtop/xbot */
|
||||||
|
xbot = xtop;
|
||||||
|
if (api_path){ /* If URI, otherwise top data/config object */
|
||||||
|
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
|
||||||
|
restconf_apipath_mount_cb, h, &xbot, &ybot, &xerr)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0){ /* validation failed */
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
if (ybot){
|
||||||
|
if (ys_real_module(ybot, &ymodapi) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (api_path){
|
if (api_path){
|
||||||
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
|
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
|
||||||
if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
||||||
|
|
@ -260,24 +279,6 @@ api_data_write(clixon_handle h,
|
||||||
op = OP_MERGE; /* bad request if it does not exist */
|
op = OP_MERGE; /* bad request if it does not exist */
|
||||||
else
|
else
|
||||||
op = OP_REPLACE; /* OP_CREATE if it does not exist */
|
op = OP_REPLACE; /* OP_CREATE if it does not exist */
|
||||||
/* Create config top-of-tree */
|
|
||||||
if ((xtop = xml_new(NETCONF_INPUT_CONFIG, NULL, CX_ELMNT)) == NULL)
|
|
||||||
goto done;
|
|
||||||
/* Translate api_path to xml in the form of xtop/xbot */
|
|
||||||
xbot = xtop;
|
|
||||||
if (api_path){ /* If URI, otherwise top data/config object */
|
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ret == 0){ /* validation failed */
|
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
|
||||||
goto done;
|
|
||||||
goto ok;
|
|
||||||
}
|
|
||||||
if (ybot){
|
|
||||||
if (ys_real_module(ybot, &ymodapi) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 4.4.1: The message-body MUST contain exactly one instance of the
|
/* 4.4.1: The message-body MUST contain exactly one instance of the
|
||||||
* expected data resource. (tested again below)
|
* expected data resource. (tested again below)
|
||||||
*/
|
*/
|
||||||
|
|
@ -319,7 +320,7 @@ api_data_write(clixon_handle h,
|
||||||
else
|
else
|
||||||
yb = YB_PARENT;
|
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
|
* 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.
|
* 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.
|
* 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;
|
break;
|
||||||
case YANG_DATA_JSON:
|
case YANG_DATA_JSON:
|
||||||
|
// XXX yspec is top-level, but data may be mounted yspec
|
||||||
if ((ret = clixon_json_parse_string(data, 1, yb, yspec, &xdata0, &xerr)) < 0){
|
if ((ret = clixon_json_parse_string(data, 1, yb, yspec, &xdata0, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -361,7 +363,7 @@ api_data_write(clixon_handle h,
|
||||||
} /* switch media_in */
|
} /* switch media_in */
|
||||||
|
|
||||||
/* The message-body MUST contain exactly one instance of the
|
/* 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 (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)
|
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)
|
* try "replace" (see comment in function header)
|
||||||
*/
|
*/
|
||||||
if (xml_add_attr(xdata, "operation", xml_operation2str(op), NETCONF_BASE_PREFIX, NULL) == NULL)
|
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)
|
CLIXON_LIB_PREFIX, CLIXON_LIB_NS) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
/* Top-of tree, no api-path
|
/* 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);
|
dname = xml_name(xdata);
|
||||||
if (api_path==NULL) {
|
if (api_path==NULL) {
|
||||||
|
|
@ -438,7 +440,7 @@ api_data_write(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
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,
|
* There are two cases, either the object is the list element itself,
|
||||||
* eg xpath:obj=a data:<obj><key>b</key></obj>
|
* eg xpath:obj=a data:<obj><key>b</key></obj>
|
||||||
* or the object is the key element:
|
* or the object is the key element:
|
||||||
|
|
@ -578,11 +580,11 @@ api_data_write(clixon_handle h,
|
||||||
if (xdata0)
|
if (xdata0)
|
||||||
xml_free(xdata0);
|
xml_free(xdata0);
|
||||||
if (cbx)
|
if (cbx)
|
||||||
cbuf_free(cbx);
|
cbuf_free(cbx);
|
||||||
return retval;
|
return retval;
|
||||||
} /* api_data_write */
|
} /* api_data_write */
|
||||||
|
|
||||||
/*! Generic REST PUT method
|
/*! Generic REST PUT method
|
||||||
*
|
*
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www 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
|
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @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 RFC8040 Sec 4.5 PUT
|
||||||
* @see api_data_post
|
* @see api_data_post
|
||||||
* @example
|
* @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
|
A request message-body MUST be present, representing the new data resource, or the server
|
||||||
MUST return a "400 Bad Request" status-line.
|
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.
|
If an existing resource is modified, a "204 No Content" status-line is returned.
|
||||||
|
|
||||||
* Netconf: <edit-config> (nc:operation="create/replace")
|
* Netconf: <edit-config> (nc:operation="create/replace")
|
||||||
|
|
@ -637,7 +639,7 @@ api_data_put(clixon_handle h,
|
||||||
media_in, media_out, 0, ds);
|
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] h Clixon handle
|
||||||
* @param[in] req Generic Www 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
|
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* Netconf: <edit-config> (nc:operation="merge")
|
* Netconf: <edit-config> (nc:operation="merge")
|
||||||
* See RFC8040 Sec 4.6.1
|
* See RFC8040 Sec 4.6.1
|
||||||
* Plain patch can be used to create or update, but not delete, a child
|
* Plain patch can be used to create or update, but not delete, a child
|
||||||
* resource within the target resource.
|
* resource within the target resource.
|
||||||
|
|
@ -709,7 +711,7 @@ api_data_patch(clixon_handle h,
|
||||||
* See RFC 8040 Sec 4.7
|
* See RFC 8040 Sec 4.7
|
||||||
* Example:
|
* Example:
|
||||||
* curl -X DELETE http://127.0.0.1/restconf/data/interfaces/interface=eth0
|
* 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
|
int
|
||||||
api_data_delete(clixon_handle h,
|
api_data_delete(clixon_handle h,
|
||||||
|
|
@ -749,7 +751,8 @@ api_data_delete(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path){
|
if (api_path){
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
|
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
|
||||||
|
restconf_apipath_mount_cb, h, &xbot, &y, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
***** BEGIN LICENSE BLOCK *****
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
Copyright (C) 2009-2019 Olof Hagsand
|
Copyright (C) 2009-2019 Olof Hagsand
|
||||||
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
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
|
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
|
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
|
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
|
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 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 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.
|
the terms of any one of the Apache License version 2 or the GPL.
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
***** END LICENSE BLOCK *****
|
||||||
|
|
||||||
* Restconf method implementation for operations get and data get and head
|
* 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);
|
static int api_data_pagination(clixon_handle h, void *req, char *api_path, int pi, cvec *qvec, int pretty, restconf_media media_out);
|
||||||
|
|
||||||
/*! Generic GET (both HEAD and GET)
|
/*! Generic GET (both HEAD and GET)
|
||||||
* According to restconf
|
*
|
||||||
|
* According to restconf
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||||
* @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] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
* @param[in] media_out Output media
|
* @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
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* curl -X GET http://localhost/restconf/data/interfaces/interface=eth0
|
* curl -X GET http://localhost/restconf/data/interfaces/interface=eth0
|
||||||
* @endcode
|
* @endcode
|
||||||
* See RFC8040 Sec 4.2 and 4.3
|
* See RFC8040 Sec 4.2 and 4.3
|
||||||
* XXX: cant find a way to use Accept request field to choose Content-Type
|
* XXX: cant find a way to use Accept request field to choose Content-Type
|
||||||
* I would like to support both xml and json.
|
* I would like to support both xml and json.
|
||||||
* Request may contain
|
* Request may contain
|
||||||
* Accept: application/yang.data+json,application/yang.data+xml
|
* Accept: application/yang.data+json,application/yang.data+xml
|
||||||
* Response contains one of:
|
* Response contains one of:
|
||||||
* Content-Type: application/yang-data+xml
|
* Content-Type: application/yang-data+xml
|
||||||
* Content-Type: application/yang-data+json
|
* Content-Type: application/yang-data+json
|
||||||
* @note: If a retrieval request for a data resource representing a YANG leaf-
|
* @note: If a retrieval request for a data resource representing a YANG leaf-
|
||||||
* list or list object identifies more than one instance, and XML
|
* list or list object identifies more than one instance, and XML
|
||||||
* encoding is used in the response, then an error response containing a
|
* encoding is used in the response, then an error response containing a
|
||||||
* "400 Bad Request" status-line MUST be returned by the server.
|
* "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
|
* @note there is an ad-hoc method to determine json pagination request instead of regular GET
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -117,16 +118,15 @@ api_data_get2(clixon_handle h,
|
||||||
size_t xlen;
|
size_t xlen;
|
||||||
int i;
|
int i;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
int ret;
|
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
char *attr; /* attribute value string */
|
char *attr; /* attribute value string */
|
||||||
netconf_content content = CONTENT_ALL;
|
netconf_content content = CONTENT_ALL;
|
||||||
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
int32_t depth = -1; /* Nr of levels to print, -1 is all, 0 is none */
|
||||||
cxobj *xtop = NULL;
|
cxobj *xtop = NULL;
|
||||||
cxobj *xbot = NULL;
|
|
||||||
yang_stmt *y = NULL;
|
yang_stmt *y = NULL;
|
||||||
char *defaults = NULL;
|
char *defaults = NULL;
|
||||||
cvec *nscd = NULL;
|
cvec *nscd = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
clixon_debug(CLIXON_DBG_RESTCONF, "");
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
|
|
@ -142,21 +142,28 @@ api_data_get2(clixon_handle h,
|
||||||
/* Translate api-path to xml, but to validate the api-path, note: strict=1
|
/* Translate api-path to xml, but to validate the api-path, note: strict=1
|
||||||
* xtop and xbot unnecessary for this function but needed by function
|
* xtop and xbot unnecessary for this function but needed by function
|
||||||
*/
|
*/
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &y, &xerr)) < 0)
|
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
|
||||||
goto done;
|
restconf_apipath_mount_cb, h, NULL, &y, &xerr)) < 0)
|
||||||
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
|
|
||||||
if (ret != 0 &&
|
|
||||||
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc) */
|
||||||
|
if ((ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (ret == 0){ /* validation failed */
|
||||||
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Ad-hoc method to determine json pagination request:
|
/* Ad-hoc method to determine json pagination request:
|
||||||
* address list and one of "item/offset/.." is defined
|
* address list and one of "item/offset/.." is defined
|
||||||
*/
|
*/
|
||||||
if (!head &&
|
if (!head && y &&
|
||||||
(yang_keyword_get(y) == Y_LIST || yang_keyword_get(y) == Y_LEAF_LIST) &&
|
(yang_keyword_get(y) == Y_LIST || yang_keyword_get(y) == Y_LEAF_LIST) &&
|
||||||
(cvec_find(qvec, "where") || cvec_find(qvec, "sort-by") ||
|
(cvec_find(qvec, "where") || cvec_find(qvec, "sort-by") ||
|
||||||
cvec_find(qvec, "direction") || cvec_find(qvec, "offset") ||
|
cvec_find(qvec, "direction") || cvec_find(qvec, "offset") ||
|
||||||
|
|
@ -203,16 +210,14 @@ api_data_get2(clixon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "path:%s", xpath);
|
clixon_debug(CLIXON_DBG_RESTCONF, "path:%s", xpath);
|
||||||
ret = clicon_rpc_get(h, xpath, nsc, content, depth, defaults, &xret);
|
if ((ret = clicon_rpc_get(h, xpath, nsc, content, depth, defaults, &xret)) < 0){
|
||||||
|
|
||||||
if (ret < 0){
|
|
||||||
if (netconf_operation_failed_xml(&xerr, "protocol", clixon_err_reason()) < 0)
|
if (netconf_operation_failed_xml(&xerr, "protocol", clixon_err_reason()) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
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.
|
* We need to cut that tree to only the object.
|
||||||
*/
|
*/
|
||||||
#if 0 /* DEBUG */
|
#if 0 /* DEBUG */
|
||||||
|
|
@ -254,9 +259,9 @@ api_data_get2(clixon_handle h,
|
||||||
}
|
}
|
||||||
/* Check if not exists */
|
/* Check if not exists */
|
||||||
if (xlen == 0){
|
if (xlen == 0){
|
||||||
/* 4.3: If a retrieval request for a data resource represents an
|
/* 4.3: If a retrieval request for a data resource represents an
|
||||||
instance that does not exist, then an error response containing
|
instance that does not exist, then an error response containing
|
||||||
a "404 Not Found" status-line MUST be returned by the server.
|
a "404 Not Found" status-line MUST be returned by the server.
|
||||||
The error-tag value "invalid-value" is used in this case. */
|
The error-tag value "invalid-value" is used in this case. */
|
||||||
if (netconf_invalid_value_xml(&xerr, "application", "Instance does not exist") < 0)
|
if (netconf_invalid_value_xml(&xerr, "application", "Instance does not exist") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -323,13 +328,13 @@ api_data_get2(clixon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! GET Collection
|
/*! GET pagination
|
||||||
*
|
*
|
||||||
* According to restconf collection draft. Lists, work in progress
|
* According to restconf pagination draft. Lists, work in progress
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||||
* @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] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
* @param[in] media_out Output media
|
* @param[in] media_out Output media
|
||||||
|
|
@ -338,11 +343,7 @@ api_data_get2(clixon_handle h,
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* curl -X GET http://localhost/restconf/data/interfaces
|
* curl -X GET http://localhost/restconf/data/interfaces
|
||||||
* @endcode
|
* @endcode
|
||||||
* A collection resource contains a set of data resources. It is used
|
|
||||||
* to represent a all instances or a subset of all instances in a YANG
|
|
||||||
* list or leaf-list.
|
|
||||||
* @see draft-ietf-netconf-restconf-collection-00.txt
|
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_data_pagination(clixon_handle h,
|
api_data_pagination(clixon_handle h,
|
||||||
|
|
@ -391,14 +392,14 @@ api_data_pagination(clixon_handle h,
|
||||||
if (api_path){
|
if (api_path){
|
||||||
if ((xtop = xml_new("top", NULL, CX_ELMNT)) == NULL)
|
if ((xtop = xml_new("top", NULL, CX_ELMNT)) == NULL)
|
||||||
goto done;
|
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
|
* xtop and xbot unnecessary for this function but needed by function
|
||||||
* Set strict=0 to accept list uri:s with =keys syntax
|
* Set strict=0 to accept list uri:s with =keys syntax
|
||||||
*/
|
*/
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 0, &xbot, &y, &xerr)) < 0)
|
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 0,
|
||||||
|
restconf_apipath_mount_cb, h, &xbot, &y, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc)
|
/* Translate api-path to xpath: xpath (cbpath) and namespace context (nsc)
|
||||||
* XXX: xpath not used in collection?
|
|
||||||
*/
|
*/
|
||||||
if (ret != 0 &&
|
if (ret != 0 &&
|
||||||
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
(ret = api_path2xpath(api_path, yspec, &xpath, &nsc, &xerr)) < 0)
|
||||||
|
|
@ -448,7 +449,7 @@ api_data_pagination(clixon_handle h,
|
||||||
clixon_err(OE_XML, EINVAL, "Invalid content attribute %d", content);
|
clixon_err(OE_XML, EINVAL, "Invalid content attribute %d", content);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Clixon extensions and collection attributes */
|
/* Clixon extensions and pagination attributes */
|
||||||
/* Check for depth attribute */
|
/* Check for depth attribute */
|
||||||
if ((attr = cvec_find_str(qvec, "depth")) != NULL){
|
if ((attr = cvec_find_str(qvec, "depth")) != NULL){
|
||||||
clixon_debug(CLIXON_DBG_RESTCONF, "depth=%s", attr);
|
clixon_debug(CLIXON_DBG_RESTCONF, "depth=%s", attr);
|
||||||
|
|
@ -502,7 +503,7 @@ api_data_pagination(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
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.
|
* We need to cut that tree to only the object.
|
||||||
*/
|
*/
|
||||||
#if 0 /* DEBUG */
|
#if 0 /* DEBUG */
|
||||||
|
|
@ -588,14 +589,14 @@ api_data_pagination(clixon_handle h,
|
||||||
|
|
||||||
/*! REST HEAD method
|
/*! REST HEAD method
|
||||||
*
|
*
|
||||||
* The HEAD method is sent by the client to retrieve just the header fields
|
* 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
|
* that would be returned for the comparable GET method, without the
|
||||||
* response message-body.
|
* response message-body.
|
||||||
* Relation to netconf: none
|
* Relation to netconf: none
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||||
* @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] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
* @param[in] media_out Output media
|
* @param[in] media_out Output media
|
||||||
|
|
@ -618,12 +619,12 @@ api_data_head(clixon_handle h,
|
||||||
|
|
||||||
/*! REST GET method
|
/*! REST GET method
|
||||||
*
|
*
|
||||||
* According to restconf
|
* According to restconf
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
|
||||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||||
* @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] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
* @param[in] media_out Output media
|
* @param[in] media_out Output media
|
||||||
|
|
@ -632,17 +633,17 @@ api_data_head(clixon_handle h,
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||||
* @endcode
|
* @endcode
|
||||||
* Request may contain
|
* Request may contain
|
||||||
* Accept: application/yang.data+json,application/yang.data+xml
|
* Accept: application/yang.data+json,application/yang.data+xml
|
||||||
* Response contains one of:
|
* Response contains one
|
||||||
* Content-Type: application/yang-data+xml
|
* Content-Type: application/yang-data+xml
|
||||||
* Content-Type: application/yang-data+json
|
* Content-Type: application/yang-data+json
|
||||||
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
||||||
* list or list object identifies more than one instance, and XML
|
* list or list object identifies more than one instance, and XML
|
||||||
* encoding is used in the response, then an error response containing a
|
* encoding is used in the response, then an error response containing a
|
||||||
* "400 Bad Request" status-line MUST be returned by the server.
|
* "400 Bad Request" status-line MUST be returned by the server.
|
||||||
* Netconf: <get-config>, <get>
|
* Netconf: <get-config>, <get>
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_data_get(clixon_handle h,
|
api_data_get(clixon_handle h,
|
||||||
|
|
@ -678,7 +679,7 @@ api_data_get(clixon_handle h,
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] path According to restconf (Sec 3.5.1.1 in [draft])
|
* @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] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] data Stream input data
|
* @param[in] data Stream input data
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @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
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
* curl -G http://localhost/restconf/operations
|
* curl -G http://localhost/restconf/operations
|
||||||
* @endcode
|
* @endcode
|
||||||
* @see RFC8040 Sec 3.3.2:
|
* @see RFC8040 Sec 3.3.2:
|
||||||
* This optional resource is a container that provides access to the
|
* This optional resource is a container that provides access to the
|
||||||
* data-model-specific RPC operations supported by the server. 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;
|
char *json_simple_patch_tmp;
|
||||||
int brace_count = 0;
|
int brace_count = 0;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
json_simple_patch = cbuf_new();
|
json_simple_patch = cbuf_new();
|
||||||
if (json_simple_patch == NULL)
|
if (json_simple_patch == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -150,7 +150,7 @@ yang_patch_xml2json_modified_cbuf(cxobj *x_simple_patch)
|
||||||
return json_simple_patch;
|
return json_simple_patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! yang_patch_strip_after_last_slash
|
/*! yang_patch_strip_after_last_slash
|
||||||
*
|
*
|
||||||
* Strip /... from end of val
|
* Strip /... from end of val
|
||||||
* so that e.g. "/interface=eth2" becomes "/"
|
* so that e.g. "/interface=eth2" becomes "/"
|
||||||
|
|
@ -166,7 +166,7 @@ yang_patch_strip_after_last_slash(char* val)
|
||||||
cbuf *cb;
|
cbuf *cb;
|
||||||
cbuf *val_tmp;
|
cbuf *val_tmp;
|
||||||
int idx;
|
int idx;
|
||||||
|
|
||||||
cb = cbuf_new();
|
cb = cbuf_new();
|
||||||
val_tmp = cbuf_new();
|
val_tmp = cbuf_new();
|
||||||
cbuf_append_str(val_tmp, val);
|
cbuf_append_str(val_tmp, val);
|
||||||
|
|
@ -212,7 +212,7 @@ yang_patch_do_replace(clixon_handle h,
|
||||||
restconf_media media_out,
|
restconf_media media_out,
|
||||||
ietf_ds_t ds,
|
ietf_ds_t ds,
|
||||||
cbuf *simple_patch_request_uri,
|
cbuf *simple_patch_request_uri,
|
||||||
char *target_val,
|
char *target_val,
|
||||||
int value_vec_len,
|
int value_vec_len,
|
||||||
cxobj **value_vec,
|
cxobj **value_vec,
|
||||||
cxobj *x_simple_patch
|
cxobj *x_simple_patch
|
||||||
|
|
@ -223,7 +223,7 @@ yang_patch_do_replace(clixon_handle h,
|
||||||
cbuf *delete_req_uri = NULL;
|
cbuf *delete_req_uri = NULL;
|
||||||
cbuf *post_req_uri = NULL;
|
cbuf *post_req_uri = NULL;
|
||||||
cbuf *json_simple_patch = NULL;
|
cbuf *json_simple_patch = NULL;
|
||||||
|
|
||||||
if ((delete_req_uri = cbuf_new()) == NULL){
|
if ((delete_req_uri = cbuf_new()) == NULL){
|
||||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -322,7 +322,7 @@ yang_patch_do_create(clixon_handle h,
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *value_vec_tmp = NULL;
|
cxobj *value_vec_tmp = NULL;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
|
|
||||||
// Send the POST request
|
// Send the POST request
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
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] 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] 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] 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
|
* @param[in] point_val value in "point" field of edit in YANG patch
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
@ -388,7 +388,7 @@ yang_patch_do_insert(clixon_handle h,
|
||||||
cg_var *cv;
|
cg_var *cv;
|
||||||
cbuf *point_str = NULL;
|
cbuf *point_str = NULL;
|
||||||
cvec *qvec_tmp = NULL;
|
cvec *qvec_tmp = NULL;
|
||||||
|
|
||||||
if ((point_str = cbuf_new()) == NULL){
|
if ((point_str = cbuf_new()) == NULL){
|
||||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -476,14 +476,13 @@ yang_patch_do_merge(clixon_handle h,
|
||||||
cxobj *value_vec_tmp = NULL;
|
cxobj *value_vec_tmp = NULL;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
cbuf *json_simple_patch = NULL;
|
cbuf *json_simple_patch = NULL;
|
||||||
|
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (key_xn != NULL)
|
if (key_xn != NULL)
|
||||||
xml_addsub(x_simple_patch, key_xn);
|
xml_addsub(x_simple_patch, key_xn);
|
||||||
|
|
||||||
// Loop through the XML, create JSON from each one, and submit a simple patch
|
// Loop through the XML, create JSON from each one, and submit a simple patch
|
||||||
for (int k = 0; k < value_vec_len; k++) {
|
for (int k = 0; k < value_vec_len; k++) {
|
||||||
if (value_vec[k] != NULL) {
|
if (value_vec[k] != NULL) {
|
||||||
|
|
@ -618,7 +617,7 @@ yang_patch_do_edit(clixon_handle h,
|
||||||
size_t veclen = 0;
|
size_t veclen = 0;
|
||||||
int ret;
|
int ret;
|
||||||
cxobj *xerr = NULL; /* malloced must be freed */
|
cxobj *xerr = NULL; /* malloced must be freed */
|
||||||
cxobj *xtop;
|
cxobj *xtop;
|
||||||
cxobj *xbot = NULL;
|
cxobj *xbot = NULL;
|
||||||
yang_patch_op_t operation;
|
yang_patch_op_t operation;
|
||||||
char *where_val = NULL;
|
char *where_val = NULL;
|
||||||
|
|
@ -683,7 +682,7 @@ yang_patch_do_edit(clixon_handle h,
|
||||||
// Get key field
|
// Get key field
|
||||||
/* Translate api_path to xml in the form of xtop/xbot */
|
/* Translate api_path to xml in the form of xtop/xbot */
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if ((ret = api_path2xml(cbuf_get(api_path_target), yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
if ((ret = api_path2xml_mnt(h, cbuf_get(api_path_target), yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
|
|
||||||
|
|
@ -64,13 +64,13 @@
|
||||||
#include "restconf_err.h"
|
#include "restconf_err.h"
|
||||||
#include "restconf_methods_post.h"
|
#include "restconf_methods_post.h"
|
||||||
|
|
||||||
/*! Print location header from
|
/*! Print location header from
|
||||||
*
|
*
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] xobj If set (eg POST) add to api-path
|
* @param[in] xobj If set (eg POST) add to api-path
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @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
|
* @note ports are ignored
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
|
|
@ -117,7 +117,7 @@ http_location_header(clixon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Generic REST POST method
|
/*! Generic REST POST method
|
||||||
*
|
*
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www 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
|
* @param[in] ds 0 if "data" resource, 1 if rfc8527 "ds" resource
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @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
|
* @see RFC8040 Sec 4.4.1
|
||||||
|
|
||||||
POST:
|
POST:
|
||||||
|
|
@ -203,7 +203,8 @@ api_data_post(clixon_handle h,
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path){
|
if (api_path){
|
||||||
/* Translate api-path to xml, side-effect: validate the api-path, note: strict=1 */
|
/* Translate api-path to xml, side-effect: validate the api-path, note: strict=1 */
|
||||||
if ((ret = api_path2xml(api_path, yspec, xtop, YC_DATANODE, 1, &xbot, &ybot, &xerr)) < 0)
|
if ((ret = api_path2xml_mnt(api_path, yspec, xtop, YC_DATANODE, 1,
|
||||||
|
restconf_apipath_mount_cb, h, &xbot, &ybot, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
|
|
@ -233,8 +234,8 @@ api_data_post(clixon_handle h,
|
||||||
yb = YB_MODULE;
|
yb = YB_MODULE;
|
||||||
else
|
else
|
||||||
yb = YB_PARENT;
|
yb = YB_PARENT;
|
||||||
/* Parse input data as json or xml into xml
|
/* 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
|
* 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.
|
* top-level (yspec) otherwise assume parent (xbot) is populated.
|
||||||
*/
|
*/
|
||||||
switch (media_in){
|
switch (media_in){
|
||||||
|
|
@ -273,7 +274,7 @@ api_data_post(clixon_handle h,
|
||||||
} /* switch media_in */
|
} /* switch media_in */
|
||||||
|
|
||||||
/* RFC 8040 4.4.1: The message-body MUST contain exactly one instance of the
|
/* 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);
|
clixon_debug(CLIXON_DBG_RESTCONF, "nrchildren0: %d", nrchildren0);
|
||||||
if (xml_child_nr_type(xbot, CX_ELMNT) - nrchildren0 != 1){
|
if (xml_child_nr_type(xbot, CX_ELMNT) - nrchildren0 != 1){
|
||||||
|
|
@ -315,7 +316,7 @@ api_data_post(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
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 (ybot && yang_parent_get(ydata) != ybot){
|
||||||
if (netconf_malformed_message_xml(&xerr, "Data is not prefixed with matching namespace") < 0)
|
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;
|
return retval;
|
||||||
} /* api_data_post */
|
} /* api_data_post */
|
||||||
|
|
||||||
/*! Handle input data to api_operations_post
|
/*! Handle input data to api_operations_post
|
||||||
*
|
*
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] data Stream input data
|
* @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] yrpc Yang rpc spec
|
||||||
* @param[in] xrpc XML pointer to rpc method
|
* @param[in] xrpc XML pointer to rpc method
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @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);
|
media_in = restconf_content_type(h);
|
||||||
switch (media_in){
|
switch (media_in){
|
||||||
case YANG_DATA_XML:
|
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 */
|
* support */
|
||||||
if ((ret = clixon_xml_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
if ((ret = clixon_xml_parse_string(data, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
||||||
|
|
@ -465,7 +466,7 @@ api_operations_post_input(clixon_handle h,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case YANG_DATA_JSON:
|
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 */
|
* support */
|
||||||
if ((ret = clixon_json_parse_string(data, 1, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
if ((ret = clixon_json_parse_string(data, 1, YB_NONE, yspec, &xdata, &xerr)) < 0){
|
||||||
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
if (netconf_malformed_message_xml(&xerr, clixon_err_reason()) < 0)
|
||||||
|
|
@ -486,7 +487,7 @@ api_operations_post_input(clixon_handle h,
|
||||||
break;
|
break;
|
||||||
} /* switch media_in */
|
} /* switch media_in */
|
||||||
xml_name_set(xdata, NETCONF_OUTPUT_DATA);
|
xml_name_set(xdata, NETCONF_OUTPUT_DATA);
|
||||||
/* Here xdata is:
|
/* Here xdata is:
|
||||||
* <data><input xmlns="urn:example:clixon">...</input></data>
|
* <data><input xmlns="urn:example:clixon">...</input></data>
|
||||||
*/
|
*/
|
||||||
#if 1
|
#if 1
|
||||||
|
|
@ -532,12 +533,12 @@ api_operations_post_input(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Handle output data to api_operations_post
|
/*! Handle output data to api_operations_post
|
||||||
*
|
*
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
* @param[in] xret XML reply messages from backend/handler
|
* @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] youtput Yang rpc output specification
|
||||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
* @param[in] media_out Output media
|
* @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 */
|
/* Validate that exactly only <rpc-reply> tag with exactly one element child */
|
||||||
if ((xoutput = xml_child_i_type(xret, 0, CX_ELMNT)) == NULL ||
|
if ((xoutput = xml_child_i_type(xret, 0, CX_ELMNT)) == NULL ||
|
||||||
strcmp(xml_name(xoutput),"rpc-reply") != 0
|
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
|
* 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.
|
* here, it should be detected in the backend plugin caller.
|
||||||
|| xml_child_nr_type(xrpc, CX_ELMNT) != 1 XXX backend can have multiple callbacks
|
|| 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)
|
if (xml_purge(xa) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Sanity check of outgoing XML
|
/* Sanity check of outgoing XML
|
||||||
* For now, skip outgoing checks.
|
* For now, skip outgoing checks.
|
||||||
* (1) Does not handle <ok/> properly
|
* (1) Does not handle <ok/> properly
|
||||||
* (2) Uncertain how validation errors should be logged/handled
|
* (2) Uncertain how validation errors should be logged/handled
|
||||||
|
|
@ -657,7 +658,7 @@ api_operations_post_output(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! REST operation POST method
|
/*! REST operation POST method
|
||||||
*
|
*
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] req Generic Www handle
|
* @param[in] req Generic Www handle
|
||||||
|
|
@ -669,7 +670,7 @@ api_operations_post_output(clixon_handle h,
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* See RFC 8040 Sec 3.6 / 4.4.2
|
* 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>
|
* POST {+restconf}/operations/<operation>
|
||||||
* 1. Initialize
|
* 1. Initialize
|
||||||
* 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
|
||||||
|
|
@ -740,7 +741,7 @@ api_operations_post(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
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>
|
* POST {+restconf}/operations/<operation>
|
||||||
*
|
*
|
||||||
* The <operation> field identifies the module name and rpc identifier
|
* The <operation> field identifies the module name and rpc identifier
|
||||||
|
|
@ -762,7 +763,7 @@ api_operations_post(clixon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* 3. Build xml tree with user and rpc:
|
/* 3. Build xml tree with user and rpc:
|
||||||
* <rpc username="foo"><myfn xmlns="uri"/>
|
* <rpc username="foo"><myfn xmlns="uri"/>
|
||||||
*/
|
*/
|
||||||
if ((username = clicon_username_get(h)) != NULL){
|
if ((username = clicon_username_get(h)) != NULL){
|
||||||
|
|
@ -777,14 +778,15 @@ api_operations_post(clixon_handle h,
|
||||||
if (xml_rootchild(xtop, 0, &xtop) < 0)
|
if (xml_rootchild(xtop, 0, &xtop) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if ((ret = api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, 1, &xbot, &y, &xerr)) < 0)
|
if ((ret = api_path2xml_mnt(oppath, yspec, xtop, YC_SCHEMANODE, 1,
|
||||||
|
restconf_apipath_mount_cb, h, &xbot, &y, &xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0){ /* validation failed */
|
if (ret == 0){ /* validation failed */
|
||||||
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
if (api_return_err0(h, req, xerr, pretty, media_out, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
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"/>
|
* xbot is <myfn xmlns="uri"/>
|
||||||
* 4. Parse input data (arguments):
|
* 4. Parse input data (arguments):
|
||||||
* JSON: {"example:input":{"x":0}}
|
* JSON: {"example:input":{"x":0}}
|
||||||
|
|
@ -799,7 +801,7 @@ api_operations_post(clixon_handle h,
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* Here xtop is:
|
/* Here xtop is:
|
||||||
<rpc username="foo"><myfn xmlns="uri"><x>42</x></myfn></rpc> */
|
<rpc username="foo"><myfn xmlns="uri"><x>42</x></myfn></rpc> */
|
||||||
#if 1
|
#if 1
|
||||||
clixon_debug_xml(CLIXON_DBG_RESTCONF, xtop, ". Translate input args:");
|
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>
|
* Note (1) xtop is <rpc><method> xbot is <method>
|
||||||
* (2) local handler wants <method> and backend wants <rpc><method>
|
* (2) local handler wants <method> and backend wants <rpc><method>
|
||||||
*/
|
*/
|
||||||
/* Look for local (client-side) restconf plugins.
|
/* Look for local (client-side) restconf plugins.
|
||||||
* -1:Error, 0:OK local, 1:OK backend
|
* -1:Error, 0:OK local, 1:OK backend
|
||||||
*/
|
*/
|
||||||
if ((ret = rpc_callback_call(h, xbot, req, &nr, cbret)) < 0)
|
if ((ret = rpc_callback_call(h, xbot, req, &nr, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -373,11 +373,11 @@ example_restconf_start(clixon_handle h)
|
||||||
* @see RFC 8528
|
* @see RFC 8528
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
restconf_yang_mount(clixon_handle h,
|
example_restconf_yang_mount(clixon_handle h,
|
||||||
cxobj *xt,
|
cxobj *xt,
|
||||||
int *config,
|
int *config,
|
||||||
validate_level *vl,
|
validate_level *vl,
|
||||||
cxobj **yanglib)
|
cxobj **yanglib)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
|
|
@ -407,7 +407,6 @@ restconf_yang_mount(clixon_handle h,
|
||||||
if (xml_rootchild(*yanglib, 0, yanglib) < 0)
|
if (xml_rootchild(*yanglib, 0, yanglib) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (cb)
|
if (cb)
|
||||||
|
|
@ -423,7 +422,7 @@ static clixon_plugin_api api = {
|
||||||
example_restconf_start,/* start */
|
example_restconf_start,/* start */
|
||||||
NULL, /* exit */
|
NULL, /* exit */
|
||||||
.ca_auth=example_restconf_credentials, /* auth */
|
.ca_auth=example_restconf_credentials, /* auth */
|
||||||
.ca_yang_mount=restconf_yang_mount, /* RFC 8528 schema mount */
|
.ca_yang_mount=example_restconf_yang_mount, /* RFC 8528 schema mount */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Restconf plugin initialization
|
/*! Restconf plugin initialization
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,9 @@ typedef struct {
|
||||||
yang_stmt *cp_yang; /* Corresponding yang spec (after XML match - ie resolved) */
|
yang_stmt *cp_yang; /* Corresponding yang spec (after XML match - ie resolved) */
|
||||||
} clixon_path;
|
} clixon_path;
|
||||||
|
|
||||||
|
/*! Callback if empty mount-point is encountered */
|
||||||
|
typedef int (api_path_mnt_cb_t)(clixon_handle h, cxobj *x, yang_stmt **yp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
|
|
@ -84,6 +87,10 @@ int api_path2xpath(char *api_path, yang_stmt *yspec, char **xpath, cvec **nsc, c
|
||||||
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
int api_path2xml(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
||||||
yang_class nodeclass, int strict,
|
yang_class nodeclass, int strict,
|
||||||
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);
|
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);
|
||||||
|
int api_path2xml_mnt(char *api_path, yang_stmt *yspec, cxobj *xtop,
|
||||||
|
yang_class nodeclass, int strict,
|
||||||
|
api_path_mnt_cb_t mnt_cb, void *arg,
|
||||||
|
cxobj **xpathp, yang_stmt **ypathp, cxobj **xerr);
|
||||||
int xml2api_path_1(cxobj *x, cbuf *cb);
|
int xml2api_path_1(cxobj *x, cbuf *cb);
|
||||||
int clixon_xml_find_api_path(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format,
|
int clixon_xml_find_api_path(cxobj *xt, yang_stmt *yt, cxobj ***xvec, int *xlen, const char *format,
|
||||||
...) __attribute__ ((format (printf, 5, 6)));;
|
...) __attribute__ ((format (printf, 5, 6)));;
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ enum array_element_type{
|
||||||
};
|
};
|
||||||
|
|
||||||
enum childtype{
|
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.
|
* array or leaf terminal, and to {} if proper object, ie container.
|
||||||
* anyxml/anydata?
|
* anyxml/anydata?
|
||||||
*/
|
*/
|
||||||
|
|
@ -107,7 +107,7 @@ enum childtype{
|
||||||
ANY_CHILD, /* eg <a><b/></a> or <a><b/><c/></a> */
|
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
|
* remove attributes from x
|
||||||
* @see tleaf in clixon_xml_map.c
|
* @see tleaf in clixon_xml_map.c
|
||||||
|
|
@ -182,14 +182,14 @@ arraytype2str(enum array_element_type lt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Check typeof x in array
|
/*! 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,..]"
|
* 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]"
|
* in the end: ",x]", or a single element: "[x]"
|
||||||
* Some complexity when x is in different namespaces
|
* Some complexity when x is in different namespaces
|
||||||
* @param[in] xprev The previous element (if any)
|
* @param[in] xprev The previous element (if any)
|
||||||
* @param[in] x The element itself
|
* @param[in] x The element itself
|
||||||
* @param[in] xnext The next element (if any)
|
* @param[in] xnext The next element (if any)
|
||||||
* @retval arraytype Type of array
|
* @retval arraytype Type of array
|
||||||
*/
|
*/
|
||||||
static enum array_element_type
|
static enum array_element_type
|
||||||
array_eval(cxobj *xprev,
|
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] x XML tree. Must be yang populated.
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[out] xerr Reason for invalid tree returned as netconf err msg or NULL
|
* @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 0 Invalid, wrt namespace. xerr set
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @see RFC7951 Sec 4 and 6.8
|
* @see RFC7951 Sec 4 and 6.8
|
||||||
|
|
@ -340,7 +340,7 @@ json2xml_decode_identityref(cxobj *x,
|
||||||
prefix, body, ns);
|
prefix, body, ns);
|
||||||
if (!xml_nsctx_get_prefix(nsc, ns, &prefix2)){
|
if (!xml_nsctx_get_prefix(nsc, ns, &prefix2)){
|
||||||
/* (no) insert a xmlns:<prefix> statement
|
/* (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
|
* I am not sure this is correct
|
||||||
*/
|
*/
|
||||||
if (yang_find_prefix_by_namespace(y, ns, &prefix2) < 0)
|
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"
|
* Assume an xml tree where prefix:name have been split into "module":"name"
|
||||||
* In other words, from JSON RFC7951 to XML namespace trees
|
* In other words, from JSON RFC7951 to XML namespace trees
|
||||||
*
|
*
|
||||||
* @param[in] x XML tree. Must be yang populated. After json parsing
|
* @param[in] x XML tree. Must be yang populated. After json parsing
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[out] xerr Reason for invalid tree returned as netconf err msg or NULL
|
* @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 0 Invalid, wrt namespace. xerr set
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @see RFC7951 Sec 4 and 6.8
|
* @see RFC7951 Sec 4 and 6.8
|
||||||
|
|
@ -606,7 +606,7 @@ xml2json_encode_leafs(cxobj *xb,
|
||||||
}
|
}
|
||||||
ok:
|
ok:
|
||||||
/* write into original cb0
|
/* write into original cb0
|
||||||
* includign quoting and encoding
|
* includign quoting and encoding
|
||||||
*/
|
*/
|
||||||
if (quote){
|
if (quote){
|
||||||
cprintf(cb0, "\"");
|
cprintf(cb0, "\"");
|
||||||
|
|
@ -625,8 +625,9 @@ xml2json_encode_leafs(cxobj *xb,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* X has no XML child - no body.
|
/*! Check if X has no XML child - no body.
|
||||||
* If x is a container, use {} instead of null
|
*
|
||||||
|
* If x is a container, use {} instead of null
|
||||||
* if leaf or leaf-list then assume EMPTY type, then [null]
|
* if leaf or leaf-list then assume EMPTY type, then [null]
|
||||||
* else null
|
* else null
|
||||||
*/
|
*/
|
||||||
|
|
@ -714,7 +715,7 @@ json_metadata_encoding(cbuf *cb,
|
||||||
/*! Encode XML attributes as JSON meta-data
|
/*! Encode XML attributes as JSON meta-data
|
||||||
*
|
*
|
||||||
* There are two methods for this:
|
* 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
|
* This is derived from a YANG module
|
||||||
* Examples: ietf-origin:origin, ietf-list-pagination: remaining
|
* Examples: ietf-origin:origin, ietf-list-pagination: remaining
|
||||||
* 2) Assigned, if someother mechanism, eg XSD is used
|
* 2) Assigned, if someother mechanism, eg XSD is used
|
||||||
|
|
@ -781,7 +782,7 @@ xml2json_encode_attr(cxobj *xa,
|
||||||
return retval;
|
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[out] cb Cligen text buffer containing json on exit
|
||||||
* @param[in] x XML tree structure containing XML to translate
|
* @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)
|
if (ys_real_module(ys, &ymod) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
modname = yang_argument_get(ymod);
|
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":...}
|
* A special case is for return data on the form {"data":...}
|
||||||
* See also json_xmlns_translate()
|
* See also json_xmlns_translate()
|
||||||
*/
|
*/
|
||||||
|
|
@ -946,7 +947,7 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Check for typed sub-body if:
|
/* 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"
|
* This is code for writing <a>42</a> as "a":42 and not "a":"42"
|
||||||
*/
|
*/
|
||||||
commas = xml_child_nr_notype(x, CX_ATTR) - 1;
|
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
|
/*! Translate an XML tree to JSON in a CLIgen buffer
|
||||||
*
|
*
|
||||||
* XML-style namespace notation in tree, but RFC7951 in output assume yang
|
* XML-style namespace notation in tree, but RFC7951 in output assume yang
|
||||||
* populated
|
* populated
|
||||||
*
|
|
||||||
* @param[in,out] cb Cligen buffer to write to
|
* @param[in,out] cb Cligen buffer to write to
|
||||||
* @param[in] x XML tree to translate from
|
* @param[in] x XML tree to translate from
|
||||||
* @param[in] pretty Set if output is pretty-printed
|
* @param[in] pretty Set if output is pretty-printed
|
||||||
|
|
@ -1121,9 +1121,8 @@ xml2json_cbuf1(cbuf *cb,
|
||||||
|
|
||||||
/*! Translate an XML tree to JSON in a CLIgen buffer skip top-level object
|
/*! 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
|
* populated
|
||||||
*
|
|
||||||
* @param[in,out] cb Cligen buffer to write to
|
* @param[in,out] cb Cligen buffer to write to
|
||||||
* @param[in] xt Top-level xml object
|
* @param[in] xt Top-level xml object
|
||||||
* @param[in] pretty Set if output is pretty-printed
|
* @param[in] pretty Set if output is pretty-printed
|
||||||
|
|
@ -1178,7 +1177,7 @@ clixon_json2cbuf(cbuf *cb,
|
||||||
* @param[in] vec Vector of xml objecst
|
* @param[in] vec Vector of xml objecst
|
||||||
* @param[in] veclen Length of vector
|
* @param[in] veclen Length of vector
|
||||||
* @param[in] pretty Set if output is pretty-printed (2 for debug)
|
* @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 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @note This only works if the vector is uniform, ie same object name.
|
* @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] vec Vector of xml objecst
|
||||||
* @param[in] veclen Length of vector
|
* @param[in] veclen Length of vector
|
||||||
* @param[in] pretty Set if output is pretty-printed (2 for debug)
|
* @param[in] pretty Set if output is pretty-printed (2 for debug)
|
||||||
* @param[in] fn File print function
|
* @param[in] fn File print function
|
||||||
* @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 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @note This only works if the vector is uniform, ie same object name.
|
* @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"
|
* Assume an xml tree where prefix:name have been split into "module":"name"
|
||||||
* In other words, from JSON to XML namespace trees
|
* In other words, from JSON to XML namespace trees
|
||||||
*
|
*
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[in,out] x XML tree. Translate it in-line
|
* @param[in,out] x XML tree. Translate it in-line
|
||||||
* @param[out] xerr Reason for invalid tree returned as netconf err msg or NULL
|
* @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 0 Invalid, wrt namespace. xerr set
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @note the opposite - xml2ns is made inline in xml2json1_cbuf
|
* @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] yb How to bind yang to XML top-level when parsing (if rfc7951)
|
||||||
* @param[in] yspec Yang specification (if rfc 7951)
|
* @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] 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 1 OK and valid
|
||||||
* @retval 0 Invalid (only if yang spec)
|
* @retval 0 Invalid (only if yang spec)
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*
|
*
|
||||||
* @see _xml_parse for XML variant
|
* @see _xml_parse for XML variant
|
||||||
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
* @see http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||||
* @see RFC 7951
|
* @see RFC 7951
|
||||||
|
|
@ -1449,11 +1448,12 @@ _json_parse(char *str,
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
clixon_json_yacc jy = {0,};
|
clixon_json_yacc jy = {0,};
|
||||||
int ret;
|
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
cbuf *cberr = NULL;
|
cbuf *cberr = NULL;
|
||||||
int i;
|
int i;
|
||||||
int failed = 0; /* yang assignment */
|
int failed = 0; /* yang assignment */
|
||||||
|
yang_stmt *yspec1;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (clixon_debug_get() & CLIXON_DBG_DETAIL)
|
if (clixon_debug_get() & CLIXON_DBG_DETAIL)
|
||||||
clixon_debug(CLIXON_DBG_PARSE|CLIXON_DBG_DETAIL, "%s", str);
|
clixon_debug(CLIXON_DBG_PARSE|CLIXON_DBG_DETAIL, "%s", str);
|
||||||
|
|
@ -1473,11 +1473,15 @@ _json_parse(char *str,
|
||||||
clixon_err(OE_JSON, 0, "JSON parser error with no error code (should not happen)");
|
clixon_err(OE_JSON, 0, "JSON parser error with no error code (should not happen)");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
if (xml_spec(xt))
|
||||||
|
yspec1 = ys_spec(xml_spec(xt));
|
||||||
|
else
|
||||||
|
yspec1 = yspec;
|
||||||
/* Traverse new objects */
|
/* Traverse new objects */
|
||||||
for (i = 0; i < jy.jy_xlen; i++) {
|
for (i = 0; i < jy.jy_xlen; i++) {
|
||||||
x = jy.jy_xvec[i];
|
x = jy.jy_xvec[i];
|
||||||
/* RFC 7951 Section 4: A namespace-qualified member name MUST be used for all
|
/* RFC 7951 Section 4: A namespace-qualified member name MUST be used for all
|
||||||
* members of a top-level JSON object
|
* members of a top-level JSON object
|
||||||
*/
|
*/
|
||||||
if (rfc7951 && xml_prefix(x) == NULL){
|
if (rfc7951 && xml_prefix(x) == NULL){
|
||||||
/* XXX: For top-level config file: */
|
/* 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 */
|
/* Names are split into name/prefix, but now add namespace info */
|
||||||
if ((ret = json_xmlns_translate(yspec, x, xerr)) < 0)
|
// XXX yspec is top-level but x may be across mtpoint
|
||||||
|
if ((ret = json_xmlns_translate(yspec1, x, xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
/* Now assign yang stmts to each XML node
|
/* Now assign yang stmts to each XML node
|
||||||
* XXX should be xml_bind_yang0_parent() sometimes.
|
* XXX should be xml_bind_yang0_parent() sometimes.
|
||||||
*/
|
*/
|
||||||
switch (yb){
|
switch (yb){
|
||||||
case YB_PARENT:
|
case YB_NONE:
|
||||||
|
break;
|
||||||
|
case YB_MODULE:
|
||||||
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
|
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
|
@ -1513,14 +1520,12 @@ _json_parse(char *str,
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
failed++;
|
failed++;
|
||||||
break;
|
break;
|
||||||
case YB_MODULE:
|
case YB_PARENT:
|
||||||
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
|
if ((ret = xml_bind_yang0(NULL, x, yb, yspec, xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
failed++;
|
failed++;
|
||||||
break;
|
break;
|
||||||
case YB_NONE:
|
|
||||||
break;
|
|
||||||
case YB_RPC:
|
case YB_RPC:
|
||||||
if ((ret = xml_bind_yang_rpc(NULL, x, yspec, xerr)) < 0)
|
if ((ret = xml_bind_yang_rpc(NULL, x, yspec, xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1528,7 +1533,7 @@ _json_parse(char *str,
|
||||||
failed++;
|
failed++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Now find leafs with identityrefs (+transitive) and translate
|
/* Now find leafs with identityrefs (+transitive) and translate
|
||||||
* prefixes in values to XML namespaces */
|
* prefixes in values to XML namespaces */
|
||||||
if ((ret = json2xml_decode(x, xerr)) < 0)
|
if ((ret = json2xml_decode(x, xerr)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -1537,7 +1542,7 @@ _json_parse(char *str,
|
||||||
}
|
}
|
||||||
if (failed)
|
if (failed)
|
||||||
goto fail;
|
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 */
|
not bound */
|
||||||
if (yb != YB_NONE)
|
if (yb != YB_NONE)
|
||||||
if (xml_sort_recurse(xt) < 0)
|
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] yb How to bind yang to XML top-level when parsing
|
||||||
* @param[in] yspec Yang specification, mandatory to make module->xmlns translation
|
* @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[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 1 OK and valid
|
||||||
* @retval 0 Invalid (only if yang spec) w xerr set
|
* @retval 0 Invalid (only if yang spec) w xerr set
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
@ -1599,24 +1604,24 @@ clixon_json_parse_string(char *str,
|
||||||
return _json_parse(str, rfc7951, yb, yspec, *xt, xerr);
|
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:
|
* File will be parsed as follows:
|
||||||
* (1) parsed according to JSON; # Only this check if yspec is NULL
|
* (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
|
* (3) namespaces check (using <ns>:<name> notation
|
||||||
* (4) an xml parse tree will be returned
|
* (4) an xml parse tree will be returned
|
||||||
* Note, only (1) and (4) will be done if yspec is NULL.
|
* 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"
|
* eg: name="a:b" -> prefix="a", name="b"
|
||||||
* But this is not done if yspec=NULL, and is not part of the JSON spec
|
* 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] 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] 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] yb How to bind yang to XML top-level when parsing
|
||||||
* @param[in] yspec Yang specification, or NULL
|
* @param[in] yspec Yang specification, or NULL
|
||||||
* @param[in,out] xt Pointer to (XML) parse tree. If empty, create.
|
* @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 1 OK and valid
|
||||||
* @retval 0 Invalid (only if yang spec) w xerr set
|
* @retval 0 Invalid (only if yang spec) w xerr set
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
|
||||||
|
|
@ -1035,6 +1035,8 @@ api_path2xpath(char *api_path,
|
||||||
* @param[in] y0 Yang spec for x0
|
* @param[in] y0 Yang spec for x0
|
||||||
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||||
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
|
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
|
||||||
|
* @param[in] mnt_cb Callback to mount new yang if mount-point is empty
|
||||||
|
* @param[in] arg Argument to callback
|
||||||
* @param[out] xbotp Resulting xml tree
|
* @param[out] xbotp Resulting xml tree
|
||||||
* @param[out] ybotp Yang spec matching xpathp
|
* @param[out] ybotp Yang spec matching xpathp
|
||||||
* @param[out] xerr Netconf error message (if retval=0)
|
* @param[out] xerr Netconf error message (if retval=0)
|
||||||
|
|
@ -1047,15 +1049,17 @@ api_path2xpath(char *api_path,
|
||||||
* @see api_path2xml
|
* @see api_path2xml
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_path2xml_vec(char **vec,
|
api_path2xml_vec(char **vec,
|
||||||
int nvec,
|
int nvec,
|
||||||
cxobj *x0,
|
cxobj *x0,
|
||||||
yang_stmt *y0,
|
yang_stmt *y0,
|
||||||
yang_class nodeclass,
|
yang_class nodeclass,
|
||||||
int strict,
|
int strict,
|
||||||
cxobj **xbotp,
|
api_path_mnt_cb_t mnt_cb,
|
||||||
yang_stmt **ybotp,
|
void *arg,
|
||||||
cxobj **xerr)
|
cxobj **xbotp,
|
||||||
|
yang_stmt **ybotp,
|
||||||
|
cxobj **xerr)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *nodeid;
|
char *nodeid;
|
||||||
|
|
@ -1081,6 +1085,7 @@ api_path2xml_vec(char **vec,
|
||||||
char *xpath = NULL;
|
char *xpath = NULL;
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
int ymtpoint; /* y is mountpoint */
|
int ymtpoint; /* y is mountpoint */
|
||||||
|
int ret;
|
||||||
|
|
||||||
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
|
if ((nodeid = vec[0]) == NULL || strlen(nodeid)==0){
|
||||||
if (xbotp)
|
if (xbotp)
|
||||||
|
|
@ -1112,9 +1117,12 @@ api_path2xml_vec(char **vec,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
if (ymtpoint){
|
if (ymtpoint){
|
||||||
/* XXX: Ignore return value: if none are mounted, no change of yspec is made here */
|
if ((ret = yang_mount_get_yspec_any(y0, &y0)) < 0)
|
||||||
if (yang_mount_get_yspec_any(y0, &y0) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
|
if (ret == 0 && mnt_cb != NULL){
|
||||||
|
if (mnt_cb(arg, x0, &y0) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ((ymod = yang_find_module_by_name(y0, prefix)) == NULL){
|
if ((ymod = yang_find_module_by_name(y0, prefix)) == NULL){
|
||||||
cprintf(cberr, "No such yang module prefix");
|
cprintf(cberr, "No such yang module prefix");
|
||||||
|
|
@ -1179,7 +1187,7 @@ api_path2xml_vec(char **vec,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
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 that vnr can be < length of cvk, due to empty or unset values
|
||||||
* Note also that valvec entries are encoded
|
* Note also that valvec entries are encoded
|
||||||
*/
|
*/
|
||||||
|
|
@ -1269,7 +1277,7 @@ api_path2xml_vec(char **vec,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* cf xml_bind_yang0_opt/xml_yang_mount_get */
|
/* 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;
|
goto done;
|
||||||
if (y1 != NULL)
|
if (y1 != NULL)
|
||||||
y = y1;
|
y = y1;
|
||||||
|
|
@ -1277,6 +1285,7 @@ api_path2xml_vec(char **vec,
|
||||||
if ((retval = api_path2xml_vec(vec+1, nvec-1,
|
if ((retval = api_path2xml_vec(vec+1, nvec-1,
|
||||||
x, y,
|
x, y,
|
||||||
nodeclass, strict,
|
nodeclass, strict,
|
||||||
|
mnt_cb, arg,
|
||||||
xbotp, ybotp, xerr)) < 1)
|
xbotp, ybotp, xerr)) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
ok:
|
ok:
|
||||||
|
|
@ -1344,6 +1353,44 @@ api_path2xml(char *api_path,
|
||||||
cxobj **xbotp,
|
cxobj **xbotp,
|
||||||
yang_stmt **ybotp,
|
yang_stmt **ybotp,
|
||||||
cxobj **xerr)
|
cxobj **xerr)
|
||||||
|
{
|
||||||
|
return api_path2xml_mnt(api_path, yspec, xtop, nodeclass, strict, NULL, NULL, xbotp, ybotp, xerr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Create xml tree from api-path as vector and as a side-effect mount yangs
|
||||||
|
*
|
||||||
|
* Specialized version of api_path2xml_vec
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] vec APIpath as char* vector
|
||||||
|
* @param[in] nvec Length of vec
|
||||||
|
* @param[in] x0 XML tree so far
|
||||||
|
* @param[in] y0 Yang spec for x0
|
||||||
|
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||||
|
* @param[in] strict Break if api-path is not "complete" otherwise ignore and continue
|
||||||
|
* @param[in] mnt_cb Callback if mount-point is empty
|
||||||
|
* @param[in] arg Argument to callback
|
||||||
|
* @param[out] xbotp Resulting xml tree
|
||||||
|
* @param[out] ybotp Yang spec matching xpathp
|
||||||
|
* @param[out] xerr Netconf error message (if retval=0)
|
||||||
|
* @retval 1 OK
|
||||||
|
* @retval 0 Invalid api_path or associated XML, netconf error
|
||||||
|
* @retval -1 Fatal error
|
||||||
|
*
|
||||||
|
* @note both retval -1 set clixon_err, retval 0 set xerr netconf xml
|
||||||
|
* @see api_path2xpath For api-path to xml xpath translation
|
||||||
|
* @see api_path2xml
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
api_path2xml_mnt(char *api_path,
|
||||||
|
yang_stmt *yspec,
|
||||||
|
cxobj *xtop,
|
||||||
|
yang_class nodeclass,
|
||||||
|
int strict,
|
||||||
|
api_path_mnt_cb_t mnt_cb,
|
||||||
|
void *arg,
|
||||||
|
cxobj **xbotp,
|
||||||
|
yang_stmt **ybotp,
|
||||||
|
cxobj **xerr)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char **vec = NULL;
|
char **vec = NULL;
|
||||||
|
|
@ -1376,6 +1423,7 @@ api_path2xml(char *api_path,
|
||||||
nvec--; /* NULL-terminated */
|
nvec--; /* NULL-terminated */
|
||||||
if ((retval = api_path2xml_vec(vec+1, nvec,
|
if ((retval = api_path2xml_vec(vec+1, nvec,
|
||||||
xtop, yspec, nodeclass, strict,
|
xtop, yspec, nodeclass, strict,
|
||||||
|
mnt_cb, arg,
|
||||||
xbotp, ybotp, xerr)) < 1)
|
xbotp, ybotp, xerr)) < 1)
|
||||||
goto done;
|
goto done;
|
||||||
/* Fix namespace */
|
/* Fix namespace */
|
||||||
|
|
@ -1471,14 +1519,6 @@ xml2api_path_1(cxobj *x,
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#if 0
|
|
||||||
{ /* Just for testing */
|
|
||||||
cxobj *xc;
|
|
||||||
if ((xc = xml_child_i_type(x, 0, CX_ELMNT)) != NULL)
|
|
||||||
if (xml2api_path_1(xc, cb) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
|
|
@ -932,7 +932,7 @@ clixon_plugin_yang_mount_all(clixon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Call single backend statedata callback
|
/*! Call single backend system-only callback
|
||||||
*
|
*
|
||||||
* Create an xml tree (xret) for one callback only on the form:
|
* Create an xml tree (xret) for one callback only on the form:
|
||||||
* <config>...</config>,
|
* <config>...</config>,
|
||||||
|
|
|
||||||
|
|
@ -890,7 +890,6 @@ _xml_parse(const char *str,
|
||||||
}
|
}
|
||||||
xy.xy_xtop = xt;
|
xy.xy_xtop = xt;
|
||||||
xy.xy_xparent = xt;
|
xy.xy_xparent = xt;
|
||||||
xy.xy_yspec = yspec;
|
|
||||||
if (clixon_xml_parsel_init(&xy) < 0)
|
if (clixon_xml_parsel_init(&xy) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clixon_xml_parseparse(&xy) != 0) /* yacc returns 1 on error */
|
if (clixon_xml_parseparse(&xy) != 0) /* yacc returns 1 on error */
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,6 @@ struct clixon_xml_parse_yacc {
|
||||||
cxobj *xy_xtop; /* cxobj top element (fixed) */
|
cxobj *xy_xtop; /* cxobj top element (fixed) */
|
||||||
cxobj *xy_xelement; /* cxobj active element (changes with parse context) */
|
cxobj *xy_xelement; /* cxobj active element (changes with parse context) */
|
||||||
cxobj *xy_xparent; /* cxobj parent element (changes with parse context) */
|
cxobj *xy_xparent; /* cxobj parent element (changes with parse context) */
|
||||||
yang_stmt *xy_yspec; /* If set, top-level yang-spec */
|
|
||||||
int xy_lex_state; /* lex return state */
|
int xy_lex_state; /* lex return state */
|
||||||
cxobj **xy_xvec; /* Vector of created top-level nodes (to know which are created) */
|
cxobj **xy_xvec; /* Vector of created top-level nodes (to know which are created) */
|
||||||
int xy_xlen; /* Length of xy_xvec */
|
int xy_xlen; /* Length of xy_xvec */
|
||||||
|
|
|
||||||
|
|
@ -2445,9 +2445,9 @@ yang_spec_print(FILE *f,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Print yang top-level specs
|
/*! Print yang top-level specs
|
||||||
*
|
*a
|
||||||
* @param[in] f File to print to.
|
* @param[in] f File to print to.
|
||||||
* @param[in] ymounts Yang mounts to print
|
* @param[in] ymounts Yang mounts to print
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
yang_mounts_print(FILE *f,
|
yang_mounts_print(FILE *f,
|
||||||
|
|
|
||||||
|
|
@ -775,6 +775,7 @@ yang_schema_find_share(clixon_handle h,
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
* @param[in] xt XML tree node
|
* @param[in] xt XML tree node
|
||||||
* @param[in] xyanglib XML yang-lib
|
* @param[in] xyanglib XML yang-lib
|
||||||
|
* @param[out] yspecp Resulting mounted yang spec
|
||||||
* @retval 1 OK
|
* @retval 1 OK
|
||||||
* @retval 0 No yanglib or problem when parsing yanglib
|
* @retval 0 No yanglib or problem when parsing yanglib
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ if [ -d ${TOP_SRCDIR}/yang/clixon ]; then
|
||||||
else
|
else
|
||||||
cp /usr/local/share/clixon/$y $dir/
|
cp /usr/local/share/clixon/$y $dir/
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${WITH_RESTCONF}" = "native" ]; then
|
if [ "${WITH_RESTCONF}" = "native" ]; then
|
||||||
# Create server certs
|
# Create server certs
|
||||||
certdir=$dir/certs
|
certdir=$dir/certs
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,9 @@ expectpart "$($clixon_cli -1 -f $cfg show config xml -- -m clixon-mount0 -M urn:
|
||||||
new "restconf get config mntpoint"
|
new "restconf get config mntpoint"
|
||||||
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/clixon-example:top/mylist=x/root)" 0 "HTTP/$HVER 200" '<root xmlns="urn:example:clixon"><mount1 xmlns="urn:example:mount1"><mylist1><name1>x1</name1><options xmlns="urn:example:mount2"><option2>bar</option2></options></mylist1></mount1><yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module-set><name>mylabel</name><module><name>clixon-mount0</name><namespace>urn:example:mount0</namespace></module></module-set></yang-library></root>'
|
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/clixon-example:top/mylist=x/root)" 0 "HTTP/$HVER 200" '<root xmlns="urn:example:clixon"><mount1 xmlns="urn:example:mount1"><mylist1><name1>x1</name1><options xmlns="urn:example:mount2"><option2>bar</option2></options></mylist1></mount1><yang-library xmlns="urn:ietf:params:xml:ns:yang:ietf-yang-library"><module-set><name>mylabel</name><module><name>clixon-mount0</name><namespace>urn:example:mount0</namespace></module></module-set></yang-library></root>'
|
||||||
|
|
||||||
|
new "restconf get across mountpoint"
|
||||||
|
expectpart "$(curl $CURLOPTS -X GET -H "Accept: application/yang-data+xml" $RCPROTO://localhost/restconf/data/clixon-example:top/mylist=x/root/clixon-mount1:mount1)" 0 "HTTP/$HVER 200" '<mount1 xmlns="urn:example:mount1"><mylist1><name1>x1</name1><options xmlns="urn:example:mount2"><option2>bar</option2></options></mylist1></mount1>' --not-- '<root' '<yang-library'
|
||||||
|
|
||||||
if [ $RC -ne 0 ]; then
|
if [ $RC -ne 0 ]; then
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
stop_restconf
|
stop_restconf
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue