* Experimental NACM RFC8341 Network Configuration Access Control Model.
* CLICON_NACM_MODE config option, default is disabled. * Added username attribute to all rpc:s from frontend to backend * Added NACM backend module in example
This commit is contained in:
parent
04a3f4db1b
commit
7650803475
32 changed files with 908 additions and 274 deletions
28
CHANGELOG.md
28
CHANGELOG.md
|
|
@ -3,7 +3,11 @@
|
||||||
## 3.6.0 (Upcoming)
|
## 3.6.0 (Upcoming)
|
||||||
|
|
||||||
### Major changes:
|
### Major changes:
|
||||||
* Restructure and more generic plugin API (cli,backend,restconf,netconf) as preparation for authorization RFC8341
|
* Experimental NACM RFC8341 Network Configuration Access Control Model.
|
||||||
|
* CLICON_NACM_MODE config option, default is disabled.
|
||||||
|
* Added username attribute to all rpc:s from frontend to backend
|
||||||
|
* Added NACM backend module in example
|
||||||
|
* Restructure and more generic plugin API (cli,backend,restconf,netconf).
|
||||||
* New design change `plugin_init()` to a single `clixon_plugin_init()` returning an api struct with function pointers, see example below. This means that there are no hardcoded plugin functions, except `clixon_plugin_init()`.
|
* New design change `plugin_init()` to a single `clixon_plugin_init()` returning an api struct with function pointers, see example below. This means that there are no hardcoded plugin functions, except `clixon_plugin_init()`.
|
||||||
* Plugin RPC callback interface have been unified between backend, netconf and restconf.
|
* Plugin RPC callback interface have been unified between backend, netconf and restconf.
|
||||||
* Backend RPC register callback function (Netconf RPC or restconf operation POST) has been changed from: `backend_rpc_cb_register()` to `rpc_callback_register()`
|
* Backend RPC register callback function (Netconf RPC or restconf operation POST) has been changed from: `backend_rpc_cb_register()` to `rpc_callback_register()`
|
||||||
|
|
@ -11,6 +15,7 @@
|
||||||
* Frontend netconf and restconf plugins can register callbacks as well with same API as backends.
|
* Frontend netconf and restconf plugins can register callbacks as well with same API as backends.
|
||||||
* Master plugins have been removed. Plugins are loaded alphabetically. You can ensure plugin load order by prefixing them with an ordering number, for example.
|
* Master plugins have been removed. Plugins are loaded alphabetically. You can ensure plugin load order by prefixing them with an ordering number, for example.
|
||||||
* Moved specific plugin functions from apps/ to generic functions in lib/
|
* Moved specific plugin functions from apps/ to generic functions in lib/
|
||||||
|
* New config option CLICON_BACKEND_REGEXP to match backkend plugins (if you do not all loaded).
|
||||||
* Added authentication plugin callback (ca_auth)
|
* Added authentication plugin callback (ca_auth)
|
||||||
* Added clicon_username_get() / clicon_username_set()
|
* Added clicon_username_get() / clicon_username_set()
|
||||||
* Removed some obscure plugin code that seem not to be used (please report if needed!)
|
* Removed some obscure plugin code that seem not to be used (please report if needed!)
|
||||||
|
|
@ -40,22 +45,11 @@ plugin_init(clicon_handle h)
|
||||||
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
static clixon_plugin_api api = {
|
static clixon_plugin_api api = {
|
||||||
"example", /* name */
|
"example", /* name */
|
||||||
clixon_plugin_init,
|
clixon_plugin_init, /* init */
|
||||||
plugin_start,
|
NULL, /* start */
|
||||||
plugin_exit,
|
NULL, /* exit */
|
||||||
NULL, /* auth N/A for backend */
|
.ca_auth=plugin_credentials /* restconf specific: auth */
|
||||||
NULL, /* cli_prompthook_t */
|
|
||||||
NULL, /* cligen_susp_cb_t */
|
|
||||||
NULL, /* cligen_interrupt_cb_t */
|
|
||||||
plugin_reset,
|
|
||||||
plugin_statedata,
|
|
||||||
transaction_begin,
|
|
||||||
transaction_validate,
|
|
||||||
transaction_complete,
|
|
||||||
transaction_commit,
|
|
||||||
transaction_end,
|
|
||||||
transaction_abort
|
|
||||||
};
|
};
|
||||||
|
|
||||||
clixon_plugin_api *clixon_plugin_init(clicon_handle h)
|
clixon_plugin_api *clixon_plugin_init(clicon_handle h)
|
||||||
|
|
|
||||||
|
|
@ -347,7 +347,7 @@ from_client_edit_config(clicon_handle h,
|
||||||
cbuf *cbx = NULL; /* Assist cbuf */
|
cbuf *cbx = NULL; /* Assist cbuf */
|
||||||
|
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec9");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if ((target = netconf_db_find(xn, "target")) == NULL){
|
if ((target = netconf_db_find(xn, "target")) == NULL){
|
||||||
|
|
@ -803,7 +803,253 @@ from_client_debug(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Match nacm access operations according to RFC8321 3.4.4.
|
||||||
|
* Incoming RPC Message Validation Step 7 (c)
|
||||||
|
* The rule's "access-operations" leaf has the "exec" bit set or
|
||||||
|
* has the special value "*".
|
||||||
|
* @retval 0 No match
|
||||||
|
* @retval 1 Match
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
nacm_match_access(char *access_operations,
|
||||||
|
char *mode)
|
||||||
|
{
|
||||||
|
if (access_operations==NULL)
|
||||||
|
return 0;
|
||||||
|
if (strcmp(access_operations,"*")==0)
|
||||||
|
return 1;
|
||||||
|
if (strstr(mode, access_operations)!=NULL)
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Match nacm single rule. Either match with access or deny. Or not match.
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] name rpc name
|
||||||
|
* @param[in] xrule NACM rule XML tree
|
||||||
|
* @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
|
||||||
|
* @retval -1 Error
|
||||||
|
* @retval 0 Matching rule AND Not access and cbret set
|
||||||
|
* @retval 1 Matchung rule AND Access
|
||||||
|
* @retval 2 No matching rule Goto step 10
|
||||||
|
* From RFC8321 3.4.4. Incoming RPC Message Validation
|
||||||
|
+---------+-----------------+---------------------+-----------------+
|
||||||
|
| Method | Resource class | NETCONF operation | Access |
|
||||||
|
| | | | operation |
|
||||||
|
+---------+-----------------+---------------------+-----------------+
|
||||||
|
| OPTIONS | all | none | none |
|
||||||
|
| HEAD | all | <get>, <get-config> | read |
|
||||||
|
| GET | all | <get>, <get-config> | read |
|
||||||
|
| POST | datastore, data | <edit-config> | create |
|
||||||
|
| POST | operation | specified operation | execute |
|
||||||
|
| PUT | data | <edit-config> | create, update |
|
||||||
|
| PUT | datastore | <copy-config> | update |
|
||||||
|
| PATCH | data, datastore | <edit-config> | update |
|
||||||
|
| DELETE | data | <edit-config> | delete |
|
||||||
|
|
||||||
|
7.(cont) A rule matches if all of the following criteria are met:
|
||||||
|
* The rule's "module-name" leaf is "*" or equals the name of
|
||||||
|
the YANG module where the protocol operation is defined.
|
||||||
|
|
||||||
|
* Either (1) the rule does not have a "rule-type" defined or
|
||||||
|
(2) the "rule-type" is "protocol-operation" and the
|
||||||
|
"rpc-name" is "*" or equals the name of the requested
|
||||||
|
protocol operation.
|
||||||
|
|
||||||
|
* The rule's "access-operations" leaf has the "exec" bit set or
|
||||||
|
has the special value "*".
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
nacm_match_rule(clicon_handle h,
|
||||||
|
char *name,
|
||||||
|
cxobj *xrule,
|
||||||
|
cbuf *cbret)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
// cxobj *x;
|
||||||
|
char *module_name;
|
||||||
|
char *rpc_name;
|
||||||
|
char *access_operations;
|
||||||
|
char *action;
|
||||||
|
|
||||||
|
module_name = xml_find_body(xrule, "module-name");
|
||||||
|
rpc_name = xml_find_body(xrule, "rpc-name");
|
||||||
|
access_operations = xml_find_body(xrule, "access-operations");
|
||||||
|
action = xml_find_body(xrule, "action");
|
||||||
|
clicon_debug(1, "%s: %s %s %s %s", __FUNCTION__,
|
||||||
|
module_name, rpc_name, access_operations, action);
|
||||||
|
if (module_name && strcmp(module_name,"*")==0){
|
||||||
|
if (nacm_match_access(access_operations, "exec")){
|
||||||
|
if (rpc_name==NULL ||
|
||||||
|
strcmp(rpc_name, "*")==0 || strcmp(rpc_name, name)==0){
|
||||||
|
/* Here is a matching rule */
|
||||||
|
if (action && strcmp(action, "permit")==0){
|
||||||
|
retval = 1;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if (netconf_access_denied(cbret, "protocol", "access denied") < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
retval = 2; /* no matching rule */
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! Make nacm access control
|
||||||
|
* @param[in] h Clicon handle
|
||||||
|
* @param[in] name rpc name
|
||||||
|
* @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
|
||||||
|
* @retval -1 Error
|
||||||
|
* @retval 0 Not access and cbret set
|
||||||
|
* @retval 1 Access
|
||||||
|
* From RFC8321 3.4.4. Incoming RPC Message Validation
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
nacm_access(clicon_handle h,
|
||||||
|
char *name,
|
||||||
|
char *username,
|
||||||
|
cbuf *cbret)
|
||||||
|
{
|
||||||
|
int retval = -1;
|
||||||
|
cxobj *xtop = NULL;
|
||||||
|
cxobj *xacm;
|
||||||
|
cxobj *x;
|
||||||
|
cxobj *xrlist;
|
||||||
|
cxobj *xrule;
|
||||||
|
char *enabled = NULL;
|
||||||
|
cxobj **gvec = NULL; /* groups */
|
||||||
|
size_t glen;
|
||||||
|
cxobj **rlistvec = NULL; /* rule-list */
|
||||||
|
size_t rlistlen;
|
||||||
|
cxobj **rvec = NULL; /* rules */
|
||||||
|
size_t rlen;
|
||||||
|
int i, j;
|
||||||
|
char *exec_default = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
/* 1. If the "enable-nacm" leaf is set to "false", then the protocol
|
||||||
|
operation is permitted. (or config does not exist) */
|
||||||
|
if (xmldb_get(h, "running", "nacm", 0, &xtop) < 0)
|
||||||
|
goto done;
|
||||||
|
if ((xacm = xpath_first(xtop, "nacm")) == NULL)
|
||||||
|
goto permit;
|
||||||
|
exec_default = xml_find_body(xacm, "exec-default");
|
||||||
|
if ((x = xpath_first(xacm, "enable-nacm")) == NULL)
|
||||||
|
goto permit;
|
||||||
|
enabled = xml_body(x);
|
||||||
|
if (strcmp(enabled, "true") != 0)
|
||||||
|
goto permit;
|
||||||
|
|
||||||
|
/* 2. If the requesting session is identified as a recovery session,
|
||||||
|
then the protocol operation is permitted. NYI */
|
||||||
|
|
||||||
|
/* 3. If the requested operation is the NETCONF <close-session>
|
||||||
|
protocol operation, then the protocol operation is permitted.
|
||||||
|
*/
|
||||||
|
if (strcmp(name, "close-session") == 0)
|
||||||
|
goto permit;
|
||||||
|
/* 4. Check all the "group" entries to see if any of them contain a
|
||||||
|
"user-name" entry that equals the username for the session
|
||||||
|
making the request. (If the "enable-external-groups" leaf is
|
||||||
|
"true", add to these groups the set of groups provided by the
|
||||||
|
transport layer.) */
|
||||||
|
if (username == NULL)
|
||||||
|
goto step10;
|
||||||
|
/* User's group */
|
||||||
|
if (xpath_vec(xacm, "groups/group[user-name=%s]", &gvec, &glen, username) < 0)
|
||||||
|
goto done;
|
||||||
|
/* 5. If no groups are found, continue with step 10. */
|
||||||
|
if (glen == 0)
|
||||||
|
goto step10;
|
||||||
|
/* 6. Process all rule-list entries, in the order they appear in the
|
||||||
|
configuration. If a rule-list's "group" leaf-list does not
|
||||||
|
match any of the user's groups, proceed to the next rule-list
|
||||||
|
entry. */
|
||||||
|
if (xpath_vec(xacm, "rule-list", &rlistvec, &rlistlen) < 0)
|
||||||
|
goto done;
|
||||||
|
for (i=0; i<rlistlen; i++){
|
||||||
|
xrlist = rlistvec[i];
|
||||||
|
/* Loop through user's group to find match in this rule-list */
|
||||||
|
for (j=0; j<glen; j++){
|
||||||
|
char *gname;
|
||||||
|
gname = xml_find_body(gvec[j], "name");
|
||||||
|
if (xpath_first(xrlist,".[group=%s]", gname)!=NULL)
|
||||||
|
break; /* found */
|
||||||
|
}
|
||||||
|
if (j==glen) /* not found */
|
||||||
|
continue;
|
||||||
|
/* 7. For each rule-list entry found, process all rules, in order,
|
||||||
|
until a rule that matches the requested access operation is
|
||||||
|
found.
|
||||||
|
*/
|
||||||
|
if (xpath_vec(xrlist, "rule", &rvec, &rlen) < 0)
|
||||||
|
goto done;
|
||||||
|
for (j=0; j<rlen; j++){
|
||||||
|
xrule = rvec[j];
|
||||||
|
/* -1 error, 0 deny, 1 permit, 2 continue */
|
||||||
|
if ((ret = nacm_match_rule(h, name, xrule, cbret)) < 0)
|
||||||
|
goto done;
|
||||||
|
switch(ret){
|
||||||
|
case 0: /* deny */
|
||||||
|
goto deny;
|
||||||
|
break;
|
||||||
|
case 1: /* permit */
|
||||||
|
goto permit;
|
||||||
|
break;
|
||||||
|
case 2: /* no match, continue */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step10:
|
||||||
|
/* 10. If the requested protocol operation is defined in a YANG module
|
||||||
|
advertised in the server capabilities and the "rpc" statement
|
||||||
|
contains a "nacm:default-deny-all" statement, then the protocol
|
||||||
|
operation is denied. */
|
||||||
|
/* 11. If the requested protocol operation is the NETCONF
|
||||||
|
<kill-session> or <delete-config>, then the protocol operation
|
||||||
|
is denied. */
|
||||||
|
if (strcmp(name, "kill-session")==0 || strcmp(name, "delete-config")==0){
|
||||||
|
if (netconf_access_denied(cbret, "protocol", "default deny") < 0)
|
||||||
|
goto done;
|
||||||
|
goto deny;
|
||||||
|
}
|
||||||
|
/* 12. If the "exec-default" leaf is set to "permit", then permit the
|
||||||
|
protocol operation; otherwise, deny the request. */
|
||||||
|
if (exec_default ==NULL || strcmp(exec_default, "permit")==0)
|
||||||
|
goto permit;
|
||||||
|
if (netconf_access_denied(cbret, "protocol", "default deny") < 0)
|
||||||
|
goto done;
|
||||||
|
goto deny;
|
||||||
|
permit:
|
||||||
|
retval = 1;
|
||||||
|
done:
|
||||||
|
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
|
||||||
|
if (xtop)
|
||||||
|
xml_free(xtop);
|
||||||
|
if (gvec)
|
||||||
|
free(gvec);
|
||||||
|
if (rlistvec)
|
||||||
|
free(rlistvec);
|
||||||
|
if (rvec)
|
||||||
|
free(rvec);
|
||||||
|
return retval;
|
||||||
|
deny: /* Here, cbret must contain a netconf error msg */
|
||||||
|
assert(cbuf_len(cbret));
|
||||||
|
retval = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/*! An internal clicon message has arrived from a client. Receive and dispatch.
|
/*! An internal clicon message has arrived from a client. Receive and dispatch.
|
||||||
|
* @param[in] h Clicon handle
|
||||||
* @param[in] s Socket where message arrived. read from this.
|
* @param[in] s Socket where message arrived. read from this.
|
||||||
* @param[in] arg Client entry (from).
|
* @param[in] arg Client entry (from).
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
|
|
@ -824,7 +1070,10 @@ from_client_msg(clicon_handle h,
|
||||||
cbuf *cbret = NULL; /* return message */
|
cbuf *cbret = NULL; /* return message */
|
||||||
int pid;
|
int pid;
|
||||||
int ret;
|
int ret;
|
||||||
|
char *username;
|
||||||
|
char *nacm_mode;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
pid = ce->ce_pid;
|
pid = ce->ce_pid;
|
||||||
/* Return netconf message. Should be filled in by the dispatch(sub) functions
|
/* Return netconf message. Should be filled in by the dispatch(sub) functions
|
||||||
* as wither rpc-error or by positive response.
|
* as wither rpc-error or by positive response.
|
||||||
|
|
@ -844,8 +1093,19 @@ from_client_msg(clicon_handle h,
|
||||||
goto reply;
|
goto reply;
|
||||||
}
|
}
|
||||||
xe = NULL;
|
xe = NULL;
|
||||||
|
username = xml_find_value(x, "username");
|
||||||
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
|
while ((xe = xml_child_each(x, xe, CX_ELMNT)) != NULL) {
|
||||||
name = xml_name(xe);
|
name = xml_name(xe);
|
||||||
|
clicon_debug(1, "%s name:%s", __FUNCTION__, name);
|
||||||
|
#if 1 /* NACM */
|
||||||
|
/* Make NACM access control if enabled as "internal"*/
|
||||||
|
nacm_mode = clicon_option_str(h, "CLICON_NACM_MODE");
|
||||||
|
if (nacm_mode && strcmp(nacm_mode,"internal") == 0)
|
||||||
|
if ((ret = nacm_access(h, name, username, cbret)) < 0)
|
||||||
|
goto done;
|
||||||
|
if (!ret)
|
||||||
|
goto reply;
|
||||||
|
#endif
|
||||||
if (strcmp(name, "get-config") == 0){
|
if (strcmp(name, "get-config") == 0){
|
||||||
if (from_client_get_config(h, xe, cbret) <0)
|
if (from_client_get_config(h, xe, cbret) <0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -947,6 +1207,7 @@ from_client_msg(clicon_handle h,
|
||||||
// ok:
|
// ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
if (xt)
|
if (xt)
|
||||||
xml_free(xt);
|
xml_free(xt);
|
||||||
if (cbret)
|
if (cbret)
|
||||||
|
|
@ -976,6 +1237,7 @@ from_client(int s,
|
||||||
clicon_handle h = ce->ce_handle;
|
clicon_handle h = ce->ce_handle;
|
||||||
int eof;
|
int eof;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
// assert(s == ce->ce_s);
|
// assert(s == ce->ce_s);
|
||||||
if (clicon_msg_rcv(ce->ce_s, &msg, &eof) < 0)
|
if (clicon_msg_rcv(ce->ce_s, &msg, &eof) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,7 @@ startup_mode_none(clicon_handle h)
|
||||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Load plugins and call plugin_init() */
|
/* Load plugins and call plugin_init() */
|
||||||
if (plugin_initiate(h) != 0)
|
if (backend_plugin_initiate(h) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -328,7 +328,7 @@ startup_mode_init(clicon_handle h)
|
||||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Load plugins and call plugin_init() */
|
/* Load plugins and call plugin_init() */
|
||||||
if (plugin_initiate(h) != 0)
|
if (backend_plugin_initiate(h) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -364,7 +364,7 @@ startup_mode_running(clicon_handle h,
|
||||||
if (xmldb_copy(h, "running", "candidate") < 0)
|
if (xmldb_copy(h, "running", "candidate") < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Load plugins and call plugin_init() */
|
/* Load plugins and call plugin_init() */
|
||||||
if (plugin_initiate(h) != 0)
|
if (backend_plugin_initiate(h) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Clear tmp db */
|
/* Clear tmp db */
|
||||||
if (db_reset(h, "tmp") < 0)
|
if (db_reset(h, "tmp") < 0)
|
||||||
|
|
@ -437,7 +437,7 @@ startup_mode_startup(clicon_handle h,
|
||||||
if (xmldb_create(h, "startup") < 0) /* diff */
|
if (xmldb_create(h, "startup") < 0) /* diff */
|
||||||
return -1;
|
return -1;
|
||||||
/* Load plugins and call plugin_init() */
|
/* Load plugins and call plugin_init() */
|
||||||
if (plugin_initiate(h) != 0)
|
if (backend_plugin_initiate(h) != 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Clear tmp db */
|
/* Clear tmp db */
|
||||||
if (db_reset(h, "tmp") < 0)
|
if (db_reset(h, "tmp") < 0)
|
||||||
|
|
@ -475,7 +475,8 @@ startup_mode_startup(clicon_handle h,
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc,
|
||||||
|
char **argv)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char c;
|
char c;
|
||||||
|
|
@ -497,14 +498,12 @@ main(int argc, char **argv)
|
||||||
int xml_cache;
|
int xml_cache;
|
||||||
int xml_pretty;
|
int xml_pretty;
|
||||||
char *xml_format;
|
char *xml_format;
|
||||||
|
|
||||||
/* In the startup, logs to stderr & syslog and debug flag set later */
|
/* In the startup, logs to stderr & syslog and debug flag set later */
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR|CLICON_LOG_SYSLOG);
|
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR|CLICON_LOG_SYSLOG);
|
||||||
/* Initiate CLICON handle */
|
/* Initiate CLICON handle */
|
||||||
if ((h = backend_handle_init()) == NULL)
|
if ((h = backend_handle_init()) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
if (backend_plugin_init(h) != 0)
|
|
||||||
return -1;
|
|
||||||
foreground = 0;
|
foreground = 0;
|
||||||
once = 0;
|
once = 0;
|
||||||
zap = 0;
|
zap = 0;
|
||||||
|
|
|
||||||
|
|
@ -64,31 +64,21 @@
|
||||||
#include "backend_plugin.h"
|
#include "backend_plugin.h"
|
||||||
#include "backend_commit.h"
|
#include "backend_commit.h"
|
||||||
|
|
||||||
/*! Initialize plugin code (not the plugins themselves)
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
* @retval 0 OK
|
|
||||||
* @retval -1 Error
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
backend_plugin_init(clicon_handle h)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Load a plugin group.
|
/*! Load a plugin group.
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
plugin_initiate(clicon_handle h)
|
backend_plugin_initiate(clicon_handle h)
|
||||||
{
|
{
|
||||||
char *dir;
|
char *dir;
|
||||||
|
|
||||||
/* Load application plugins */
|
/* Load application plugins */
|
||||||
if ((dir = clicon_backend_dir(h)) == NULL)
|
if ((dir = clicon_backend_dir(h)) == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
return clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir);
|
return clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir,
|
||||||
|
clicon_option_str(h, "CLICON_BACKEND_REGEXP"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Request plugins to reset system state
|
/*! Request plugins to reset system state
|
||||||
|
|
@ -124,6 +114,7 @@ clixon_plugin_reset(clicon_handle h,
|
||||||
* @param[in] h clicon handle
|
* @param[in] h clicon handle
|
||||||
* @param[in] xpath String with XPATH syntax. or NULL for all
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||||
* @param[in,out] xml XML tree.
|
* @param[in,out] xml XML tree.
|
||||||
|
* @param[out] cbret Return xml value cligen buffer
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval 1 Statedata callback failed
|
* @retval 1 Statedata callback failed
|
||||||
|
|
@ -139,8 +130,10 @@ clixon_plugin_statedata(clicon_handle h,
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
cxobj **xvec = NULL;
|
cxobj **xvec = NULL;
|
||||||
size_t xlen;
|
size_t xlen;
|
||||||
|
cxobj *xc;
|
||||||
clixon_plugin *cp = NULL;
|
clixon_plugin *cp = NULL;
|
||||||
plgstatedata_t *fn; /* Plugin statedata fn */
|
plgstatedata_t *fn; /* Plugin statedata fn */
|
||||||
|
char *reason = NULL;
|
||||||
|
|
||||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
||||||
|
|
@ -159,8 +152,23 @@ clixon_plugin_statedata(clicon_handle h,
|
||||||
retval = 1;
|
retval = 1;
|
||||||
goto done; /* Dont quit here on user callbacks */
|
goto done; /* Dont quit here on user callbacks */
|
||||||
}
|
}
|
||||||
if (xml_merge(xtop, x, yspec) < 0)
|
if (xml_merge(xtop, x, yspec, &reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (reason){
|
||||||
|
cbuf *cb;
|
||||||
|
if ((cb = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (netconf_operation_failed(cb, "rpc", reason)< 0)
|
||||||
|
goto done;
|
||||||
|
while ((xc = xml_child_i(xtop, 0)) != NULL)
|
||||||
|
xml_purge(xc);
|
||||||
|
if (xml_parse_string(cbuf_get(cb), NULL, &xtop) < 0)
|
||||||
|
goto done;
|
||||||
|
cbuf_free(cb);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (x){
|
if (x){
|
||||||
xml_free(x);
|
xml_free(x);
|
||||||
x = NULL;
|
x = NULL;
|
||||||
|
|
@ -187,6 +195,8 @@ clixon_plugin_statedata(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (reason)
|
||||||
|
free(reason);
|
||||||
if (x)
|
if (x)
|
||||||
xml_free(x);
|
xml_free(x);
|
||||||
if (xvec)
|
if (xvec)
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,7 @@ typedef struct {
|
||||||
/*
|
/*
|
||||||
* Prototypes
|
* Prototypes
|
||||||
*/
|
*/
|
||||||
int backend_plugin_init(clicon_handle h);
|
int backend_plugin_initiate(clicon_handle h);
|
||||||
int plugin_initiate(clicon_handle h);
|
|
||||||
|
|
||||||
int clixon_plugin_reset(clicon_handle h, char *db);
|
int clixon_plugin_reset(clicon_handle h, char *db);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <pwd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
|
|
@ -243,17 +244,24 @@ main(int argc, char **argv)
|
||||||
char *restarg = NULL; /* what remains after options */
|
char *restarg = NULL; /* what remains after options */
|
||||||
int dump_configfile_xml = 0;
|
int dump_configfile_xml = 0;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
|
struct passwd *pw;
|
||||||
|
|
||||||
/* Defaults */
|
/* Defaults */
|
||||||
|
once = 0;
|
||||||
|
|
||||||
/* In the startup, logs to stderr & debug flag set later */
|
/* In the startup, logs to stderr & debug flag set later */
|
||||||
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
|
clicon_log_init(__PROGRAM__, LOG_INFO, logdst);
|
||||||
/* Initiate CLICON handle */
|
/* Initiate CLICON handle */
|
||||||
if ((h = cli_handle_init()) == NULL)
|
if ((h = cli_handle_init()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Set username to clicon handle. Use in all communication to backend */
|
||||||
if (cli_plugin_init(h) != 0)
|
if ((pw = getpwuid(getuid())) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "getpwuid");
|
||||||
goto done;
|
goto done;
|
||||||
once = 0;
|
}
|
||||||
|
if (clicon_username_set(h, pw->pw_name) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
cligen_comment_set(cli_cligen(h), '#'); /* Default to handle #! clicon_cli scripts */
|
cligen_comment_set(cli_cligen(h), '#'); /* Default to handle #! clicon_cli scripts */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -346,7 +346,7 @@ cli_syntax_load (clicon_handle h)
|
||||||
|
|
||||||
/* Load cli plugins */
|
/* Load cli plugins */
|
||||||
if (plugin_dir &&
|
if (plugin_dir &&
|
||||||
clixon_plugins_load(h, CLIXON_PLUGIN_INIT, plugin_dir)< 0)
|
clixon_plugins_load(h, CLIXON_PLUGIN_INIT, plugin_dir, NULL)< 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (clispec_file){
|
if (clispec_file){
|
||||||
if (cli_load_syntax(h, clispec_file, NULL) < 0)
|
if (cli_load_syntax(h, clispec_file, NULL) < 0)
|
||||||
|
|
@ -606,15 +606,6 @@ clicon_cliread(clicon_handle h)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Initialize plugin code (not the plugins themselves)
|
|
||||||
* @param[in] h Clicon handle
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
cli_plugin_init(clicon_handle h)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* CLI PLUGIN INTERFACE, PUBLIC SECTION
|
* CLI PLUGIN INTERFACE, PUBLIC SECTION
|
||||||
|
|
|
||||||
|
|
@ -63,8 +63,6 @@ typedef struct {
|
||||||
|
|
||||||
void *clixon_str2fn(char *name, void *handle, char **error);
|
void *clixon_str2fn(char *name, void *handle, char **error);
|
||||||
|
|
||||||
int cli_plugin_init(clicon_handle h);
|
|
||||||
|
|
||||||
int clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr);
|
int clicon_eval(clicon_handle h, char *cmd, cg_obj *match_obj, cvec *vr);
|
||||||
|
|
||||||
int clicon_parse(clicon_handle h, char *cmd, char **mode, int *result);
|
int clicon_parse(clicon_handle h, char *cmd, char **mode, int *result);
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,8 @@ expand_dbvar(void *h,
|
||||||
yang_stmt *ypath;
|
yang_stmt *ypath;
|
||||||
cxobj *xcur;
|
cxobj *xcur;
|
||||||
char *xpathcur;
|
char *xpathcur;
|
||||||
|
char *reason = NULL;
|
||||||
|
|
||||||
if (argv == NULL || cvec_len(argv) != 2){
|
if (argv == NULL || cvec_len(argv) != 2){
|
||||||
clicon_err(OE_PLUGIN, 0, "%s: requires arguments: <db> <xmlkeyfmt>",
|
clicon_err(OE_PLUGIN, 0, "%s: requires arguments: <db> <xmlkeyfmt>",
|
||||||
__FUNCTION__);
|
__FUNCTION__);
|
||||||
|
|
@ -190,8 +191,12 @@ expand_dbvar(void *h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
xpathcur = ypath->ys_argument;
|
xpathcur = ypath->ys_argument;
|
||||||
if (xml_merge(xt, xtop, yspec) < 0) /* Merge xtop into xt */
|
if (xml_merge(xt, xtop, yspec, &reason) < 0) /* Merge xtop into xt */
|
||||||
goto done;
|
goto done;
|
||||||
|
if (reason){
|
||||||
|
cli_output(stderr, "%s\n", reason);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if ((xcur = xpath_first(xt, xpath)) == NULL){
|
if ((xcur = xpath_first(xt, xpath)) == NULL){
|
||||||
clicon_err(OE_DB, 0, "xpath %s should return merged content", xpath);
|
clicon_err(OE_DB, 0, "xpath %s should return merged content", xpath);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -241,6 +246,8 @@ expand_dbvar(void *h,
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (reason)
|
||||||
|
free(reason);
|
||||||
if (api_path)
|
if (api_path)
|
||||||
free(api_path);
|
free(api_path);
|
||||||
if (xvec)
|
if (xvec)
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <pwd.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
|
|
||||||
|
|
@ -309,7 +310,8 @@ main(int argc,
|
||||||
clicon_handle h;
|
clicon_handle h;
|
||||||
int use_syslog;
|
int use_syslog;
|
||||||
char *dir;
|
char *dir;
|
||||||
|
struct passwd *pw;
|
||||||
|
|
||||||
/* Defaults */
|
/* Defaults */
|
||||||
use_syslog = 0;
|
use_syslog = 0;
|
||||||
|
|
||||||
|
|
@ -319,6 +321,14 @@ main(int argc,
|
||||||
if ((h = clicon_handle_init()) == NULL)
|
if ((h = clicon_handle_init()) == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
/* Set username to clicon handle. Use in all communication to backend */
|
||||||
|
if ((pw = getpwuid(getuid())) == NULL){
|
||||||
|
clicon_err(OE_UNIX, errno, "getpwuid");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (clicon_username_set(h, pw->pw_name) < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1)
|
while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1)
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'h' : /* help */
|
case 'h' : /* help */
|
||||||
|
|
@ -376,6 +386,8 @@ main(int argc,
|
||||||
argc -= optind;
|
argc -= optind;
|
||||||
argv += optind;
|
argv += optind;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Parse yang database spec file */
|
/* Parse yang database spec file */
|
||||||
if (yang_spec_main(h) == NULL)
|
if (yang_spec_main(h) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -386,7 +398,7 @@ main(int argc,
|
||||||
|
|
||||||
/* Initialize plugins group */
|
/* Initialize plugins group */
|
||||||
if ((dir = clicon_netconf_dir(h)) != NULL)
|
if ((dir = clicon_netconf_dir(h)) != NULL)
|
||||||
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir) < 0)
|
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
/* Call start function is all plugins before we go interactive */
|
/* Call start function is all plugins before we go interactive */
|
||||||
|
|
|
||||||
|
|
@ -212,8 +212,10 @@ notfound(FCGX_Request *r)
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
||||||
FCGX_FPrintF(r->out, "Status: 404\r\n"); /* 404 not found */
|
FCGX_FPrintF(r->out, "Status: 404\r\n"); /* 404 not found */
|
||||||
|
|
||||||
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||||
FCGX_FPrintF(r->out, "<h1>Not Found</h1>\n");
|
FCGX_FPrintF(r->out, "<h1>Not Found</h1>\n");
|
||||||
|
FCGX_FPrintF(r->out, "Not Found\n");
|
||||||
FCGX_FPrintF(r->out, "The requested URL %s was not found on this server.\n",
|
FCGX_FPrintF(r->out, "The requested URL %s was not found on this server.\n",
|
||||||
path);
|
path);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -409,8 +411,8 @@ api_return_err(clicon_handle h,
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
if ((cb = cbuf_new()) == NULL)
|
if ((cb = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xtag = xpath_first(xerr, "error-tag")) == NULL){
|
if ((xtag = xpath_first(xerr, "//error-tag")) == NULL){
|
||||||
notfound(r); /* bad reply? */
|
notfound(r);
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
tagstr = xml_body(xtag);
|
tagstr = xml_body(xtag);
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,9 @@
|
||||||
* @param[in] pi Offset, where to start pcvec
|
* @param[in] pi Offset, where to start pcvec
|
||||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] dvec Stream input daat
|
* @param[in] dvec Stream input daat
|
||||||
|
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||||
|
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||||
|
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_data(clicon_handle h,
|
api_data(clicon_handle h,
|
||||||
|
|
@ -100,28 +103,17 @@ api_data(clicon_handle h,
|
||||||
cvec *pcvec,
|
cvec *pcvec,
|
||||||
int pi,
|
int pi,
|
||||||
cvec *qvec,
|
cvec *qvec,
|
||||||
char *data)
|
char *data,
|
||||||
|
int pretty,
|
||||||
|
int use_xml,
|
||||||
|
int parse_xml)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *request_method;
|
char *request_method;
|
||||||
int pretty;
|
|
||||||
char *media_content_type;
|
|
||||||
int parse_xml = 0; /* By default expect and parse JSON */
|
|
||||||
char *media_accept;
|
|
||||||
int use_xml = 0; /* By default use JSON */
|
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
|
||||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
|
||||||
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
|
||||||
use_xml++;
|
|
||||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
|
||||||
if (media_content_type &&
|
|
||||||
strcmp(media_content_type, "application/yang-data+xml")==0)
|
|
||||||
parse_xml++;
|
|
||||||
|
|
||||||
if (strcmp(request_method, "OPTIONS")==0)
|
if (strcmp(request_method, "OPTIONS")==0)
|
||||||
retval = api_data_options(h, r);
|
retval = api_data_options(h, r);
|
||||||
else if (strcmp(request_method, "HEAD")==0)
|
else if (strcmp(request_method, "HEAD")==0)
|
||||||
|
|
@ -150,6 +142,7 @@ api_data(clicon_handle h,
|
||||||
* @param[in] pi Offset, where to start pcvec
|
* @param[in] pi Offset, where to start pcvec
|
||||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||||
* @param[in] data Stream input data
|
* @param[in] data Stream input data
|
||||||
|
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
api_operations(clicon_handle h,
|
api_operations(clicon_handle h,
|
||||||
|
|
@ -158,28 +151,17 @@ api_operations(clicon_handle h,
|
||||||
cvec *pcvec,
|
cvec *pcvec,
|
||||||
int pi,
|
int pi,
|
||||||
cvec *qvec,
|
cvec *qvec,
|
||||||
char *data)
|
char *data,
|
||||||
|
int pretty,
|
||||||
|
int use_xml,
|
||||||
|
int parse_xml)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *request_method;
|
char *request_method;
|
||||||
int pretty;
|
|
||||||
char *media_content_type;
|
|
||||||
int parse_xml = 0; /* By default expect and parse JSON */
|
|
||||||
char *media_accept;
|
|
||||||
int use_xml = 0; /* By default use JSON */
|
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
|
||||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
|
||||||
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
|
||||||
use_xml++;
|
|
||||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
|
||||||
if (media_content_type &&
|
|
||||||
strcmp(media_content_type, "application/yang-data+xml")==0)
|
|
||||||
parse_xml++;
|
|
||||||
|
|
||||||
if (strcmp(request_method, "GET")==0)
|
if (strcmp(request_method, "GET")==0)
|
||||||
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, use_xml);
|
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, use_xml);
|
||||||
else if (strcmp(request_method, "POST")==0)
|
else if (strcmp(request_method, "POST")==0)
|
||||||
|
|
@ -293,7 +275,6 @@ api_yang_library_version(clicon_handle h,
|
||||||
if (xml_rootchild(xt, 0, &xt) < 0)
|
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((cb = cbuf_new()) == NULL){
|
if ((cb = cbuf_new()) == NULL){
|
||||||
clicon_err(OE_XML, errno, "cbuf_new");
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (use_xml){
|
if (use_xml){
|
||||||
|
|
@ -335,16 +316,33 @@ api_restconf(clicon_handle h,
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
char *data;
|
char *data;
|
||||||
int authenticated = 0;
|
int authenticated = 0;
|
||||||
|
char *media_accept;
|
||||||
|
char *media_content_type;
|
||||||
|
int pretty;
|
||||||
|
int parse_xml = 0; /* By default expect and parse JSON */
|
||||||
|
int use_xml = 0; /* By default use JSON */
|
||||||
|
cbuf *cbret = NULL;
|
||||||
|
cxobj *xret = NULL;
|
||||||
|
cxobj *xerr;
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
path = FCGX_GetParam("REQUEST_URI", r->envp);
|
path = FCGX_GetParam("REQUEST_URI", r->envp);
|
||||||
query = FCGX_GetParam("QUERY_STRING", r->envp);
|
query = FCGX_GetParam("QUERY_STRING", r->envp);
|
||||||
|
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||||
|
/* get xml/json in put and output */
|
||||||
|
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||||
|
if (media_accept && strcmp(media_accept, "application/yang-data+xml")==0)
|
||||||
|
use_xml++;
|
||||||
|
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||||
|
if (media_content_type &&
|
||||||
|
strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||||
|
parse_xml++;
|
||||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
/* Sanity check of path. Should be /restconf/ */
|
/* Sanity check of path. Should be /restconf/ */
|
||||||
if (pn < 2){
|
if (pn < 2){
|
||||||
retval = notfound(r);
|
notfound(r);
|
||||||
goto done;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (strlen(pvec[0]) != 0){
|
if (strlen(pvec[0]) != 0){
|
||||||
retval = notfound(r);
|
retval = notfound(r);
|
||||||
|
|
@ -390,7 +388,13 @@ api_restconf(clicon_handle h,
|
||||||
clicon_username_set(h, "none");
|
clicon_username_set(h, "none");
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
unauthorized(r);
|
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
||||||
|
goto done;
|
||||||
|
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||||
|
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||||
|
goto done;
|
||||||
|
goto ok;
|
||||||
|
}
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||||
|
|
@ -399,11 +403,13 @@ api_restconf(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else if (strcmp(method, "data") == 0){ /* restconf, skip /api/data */
|
else if (strcmp(method, "data") == 0){ /* restconf, skip /api/data */
|
||||||
if (api_data(h, r, path, pcvec, 2, qvec, data) < 0)
|
if (api_data(h, r, path, pcvec, 2, qvec, data,
|
||||||
|
pretty, use_xml, parse_xml) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else if (strcmp(method, "operations") == 0){ /* rpc */
|
else if (strcmp(method, "operations") == 0){ /* rpc */
|
||||||
if (api_operations(h, r, path, pcvec, 2, qvec, data) < 0)
|
if (api_operations(h, r, path, pcvec, 2, qvec, data,
|
||||||
|
pretty, use_xml, parse_xml) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
else if (strcmp(method, "test") == 0)
|
else if (strcmp(method, "test") == 0)
|
||||||
|
|
@ -424,6 +430,10 @@ api_restconf(clicon_handle h,
|
||||||
cvec_free(pcvec);
|
cvec_free(pcvec);
|
||||||
if (cb)
|
if (cb)
|
||||||
cbuf_free(cb);
|
cbuf_free(cb);
|
||||||
|
if (cbret)
|
||||||
|
cbuf_free(cbret);
|
||||||
|
if (xret)
|
||||||
|
xml_free(xret);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -557,7 +567,7 @@ main(int argc,
|
||||||
|
|
||||||
/* Initialize plugins group */
|
/* Initialize plugins group */
|
||||||
if ((dir = clicon_restconf_dir(h)) != NULL)
|
if ((dir = clicon_restconf_dir(h)) != NULL)
|
||||||
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir) < 0)
|
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Parse yang database spec file */
|
/* Parse yang database spec file */
|
||||||
|
|
@ -598,7 +608,6 @@ main(int argc,
|
||||||
clicon_debug(1, "top-level %s not found", path);
|
clicon_debug(1, "top-level %s not found", path);
|
||||||
notfound(r);
|
notfound(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
clicon_debug(1, "NULL URI");
|
clicon_debug(1, "NULL URI");
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,7 @@ api_data_get2(clicon_handle h,
|
||||||
cbuf *cbx = NULL;
|
cbuf *cbx = NULL;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr = NULL;
|
||||||
cxobj **xvec = NULL;
|
cxobj **xvec = NULL;
|
||||||
size_t xlen;
|
size_t xlen;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -199,13 +199,19 @@ api_data_get2(clicon_handle h,
|
||||||
clicon_debug(1, "%s pi:%d", __FUNCTION__, pi);
|
clicon_debug(1, "%s pi:%d", __FUNCTION__, pi);
|
||||||
/* We know "data" is element pi-1 */
|
/* We know "data" is element pi-1 */
|
||||||
if (api_path2xpath_cvv(yspec, pcvec, pi, cbpath) < 0){
|
if (api_path2xpath_cvv(yspec, pcvec, pi, cbpath) < 0){
|
||||||
notfound(r);
|
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
||||||
|
goto done;
|
||||||
|
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||||
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
path = cbuf_get(cbpath);
|
path = cbuf_get(cbpath);
|
||||||
clicon_debug(1, "%s path:%s", __FUNCTION__, path);
|
clicon_debug(1, "%s path:%s", __FUNCTION__, path);
|
||||||
if (clicon_rpc_get(h, path, &xret) < 0){
|
if (clicon_rpc_get(h, path, &xret) < 0){
|
||||||
notfound(r);
|
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
||||||
|
goto done;
|
||||||
|
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||||
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* We get return via netconf which is complete tree from root
|
/* We get return via netconf which is complete tree from root
|
||||||
|
|
@ -394,10 +400,9 @@ api_data_post(clicon_handle h,
|
||||||
yang_node *y = NULL;
|
yang_node *y = NULL;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
cxobj *xa;
|
cxobj *xa;
|
||||||
cxobj *xu;
|
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xretcom = NULL;
|
cxobj *xretcom = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr = NULL;
|
||||||
char *username;
|
char *username;
|
||||||
|
|
||||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||||
|
|
@ -414,16 +419,6 @@ api_data_post(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
/* Translate api_path to xtop/xbot */
|
/* Translate api_path to xtop/xbot */
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
/* For internal XML protocol: add username attribute for backend access control
|
|
||||||
*/
|
|
||||||
if ((username = clicon_username_get(h)) != NULL){
|
|
||||||
if ((xu = xml_new("username", xtop, NULL)) == NULL)
|
|
||||||
goto done;
|
|
||||||
xml_type_set(xu, CX_ATTR);
|
|
||||||
if (xml_value_set(xu, username) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Parse input data as json or xml into xml */
|
/* Parse input data as json or xml into xml */
|
||||||
|
|
@ -457,7 +452,11 @@ api_data_post(clicon_handle h,
|
||||||
/* Create text buffer for transfer to backend */
|
/* Create text buffer for transfer to backend */
|
||||||
if ((cbx = cbuf_new()) == NULL)
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cbx, "<rpc><edit-config><target><candidate /></target>");
|
/* For internal XML protocol: add username attribute for access control
|
||||||
|
*/
|
||||||
|
username = clicon_username_get(h);
|
||||||
|
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||||
|
cprintf(cbx, "<edit-config><target><candidate /></target>");
|
||||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -471,7 +470,10 @@ api_data_post(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* Assume this is validation failed since commit includes validate */
|
/* Assume this is validation failed since commit includes validate */
|
||||||
if (clicon_rpc_netconf(h, "<rpc><commit/></rpc>", &xretcom, NULL) < 0)
|
cbuf_reset(cbx);
|
||||||
|
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||||
|
cprintf(cbx, "<commit/></rpc>");
|
||||||
|
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||||
if (clicon_rpc_discard_changes(h) < 0)
|
if (clicon_rpc_discard_changes(h) < 0)
|
||||||
|
|
@ -600,11 +602,10 @@ api_data_put(clicon_handle h,
|
||||||
yang_node *y = NULL;
|
yang_node *y = NULL;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
cxobj *xa;
|
cxobj *xa;
|
||||||
cxobj *xu;
|
|
||||||
char *api_path;
|
char *api_path;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xretcom = NULL;
|
cxobj *xretcom = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr = NULL;
|
||||||
char *username;
|
char *username;
|
||||||
|
|
||||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||||
|
|
@ -621,15 +622,7 @@ api_data_put(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
/* Translate api_path to xtop/xbot */
|
/* Translate api_path to xtop/xbot */
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
/* For internal XML protocol: add username attribute for backend access control
|
|
||||||
*/
|
|
||||||
if ((username = clicon_username_get(h)) != NULL){
|
|
||||||
if ((xu = xml_new("username", xtop, NULL)) == NULL)
|
|
||||||
goto done;
|
|
||||||
xml_type_set(xu, CX_ATTR);
|
|
||||||
if (xml_value_set(xu, username) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Parse input data as json or xml into xml */
|
/* Parse input data as json or xml into xml */
|
||||||
|
|
@ -688,7 +681,11 @@ api_data_put(clicon_handle h,
|
||||||
/* Create text buffer for transfer to backend */
|
/* Create text buffer for transfer to backend */
|
||||||
if ((cbx = cbuf_new()) == NULL)
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cbx, "<rpc><edit-config><target><candidate /></target>");
|
/* For internal XML protocol: add username attribute for access control
|
||||||
|
*/
|
||||||
|
username = clicon_username_get(h);
|
||||||
|
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||||
|
cprintf(cbx, "<edit-config><target><candidate /></target>");
|
||||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -701,7 +698,10 @@ api_data_put(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
if (clicon_rpc_netconf(h, "<rpc><commit/></rpc>", &xretcom, NULL) < 0)
|
cbuf_reset(cbx);
|
||||||
|
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||||
|
cprintf(cbx, "<commit/></rpc>");
|
||||||
|
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||||
if (clicon_rpc_discard_changes(h) < 0)
|
if (clicon_rpc_discard_changes(h) < 0)
|
||||||
|
|
@ -779,14 +779,13 @@ api_data_delete(clicon_handle h,
|
||||||
cxobj *xtop = NULL; /* xpath root */
|
cxobj *xtop = NULL; /* xpath root */
|
||||||
cxobj *xbot = NULL;
|
cxobj *xbot = NULL;
|
||||||
cxobj *xa;
|
cxobj *xa;
|
||||||
cxobj *xu;
|
|
||||||
cbuf *cbx = NULL;
|
cbuf *cbx = NULL;
|
||||||
yang_node *y = NULL;
|
yang_node *y = NULL;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
enum operation_type op = OP_DELETE;
|
enum operation_type op = OP_DELETE;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xretcom = NULL;
|
cxobj *xretcom = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr = NULL;
|
||||||
char *username;
|
char *username;
|
||||||
|
|
||||||
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
||||||
|
|
@ -800,15 +799,7 @@ api_data_delete(clicon_handle h,
|
||||||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
/* For internal XML protocol: add username attribute for backend access control
|
|
||||||
*/
|
|
||||||
if ((username = clicon_username_get(h)) != NULL){
|
|
||||||
if ((xu = xml_new("username", xtop, NULL)) == NULL)
|
|
||||||
goto done;
|
|
||||||
xml_type_set(xu, CX_ATTR);
|
|
||||||
if (xml_value_set(xu, username) < 0)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
||||||
|
|
@ -818,7 +809,11 @@ api_data_delete(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
if ((cbx = cbuf_new()) == NULL)
|
if ((cbx = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cbx, "<rpc><edit-config><target><candidate /></target>");
|
/* For internal XML protocol: add username attribute for access control
|
||||||
|
*/
|
||||||
|
username = clicon_username_get(h);
|
||||||
|
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||||
|
cprintf(cbx, "<edit-config><target><candidate /></target>");
|
||||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -831,7 +826,10 @@ api_data_delete(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* Assume this is validation failed since commit includes validate */
|
/* Assume this is validation failed since commit includes validate */
|
||||||
if (clicon_rpc_netconf(h, "<rpc><commit/></rpc>", &xretcom, NULL) < 0)
|
cbuf_reset(cbx);
|
||||||
|
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||||
|
cprintf(cbx, "<commit/></rpc>");
|
||||||
|
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||||
if (clicon_rpc_discard_changes(h) < 0)
|
if (clicon_rpc_discard_changes(h) < 0)
|
||||||
|
|
@ -984,7 +982,7 @@ api_operations_post(clicon_handle h,
|
||||||
yang_stmt *youtput;
|
yang_stmt *youtput;
|
||||||
cxobj *xdata = NULL;
|
cxobj *xdata = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr = NULL;
|
||||||
cbuf *cbx = NULL;
|
cbuf *cbx = NULL;
|
||||||
cxobj *xtop = NULL; /* xpath root */
|
cxobj *xtop = NULL; /* xpath root */
|
||||||
cxobj *xe;
|
cxobj *xe;
|
||||||
|
|
@ -1011,7 +1009,10 @@ api_operations_post(clicon_handle h,
|
||||||
if (yang_abs_schema_nodeid(yspec, oppath, &yrpc) < 0)
|
if (yang_abs_schema_nodeid(yspec, oppath, &yrpc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (yrpc == NULL){
|
if (yrpc == NULL){
|
||||||
retval = notfound(r);
|
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
|
||||||
|
goto done;
|
||||||
|
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||||
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
/* Create an xml message:
|
/* Create an xml message:
|
||||||
|
|
|
||||||
|
|
@ -770,7 +770,7 @@ text_modify_top(cxobj *x0,
|
||||||
if (xml_operation(opstr, &op) < 0)
|
if (xml_operation(opstr, &op) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Special case if x1 is empty, top-level only <config/> */
|
/* Special case if x1 is empty, top-level only <config/> */
|
||||||
if (!xml_child_nr(x1)){
|
if (xml_child_nr(x1) == 0){
|
||||||
if (xml_child_nr(x0)) /* base tree not empty */
|
if (xml_child_nr(x0)) /* base tree not empty */
|
||||||
switch(op){
|
switch(op){
|
||||||
case OP_DELETE:
|
case OP_DELETE:
|
||||||
|
|
@ -797,7 +797,7 @@ text_modify_top(cxobj *x0,
|
||||||
/* Special case top-level replace */
|
/* Special case top-level replace */
|
||||||
if (op == OP_REPLACE || op == OP_DELETE){
|
if (op == OP_REPLACE || op == OP_DELETE){
|
||||||
x0c = NULL;
|
x0c = NULL;
|
||||||
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL)
|
while ((x0c = xml_child_i(x0, 0)) != 0)
|
||||||
xml_purge(x0c);
|
xml_purge(x0c);
|
||||||
}
|
}
|
||||||
/* Loop through children of the modification tree */
|
/* Loop through children of the modification tree */
|
||||||
|
|
@ -806,7 +806,7 @@ text_modify_top(cxobj *x0,
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
/* Get yang spec of the child */
|
/* Get yang spec of the child */
|
||||||
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
|
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
clicon_err(OE_YANG, ENOENT, "XML node %s/%s has no corresponding yang specification (Invalid XML or wrong Yang spec?", x1, x1cname);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ CFLAGS = @CFLAGS@ -rdynamic -fPIC
|
||||||
INCLUDES = -I$(includedir) @INCLUDES@
|
INCLUDES = -I$(includedir) @INCLUDES@
|
||||||
|
|
||||||
BE_PLUGIN = $(APPNAME)_backend.so
|
BE_PLUGIN = $(APPNAME)_backend.so
|
||||||
BE2_PLUGIN = $(APPNAME)_backend_secondary.so
|
BE2_PLUGIN = $(APPNAME)_backend_nacm.so
|
||||||
CLI_PLUGIN = $(APPNAME)_cli.so
|
CLI_PLUGIN = $(APPNAME)_cli.so
|
||||||
NETCONF_PLUGIN = $(APPNAME)_netconf.so
|
NETCONF_PLUGIN = $(APPNAME)_netconf.so
|
||||||
RESTCONF_PLUGIN = $(APPNAME)_restconf.so
|
RESTCONF_PLUGIN = $(APPNAME)_restconf.so
|
||||||
|
|
@ -75,8 +75,8 @@ BE_OBJ = $(BE_SRC:%.c=%.o)
|
||||||
$(BE_PLUGIN): $(BE_OBJ)
|
$(BE_PLUGIN): $(BE_OBJ)
|
||||||
$(CC) -Wall -shared -o $@ -lc $<
|
$(CC) -Wall -shared -o $@ -lc $<
|
||||||
|
|
||||||
# Secondary backend plugin
|
# Secondary NACM backend plugin
|
||||||
BE2_SRC = $(APPNAME)_backend_secondary.c
|
BE2_SRC = $(APPNAME)_backend_nacm.c
|
||||||
BE2_OBJ = $(BE2_SRC:%.c=%.o)
|
BE2_OBJ = $(BE2_SRC:%.c=%.o)
|
||||||
$(BE2_PLUGIN): $(BE2_OBJ)
|
$(BE2_PLUGIN): $(BE2_OBJ)
|
||||||
$(CC) -Wall -shared -o $@ -lc $<
|
$(CC) -Wall -shared -o $@ -lc $<
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,30 @@ module example {
|
||||||
import ietf-routing {
|
import ietf-routing {
|
||||||
prefix rt;
|
prefix rt;
|
||||||
}
|
}
|
||||||
|
import ietf-netconf-acm {
|
||||||
|
prefix nacm;
|
||||||
|
}
|
||||||
description
|
description
|
||||||
"Example code that includes ietf-ip and ietf-routing";
|
"Example code that includes ietf-ip and ietf-routing";
|
||||||
leaf basic_auth{
|
container authentication {
|
||||||
description "Basic user / password authentication as in HTTP basic auth";
|
description "Example code for enabling www basic auth and some example
|
||||||
type boolean;
|
users";
|
||||||
default false;
|
leaf basic_auth{
|
||||||
}
|
description "Basic user / password authentication as in HTTP basic auth";
|
||||||
list auth {
|
type boolean;
|
||||||
description "user / password entries. Valid if basic_auth=true";
|
default false;
|
||||||
key user;
|
|
||||||
leaf user{
|
|
||||||
description "User name";
|
|
||||||
type string;
|
|
||||||
}
|
}
|
||||||
leaf password{
|
list auth {
|
||||||
description "Password";
|
description "user / password entries. Valid if basic_auth=true";
|
||||||
type string;
|
key user;
|
||||||
|
leaf user{
|
||||||
|
description "User name";
|
||||||
|
type string;
|
||||||
|
}
|
||||||
|
leaf password{
|
||||||
|
description "Password";
|
||||||
|
type string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rpc client-rpc {
|
rpc client-rpc {
|
||||||
|
|
|
||||||
|
|
@ -251,22 +251,18 @@ plugin_start(clicon_handle h,
|
||||||
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
static clixon_plugin_api api = {
|
static clixon_plugin_api api = {
|
||||||
"example", /* name */
|
"example", /* name */ /*--- Common fields. ---*/
|
||||||
clixon_plugin_init, /* init */
|
clixon_plugin_init, /* init */
|
||||||
plugin_start, /* start */
|
plugin_start, /* start */
|
||||||
NULL, /* exit */
|
NULL, /* exit */
|
||||||
NULL, /* auth */
|
.ca_reset=plugin_reset,/* reset */ /*--- Backend plugin only ---*/
|
||||||
NULL, /* cli prompt */
|
.ca_statedata=plugin_statedata, /* statedata */
|
||||||
NULL, /* cli suspend */
|
.ca_trans_begin=NULL, /* trans begin */
|
||||||
NULL, /* cli interrupt */
|
.ca_trans_validate=transaction_validate,/* trans validate */
|
||||||
plugin_reset, /* reset */
|
.ca_trans_complete=NULL, /* trans complete */
|
||||||
plugin_statedata, /* statedata */
|
.ca_trans_commit=transaction_commit, /* trans commit */
|
||||||
NULL, /* trans begin */
|
.ca_trans_end=NULL, /* trans end */
|
||||||
transaction_validate,/* trans validate */
|
.ca_trans_abort=NULL /* trans abort */
|
||||||
NULL, /* trans complete */
|
|
||||||
transaction_commit, /* trans commit */
|
|
||||||
NULL, /* trans end */
|
|
||||||
NULL /* trans abort */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Backend plugin initialization
|
/*! Backend plugin initialization
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,41 @@
|
||||||
#include <clixon/clixon_backend.h>
|
#include <clixon/clixon_backend.h>
|
||||||
|
|
||||||
|
|
||||||
int
|
/*! Called to get NACM state data
|
||||||
transaction_commit_2(clicon_handle h,
|
* @param[in] h Clicon handle
|
||||||
transaction_data td)
|
* @param[in] xpath String with XPATH syntax. or NULL for all
|
||||||
|
* @param[in] xtop XML tree, <config/> on entry.
|
||||||
|
* @retval 0 OK
|
||||||
|
* @retval -1 Error
|
||||||
|
* @see xmldb_get
|
||||||
|
* @note this example code returns a static statedata used in testing.
|
||||||
|
* Real code would poll state
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
nacm_statedata(clicon_handle h,
|
||||||
|
char *xpath,
|
||||||
|
cxobj *xstate)
|
||||||
{
|
{
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
int retval = -1;
|
||||||
return 0;
|
cxobj **xvec = NULL;
|
||||||
|
|
||||||
|
/* Example of (static) statedata, real code would poll state */
|
||||||
|
if (xml_parse_string("<nacm>"
|
||||||
|
"<denied-data-writes>0</denied-data-writes>"
|
||||||
|
"<denied-operations>0</denied-operations>"
|
||||||
|
"<denied-notifications>0</denied-notifications>"
|
||||||
|
"</nacm>", NULL, &xstate) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (xvec)
|
||||||
|
free(xvec);
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
plugin_start_2(clicon_handle h,
|
plugin_start(clicon_handle h,
|
||||||
int argc,
|
int argc,
|
||||||
char **argv)
|
char **argv)
|
||||||
{
|
{
|
||||||
|
|
@ -74,19 +99,12 @@ plugin_start_2(clicon_handle h,
|
||||||
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
clixon_plugin_api *clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
static clixon_plugin_api api = {
|
static clixon_plugin_api api = {
|
||||||
"secondary", /* name */
|
"nacm", /* name */ /*--- Common fields. ---*/
|
||||||
clixon_plugin_init, /* init */
|
clixon_plugin_init, /* init */
|
||||||
plugin_start_2, /* start */
|
plugin_start, /* start */
|
||||||
NULL, /* exit */
|
NULL, /* exit */
|
||||||
NULL, /* auth */
|
.ca_reset=NULL, /* reset */ /*--- Backend plugin only ---*/
|
||||||
NULL, /* reset */
|
.ca_statedata=nacm_statedata, /* statedata */
|
||||||
NULL, /* statedata */
|
|
||||||
NULL, /* trans begin */
|
|
||||||
NULL, /* trans validate */
|
|
||||||
NULL, /* trans complete */
|
|
||||||
transaction_commit_2,/* trans commit */
|
|
||||||
NULL, /* trans end */
|
|
||||||
NULL /* trans abort */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Backend plugin initialization
|
/*! Backend plugin initialization
|
||||||
|
|
@ -117,10 +117,9 @@ static clixon_plugin_api api = {
|
||||||
clixon_plugin_init, /* init */
|
clixon_plugin_init, /* init */
|
||||||
NULL, /* start */
|
NULL, /* start */
|
||||||
NULL, /* exit */
|
NULL, /* exit */
|
||||||
NULL, /* auth */
|
.ca_prompt=NULL, /* cli_prompthook_t */
|
||||||
NULL, /* cli_prompthook_t */
|
.ca_suspend=NULL, /* cligen_susp_cb_t */
|
||||||
NULL, /* cligen_susp_cb_t */
|
.ca_interrupt=NULL, /* cligen_interrupt_cb_t */
|
||||||
NULL, /* cligen_interrupt_cb_t */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! CLI plugin initialization
|
/*! CLI plugin initialization
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,7 @@ static struct clixon_plugin_api api = {
|
||||||
"example", /* name */
|
"example", /* name */
|
||||||
clixon_plugin_init, /* init */
|
clixon_plugin_init, /* init */
|
||||||
plugin_start, /* start */
|
plugin_start, /* start */
|
||||||
plugin_exit, /* exit */
|
plugin_exit /* exit */
|
||||||
NULL /* auth */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Netconf plugin initialization
|
/*! Netconf plugin initialization
|
||||||
|
|
|
||||||
|
|
@ -180,7 +180,6 @@ b64_decode(const char *src,
|
||||||
return (tarindex);
|
return (tarindex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Process a rest request that requires (cookie) "authentication"
|
/*! Process a rest request that requires (cookie) "authentication"
|
||||||
* Note, this is loaded as dlsym fixed symbol in plugin
|
* Note, this is loaded as dlsym fixed symbol in plugin
|
||||||
* @param[in] h Clixon handle
|
* @param[in] h Clixon handle
|
||||||
|
|
@ -188,7 +187,7 @@ b64_decode(const char *src,
|
||||||
* @retval -1 Fatal error
|
* @retval -1 Fatal error
|
||||||
* @retval 0 Unauth
|
* @retval 0 Unauth
|
||||||
* @retval 1 Auth
|
* @retval 1 Auth
|
||||||
* For grideye, return "u" entry name if it has a valid "user" entry.
|
*
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
plugin_credentials(clicon_handle h,
|
plugin_credentials(clicon_handle h,
|
||||||
|
|
@ -206,12 +205,17 @@ plugin_credentials(clicon_handle h,
|
||||||
size_t authlen;
|
size_t authlen;
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* XXX This is a kludge to reset the user not remaining from previous */
|
||||||
|
if (clicon_username_set(h, "admin") < 0)
|
||||||
|
goto done;
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
/* Check if basic_auth set, if not return OK */
|
/* Check if basic_auth set, if not return OK */
|
||||||
if (clicon_rpc_get_config(h, "running", "/", &xt) < 0)
|
if (clicon_rpc_get_config(h, "running", "authentication", &xt) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if ((x = xpath_first(xt, "basic_auth")) == NULL)
|
if (clicon_username_set(h, "none") < 0)
|
||||||
|
goto done;
|
||||||
|
if ((x = xpath_first(xt, "authentication/basic_auth")) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
if ((xbody = xml_body(x)) == NULL)
|
if ((xbody = xml_body(x)) == NULL)
|
||||||
goto ok;
|
goto ok;
|
||||||
|
|
@ -219,8 +223,8 @@ plugin_credentials(clicon_handle h,
|
||||||
goto ok;
|
goto ok;
|
||||||
/* At this point in the code we must use HTTP basic authentication */
|
/* At this point in the code we must use HTTP basic authentication */
|
||||||
if ((auth = FCGX_GetParam("HTTP_AUTHORIZATION", r->envp)) == NULL)
|
if ((auth = FCGX_GetParam("HTTP_AUTHORIZATION", r->envp)) == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (strlen(auth) < strlen("Basic "))
|
if (strlen(auth) < strlen("Basic "))
|
||||||
goto fail;
|
goto fail;
|
||||||
if (strncmp("Basic ", auth, strlen("Basic ")))
|
if (strncmp("Basic ", auth, strlen("Basic ")))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
@ -239,15 +243,18 @@ plugin_credentials(clicon_handle h,
|
||||||
*passwd = '\0';
|
*passwd = '\0';
|
||||||
passwd++;
|
passwd++;
|
||||||
clicon_debug(1, "%s user:%s passwd:%s", __FUNCTION__, user, passwd);
|
clicon_debug(1, "%s user:%s passwd:%s", __FUNCTION__, user, passwd);
|
||||||
|
/* Here get auth sub-tree whjere all the users are */
|
||||||
if ((cb = cbuf_new()) == NULL)
|
if ((cb = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cb, "auth[user=%s]", user);
|
cprintf(cb, "authentication/auth[user=%s]", user);
|
||||||
if ((x = xpath_first(xt, cbuf_get(cb))) == NULL)
|
if ((x = xpath_first(xt, cbuf_get(cb))) == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
passwd2 = xml_find_body(x, "password");
|
passwd2 = xml_find_body(x, "password");
|
||||||
if (strcmp(passwd, passwd2))
|
if (strcmp(passwd, passwd2))
|
||||||
goto fail;
|
goto fail;
|
||||||
retval = 1;
|
retval = 1;
|
||||||
|
clicon_debug(1, "%s user:%s", __FUNCTION__, user);
|
||||||
if (clicon_username_set(h, user) < 0)
|
if (clicon_username_set(h, user) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
ok: /* authenticated */
|
ok: /* authenticated */
|
||||||
|
|
@ -281,7 +288,6 @@ restconf_client_rpc(clicon_handle h,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
clixon_plugin_api * clixon_plugin_init(clicon_handle h);
|
||||||
|
|
||||||
static clixon_plugin_api api = {
|
static clixon_plugin_api api = {
|
||||||
|
|
@ -289,7 +295,7 @@ static clixon_plugin_api api = {
|
||||||
clixon_plugin_init, /* init */
|
clixon_plugin_init, /* init */
|
||||||
NULL, /* start */
|
NULL, /* start */
|
||||||
NULL, /* exit */
|
NULL, /* exit */
|
||||||
plugin_credentials /* auth */
|
.ca_auth=plugin_credentials /* auth */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! Restconf plugin initialization
|
/*! Restconf plugin initialization
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ int netconf_bad_element(cbuf *cb, char *type, char *info, char *message);
|
||||||
int netconf_unknown_element(cbuf *cb, char *type, char *info, char *message);
|
int netconf_unknown_element(cbuf *cb, char *type, char *info, char *message);
|
||||||
int netconf_unknown_namespace(cbuf *cb, char *type, char *info, char *message);
|
int netconf_unknown_namespace(cbuf *cb, char *type, char *info, char *message);
|
||||||
int netconf_access_denied(cbuf *cb, char *type, char *message);
|
int netconf_access_denied(cbuf *cb, char *type, char *message);
|
||||||
|
int netconf_access_denied_xml(cxobj **xret, char *type, char *message);
|
||||||
int netconf_lock_denied(cbuf *cb, char *info, char *message);
|
int netconf_lock_denied(cbuf *cb, char *info, char *message);
|
||||||
int netconf_resource_denied(cbuf *cb, char *type, char *message);
|
int netconf_resource_denied(cbuf *cb, char *type, char *message);
|
||||||
int netconf_rollback_failed(cbuf *cb, char *type, char *message);
|
int netconf_rollback_failed(cbuf *cb, char *type, char *message);
|
||||||
|
|
@ -58,6 +59,7 @@ int netconf_data_exists(cbuf *cb, char *message);
|
||||||
int netconf_data_missing(cbuf *cb, char *message);
|
int netconf_data_missing(cbuf *cb, char *message);
|
||||||
int netconf_operation_not_supported(cbuf *cb, char *type, char *message);
|
int netconf_operation_not_supported(cbuf *cb, char *type, char *message);
|
||||||
int netconf_operation_failed(cbuf *cb, char *type, char *message);
|
int netconf_operation_failed(cbuf *cb, char *type, char *message);
|
||||||
|
int netconf_operation_failed_xml(cxobj **xret, char *type, char *message);
|
||||||
int netconf_malformed_message(cbuf *cb, char *message);
|
int netconf_malformed_message(cbuf *cb, char *message);
|
||||||
|
|
||||||
#endif /* _CLIXON_NETCONF_LIB_H */
|
#endif /* _CLIXON_NETCONF_LIB_H */
|
||||||
|
|
|
||||||
|
|
@ -122,23 +122,44 @@ struct clixon_plugin_api{
|
||||||
plginit2_t *ca_init; /* Clixon plugin Init (implicit) */
|
plginit2_t *ca_init; /* Clixon plugin Init (implicit) */
|
||||||
plgstart_t *ca_start; /* Plugin start */
|
plgstart_t *ca_start; /* Plugin start */
|
||||||
plgexit_t *ca_exit; /* Plugin exit */
|
plgexit_t *ca_exit; /* Plugin exit */
|
||||||
plgauth_t *ca_auth; /* Auth credentials */
|
union {
|
||||||
|
struct {
|
||||||
|
cli_prompthook_t *ci_prompt; /* Prompt hook */
|
||||||
|
cligen_susp_cb_t *ci_suspend; /* Ctrl-Z hook, see cligen getline */
|
||||||
|
cligen_interrupt_cb_t *ci_interrupt; /* Ctrl-C, see cligen getline */
|
||||||
|
} cau_cli;
|
||||||
|
struct {
|
||||||
|
plgauth_t *cr_auth; /* Auth credentials */
|
||||||
|
} cau_restconf;
|
||||||
|
struct {
|
||||||
|
} cau_netconf;
|
||||||
|
struct {
|
||||||
|
plgreset_t *cb_reset; /* Reset system status (backend only) */
|
||||||
|
plgstatedata_t *cb_statedata; /* Get state data from plugin (backend only) */
|
||||||
|
trans_cb_t *cb_trans_begin; /* Transaction start */
|
||||||
|
trans_cb_t *cb_trans_validate; /* Transaction validation */
|
||||||
|
trans_cb_t *cb_trans_complete; /* Transaction validation complete */
|
||||||
|
trans_cb_t *cb_trans_commit; /* Transaction commit */
|
||||||
|
trans_cb_t *cb_trans_end; /* Transaction completed */
|
||||||
|
trans_cb_t *cb_trans_abort; /* Transaction aborted */
|
||||||
|
} cau_backend;
|
||||||
|
|
||||||
/*--- CLI plugin-only ---*/
|
} u;
|
||||||
cli_prompthook_t *ca_prompt; /* Prompt hook */
|
|
||||||
cligen_susp_cb_t *ca_suspend; /* Ctrl-Z hook, see cligen getline */
|
|
||||||
cligen_interrupt_cb_t *ca_interrupt; /* Ctrl-C, see cligen getline */
|
|
||||||
|
|
||||||
/*--- Backend plugin only ---*/
|
|
||||||
plgreset_t *ca_reset; /* Reset system status (backend only) */
|
|
||||||
plgstatedata_t *ca_statedata; /* Get state data from plugin (backend only) */
|
|
||||||
trans_cb_t *ca_trans_begin; /* Transaction start */
|
|
||||||
trans_cb_t *ca_trans_validate; /* Transaction validation */
|
|
||||||
trans_cb_t *ca_trans_complete; /* Transaction validation complete */
|
|
||||||
trans_cb_t *ca_trans_commit; /* Transaction commit */
|
|
||||||
trans_cb_t *ca_trans_end; /* Transaction completed */
|
|
||||||
trans_cb_t *ca_trans_abort; /* Transaction aborted */
|
|
||||||
};
|
};
|
||||||
|
/* Access fields */
|
||||||
|
#define ca_prompt u.cau_cli.ci_prompt
|
||||||
|
#define ca_suspend u.cau_cli.ci_suspend
|
||||||
|
#define ca_interrupt u.cau_cli.ci_interrupt
|
||||||
|
#define ca_auth u.cau_restconf.cr_auth
|
||||||
|
#define ca_reset u.cau_backend.cb_reset
|
||||||
|
#define ca_statedata u.cau_backend.cb_statedata
|
||||||
|
#define ca_trans_begin u.cau_backend.cb_trans_begin
|
||||||
|
#define ca_trans_validate u.cau_backend.cb_trans_validate
|
||||||
|
#define ca_trans_complete u.cau_backend.cb_trans_complete
|
||||||
|
#define ca_trans_commit u.cau_backend.cb_trans_commit
|
||||||
|
#define ca_trans_end u.cau_backend.cb_trans_end
|
||||||
|
#define ca_trans_abort u.cau_backend.cb_trans_abort
|
||||||
|
|
||||||
typedef struct clixon_plugin_api clixon_plugin_api;
|
typedef struct clixon_plugin_api clixon_plugin_api;
|
||||||
|
|
||||||
/* Internal plugin structure with dlopen() handle and plugin_api
|
/* Internal plugin structure with dlopen() handle and plugin_api
|
||||||
|
|
@ -167,7 +188,7 @@ clixon_plugin *clixon_plugin_each_revert(clicon_handle h, clixon_plugin *cpprev,
|
||||||
|
|
||||||
clixon_plugin *clixon_plugin_find(clicon_handle h, char *name);
|
clixon_plugin *clixon_plugin_find(clicon_handle h, char *name);
|
||||||
|
|
||||||
int clixon_plugins_load(clicon_handle h, char *function, char *dir);
|
int clixon_plugins_load(clicon_handle h, char *function, char *dir, char *regexp);
|
||||||
|
|
||||||
int clixon_plugin_start(clicon_handle h, int argc, char **argv);
|
int clixon_plugin_start(clicon_handle h, int argc, char **argv);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
|
||||||
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
|
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
|
||||||
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
|
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop,
|
||||||
yang_class nodeclass, cxobj **xpathp, yang_node **ypathp);
|
yang_class nodeclass, cxobj **xpathp, yang_node **ypathp);
|
||||||
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
|
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec, char **reason);
|
||||||
int yang_enum_int_value(cxobj *node, int32_t *val);
|
int yang_enum_int_value(cxobj *node, int32_t *val);
|
||||||
|
|
||||||
#endif /* _CLIXON_XML_MAP_H_ */
|
#endif /* _CLIXON_XML_MAP_H_ */
|
||||||
|
|
|
||||||
|
|
@ -125,16 +125,16 @@ clicon_file_dirent(const char *dir,
|
||||||
clicon_err(OE_DB, 0, "regcomp: %s", errbuf);
|
clicon_err(OE_DB, 0, "regcomp: %s", errbuf);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if ((dirp = opendir (dir)) == NULL) {
|
if ((dirp = opendir(dir)) == NULL) {
|
||||||
if (errno == ENOENT) /* Dir does not exist -> return 0 matches */
|
if (errno == ENOENT) /* Dir does not exist -> return 0 matches */
|
||||||
retval = 0;
|
retval = 0;
|
||||||
else
|
else
|
||||||
clicon_err(OE_UNIX, errno, "opendir(%s)", dir);
|
clicon_err(OE_UNIX, errno, "opendir(%s)", dir);
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
for (res = readdir_r (dirp, &dent, &dresp);
|
for (res = readdir_r(dirp, &dent, &dresp);
|
||||||
dresp;
|
dresp;
|
||||||
res = readdir_r (dirp, &dent, &dresp)) {
|
res = readdir_r(dirp, &dent, &dresp)) {
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
clicon_err(OE_UNIX, 0, "readdir: %s", strerror(errno));
|
clicon_err(OE_UNIX, 0, "readdir: %s", strerror(errno));
|
||||||
goto quit;
|
goto quit;
|
||||||
|
|
@ -161,7 +161,7 @@ clicon_file_dirent(const char *dir,
|
||||||
goto quit;
|
goto quit;
|
||||||
}
|
}
|
||||||
new = tmp;
|
new = tmp;
|
||||||
memcpy (&new[nent], &dent, sizeof(dent));
|
memcpy(&new[nent], &dent, sizeof(dent));
|
||||||
nent++;
|
nent++;
|
||||||
|
|
||||||
} /* while */
|
} /* while */
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@
|
||||||
#include "clixon_err.h"
|
#include "clixon_err.h"
|
||||||
#include "clixon_handle.h"
|
#include "clixon_handle.h"
|
||||||
#include "clixon_yang.h"
|
#include "clixon_yang.h"
|
||||||
|
#include "clixon_log.h"
|
||||||
#include "clixon_xml.h"
|
#include "clixon_xml.h"
|
||||||
#include "clixon_netconf_lib.h"
|
#include "clixon_netconf_lib.h"
|
||||||
|
|
||||||
|
|
@ -438,6 +439,38 @@ netconf_access_denied(cbuf *cb,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Create Netconf access-denied error XML tree according to RFC 6241 App A
|
||||||
|
*
|
||||||
|
* An expected element is missing.
|
||||||
|
* @param[out] xret Error XML tree
|
||||||
|
* @param[in] type Error type: "application" or "protocol"
|
||||||
|
* @param[in] message Error message
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
netconf_access_denied_xml(cxobj **xret,
|
||||||
|
char *type,
|
||||||
|
char *message)
|
||||||
|
{
|
||||||
|
int retval =-1;
|
||||||
|
cbuf *cbret = NULL;
|
||||||
|
|
||||||
|
if ((cbret = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (netconf_access_denied(cbret, type, message) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_parse_string(cbuf_get(cbret), NULL, xret) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_rootchild(*xret, 0, xret) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (cbret)
|
||||||
|
cbuf_free(cbret);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Create Netconf lock-denied error XML tree according to RFC 6241 App A
|
/*! Create Netconf lock-denied error XML tree according to RFC 6241 App A
|
||||||
*
|
*
|
||||||
* Access to the requested lock is denied because the lock is currently held
|
* Access to the requested lock is denied because the lock is currently held
|
||||||
|
|
@ -655,7 +688,7 @@ netconf_operation_failed(cbuf *cb,
|
||||||
goto err;
|
goto err;
|
||||||
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
|
if (message && cprintf(cb, "<error-message>%s</error-message>", message) < 0)
|
||||||
goto err;
|
goto err;
|
||||||
if (cprintf(cb, "</rpc-error></rpc-reply>") <0)
|
if (cprintf(cb, "</rpc-error></rpc-reply>") < 0)
|
||||||
goto err;
|
goto err;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
@ -665,6 +698,39 @@ netconf_operation_failed(cbuf *cb,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! Create Netconf operation-failed error XML tree according to RFC 6241 App A
|
||||||
|
*
|
||||||
|
* Request could not be completed because the requested operation failed for
|
||||||
|
* some reason not covered by any other error condition.
|
||||||
|
* @param[out] xret Error XML tree
|
||||||
|
* @param[in] type Error type: "rpc", "application" or "protocol"
|
||||||
|
* @param[in] message Error message
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
netconf_operation_failed_xml(cxobj **xret,
|
||||||
|
char *type,
|
||||||
|
char *message)
|
||||||
|
{
|
||||||
|
int retval =-1;
|
||||||
|
cbuf *cbret = NULL;
|
||||||
|
|
||||||
|
if ((cbret = cbuf_new()) == NULL){
|
||||||
|
clicon_err(OE_XML, errno, "cbuf_new");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (netconf_operation_failed(cbret, type, message) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_parse_string(cbuf_get(cbret), NULL, xret) < 0)
|
||||||
|
goto done;
|
||||||
|
if (xml_rootchild(*xret, 0, xret) < 0)
|
||||||
|
goto done;
|
||||||
|
retval = 0;
|
||||||
|
done:
|
||||||
|
if (cbret)
|
||||||
|
cbuf_free(cbret);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A
|
/*! Create Netconf malformed-message error XML tree according to RFC 6241 App A
|
||||||
*
|
*
|
||||||
* A message could not be handled because it failed to be parsed correctly.
|
* A message could not be handled because it failed to be parsed correctly.
|
||||||
|
|
|
||||||
|
|
@ -242,13 +242,15 @@ plugin_load_one(clicon_handle h,
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] function Which function symbol to load and call (eg CLIXON_PLUGIN_INIT)
|
* @param[in] function Which function symbol to load and call (eg CLIXON_PLUGIN_INIT)
|
||||||
* @param[in] dir Directory. .so files in this dir will be loaded.
|
* @param[in] dir Directory. .so files in this dir will be loaded.
|
||||||
|
* @param[in] regexp Regexp for matching files in plugin directory. Default *.so.
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clixon_plugins_load(clicon_handle h,
|
clixon_plugins_load(clicon_handle h,
|
||||||
char *function,
|
char *function,
|
||||||
char *dir)
|
char *dir,
|
||||||
|
char *regexp)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int ndp;
|
int ndp;
|
||||||
|
|
@ -259,7 +261,8 @@ clixon_plugins_load(clicon_handle h,
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
/* Get plugin objects names from plugin directory */
|
/* Get plugin objects names from plugin directory */
|
||||||
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0)
|
if((ndp = clicon_file_dirent(dir, &dp,
|
||||||
|
regexp?regexp:"(.so)$", S_IFREG))<0)
|
||||||
goto done;
|
goto done;
|
||||||
/* Load all plugins */
|
/* Load all plugins */
|
||||||
for (i = 0; i < ndp; i++) {
|
for (i = 0; i < ndp; i++) {
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ clicon_rpc_msg(clicon_handle h,
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
yang_spec *yspec;
|
yang_spec *yspec;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s request:%s", __FUNCTION__, msg->op_body);
|
||||||
if ((sock = clicon_sock(h)) == NULL){
|
if ((sock = clicon_sock(h)) == NULL){
|
||||||
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
|
clicon_err(OE_FATAL, 0, "CLICON_SOCK option not set");
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -327,10 +328,14 @@ clicon_rpc_edit_config(clicon_handle h,
|
||||||
cbuf *cb = NULL;
|
cbuf *cb = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((cb = cbuf_new()) == NULL)
|
if ((cb = cbuf_new()) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cb, "<rpc><edit-config><target><%s/></target>", db);
|
cprintf(cb, "<rpc");
|
||||||
|
if ((username = clicon_username_get(h)) != NULL)
|
||||||
|
cprintf(cb, " username=\"%s\"", username);
|
||||||
|
cprintf(cb, "><edit-config><target><%s/></target>", db);
|
||||||
cprintf(cb, "<default-operation>%s</default-operation>",
|
cprintf(cb, "<default-operation>%s</default-operation>",
|
||||||
xml_operation2str(op));
|
xml_operation2str(op));
|
||||||
if (xmlstr)
|
if (xmlstr)
|
||||||
|
|
@ -377,8 +382,12 @@ clicon_rpc_copy_config(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><copy-config><source><%s/></source><target><%s/></target></copy-config></rpc>", db1, db2)) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><copy-config><source><%s/></source><target><%s/></target></copy-config></rpc>",
|
||||||
|
username?username:"",
|
||||||
|
db1, db2)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -413,8 +422,11 @@ clicon_rpc_delete_config(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><delete-config><target><%s/></target></delete-config></rpc>", db)) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><delete-config><target><%s/></target></delete-config></rpc>",
|
||||||
|
username?username:"", db)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -445,8 +457,11 @@ clicon_rpc_lock(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><lock><target><%s/></target></lock></rpc>", db)) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><lock><target><%s/></target></lock></rpc>",
|
||||||
|
username?username:"", db)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -477,8 +492,10 @@ clicon_rpc_unlock(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><unlock><target><%s/></target></unlock></rpc>", db)) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><unlock><target><%s/></target></unlock></rpc>", username?username:"", db)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -574,8 +591,11 @@ clicon_rpc_close_session(clicon_handle h)
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><close-session/></rpc>")) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><close-session/></rpc>",
|
||||||
|
username?username:"")) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -606,8 +626,11 @@ clicon_rpc_kill_session(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><kill-session><session-id>%d</session-id></kill-session></rpc>", session_id)) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><kill-session><session-id>%d</session-id></kill-session></rpc>",
|
||||||
|
username?username:"", session_id)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -638,8 +661,10 @@ clicon_rpc_validate(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><validate><source><%s/></source></validate></rpc>", db)) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><validate><source><%s/></source></validate></rpc>", username?username:"", db)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -668,8 +693,10 @@ clicon_rpc_commit(clicon_handle h)
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><commit/></rpc>")) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><commit/></rpc>", username?username:"")) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -698,8 +725,10 @@ clicon_rpc_discard_changes(clicon_handle h)
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><discard-changes/></rpc>")) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><discard-changes/></rpc>", username?username:"")) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -736,11 +765,14 @@ clicon_rpc_create_subscription(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><create-subscription>"
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><create-subscription>"
|
||||||
"<stream>%s</stream>"
|
"<stream>%s</stream>"
|
||||||
"<filter>%s</filter>"
|
"<filter>%s</filter>"
|
||||||
"</create-subscription></rpc>",
|
"</create-subscription></rpc>",
|
||||||
|
username?username:"",
|
||||||
stream?stream:"", filter?filter:"")) == NULL)
|
stream?stream:"", filter?filter:"")) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, s0) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, s0) < 0)
|
||||||
|
|
@ -772,8 +804,10 @@ clicon_rpc_debug(clicon_handle h,
|
||||||
struct clicon_msg *msg = NULL;
|
struct clicon_msg *msg = NULL;
|
||||||
cxobj *xret = NULL;
|
cxobj *xret = NULL;
|
||||||
cxobj *xerr;
|
cxobj *xerr;
|
||||||
|
char *username;
|
||||||
|
|
||||||
if ((msg = clicon_msg_encode("<rpc><debug><level>%d</level></debug></rpc>", level)) == NULL)
|
username = clicon_username_get(h);
|
||||||
|
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><debug><level>%d</level></debug></rpc>", username?username:"", level)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
if (clicon_rpc_msg(h, msg, &xret, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
|
|
@ -317,8 +317,9 @@ xml_yang_validate_add(cxobj *xt,
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
char *body;
|
char *body;
|
||||||
|
|
||||||
/* if not given by argument (overide) use default link */
|
/* if not given by argument (overide) use default link
|
||||||
if ((ys = xml_spec(xt)) != NULL){
|
and !Node has a config sub-statement and it is false */
|
||||||
|
if ((ys = xml_spec(xt)) != NULL && yang_config(ys) != 0){
|
||||||
switch (ys->ys_keyword){
|
switch (ys->ys_keyword){
|
||||||
case Y_LIST:
|
case Y_LIST:
|
||||||
/* fall thru */
|
/* fall thru */
|
||||||
|
|
@ -327,6 +328,8 @@ xml_yang_validate_add(cxobj *xt,
|
||||||
yc = ys->ys_stmt[i];
|
yc = ys->ys_stmt[i];
|
||||||
if (yc->ys_keyword != Y_LEAF)
|
if (yc->ys_keyword != Y_LEAF)
|
||||||
continue;
|
continue;
|
||||||
|
if (yang_config(yc)==0)
|
||||||
|
continue;
|
||||||
if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
|
if (yang_mandatory(yc) && xml_find(xt, yc->ys_argument)==NULL){
|
||||||
clicon_err(OE_CFG, 0,"Missing mandatory variable: %s",
|
clicon_err(OE_CFG, 0,"Missing mandatory variable: %s",
|
||||||
yc->ys_argument);
|
yc->ys_argument);
|
||||||
|
|
@ -386,8 +389,10 @@ xml_yang_validate_all(cxobj *xt,
|
||||||
yang_stmt *ys;
|
yang_stmt *ys;
|
||||||
yang_stmt *ytype;
|
yang_stmt *ytype;
|
||||||
|
|
||||||
/* if not given by argument (overide) use default link */
|
/* if not given by argument (overide) use default link
|
||||||
if ((ys = xml_spec(xt)) != NULL){
|
and !Node has a config sub-statement and it is false */
|
||||||
|
if ((ys = xml_spec(xt)) != NULL &&
|
||||||
|
yang_config(ys) != 0){
|
||||||
switch (ys->ys_keyword){
|
switch (ys->ys_keyword){
|
||||||
case Y_LEAF:
|
case Y_LEAF:
|
||||||
/* fall thru */
|
/* fall thru */
|
||||||
|
|
@ -1644,6 +1649,9 @@ api_path2xml(char *api_path,
|
||||||
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
|
||||||
* @param[in] x0p Parent of x0
|
* @param[in] x0p Parent of x0
|
||||||
* @param[in] x1 xml tree which modifies base
|
* @param[in] x1 xml tree which modifies base
|
||||||
|
* @param[out] reason If retval=0 a malloced string
|
||||||
|
* @retval 0 OK. If reason is set, Yang error
|
||||||
|
* @retval -1 Error
|
||||||
* Assume x0 and x1 are same on entry and that y is the spec
|
* Assume x0 and x1 are same on entry and that y is the spec
|
||||||
* @see put in clixon_keyvalue.c
|
* @see put in clixon_keyvalue.c
|
||||||
*/
|
*/
|
||||||
|
|
@ -1651,7 +1659,8 @@ static int
|
||||||
xml_merge1(cxobj *x0,
|
xml_merge1(cxobj *x0,
|
||||||
yang_node *y0,
|
yang_node *y0,
|
||||||
cxobj *x0p,
|
cxobj *x0p,
|
||||||
cxobj *x1)
|
cxobj *x1,
|
||||||
|
char **reason)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *x1name;
|
char *x1name;
|
||||||
|
|
@ -1699,24 +1708,35 @@ xml_merge1(cxobj *x0,
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
/* Get yang spec of the child */
|
/* Get yang spec of the child */
|
||||||
if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
|
if ((yc = yang_find_datanode(y0, x1cname)) == NULL){
|
||||||
clicon_err(OE_YANG, errno, "No yang node found: %s", x1cname);
|
if (reason && (*reason = strdup("XML node has no corresponding yang specification (Invalid XML or wrong Yang spec?")) == NULL){
|
||||||
goto done;
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
x0c = NULL;
|
x0c = NULL;
|
||||||
if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
|
if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
|
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (*reason != NULL)
|
||||||
|
goto ok;
|
||||||
}
|
}
|
||||||
} /* else Y_CONTAINER */
|
} /* else Y_CONTAINER */
|
||||||
// ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Merge XML trees x1 into x0 according to yang spec yspec
|
/*! Merge XML trees x1 into x0 according to yang spec yspec
|
||||||
|
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
|
||||||
|
* @param[in] x1 xml tree which modifies base
|
||||||
|
* @param[in] yspec Yang spec
|
||||||
|
* @param[out] reason If retval=0 a malloced string. Needs to be freed by caller
|
||||||
|
* @retval 0 OK. If reason is set, Yang error
|
||||||
|
* @retval -1 Error
|
||||||
* @note both x0 and x1 need to be top-level trees
|
* @note both x0 and x1 need to be top-level trees
|
||||||
* @see text_modify_top as more generic variant (in datastore text)
|
* @see text_modify_top as more generic variant (in datastore text)
|
||||||
* @note returns -1 if YANG do not match, you may want to have a softer error
|
* @note returns -1 if YANG do not match, you may want to have a softer error
|
||||||
|
|
@ -1724,7 +1744,8 @@ xml_merge1(cxobj *x0,
|
||||||
int
|
int
|
||||||
xml_merge(cxobj *x0,
|
xml_merge(cxobj *x0,
|
||||||
cxobj *x1,
|
cxobj *x1,
|
||||||
yang_spec *yspec)
|
yang_spec *yspec,
|
||||||
|
char **reason)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *x1cname; /* child name */
|
char *x1cname; /* child name */
|
||||||
|
|
@ -1738,16 +1759,21 @@ xml_merge(cxobj *x0,
|
||||||
x1cname = xml_name(x1c);
|
x1cname = xml_name(x1c);
|
||||||
/* Get yang spec of the child */
|
/* Get yang spec of the child */
|
||||||
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
|
if ((yc = yang_find_topnode(yspec, x1cname, YC_DATANODE)) == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "No yang spec");
|
if (reason && (*reason = strdup("XML node has no corresponding yang specification (Invalid XML or wrong Yang spec?")) == NULL){
|
||||||
goto done;
|
clicon_err(OE_UNIX, errno, "strdup");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
/* See if there is a corresponding node in the base tree */
|
/* See if there is a corresponding node in the base tree */
|
||||||
if (match_base_child(x0, x1c, &x0c, yc) < 0)
|
if (match_base_child(x0, x1c, &x0c, yc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
|
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c, reason) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (*reason != NULL)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0; /* OK */
|
||||||
done:
|
done:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Authentication and authorization
|
# Authentication and authorization and IETF NACM
|
||||||
|
# See RFC 8321 A.2
|
||||||
|
# But replaced ietf-netconf-monitoring with *
|
||||||
|
|
||||||
APPNAME=example
|
APPNAME=example
|
||||||
# include err() and new() functions and creates $dir
|
# include err() and new() functions and creates $dir
|
||||||
|
|
@ -24,18 +26,25 @@ cat <<EOF > $cfg
|
||||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||||
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
|
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
|
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||||
</config>
|
</config>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cat <<EOF > $fyang
|
cat <<EOF > $fyang
|
||||||
module $APPNAME{
|
module $APPNAME{
|
||||||
prefix ex;
|
prefix ex;
|
||||||
leaf basic_auth{
|
import ietf-netconf-acm {
|
||||||
|
prefix nacm;
|
||||||
|
}
|
||||||
|
container authentication {
|
||||||
|
description "Example code for enabling www basic auth and some example
|
||||||
|
users";
|
||||||
|
leaf basic_auth{
|
||||||
description "Basic user / password authentication as in HTTP basic auth";
|
description "Basic user / password authentication as in HTTP basic auth";
|
||||||
type boolean;
|
type boolean;
|
||||||
default false;
|
default false;
|
||||||
}
|
}
|
||||||
list auth {
|
list auth {
|
||||||
description "user / password entries. Valid if basic_auth=true";
|
description "user / password entries. Valid if basic_auth=true";
|
||||||
key user;
|
key user;
|
||||||
leaf user{
|
leaf user{
|
||||||
|
|
@ -46,10 +55,106 @@ module $APPNAME{
|
||||||
description "Password";
|
description "Password";
|
||||||
type string;
|
type string;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
leaf x{
|
||||||
|
type int32;
|
||||||
|
description "something to edit";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
RULES=$(cat <<EOF
|
||||||
|
<authentication>
|
||||||
|
<basic_auth>true</basic_auth>
|
||||||
|
<auth>
|
||||||
|
<user>adm1</user><password>bar</password>
|
||||||
|
</auth>
|
||||||
|
<auth>
|
||||||
|
<user>wilma</user><password>bar</password>
|
||||||
|
</auth>
|
||||||
|
<auth>
|
||||||
|
<user>guest</user><password>bar</password>
|
||||||
|
</auth>
|
||||||
|
</authentication>
|
||||||
|
<nacm>
|
||||||
|
<enable-nacm>false</enable-nacm>
|
||||||
|
<read-default>deny</read-default>
|
||||||
|
<write-default>deny</write-default>
|
||||||
|
<exec-default>deny</exec-default>
|
||||||
|
<groups>
|
||||||
|
<group>
|
||||||
|
<name>admin</name>
|
||||||
|
<user-name>admin</user-name>
|
||||||
|
<user-name>adm1</user-name>
|
||||||
|
<user-name>olof</user-name>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<name>limited</name>
|
||||||
|
<user-name>wilma</user-name>
|
||||||
|
<user-name>bam-bam</user-name>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<name>guest</name>
|
||||||
|
<user-name>guest</user-name>
|
||||||
|
<user-name>guest@example.com</user-name>
|
||||||
|
</group>
|
||||||
|
</groups>
|
||||||
|
<rule-list>
|
||||||
|
<name>guest-acl</name>
|
||||||
|
<group>guest</group>
|
||||||
|
<rule>
|
||||||
|
<name>deny-ncm</name>
|
||||||
|
<module-name>*</module-name>
|
||||||
|
<access-operations>*</access-operations>
|
||||||
|
<action>deny</action>
|
||||||
|
<comment>
|
||||||
|
Do not allow guests any access to any information.
|
||||||
|
</comment>
|
||||||
|
</rule>
|
||||||
|
</rule-list>
|
||||||
|
<rule-list>
|
||||||
|
<name>limited-acl</name>
|
||||||
|
<group>limited</group>
|
||||||
|
<rule>
|
||||||
|
<name>permit-get</name>
|
||||||
|
<rpc-name>get</rpc-name>
|
||||||
|
<module-name>*</module-name>
|
||||||
|
<access-operations>exec</access-operations>
|
||||||
|
<action>permit</action>
|
||||||
|
<comment>
|
||||||
|
Allow get
|
||||||
|
</comment>
|
||||||
|
</rule>
|
||||||
|
<rule>
|
||||||
|
<name>permit-get-config</name>
|
||||||
|
<rpc-name>get-config</rpc-name>
|
||||||
|
<module-name>*</module-name>
|
||||||
|
<access-operations>exec</access-operations>
|
||||||
|
<action>permit</action>
|
||||||
|
<comment>
|
||||||
|
Allow get-config
|
||||||
|
</comment>
|
||||||
|
</rule>
|
||||||
|
</rule-list>
|
||||||
|
<rule-list>
|
||||||
|
<name>admin-acl</name>
|
||||||
|
<group>admin</group>
|
||||||
|
<rule>
|
||||||
|
<name>permit-all</name>
|
||||||
|
<module-name>*</module-name>
|
||||||
|
<access-operations>*</access-operations>
|
||||||
|
<action>permit</action>
|
||||||
|
<comment>
|
||||||
|
Allow the 'admin' group complete access to all operations and data.
|
||||||
|
</comment>
|
||||||
|
</rule>
|
||||||
|
</rule-list>
|
||||||
|
</nacm>
|
||||||
|
<x>0</x>
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
# kill old backend (if any)
|
# kill old backend (if any)
|
||||||
new "kill old backend"
|
new "kill old backend"
|
||||||
sudo clixon_backend -zf $cfg -y $fyang
|
sudo clixon_backend -zf $cfg -y $fyang
|
||||||
|
|
@ -66,29 +171,60 @@ fi
|
||||||
|
|
||||||
new "kill old restconf daemon"
|
new "kill old restconf daemon"
|
||||||
sudo pkill -u www-data clixon_restconf
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
sleep 1
|
||||||
new "start restconf daemon"
|
new "start restconf daemon"
|
||||||
sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg # -D
|
sudo start-stop-daemon -S -q -o -b -x /www-data/clixon_restconf -d /www-data -c www-data -- -f $cfg -y $fyang
|
||||||
|
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|
||||||
|
new "restconf DELETE whole datastore"
|
||||||
|
expecteq "$(curl -u adm1:bar -sS -X DELETE http://localhost/restconf/data)" ""
|
||||||
|
|
||||||
new2 "auth get"
|
new2 "auth get"
|
||||||
expecteq "$(curl -sS -X GET http://localhost/restconf/data)" '{"data": null}
|
expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data)" '{"data": null}
|
||||||
'
|
'
|
||||||
|
|
||||||
new "auth set authentication config"
|
new "auth set authentication config"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config><basic_auth>true</basic_auth><auth><user>foo</user><password>bar</password></auth></config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><edit-config><target><candidate/></target><config>$RULES</config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
|
new "commit it"
|
||||||
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
expecteof "$clixon_netconf -qf $cfg -y $fyang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||||
|
|
||||||
new2 "auth get (access denied)"
|
new2 "auth get (no user: access denied)"
|
||||||
expecteq "$(curl -sS -X GET http://localhost/restconf/data)" "<error-tag>access-denied</error-tag>
|
expecteq "$(curl -sS -X GET -H \"Accept:\ application/yang-data+json\" http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
|
||||||
The requested URL /restconf/data was unauthorized."
|
|
||||||
|
new2 "auth get (wrong passwd: access denied)"
|
||||||
|
expecteq "$(curl -u adm1:foo -sS -X GET http://localhost/restconf/data)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "The requested URL was unauthorized"}}}
'
|
||||||
|
|
||||||
new2 "auth get (access)"
|
new2 "auth get (access)"
|
||||||
expecteq "$(curl -u foo:bar -sS -X GET http://localhost/restconf/data)" '{"data": {"basic_auth": true,"auth": [{"user": "foo","password": "bar"}]}}
|
expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
||||||
'
|
'
|
||||||
|
|
||||||
|
#----------------Enable NACM
|
||||||
|
|
||||||
|
new "enable nacm"
|
||||||
|
expecteq "$(curl -u adm1:bar -sS -X PUT -d '{"enable-nacm": true}' http://localhost/restconf/data/nacm/enable-nacm)" ""
|
||||||
|
|
||||||
|
new2 "admin get nacm"
|
||||||
|
expecteq "$(curl -u adm1:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
||||||
|
'
|
||||||
|
|
||||||
|
new2 "limited get nacm"
|
||||||
|
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/x)" '{"x": 0}
|
||||||
|
'
|
||||||
|
|
||||||
|
new2 "guest get nacm"
|
||||||
|
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}}
'
|
||||||
|
|
||||||
|
new "admin edit nacm"
|
||||||
|
expecteq "$(curl -u adm1:bar -sS -X PUT -d '{"x": 1}' http://localhost/restconf/data/x)" ""
|
||||||
|
|
||||||
|
new2 "limited edit nacm"
|
||||||
|
expecteq "$(curl -u wilma:bar -sS -X PUT -d '{"x": 2}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "default deny"}}}
'
|
||||||
|
|
||||||
|
new2 "guest edit nacm"
|
||||||
|
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"x": 3}' http://localhost/restconf/data/x)" '{"ietf-restconf:errors" : {"error": {"error-tag": "access-denied","error-type": "protocol","error-severity": "error","error-message": "access denied"}}}
'
|
||||||
|
|
||||||
new "Kill restconf daemon"
|
new "Kill restconf daemon"
|
||||||
sudo pkill -u www-data clixon_restconf
|
sudo pkill -u www-data clixon_restconf
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ cat <<EOF > $cfg
|
||||||
<CLICON_YANG_MODULE_MAIN>$fyang</CLICON_YANG_MODULE_MAIN>
|
<CLICON_YANG_MODULE_MAIN>$fyang</CLICON_YANG_MODULE_MAIN>
|
||||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||||
|
<CLICON_BACKEND_REGEXP>example_backend.so$</CLICON_BACKEND_REGEXP>
|
||||||
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,24 @@ module clixon-config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
typedef nacm_mode{
|
||||||
|
description
|
||||||
|
"Mode of RFC8341 Network Configuration Access Control Model.
|
||||||
|
It is unclear from the RFC whether NACM rules are internal
|
||||||
|
in a configuration (ie embedded in regular config) or external/OOB
|
||||||
|
in s separate, specific NACM-config";
|
||||||
|
type enumeration{
|
||||||
|
enum disabled{
|
||||||
|
description "NACM is disabled";
|
||||||
|
}
|
||||||
|
enum internal{
|
||||||
|
description "NACM is enabled and available in the regular config";
|
||||||
|
}
|
||||||
|
enum external{
|
||||||
|
description "NACM is enabled and available in a separate config";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
container config {
|
container config {
|
||||||
leaf CLICON_CONFIGFILE{
|
leaf CLICON_CONFIGFILE{
|
||||||
type string;
|
type string;
|
||||||
|
|
@ -113,6 +131,12 @@ module clixon-config {
|
||||||
"Location of backend .so plugins. Load all .so
|
"Location of backend .so plugins. Load all .so
|
||||||
plugins in this dir as backend plugins";
|
plugins in this dir as backend plugins";
|
||||||
}
|
}
|
||||||
|
leaf CLICON_BACKEND_REGEXP {
|
||||||
|
type string;
|
||||||
|
description
|
||||||
|
"Regexp of matching backend plugins in CLICON_BACKEND_DIR";
|
||||||
|
default "(.so)$";
|
||||||
|
}
|
||||||
leaf CLICON_NETCONF_DIR {
|
leaf CLICON_NETCONF_DIR {
|
||||||
type string;
|
type string;
|
||||||
description "Location of netconf (frontend) .so plugins";
|
description "Location of netconf (frontend) .so plugins";
|
||||||
|
|
@ -298,5 +322,9 @@ module clixon-config {
|
||||||
description "If set, modifications in validation and commit
|
description "If set, modifications in validation and commit
|
||||||
callbacks are written back into the datastore";
|
callbacks are written back into the datastore";
|
||||||
}
|
}
|
||||||
|
leaf CLICON_NACM_MODE {
|
||||||
|
type nacm_mode;
|
||||||
|
default disabled;
|
||||||
|
description "RFC8341 network access configuration control model"; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue