Merge branch 'clixon-4.5'
C-API changes for CLIgen code: * `clicon_parse()`: Changed signature due to new cligen error and result handling: * Removed: `cli_nomatch()`
This commit is contained in:
commit
f9998c441c
9 changed files with 69 additions and 466 deletions
|
|
@ -24,6 +24,12 @@
|
||||||
## 4.5.0
|
## 4.5.0
|
||||||
Expected: May 2020
|
Expected: May 2020
|
||||||
|
|
||||||
|
### C-API changes on existing features (you may need to change your plugin C-code)
|
||||||
|
|
||||||
|
* CLI
|
||||||
|
* `clicon_parse()`: Changed signature due to new cligen error and result handling:
|
||||||
|
* Removed: `cli_nomatch()`
|
||||||
|
|
||||||
## 4.4.0
|
## 4.4.0
|
||||||
5 April 2020
|
5 April 2020
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
*
|
*
|
||||||
***** BEGIN LICENSE BLOCK *****
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||||
|
Copyright (C) 2017-2019 Olof Hagsand
|
||||||
|
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||||
|
|
||||||
This file is part of CLIXON.
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
|
@ -191,14 +193,6 @@ cli_interrupt_hook(clicon_handle h,
|
||||||
return cligen_interrupt_hook(ch, fn);
|
return cligen_interrupt_hook(ch, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
|
||||||
cli_nomatch(clicon_handle h)
|
|
||||||
{
|
|
||||||
cligen_handle ch = cligen(h);
|
|
||||||
|
|
||||||
return cligen_nomatch(ch);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
cli_prompt_set(clicon_handle h,
|
cli_prompt_set(clicon_handle h,
|
||||||
char *prompt)
|
char *prompt)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
*
|
*
|
||||||
***** BEGIN LICENSE BLOCK *****
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||||
|
Copyright (C) 2017-2019 Olof Hagsand
|
||||||
|
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||||
|
|
||||||
This file is part of CLIXON.
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
|
@ -51,8 +53,6 @@ int cli_susp_hook(clicon_handle h, cligen_susp_cb_t *fn);
|
||||||
|
|
||||||
int cli_interrupt_hook(clicon_handle h, cligen_interrupt_cb_t *fn);
|
int cli_interrupt_hook(clicon_handle h, cligen_interrupt_cb_t *fn);
|
||||||
|
|
||||||
char *cli_nomatch(clicon_handle h);
|
|
||||||
|
|
||||||
int cli_prompt_set(clicon_handle h, char *prompt);
|
int cli_prompt_set(clicon_handle h, char *prompt);
|
||||||
|
|
||||||
int cli_logsyntax_set(clicon_handle h, int status);
|
int cli_logsyntax_set(clicon_handle h, int status);
|
||||||
|
|
|
||||||
|
|
@ -212,11 +212,10 @@ cli_signal_init (clicon_handle h)
|
||||||
static int
|
static int
|
||||||
cli_interactive(clicon_handle h)
|
cli_interactive(clicon_handle h)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int res;
|
char *cmd;
|
||||||
char *cmd;
|
char *new_mode;
|
||||||
char *new_mode;
|
cligen_result result;
|
||||||
int eval;
|
|
||||||
|
|
||||||
/* Loop through all commands */
|
/* Loop through all commands */
|
||||||
while(!cligen_exiting(cli_cligen(h))) {
|
while(!cligen_exiting(cli_cligen(h))) {
|
||||||
|
|
@ -225,8 +224,9 @@ cli_interactive(clicon_handle h)
|
||||||
cligen_exiting_set(cli_cligen(h), 1); /* EOF */
|
cligen_exiting_set(cli_cligen(h), 1); /* EOF */
|
||||||
goto ok; /* EOF should not be -1 error? */
|
goto ok; /* EOF should not be -1 error? */
|
||||||
}
|
}
|
||||||
if ((res = clicon_parse(h, cmd, &new_mode, &eval)) < 0)
|
if (clicon_parse(h, cmd, &new_mode, &result, NULL) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
/* Why not check result? */
|
||||||
}
|
}
|
||||||
ok:
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
@ -587,8 +587,10 @@ main(int argc, char **argv)
|
||||||
/* Join rest of argv to a single command */
|
/* Join rest of argv to a single command */
|
||||||
restarg = clicon_strjoin(argc, argv, " ");
|
restarg = clicon_strjoin(argc, argv, " ");
|
||||||
|
|
||||||
|
#if 0 /* Unsure for why this is enabled by default, turned off in clixon 4.5? */
|
||||||
/* If several cligen object variables match same preference, select first */
|
/* If several cligen object variables match same preference, select first */
|
||||||
cligen_preference_mode_set(cli_cligen(h), 1);
|
cligen_preference_mode_set(cli_cligen(h), 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Call start function in all plugins before we go interactive
|
/* Call start function in all plugins before we go interactive
|
||||||
*/
|
*/
|
||||||
|
|
@ -603,13 +605,15 @@ main(int argc, char **argv)
|
||||||
cligen_utf8_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_UTF8"));
|
cligen_utf8_set(cli_cligen(h), clicon_option_int(h,"CLICON_CLI_UTF8"));
|
||||||
/* Launch interfactive event loop, unless -1 */
|
/* Launch interfactive event loop, unless -1 */
|
||||||
if (restarg != NULL && strlen(restarg)){
|
if (restarg != NULL && strlen(restarg)){
|
||||||
char *mode = cli_syntax_mode(h);
|
char *mode = cli_syntax_mode(h);
|
||||||
int result;
|
cligen_result result; /* match result */
|
||||||
|
int evalresult = 0; /* if result == 1, calback result */
|
||||||
|
|
||||||
/* */
|
if (clicon_parse(h, restarg, &mode, &result, &evalresult) < 0)
|
||||||
if (clicon_parse(h, restarg, &mode, &result) != 1)
|
|
||||||
goto done;
|
goto done;
|
||||||
if (result < 0)
|
if (result != 1) /* Not unique match */
|
||||||
|
goto done;
|
||||||
|
if (evalresult < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
*
|
*
|
||||||
***** BEGIN LICENSE BLOCK *****
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||||
|
Copyright (C) 2017-2019 Olof Hagsand
|
||||||
|
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||||
|
|
||||||
This file is part of CLIXON.
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
|
@ -465,10 +467,10 @@ cli_handler_err(FILE *f)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Evaluate a matched command
|
/*! Evaluate a matched command
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cmd The command string
|
* @param[in] cmd The command string
|
||||||
* @retval int If there is a callback, the return value of the callback is returned,
|
* @retval int If there is a callback, the return value of the callback is returned,
|
||||||
* @retval 0 otherwise
|
* @retval 0 otherwise
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_eval(clicon_handle h,
|
clicon_eval(clicon_handle h,
|
||||||
|
|
@ -501,35 +503,32 @@ clicon_eval(clicon_handle h,
|
||||||
* match is found in another mode, the mode variable is updated to point at
|
* match is found in another mode, the mode variable is updated to point at
|
||||||
* the new mode string.
|
* the new mode string.
|
||||||
*
|
*
|
||||||
* @param[in] h Clicon handle
|
* @param[in] h Clicon handle
|
||||||
* @param[in] cmd Command string
|
* @param[in] cmd Command string
|
||||||
* @param[in,out] modenamep Pointer to the mode string pointer
|
* @param[in,out] modenamep Pointer to the mode string pointer
|
||||||
* @param[out] evalres Evaluation result if retval=1
|
* @param[out] result CLIgen match result, < 0: errors, >=0 number of matches
|
||||||
* -2 On eof (shouldnt happen)
|
* @param[out] evalres Evaluation result if result=1
|
||||||
* -1 On parse error
|
* @retval 0 OK
|
||||||
* >=0 Number of matches
|
* @retval -1 Error
|
||||||
* @retval -2 Eof CG_EOF
|
|
||||||
* @retval -1 Error CG_ERROR
|
|
||||||
* @retval 0 No match CG_NOMATCH
|
|
||||||
* @retval 1 Exactly one match CG_MATCH
|
|
||||||
* @retval 2+ Multiple matches
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
clicon_parse(clicon_handle h,
|
clicon_parse(clicon_handle h,
|
||||||
char *cmd,
|
char *cmd,
|
||||||
char **modenamep,
|
char **modenamep,
|
||||||
int *evalres)
|
cligen_result *result,
|
||||||
|
int *evalres)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
char *modename;
|
char *modename;
|
||||||
char *modename0;
|
char *modename0;
|
||||||
int r;
|
int r;
|
||||||
cli_syntax_t *stx = NULL;
|
cli_syntax_t *stx = NULL;
|
||||||
cli_syntaxmode_t *smode;
|
cli_syntaxmode_t *smode;
|
||||||
parse_tree *pt; /* Orig */
|
parse_tree *pt; /* Orig */
|
||||||
cg_obj *match_obj;
|
cg_obj *match_obj;
|
||||||
cvec *cvv = NULL;
|
cvec *cvv = NULL;
|
||||||
FILE *f;
|
FILE *f;
|
||||||
|
char *reason = NULL;
|
||||||
|
|
||||||
if (clicon_get_logflags()&CLICON_LOG_STDOUT)
|
if (clicon_get_logflags()&CLICON_LOG_STDOUT)
|
||||||
f = stdout;
|
f = stdout;
|
||||||
|
|
@ -562,22 +561,21 @@ clicon_parse(clicon_handle h,
|
||||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||||
goto done;;
|
goto done;;
|
||||||
}
|
}
|
||||||
retval = cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv);
|
if (cliread_parse(cli_cligen(h), cmd, pt, &match_obj, cvv, result, &reason) < 0)
|
||||||
if (retval != CG_MATCH)
|
goto done;
|
||||||
|
if (*result != CG_MATCH)
|
||||||
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
|
pt_expand_cleanup_1(pt); /* XXX change to pt_expand_treeref_cleanup */
|
||||||
if (modename0){
|
if (modename0){
|
||||||
cligen_tree_active_set(cli_cligen(h), modename0);
|
cligen_tree_active_set(cli_cligen(h), modename0);
|
||||||
modename0 = NULL;
|
modename0 = NULL;
|
||||||
}
|
}
|
||||||
switch (retval) {
|
switch (*result) {
|
||||||
case CG_EOF: /* eof */
|
case CG_EOF: /* eof */
|
||||||
case CG_ERROR:
|
case CG_ERROR:
|
||||||
fprintf(f, "CLI parse error: %s\n", cmd);
|
fprintf(f, "CLI parse error: %s\n", cmd);
|
||||||
break;
|
break;
|
||||||
case CG_NOMATCH: /* no match */
|
case CG_NOMATCH: /* no match */
|
||||||
/* clicon_err(OE_CFG, 0, "CLI syntax error: \"%s\": %s",
|
fprintf(f, "CLI syntax error: \"%s\": %s\n", cmd, reason);
|
||||||
cmd, cli_nomatch(h));*/
|
|
||||||
fprintf(f, "CLI syntax error: \"%s\": %s\n", cmd, cli_nomatch(h));
|
|
||||||
break;
|
break;
|
||||||
case CG_MATCH:
|
case CG_MATCH:
|
||||||
if (strcmp(modename, *modenamep)){ /* Command in different mode */
|
if (strcmp(modename, *modenamep)){ /* Command in different mode */
|
||||||
|
|
@ -593,9 +591,12 @@ clicon_parse(clicon_handle h,
|
||||||
default:
|
default:
|
||||||
fprintf(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd);
|
fprintf(f, "CLI syntax error: \"%s\" is ambiguous\n", cmd);
|
||||||
break;
|
break;
|
||||||
} /* switch retval */
|
} /* switch result */
|
||||||
}
|
}
|
||||||
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
if (reason)
|
||||||
|
free(reason);
|
||||||
if (cvv)
|
if (cvv)
|
||||||
cvec_free(cvv);
|
cvec_free(cvv);
|
||||||
return retval;
|
return retval;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@
|
||||||
*
|
*
|
||||||
***** BEGIN LICENSE BLOCK *****
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||||
|
Copyright (C) 2017-2019 Olof Hagsand
|
||||||
|
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||||
|
|
||||||
This file is part of CLIXON.
|
This file is part of CLIXON.
|
||||||
|
|
||||||
|
|
@ -65,7 +67,7 @@ void *clixon_str2fn(char *name, void *handle, char **error);
|
||||||
|
|
||||||
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, cligen_result *result, int *evalres);
|
||||||
|
|
||||||
char *clicon_cliread(clicon_handle h);
|
char *clicon_cliread(clicon_handle h);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,6 @@ int xml_namespace_change(cxobj *x, char *namespace, char *prefix);
|
||||||
int xml_default(cxobj *x, void *arg);
|
int xml_default(cxobj *x, void *arg);
|
||||||
int xml_sanity(cxobj *x, void *arg);
|
int xml_sanity(cxobj *x, void *arg);
|
||||||
int xml_non_config_data(cxobj *xt, void *arg);
|
int xml_non_config_data(cxobj *xt, void *arg);
|
||||||
int xml_bind_yang_rpc(cxobj *xrpc, yang_stmt *yspec, cxobj **xerr);
|
|
||||||
int xml_bind_yang_rpc_reply(cxobj *xrpc, char *name, yang_stmt *yspec, cxobj **xerr);
|
|
||||||
int xml_bind_yang0(cxobj *xt, yang_bind yb, yang_stmt *yspec, cxobj **xerr);
|
|
||||||
int xml_bind_yang(cxobj *xt, yang_bind yb, yang_stmt *yspec, cxobj **xerr);
|
|
||||||
|
|
||||||
int xml2xpath(cxobj *x, char **xpath);
|
int xml2xpath(cxobj *x, char **xpath);
|
||||||
int assign_namespaces(cxobj *x0, cxobj *x1, cxobj *x1p);
|
int assign_namespaces(cxobj *x0, cxobj *x1, cxobj *x1p);
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ xmldb_db2file(clicon_handle h,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Validate database name
|
/*! Ensure database name is correct
|
||||||
* @param[in] db Name of database
|
* @param[in] db Name of database
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Failed validate, xret set to error
|
* @retval -1 Failed validate, xret set to error
|
||||||
|
|
|
||||||
|
|
@ -1022,406 +1022,6 @@ xml_non_config_data(cxobj *xt,
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Associate XML node x with x:s parents yang:s matching child
|
|
||||||
*
|
|
||||||
* @param[in] xt XML tree node
|
|
||||||
* @param[out] xerr Reason for failure, or NULL
|
|
||||||
* @retval 2 OK yang assignment made
|
|
||||||
* @retval 1 OK, Yang assignment not made because yang parent is anyxml or anydata
|
|
||||||
* @retval 0 Yang assigment not made and xerr set
|
|
||||||
* @retval -1 Error
|
|
||||||
* @see populate_self_top
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
populate_self_parent(cxobj *xt,
|
|
||||||
cxobj **xerr)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
yang_stmt *y = NULL; /* yang node */
|
|
||||||
yang_stmt *yparent; /* yang parent */
|
|
||||||
cxobj *xp = NULL; /* xml parent */
|
|
||||||
char *name;
|
|
||||||
char *ns = NULL; /* XML namespace of xt */
|
|
||||||
char *nsy = NULL; /* Yang namespace of xt */
|
|
||||||
|
|
||||||
xp = xml_parent(xt);
|
|
||||||
name = xml_name(xt);
|
|
||||||
if (xp == NULL){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Missing parent") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if ((yparent = xml_spec(xp)) == NULL){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Missing parent yang node") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (yang_keyword_get(yparent) == Y_ANYXML || yang_keyword_get(yparent) == Y_ANYDATA){
|
|
||||||
retval = 1;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if ((y = yang_find_datanode(yparent, name)) == NULL){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Missing matching yang node") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (xml2ns(xt, xml_prefix(xt), &ns) < 0)
|
|
||||||
goto done;
|
|
||||||
nsy = yang_find_mynamespace(y);
|
|
||||||
if (ns == NULL || nsy == NULL){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Missing namespace") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
/* Assign spec only if namespaces match */
|
|
||||||
if (strcmp(ns, nsy) != 0){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
xml_spec_set(xt, y);
|
|
||||||
#ifdef XML_EXPLICIT_INDEX
|
|
||||||
if (xml_search_index_p(xt))
|
|
||||||
xml_search_child_insert(xp, xt);
|
|
||||||
#endif
|
|
||||||
retval = 2;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
fail:
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Associate XML node x with yang spec y by going through all top-level modules and finding match
|
|
||||||
*
|
|
||||||
* @param[in] xt XML tree node
|
|
||||||
* @param[in] yspec Yang spec
|
|
||||||
* @param[out] xerr Reason for failure, or NULL
|
|
||||||
* @retval 2 OK yang assignment made
|
|
||||||
* @retval 1 OK, Yang assignment not made because yang parent is anyxml or anydata
|
|
||||||
* @retval 0 yang assigment not made and xerr set
|
|
||||||
* @retval -1 Error
|
|
||||||
* @see populate_self_parent
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
populate_self_top(cxobj *xt,
|
|
||||||
yang_stmt *yspec,
|
|
||||||
cxobj **xerr)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
yang_stmt *y = NULL; /* yang node */
|
|
||||||
yang_stmt *ymod; /* yang module */
|
|
||||||
char *name;
|
|
||||||
char *ns = NULL; /* XML namespace of xt */
|
|
||||||
char *nsy = NULL; /* Yang namespace of xt */
|
|
||||||
|
|
||||||
name = xml_name(xt);
|
|
||||||
if (yspec == NULL){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Missing yang spec") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (ys_module_by_xml(yspec, xt, &ymod) < 0)
|
|
||||||
goto done;
|
|
||||||
/* ymod is "real" module, name may belong to included submodule */
|
|
||||||
if (ymod == NULL){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "No such yang module") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if ((y = yang_find_schemanode(ymod, name)) == NULL){ /* also rpc */
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Missing matching yang node") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
if (xml2ns(xt, xml_prefix(xt), &ns) < 0)
|
|
||||||
goto done;
|
|
||||||
nsy = yang_find_mynamespace(y);
|
|
||||||
if (ns == NULL || nsy == NULL){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Missing namespace") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
/* Assign spec only if namespaces match */
|
|
||||||
if (strcmp(ns, nsy) != 0){
|
|
||||||
if (xerr &&
|
|
||||||
netconf_bad_element_xml(xerr, "application", name, "Namespace mismatch") < 0)
|
|
||||||
goto done;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
xml_spec_set(xt, y);
|
|
||||||
retval = 1;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
fail:
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! After yang binding, bodies of containers and lists are stripped from XML bodies
|
|
||||||
* May apply to other nodes?
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
strip_whitespace(cxobj *xt)
|
|
||||||
{
|
|
||||||
yang_stmt *yt;
|
|
||||||
enum rfc_6020 keyword;
|
|
||||||
cxobj *xc;
|
|
||||||
|
|
||||||
if ((yt = xml_spec(xt)) != NULL){
|
|
||||||
keyword = yang_keyword_get(yt);
|
|
||||||
if (keyword == Y_LIST || keyword == Y_CONTAINER){
|
|
||||||
xc = NULL;
|
|
||||||
while ((xc = xml_find_type(xt, NULL, "body", CX_BODY)) != NULL)
|
|
||||||
xml_purge(xc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Find yang spec association of tree of XML nodes
|
|
||||||
*
|
|
||||||
* Populate xt:s children as top-level symbols
|
|
||||||
* This may be unnecessary if yspec is set on manual creation. Also note that for incoming or
|
|
||||||
* outgoing RPC need specialized function. maybe it can be built into the same function, but
|
|
||||||
* you dont know whether it is input or output rpc.
|
|
||||||
* @param[in] xt XML tree node
|
|
||||||
* @param[in] yspec Yang spec
|
|
||||||
* @param[out] xerr Reason for failure, or NULL
|
|
||||||
* @retval 1 OK yang assignment made
|
|
||||||
* @retval 0 Partial or no yang assigment made (at least one failed) and xerr set
|
|
||||||
* @retval -1 Error
|
|
||||||
* @code
|
|
||||||
* if (xml_bind_yang(x, YB_MODULE, yspec, NULL) < 0)
|
|
||||||
* err;
|
|
||||||
* @endcode
|
|
||||||
* @note For subs to anyxml nodes will not have spec set
|
|
||||||
* There are several functions in the API family
|
|
||||||
* @see xml_bind_yang_rpc for incoming rpc
|
|
||||||
* @see xml_bind_yang0 If the calling xml object should also be populated
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
xml_bind_yang(cxobj *xt,
|
|
||||||
yang_bind yb,
|
|
||||||
yang_stmt *yspec,
|
|
||||||
cxobj **xerr)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
cxobj *xc; /* xml child */
|
|
||||||
int ret;
|
|
||||||
int failed = 0; /* we continue loop after failure, should we stop at fail?`*/
|
|
||||||
|
|
||||||
strip_whitespace(xt);
|
|
||||||
xc = NULL; /* Apply on children */
|
|
||||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
|
||||||
if ((ret = xml_bind_yang0(xc, yb, yspec, xerr)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ret == 0)
|
|
||||||
failed++;
|
|
||||||
}
|
|
||||||
if (failed)
|
|
||||||
goto fail;
|
|
||||||
retval = 1;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
fail:
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Find yang spec association of tree of XML nodes
|
|
||||||
*
|
|
||||||
* @param[in] xt XML tree node
|
|
||||||
* @param[in] yb How to bind yang to XML top-level when parsing
|
|
||||||
* @param[in] yspec Yang spec
|
|
||||||
* @param[out] xerr Reason for failure, or NULL
|
|
||||||
* @retval 1 OK yang assignment made
|
|
||||||
* @retval 0 Partial or no yang assigment made (at least one failed) and xerr set
|
|
||||||
* @retval -1 Error
|
|
||||||
* Populate xt as top-level node
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
xml_bind_yang0(cxobj *xt,
|
|
||||||
yang_bind yb,
|
|
||||||
yang_stmt *yspec,
|
|
||||||
cxobj **xerr)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
cxobj *xc; /* xml child */
|
|
||||||
int ret;
|
|
||||||
int failed = 0; /* we continue loop after failure, should we stop at fail?`*/
|
|
||||||
|
|
||||||
switch (yb){
|
|
||||||
case YB_MODULE:
|
|
||||||
if ((ret = populate_self_top(xt, yspec, xerr)) < 0)
|
|
||||||
goto done;
|
|
||||||
break;
|
|
||||||
case YB_PARENT:
|
|
||||||
if ((ret = populate_self_parent(xt, xerr)) < 0)
|
|
||||||
goto done;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
clicon_err(OE_XML, EINVAL, "Invalid yang binding: %d", yb);
|
|
||||||
goto done;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ret == 0)
|
|
||||||
goto fail;
|
|
||||||
strip_whitespace(xt);
|
|
||||||
xc = NULL; /* Apply on children */
|
|
||||||
while ((xc = xml_child_each(xt, xc, CX_ELMNT)) != NULL) {
|
|
||||||
if ((ret = xml_bind_yang0(xc, YB_PARENT, yspec, xerr)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ret == 0)
|
|
||||||
failed++;
|
|
||||||
}
|
|
||||||
if (failed)
|
|
||||||
goto fail;
|
|
||||||
retval = 1;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
fail:
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Find yang spec association of XML node for incoming RPC starting with <rpc>
|
|
||||||
*
|
|
||||||
* Incoming RPC has an "input" structure that is not taken care of by xml_bind_yang
|
|
||||||
* @param[in] xrpc XML rpc node
|
|
||||||
* @param[in] yspec Yang spec
|
|
||||||
* @param[out] xerr Reason for failure, or NULL
|
|
||||||
* @retval 1 OK yang assignment made
|
|
||||||
* @retval 0 Partial or no yang assigment made (at least one failed) and xerr set
|
|
||||||
* @retval -1 Error
|
|
||||||
* The
|
|
||||||
* @code
|
|
||||||
* if (xml_bind_yang_rpc(h, x, NULL) < 0)
|
|
||||||
* err;
|
|
||||||
* @endcode
|
|
||||||
* @see xml_bind_yang For other generic cases
|
|
||||||
* @see xml_bind_yang_rpc_reply
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
xml_bind_yang_rpc(cxobj *xrpc,
|
|
||||||
yang_stmt *yspec,
|
|
||||||
cxobj **xerr)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
yang_stmt *yrpc = NULL; /* yang node */
|
|
||||||
yang_stmt *ymod=NULL; /* yang module */
|
|
||||||
yang_stmt *yi = NULL; /* input */
|
|
||||||
cxobj *x;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if ((strcmp(xml_name(xrpc), "rpc")) != 0){
|
|
||||||
clicon_err(OE_UNIX, EINVAL, "RPC expected");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
x = NULL;
|
|
||||||
while ((x = xml_child_each(xrpc, x, CX_ELMNT)) != NULL) {
|
|
||||||
if (ys_module_by_xml(yspec, x, &ymod) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ymod != NULL)
|
|
||||||
yrpc = yang_find(ymod, Y_RPC, xml_name(x));
|
|
||||||
/* Non-strict semantics: loop through all modules to find the node
|
|
||||||
*/
|
|
||||||
if (yrpc){
|
|
||||||
xml_spec_set(x, yrpc);
|
|
||||||
if ((yi = yang_find(yrpc, Y_INPUT, NULL)) != NULL){
|
|
||||||
/* xml_bind_yang need to have parent with yang spec for
|
|
||||||
* recursive population to work. Therefore, assign input yang
|
|
||||||
* to rpc level although not 100% intuitive */
|
|
||||||
xml_spec_set(x, yi);
|
|
||||||
if ((ret = xml_bind_yang(x, YB_PARENT, NULL, xerr)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ret == 0)
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
retval = 1;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
fail:
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Find yang spec association of XML node for outgoing RPC starting with <rpc-reply>
|
|
||||||
*
|
|
||||||
* Incoming RPC has an "input" structure that is not taken care of by xml_bind_yang
|
|
||||||
* @param[in] xrpc XML rpc node
|
|
||||||
* @param[in] name Name of RPC (not seen in output/reply)
|
|
||||||
* @param[in] yspec Yang spec
|
|
||||||
* @param[out] xerr Reason for failure, or NULL
|
|
||||||
* @retval 1 OK yang assignment made
|
|
||||||
* @retval 0 Partial or no yang assigment made (at least one failed) and xerr set
|
|
||||||
* @retval -1 Error
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* if (xml_bind_yang_rpc_reply(x, "get-config", yspec, name) < 0)
|
|
||||||
* err;
|
|
||||||
* @endcode
|
|
||||||
* @see xml_bind_yang For other generic cases
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
xml_bind_yang_rpc_reply(cxobj *xrpc,
|
|
||||||
char *name,
|
|
||||||
yang_stmt *yspec,
|
|
||||||
cxobj **xerr)
|
|
||||||
{
|
|
||||||
int retval = -1;
|
|
||||||
yang_stmt *yrpc = NULL; /* yang node */
|
|
||||||
yang_stmt *ymod=NULL; /* yang module */
|
|
||||||
yang_stmt *yo = NULL; /* output */
|
|
||||||
cxobj *x;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (strcmp(xml_name(xrpc), "rpc-reply")){
|
|
||||||
clicon_err(OE_UNIX, EINVAL, "rpc-reply expected");
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
x = NULL;
|
|
||||||
while ((x = xml_child_each(xrpc, x, CX_ELMNT)) != NULL) {
|
|
||||||
if (ys_module_by_xml(yspec, x, &ymod) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ymod == NULL)
|
|
||||||
continue;
|
|
||||||
if ((yrpc = yang_find(ymod, Y_RPC, name)) == NULL)
|
|
||||||
continue;
|
|
||||||
// xml_spec_set(xrpc, yrpc);
|
|
||||||
if ((yo = yang_find(yrpc, Y_OUTPUT, NULL)) == NULL)
|
|
||||||
continue;
|
|
||||||
/* xml_bind_yang need to have parent with yang spec for
|
|
||||||
* recursive population to work. Therefore, assign input yang
|
|
||||||
* to rpc level although not 100% intuitive */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (yo != NULL){
|
|
||||||
xml_spec_set(xrpc, yo);
|
|
||||||
if ((ret = xml_bind_yang(xrpc, YB_MODULE, yspec, xerr)) < 0)
|
|
||||||
goto done;
|
|
||||||
if (ret == 0)
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
retval = 1;
|
|
||||||
done:
|
|
||||||
return retval;
|
|
||||||
fail:
|
|
||||||
retval = 0;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! Given an XML node, build an xpath to root, internal function
|
/*! Given an XML node, build an xpath to root, internal function
|
||||||
* @retval 0 OK
|
* @retval 0 OK
|
||||||
* @retval -1 Error. eg XML malformed
|
* @retval -1 Error. eg XML malformed
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue