Added state data
This commit is contained in:
parent
43c39160a5
commit
f5c19d8586
24 changed files with 512 additions and 224 deletions
|
|
@ -1,5 +1,10 @@
|
|||
# Clixon CHANGELOG
|
||||
|
||||
- Added state data: Netconf <get> operation introduced; Error when
|
||||
adding state data in <edit-config>.
|
||||
|
||||
- Fixed bug where cli set of leaf-list were doubled, eg cli set foo -> foofoo
|
||||
|
||||
- Restricted yang (sub)module file match to match RFC6020 exactly
|
||||
|
||||
- Generalized yang type resolution to all included (sub)modules not just the topmost
|
||||
|
|
@ -12,7 +17,7 @@
|
|||
|
||||
- Fixed yang leafref cli completion.
|
||||
|
||||
- Removed non-standard api_path extension from internal netconf so that the internal com.
|
||||
- Removed non-standard api_path extension from the internal netconf protocol so that the internal netcinf is now fully standard.
|
||||
|
||||
- Strings in xmldb_put not properly encoded, eg eth/0 became eth.00000
|
||||
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ from_client_get_config(clicon_handle h,
|
|||
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
||||
if ((selector = xml_find_value(xfilter, "select"))==NULL)
|
||||
selector="/";
|
||||
if (xmldb_get(h, db, selector, &xret) < 0){
|
||||
if (xmldb_get(h, db, selector, 1, &xret) < 0){
|
||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>application</error-type>"
|
||||
|
|
@ -260,6 +260,54 @@ from_client_get_config(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Internal message: get
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xe Netconf request xml tree
|
||||
* @param[out] cbret Return xml value cligen buffer
|
||||
*/
|
||||
static int
|
||||
from_client_get(clicon_handle h,
|
||||
cxobj *xe,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xfilter;
|
||||
char *selector = "/";
|
||||
cxobj *xret = NULL;
|
||||
|
||||
if ((xfilter = xml_find(xe, "filter")) != NULL)
|
||||
if ((selector = xml_find_value(xfilter, "select"))==NULL)
|
||||
selector="/";
|
||||
if (xmldb_get(h, "running", selector, 0, &xret) < 0){
|
||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>application</error-type>"
|
||||
"<error-severity>error</error-severity>"
|
||||
"<error-info>read-registry</error-info>"
|
||||
"</rpc-error></rpc-reply>");
|
||||
goto ok;
|
||||
}
|
||||
cprintf(cbret, "<rpc-reply><data>");
|
||||
/* if empty only <data/>, if any data then <data><config>..</config></data> */
|
||||
if (xret!=NULL){
|
||||
if (xml_child_nr(xret)){
|
||||
if (xml_name_set(xret, "config") < 0)
|
||||
goto done;
|
||||
if (clicon_xml2cbuf(cbret, xret, 0, 0) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
cprintf(cbret, "</data></rpc-reply>");
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Internal message: edit-config
|
||||
*
|
||||
* @param[in] h Clicon handle
|
||||
|
|
@ -281,7 +329,13 @@ from_client_edit_config(clicon_handle h,
|
|||
cxobj *x;
|
||||
enum operation_type operation = OP_MERGE;
|
||||
int piddb;
|
||||
int non_config = 0;
|
||||
yang_spec *yspec;
|
||||
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
if ((target = netconf_db_find(xn, "target")) == NULL){
|
||||
clicon_err(OE_XML, 0, "db not found");
|
||||
goto done;
|
||||
|
|
@ -295,7 +349,6 @@ from_client_edit_config(clicon_handle h,
|
|||
"</rpc-error></rpc-reply>", target);
|
||||
goto ok;
|
||||
}
|
||||
|
||||
/* Check if target locked by other client */
|
||||
piddb = xmldb_islocked(h, target);
|
||||
if (piddb && mypid != piddb){
|
||||
|
|
@ -320,6 +373,20 @@ from_client_edit_config(clicon_handle h,
|
|||
}
|
||||
}
|
||||
if ((xc = xpath_first(xn, "config")) != NULL){
|
||||
if (xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xc, CX_ELMNT, xml_non_config_data, &non_config) < 0)
|
||||
goto done;
|
||||
if (non_config){
|
||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>invalid-value</error-tag>"
|
||||
"<error-type>protocol</error-type>"
|
||||
"<error-severity>error</error-severity>"
|
||||
"<error-message>state data not allowed</error-message>"
|
||||
"</rpc-error></rpc-reply>");
|
||||
goto ok;
|
||||
}
|
||||
|
||||
if (xmldb_put(h, target, operation, xc) < 0){
|
||||
cprintf(cbret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
|
|
@ -875,6 +942,10 @@ from_client_msg(clicon_handle h,
|
|||
if (from_client_unlock(h, xe, pid, cbret) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(name, "get") == 0){
|
||||
if (from_client_get(h, xe, cbret) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(name, "close-session") == 0){
|
||||
xmldb_unlock_all(h, pid);
|
||||
cprintf(cbret, "<rpc-reply><ok/></rpc-reply>");
|
||||
|
|
|
|||
|
|
@ -146,9 +146,9 @@ validate_common(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
/* 2. Parse xml trees */
|
||||
if (xmldb_get(h, "running", "/", &td->td_src) < 0)
|
||||
if (xmldb_get(h, "running", "/", 1, &td->td_src) < 0)
|
||||
goto done;
|
||||
if (xmldb_get(h, candidate, "/", &td->td_target) < 0)
|
||||
if (xmldb_get(h, candidate, "/", 1, &td->td_target) < 0)
|
||||
goto done;
|
||||
|
||||
/* 3. Compute differences */
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ cli_dbxml(clicon_handle h,
|
|||
xml_type_set(xa, CX_ATTR);
|
||||
if (xml_value_set(xa, xml_operation2str(op)) < 0)
|
||||
goto done;
|
||||
if (y->yn_keyword != Y_LIST){
|
||||
if (y->yn_keyword != Y_LIST && y->yn_keyword != Y_LEAF_LIST){
|
||||
len = cvec_len(cvv);
|
||||
if (len > 1){
|
||||
cval = cvec_i(cvv, len-1);
|
||||
|
|
|
|||
|
|
@ -530,6 +530,76 @@ netconf_unlock(clicon_handle h,
|
|||
return netconf_lock(h, xn, xret);
|
||||
}
|
||||
|
||||
/*! Get running configuration and device state information
|
||||
*
|
||||
*
|
||||
|
||||
* @param[in] h Clicon handle
|
||||
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> level.
|
||||
* @param[out] xret Return XML, error or OK
|
||||
* @note filter type subtree and xpath is supported, but xpath is preferred, and
|
||||
* better performance and tested. Please use xpath.
|
||||
*
|
||||
* @example
|
||||
* <rpc><get><filter type="xpath" select="//SenderTwampIpv4"/>
|
||||
* </get></rpc>]]>]]>
|
||||
*/
|
||||
static int
|
||||
netconf_get(clicon_handle h,
|
||||
cxobj *xn,
|
||||
cxobj **xret)
|
||||
{
|
||||
cxobj *xfilter; /* filter */
|
||||
int retval = -1;
|
||||
char *ftype = NULL;
|
||||
cxobj *xfilterconf;
|
||||
cxobj *xconf;
|
||||
|
||||
/* ie <filter>...</filter> */
|
||||
if ((xfilter = xpath_first(xn, "filter")) != NULL)
|
||||
ftype = xml_find_value(xfilter, "type");
|
||||
if (ftype == NULL || strcmp(ftype, "xpath")==0){
|
||||
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(ftype, "subtree")==0){
|
||||
/* Default rfc filter is subtree. I prefer xpath and use it internally.
|
||||
Get whole subtree and then filter aftwerwards. This is suboptimal.
|
||||
Therefore please use xpath.
|
||||
*/
|
||||
if (clicon_rpc_netconf_xml(h, xml_parent(xn), xret, NULL) < 0)
|
||||
goto done;
|
||||
if (xfilter &&
|
||||
(xfilterconf = xpath_first(xfilter, "//configuration"))!= NULL &&
|
||||
(xconf = xpath_first(*xret, "/rpc-reply/data/configuration")) != NULL){
|
||||
/* xml_filter removes parts of xml tree not matching */
|
||||
if ((strcmp(xml_name(xfilterconf), xml_name(xconf))!=0) ||
|
||||
xml_filter(xfilterconf, xconf) < 0){
|
||||
clicon_xml_parse(xret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>applicatio</error-type>"
|
||||
"<error-severity>error</error-severity>"
|
||||
"<error-info>filtering</error-info>"
|
||||
"</rpc-error></rpc-reply>");
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
clicon_xml_parse(xret, "<rpc-reply><rpc-error>"
|
||||
"<error-tag>operation-failed</error-tag>"
|
||||
"<error-type>applicatio</error-type>"
|
||||
"<error-severity>error</error-severity>"
|
||||
"<error-message>filter type not supported</error-message>"
|
||||
"<error-info>type</error-info>"
|
||||
"</rpc-error></rpc-reply>");
|
||||
}
|
||||
// ok: /* netconf error is not fatal */
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Close a (user) session
|
||||
<close-session/>
|
||||
* @param[in] xn Sub-tree (under xorig) at <rpc>...</rpc> level.
|
||||
|
|
@ -818,6 +888,8 @@ netconf_rpc_dispatch(clicon_handle h,
|
|||
return netconf_lock(h, xe, xret);
|
||||
else if (strcmp(xml_name(xe), "unlock") == 0)
|
||||
return netconf_unlock(h, xe, xret);
|
||||
else if (strcmp(xml_name(xe), "get") == 0)
|
||||
return netconf_get(h, xe, xret);
|
||||
else if (strcmp(xml_name(xe), "close-session") == 0)
|
||||
return netconf_close_session(h, xe, xret);
|
||||
else if (strcmp(xml_name(xe), "kill-session") == 0)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,96 @@
|
|||
|
||||
#include "restconf_lib.h"
|
||||
|
||||
/* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code
|
||||
* and RFC 6241 Appendix A. NETCONF Error list
|
||||
*/
|
||||
static const map_str2int netconf_restconf_map[] = {
|
||||
{"in-use", 409},
|
||||
{"invalid-value", 400},
|
||||
{"invalid-value", 404},
|
||||
{"invalid-value", 406},
|
||||
{"too-big", 413}, /* request */
|
||||
{"too-big", 400}, /* response */
|
||||
{"missing-attribute", 400},
|
||||
{"bad-attribute", 400},
|
||||
{"unknown-attribute", 400},
|
||||
{"bad-element", 400},
|
||||
{"unknown-element", 400},
|
||||
{"unknown-namespace", 400},
|
||||
{"access-denied", 401},
|
||||
{"access-denied", 403},
|
||||
{"lock-denied", 409},
|
||||
{"resource-denied", 409},
|
||||
{"rollback-failed", 500},
|
||||
{"data-exists", 409},
|
||||
{"data-missing", 409},
|
||||
{"operation-not-supported",405},
|
||||
{"operation-not-supported",501},
|
||||
{"operation-failed", 412},
|
||||
{"operation-failed", 500},
|
||||
{"partial-operation", 500},
|
||||
{"malformed-message", 400},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* See 7231 Section 6.1
|
||||
*/
|
||||
static const map_str2int http_reason_phrase_map[] = {
|
||||
{"Continue", 100},
|
||||
{"Switching Protocols", 101},
|
||||
{"OK", 200},
|
||||
{"Created", 201},
|
||||
{"Accepted", 202},
|
||||
{"Non-Authoritative Information", 203},
|
||||
{"No Content", 204},
|
||||
{"Reset Content", 205},
|
||||
{"Partial Content", 206},
|
||||
{"Multiple Choices", 300},
|
||||
{"Moved Permanently", 301},
|
||||
{"Found", 302},
|
||||
{"See Other", 303},
|
||||
{"Not Modified", 304},
|
||||
{"Use Proxy", 305},
|
||||
{"Temporary Redirect", 307},
|
||||
{"Bad Request", 400},
|
||||
{"Unauthorized", 401},
|
||||
{"Payment Required", 402},
|
||||
{"Forbidden", 403},
|
||||
{"Not Found", 404},
|
||||
{"Method Not Allowed", 405},
|
||||
{"Not Acceptable", 406},
|
||||
{"Proxy Authentication Required", 407},
|
||||
{"Request Timeout", 408},
|
||||
{"Conflict", 409},
|
||||
{"Gone", 410},
|
||||
{"Length Required", 411},
|
||||
{"Precondition Failed", 412},
|
||||
{"Payload Too Large", 413},
|
||||
{"URI Too Long", 414},
|
||||
{"Unsupported Media Type", 415},
|
||||
{"Range Not Satisfiable", 416},
|
||||
{"Expectation Failed", 417},
|
||||
{"Upgrade Required", 426},
|
||||
{"Internal Server Error", 500},
|
||||
{"Not Implemented", 501},
|
||||
{"Bad Gateway", 502},
|
||||
{"Service Unavailable", 503},
|
||||
{"Gateway Timeout", 504},
|
||||
{"HTTP Version Not Supported", 505},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
int
|
||||
restconf_err2code(char *tag)
|
||||
{
|
||||
return clicon_str2int(netconf_restconf_map, tag);
|
||||
}
|
||||
|
||||
const char *
|
||||
restconf_code2reason(int code)
|
||||
{
|
||||
return clicon_int2str(http_reason_phrase_map, code);
|
||||
}
|
||||
|
||||
/*!
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int restconf_err2code(char *tag);
|
||||
const char *restconf_code2reason(int code);
|
||||
int notfound(FCGX_Request *r);
|
||||
int badrequest(FCGX_Request *r);
|
||||
int conflict(FCGX_Request *r);
|
||||
|
|
|
|||
|
|
@ -190,8 +190,8 @@ api_data_get_gen(clicon_handle h,
|
|||
notfound(r); /* bad reply? */
|
||||
goto done;
|
||||
}
|
||||
code = clicon_str2int(netconf_restconf_map, xml_body(xtag));
|
||||
if ((reason_phrase = clicon_int2str(http_reason_phrase_map, code)) == NULL)
|
||||
code = restconf_err2code(xml_body(xtag));
|
||||
if ((reason_phrase = restconf_code2reason(code)) == NULL)
|
||||
reason_phrase="";
|
||||
clicon_debug(1, "%s code:%d reason phrase:%s",
|
||||
__FUNCTION__, code, reason_phrase);
|
||||
|
|
|
|||
|
|
@ -213,7 +213,7 @@ main(int argc, char **argv)
|
|||
if (strcmp(cmd, "get")==0){
|
||||
if (argc != 1 && argc != 2)
|
||||
usage(argv0);
|
||||
if (xmldb_get(h, db, argc==2?argv[1]:"/", &xt) < 0)
|
||||
if (xmldb_get(h, db, argc==2?argv[1]:"/", 0, &xt) < 0)
|
||||
goto done;
|
||||
clicon_xml2file(stdout, xt, 0, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -71,17 +71,6 @@
|
|||
* cli_expand_var_generate | yang2api_path_fmt |
|
||||
* yang -------------> | |
|
||||
* +----------------+
|
||||
* xmldb_get_tree
|
||||
* - compare_dbs
|
||||
* - netconf
|
||||
* - validate
|
||||
* - from_client_save
|
||||
*
|
||||
* xmldb_get_vec
|
||||
* - restconf
|
||||
* - expand_dbvar
|
||||
* - show_conf_xpath
|
||||
*
|
||||
* dependency on clixon handle:
|
||||
* clixon_xmldb_dir()
|
||||
* clicon_dbspec_yang(h)
|
||||
|
|
@ -578,6 +567,7 @@ int
|
|||
kv_get(xmldb_handle xh,
|
||||
char *db,
|
||||
char *xpath,
|
||||
int config,
|
||||
cxobj **xtop)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -627,13 +617,14 @@ kv_get(xmldb_handle xh,
|
|||
}
|
||||
/* Top is special case */
|
||||
if (!xml_flag(xt, XML_FLAG_MARK))
|
||||
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||
goto done;
|
||||
/* Add default values (if not set) */
|
||||
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
||||
goto done;
|
||||
/* XXX does not work for top-level */
|
||||
/* Order XML children according to YANG */
|
||||
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int kv_get(xmldb_handle h, char *db, char *xpath, cxobj **xtop);
|
||||
int kv_get(xmldb_handle h, char *db, char *xpath, int config, cxobj **xtop);
|
||||
int kv_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt);
|
||||
int kv_dump(FILE *f, char *dbfilename, char *rxkey);
|
||||
int kv_copy(xmldb_handle h, char *from, char *to);
|
||||
|
|
|
|||
|
|
@ -68,9 +68,10 @@
|
|||
/*! Internal structure of text datastore handle.
|
||||
*/
|
||||
struct text_handle {
|
||||
int th_magic; /* magic */
|
||||
char *th_dbdir; /* Directory of database files */
|
||||
yang_spec *th_yangspec; /* Yang spec if this datastore */
|
||||
int th_magic; /* magic */
|
||||
char *th_dbdir; /* Directory of database files */
|
||||
yang_spec *th_yangspec; /* Yang spec if this datastore */
|
||||
clicon_hash_t *th_dbs; /* Hash of databases */
|
||||
};
|
||||
|
||||
/*! Check struct magic number for sanity checks
|
||||
|
|
@ -85,16 +86,6 @@ text_handle_check(xmldb_handle xh)
|
|||
return th->th_magic == TEXT_HANDLE_MAGIC ? 0 : -1;
|
||||
}
|
||||
|
||||
/*! Database locking for candidate and running non-persistent
|
||||
* Store an integer for running and candidate containing
|
||||
* the session-id of the client holding the lock.
|
||||
* @note This should probably be on file-system
|
||||
*/
|
||||
static int _running_locked = 0;
|
||||
static int _candidate_locked = 0;
|
||||
static int _startup_locked = 0;
|
||||
|
||||
|
||||
/*! Translate from symbolic database name to actual filename in file-system
|
||||
* @param[in] th text handle handle
|
||||
* @param[in] db Symbolic database name, eg "candidate", "running"
|
||||
|
|
@ -153,6 +144,8 @@ text_connect(void)
|
|||
}
|
||||
memset(th, 0, size);
|
||||
th->th_magic = TEXT_HANDLE_MAGIC;
|
||||
if ((th->th_dbs = hash_init()) == NULL)
|
||||
goto done;
|
||||
xh = (xmldb_handle)th;
|
||||
done:
|
||||
return xh;
|
||||
|
|
@ -172,6 +165,8 @@ text_disconnect(xmldb_handle xh)
|
|||
if (th){
|
||||
if (th->th_dbdir)
|
||||
free(th->th_dbdir);
|
||||
if (th->th_dbs)
|
||||
hash_free(th->th_dbs);
|
||||
free(th);
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -239,37 +234,6 @@ text_setopt(xmldb_handle xh,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Populate with spec
|
||||
* @param[in] xt XML tree with some node marked
|
||||
*/
|
||||
int
|
||||
xml_spec_populate(cxobj *x,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec = (yang_spec*)arg;
|
||||
char *name;
|
||||
yang_stmt *y; /* yang node */
|
||||
cxobj *xp; /* xml parent */
|
||||
yang_stmt *yp; /* parent yang */
|
||||
|
||||
name = xml_name(x);
|
||||
if ((xp = xml_parent(x)) != NULL &&
|
||||
(yp = xml_spec(xp)) != NULL)
|
||||
y = yang_find_syntax((yang_node*)yp, xml_name(x));
|
||||
else
|
||||
y = yang_find_topnode(yspec, name); /* still NULL for config */
|
||||
if (y==NULL){
|
||||
clicon_err(OE_XML, EBADF, "yang spec not found for xml node '%s' xml parent name: '%s' yangspec:'%s']",
|
||||
name,
|
||||
xp?xml_name(xp):"", yp?yp->ys_argument:"");
|
||||
goto done;
|
||||
}
|
||||
xml_spec_set(x, y);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Ensure that xt only has a single sub-element and that is "config"
|
||||
*/
|
||||
|
|
@ -319,6 +283,7 @@ int
|
|||
text_get(xmldb_handle xh,
|
||||
char *db,
|
||||
char *xpath,
|
||||
int config,
|
||||
cxobj **xtop)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -337,7 +302,7 @@ text_get(xmldb_handle xh,
|
|||
clicon_err(OE_XML, 0, "dbfile NULL");
|
||||
goto done;
|
||||
}
|
||||
if ((yspec = th->th_yangspec) == NULL){
|
||||
if ((yspec = th->th_yangspec) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -362,7 +327,7 @@ text_get(xmldb_handle xh,
|
|||
goto done;
|
||||
}
|
||||
/* Here xt looks like: <config>...</config> */
|
||||
/* Validate existing config tree */
|
||||
/* Add yang specification backpointer to all XML nodes */
|
||||
if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
|
||||
|
|
@ -370,28 +335,34 @@ text_get(xmldb_handle xh,
|
|||
if (xpath_vec(xt, xpath?xpath:"/", &xvec, &xlen) < 0)
|
||||
goto done;
|
||||
|
||||
/* If vectors are specified then filter out everything else,
|
||||
/* If vectors are specified then mark the nodes found and
|
||||
* then filter out everything else,
|
||||
* otherwise return complete tree.
|
||||
*/
|
||||
if (xvec != NULL){
|
||||
for (i=0; i<xlen; i++)
|
||||
xml_flag_set(xvec[i], XML_FLAG_MARK);
|
||||
}
|
||||
/* Top is special case */
|
||||
/* Remove everything that is not marked */
|
||||
if (!xml_flag(xt, XML_FLAG_MARK))
|
||||
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
|
||||
goto done;
|
||||
/* reset flag */
|
||||
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xt, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
/* filter out state (operations) data if config not set. Mark all nodes
|
||||
that are not config data */
|
||||
if (config && xml_apply(xt, CX_ELMNT, xml_non_config_data, NULL) < 0)
|
||||
goto done;
|
||||
/* Remove (prune) nodes that are marked (that does not pass test) */
|
||||
if (xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1) < 0)
|
||||
goto done;
|
||||
/* Add default values (if not set) */
|
||||
if (xml_apply(xt, CX_ELMNT, xml_default, NULL) < 0)
|
||||
goto done;
|
||||
/* XXX does not work for top-level */
|
||||
/* Order XML children according to YANG */
|
||||
if (xml_apply(xt, CX_ELMNT, xml_order, NULL) < 0)
|
||||
goto done;
|
||||
if (xml_apply(xt, CX_ELMNT, xml_sanity, NULL) < 0)
|
||||
goto done;
|
||||
|
||||
if (debug>1)
|
||||
clicon_xml2file(stderr, xt, 0, 1);
|
||||
|
|
@ -696,7 +667,6 @@ text_modify_top(cxobj *x0,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Modify database provided an xml tree and an operation
|
||||
* This is a clixon datastore plugin of the the xmldb api
|
||||
* @see xmldb_put
|
||||
|
|
@ -757,11 +727,11 @@ text_put(xmldb_handle xh,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* Validate existing config tree */
|
||||
/* Add yang specification backpointer to all XML nodes */
|
||||
if (xml_apply(x0, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
|
||||
/* Validate modification tree */
|
||||
/* Add yang specification backpointer to all XML nodes */
|
||||
if (xml_apply(x1, CX_ELMNT, xml_spec_populate, yspec) < 0)
|
||||
goto done;
|
||||
|
||||
|
|
@ -772,7 +742,7 @@ text_put(xmldb_handle xh,
|
|||
goto done;
|
||||
|
||||
/* Remove NONE nodes if all subs recursively are also NONE */
|
||||
if (xml_tree_prune_flagged(x0, XML_FLAG_NONE, 0, NULL) <0)
|
||||
if (xml_tree_prune_flagged_sub(x0, XML_FLAG_NONE, 0, NULL) <0)
|
||||
goto done;
|
||||
if (xml_apply(x0, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset,
|
||||
(void*)XML_FLAG_NONE) < 0)
|
||||
|
|
@ -842,7 +812,7 @@ text_copy(xmldb_handle xh,
|
|||
}
|
||||
|
||||
/*! Lock database
|
||||
* @param[in] xh XMLDB handle
|
||||
* @param[in] xh XMLDB handle
|
||||
* @param[in] db Database
|
||||
* @param[in] pid Process id
|
||||
* @retval -1 Error
|
||||
|
|
@ -853,13 +823,9 @@ text_lock(xmldb_handle xh,
|
|||
char *db,
|
||||
int pid)
|
||||
{
|
||||
// struct text_handle *th = handle(xh);
|
||||
if (strcmp("running", db) == 0)
|
||||
_running_locked = pid;
|
||||
else if (strcmp("candidate", db) == 0)
|
||||
_candidate_locked = pid;
|
||||
else if (strcmp("startup", db) == 0)
|
||||
_startup_locked = pid;
|
||||
struct text_handle *th = handle(xh);
|
||||
|
||||
hash_add(th->th_dbs, db, &pid, sizeof(pid));
|
||||
clicon_debug(1, "%s: locked by %u", db, pid);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -876,13 +842,11 @@ int
|
|||
text_unlock(xmldb_handle xh,
|
||||
char *db)
|
||||
{
|
||||
// struct text_handle *th = handle(xh);
|
||||
if (strcmp("running", db) == 0)
|
||||
_running_locked = 0;
|
||||
else if (strcmp("candidate", db) == 0)
|
||||
_candidate_locked = 0;
|
||||
else if (strcmp("startup", db) == 0)
|
||||
_startup_locked = 0;
|
||||
struct text_handle *th = handle(xh);
|
||||
int zero = 0;
|
||||
|
||||
hash_add(th->th_dbs, db, &zero, sizeof(zero));
|
||||
// hash_del(th->th_dbs, db);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -896,14 +860,19 @@ int
|
|||
text_unlock_all(xmldb_handle xh,
|
||||
int pid)
|
||||
{
|
||||
// struct text_handle *th = handle(xh);
|
||||
struct text_handle *th = handle(xh);
|
||||
char **keys;
|
||||
size_t klen;
|
||||
int i;
|
||||
int *val;
|
||||
size_t vlen;
|
||||
|
||||
if (_running_locked == pid)
|
||||
_running_locked = 0;
|
||||
if (_candidate_locked == pid)
|
||||
_candidate_locked = 0;
|
||||
if (_startup_locked == pid)
|
||||
_startup_locked = 0;
|
||||
if ((keys = hash_keys(th->th_dbs, &klen)) == NULL)
|
||||
return 0;
|
||||
for(i = 0; i < klen; i++)
|
||||
if ((val = hash_value(th->th_dbs, keys[i], &vlen)) != NULL &&
|
||||
*val == pid)
|
||||
hash_del(th->th_dbs, keys[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -918,14 +887,13 @@ int
|
|||
text_islocked(xmldb_handle xh,
|
||||
char *db)
|
||||
{
|
||||
// struct text_handle *th = handle(xh);
|
||||
struct text_handle *th = handle(xh);
|
||||
size_t vlen;
|
||||
int *val;
|
||||
|
||||
if (strcmp("running", db) == 0)
|
||||
return (_running_locked);
|
||||
else if (strcmp("candidate", db) == 0)
|
||||
return(_candidate_locked);
|
||||
else if (strcmp("startup", db) == 0)
|
||||
return(_startup_locked);
|
||||
if ((val = hash_value(th->th_dbs, db, &vlen)) == NULL)
|
||||
return 0;
|
||||
return *val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1022,6 +990,7 @@ text_plugin_exit(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct xmldb_api api;
|
||||
static const struct xmldb_api api;
|
||||
|
||||
/*! plugin init function */
|
||||
|
|
@ -1111,7 +1080,7 @@ main(int argc, char **argv)
|
|||
if (argc < 5)
|
||||
usage(argv[0]);
|
||||
xpath = argc>5?argv[5]:NULL;
|
||||
if (xmldb_get(h, db, xpath, &xt, NULL, NULL) < 0)
|
||||
if (xmldb_get(h, db, xpath, &xt, NULL, 1, NULL) < 0)
|
||||
goto done;
|
||||
clicon_xml2file(stdout, xt, 0, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int text_get(xmldb_handle h, char *db, char *xpath, cxobj **xtop);
|
||||
int text_get(xmldb_handle h, char *db, char *xpath, int config, cxobj **xtop);
|
||||
int text_put(xmldb_handle h, char *db, enum operation_type op, cxobj *xt);
|
||||
int text_dump(FILE *f, char *dbfilename, char *rxkey);
|
||||
int text_copy(xmldb_handle h, char *from, char *to);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ typedef int (xmldb_getopt_t)(xmldb_handle xh, char *optname, void **value);
|
|||
typedef int (xmldb_setopt_t)(xmldb_handle xh, char *optname, void *value);
|
||||
|
||||
/* Type of xmldb get function */
|
||||
typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath, cxobj **xtop);
|
||||
typedef int (xmldb_get_t)(xmldb_handle xh, char *db, char *xpath, int config, cxobj **xtop);
|
||||
|
||||
/* Type of xmldb put function */
|
||||
typedef int (xmldb_put_t)(xmldb_handle xh, char *db, enum operation_type op, cxobj *xt);
|
||||
|
|
@ -138,7 +138,7 @@ int xmldb_connect(clicon_handle h);
|
|||
int xmldb_disconnect(clicon_handle h);
|
||||
int xmldb_getopt(clicon_handle h, char *optname, void **value);
|
||||
int xmldb_setopt(clicon_handle h, char *optname, void *value);
|
||||
int xmldb_get(clicon_handle h, char *db, char *xpath, cxobj **xtop);
|
||||
int xmldb_get(clicon_handle h, char *db, char *xpath, int config, cxobj **xtop);
|
||||
int xmldb_put(clicon_handle h, char *db, enum operation_type op, cxobj *xt);
|
||||
int xmldb_copy(clicon_handle h, char *from, char *to);
|
||||
int xmldb_lock(clicon_handle h, char *db, int pid);
|
||||
|
|
|
|||
|
|
@ -47,8 +47,6 @@ enum {
|
|||
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 */
|
||||
};
|
||||
extern const map_str2int netconf_restconf_map[];
|
||||
extern const map_str2int http_reason_phrase_map[];
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
|
|
@ -65,10 +63,13 @@ int xml_diff(yang_spec *yspec, cxobj *xt1, cxobj *xt2,
|
|||
int yang2api_path_fmt(yang_stmt *ys, int inclkey, char **api_path_fmt);
|
||||
int api_path_fmt2api_path(char *api_path_fmt, cvec *cvv, char **api_path);
|
||||
int api_path_fmt2xpath(char *api_path_fmt, cvec *cvv, char **xpath);
|
||||
int xml_tree_prune_flagged(cxobj *xt, int flag, int test, int *upmark);
|
||||
int xml_tree_prune_flagged_sub(cxobj *xt, int flag, int test, int *upmark);
|
||||
int xml_tree_prune_flagged(cxobj *xt, int flag, int test);
|
||||
int xml_default(cxobj *x, void *arg);
|
||||
int xml_order(cxobj *x, void *arg);
|
||||
int xml_sanity(cxobj *x, void *arg);
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ hash_del(clicon_hash_t *hash,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Return vector of keys in has table
|
||||
/*! Return vector of keys in hash table
|
||||
*
|
||||
* @param[in] hash Hash table
|
||||
* @param[out] nkeys Size of key vector
|
||||
|
|
|
|||
|
|
@ -1345,6 +1345,10 @@ cxvec_append(cxobj *x,
|
|||
* @param[in] type matching type or -1 for any
|
||||
* @param[in] fn Callback
|
||||
* @param[in] arg Argument
|
||||
* @retval -1 Error, aborted at first error encounter
|
||||
* @retval 0 OK, all nodes traversed
|
||||
* @retval n OK, aborted at first encounter of first match
|
||||
*
|
||||
* @code
|
||||
* int x_fn(cxobj *x, void *arg)
|
||||
* {
|
||||
|
|
@ -1353,7 +1357,7 @@ cxvec_append(cxobj *x,
|
|||
* xml_apply(xn, CX_ELMNT, x_fn, NULL);
|
||||
* @endcode
|
||||
* @note do not delete or move around any children during this function
|
||||
* @note It does not apply fn to the root node,..
|
||||
* @note return value > 0 aborts the traversal
|
||||
* @see xml_apply0 including top object
|
||||
*/
|
||||
int
|
||||
|
|
@ -1363,13 +1367,19 @@ xml_apply(cxobj *xn,
|
|||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x = NULL;
|
||||
cxobj *x;
|
||||
int ret;
|
||||
|
||||
x = NULL;
|
||||
while ((x = xml_child_each(xn, x, type)) != NULL) {
|
||||
if (fn(x, arg) < 0)
|
||||
goto done;
|
||||
if (xml_apply(x, type, fn, arg) < 0)
|
||||
if ((ret = xml_apply(x, type, fn, arg)) < 0)
|
||||
goto done;
|
||||
if (ret > 0){
|
||||
retval = ret;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
@ -1377,7 +1387,11 @@ xml_apply(cxobj *xn,
|
|||
}
|
||||
|
||||
/*! Apply a function call on top object and all xml node children recursively
|
||||
* @retval -1 Error, aborted at first error encounter
|
||||
* @retval 0 OK, all nodes traversed
|
||||
* @retval n OK, aborted at first encounter of first match
|
||||
* @see xml_apply not including top object
|
||||
|
||||
*/
|
||||
int
|
||||
xml_apply0(cxobj *xn,
|
||||
|
|
@ -1386,10 +1400,14 @@ xml_apply0(cxobj *xn,
|
|||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
int ret;
|
||||
|
||||
if (fn(xn, arg) < 0)
|
||||
if ((ret = fn(xn, arg)) < 0)
|
||||
goto done;
|
||||
retval = xml_apply(xn, type, fn, arg);
|
||||
if (ret > 0)
|
||||
retval = ret;
|
||||
else
|
||||
retval = xml_apply(xn, type, fn, arg);
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
|
@ -1402,6 +1420,9 @@ xml_apply0(cxobj *xn,
|
|||
* @param[in] xn XML node
|
||||
* @param[in] fn Callback
|
||||
* @param[in] arg Argument
|
||||
* @retval -1 Error, aborted at first error encounter
|
||||
* @retval 0 OK, all nodes traversed
|
||||
* @retval n OK, aborted at first encounter of first match
|
||||
* @code
|
||||
* int x_fn(cxobj *x, void *arg)
|
||||
* {
|
||||
|
|
@ -1420,12 +1441,17 @@ xml_apply_ancestor(cxobj *xn,
|
|||
{
|
||||
int retval = -1;
|
||||
cxobj *xp = NULL;
|
||||
int ret;
|
||||
|
||||
while ((xp = xml_parent(xn)) != NULL) {
|
||||
if (fn(xp, arg) < 0)
|
||||
goto done;
|
||||
if (xml_apply_ancestor(xp, fn, arg) < 0)
|
||||
if ((ret = xml_apply_ancestor(xp, fn, arg)) < 0)
|
||||
goto done;
|
||||
if (ret > 0){
|
||||
retval = ret;
|
||||
goto done;
|
||||
}
|
||||
xn = xp;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -322,12 +322,13 @@ xmldb_setopt(clicon_handle h,
|
|||
* @param[in] h Clicon handle
|
||||
* @param[in] dbname Name of database to search in (filename including dir path
|
||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||
* @param[in] config If set only configuration data, else also state
|
||||
* @param[out] xtop Single XML tree which xvec points to. Free with xml_free()
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj *xt;
|
||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", &xt) < 0)
|
||||
* if (xmldb_get(xh, "running", "/interfaces/interface[name="eth"]", 1, &xt) < 0)
|
||||
* err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xv[i];
|
||||
|
|
@ -343,6 +344,7 @@ int
|
|||
xmldb_get(clicon_handle h,
|
||||
char *db,
|
||||
char *xpath,
|
||||
int config,
|
||||
cxobj **xtop)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -361,7 +363,7 @@ xmldb_get(clicon_handle h,
|
|||
clicon_err(OE_DB, 0, "Not connected to datastore plugin");
|
||||
goto done;
|
||||
}
|
||||
retval = xa->xa_get_fn(xh, db, xpath, xtop);
|
||||
retval = xa->xa_get_fn(xh, db, xpath, config, xtop);
|
||||
#if DEBUG
|
||||
if (retval == 0) {
|
||||
cbuf *cb = cbuf_new();
|
||||
|
|
@ -391,6 +393,9 @@ xmldb_get(clicon_handle h,
|
|||
* if (xmldb_put(xh, "running", OP_MERGE, xt) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @note that you can add both config data and state data. In comparison,
|
||||
* xmldb_get has a parameter to get config data only.
|
||||
*
|
||||
*/
|
||||
int
|
||||
xmldb_put(clicon_handle h,
|
||||
|
|
|
|||
|
|
@ -1084,23 +1084,25 @@ api_path_fmt2xpath(char *api_path_fmt,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Prune everything that does not pass test
|
||||
/*! Prune everything that does not pass test or have at least a child* does not
|
||||
* @param[in] xt XML tree with some node marked
|
||||
* @param[in] flag Which flag to test for
|
||||
* @param[in] test 1: test that flag is set, 0: test that flag is not set
|
||||
* @param[out] upmark Set if a child (recursively) has marked set.
|
||||
* The function removes all branches that does not a child that pass the test
|
||||
* The function removes all branches that does not pass the test
|
||||
* Purge all nodes that dont have MARK flag set recursively.
|
||||
* Save all nodes that is MARK:ed or have at least one (grand*)child that is MARKed
|
||||
* @code
|
||||
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL);
|
||||
* xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL);
|
||||
* @endcode
|
||||
* @note This function seems a little too complex semantics
|
||||
* @see xml_tree_prune_flagged for a simpler variant
|
||||
*/
|
||||
int
|
||||
xml_tree_prune_flagged(cxobj *xt,
|
||||
int flag,
|
||||
int test,
|
||||
int *upmark)
|
||||
xml_tree_prune_flagged_sub(cxobj *xt,
|
||||
int flag,
|
||||
int test,
|
||||
int *upmark)
|
||||
{
|
||||
int retval = -1;
|
||||
int submark;
|
||||
|
|
@ -1117,6 +1119,7 @@ xml_tree_prune_flagged(cxobj *xt,
|
|||
xprev = x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if (xml_flag(x, flag) == test?flag:0){
|
||||
/* Pass test */
|
||||
mark++;
|
||||
xprev = x;
|
||||
continue; /* mark and stop here */
|
||||
|
|
@ -1131,7 +1134,7 @@ xml_tree_prune_flagged(cxobj *xt,
|
|||
continue;
|
||||
}
|
||||
}
|
||||
if (xml_tree_prune_flagged(x, flag, test, &submark) < 0)
|
||||
if (xml_tree_prune_flagged_sub(x, flag, test, &submark) < 0)
|
||||
goto done;
|
||||
/* if xt is list and submark anywhere, then key subs are also marked
|
||||
*/
|
||||
|
|
@ -1167,6 +1170,43 @@ xml_tree_prune_flagged(cxobj *xt,
|
|||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Prune everything that passes test
|
||||
* @param[in] xt XML tree with some node marked
|
||||
* @param[in] flag Which flag to test for
|
||||
* @param[in] test 1: test that flag is set, 0: test that flag is not set
|
||||
* The function removes all branches that does not pass test
|
||||
* @code
|
||||
* xml_tree_prune_flagged(xt, XML_FLAG_MARK, 1, NULL);
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml_tree_prune_flagged(cxobj *xt,
|
||||
int flag,
|
||||
int test)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
cxobj *xprev;
|
||||
|
||||
x = NULL;
|
||||
xprev = x = NULL;
|
||||
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
|
||||
if (xml_flag(x, flag) == test?flag:0){ /* Pass test means purge */
|
||||
if (xml_purge(x) < 0)
|
||||
goto done;
|
||||
x = xprev;
|
||||
continue;
|
||||
}
|
||||
if (xml_tree_prune_flagged(x, flag, test) < 0)
|
||||
goto done;
|
||||
xprev = x;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Add default values (if not set)
|
||||
* @param[in] xt XML tree with some node marked
|
||||
*/
|
||||
|
|
@ -1293,10 +1333,6 @@ xml_sanity(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
name = xml_name(xt);
|
||||
if (ys==NULL){
|
||||
clicon_err(OE_XML, 0, "No spec for xml node %s", name);
|
||||
goto done;
|
||||
}
|
||||
if (strstr(ys->ys_argument, name)==NULL){
|
||||
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
|
||||
name, ys->ys_argument);
|
||||
|
|
@ -1307,6 +1343,69 @@ xml_sanity(cxobj *xt,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Mark all nodes that are not configure data and set return
|
||||
* @param[in] xt XML tree
|
||||
* @param[out] arg If set, set to 1 as int* if not config data
|
||||
*/
|
||||
int
|
||||
xml_non_config_data(cxobj *xt,
|
||||
void *arg) /* Set to 1 if state node */
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ys;
|
||||
|
||||
if ((ys = (yang_stmt*)xml_spec(xt)) == NULL){
|
||||
clicon_log(LOG_WARNING, "%s: no xml_spec(%s)", __FUNCTION__, xml_name(xt));
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
if (!yang_config(ys)){ /* config == false means state data: mark for remove */
|
||||
xml_flag_set(xt, XML_FLAG_MARK);
|
||||
if (arg)
|
||||
(*(int*)arg) = 1;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Add yang specification backpoint to XML node
|
||||
* @param[in] xt XML tree node
|
||||
* @note This should really be unnecessary since yspec should be set on creation
|
||||
* @code
|
||||
* xml_apply(xc, CX_ELMNT, xml_spec_populate, yspec)
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml_spec_populate(cxobj *x,
|
||||
void *arg)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_spec *yspec = (yang_spec*)arg;
|
||||
char *name;
|
||||
yang_stmt *y; /* yang node */
|
||||
cxobj *xp; /* xml parent */
|
||||
yang_stmt *yp; /* parent yang */
|
||||
|
||||
name = xml_name(x);
|
||||
if ((xp = xml_parent(x)) != NULL &&
|
||||
(yp = xml_spec(xp)) != NULL)
|
||||
y = yang_find_syntax((yang_node*)yp, xml_name(x));
|
||||
else
|
||||
y = yang_find_topnode(yspec, name); /* still NULL for config */
|
||||
if (y==NULL){
|
||||
clicon_err(OE_XML, EBADF, "yang spec not found for xml node '%s' xml parent name: '%s' yangspec:'%s']",
|
||||
name,
|
||||
xp?xml_name(xp):"", yp?yp->ys_argument:"");
|
||||
goto done;
|
||||
}
|
||||
xml_spec_set(x, y);
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Translate from restconf api-path in cvv form to xml xpath
|
||||
* eg a/b=c -> a/[b=c]
|
||||
* @param[in] yspec Yang spec
|
||||
|
|
@ -1618,83 +1717,5 @@ api_path2xml(char *api_path,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* See RFC 8040 Section 7: Mapping from NETCONF<error-tag> to Status Code
|
||||
* and RFC 6241 Appendix A. NETCONF Error list
|
||||
*/
|
||||
const map_str2int netconf_restconf_map[] = {
|
||||
{"in-use", 409},
|
||||
{"invalid-value", 400},
|
||||
{"invalid-value", 404},
|
||||
{"invalid-value", 406},
|
||||
{"too-big", 413}, /* request */
|
||||
{"too-big", 400}, /* response */
|
||||
{"missing-attribute", 400},
|
||||
{"bad-attribute", 400},
|
||||
{"unknown-attribute", 400},
|
||||
{"bad-element", 400},
|
||||
{"unknown-element", 400},
|
||||
{"unknown-namespace", 400},
|
||||
{"access-denied", 401},
|
||||
{"access-denied", 403},
|
||||
{"lock-denied", 409},
|
||||
{"resource-denied", 409},
|
||||
{"rollback-failed", 500},
|
||||
{"data-exists", 409},
|
||||
{"data-missing", 409},
|
||||
{"operation-not-supported",405},
|
||||
{"operation-not-supported",501},
|
||||
{"operation-failed", 412},
|
||||
{"operation-failed", 500},
|
||||
{"partial-operation", 500},
|
||||
{"malformed-message", 400},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
/* See 7231 Section 6.1
|
||||
*/
|
||||
const map_str2int http_reason_phrase_map[] = {
|
||||
{"Continue", 100},
|
||||
{"Switching Protocols", 101},
|
||||
{"OK", 200},
|
||||
{"Created", 201},
|
||||
{"Accepted", 202},
|
||||
{"Non-Authoritative Information", 203},
|
||||
{"No Content", 204},
|
||||
{"Reset Content", 205},
|
||||
{"Partial Content", 206},
|
||||
{"Multiple Choices", 300},
|
||||
{"Moved Permanently", 301},
|
||||
{"Found", 302},
|
||||
{"See Other", 303},
|
||||
{"Not Modified", 304},
|
||||
{"Use Proxy", 305},
|
||||
{"Temporary Redirect", 307},
|
||||
{"Bad Request", 400},
|
||||
{"Unauthorized", 401},
|
||||
{"Payment Required", 402},
|
||||
{"Forbidden", 403},
|
||||
{"Not Found", 404},
|
||||
{"Method Not Allowed", 405},
|
||||
{"Not Acceptable", 406},
|
||||
{"Proxy Authentication Required", 407},
|
||||
{"Request Timeout", 408},
|
||||
{"Conflict", 409},
|
||||
{"Gone", 410},
|
||||
{"Length Required", 411},
|
||||
{"Precondition Failed", 412},
|
||||
{"Payload Too Large", 413},
|
||||
{"URI Too Long", 414},
|
||||
{"Unsupported Media Type", 415},
|
||||
{"Range Not Satisfiable", 416},
|
||||
{"Expectation Failed", 417},
|
||||
{"Upgrade Required", 426},
|
||||
{"Internal Server Error", 500},
|
||||
{"Not Implemented", 501},
|
||||
{"Bad Gateway", 502},
|
||||
{"Service Unavailable", 503},
|
||||
{"Gateway Timeout", 504},
|
||||
{"HTTP Version Not Supported", 505},
|
||||
{NULL, -1}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ enum axis_type{
|
|||
};
|
||||
|
||||
/* Mapping between axis type string <--> int */
|
||||
static const map_str2int atmap[] = {
|
||||
static const map_str2int axismap[] = {
|
||||
{"self", A_SELF},
|
||||
{"child", A_CHILD},
|
||||
{"parent", A_PARENT},
|
||||
|
|
@ -163,7 +163,7 @@ xpath_print(FILE *f, struct xpath_element *xplist)
|
|||
struct xpath_predicate *xp;
|
||||
|
||||
for (xe=xplist; xe; xe=xe->xe_next){
|
||||
fprintf(f, "\t:%s %s ", clicon_int2str(atmap, xe->xe_type),
|
||||
fprintf(f, "\t:%s %s ", clicon_int2str(axismap, xe->xe_type),
|
||||
xe->xe_str?xe->xe_str:"");
|
||||
for (xp=xe->xe_predicate; xp; xp=xp->xp_next)
|
||||
fprintf(f, "[%s]", xp->xp_expr);
|
||||
|
|
@ -581,7 +581,7 @@ xpath_find(struct xpath_element *xe,
|
|||
}
|
||||
#if 0
|
||||
fprintf(stderr, "%s: %s: \"%s\"\n", __FUNCTION__,
|
||||
clicon_int2str(atmap, xe->xe_type), xe->xe_str?xe->xe_str:"");
|
||||
clicon_int2str(axismap, xe->xe_type), xe->xe_str?xe->xe_str:"");
|
||||
#endif
|
||||
switch (xe->xe_type){
|
||||
case A_SELF:
|
||||
|
|
|
|||
|
|
@ -103,12 +103,14 @@ static const map_str2int ytmap[] = {
|
|||
static int
|
||||
yang_builtin(char *type)
|
||||
{
|
||||
if (clicon_str2int(ytmap, type) != -1)
|
||||
return 1;
|
||||
#if 0
|
||||
const struct map_str2int *yt;
|
||||
|
||||
/* built-in types */
|
||||
for (yt = &ytmap[0]; yt->ms_str; yt++)
|
||||
if (strcmp(yt->ms_str, type) == 0)
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -86,6 +86,9 @@ expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><get-config><source><candidate/
|
|||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><discard-changes/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf edit state operation should fail"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><edit-config><target><candidate/></target><config><interfaces-state><interface><name>eth1</name><type>eth</type></interface></interfaces-state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf lock/unlock"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf" "<rpc><lock><target><candidate/></target></lock></rpc>]]>]]><rpc><unlock><target><candidate/></target></unlock></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]><rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
# For memcheck
|
||||
# clixon_netconf="valgrind --leak-check=full --show-leak-kinds=all clixon_netconf"
|
||||
clixon_netconf=clixon_netconf
|
||||
clixon_cli=clixon_cli
|
||||
|
||||
cat <<EOF > /tmp/test.yang
|
||||
module ietf-ip{
|
||||
|
|
@ -40,6 +41,12 @@ module ietf-ip{
|
|||
}
|
||||
}
|
||||
}
|
||||
container state {
|
||||
config false;
|
||||
leaf-list op {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
|
|
@ -74,6 +81,19 @@ expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><sourc
|
|||
new "netconf get leaf-list path"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get-config><source><candidate/></source><filter type=\"xpath\" select=\"/x/f[e=hej]\"/></get-config></rpc>]]>]]>" "^<rpc-reply><data><config><x><f><e>hej</e><e>hopp</e></f></x></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf get (state data XXX should be some)"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><get><source><candidate/></source><filter type=\"xpath\" select=\"/\"/></get></rpc>]]>]]>" "^<rpc-reply><data><config><x><y><a>1</a><b>2</b><c>5</c></y><d/></x></config></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "cli set leaf-list"
|
||||
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test set x f e foo" ""
|
||||
|
||||
new "cli show leaf-list"
|
||||
expectfn "$clixon_cli -1f $clixon_cf -y /tmp/test show xpath /x/f/e" "<e>foo</e>"
|
||||
|
||||
new "netconf set state data (not allowed)"
|
||||
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/test" "<rpc><edit-config><target><candidate/></target><config><state><op>42</op></state></config></edit-config></rpc>]]>]]>" "^<rpc-reply><rpc-error><error-tag>invalid-value"
|
||||
|
||||
|
||||
new "Kill backend"
|
||||
# Check if still alive
|
||||
pid=`pgrep clixon_backend`
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#!/bin/bash
|
||||
# Test5: datastore
|
||||
# Test5: datastore tests.
|
||||
# Just run a binary direct to datastore. No clixon.
|
||||
|
||||
# include err() and new() functions
|
||||
. ./lib.sh
|
||||
|
||||
datastore=datastore_client
|
||||
|
||||
|
||||
cat <<EOF > /tmp/ietf-ip.yang
|
||||
module ietf-ip{
|
||||
container x {
|
||||
|
|
@ -54,7 +54,7 @@ run(){
|
|||
rm -rf $dir/*
|
||||
|
||||
conf="-d candidate -b $dir -p ../datastore/$name/$name.so -y /tmp -m ietf-ip"
|
||||
# echo "conf:$conf"
|
||||
echo "conf:$conf"
|
||||
new "datastore $name init"
|
||||
expectfn "$datastore $conf init" ""
|
||||
|
||||
|
|
@ -139,8 +139,18 @@ run(){
|
|||
new "datastore $name create leaf"
|
||||
expectfn "$datastore $conf put create <config><x><y><a>1</a><b>3</b><c>newentry</c></y></x></config>"
|
||||
|
||||
new "datastore other db init"
|
||||
expectfn "$datastore -d kalle -b $dir -p ../datastore/$name/$name.so -y /tmp -m ietf-ip init"
|
||||
|
||||
new "datastore other db copy"
|
||||
expectfn "$datastore $conf copy kalle" ""
|
||||
|
||||
diff $dir/kalle_db $dir/candidate_db
|
||||
|
||||
new "datastore lock"
|
||||
expectfn "$datastore $conf lock 756" ""
|
||||
|
||||
#leaf-list
|
||||
|
||||
|
||||
rm -rf $dir
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue