Merge branch 'develop'
This commit is contained in:
commit
a8ea3ead11
24 changed files with 850 additions and 330 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
|
@ -3,6 +3,8 @@
|
|||
## Upcoming 3.3.2
|
||||
|
||||
### Known issues
|
||||
* Please use text datastore, key-value datastore no up-to-date
|
||||
* Restconf RPC does not encode output correct
|
||||
|
||||
### Major changes:
|
||||
* Added support for YANG anyxml.
|
||||
|
|
@ -50,13 +52,12 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
|
|||
</nopresence>
|
||||
```
|
||||
|
||||
* Added YANG RPC support for netconf and CLI. With example rpc documentation and testcase. This replaces the previous "downcall" mechanism.
|
||||
* This means you can make netconf rpc calls as defined by YANG.
|
||||
* Added YANG RPC support for netconf, restconf and CLI. With example rpc documentation and testcase. This replaces the previous "downcall" mechanism.
|
||||
* This means you can make netconf/restconf rpc calls
|
||||
* However you need to register an RPC backend callback using the backend_rpc_cb_register() function. See documentation and example for more details.
|
||||
* Note that RESTCONF PUT for RCP calls is not yet supported.
|
||||
* Example, the following YANG RPC definition enables you to run a netconf rpc.
|
||||
```
|
||||
YANG:
|
||||
YANG:
|
||||
rpc myrpc {
|
||||
input {
|
||||
leaf name {
|
||||
|
|
@ -64,8 +65,10 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
|
|||
}
|
||||
}
|
||||
}
|
||||
NETCONF:
|
||||
NETCONF:
|
||||
<rpc><myrpc><name>hello</name><rpc>
|
||||
RESTCONF:
|
||||
curl -sS -X POST -d {"input":{"name":"hello"}} http://localhost/restconf/operations/myroute'
|
||||
```
|
||||
|
||||
* Enhanced leafref functionality:
|
||||
|
|
@ -86,6 +89,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
|
|||
* You need to define state data in a backend callback. See the example and documentation for more details.
|
||||
|
||||
### Minor changes:
|
||||
* Added xpath support for predicate: current(), eg /interface[name=current()/../name]
|
||||
* Added prefix parsing of xpath, allowing eg /p:x/p:y, but prefix ignored.
|
||||
* Corrected Yang union CLI generation and type validation. Recursive unions did not work.
|
||||
* Corrected Yang pattern type escaping problem, ie '\.' did not work properly. This requires update of cligen as well.
|
||||
|
|
|
|||
11
README.md
11
README.md
|
|
@ -88,9 +88,14 @@ used to generate an interactive CLI client as well as provide
|
|||
[Restconf](apps/restconf/README.md) clients.
|
||||
|
||||
The [YANG RFC 6020](https://www.rfc-editor.org/rfc/rfc6020.txt) is implemented with the following exceptions:
|
||||
- object-references
|
||||
- if-feature
|
||||
- unique
|
||||
- conformance: feature, if-feature, deviation
|
||||
- identy, base, identityref
|
||||
- list features: min/max-elements, unique, ordered-by
|
||||
|
||||
There are also new features in YANG 1.1 [YANG RFC
|
||||
7950](https://www.rfc-editor.org/rfc/rfc7950.txt) most of which are
|
||||
not implemented.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ cli_dbxml(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
if ((xa = xml_new("operation", xbot)) == NULL)
|
||||
goto done;
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ expand_dbvar(void *h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Special case for leafref. Detect leafref via Yang-type,
|
||||
* Get Yang path element, tentatively add the new syntax to the whole
|
||||
|
|
|
|||
|
|
@ -196,27 +196,6 @@ catch:
|
|||
return -1;
|
||||
}
|
||||
|
||||
/*! Struct to carry info into and out of ys_find_rpc callback
|
||||
*/
|
||||
typedef struct {
|
||||
char *name; /* name of rpc */
|
||||
yang_stmt *yrpc; /* matching yang statement */
|
||||
} find_rpc_arg;
|
||||
|
||||
/*! Check yang rpc statement, return yang rpc statement if found
|
||||
*/
|
||||
static int
|
||||
ys_find_rpc(yang_stmt *ys,
|
||||
void *arg)
|
||||
{
|
||||
find_rpc_arg *fra = (find_rpc_arg*)arg;
|
||||
|
||||
if (strcmp(fra->name, ys->ys_argument) == 0){
|
||||
fra->yrpc = ys;
|
||||
return 1; /* handled */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! See if there is any callback registered for this tag
|
||||
*
|
||||
|
|
@ -240,8 +219,7 @@ netconf_plugin_callbacks(clicon_handle h,
|
|||
yang_stmt *yinput;
|
||||
yang_stmt *youtput;
|
||||
cxobj *xoutput;
|
||||
find_rpc_arg fra = {0,0};
|
||||
int ret;
|
||||
cbuf *cb;
|
||||
|
||||
if (deps != NULL){
|
||||
nreg = deps;
|
||||
|
|
@ -259,13 +237,17 @@ netconf_plugin_callbacks(clicon_handle h,
|
|||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, 0, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
/* create absolute path */
|
||||
cprintf(cb, "/%s:%s", xml_namespace(xn), xml_name(xn));
|
||||
/* Find yang rpc statement, return yang rpc statement if found */
|
||||
fra.name = xml_name(xn);
|
||||
if ((ret = yang_apply((yang_node*)yspec, Y_RPC, ys_find_rpc, &fra)) < 0)
|
||||
if (yang_abs_schema_nodeid(yspec, cbuf_get(cb), &yrpc) < 0)
|
||||
goto done;
|
||||
/* Check if found */
|
||||
if (ret == 1){
|
||||
yrpc = fra.yrpc;
|
||||
if (yrpc != NULL){
|
||||
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
|
||||
xml_spec_set(xn, yinput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xn, CX_ELMNT, xml_spec_populate, yinput) < 0)
|
||||
|
|
@ -287,7 +269,7 @@ netconf_plugin_callbacks(clicon_handle h,
|
|||
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL){
|
||||
xoutput=xpath_first(*xret, "/");
|
||||
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, yinput) < 0)
|
||||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, youtput) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xoutput, CX_ELMNT,
|
||||
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
|
||||
|
|
@ -300,6 +282,8 @@ netconf_plugin_callbacks(clicon_handle h,
|
|||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -264,7 +264,19 @@ test(FCGX_Request *r,
|
|||
printparam(r, "SERVER_NAME", dbg);
|
||||
printparam(r, "HTTP_COOKIE", dbg);
|
||||
printparam(r, "HTTPS", dbg);
|
||||
|
||||
printparam(r, "HTTP_ACCEPT", dbg);
|
||||
printparam(r, "HTTP_CONTENT_TYPE", dbg);
|
||||
#if 0 /* For debug */
|
||||
clicon_debug(1, "All environment vars:");
|
||||
{
|
||||
extern char **environ;
|
||||
int i;
|
||||
for (i = 0; environ[i] != NULL; i++){
|
||||
clicon_debug(1, "%s", environ[i]);
|
||||
}
|
||||
}
|
||||
clicon_debug(1, "End environment vars:");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@
|
|||
resource ([RFC6415]) */
|
||||
#define RESTCONF_API_ROOT "/restconf/"
|
||||
|
||||
/*! Generic REST method, GET, PUT, DELETE
|
||||
/*! Generic REST method, GET, PUT, DELETE, etc
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_path According to restconf (Sec 3.5.1.1 in [draft])
|
||||
|
|
@ -120,6 +120,37 @@ api_data(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Operations REST method, POST
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] api_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 to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] dvec Stream input data
|
||||
*/
|
||||
static int
|
||||
api_operations(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
int retval = -1;
|
||||
char *request_method;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||
if (strcmp(request_method, "POST")==0)
|
||||
retval = api_operation_post(h, r, path, pcvec, pi, qvec, data);
|
||||
else
|
||||
retval = notfound(r);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Process a FastCGI request
|
||||
* @param[in] r Fastcgi request handle
|
||||
*/
|
||||
|
|
@ -157,12 +188,10 @@ request_process(clicon_handle h,
|
|||
clicon_debug(1, "DATA=%s", data);
|
||||
if (str2cvec(data, '&', '=', &dvec) < 0)
|
||||
goto done;
|
||||
|
||||
if ((method = pvec[2]) == NULL){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
test(r, 1);
|
||||
/* If present, check credentials */
|
||||
|
|
@ -176,6 +205,9 @@ request_process(clicon_handle h,
|
|||
|
||||
if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
|
||||
retval = api_data(h, r, path, pcvec, 2, qvec, data);
|
||||
else
|
||||
if (strcmp(method, "operations") == 0) /* rpc */
|
||||
retval = api_operations(h, r, path, pcvec, 2, qvec, data);
|
||||
else if (strcmp(method, "test") == 0)
|
||||
retval = test(r, 0);
|
||||
else
|
||||
|
|
@ -357,7 +389,7 @@ main(int argc,
|
|||
if ((path = FCGX_GetParam("REQUEST_URI", r->envp)) != NULL){
|
||||
if (strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)) == 0 ||
|
||||
strncmp(path, RESTCONF_API_ROOT, strlen(RESTCONF_API_ROOT)-1) == 0)
|
||||
request_process(h, r);
|
||||
request_process(h, r); /* This is the function */
|
||||
else{
|
||||
clicon_debug(1, "top-level not found");
|
||||
notfound(r);
|
||||
|
|
|
|||
|
|
@ -161,8 +161,13 @@ api_data_get_gen(clicon_handle h,
|
|||
cbuf *cbj = NULL;;
|
||||
int code;
|
||||
const char *reason_phrase;
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
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 ((path = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
|
|
@ -213,15 +218,22 @@ api_data_get_gen(clicon_handle h,
|
|||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
FCGX_SetExitStatus(200, r->out); /* OK */
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang.data+json\r\n");
|
||||
FCGX_FPrintF(r->out, "Content-Type: application/yang-data+%s\r\n", use_xml?"xml":"json");
|
||||
FCGX_FPrintF(r->out, "\r\n");
|
||||
if (head)
|
||||
goto ok;
|
||||
clicon_debug(1, "%s name:%s child:%d", __FUNCTION__, xml_name(xret), xml_child_nr(xret));
|
||||
vec = xml_childvec_get(xret);
|
||||
|
||||
clicon_debug(1, "%s xretnr:%d", __FUNCTION__, xml_child_nr(xret));
|
||||
if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0)
|
||||
goto done;
|
||||
if (use_xml){
|
||||
if (clicon_xml2cbuf(cbx, xret, 0, 1) < 0) /* Dont print top object? */
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
vec = xml_childvec_get(xret);
|
||||
if (xml2json_cbuf_vec(cbx, vec, xml_child_nr(xret), 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
clicon_debug(1, "%s cbuf:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
|
|
@ -276,8 +288,8 @@ api_data_head(clicon_handle h,
|
|||
* Request may contain
|
||||
* Accept: application/yang.data+json,application/yang.data+xml
|
||||
* Response contains one of:
|
||||
* Content-Type: application/yang.data+xml
|
||||
* Content-Type: application/yang.data+json
|
||||
* Content-Type: application/yang-data+xml
|
||||
* Content-Type: application/yang-data+json
|
||||
* NOTE: If a retrieval request for a data resource representing a YANG leaf-
|
||||
* list or list object identifies more than one instance, and XML
|
||||
* encoding is used in the response, then an error response containing a
|
||||
|
|
@ -340,10 +352,15 @@ api_data_post(clicon_handle h,
|
|||
yang_node *y = NULL;
|
||||
yang_spec *yspec;
|
||||
cxobj *xa;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||
__FUNCTION__,
|
||||
api_path, data);
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
|
|
@ -354,10 +371,16 @@ api_data_post(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Parse input data as json into xml */
|
||||
if (json_parse_str(data, &xdata) < 0){
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
if (clicon_xml_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -448,10 +471,15 @@ api_data_put(clicon_handle h,
|
|||
yang_node *y = NULL;
|
||||
yang_spec *yspec;
|
||||
cxobj *xa;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||
__FUNCTION__,
|
||||
api_path, data);
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
|
|
@ -462,10 +490,16 @@ api_data_put(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Parse input data as json into xml */
|
||||
if (json_parse_str(data, &xdata) < 0){
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
if (clicon_xml_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -581,7 +615,7 @@ api_data_delete(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, &xbot, &y) < 0)
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, 0, &xbot, &y) < 0)
|
||||
goto done;
|
||||
if ((xa = xml_new("operation", xbot)) == NULL)
|
||||
goto done;
|
||||
|
|
@ -606,7 +640,7 @@ api_data_delete(clicon_handle h,
|
|||
FCGX_FPrintF(r->out, "\r\n");
|
||||
retval = 0;
|
||||
done:
|
||||
if (cbx)
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
|
|
@ -614,3 +648,158 @@ api_data_delete(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! REST operation POST method
|
||||
* @param[in] h CLIXON handle
|
||||
* @param[in] r Fastcgi request handle
|
||||
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
|
||||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @note We map post to edit-config create.
|
||||
|
||||
POST {+restconf}/operations/<operation>
|
||||
|
||||
|
||||
*/
|
||||
int
|
||||
api_operation_post(clicon_handle h,
|
||||
FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
char *oppath = path;
|
||||
yang_stmt *yrpc = NULL;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *yinput;
|
||||
yang_stmt *youtput;
|
||||
cxobj *xdata = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xbot = NULL;
|
||||
yang_node *y = NULL;
|
||||
cxobj *xinput;
|
||||
cxobj *xoutput;
|
||||
cxobj *x;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default return JSON */
|
||||
|
||||
clicon_debug(1, "%s json:\"%s\"", __FUNCTION__, data);
|
||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
||||
use_xml++;
|
||||
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
clicon_debug(1, "%s accept:\"%s\" content-type:\"%s\"",
|
||||
__FUNCTION__, media_accept, media_content_type);
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
goto done;
|
||||
}
|
||||
for (i=0; i<pi; i++)
|
||||
oppath = index(oppath+1, '/');
|
||||
/* Find yang rpc statement, return yang rpc statement if found */
|
||||
if (yang_abs_schema_nodeid(yspec, oppath, &yrpc) < 0)
|
||||
goto done;
|
||||
if (yrpc == NULL){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
}
|
||||
/* Create an xml message:
|
||||
* <"rpc"><operation><input-args>...
|
||||
* eg <rpc><fib-route><name>
|
||||
*/
|
||||
/* Create config top-of-tree */
|
||||
if ((xtop = xml_new("rpc", NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
if (api_path2xml(oppath, yspec, xtop, 1, &xbot, &y) < 0)
|
||||
goto done;
|
||||
if (data){
|
||||
/* Parse input data as json or xml into xml */
|
||||
if (parse_xml){
|
||||
if (clicon_xml_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else if (json_parse_str(data, &xdata) < 0){
|
||||
clicon_debug(1, "%s json parse fail: %s", __FUNCTION__, data);
|
||||
goto done;
|
||||
}
|
||||
/* xdata should have format <top><input> */
|
||||
if ((xinput = xpath_first(xdata, "/input")) != NULL){
|
||||
/* Add all input under <rpc>path */
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xinput, x, -1)) != NULL)
|
||||
if (xml_addsub(xbot, x) < 0)
|
||||
goto done;
|
||||
if ((yinput = yang_find((yang_node*)yrpc, Y_INPUT, NULL)) != NULL){
|
||||
xml_spec_set(xinput, yinput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xinput, CX_ELMNT, xml_spec_populate, yinput) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xinput, CX_ELMNT,
|
||||
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_yang_validate_add(xinput, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clicon_rpc_netconf_xml(h, xtop, &xret, NULL) < 0)
|
||||
goto done;
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
xoutput=xpath_first(xret, "/");
|
||||
if ((youtput = yang_find((yang_node*)yrpc, Y_OUTPUT, NULL)) != NULL &&
|
||||
xoutput){
|
||||
xml_name_set(xoutput, "output");
|
||||
clicon_debug(1, "%s xoutput:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
cbuf_reset(cbx);
|
||||
xml_spec_set(xoutput, youtput); /* needed for xml_spec_populate */
|
||||
if (xml_apply(xoutput, CX_ELMNT, xml_spec_populate, youtput) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xoutput, CX_ELMNT,
|
||||
(xml_applyfn_t*)xml_yang_validate_all, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_yang_validate_add(xoutput, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Sanity check of outgoing XML */
|
||||
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");
|
||||
if (xoutput){
|
||||
if (use_xml){
|
||||
if (clicon_xml2cbuf(cbx, xoutput, 0, 1) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (xml2json_cbuf(cbx, xoutput, 1) < 0)
|
||||
goto done;
|
||||
clicon_debug(1, "%s xoutput:%s", __FUNCTION__, cbuf_get(cbx));
|
||||
FCGX_FPrintF(r->out, "%s", cbx?cbuf_get(cbx):"");
|
||||
FCGX_FPrintF(r->out, "\r\n\r\n");
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xdata)
|
||||
xml_free(xdata);
|
||||
if (xtop)
|
||||
xml_free(xtop);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
if (cbx)
|
||||
cbuf_free(cbx);
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,4 +60,8 @@ int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path,
|
|||
cvec *qvec, char *data);
|
||||
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi);
|
||||
|
||||
int api_operation_post(clicon_handle h, FCGX_Request *r,
|
||||
char *path,
|
||||
cvec *pcvec, int pi, cvec *qvec, char *data);
|
||||
|
||||
#endif /* _RESTCONF_METHODS_H_ */
|
||||
|
|
|
|||
|
|
@ -321,7 +321,7 @@ get(char *dbname,
|
|||
restval++;
|
||||
}
|
||||
if (i == 1){ /* spec->module->node */
|
||||
if ((y = yang_find_topnode(ys, name)) == NULL){
|
||||
if ((y = yang_find_topnode(ys, name, 0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -808,7 +808,7 @@ kv_put(xmldb_handle xh,
|
|||
}
|
||||
// clicon_log(LOG_WARNING, "%s", __FUNCTION__);
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL){
|
||||
if ((ys = yang_find_topnode(yspec, xml_name(x))) == NULL){
|
||||
if ((ys = yang_find_topnode(yspec, xml_name(x), 0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", xml_name(x));
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -663,7 +663,7 @@ text_modify_top(cxobj *x0,
|
|||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||
x1cname = xml_name(x1c);
|
||||
/* Get yang spec of the child */
|
||||
if ((yc = yang_find_topnode(yspec, x1cname)) == NULL){
|
||||
if ((yc = yang_find_topnode(yspec, x1cname, 0)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,16 +38,6 @@
|
|||
#ifndef _CLIXON_XML_MAP_H_
|
||||
#define _CLIXON_XML_MAP_H_
|
||||
|
||||
/*
|
||||
* lvmap_xml op codes
|
||||
*/
|
||||
enum {
|
||||
LVXML, /* a.b{x=1} -> <a><b>1 */
|
||||
LVXML_VAL, /* a.b{x=1} -> <a><b><x>1 */
|
||||
LVXML_VECVAL, /* key: a.b.0{x=1} -> <a><b><x>1</x></b></a> och */
|
||||
LVXML_VECVAL2, /* key: a.b.0{x=1} -> <a><x>1</x></a> och */
|
||||
};
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
|
|
@ -73,7 +63,9 @@ int xml_non_config_data(cxobj *xt, void *arg);
|
|||
int xml_spec_populate(cxobj *x, void *arg);
|
||||
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_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop, cxobj **xpathp, yang_node **ypathp);
|
||||
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
|
||||
int schemanode, cxobj **xpathp, yang_node **ypathp);
|
||||
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
|
||||
int yang_enum_int_value(cxobj *node, int32_t *val);
|
||||
|
||||
#endif /* _CLIXON_XML_MAP_H_ */
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ enum rfc_6020{
|
|||
typedef struct yang_stmt yang_stmt; /* forward */
|
||||
|
||||
/*! Yang type cache. Yang type statements can cache all typedef info here
|
||||
* @note unions not cached
|
||||
*/
|
||||
struct yang_type_cache{
|
||||
int yc_options;
|
||||
|
|
@ -158,7 +159,7 @@ struct yang_stmt{
|
|||
leaf, leaf-list, mandatory, fraction-digits */
|
||||
cvec *ys_cvec; /* List of stmt-specific variables
|
||||
Y_RANGE: range_min, range_max */
|
||||
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data */
|
||||
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -205,7 +206,8 @@ yang_spec *ys_spec(yang_stmt *ys);
|
|||
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_datanode(yang_node *yn, char *argument);
|
||||
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name);
|
||||
yang_stmt *yang_find_schemanode(yang_node *yn, char *argument);
|
||||
yang_stmt *yang_find_topnode(yang_spec *ysp, char *name, int schemanode);
|
||||
|
||||
int yang_print(FILE *f, yang_node *yn);
|
||||
int yang_print_cbuf(cbuf *cb, yang_node *yn, int marginal);
|
||||
|
|
@ -213,9 +215,10 @@ int yang_parse(clicon_handle h, const char *yang_dir,
|
|||
const char *module, const char *revision, yang_spec *ysp);
|
||||
int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,
|
||||
void *arg);
|
||||
yang_node *yang_abs_schema_nodeid(yang_node *yn, char *schema_nodeid);
|
||||
yang_node *yang_desc_schema_nodeid(yang_node *yn, char *schema_nodeid);
|
||||
yang_node *yang_schema_nodeid(yang_node *yn, char *xpath);
|
||||
int yang_abs_schema_nodeid(yang_spec *yspec, char *schema_nodeid,
|
||||
yang_stmt **yres);
|
||||
int yang_desc_schema_nodeid(yang_node *yn, char *schema_nodeid,
|
||||
yang_stmt **yres);
|
||||
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
|
||||
int ys_parse_sub(yang_stmt *ys);
|
||||
int yang_mandatory(yang_stmt *ys);
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ xml2json1_cbuf(cbuf *cb,
|
|||
* goto err;
|
||||
* cbuf_free(cb);
|
||||
* @endcode
|
||||
* See also xml2json
|
||||
* @see clicon_xml2cbuf
|
||||
*/
|
||||
int
|
||||
xml2json_cbuf(cbuf *cb,
|
||||
|
|
@ -442,7 +442,7 @@ xml2json_cbuf(cbuf *cb,
|
|||
|
||||
/*! Translate a vector of xml objects to JSON CLigen buffer.
|
||||
* This is done by adding a top pseudo-object, and add the vector as subs,
|
||||
* and then not pritning the top pseudo-.object using the 'flat' option.
|
||||
* and then not printing the top pseudo-object using the 'flat' option.
|
||||
* @param[out] cb Cligen buffer to write to
|
||||
* @param[in] vec Vector of xml objecst
|
||||
* @param[in] veclen Length of vector
|
||||
|
|
|
|||
|
|
@ -974,7 +974,10 @@ clicon_xml2cbuf(cbuf *cb,
|
|||
}
|
||||
if (prettyprint && xml_body(x)==NULL)
|
||||
cprintf(cb, "%*s", level*XML_INDENT, "");
|
||||
cprintf(cb, "</%s>", name);
|
||||
cprintf(cb, "</");
|
||||
if (xml_namespace(x))
|
||||
cprintf(cb, "%s:", xml_namespace(x));
|
||||
cprintf(cb, "%s>", name);
|
||||
}
|
||||
if (prettyprint)
|
||||
cprintf(cb, "\n");
|
||||
|
|
|
|||
|
|
@ -644,7 +644,7 @@ xml_diff1(yang_stmt *ys,
|
|||
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){
|
||||
name = xml_name(x1);
|
||||
if (ys->ys_keyword == Y_SPEC)
|
||||
y = yang_find_topnode((yang_spec*)ys, name);
|
||||
y = yang_find_topnode((yang_spec*)ys, name, 0);
|
||||
else
|
||||
y = yang_find_datanode((yang_node*)ys, name);
|
||||
if (y == NULL){
|
||||
|
|
@ -754,7 +754,7 @@ xml_diff1(yang_stmt *ys,
|
|||
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
|
||||
name = xml_name(x2);
|
||||
if (ys->ys_keyword == Y_SPEC)
|
||||
y = yang_find_topnode((yang_spec*)ys, name);
|
||||
y = yang_find_topnode((yang_spec*)ys, name, 0);
|
||||
else
|
||||
y = yang_find_datanode((yang_node*)ys, name);
|
||||
if (y == NULL){
|
||||
|
|
@ -1482,7 +1482,7 @@ xml_spec_populate(cxobj *x,
|
|||
(yp = xml_spec(xp)) != NULL)
|
||||
y = yang_find_datanode((yang_node*)yp, xml_name(x));
|
||||
else
|
||||
y = yang_find_topnode(yspec, name); /* still NULL for config */
|
||||
y = yang_find_topnode(yspec, name, 0); /* still NULL for config */
|
||||
#ifdef XXX_OBSOLETE /* Add validate elsewhere */
|
||||
if (y==NULL){
|
||||
clicon_err(OE_XML, EBADF, "yang spec not found for xml node '%s' xml parent name: '%s' yangspec:'%s']",
|
||||
|
|
@ -1544,7 +1544,7 @@ api_path2xpath_cvv(yang_spec *yspec,
|
|||
clicon_debug(1, "[%d] cvname:%s", i, name);
|
||||
clicon_debug(1, "cv2str%d", cv2str(cv, NULL, 0));
|
||||
if (i == offset){
|
||||
if ((y = yang_find_topnode(yspec, name)) == NULL){
|
||||
if ((y = yang_find_topnode(yspec, name, 0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1635,17 +1635,19 @@ api_path2xpath(yang_spec *yspec,
|
|||
* @param[in] nvec Length of vec
|
||||
* @param[in] x0 Xpath tree so far
|
||||
* @param[in] y0 Yang spec for x0
|
||||
* @param[in] schemanode If set use schema nodes otherwise data nodes.
|
||||
* @param[out] xpathp Resulting xml tree
|
||||
* @param[out] ypathp Yang spec matching xpathp
|
||||
* @see api_path2xml
|
||||
*/
|
||||
static int
|
||||
api_path2xml_vec(char **vec,
|
||||
int nvec,
|
||||
cxobj *x0,
|
||||
yang_node *y0,
|
||||
cxobj **xpathp,
|
||||
yang_node **ypathp)
|
||||
api_path2xml_vec(char **vec,
|
||||
int nvec,
|
||||
cxobj *x0,
|
||||
yang_node *y0,
|
||||
int schemanode,
|
||||
cxobj **xpathp,
|
||||
yang_node **ypathp)
|
||||
{
|
||||
int retval = -1;
|
||||
int j;
|
||||
|
|
@ -1663,6 +1665,7 @@ api_path2xml_vec(char **vec,
|
|||
int nvalvec;
|
||||
cxobj *x = NULL;
|
||||
yang_stmt *y = NULL;
|
||||
char *local;
|
||||
|
||||
if ((name = vec[0]) == NULL){
|
||||
if (xpathp)
|
||||
|
|
@ -1678,10 +1681,21 @@ api_path2xml_vec(char **vec,
|
|||
if (percent_decode(restval_enc, &restval) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (y0->yn_keyword == Y_SPEC) /* top-node */
|
||||
y = yang_find_topnode((yang_spec*)y0, name);
|
||||
else
|
||||
y = yang_find_datanode((yang_node*)y0, name);
|
||||
/* Split into prefix and localname, ignore prefix for now */
|
||||
if ((local = index(name, ':')) != NULL){
|
||||
*local = '\0';
|
||||
local++;
|
||||
name = local;
|
||||
}
|
||||
if (y0->yn_keyword == Y_SPEC){ /* top-node */
|
||||
clicon_debug(1, "%s 1 %s", __FUNCTION__, name);
|
||||
y = yang_find_topnode((yang_spec*)y0, name, schemanode);
|
||||
}
|
||||
else {
|
||||
clicon_debug(1, "%s 2 %s", __FUNCTION__, name);
|
||||
y = schemanode?yang_find_schemanode((yang_node*)y0, name):
|
||||
yang_find_datanode((yang_node*)y0, name);
|
||||
}
|
||||
if (y == NULL){
|
||||
clicon_err(OE_YANG, errno, "No yang node found: %s", name);
|
||||
goto done;
|
||||
|
|
@ -1756,6 +1770,7 @@ api_path2xml_vec(char **vec,
|
|||
}
|
||||
if (api_path2xml_vec(vec+1, nvec-1,
|
||||
x, (yang_node*)y,
|
||||
schemanode,
|
||||
xpathp, ypathp) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
|
|
@ -1769,14 +1784,17 @@ api_path2xml_vec(char **vec,
|
|||
|
||||
/*! Create xml tree from api-path
|
||||
* @param[in] api_path API-path as defined in RFC 8040
|
||||
* @param[in] yspec Yang spec
|
||||
* @param[in] schemanode If set use schema nodes otherwise data nodes.
|
||||
* @param[out] xpathp Resulting xml tree
|
||||
* @param[out] ypathp Yang spec matching xpathp
|
||||
* @see api_path2xml_vec
|
||||
*/
|
||||
int
|
||||
api_path2xml(char *api_path,
|
||||
api_path2xml(char *api_path,
|
||||
yang_spec *yspec,
|
||||
cxobj *xpath,
|
||||
int schemanode,
|
||||
cxobj **xpathp,
|
||||
yang_node **ypathp)
|
||||
{
|
||||
|
|
@ -1800,7 +1818,7 @@ api_path2xml(char *api_path,
|
|||
}
|
||||
nvec--; /* NULL-terminated */
|
||||
if (api_path2xml_vec(vec+1, nvec,
|
||||
xpath, (yang_node*)yspec,
|
||||
xpath, (yang_node*)yspec, schemanode,
|
||||
xpathp, ypathp) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
|
|
@ -1984,7 +2002,7 @@ xml_merge(cxobj *x0,
|
|||
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL) {
|
||||
x1cname = xml_name(x1c);
|
||||
/* Get yang spec of the child */
|
||||
if ((yc = yang_find_topnode(yspec, x1cname)) == NULL){
|
||||
if ((yc = yang_find_topnode(yspec, x1cname, 0)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1999,3 +2017,55 @@ xml_merge(cxobj *x0,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Get integer value from xml node from yang enumeration
|
||||
* @param[in] node XML node in a tree
|
||||
* @param[out] val Integer value returned
|
||||
* @retval 0 OK, value parsed
|
||||
* @retval -1 Error, value not obtained or parsed, no reason given
|
||||
* @note assume yang spec set
|
||||
* @note The function only returns assigned values, but according to RFC:
|
||||
If a value is not specified, then one will be automatically assigned.
|
||||
If the "enum" substatement is the first one defined, the assigned
|
||||
value is zero (0); otherwise, the assigned value is one greater than
|
||||
the current highest enum value (i.e., the highest enum value,
|
||||
* Thanks: Matthew Smith
|
||||
*/
|
||||
int
|
||||
yang_enum_int_value(cxobj *node,
|
||||
int32_t *val)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *ys;
|
||||
yang_stmt *ytype;
|
||||
yang_stmt *yrestype; /* resolved type */
|
||||
yang_stmt *yenum;
|
||||
yang_stmt *yval;
|
||||
char *reason = NULL;
|
||||
|
||||
if (node == NULL)
|
||||
goto done;
|
||||
if ((ys = (yang_stmt *) xml_spec(node)) == NULL)
|
||||
goto done;
|
||||
if ((yspec = ys_spec(ys)) == NULL)
|
||||
goto done;
|
||||
if ((ytype = yang_find((yang_node *)ys, Y_TYPE, NULL)) == NULL)
|
||||
goto done;
|
||||
if (yang_type_resolve(ys, ytype, &yrestype,
|
||||
NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (yrestype==NULL || strcmp(yrestype->ys_argument, "enumeration"))
|
||||
goto done;
|
||||
if ((yenum = yang_find((yang_node *)yrestype, Y_ENUM, xml_body(node))) == NULL)
|
||||
goto done;
|
||||
/* Should assign value if yval not found */
|
||||
if ((yval = yang_find((yang_node *)yenum, Y_VALUE, NULL)) == NULL)
|
||||
goto done;
|
||||
/* reason is string containing why int could not be parsed */
|
||||
if (parse_int32(yval->ys_argument, val, &reason) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
* Look at the end of the file for a test unit program
|
||||
*/
|
||||
/*
|
||||
https://www.w3.org/TR/xpath/
|
||||
See https://www.w3.org/TR/xpath/
|
||||
|
||||
Implementation of a limited xslt xpath syntax. Some examples. Given the following
|
||||
xml tree:
|
||||
|
|
@ -172,6 +172,9 @@ xpath_print(FILE *f, struct xpath_element *xplist)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Extract PredicateExpr (Expr) from a Predicate within []
|
||||
* @see xpath_expr For evaluation of predicate
|
||||
*/
|
||||
static int
|
||||
xpath_parse_predicate(struct xpath_element *xe,
|
||||
char *pred)
|
||||
|
|
@ -439,7 +442,13 @@ recursive_find(cxobj *xn,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* forward */
|
||||
static int
|
||||
xpath_exec(cxobj *xcur, char *xpath, cxobj **vec0, size_t vec0len,
|
||||
uint16_t flags, cxobj ***vec2, size_t *vec2len);
|
||||
|
||||
/*! XPath predicate expression check
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] predicate_expression xpath expression as a string
|
||||
* @param[in] flags Extra xml flag checks that must match (apart from predicate)
|
||||
* @param[in,out] vec0 Vector or xml nodes that are checked. Not matched are filtered
|
||||
|
|
@ -454,7 +463,8 @@ recursive_find(cxobj *xn,
|
|||
* @see https://www.w3.org/TR/xpath/#predicates
|
||||
*/
|
||||
static int
|
||||
xpath_expr(char *predicate_expression,
|
||||
xpath_expr(cxobj *xcur,
|
||||
char *predicate_expression,
|
||||
uint16_t flags,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
|
|
@ -462,6 +472,7 @@ xpath_expr(char *predicate_expression,
|
|||
char *e_a;
|
||||
char *e_v;
|
||||
int i;
|
||||
int j;
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
cxobj *xv;
|
||||
|
|
@ -520,25 +531,72 @@ xpath_expr(char *predicate_expression,
|
|||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
else{ /* name = expr */
|
||||
if ((tag = strsep(&e, "=")) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: malformed expression: [%s]",
|
||||
__FUNCTION__, e);
|
||||
goto done;
|
||||
}
|
||||
for (i=0; i<*vec0len; i++){
|
||||
xv = (*vec0)[i];
|
||||
/* Check if more may match,... */
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(tag, xml_name(x)) != 0)
|
||||
continue;
|
||||
if ((val = xml_body(x)) != NULL &&
|
||||
strcmp(val, e) == 0){
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
|
||||
if (flags==0x0 || xml_flag(xv, flags))
|
||||
if (cxvec_append(xv, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
/* Strip trailing spaces */
|
||||
while (tag[strlen(tag)-1] == ' ')
|
||||
tag[strlen(tag)-1] = '\0';
|
||||
/* Strip heading spaces */
|
||||
while (e[0]==' ')
|
||||
e++;
|
||||
if (strncmp(e, "current()", strlen("current()")) == 0){
|
||||
/* name = current()xpath */
|
||||
cxobj **svec0 = NULL;
|
||||
size_t svec0len = 0;
|
||||
cxobj **svec1 = NULL;
|
||||
size_t svec1len = 0;
|
||||
char *ebody;
|
||||
|
||||
e += strlen("current("); /* e is path expression */
|
||||
*e = '.';
|
||||
if ((svec0 = calloc(1, sizeof(cxobj *))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
svec0[0] = xcur;
|
||||
svec0len++;
|
||||
/* Recursive invocation */
|
||||
if (xpath_exec(xcur, e, svec0, svec0len,
|
||||
flags, &svec1, &svec1len) < 0)
|
||||
goto done;
|
||||
for (j=0; j<svec1len; j++){
|
||||
ebody = xml_body(svec1[j]);
|
||||
for (i=0; i<*vec0len; i++){
|
||||
xv = (*vec0)[i];
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(tag, xml_name(x)) != 0)
|
||||
continue;
|
||||
if ((val = xml_body(x)) != NULL &&
|
||||
strcmp(val, ebody) == 0){
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
|
||||
if (flags==0x0 || xml_flag(xv, flags))
|
||||
if (cxvec_append(xv, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else { /* name = value */
|
||||
for (i=0; i<*vec0len; i++){
|
||||
xv = (*vec0)[i];
|
||||
/* Check if more may match,... */
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(tag, xml_name(x)) != 0)
|
||||
continue;
|
||||
if ((val = xml_body(x)) != NULL &&
|
||||
strcmp(val, e) == 0){
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
|
||||
if (flags==0x0 || xml_flag(xv, flags))
|
||||
if (cxvec_append(xv, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -556,6 +614,7 @@ xpath_expr(char *predicate_expression,
|
|||
}
|
||||
|
||||
/*! Given vec0, add matches to vec1
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xe XPATH in structured (parsed) form
|
||||
* @param[in] descendants0
|
||||
* @param[in] vec0 vector of XML trees
|
||||
|
|
@ -565,7 +624,8 @@ xpath_expr(char *predicate_expression,
|
|||
* @param[out] vec2len Length of result vector.
|
||||
*/
|
||||
static int
|
||||
xpath_find(struct xpath_element *xe,
|
||||
xpath_find(cxobj *xcur,
|
||||
struct xpath_element *xe,
|
||||
int descendants0,
|
||||
cxobj **vec0,
|
||||
size_t vec0len,
|
||||
|
|
@ -669,10 +729,10 @@ xpath_find(struct xpath_element *xe,
|
|||
}
|
||||
|
||||
for (xp = xe->xe_predicate; xp; xp = xp->xp_next){
|
||||
if (xpath_expr(xp->xp_expr, flags, &vec0, &vec0len) < 0)
|
||||
if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (xpath_find(xe->xe_next, descendants,
|
||||
if (xpath_find(xcur, xe->xe_next, descendants,
|
||||
vec0, vec0len, flags,
|
||||
vec2, vec2len) < 0)
|
||||
goto done;
|
||||
|
|
@ -719,6 +779,7 @@ xpath_split(char *xpathstr,
|
|||
}
|
||||
|
||||
/*! Process single xpath expression on xml tree
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[in] vec0 vector of XML trees
|
||||
* @param[in] vec0len length of XML trees
|
||||
|
|
@ -727,7 +788,8 @@ xpath_split(char *xpathstr,
|
|||
* @param[out] vec2len Length of result vector.
|
||||
*/
|
||||
static int
|
||||
xpath_exec(char *xpath,
|
||||
xpath_exec(cxobj *xcur,
|
||||
char *xpath,
|
||||
cxobj **vec0,
|
||||
size_t vec0len,
|
||||
uint16_t flags,
|
||||
|
|
@ -744,7 +806,7 @@ xpath_exec(char *xpath,
|
|||
goto done;
|
||||
if (debug > 1)
|
||||
xpath_print(stderr, xplist);
|
||||
if (xpath_find(xplist, 0, vec1, vec1len, flags, vec2, vec2len) < 0)
|
||||
if (xpath_find(xcur, xplist, 0, vec1, vec1len, flags, vec2, vec2len) < 0)
|
||||
goto done;
|
||||
if (xpath_free(xplist) < 0)
|
||||
goto done;
|
||||
|
|
@ -754,6 +816,11 @@ xpath_exec(char *xpath,
|
|||
|
||||
|
||||
/*! Intermediate xpath function to handle 'conditional' cases.
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[in] flags if != 0, only match xml nodes matching flags
|
||||
* @param[in] vec1 vector of XML trees
|
||||
* @param[in] vec1len length of XML trees
|
||||
* For example: xpath = //a | //b.
|
||||
* xpath_first+ splits xpath up in several subcalls
|
||||
* (eg xpath=//a and xpath=//b) and collects the results.
|
||||
|
|
@ -762,7 +829,7 @@ xpath_exec(char *xpath,
|
|||
* Note, this could be 'folded' into xpath1 but I judged it too complex.
|
||||
*/
|
||||
static int
|
||||
xpath_choice(cxobj *xtop,
|
||||
xpath_choice(cxobj *xcur,
|
||||
char *xpath0,
|
||||
uint16_t flags,
|
||||
cxobj ***vec1,
|
||||
|
|
@ -776,7 +843,6 @@ xpath_choice(cxobj *xtop,
|
|||
cxobj **vec0 = NULL;
|
||||
size_t vec0len = 0;
|
||||
|
||||
|
||||
if ((s0 = strdup(xpath0)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
||||
goto done;
|
||||
|
|
@ -786,7 +852,7 @@ xpath_choice(cxobj *xtop,
|
|||
clicon_err(OE_UNIX, errno, "calloc");
|
||||
goto done;
|
||||
}
|
||||
vec0[0] = xtop;
|
||||
vec0[0] = xcur;
|
||||
vec0len++;
|
||||
while (s1 != NULL){
|
||||
s2 = strstr(s1, " | ");
|
||||
|
|
@ -796,7 +862,7 @@ xpath_choice(cxobj *xtop,
|
|||
}
|
||||
xpath = s1;
|
||||
s1 = s2;
|
||||
if (xpath_exec(xpath, vec0, vec0len, flags, vec1, vec1len) < 0)
|
||||
if (xpath_exec(xcur, xpath, vec0, vec0len, flags, vec1, vec1len) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -808,32 +874,35 @@ xpath_choice(cxobj *xtop,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Help function to xpath_first
|
||||
*/
|
||||
static cxobj *
|
||||
xpath_first0(cxobj *cxtop,
|
||||
char *xpath)
|
||||
xpath_first0(cxobj *xcur,
|
||||
char *xpath)
|
||||
{
|
||||
cxobj **vec0 = NULL;
|
||||
size_t vec0len = 0;
|
||||
cxobj **vec1 = NULL;
|
||||
size_t vec1len = 0;
|
||||
cxobj *xn = NULL;
|
||||
|
||||
if (xpath_choice(cxtop, xpath, 0, &vec0, &vec0len) < 0)
|
||||
if (xpath_choice(xcur, xpath, 0, &vec1, &vec1len) < 0)
|
||||
goto done;
|
||||
if (vec0len)
|
||||
xn = vec0[0];
|
||||
if (vec1len)
|
||||
xn = vec1[0];
|
||||
else
|
||||
xn = NULL;
|
||||
done:
|
||||
if (vec0)
|
||||
free(vec0);
|
||||
if (vec1)
|
||||
free(vec1);
|
||||
return xn;
|
||||
}
|
||||
|
||||
/*! A restricted xpath function where the first matching entry is returned
|
||||
* See xpath1() on details for subset.
|
||||
* args:
|
||||
* @param[in] cxtop xml-tree where to search
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @retval xml-tree of first match, or NULL on error.
|
||||
* @retval xml-tree of first match
|
||||
* @retval NULL Error or not found
|
||||
*
|
||||
* @code
|
||||
* cxobj *x;
|
||||
|
|
@ -846,7 +915,7 @@ xpath_first0(cxobj *cxtop,
|
|||
* @see also xpath_vec.
|
||||
*/
|
||||
cxobj *
|
||||
xpath_first(cxobj *cxtop,
|
||||
xpath_first(cxobj *xcur,
|
||||
char *format,
|
||||
...)
|
||||
{
|
||||
|
|
@ -871,7 +940,7 @@ xpath_first(cxobj *cxtop,
|
|||
goto done;
|
||||
}
|
||||
va_end(ap);
|
||||
retval = xpath_first0(cxtop, xpath);
|
||||
retval = xpath_first0(xcur, xpath);
|
||||
done:
|
||||
if (xpath)
|
||||
free(xpath);
|
||||
|
|
@ -881,14 +950,14 @@ xpath_first(cxobj *cxtop,
|
|||
/*! A restricted xpath iterator that loops over all matching entries. Dont use.
|
||||
*
|
||||
* See xpath1() on details for subset.
|
||||
* @param[in] cxtop xml-tree where to search
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[in] xprev iterator/result should be initiated to NULL
|
||||
* @retval xml-tree of n:th match, or NULL on error.
|
||||
*
|
||||
* @code
|
||||
* cxobj *x = NULL;
|
||||
* while ((x = xpath_each(cxtop, "//symbol/foo", x)) != NULL) {
|
||||
* while ((x = xpath_each(xcur, "//symbol/foo", x)) != NULL) {
|
||||
* ...
|
||||
* }
|
||||
* @endcode
|
||||
|
|
@ -899,33 +968,33 @@ xpath_first(cxobj *cxtop,
|
|||
* NOTE: uses a static variable: consider replacing with xpath_vec() instead
|
||||
*/
|
||||
cxobj *
|
||||
xpath_each(cxobj *cxtop,
|
||||
xpath_each(cxobj *xcur,
|
||||
char *xpath,
|
||||
cxobj *xprev)
|
||||
{
|
||||
static cxobj **vec0 = NULL; /* XXX */
|
||||
static size_t vec0len = 0;
|
||||
static cxobj **vec1 = NULL; /* XXX */
|
||||
static size_t vec1len = 0;
|
||||
cxobj *xn = NULL;
|
||||
int i;
|
||||
|
||||
if (xprev == NULL){
|
||||
if (vec0) // XXX
|
||||
free(vec0); // XXX
|
||||
vec0len = 0;
|
||||
if (xpath_choice(cxtop, xpath, 0, &vec0, &vec0len) < 0)
|
||||
if (vec1) // XXX
|
||||
free(vec1); // XXX
|
||||
vec1len = 0;
|
||||
if (xpath_choice(xcur, xpath, 0, &vec1, &vec1len) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (vec0len){
|
||||
if (vec1len){
|
||||
if (xprev==NULL)
|
||||
xn = vec0[0];
|
||||
xn = vec1[0];
|
||||
else{
|
||||
for (i=0; i<vec0len; i++)
|
||||
if (vec0[i] == xprev)
|
||||
for (i=0; i<vec1len; i++)
|
||||
if (vec1[i] == xprev)
|
||||
break;
|
||||
if (i>=vec0len-1)
|
||||
if (i>=vec1len-1)
|
||||
xn = NULL;
|
||||
else
|
||||
xn = vec0[i+1];
|
||||
xn = vec1[i+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -937,7 +1006,7 @@ xpath_each(cxobj *cxtop,
|
|||
/*! A restricted xpath that returns a vector of matches
|
||||
*
|
||||
* See xpath1() on details for subset
|
||||
. * @param[in] cxtop xml-tree where to search
|
||||
. * @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] veclen returns length of vector in return value
|
||||
|
|
@ -947,7 +1016,7 @@ xpath_each(cxobj *cxtop,
|
|||
* @code
|
||||
* cxobj **xvec;
|
||||
* size_t xlen;
|
||||
* if (xpath_vec(cxtop, "//symbol/foo", &xvec, &xlen) < 0)
|
||||
* if (xpath_vec(xcur, "//symbol/foo", &xvec, &xlen) < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xvec[i];
|
||||
|
|
@ -960,7 +1029,7 @@ xpath_each(cxobj *cxtop,
|
|||
* @see also xpath_first, xpath_each.
|
||||
*/
|
||||
int
|
||||
xpath_vec(cxobj *cxtop,
|
||||
xpath_vec(cxobj *xcur,
|
||||
char *format,
|
||||
cxobj ***vec,
|
||||
size_t *veclen,
|
||||
|
|
@ -989,7 +1058,7 @@ xpath_vec(cxobj *cxtop,
|
|||
va_end(ap);
|
||||
*vec = NULL;
|
||||
*veclen = 0;
|
||||
retval = xpath_choice(cxtop, xpath, 0x0, vec, veclen);
|
||||
retval = xpath_choice(xcur, xpath, 0x0, vec, veclen);
|
||||
done:
|
||||
if (xpath)
|
||||
free(xpath);
|
||||
|
|
@ -997,7 +1066,7 @@ xpath_vec(cxobj *cxtop,
|
|||
}
|
||||
|
||||
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
|
||||
* @param[in] cxtop xml-tree where to search
|
||||
* @param[in] xcur xml-tree where to search
|
||||
* @param[in] xpath string with XPATH syntax
|
||||
* @param[in] flags Set of flags that return nodes must match (0 if all)
|
||||
* @param[out] vec vector of xml-trees. Vector must be free():d after use
|
||||
|
|
@ -1007,7 +1076,7 @@ xpath_vec(cxobj *cxtop,
|
|||
* @code
|
||||
* cxobj **vec;
|
||||
* size_t veclen;
|
||||
* if (xpath_vec_flag(cxtop, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
|
||||
* if (xpath_vec_flag(xcur, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<veclen; i++){
|
||||
* xn = vec[i];
|
||||
|
|
@ -1020,7 +1089,7 @@ xpath_vec(cxobj *cxtop,
|
|||
* @see also xpath_vec This is a specialized version.
|
||||
*/
|
||||
int
|
||||
xpath_vec_flag(cxobj *cxtop,
|
||||
xpath_vec_flag(cxobj *xcur,
|
||||
char *format,
|
||||
uint16_t flags,
|
||||
cxobj ***vec,
|
||||
|
|
@ -1050,7 +1119,7 @@ xpath_vec_flag(cxobj *cxtop,
|
|||
va_end(ap);
|
||||
*vec=NULL;
|
||||
*veclen = 0;
|
||||
retval = xpath_choice(cxtop, xpath, flags, vec, veclen);
|
||||
retval = xpath_choice(xcur, xpath, flags, vec, veclen);
|
||||
done:
|
||||
if (xpath)
|
||||
free(xpath);
|
||||
|
|
|
|||
|
|
@ -453,18 +453,68 @@ yang_find_datanode(yang_node *yn,
|
|||
return ysmatch;
|
||||
}
|
||||
|
||||
/*! Find 'top-node', eg first data node in all (sub)modules in a yang spec
|
||||
/*! Find child schema node with matching argument (container, leaf, etc)
|
||||
* @note XXX unify code with yang_find_datanode?
|
||||
* @see yang_find_datanode
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_schemanode(yang_node *yn,
|
||||
char *argument)
|
||||
{
|
||||
yang_stmt *ys = NULL;
|
||||
yang_stmt *yc = NULL;
|
||||
yang_stmt *ysmatch = NULL;
|
||||
int i, j;
|
||||
|
||||
for (i=0; i<yn->yn_len; i++){
|
||||
ys = yn->yn_stmt[i];
|
||||
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_schemanode((yang_node*)yc, argument);
|
||||
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 (yang_schemanode(ys)){
|
||||
if (argument == NULL)
|
||||
ysmatch = ys;
|
||||
else
|
||||
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
|
||||
ysmatch = ys;
|
||||
if (ysmatch)
|
||||
goto match;
|
||||
}
|
||||
}
|
||||
match:
|
||||
return ysmatch;
|
||||
}
|
||||
|
||||
|
||||
/*! Find first matching data node in all (sub)modules in a yang spec
|
||||
*
|
||||
* @param[in] ysp Yang specification
|
||||
* @param[in] name if NULL, match any(first) argument. XXX is that really a case?
|
||||
* @param[in] name 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
|
||||
* A yang specification has modules as children which in turn can have
|
||||
* syntax-nodes as children. This function goes through all the modules to
|
||||
* look for syntax-nodes. Note that if a child to a module is a choice,
|
||||
* look for nodes. Note that if a child to a module is a choice,
|
||||
* the search is made recursively made to the choice's children.
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_topnode(yang_spec *ysp,
|
||||
char *name)
|
||||
char *name,
|
||||
int schemanode)
|
||||
{
|
||||
yang_stmt *ys = NULL;
|
||||
yang_stmt *yc = NULL;
|
||||
|
|
@ -472,47 +522,43 @@ yang_find_topnode(yang_spec *ysp,
|
|||
|
||||
for (i=0; i<ysp->yp_len; i++){
|
||||
ys = ysp->yp_stmt[i];
|
||||
if ((yc = yang_find_datanode((yang_node*)ys, name)) != NULL)
|
||||
return yc;
|
||||
if (schemanode){
|
||||
if ((yc = yang_find_schemanode((yang_node*)ys, name)) != NULL)
|
||||
return yc;
|
||||
}
|
||||
else
|
||||
if ((yc = yang_find_datanode((yang_node*)ys, name)) != NULL)
|
||||
return yc;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Find a child spec-node yang_stmt with matching argument for schema-nodid
|
||||
*
|
||||
* See also yang_find() but this looks only for the yang specification nodes with
|
||||
* the following keyword: container, leaf, list, leaf-list
|
||||
* That is, basic syntax nodes.
|
||||
* @see yang_find_datanode
|
||||
* @see clicon_dbget_xpath
|
||||
/*! Given a yang statement, find the prefix associated to this module
|
||||
* @param[in] ys Yang statement
|
||||
* @retval NULL Not found
|
||||
* @retval prefix Prefix as char* pointer into yang tree
|
||||
*/
|
||||
static yang_stmt *
|
||||
schema_nodeid_stmt(yang_node *yn,
|
||||
char *argument)
|
||||
char *
|
||||
yang_find_myprefix(yang_stmt *ys)
|
||||
{
|
||||
yang_stmt *ys = NULL;
|
||||
int i;
|
||||
int match = 0;
|
||||
yang_stmt *ymod; /* My module */
|
||||
yang_stmt *yprefix;
|
||||
char *prefix = NULL;
|
||||
|
||||
for (i=0; i<yn->yn_len; i++){
|
||||
ys = yn->yn_stmt[i];
|
||||
if (!yang_schemanode(ys))
|
||||
continue;
|
||||
/* some keys dont have arguments, match on key */
|
||||
if (ys->ys_keyword == Y_INPUT || ys->ys_keyword == Y_OUTPUT){
|
||||
if (strcmp(argument, yang_key2str(ys->ys_keyword)) == 0){
|
||||
match++;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0){
|
||||
match++;
|
||||
break;
|
||||
}
|
||||
if ((ymod = ys_module(ys)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||
goto done;
|
||||
}
|
||||
return match ? ys : NULL;
|
||||
if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "No prefix in my module");
|
||||
goto done;
|
||||
}
|
||||
prefix = yprefix->ys_argument;
|
||||
done:
|
||||
return prefix;
|
||||
}
|
||||
|
||||
|
||||
/*! Reset flag in complete tree, arg contains flag */
|
||||
static int
|
||||
ys_flag_reset(yang_stmt *ys,
|
||||
|
|
@ -540,8 +586,13 @@ ys_module(yang_stmt *ys)
|
|||
{
|
||||
yang_node *yn;
|
||||
|
||||
#if 1
|
||||
if (ys==NULL || ys->ys_keyword==Y_SPEC)
|
||||
return NULL;
|
||||
#else
|
||||
if (ys==NULL || ys->ys_keyword==Y_SPEC)
|
||||
return ys;
|
||||
#endif
|
||||
while (ys != NULL && ys->ys_keyword != Y_MODULE && ys->ys_keyword != Y_SUBMODULE){
|
||||
yn = ys->ys_parent;
|
||||
/* Some extra stuff to ensure ys is a stmt */
|
||||
|
|
@ -607,6 +658,7 @@ ytype_prefix(yang_stmt *ys)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*! Given a yang statement and a prefix, return yang module to that prefix
|
||||
* Note, not the other module but the proxy import statement only
|
||||
* @param[in] ys A yang statement
|
||||
|
|
@ -623,22 +675,25 @@ yang_find_module_by_prefix(yang_stmt *ys,
|
|||
yang_stmt *my_ymod;
|
||||
yang_stmt *ymod = NULL;
|
||||
yang_spec *yspec;
|
||||
char *myprefix;
|
||||
|
||||
if ((yspec = ys_spec(ys)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "My yang spec not found");
|
||||
goto done;
|
||||
}
|
||||
myprefix = yang_find_myprefix(ys);
|
||||
if ((my_ymod = ys_module(ys)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||
goto done;
|
||||
}
|
||||
if ((yspec = ys_spec(my_ymod)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "My yang spec not found");
|
||||
goto done;
|
||||
}
|
||||
#if 0
|
||||
if (my_ymod->ys_keyword != Y_MODULE &&
|
||||
my_ymod->ys_keyword != Y_SUBMODULE){
|
||||
clicon_err(OE_YANG, 0, "%s not module or sub-module", my_ymod->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
if ((yprefix = yang_find((yang_node*)my_ymod, Y_PREFIX, NULL)) != NULL &&
|
||||
strcmp(yprefix->ys_argument, prefix) == 0){
|
||||
#endif
|
||||
if (strcmp(myprefix, prefix) == 0){
|
||||
ymod = my_ymod;
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -1074,7 +1129,7 @@ ys_grouping_resolve(yang_stmt *ys,
|
|||
The target node MUST be either a container, list, choice, case, input,
|
||||
output, or notification node.
|
||||
If the "augment" statement is on the top level the absolute form MUST be used.
|
||||
XXX: Destructively changing a datamodel may affect outlying loop?
|
||||
@note Destructively changing a datamodel may affect outlying loop?
|
||||
*/
|
||||
static int
|
||||
yang_augment_node(yang_stmt *ys,
|
||||
|
|
@ -1082,7 +1137,7 @@ yang_augment_node(yang_stmt *ys,
|
|||
{
|
||||
int retval = -1;
|
||||
char *schema_nodeid;
|
||||
yang_node *yn;
|
||||
yang_stmt *yss = NULL;
|
||||
yang_stmt *yc;
|
||||
int i;
|
||||
|
||||
|
|
@ -1090,21 +1145,21 @@ yang_augment_node(yang_stmt *ys,
|
|||
clicon_debug(1, "%s %s", __FUNCTION__, schema_nodeid);
|
||||
|
||||
/* Find the target */
|
||||
if ((yn = yang_abs_schema_nodeid((yang_node*)ys, schema_nodeid)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Augment schema_nodeid %s not found", schema_nodeid);
|
||||
// retval = 0; /* Ignore, continue */
|
||||
if (yang_abs_schema_nodeid(ysp, schema_nodeid, &yss) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Extend yn with ys' children
|
||||
* First enlarge yn vector
|
||||
if (yss == NULL)
|
||||
goto ok;
|
||||
/* Extend yss with ys' children
|
||||
* First enlarge yss vector
|
||||
*/
|
||||
for (i=0; i<ys->ys_len; i++){
|
||||
if ((yc = ys_dup(ys->ys_stmt[i])) == NULL)
|
||||
goto done;
|
||||
/* XXX: use prefix of origin */
|
||||
if (yn_insert(yn, yc) < 0)
|
||||
if (yn_insert((yang_node*)yss, yc) < 0)
|
||||
goto done;
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -1126,10 +1181,14 @@ yang_augment_spec(yang_spec *ysp)
|
|||
j = 0;
|
||||
while (j<ym->ys_len){ /* Top-level symbols in modules */
|
||||
ys = ym->ys_stmt[j++];
|
||||
if (ys->ys_keyword != Y_AUGMENT)
|
||||
continue;
|
||||
if (yang_augment_node(ys, ysp) < 0)
|
||||
goto done;
|
||||
switch (ys->ys_keyword){
|
||||
case Y_AUGMENT: /* top-level */
|
||||
if (yang_augment_node(ys, ysp) < 0)
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -1498,6 +1557,49 @@ yang_parse_recurse(clicon_handle h,
|
|||
return ymod; /* top-level (sub)module */
|
||||
}
|
||||
|
||||
int
|
||||
ys_schemanode_check(yang_stmt *ys,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec;
|
||||
yang_stmt *yres;
|
||||
yang_node *yp;
|
||||
|
||||
yp = ys->ys_parent;
|
||||
switch (ys->ys_keyword){
|
||||
case Y_AUGMENT:
|
||||
if (yp->yn_keyword == Y_MODULE) /* Not top-level */
|
||||
break;
|
||||
/* fallthru */
|
||||
case Y_REFINE:
|
||||
case Y_UNIQUE:
|
||||
if (yang_desc_schema_nodeid(yp, ys->ys_argument, &yres) < 0)
|
||||
goto done;
|
||||
if (yres == NULL){
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %d %s",
|
||||
ys->ys_keyword,
|
||||
ys->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case Y_DEVIATION:
|
||||
yspec = ys_spec(ys);
|
||||
if (yang_abs_schema_nodeid(yspec, ys->ys_argument, &yres) < 0)
|
||||
goto done;
|
||||
if (yres == NULL){
|
||||
clicon_err(OE_YANG, 0, "schemanode sanity check of %s", ys->ys_argument);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
|
||||
*
|
||||
* @param[in] h CLICON handle
|
||||
|
|
@ -1552,12 +1654,14 @@ yang_parse(clicon_handle h,
|
|||
goto done;
|
||||
yang_apply((yang_node*)ymod, -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
|
||||
|
||||
|
||||
|
||||
/* Step 4: Top-level augmentation of all modules */
|
||||
if (yang_augment_spec(ysp) < 0)
|
||||
goto done;
|
||||
|
||||
/* sanity check of schemanode references, need more here */
|
||||
if (yang_apply((yang_node*)ysp, -1, ys_schemanode_check, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -1621,21 +1725,26 @@ yang_apply(yang_node *yn,
|
|||
|
||||
/*! All the work for schema_nodeid functions both absolute and descendant
|
||||
* Ignore prefixes, see _abs
|
||||
* @param[in] yn Yang node
|
||||
* @param[in] yn Yang node. Find next yang stmt and return that if match.
|
||||
* @param[in] vec Vector of nodeid's in a schema node identifier, eg a/b
|
||||
* @param[in] nvec Length of vec
|
||||
* @retval NULL Error, with clicon_err called
|
||||
* @retval yres First yang node matching schema nodeid
|
||||
* @param[out] yres Result yang statement node, or NULL if not found
|
||||
* @retval -1 Error, with clicon_err called
|
||||
* @retval 0 OK
|
||||
*/
|
||||
static yang_node *
|
||||
schema_nodeid_vec(yang_node *yn,
|
||||
char **vec,
|
||||
int nvec)
|
||||
static int
|
||||
schema_nodeid_vec(yang_node *yn,
|
||||
char **vec,
|
||||
int nvec,
|
||||
yang_stmt **yres)
|
||||
{
|
||||
int retval = -1;
|
||||
char *arg;
|
||||
yang_node *ynext;
|
||||
char *nodeid;
|
||||
int i;
|
||||
yang_stmt *ys;
|
||||
yang_node *yres = NULL;
|
||||
char *id;
|
||||
int match;
|
||||
|
||||
if (nvec <= 0)
|
||||
goto done;
|
||||
|
|
@ -1644,88 +1753,129 @@ schema_nodeid_vec(yang_node *yn,
|
|||
__FUNCTION__, yang_key2str(yn->yn_keyword), yn->yn_argument,
|
||||
arg, yn->yn_len);
|
||||
if (strcmp(arg, "..") == 0)
|
||||
ys = (yang_stmt*)yn->yn_parent;
|
||||
ynext = yn->yn_parent; /* This could actually be a MODULE */
|
||||
else{
|
||||
/* ignore prefixes */
|
||||
if ((id = strchr(arg, ':')) == NULL)
|
||||
id = arg;
|
||||
if ((nodeid = strchr(arg, ':')) == NULL)
|
||||
nodeid = arg;
|
||||
else
|
||||
id++;
|
||||
if ((ys = schema_nodeid_stmt(yn, id)) == NULL){
|
||||
clicon_debug(1, "%s %s not found", __FUNCTION__, id);
|
||||
goto done;
|
||||
nodeid++;
|
||||
match = 0;
|
||||
ys = NULL;
|
||||
for (i=0; i<yn->yn_len; i++){
|
||||
ys = yn->yn_stmt[i];
|
||||
if (!yang_schemanode(ys))
|
||||
continue;
|
||||
/* some keys dont have arguments, match on key */
|
||||
if (ys->ys_keyword == Y_INPUT || ys->ys_keyword == Y_OUTPUT){
|
||||
if (strcmp(nodeid, yang_key2str(ys->ys_keyword)) == 0){
|
||||
match++;
|
||||
break;
|
||||
}
|
||||
} else
|
||||
if (ys->ys_argument && strcmp(nodeid, ys->ys_argument) == 0){
|
||||
match++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match){
|
||||
clicon_debug(1, "%s not found", nodeid);
|
||||
goto ok;
|
||||
}
|
||||
ynext = (yang_node*)ys;
|
||||
}
|
||||
if (nvec == 1){
|
||||
yres = (yang_node*)ys;
|
||||
if (nvec == 1){ /* match */
|
||||
if (yang_schemanode((yang_stmt*)ynext))
|
||||
*yres = (yang_stmt*)ynext;
|
||||
else
|
||||
clicon_debug(1, "%s not schema node", nodeid);
|
||||
goto ok;
|
||||
}
|
||||
/* recursive call using ynext */
|
||||
if (schema_nodeid_vec(ynext, vec+1, nvec-1, yres) < 0)
|
||||
goto done;
|
||||
}
|
||||
yres = schema_nodeid_vec((yang_node*)ys, vec+1, nvec-1);
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return yres;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Given an absolute schema-nodeid (eg /a/b/c) find matching yang spec
|
||||
* @param[in] yn Yang node
|
||||
* @param[in] yspec Yang specification.
|
||||
* @param[in] schema_nodeid Absolute schema-node-id, ie /a/b
|
||||
* @retval NULL Error, with clicon_err called
|
||||
* @retval yres First yang node matching schema nodeid
|
||||
* @see yang_schema_nodeid
|
||||
* Assume schema nodeid:s have prefixes, (actually the first).
|
||||
* @see yang_desc_schema_nodeid
|
||||
* Used in yang: deviation, top-level augment
|
||||
*/
|
||||
yang_node *
|
||||
yang_abs_schema_nodeid(yang_node *yn,
|
||||
char *schema_nodeid)
|
||||
int
|
||||
yang_abs_schema_nodeid(yang_spec *yspec,
|
||||
char *schema_nodeid,
|
||||
yang_stmt **yres)
|
||||
{
|
||||
int retval = -1;
|
||||
char **vec = NULL;
|
||||
int nvec;
|
||||
yang_node *yres = NULL;
|
||||
yang_stmt *ymod;
|
||||
char *id;
|
||||
char *prefix = NULL;
|
||||
|
||||
yang_stmt *yprefix;
|
||||
|
||||
/* check absolute schema_nodeid */
|
||||
if (schema_nodeid[0] != '/'){
|
||||
clicon_err(OE_YANG, EINVAL, "absolute schema nodeid should start with /");
|
||||
goto done;
|
||||
}
|
||||
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
/* Assume schame nodeid looks like: "/prefix:id[/prefix:id]*" */
|
||||
/* Assume schema nodeid looks like: "/prefix:id[/prefix:id]*" */
|
||||
if (nvec < 2){
|
||||
clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s",
|
||||
__FUNCTION__, schema_nodeid);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check for absolute path */
|
||||
if (strcmp(vec[0], "") != 0){
|
||||
clicon_err(OE_YANG, errno, "%s: %s: expected absolute path",
|
||||
__FUNCTION__, vec[0]);
|
||||
goto done;
|
||||
}
|
||||
/* Find correct module */
|
||||
if ((ymod = ys_module((yang_stmt*)yn)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Could not find top-level");
|
||||
goto done;
|
||||
}
|
||||
/* split <prefix>:<id> */
|
||||
if ((id = strchr(vec[1], ':')) == NULL){
|
||||
id = vec[1];
|
||||
if ((id = strchr(vec[1], ':')) == NULL){ /* no prefix */
|
||||
clicon_err(OE_YANG, 0, "Absolute schema nodeid must have prefix");
|
||||
goto done;
|
||||
}
|
||||
else{ /* other module - peek into first element to find module */
|
||||
if ((prefix = strdup(vec[1])) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__);
|
||||
if ((prefix = strdup(vec[1])) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__);
|
||||
goto done;
|
||||
}
|
||||
prefix[id-vec[1]] = '\0';
|
||||
id++;
|
||||
ymod = NULL;
|
||||
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
|
||||
if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL &&
|
||||
strcmp(yprefix->ys_argument, prefix) == 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ymod == NULL){ /* Try with topnode */
|
||||
yang_stmt *ys;
|
||||
if ((ys = yang_find_topnode(yspec, id, 1)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found", prefix,id);
|
||||
goto done;
|
||||
}
|
||||
prefix[id-vec[1]] = '\0';
|
||||
id++;
|
||||
if ((ymod = yang_find_module_by_prefix((yang_stmt*)yn, prefix)) == NULL)
|
||||
if ((ymod = ys_module(ys)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "Module with id:%s:%s not found2", prefix,id);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
yres = schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1);
|
||||
if (schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1, yres) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (vec)
|
||||
free(vec);
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
return yres;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Given a descendant schema-nodeid (eg a/b/c) find matching yang spec
|
||||
|
|
@ -1734,12 +1884,14 @@ yang_abs_schema_nodeid(yang_node *yn,
|
|||
* @retval NULL Error, with clicon_err called
|
||||
* @retval yres First yang node matching schema nodeid
|
||||
* @see yang_schema_nodeid
|
||||
* Used in yang: unique, refine, uses augment
|
||||
*/
|
||||
yang_node *
|
||||
yang_desc_schema_nodeid(yang_node *yn,
|
||||
char *schema_nodeid)
|
||||
int
|
||||
yang_desc_schema_nodeid(yang_node *yn,
|
||||
char *schema_nodeid,
|
||||
yang_stmt **yres)
|
||||
{
|
||||
yang_node *yres = NULL;
|
||||
int retval = -1;
|
||||
char **vec = NULL;
|
||||
int nvec;
|
||||
|
||||
|
|
@ -1747,47 +1899,18 @@ yang_desc_schema_nodeid(yang_node *yn,
|
|||
goto done;
|
||||
/* check absolute schema_nodeid */
|
||||
if (schema_nodeid[0] == '/'){
|
||||
clicon_err(OE_YANG, EINVAL, "descenadant schema nodeid should not start with /");
|
||||
clicon_err(OE_YANG, EINVAL, "descendant schema nodeid should not start with /");
|
||||
goto done;
|
||||
}
|
||||
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL)
|
||||
goto done;
|
||||
yres = schema_nodeid_vec((yang_node*)yn, vec, nvec);
|
||||
if (schema_nodeid_vec(yn, vec, nvec, yres) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (vec)
|
||||
free(vec);
|
||||
return yres;
|
||||
}
|
||||
|
||||
/*! Given a schema-node-id (eg /a/b/c or a/b/c) find matching yang specification
|
||||
*
|
||||
* They are either absolute (start with /) or descendant (no /)
|
||||
* Compare with XPATH for XML, this is for YANG spec
|
||||
* @param[in] yn Yang node tree
|
||||
* @param[in] schema_nodeid schema-node-id, ie a/b or /a/b
|
||||
* @retval NULL Error, with clicon_err called
|
||||
* @retval yres First yang node matching schema nodeid
|
||||
* @note: the identifiers in the xpath (eg a, b in a/b) can match the nodes
|
||||
* defined in yang_xpath: container, leaf,list,leaf-list, modules, sub-modules
|
||||
* Example:
|
||||
* yn : module m { prefix b; container b { list c { key d; leaf d; }} }
|
||||
* schema-node-id = m/b/c, returns the list 'c'.
|
||||
*/
|
||||
yang_node *
|
||||
yang_schema_nodeid(yang_node *yn,
|
||||
char *schema_nodeid)
|
||||
{
|
||||
yang_node *yres = NULL;
|
||||
|
||||
if (strlen(schema_nodeid) == 0)
|
||||
goto done;
|
||||
/* check absolute schema_nodeid */
|
||||
if (schema_nodeid[0] == '/')
|
||||
yres = yang_abs_schema_nodeid(yn, schema_nodeid);
|
||||
else
|
||||
yres = yang_desc_schema_nodeid(yn, schema_nodeid);
|
||||
done:
|
||||
return yres;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Parse argument as CV and save result in yang cv variable
|
||||
|
|
|
|||
|
|
@ -107,6 +107,8 @@ yang_builtin(char *type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Set type cache for yang type
|
||||
*/
|
||||
int
|
||||
yang_type_cache_set(yang_type_cache **ycache0,
|
||||
yang_stmt *resolved,
|
||||
|
|
@ -142,7 +144,6 @@ yang_type_cache_set(yang_type_cache **ycache0,
|
|||
}
|
||||
ycache->yc_fraction = fraction;
|
||||
retval = 0;
|
||||
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -209,6 +210,7 @@ yang_type_cache_free(yang_type_cache *ycache)
|
|||
* @param[in] ys This is a type statement
|
||||
* @param[in] arg Not used
|
||||
* Typically only called once when loading te yang type system.
|
||||
* @note unions not cached
|
||||
*/
|
||||
int
|
||||
ys_resolve_type(yang_stmt *ys,
|
||||
|
|
@ -226,13 +228,16 @@ ys_resolve_type(yang_stmt *ys,
|
|||
if (yang_type_resolve((yang_stmt*)ys->ys_parent, ys, &resolved,
|
||||
&options, &mincv, &maxcv, &pattern, &fraction) < 0)
|
||||
goto done;
|
||||
/* skip unions since they may have different sets of options, mincv, etc */
|
||||
|
||||
if (resolved && strcmp(resolved->ys_argument, "union")==0)
|
||||
;
|
||||
;
|
||||
/* skip unions since they may have different sets of options, mincv, etc
|
||||
* You would have to resolve all sub-types also recursively
|
||||
*/
|
||||
else
|
||||
if (yang_type_cache_set(&ys->ys_typecache,
|
||||
resolved, options, mincv, maxcv, pattern, fraction) < 0)
|
||||
goto done;
|
||||
if (yang_type_cache_set(&ys->ys_typecache,
|
||||
resolved, options, mincv, maxcv, pattern, fraction) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
|
|
@ -1041,3 +1046,4 @@ yang_type_get(yang_stmt *ys,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ expectfn(){
|
|||
return
|
||||
fi
|
||||
# grep extended grep
|
||||
match=`echo "$ret" | grep -EZo "$expect"`
|
||||
match=`echo $ret | grep -EZo "$expect"`
|
||||
# echo "ret:\"$ret\""
|
||||
# echo "expect:\"$expect\""
|
||||
# echo "match:\"$match\""
|
||||
|
|
|
|||
|
|
@ -27,14 +27,13 @@ module example{
|
|||
}
|
||||
leaf address {
|
||||
type leafref {
|
||||
path "../../interfaces/interface[name=eth0]"
|
||||
+ "/address/ip";
|
||||
}
|
||||
path "../../interfaces/interface[name = current()/../relname]"
|
||||
+ "/ipv4/address/ip";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
# XXX not eth0 path "../../interface[name = current()/../ifname]"
|
||||
|
||||
# kill old backend (if any)
|
||||
new "kill old backend"
|
||||
|
|
|
|||
|
|
@ -126,6 +126,9 @@ new "netconf check empty startup"
|
|||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><startup/></source></get-config></rpc>]]>]]>" "^<rpc-reply><data/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf rpc"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><rt:fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></rt:fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
|
||||
|
||||
new "netconf rpc w/o namespace"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><fib-route><routing-instance-name>ipv4</routing-instance-name><destination-address><address-family>ipv4</address-family></destination-address></fib-route></rpc>]]>]]>" "^<rpc-reply><route><address-family>ipv4</address-family><next-hop><next-hop-list>"
|
||||
|
||||
new "netconf subscription"
|
||||
|
|
|
|||
|
|
@ -30,58 +30,68 @@ new "restconf options"
|
|||
expectfn "curl -i -sS -X OPTIONS http://localhost/restconf/data" "Allow: OPTIONS,HEAD,GET,POST,PUT,DELETE"
|
||||
|
||||
new "restconf head"
|
||||
expectfn "curl -sS -I http://localhost/restconf/data" "Content-Type: application/yang.data\+json"
|
||||
expectfn "curl -sS -I http://localhost/restconf/data" "HTTP/1.1 200 OK"
|
||||
#Content-Type: application/yang-data+json"
|
||||
|
||||
new "restconf get empty config"
|
||||
expectfn "curl -sSG http://localhost/restconf/data" "^null
$"
|
||||
expectfn "curl -sSG http://localhost/restconf/data" "null"
|
||||
|
||||
#
|
||||
new "Add subtree to datastore using POST"
|
||||
new "restconf Add subtree to datastore using POST"
|
||||
expectfn 'curl -sS -X POST -d {"interfaces":{"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}}} http://localhost/restconf/data' ""
|
||||
|
||||
new "Check interfaces eth/0/0 added"
|
||||
new "restconf Check interfaces eth/0/0 added"
|
||||
expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}}}
|
||||
$'
|
||||
|
||||
new "delete interfaces"
|
||||
new "restconf delete interfaces"
|
||||
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces' ""
|
||||
|
||||
new "Check empty config"
|
||||
expectfn "curl -sSG http://localhost/restconf/data" "^null
$"
|
||||
new "restconf Check empty config"
|
||||
expectfn "curl -sSG http://localhost/restconf/data" "null"
|
||||
|
||||
new "Add interfaces subtree eth/0/0 using POST"
|
||||
new "restconf Add interfaces subtree eth/0/0 using POST"
|
||||
expectfn 'curl -sS -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces' ""
|
||||
|
||||
new "Check eth/0/0 added"
|
||||
new "restconf Check eth/0/0 added"
|
||||
expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}}}
|
||||
$'
|
||||
|
||||
new "Re-post eth/0/0 which should generate error"
|
||||
new "restconf Re-post eth/0/0 which should generate error"
|
||||
expectfn 'curl -sS -X POST -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces' "Data resource already exists"
|
||||
|
||||
new "Add leaf description using POST"
|
||||
expectfn 'curl -sS -X POST -d {"description":"The-first-interface"} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
|
||||
|
||||
new "Check description added"
|
||||
new "restconf Check description added"
|
||||
expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","description": "The-first-interface","type": "eth","enabled": "true"}}
|
||||
$'
|
||||
|
||||
new "delete eth/0/0"
|
||||
new "restconf delete eth/0/0"
|
||||
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
|
||||
|
||||
#new "Check deleted eth/0/0"
|
||||
#expectfn 'curl -sS -G http://localhost/restconf/data' '{"interfaces": null}
|
||||
#$'
|
||||
new "Check deleted eth/0/0"
|
||||
expectfn 'curl -sS -G http://localhost/restconf/data' "null"
|
||||
|
||||
new "Re-Delete eth/0/0 using none should generate error"
|
||||
new "restconf Re-Delete eth/0/0 using none should generate error"
|
||||
expectfn 'curl -sS -X DELETE http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' "Not Found"
|
||||
|
||||
new "Add subtree eth/0/0 using PUT"
|
||||
new "restconf Add subtree eth/0/0 using PUT"
|
||||
expectfn 'curl -sS -X PUT -d {"interface":{"name":"eth/0/0","type":"eth","enabled":"true"}} http://localhost/restconf/data/interfaces/interface=eth%2f0%2f0' ""
|
||||
|
||||
new "restconf get subtree"
|
||||
expectfn "curl -sS -G http://localhost/restconf/data" '{"interfaces": {"interface": {"name": "eth/0/0","type": "eth","enabled": "true"}}}
|
||||
$'
|
||||
|
||||
new "restconf rpc using POST json"
|
||||
expectfn 'curl -sS -X POST -d {"input":{"routing-instance-name":"ipv4"}} http://localhost/restconf/operations/rt:fib-route' '{ "output": { "route": { "address-family": "ipv4", "next-hop": { "next-hop-list": "2.3.4.5" } } } } '
|
||||
|
||||
new "restconf rpc using POST xml"
|
||||
ret=$(curl -sS -X POST -H "Accept: application/yang-data+xml" -d '{"input":{"routing-instance-name":"ipv4"}}' http://localhost/restconf/operations/rt:fib-route)
|
||||
expect="<output> <route> <address-family>ipv4</address-family> <next-hop> <next-hop-list>2.3.4.5</next-hop-li t> </next-hop> </route> </output> "
|
||||
match=`echo $ret | grep -EZo "$expect"`
|
||||
echo -n "ret: "
|
||||
echo $ret
|
||||
|
||||
new "Kill restconf daemon"
|
||||
sudo pkill -u www-data clixon_restconf
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/bin/bash
|
||||
# Advanced union types and generated code
|
||||
# and enum w values
|
||||
|
||||
# include err() and new() functions
|
||||
. ./lib.sh
|
||||
|
|
@ -48,6 +49,14 @@ module example{
|
|||
type af;
|
||||
}
|
||||
}
|
||||
leaf status {
|
||||
type enumeration {
|
||||
enum up {
|
||||
value 1;
|
||||
}
|
||||
enum down;
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
|
|
@ -101,6 +110,9 @@ expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><discard-chan
|
|||
new "netconf commit"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "cli enum value"
|
||||
expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang set status down" "^$"
|
||||
|
||||
new "Kill backend"
|
||||
# Check if still alive
|
||||
pid=`pgrep clixon_backend`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue