Added restconf/operations get, see RFC8040 Sec 3.3.2:
This commit is contained in:
parent
6a22524d38
commit
990700b68d
16 changed files with 355 additions and 93 deletions
|
|
@ -1,14 +1,13 @@
|
||||||
# Clixon Changelog
|
# Clixon Changelog
|
||||||
|
|
||||||
## 3.6.0 (Upcoming)
|
## 3.6.0 (Upcoming)
|
||||||
### Major changes:
|
|
||||||
### Minor changes:
|
|
||||||
### Corrected Bugs
|
|
||||||
* Translate xml->json \n correctly
|
|
||||||
|
|
||||||
### Major changes:
|
### Major changes:
|
||||||
### Minor changes:
|
### Minor changes:
|
||||||
|
* yang_find_topnode() and api_path2xml() schemanode parameter replaced with yang_class. Replace as follows: 0 -> YC_DATANODE, 1 -> YC_SCHEMANODE
|
||||||
|
|
||||||
|
* xml2json: include prefix in translation, so <a:b> is translated to {"a:b" ..}
|
||||||
* Use <config> instead of <data> when save/load configuration to file. This
|
* Use <config> instead of <data> when save/load configuration to file. This
|
||||||
enables saved files to be used as datastore without any editing. Thanks Matt.
|
enables saved files to be used as datastore without any editing. Thanks Matt.
|
||||||
|
|
||||||
|
|
@ -21,6 +20,7 @@ enables saved files to be used as datastore without any editing. Thanks Matt.
|
||||||
* Added cli_show_version()
|
* Added cli_show_version()
|
||||||
|
|
||||||
### Corrected Bugs
|
### Corrected Bugs
|
||||||
|
* Translate xml->json \n correctly
|
||||||
* Fix issue: https://github.com/clicon/clixon/issues/15 Replace whole config
|
* Fix issue: https://github.com/clicon/clixon/issues/15 Replace whole config
|
||||||
|
|
||||||
## 3.5.0 (12 February 2018)
|
## 3.5.0 (12 February 2018)
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ cli_dbxml(clicon_handle h,
|
||||||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ expand_dbvar(void *h,
|
||||||
/* This is primarily to get "y",
|
/* This is primarily to get "y",
|
||||||
* xpath2xml would have worked!!
|
* xpath2xml would have worked!!
|
||||||
*/
|
*/
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Special case for leafref. Detect leafref via Yang-type,
|
/* Special case for leafref. Detect leafref via Yang-type,
|
||||||
* Get Yang path element, tentatively add the new syntax to the whole
|
* Get Yang path element, tentatively add the new syntax to the whole
|
||||||
|
|
|
||||||
|
|
@ -131,11 +131,12 @@ api_data(clicon_handle h,
|
||||||
/*! Operations REST method, POST
|
/*! Operations REST method, POST
|
||||||
* @param[in] h CLIXON handle
|
* @param[in] h CLIXON handle
|
||||||
* @param[in] r Fastcgi request handle
|
* @param[in] r Fastcgi request handle
|
||||||
* @param[in] api_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] pcvec Vector of path ie DOCUMENT_URI element
|
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||||
* @param[in] pi Offset, where to start pcvec
|
* @param[in] pi Offset, where to start pcvec
|
||||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] dvec Stream input data
|
* @param[in] data Stream input data
|
||||||
|
* @param[in] username Authenticated user
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_operations(clicon_handle h,
|
api_operations(clicon_handle h,
|
||||||
|
|
@ -154,16 +155,42 @@ api_operations(clicon_handle h,
|
||||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||||
if (strcmp(request_method, "GET")==0)
|
if (strcmp(request_method, "GET")==0)
|
||||||
retval = api_operation_get(h, r, path, pcvec, pi, qvec, data, username);
|
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, username);
|
||||||
else if (strcmp(request_method, "POST")==0)
|
else if (strcmp(request_method, "POST")==0)
|
||||||
retval = api_operation_post(h, r, path, pcvec, pi, qvec, data, username);
|
retval = api_operations_post(h, r, path, pcvec, pi, qvec, data, username);
|
||||||
else
|
else
|
||||||
retval = notfound(r);
|
retval = notfound(r);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Determine the root of the RESTCONF API
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @note Hardcoded to "/restconf"
|
||||||
|
* Return see RFC8040 3.1 and RFC7320
|
||||||
|
* In line with the best practices defined by [RFC7320], RESTCONF
|
||||||
|
* enables deployments to specify where the RESTCONF API is located.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
api_well_known(clicon_handle h,
|
||||||
|
FCGX_Request *r)
|
||||||
|
{
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: application/xrd+xml\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n");
|
||||||
|
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||||
|
FCGX_FPrintF(r->out, "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>\r\n");
|
||||||
|
FCGX_FPrintF(r->out, " <Link rel='restconf' href='/restconf'/>\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "</XRD>\r\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Retrieve the Top-Level API Resource
|
/*! Retrieve the Top-Level API Resource
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
* @note Only returns null for operations and data,...
|
* @note Only returns null for operations and data,...
|
||||||
|
* See RFC8040 3.3
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_root(clicon_handle h,
|
api_root(clicon_handle h,
|
||||||
|
|
@ -181,9 +208,11 @@ api_root(clicon_handle h,
|
||||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||||
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
||||||
use_xml++;
|
use_xml++;
|
||||||
|
clicon_debug(1, "%s use-xml:%d media-accept:%s", __FUNCTION__, use_xml, media_accept);
|
||||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||||
FCGX_FPrintF(r->out, "\r\n");
|
FCGX_FPrintF(r->out, "\r\n");
|
||||||
|
|
||||||
if (xml_parse_string("<restconf><data></data><operations></operations><yang-library-version>2016-06-21</yang-library-version></restconf>", NULL, &xt) < 0)
|
if (xml_parse_string("<restconf><data></data><operations></operations><yang-library-version>2016-06-21</yang-library-version></restconf>", NULL, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
|
@ -298,6 +327,8 @@ api_restconf(clicon_handle h,
|
||||||
retval = notfound(r);
|
retval = notfound(r);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
test(r, 1);
|
||||||
|
|
||||||
if (pn == 2){
|
if (pn == 2){
|
||||||
retval = api_root(h, r);
|
retval = api_root(h, r);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -319,7 +350,6 @@ api_restconf(clicon_handle h,
|
||||||
if (str2cvec(data, '&', '=', &dvec) < 0)
|
if (str2cvec(data, '&', '=', &dvec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
test(r, 1);
|
|
||||||
/* If present, check credentials. See "plugin_credentials" in plugin
|
/* If present, check credentials. See "plugin_credentials" in plugin
|
||||||
* See RFC 8040 section 2.5
|
* See RFC 8040 section 2.5
|
||||||
*/
|
*/
|
||||||
|
|
@ -367,23 +397,6 @@ api_restconf(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Process a FastCGI request
|
|
||||||
* @param[in] r Fastcgi request handle
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
api_well_known(clicon_handle h,
|
|
||||||
FCGX_Request *r)
|
|
||||||
{
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
|
||||||
FCGX_FPrintF(r->out, "Content-Type: application/xrd+xml\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "\r\n");
|
|
||||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
|
||||||
FCGX_FPrintF(r->out, "<XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0'>\r\n");
|
|
||||||
FCGX_FPrintF(r->out, " <Link rel='restconf' href='/restconf'/>\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "</XRD>\r\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
restconf_terminate(clicon_handle h)
|
restconf_terminate(clicon_handle h)
|
||||||
|
|
@ -547,7 +560,7 @@ main(int argc,
|
||||||
if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0)
|
if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0)
|
||||||
api_restconf(h, r); /* This is the function */
|
api_restconf(h, r); /* This is the function */
|
||||||
else if (strncmp(path, RESTCONF_WELL_KNOWN, strlen(RESTCONF_WELL_KNOWN)) == 0) {
|
else if (strncmp(path, RESTCONF_WELL_KNOWN, strlen(RESTCONF_WELL_KNOWN)) == 0) {
|
||||||
api_well_known(h, r); /* This is the function */
|
api_well_known(h, r); /* */
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
clicon_debug(1, "top-level %s not found", path);
|
clicon_debug(1, "top-level %s not found", path);
|
||||||
|
|
|
||||||
|
|
@ -195,6 +195,7 @@ api_data_get_err(clicon_handle h,
|
||||||
* @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] username Authenticated user
|
||||||
* @param[in] head If 1 is HEAD, otherwise GET
|
* @param[in] head If 1 is HEAD, otherwise GET
|
||||||
* @code
|
* @code
|
||||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||||
|
|
@ -332,6 +333,7 @@ api_data_get2(clicon_handle h,
|
||||||
* @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] username Authenticated user
|
||||||
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.
|
||||||
|
|
@ -355,6 +357,7 @@ api_data_head(clicon_handle h,
|
||||||
* @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] username Authenticated user
|
||||||
* @code
|
* @code
|
||||||
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
* curl -G http://localhost/restconf/data/interfaces/interface=eth0
|
||||||
* @endcode
|
* @endcode
|
||||||
|
|
@ -390,6 +393,8 @@ api_data_get(clicon_handle h,
|
||||||
* @param[in] pi Offset, where to start pcvec
|
* @param[in] pi Offset, where to start pcvec
|
||||||
* @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] username Authenticated user
|
||||||
|
|
||||||
* @note restconf POST is mapped to edit-config create.
|
* @note restconf POST is mapped to edit-config create.
|
||||||
POST:
|
POST:
|
||||||
target resource type is datastore --> create a top-level resource
|
target resource type is datastore --> create a top-level resource
|
||||||
|
|
@ -461,7 +466,7 @@ api_data_post(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Parse input data as json or xml into xml */
|
/* Parse input data as json or xml into xml */
|
||||||
if (parse_xml){
|
if (parse_xml){
|
||||||
|
|
@ -586,6 +591,7 @@ match_list_keys(yang_stmt *y,
|
||||||
* @param[in] pi Offset, where to start pcvec
|
* @param[in] pi Offset, where to start pcvec
|
||||||
* @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] username Authenticated user
|
||||||
* @note restconf PUT is mapped to edit-config replace.
|
* @note restconf PUT is mapped to edit-config replace.
|
||||||
* @example
|
* @example
|
||||||
curl -X PUT -d '{"enabled":"false"}' http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
curl -X PUT -d '{"enabled":"false"}' http://127.0.0.1/restconf/data/interfaces/interface=eth1
|
||||||
|
|
@ -651,7 +657,7 @@ api_data_put(clicon_handle h,
|
||||||
if (xml_value_set(xu, username) < 0)
|
if (xml_value_set(xu, username) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Parse input data as json or xml into xml */
|
/* Parse input data as json or xml into xml */
|
||||||
if (parse_xml){
|
if (parse_xml){
|
||||||
|
|
@ -749,6 +755,7 @@ api_data_put(clicon_handle h,
|
||||||
* @param[in] pi Offset, where to start pcvec
|
* @param[in] pi Offset, where to start pcvec
|
||||||
* @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] username Authenticated user
|
||||||
* Netconf: <edit-config> (nc:operation="merge")
|
* Netconf: <edit-config> (nc:operation="merge")
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
|
|
@ -769,6 +776,7 @@ api_data_patch(clicon_handle h,
|
||||||
* @param[in] h CLIXON handle
|
* @param[in] h CLIXON handle
|
||||||
* @param[in] r Fastcgi request handle
|
* @param[in] r Fastcgi request 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] username Authenticated user
|
||||||
* @param[in] pi Offset, where path starts
|
* @param[in] pi Offset, where path starts
|
||||||
* 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
|
||||||
|
|
@ -812,7 +820,7 @@ api_data_delete(clicon_handle h,
|
||||||
if (xml_value_set(xu, username) < 0)
|
if (xml_value_set(xu, username) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -851,10 +859,27 @@ api_data_delete(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! NYI
|
/*! GET restconf/operations resource
|
||||||
|
* @param[in] h Clixon handle
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @param[in] path According to restconf (Sec 3.5.1.1 in [draft])
|
||||||
|
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||||
|
* @param[in] pi Offset, where path starts
|
||||||
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
|
* @param[in] data Stream input data
|
||||||
|
* @param[in] username Authenticated user
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* curl -G http://localhost/restconf/operations
|
||||||
|
* @endcode
|
||||||
|
* RFC8040 Sec 3.3.2:
|
||||||
|
* This optional resource is a container that provides access to the
|
||||||
|
* data-model-specific RPC operations supported by the server. The
|
||||||
|
* server MAY omit this resource if no data-model-specific RPC
|
||||||
|
* operations are advertised.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_operation_get(clicon_handle h,
|
api_operations_get(clicon_handle h,
|
||||||
FCGX_Request *r,
|
FCGX_Request *r,
|
||||||
char *path,
|
char *path,
|
||||||
cvec *pcvec,
|
cvec *pcvec,
|
||||||
|
|
@ -863,23 +888,87 @@ api_operation_get(clicon_handle h,
|
||||||
char *data,
|
char *data,
|
||||||
char *username)
|
char *username)
|
||||||
{
|
{
|
||||||
notimplemented(r);
|
int retval = -1;
|
||||||
return 0;
|
int pretty;
|
||||||
|
char *media_accept;
|
||||||
|
int use_xml = 0; /* By default use JSON */
|
||||||
|
yang_spec *yspec;
|
||||||
|
yang_stmt *ym;
|
||||||
|
yang_stmt *yc;
|
||||||
|
yang_stmt *yprefix;
|
||||||
|
char *prefix;
|
||||||
|
cbuf *cbx = NULL;
|
||||||
|
cxobj *xt = NULL;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||||
|
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||||
|
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
||||||
|
use_xml++;
|
||||||
|
yspec = clicon_dbspec_yang(h);
|
||||||
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
|
goto done;
|
||||||
|
cprintf(cbx, "<operations>");
|
||||||
|
ym = NULL;
|
||||||
|
while ((ym = yn_each((yang_node*)yspec, ym)) != NULL) {
|
||||||
|
if ((yprefix = yang_find((yang_node*)ym, Y_PREFIX, NULL)) != NULL)
|
||||||
|
prefix = yprefix->ys_argument;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
yc = NULL;
|
||||||
|
while ((yc = yn_each((yang_node*)ym, yc)) != NULL) {
|
||||||
|
if (yc->ys_keyword != Y_RPC)
|
||||||
|
continue;
|
||||||
|
cprintf(cbx, "<%s:%s />", prefix, yc->ys_argument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cprintf(cbx, "</operations>");
|
||||||
|
clicon_debug(1, "%s xml:%s", __FUNCTION__, cbuf_get(cbx));
|
||||||
|
if (xml_parse_string(cbuf_get(cbx), yspec, &xt) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||||
|
goto done;
|
||||||
|
cbuf_reset(cbx); /* reuse same cbuf */
|
||||||
|
if (use_xml){
|
||||||
|
if (clicon_xml2cbuf(cbx, xt, 0, pretty) < 0) /* Dont print top object? */
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (xml2json_cbuf(cbx, xt, pretty) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
clicon_debug(1, "%s ret:%s", __FUNCTION__, cbuf_get(cbx));
|
||||||
|
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||||
|
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||||
|
// ok:
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
|
if (cbx)
|
||||||
|
cbuf_free(cbx);
|
||||||
|
if (xt)
|
||||||
|
xml_free(xt);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! REST operation POST method
|
/*! REST operation POST method
|
||||||
* @param[in] h CLIXON handle
|
* @param[in] h CLIXON handle
|
||||||
* @param[in] r Fastcgi request handle
|
* @param[in] r Fastcgi request handle
|
||||||
|
* @param[in] path According to restconf (Sec 3.5.1.1 in [draft])
|
||||||
* @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 to start pcvec
|
* @param[in] pi Offset, where to start pcvec
|
||||||
* @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
|
||||||
* @note We map post to edit-config create.
|
* @param[in] username Authenticated user
|
||||||
|
|
||||||
|
* @note We map post to edit-config create.
|
||||||
POST {+restconf}/operations/<operation>
|
POST {+restconf}/operations/<operation>
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_operation_post(clicon_handle h,
|
api_operations_post(clicon_handle h,
|
||||||
FCGX_Request *r,
|
FCGX_Request *r,
|
||||||
char *path,
|
char *path,
|
||||||
cvec *pcvec,
|
cvec *pcvec,
|
||||||
|
|
@ -956,7 +1045,7 @@ api_operation_post(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: something strange for rpc user */
|
/* XXX: something strange for rpc user */
|
||||||
if (api_path2xml(oppath, yspec, xtop, 1, &xbot, &y) < 0)
|
if (api_path2xml(oppath, yspec, xtop, YC_SCHEMANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
#if 1
|
#if 1
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,11 @@ int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path,
|
||||||
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi,
|
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi,
|
||||||
char *username);
|
char *username);
|
||||||
|
|
||||||
int api_operation_get(clicon_handle h, FCGX_Request *r,
|
int api_operations_get(clicon_handle h, FCGX_Request *r,
|
||||||
char *path,
|
char *path,
|
||||||
cvec *pcvec, int pi, cvec *qvec, char *data, char *username);
|
cvec *pcvec, int pi, cvec *qvec, char *data, char *username);
|
||||||
|
|
||||||
int api_operation_post(clicon_handle h, FCGX_Request *r,
|
int api_operations_post(clicon_handle h, FCGX_Request *r,
|
||||||
char *path,
|
char *path,
|
||||||
cvec *pcvec, int pi, cvec *qvec, char *data,
|
cvec *pcvec, int pi, cvec *qvec, char *data,
|
||||||
char *username);
|
char *username);
|
||||||
|
|
|
||||||
|
|
@ -312,7 +312,7 @@ get(char *dbname,
|
||||||
restval++;
|
restval++;
|
||||||
}
|
}
|
||||||
if (i == 1){ /* spec->module->node */
|
if (i == 1){ /* spec->module->node */
|
||||||
if ((y = yang_find_topnode(ys, name, 0)) == NULL){
|
if ((y = yang_find_topnode(ys, name, YC_DATANODE)) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -799,7 +799,7 @@ kv_put(xmldb_handle xh,
|
||||||
}
|
}
|
||||||
// clicon_log(LOG_WARNING, "%s", __FUNCTION__);
|
// clicon_log(LOG_WARNING, "%s", __FUNCTION__);
|
||||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
|
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
|
||||||
if ((ys = yang_find_topnode(yspec, xml_name(x), 0)) == NULL){
|
if ((ys = yang_find_topnode(yspec, xml_name(x), YC_DATANODE)) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x));
|
clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x));
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -792,7 +792,7 @@ text_modify_top(cxobj *x0,
|
||||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
/* Get yang spec of the child */
|
/* Get yang spec of the child */
|
||||||
if ((yc = yang_find_topnode(yspec, x1cname, 0)) == NULL){
|
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ int xml_spec_populate(cxobj *x, void *arg);
|
||||||
int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
|
int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
|
||||||
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
|
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
|
||||||
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
|
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
|
||||||
int schemanode, cxobj **xpathp, yang_node **ypathp);
|
yang_class nodeclass, cxobj **xpathp, yang_node **ypathp);
|
||||||
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
|
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
|
||||||
int yang_enum_int_value(cxobj *node, int32_t *val);
|
int yang_enum_int_value(cxobj *node, int32_t *val);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,14 +123,45 @@ enum rfc_6020{
|
||||||
Y_SPEC /* XXX: NOTE NOT YANG STATEMENT, reserved for top level spec */
|
Y_SPEC /* XXX: NOTE NOT YANG STATEMENT, reserved for top level spec */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Type used to group yang nodes used in some functions
|
||||||
|
* See RFC7950 Sec 3
|
||||||
|
*/
|
||||||
|
enum yang_class{
|
||||||
|
YC_NONE, /* Someting else,... */
|
||||||
|
YC_DATANODE, /* See yang_datanode() */
|
||||||
|
YC_DATADEFINITION, /* See yang_datadefinition() */
|
||||||
|
YC_SCHEMANODE /* See yang_schemanode() */
|
||||||
|
};
|
||||||
|
typedef enum yang_class yang_class;
|
||||||
|
|
||||||
#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
|
#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
|
||||||
|
|
||||||
/* Yang data node */
|
/* Yang data node
|
||||||
|
* See RFC7950 Sec 3:
|
||||||
|
* o data node: A node in the schema tree that can be instantiated in a
|
||||||
|
* data tree. One of container, leaf, leaf-list, list, anydata, and
|
||||||
|
* anyxml.
|
||||||
|
*/
|
||||||
#define yang_datanode(y) ((y)->ys_keyword == Y_CONTAINER || (y)->ys_keyword == Y_LEAF || (y)->ys_keyword == Y_LIST || (y)->ys_keyword == Y_LEAF_LIST || (y)->ys_keyword == Y_ANYXML)
|
#define yang_datanode(y) ((y)->ys_keyword == Y_CONTAINER || (y)->ys_keyword == Y_LEAF || (y)->ys_keyword == Y_LIST || (y)->ys_keyword == Y_LEAF_LIST || (y)->ys_keyword == Y_ANYXML)
|
||||||
|
|
||||||
/* Yang schema node */
|
/* Yang data definition statement
|
||||||
|
* See RFC 7950 Sec 3:
|
||||||
|
* o data definition statement: A statement that defines new data
|
||||||
|
* nodes. One of "container", "leaf", "leaf-list", "list", "choice",
|
||||||
|
* "case", "augment", "uses", "anydata", and "anyxml".
|
||||||
|
*/
|
||||||
|
#define yang_datadefinition(y) (yang_datanode(y) || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_AUGMENT || (y)->ys_keyword == Y_USES)
|
||||||
|
|
||||||
|
|
||||||
|
/* Yang schema node .
|
||||||
|
* See RFC 7950 Sec 3:
|
||||||
|
* o schema node: A node in the schema tree. One of action, container,
|
||||||
|
* leaf, leaf-list, list, choice, case, rpc, input, output,
|
||||||
|
* notification, anydata, and anyxml.
|
||||||
|
*/
|
||||||
#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION)
|
#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION)
|
||||||
|
|
||||||
|
|
||||||
typedef struct yang_stmt yang_stmt; /* forward */
|
typedef struct yang_stmt yang_stmt; /* forward */
|
||||||
|
|
||||||
/*! Yang type cache. Yang type statements can cache all typedef info here
|
/*! Yang type cache. Yang type statements can cache all typedef info here
|
||||||
|
|
@ -217,7 +248,7 @@ yang_stmt *yang_find_module_by_prefix(yang_stmt *ys, char *prefix);
|
||||||
yang_stmt *yang_find(yang_node *yn, int keyword, char *argument);
|
yang_stmt *yang_find(yang_node *yn, int keyword, char *argument);
|
||||||
yang_stmt *yang_find_datanode(yang_node *yn, char *argument);
|
yang_stmt *yang_find_datanode(yang_node *yn, char *argument);
|
||||||
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
|
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
|
||||||
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name, int schemanode);
|
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name, yang_class class);
|
||||||
int yang_order(yang_stmt *y);
|
int yang_order(yang_stmt *y);
|
||||||
int yang_print(FILE *f, yang_node *yn);
|
int yang_print(FILE *f, yang_node *yn);
|
||||||
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
|
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
|
||||||
|
|
|
||||||
|
|
@ -304,10 +304,12 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NO_ARRAY:
|
case NO_ARRAY:
|
||||||
if (!flat)
|
if (!flat){
|
||||||
cprintf(cb, "%*s\"%s\": ",
|
cprintf(cb, "%*s\"", pretty?(level*JSON_INDENT):0, "");
|
||||||
pretty?(level*JSON_INDENT):0, "",
|
if (xml_namespace(x))
|
||||||
xml_name(x));
|
cprintf(cb, "%s:", xml_namespace(x));
|
||||||
|
cprintf(cb, "%s\": ", xml_name(x));
|
||||||
|
}
|
||||||
switch (childt){
|
switch (childt){
|
||||||
case NULL_CHILD:
|
case NULL_CHILD:
|
||||||
cprintf(cb, "null");
|
cprintf(cb, "null");
|
||||||
|
|
@ -323,9 +325,10 @@ xml2json1_cbuf(cbuf *cb,
|
||||||
break;
|
break;
|
||||||
case FIRST_ARRAY:
|
case FIRST_ARRAY:
|
||||||
case SINGLE_ARRAY:
|
case SINGLE_ARRAY:
|
||||||
cprintf(cb, "%*s\"%s\": ",
|
cprintf(cb, "%*s\"", pretty?(level*JSON_INDENT):0, "");
|
||||||
pretty?(level*JSON_INDENT):0, "",
|
if (xml_namespace(x))
|
||||||
xml_name(x));
|
cprintf(cb, "%s:", xml_namespace(x));
|
||||||
|
cprintf(cb, "%s\": ", xml_name(x));
|
||||||
level++;
|
level++;
|
||||||
cprintf(cb, "[%s%*s",
|
cprintf(cb, "[%s%*s",
|
||||||
pretty?"\n":"",
|
pretty?"\n":"",
|
||||||
|
|
|
||||||
|
|
@ -587,7 +587,7 @@ yang_next(yang_node *y,
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
|
|
||||||
if (y->yn_keyword == Y_SPEC)
|
if (y->yn_keyword == Y_SPEC)
|
||||||
ys = yang_find_topnode((yang_spec*)y, name, 0);
|
ys = yang_find_topnode((yang_spec*)y, name, YC_DATANODE);
|
||||||
else
|
else
|
||||||
ys = yang_find_datanode(y, name);
|
ys = yang_find_datanode(y, name);
|
||||||
if (ys == NULL)
|
if (ys == NULL)
|
||||||
|
|
@ -1313,7 +1313,7 @@ xml_spec_populate(cxobj *x,
|
||||||
(yp = xml_spec(xp)) != NULL)
|
(yp = xml_spec(xp)) != NULL)
|
||||||
y = yang_find_datanode((yang_node*)yp, xml_name(x));
|
y = yang_find_datanode((yang_node*)yp, xml_name(x));
|
||||||
else
|
else
|
||||||
y = yang_find_topnode(yspec, name, 0); /* still NULL for config */
|
y = yang_find_topnode(yspec, name, YC_DATANODE); /* still NULL for config */
|
||||||
#endif
|
#endif
|
||||||
if (y)
|
if (y)
|
||||||
xml_spec_set(x, y);
|
xml_spec_set(x, y);
|
||||||
|
|
@ -1367,7 +1367,7 @@ api_path2xpath_cvv(yang_spec *yspec,
|
||||||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
clicon_debug(1, "[%d] cvname:%s", i, name);
|
||||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
||||||
if (i == offset){
|
if (i == offset){
|
||||||
if ((y = yang_find_topnode(yspec, name, 0)) == NULL){
|
if ((y = yang_find_topnode(yspec, name, YC_DATANODE)) == NULL){
|
||||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
@ -1448,7 +1448,7 @@ api_path2xpath(yang_spec *yspec,
|
||||||
* @param[in] nvec Length of vec
|
* @param[in] nvec Length of vec
|
||||||
* @param[in] x0 Xpath tree so far
|
* @param[in] x0 Xpath tree so far
|
||||||
* @param[in] y0 Yang spec for x0
|
* @param[in] y0 Yang spec for x0
|
||||||
* @param[in] schemanode If set use schema nodes otherwise data nodes.
|
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||||
* @param[out] xpathp Resulting xml tree
|
* @param[out] xpathp Resulting xml tree
|
||||||
* @param[out] ypathp Yang spec matching xpathp
|
* @param[out] ypathp Yang spec matching xpathp
|
||||||
* @see api_path2xml
|
* @see api_path2xml
|
||||||
|
|
@ -1458,7 +1458,7 @@ api_path2xml_vec(char **vec,
|
||||||
int nvec,
|
int nvec,
|
||||||
cxobj *x0,
|
cxobj *x0,
|
||||||
yang_node *y0,
|
yang_node *y0,
|
||||||
int schemanode,
|
yang_class nodeclass,
|
||||||
cxobj **xpathp,
|
cxobj **xpathp,
|
||||||
yang_node **ypathp)
|
yang_node **ypathp)
|
||||||
{
|
{
|
||||||
|
|
@ -1500,10 +1500,10 @@ api_path2xml_vec(char **vec,
|
||||||
name = local;
|
name = local;
|
||||||
}
|
}
|
||||||
if (y0->yn_keyword == Y_SPEC){ /* top-node */
|
if (y0->yn_keyword == Y_SPEC){ /* top-node */
|
||||||
y = yang_find_topnode((yang_spec*)y0, name, schemanode);
|
y = yang_find_topnode((yang_spec*)y0, name, nodeclass);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
y = schemanode?yang_find_schemanode((yang_node*)y0, name):
|
y = (nodeclass==YC_SCHEMANODE)?yang_find_schemanode((yang_node*)y0, name):
|
||||||
yang_find_datanode((yang_node*)y0, name);
|
yang_find_datanode((yang_node*)y0, name);
|
||||||
}
|
}
|
||||||
if (y == NULL){
|
if (y == NULL){
|
||||||
|
|
@ -1572,7 +1572,7 @@ api_path2xml_vec(char **vec,
|
||||||
}
|
}
|
||||||
if (api_path2xml_vec(vec+1, nvec-1,
|
if (api_path2xml_vec(vec+1, nvec-1,
|
||||||
x, (yang_node*)y,
|
x, (yang_node*)y,
|
||||||
schemanode,
|
nodeclass,
|
||||||
xpathp, ypathp) < 0)
|
xpathp, ypathp) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -1588,7 +1588,7 @@ api_path2xml_vec(char **vec,
|
||||||
* @param[in] api_path API-path as defined in RFC 8040
|
* @param[in] api_path API-path as defined in RFC 8040
|
||||||
* @param[in] yspec Yang spec
|
* @param[in] yspec Yang spec
|
||||||
* @param[in,out] xtop Incoming XML tree
|
* @param[in,out] xtop Incoming XML tree
|
||||||
* @param[in] schemanode If set use schema nodes otherwise data nodes.
|
* @param[in] nodeclass Set to schema nodes, data nodes, etc
|
||||||
* @param[out] xbotp Resulting xml tree (end of xpath)
|
* @param[out] xbotp Resulting xml tree (end of xpath)
|
||||||
* @param[out] ybotp Yang spec matching xbotp
|
* @param[out] ybotp Yang spec matching xbotp
|
||||||
* @example
|
* @example
|
||||||
|
|
@ -1605,7 +1605,7 @@ int
|
||||||
api_path2xml(char *api_path,
|
api_path2xml(char *api_path,
|
||||||
yang_spec *yspec,
|
yang_spec *yspec,
|
||||||
cxobj *xtop,
|
cxobj *xtop,
|
||||||
int schemanode,
|
yang_class nodeclass,
|
||||||
cxobj **xbotp,
|
cxobj **xbotp,
|
||||||
yang_node **ybotp)
|
yang_node **ybotp)
|
||||||
{
|
{
|
||||||
|
|
@ -1628,7 +1628,7 @@ api_path2xml(char *api_path,
|
||||||
}
|
}
|
||||||
nvec--; /* NULL-terminated */
|
nvec--; /* NULL-terminated */
|
||||||
if (api_path2xml_vec(vec+1, nvec,
|
if (api_path2xml_vec(vec+1, nvec,
|
||||||
xtop, (yang_node*)yspec, schemanode,
|
xtop, (yang_node*)yspec, nodeclass,
|
||||||
xbotp, ybotp) < 0)
|
xbotp, ybotp) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -1736,7 +1736,7 @@ xml_merge(cxobj *x0,
|
||||||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
/* Get yang spec of the child */
|
/* Get yang spec of the child */
|
||||||
if ((yc = yang_find_topnode(yspec, x1cname, 0)) == NULL){
|
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ xml_child_spec(char *name,
|
||||||
if (xp && (yparent = xml_spec(xp)) != NULL)
|
if (xp && (yparent = xml_spec(xp)) != NULL)
|
||||||
y = yang_find_datanode((yang_node*)yparent, name);
|
y = yang_find_datanode((yang_node*)yparent, name);
|
||||||
else if (yspec)
|
else if (yspec)
|
||||||
y = yang_find_topnode(yspec, name, 0); /* still NULL for config */
|
y = yang_find_topnode(yspec, name, YC_DATANODE); /* still NULL for config */
|
||||||
else
|
else
|
||||||
y = NULL;
|
y = NULL;
|
||||||
*yresult = y;
|
*yresult = y;
|
||||||
|
|
|
||||||
|
|
@ -402,6 +402,94 @@ yang_find(yang_node *yn,
|
||||||
}
|
}
|
||||||
return match ? ys : NULL;
|
return match ? ys : NULL;
|
||||||
}
|
}
|
||||||
|
#ifdef NOTYET
|
||||||
|
/*! Prototype more generic than yang_find_datanode and yang_find_schemanode
|
||||||
|
*/
|
||||||
|
yang_stmt *
|
||||||
|
yang_find_class(yang_node *yn,
|
||||||
|
char *argument,
|
||||||
|
yang_class class)
|
||||||
|
{
|
||||||
|
yang_stmt *ys = NULL;
|
||||||
|
yang_stmt *yc = NULL;
|
||||||
|
yang_stmt *ysmatch = NULL;
|
||||||
|
int i, j;
|
||||||
|
int ok;
|
||||||
|
|
||||||
|
for (i=0; i<yn->yn_len; i++){
|
||||||
|
ys = yn->yn_stmt[i];
|
||||||
|
switch(class){
|
||||||
|
case YC_NONE:
|
||||||
|
ok = 1;
|
||||||
|
break;
|
||||||
|
case YC_DATANODE:
|
||||||
|
ok = yang_datanode(ys);
|
||||||
|
break;
|
||||||
|
case YC_DATADEFINITION:
|
||||||
|
ok = yang_datadefinition(ys);
|
||||||
|
break;
|
||||||
|
case YC_SCHEMANODE:
|
||||||
|
ok = yang_schemanode(ys);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!ok)
|
||||||
|
continue;
|
||||||
|
switch(class){
|
||||||
|
case YC_NONE:
|
||||||
|
if (argument == NULL)
|
||||||
|
ysmatch = ys;
|
||||||
|
else
|
||||||
|
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
||||||
|
ysmatch = ys;
|
||||||
|
if (ysmatch)
|
||||||
|
goto match;
|
||||||
|
break;
|
||||||
|
case YC_DATANODE:
|
||||||
|
case YC_DATADEFINITION:
|
||||||
|
if (argument == NULL)
|
||||||
|
ysmatch = ys;
|
||||||
|
else
|
||||||
|
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
||||||
|
ysmatch = ys;
|
||||||
|
if (ysmatch)
|
||||||
|
goto match;
|
||||||
|
break;
|
||||||
|
case YC_SCHEMANODE:
|
||||||
|
if (ys->ys_keyword == Y_CHOICE){ /* Look for its children */
|
||||||
|
for (j=0; j<ys->ys_len; j++){
|
||||||
|
yc = ys->ys_stmt[j];
|
||||||
|
if (yc->ys_keyword == Y_CASE) /* Look for its children */
|
||||||
|
ysmatch = yang_find_class((yang_node*)yc, argument, class);
|
||||||
|
else{
|
||||||
|
if (yang_schemanode(yc)){
|
||||||
|
if (argument == NULL)
|
||||||
|
ysmatch = yc;
|
||||||
|
else
|
||||||
|
if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
|
||||||
|
ysmatch = yc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ysmatch)
|
||||||
|
goto match;
|
||||||
|
}
|
||||||
|
} /* Y_CHOICE */
|
||||||
|
else{
|
||||||
|
if (argument == NULL)
|
||||||
|
ysmatch = ys;
|
||||||
|
else
|
||||||
|
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
||||||
|
ysmatch = ys;
|
||||||
|
if (ysmatch)
|
||||||
|
goto match;
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} /* switch */
|
||||||
|
} /* for */
|
||||||
|
match:
|
||||||
|
return ysmatch;
|
||||||
|
}
|
||||||
|
#endif /* NOTYET */
|
||||||
|
|
||||||
/*! Find child data node with matching argument (container, leaf, etc)
|
/*! Find child data node with matching argument (container, leaf, etc)
|
||||||
*
|
*
|
||||||
|
|
@ -455,6 +543,8 @@ yang_find_datanode(yang_node *yn,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Find child schema node with matching argument (container, leaf, etc)
|
/*! Find child schema node with matching argument (container, leaf, etc)
|
||||||
|
* @param[in] yn Yang node, current context node.
|
||||||
|
* @param[in] argument if NULL, match any(first) argument.
|
||||||
* @note XXX unify code with yang_find_datanode?
|
* @note XXX unify code with yang_find_datanode?
|
||||||
* @see yang_find_datanode
|
* @see yang_find_datanode
|
||||||
*/
|
*/
|
||||||
|
|
@ -505,7 +595,7 @@ yang_find_schemanode(yang_node *yn,
|
||||||
/*! Find first matching data node in all (sub)modules in a yang spec
|
/*! Find first matching data node in all (sub)modules in a yang spec
|
||||||
*
|
*
|
||||||
* @param[in] ysp Yang specification
|
* @param[in] ysp Yang specification
|
||||||
* @param[in] name if NULL, match any(first) argument. XXX is that really a case?
|
* @param[in] argument if NULL, match any(first) argument. XXX is that really a case?
|
||||||
* @param[in] schemanode If set look for schema nodes, otherwise only data nodes
|
* @param[in] schemanode If set look for schema nodes, otherwise only data nodes
|
||||||
* A yang specification has modules as children which in turn can have
|
* A yang specification has modules as children which in turn can have
|
||||||
* syntax-nodes as children. This function goes through all the modules to
|
* syntax-nodes as children. This function goes through all the modules to
|
||||||
|
|
@ -514,8 +604,8 @@ yang_find_schemanode(yang_node *yn,
|
||||||
*/
|
*/
|
||||||
yang_stmt *
|
yang_stmt *
|
||||||
yang_find_topnode(yang_spec *ysp,
|
yang_find_topnode(yang_spec *ysp,
|
||||||
char *name,
|
char *argument,
|
||||||
int schemanode)
|
yang_class class)
|
||||||
{
|
{
|
||||||
yang_stmt *ys = NULL;
|
yang_stmt *ys = NULL;
|
||||||
yang_stmt *yc = NULL;
|
yang_stmt *yc = NULL;
|
||||||
|
|
@ -523,13 +613,22 @@ yang_find_topnode(yang_spec *ysp,
|
||||||
|
|
||||||
for (i=0; i<ysp->yp_len; i++){
|
for (i=0; i<ysp->yp_len; i++){
|
||||||
ys = ysp->yp_stmt[i];
|
ys = ysp->yp_stmt[i];
|
||||||
if (schemanode){
|
switch (class){
|
||||||
if ((yc = yang_find_schemanode((yang_node*)ys, name)) != NULL)
|
case YC_NONE:
|
||||||
|
if ((yc = yang_find((yang_node*)ys, 0, argument)) != NULL)
|
||||||
return yc;
|
return yc;
|
||||||
|
break;
|
||||||
|
case YC_DATANODE:
|
||||||
|
if ((yc = yang_find_datanode((yang_node*)ys, argument)) != NULL)
|
||||||
|
return yc;
|
||||||
|
break;
|
||||||
|
case YC_SCHEMANODE:
|
||||||
|
if ((yc = yang_find_schemanode((yang_node*)ys, argument)) != NULL)
|
||||||
|
return yc;
|
||||||
|
break;
|
||||||
|
case YC_DATADEFINITION:
|
||||||
|
break; /* nyi */
|
||||||
}
|
}
|
||||||
else
|
|
||||||
if ((yc = yang_find_datanode((yang_node*)ys, name)) != NULL)
|
|
||||||
return yc;
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
@ -1929,7 +2028,7 @@ yang_abs_schema_nodeid(yang_spec *yspec,
|
||||||
}
|
}
|
||||||
if (ymod == NULL){ /* Try with topnode */
|
if (ymod == NULL){ /* Try with topnode */
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
if ((ys = yang_find_topnode(yspec, id, 1)) == NULL){
|
if ((ys = yang_find_topnode(yspec, id, YC_SCHEMANODE)) == NULL){
|
||||||
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id);
|
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,22 +78,49 @@ sleep 1
|
||||||
|
|
||||||
new "restconf tests"
|
new "restconf tests"
|
||||||
|
|
||||||
new "restconf options"
|
new "restconf root discovery. RFC 8040 3.1 (xml+xrd)"
|
||||||
expectfn "curl -i -sS -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
|
|
||||||
|
|
||||||
new "restconf head"
|
|
||||||
expectfn "curl -s -I http://localhost/restconf/data" "HTTP/1.1 200 OK"
|
|
||||||
#Content-Type: application/yang-data+json"
|
|
||||||
|
|
||||||
new "restconf root discovery"
|
|
||||||
expectfn "curl -s -X GET http://localhost/.well-known/host-meta" "<Link rel='restconf' href='/restconf'/>"
|
expectfn "curl -s -X GET http://localhost/.well-known/host-meta" "<Link rel='restconf' href='/restconf'/>"
|
||||||
|
|
||||||
new "restconf get restconf json"
|
new "restconf get restconf resource. RFC 8040 3.3 (json)"
|
||||||
expectfn "curl -sG http://localhost/restconf" '{"data": null,"operations": null,"yang-library-version": "2016-06-21"}}'
|
expectfn "curl -sG http://localhost/restconf" '{"data": null,"operations": null,"yang-library-version": "2016-06-21"}}'
|
||||||
|
|
||||||
new "restconf get restconf/yang-library-version json"
|
new "restconf get restconf resource. RFC 8040 3.3 (xml)"
|
||||||
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf)
|
||||||
|
expect="<restconf><data/><operations/><yang-library-version>2016-06-21</yang-library-version></restconf>"
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "restconf get restconf/operations. RFC8040 3.3.2"
|
||||||
|
expectfn "curl -sG http://localhost/restconf/operations" '{"operations": {"ex:empty": null,"ex:input": null,"ex:output": null,"rt:fib-route": null,"rt:route-count": null}}'
|
||||||
|
|
||||||
|
new "restconf get restconf/operations. RFC8040 3.3.2 (xml)"
|
||||||
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/operations)
|
||||||
|
expect="<operations><ex:empty/><ex:input/><ex:output/><rt:fib-route/><rt:route-count/></operations>"
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "restconf get restconf/yang-library-version. RFC8040 3.3.3"
|
||||||
expectfn "curl -sG http://localhost/restconf/yang-library-version" '{"yang-library-version": "2016-06-21"}'
|
expectfn "curl -sG http://localhost/restconf/yang-library-version" '{"yang-library-version": "2016-06-21"}'
|
||||||
|
|
||||||
|
new "restconf get restconf/yang-library-version. RFC8040 3.3.3 (xml)"
|
||||||
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/yang-library-version)
|
||||||
|
expect="<yang-library-version>2016-06-21</yang-library-version>"
|
||||||
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
if [ -z "$match" ]; then
|
||||||
|
err "$expect" "$ret"
|
||||||
|
fi
|
||||||
|
|
||||||
|
new "restconf options. RFC 8040 4.1"
|
||||||
|
expectfn "curl -i -s -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
|
||||||
|
|
||||||
|
new "restconf head. RFC 8040 4.2"
|
||||||
|
expectfn "curl -s -I http://localhost/restconf/data" "HTTP/1.1 200 OK"
|
||||||
|
#Content-Type: application/yang-data+json"
|
||||||
|
|
||||||
new "restconf empty rpc"
|
new "restconf empty rpc"
|
||||||
expectfn 'curl -s -X POST -d {"input":{"name":""}} http://localhost/restconf/operations/ex:empty' '{"output": null}'
|
expectfn 'curl -s -X POST -d {"input":{"name":""}} http://localhost/restconf/operations/ex:empty' '{"output": null}'
|
||||||
|
|
||||||
|
|
@ -101,6 +128,7 @@ new "restconf get empty config + state json"
|
||||||
expectfn "curl -sSG http://localhost/restconf/data" "{\"data\": $state}"
|
expectfn "curl -sSG http://localhost/restconf/data" "{\"data\": $state}"
|
||||||
|
|
||||||
new "restconf get empty config + state xml"
|
new "restconf get empty config + state xml"
|
||||||
|
# Cant get shell macros to work, inline matching from lib.sh
|
||||||
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data)
|
ret=$(curl -s -H "Accept: application/yang-data+xml" -G http://localhost/restconf/data)
|
||||||
expect="<data><interfaces-state><interface><name>eth0</name><type>eth</type><if-index>42</if-index></interface></interfaces-state></data>"
|
expect="<data><interfaces-state><interface><name>eth0</name><type>eth</type><if-index>42</if-index></interface></interfaces-state></data>"
|
||||||
match=`echo $ret | grep -EZo "$expect"`
|
match=`echo $ret | grep -EZo "$expect"`
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,6 @@ sleep 1
|
||||||
|
|
||||||
new "restconf tests"
|
new "restconf tests"
|
||||||
|
|
||||||
|
|
||||||
new "restconf POST initial tree"
|
new "restconf POST initial tree"
|
||||||
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' ""
|
expectfn 'curl -s -X POST -d {"cont1":{"interface":{"name":"local0","type":"regular"}}} http://localhost/restconf/data' ""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue