Added state data

This commit is contained in:
Olof hagsand 2017-07-01 17:42:39 +02:00
parent 43c39160a5
commit f5c19d8586
24 changed files with 512 additions and 224 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}
/*!
*/

View file

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

View file

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