commit xpath

This commit is contained in:
Olof hagsand 2016-02-24 21:16:15 +01:00
parent 554c1cb9de
commit 9bd941ab76
14 changed files with 306 additions and 130 deletions

View file

@ -42,7 +42,7 @@ LIBS = @LIBS@
INCLUDES = -I. -I@srcdir@ @INCLUDES@ INCLUDES = -I. -I@srcdir@ @INCLUDES@
SHELL = /bin/sh SHELL = /bin/sh
SUBDIRS = lib apps include doc etc SUBDIRS = lib apps include etc
.PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status .PHONY: doc all clean depend $(SUBDIRS) install loc TAGS .config.status
@ -52,7 +52,7 @@ $(SUBDIRS):
(cd $@ && $(MAKE) $(MFLAGS) all) (cd $@ && $(MAKE) $(MFLAGS) all)
depend: depend:
for i in $(SUBDIRS) example; \ for i in $(SUBDIRS) doc example; \
do (cd $$i && $(MAKE) $(MFLAGS) depend); done do (cd $$i && $(MAKE) $(MFLAGS) depend); done
# template clicon.conf file # template clicon.conf file
@ -63,7 +63,7 @@ clicon.mk: clicon.mk.cpp
$(CPP) -P -traditional-cpp -x assembler-with-cpp -Dprefix=$(prefix) -Dlocalstatedir=$(localstatedir) -Dsysconfdir=$(sysconfdir) -Ddatadir=$(datadir) -Dlibdir=$(libdir) $< > $@ $(CPP) -P -traditional-cpp -x assembler-with-cpp -Dprefix=$(prefix) -Dlocalstatedir=$(localstatedir) -Dsysconfdir=$(sysconfdir) -Ddatadir=$(datadir) -Dlibdir=$(libdir) $< > $@
install: clicon.conf.cpp clicon.mk install: clicon.conf.cpp clicon.mk
for i in $(SUBDIRS); \ for i in $(SUBDIRS) doc; \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done; \ do (cd $$i && $(MAKE) $(MFLAGS) $@); done; \
install -d -m 755 $(DESTDIR)$(datadir)/clicon install -d -m 755 $(DESTDIR)$(datadir)/clicon
install -m 755 clicon.conf.cpp $(DESTDIR)$(datadir)/clicon install -m 755 clicon.conf.cpp $(DESTDIR)$(datadir)/clicon
@ -71,19 +71,18 @@ install: clicon.conf.cpp clicon.mk
echo "Install for compilation by: make install-include" echo "Install for compilation by: make install-include"
install-include: install-include:
for i in $(SUBDIRS); \ for i in $(SUBDIRS) doc; \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done; \ do (cd $$i && $(MAKE) $(MFLAGS) $@); done; \
echo "To install example app: cd example; make; make install" echo "To install example app: cd example; make; make install"
uninstall: uninstall:
for i in $(SUBDIRS) example; \ for i in $(SUBDIRS) doc example; \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done; do (cd $$i && $(MAKE) $(MFLAGS) $@); done;
rm -f $(datadir)/clicon/clicon.conf.cpp rm -f $(datadir)/clicon/clicon.conf.cpp
rm -f $(datadir)/clicon/clicon.mk rm -f $(datadir)/clicon/clicon.mk
# Overrides SUBDIRS above doc:
#doc: cd $@; $(MAKE) $(MFLAGS) $@
# cd $@; $(MAKE) $(MFLAGS) $@
config.status: configure config.status: configure
$(SHELL) config.status --recheck $(SHELL) config.status --recheck
@ -92,14 +91,14 @@ configure: configure.ac
cd $(srcdir) && autoconf cd $(srcdir) && autoconf
clean: clean:
for i in $(SUBDIRS) example; \ for i in $(SUBDIRS) doc example; \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done; do (cd $$i && $(MAKE) $(MFLAGS) $@); done;
distclean: distclean:
rm -f Makefile TAGS config.status config.log *~ .depend rm -f Makefile TAGS config.status config.log *~ .depend
rm -rf Makefile autom4te.cache rm -rf Makefile autom4te.cache
rm -rf clicon.conf.cpp clicon.mk rm -rf clicon.conf.cpp clicon.mk
for i in $(SUBDIRS) example; \ for i in $(SUBDIRS) doc example; \
do (cd $$i && $(MAKE) $(MFLAGS) $@); done do (cd $$i && $(MAKE) $(MFLAGS) $@); done
# Lines of code # Lines of code

View file

@ -126,8 +126,8 @@ candidate_commit(clicon_handle h,
char *running) char *running)
{ {
int retval = -1; int retval = -1;
// int i, j; int i;
// int failed = 0; cxobj *xn;
struct stat sb; struct stat sb;
void *firsterr = NULL; void *firsterr = NULL;
yang_spec *yspec; yang_spec *yspec;
@ -171,6 +171,25 @@ candidate_commit(clicon_handle h,
goto done; goto done;
if (debug) if (debug)
transaction_print(stderr, td); transaction_print(stderr, td);
/* Mark as changed in tree */
for (i=0; i<td->td_dlen; i++){ /* Also down */
xn = td->td_dvec[i];
xml_flag_set(xn, XML_FLAG_DEL);
xml_apply(xn, CX_ELMNT, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_DEL);
}
for (i=0; i<td->td_alen; i++){ /* Also down */
xn = td->td_avec[i];
xml_flag_set(xn, XML_FLAG_ADD);
xml_apply(xn, CX_ELMNT, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_ADD);
}
for (i=0; i<td->td_clen; i++){ /* Also up */
xn = td->td_scvec[i];
xml_flag(xn, XML_FLAG_CHANGE);
xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
xn = td->td_tcvec[i];
xml_flag_set(xn, XML_FLAG_CHANGE);
xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
}
/* 4. Call plugin transaction start callbacks */ /* 4. Call plugin transaction start callbacks */
if (plugin_transaction_begin(h, td) < 0) if (plugin_transaction_begin(h, td) < 0)
@ -236,6 +255,8 @@ candidate_validate(clicon_handle h,
struct stat sb; struct stat sb;
yang_spec *yspec; yang_spec *yspec;
transaction_data_t *td = NULL; transaction_data_t *td = NULL;
int i;
cxobj *xn;
if ((yspec = clicon_dbspec_yang(h)) == NULL){ if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC"); clicon_err(OE_FATAL, 0, "No DB_SPEC");
@ -276,6 +297,22 @@ candidate_validate(clicon_handle h,
if (debug) if (debug)
transaction_print(stderr, td); transaction_print(stderr, td);
/* Mark as changed in tree */
for (i=0; i<td->td_dlen; i++){ /* Also down */
xn = td->td_dvec[i];
xml_flag_set(xn, XML_FLAG_DEL);
}
for (i=0; i<td->td_alen; i++){ /* Also down */
xn = td->td_avec[i];
xml_flag_set(xn, XML_FLAG_ADD);
}
for (i=0; i<td->td_clen; i++){ /* Also up */
xn = td->td_scvec[i];
xml_flag_set(xn, XML_FLAG_CHANGE);
xn = td->td_tcvec[i];
xml_flag(xn, XML_FLAG_CHANGE);
}
/* 4. Call plugin start transaction callbacks */ /* 4. Call plugin start transaction callbacks */
if (plugin_transaction_begin(h, td) < 0) if (plugin_transaction_begin(h, td) < 0)

View file

@ -1492,7 +1492,7 @@ show_conf_xpath(clicon_handle h, cvec *cvv, cg_var *arg)
cg_var *cv; cg_var *cv;
cxobj *xt = NULL; cxobj *xt = NULL;
cxobj **xv = NULL; cxobj **xv = NULL;
int xlen; size_t xlen;
int i; int i;
if (arg == NULL || (str = cv_string_get(arg)) == NULL){ if (arg == NULL || (str = cv_string_get(arg)) == NULL){
@ -1516,7 +1516,7 @@ show_conf_xpath(clicon_handle h, cvec *cvv, cg_var *arg)
cv = cvec_find_var(cvv, "xpath"); cv = cvec_find_var(cvv, "xpath");
xpath = cv_string_get(cv); xpath = cv_string_get(cv);
yspec = clicon_dbspec_yang(h); yspec = clicon_dbspec_yang(h);
if (xmldb_get_xpath(dbname, xpath, yspec, &xt, &xv, &xlen) < 0) if (xmldb_get_vec(dbname, xpath, yspec, &xt, &xv, &xlen) < 0)
goto done; goto done;
for (i=0; i<xlen; i++) for (i=0; i<xlen; i++)
clicon_xml2file(stdout, xv[i], 0, 1); clicon_xml2file(stdout, xv[i], 0, 1);

View file

@ -219,11 +219,11 @@ netconf_xpath(cxobj *xsearch,
cxobj *xorig) cxobj *xorig)
{ {
cxobj *x; cxobj *x;
int retval = -1; int retval = -1;
char *selector; char *selector;
cxobj **xv; cxobj **xv;
int xlen; size_t xlen;
int i; int i;
if ((selector = xml_find_value(xfilter, "select")) == NULL){ if ((selector = xml_find_value(xfilter, "select")) == NULL){
netconf_create_rpc_error(cb_err, xorig, netconf_create_rpc_error(cb_err, xorig,

View file

@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
# title of most generated pages and in a few other places. # title of most generated pages and in a few other places.
# The default value is: My Project. # The default value is: My Project.
PROJECT_NAME = "CLICON" PROJECT_NAME = "CliXoN"
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This # The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version

View file

@ -1,86 +1,140 @@
Frequently Asked Questions - Clixon Frequently Asked Questions - CliXon
=================================== ===================================
Q: What is the best way of understanding Clixon? Q: What is CliXon?
A: Run the ietf yang routing example. ------------------
CliXon is a configuration management tool including CLI generation,
Yang parser, netconf interface and an embedded databases.
Q: How do you build and install Clixon (and the example)? Q: Why should you use CliXon?
A: Clixon: ./configure; make; sudo make install; sudo make install-include -----------------------------
Example: cd example; make; sudo make install If you want an easy-to-use config frontend based on yang with an open-source license.
Typically for embedded devices requiring a config interface such as routers and switches.
Q: Is there any reference documentation? Q: What license is available?
A: Clixon uses Doxygen for reference documentation. -----------------------------
The basic license is open-source, GPLv3. Contact authors for commercial license.
Q: Is CliXon extendible?
------------------------
Yes. All application semantics is defined in plugins with well-defined APIs. There are currently three types of plugins: for CLI, for Netconf and for the backend.
Q: Which language is CliXon implemented in?
-------------------------------------------
CliXon is written in C. The plugins are written in C. The CLI
specification uses cligen.
There is a project for writing plugins in Python. It is reasonable
simple to spawn an external script from a backend.
Q: Is CliXon different from Clicon?
-----------------------------------
Yes, but CliXon is a fork of Clicon focussing on Yang specification and XML
database. The yang support in clicon was grown out of a key-based
scheme that became more and more complex since it had to translate
between many different formats. CliXon uses only XML internally.
The commit transaction mechanism has been simplified too. But CliXon
is not backward compliant with key-based Clicon applications, such as
Rost.
Q: How to best understand CliXon?
---------------------------------
Run the ietf yang routing example. It is used in many of the answers below.
Q: How do you build and install CliXon (and the example)?
---------------------------------------------------------
CliXon:
./configure;
make;
sudo make install;
sudo make install-include
The example:
cd example;
make;
sudo make install
Q: What about reference documentation?
--------------------------------------
CliXon uses Doxygen for reference documentation.
Build using 'make doc' and aim your browser at doc/html/index.html Build using 'make doc' and aim your browser at doc/html/index.html
Q: How do you run the example? Q: How do you run the example?
A: Start a backend server: 'clicon_backend -Ff /usr/local/etc/routing.conf' ------------------------------
Start a cli session: clicon_cli -f /usr/local/etc/routing.conf - Start a backend server: 'clicon_backend -Ff /usr/local/etc/routing.conf'
Start a netconf session: clicon_netconf -f /usr/local/etc/routing.conf - Start a cli session: clicon_cli -f /usr/local/etc/routing.conf
- Start a netconf session: clicon_netconf -f /usr/local/etc/routing.conf
Q: How does Clixon differ from Clicon? Q: How do you change the example?
A: Clixon is a fork of Clicon focussing on Yang specification and XML ---------------------------------
database. The yang support in clicon was grown out of a key-based
scheme that was complex since it had to translate between
formats. Clixon uses only XML. A simplified commit transaction
mechanism has been added to. But Clixon is not backward compliant with
key-based Clicon applications, such as ROST.
Q: In the example, how can you alter its semantics?
- routing.conf.local - Override default settings - routing.conf.local - Override default settings
- The yang specifications - Alter accepted XML, database format and the configuration part of the CLI commands that are generated. - The yang specifications - This is the central part. It changes the XML, database and the config cli.
- routing_cli.cli - Change the fixed part of the CLI commands - routing_cli.cli - Change the fixed part of the CLI commands
- routing_cli.c - New cli commands may need to write a new C-funtion. - routing_cli.c - Cli C-commands are placed here.
- routing_backend.c - What happens at commit. - routing_backend.c - Commit and validate functions.
- routing_netconf.c - Modify semantics of netconf commands. - routing_netconf.c - Modify semantics of netconf commands.
Q: How do you check what is in a database? Q: How do you check what is in a database?
clicon_dbctrl -d <database> -p ------------------------------------------
The name of the running or candidate databases are found in the Use clicon_dbctrl. The name of the running or candidate databases are found in the
configuration file, eg /usr/local/var/routing/candidate_db configuration file.
Example: Example:
/interfaces/interface/eth0/ipv4 > clicon_dbctrl -d /usr/local/var/routing/candidate_db / -p
/interfaces/interface/eth0/type bgp /interfaces/interface/eth0/ipv4
/interfaces/interface/eth0 /interfaces/interface/eth0/type bgp
/interfaces/interface/eth0/name eth0 /interfaces/interface/eth0
Each line corresponds to a database entry (node in an XML tree). If /interfaces/interface/eth0/name eth0
the node is a leaf, the value appears as the second entry. Each line corresponds to a database entry (node in an XML tree).
If the node is a leaf, the value appears as the second entry ('bgp' and 'eth0').
Q: How do you write a commit rule in the example? Q: How do you write a commit function?
A: You write a commit function in routing_backend.c. --------------------------------------
You write a commit function in routing_backend.c.
Every time a commit is made, transaction_commit() is called in the Every time a commit is made, transaction_commit() is called in the
backend. It has a 'transaction_data td' argument which is used to fetch backend. It has a 'transaction_data td' argument which is used to fetch
information on added, deleted and changed entries. You access this information on added, deleted and changed entries. You access this
information using access functions as defined in clicon_backend_transaction.h information using access functions as defined in clicon_backend_transaction.h
Q: How do you check what has changed on commit? Q: How do you check what has changed on commit?
A: You use XPATHs on the XML trees in the transaction commit callback. -----------------------------------------------
You use XPATHs on the XML trees in the transaction commit callback.
Suppose you want to print all added interfaces: Suppose you want to print all added interfaces:
cxobj *target = transaction_target(td); # wanted XML tree cxobj *target = transaction_target(td); # wanted XML tree
# Get all added i/fs vec = xpath_vec_flag(target, "//interface", &len, XML_FLAG_ADD); /* Get added i/fs */
cxobj **vec = xpath_vec_flag(target, "//interface", &len, XML_FLAG_ADD); for (i=0; i<len; i++) /* Loop over added i/fs */
for (i=0; i<len; i++) # Loop over added i/fs clicon_xml2file(stdout, vec[i], 0, 1); /* Print the added interface */
clicon_xml2file(stdout, vec[i]) # Print the added interface You can look for added, deleted and changed entries in this way.
The flags you can check for are: XML_FLAG_ADD, _DEL and _CHANGE, and their combinations.
Q How do I access the XML tree? Q: How do I access the XML tree?
A: Using XPATH, find and iteration functions defined in the XML library. Example --------------------------------
Using XPATH, find and iteration functions defined in the XML library. Example library functions:
xml_child_each(),
xml_find(),
xml_body(),
clicon_xml2file(),
xml_apply()
More are found in the doxygen reference.
Q: How do you write a CLI callback function? Q: How do you write a CLI callback function?
You add an entry in routing_cli.cli --------------------------------------------
(1) You add an entry in routing_cli.cli
example("This is a comment") <var:int32>("This is a variable"), mycallback("myarg"); example("This is a comment") <var:int32>("This is a variable"), mycallback("myarg");
Then define a function in routing_cli.c (2) Then define a function in routing_cli.c
mycallback(clicon_handle h, cvec *cvv, cg_var *arg) mycallback(clicon_handle h, cvec *cvv, cg_var *arg)
where 'cvv' contains the value of the variable and 'arg' contains the where 'cvv' contains the value of the variable and 'arg' contains the
function parameter 'myarg'. function parameter 'myarg'.
Q: What are cg_var and cvec used in CLI callbacks? Q: What are cg_var and cvec used in CLI callbacks?
Those are 'CLIgen variables' and vector of CLOgen --------------------------------------------------
variables. Documented in CLIgen documentation. Some examples on usage is found in the Those are 'CLIgen variables' and vector of CLIgen variables.
They are documented in CLIgen documentation. Some examples on usage is found in the
routing_cli.c routing_cli.c
Q: How do you write a validation rule? Q: How do you write a validation function?
Similar to a commit rule, but instead write the transaction_validate() function. ------------------------------------------
Check for consitencies in the XML trees and if they fail, make an clicon_err() call. Similar to a commit function, but instead write the transaction_validate() function.
Check for inconsistencies in the XML trees and if they fail, make an clicon_err() call.
clicon_err(OE_PLUGIN, 0, "Route %s lacks ipv4 addr", name); clicon_err(OE_PLUGIN, 0, "Route %s lacks ipv4 addr", name);
return -1; return -1;
The validation or commit will then be aborted. The validation or commit will then be aborted.

View file

@ -30,13 +30,13 @@ pdflatex = @PDFLATEX@
SUBDIRS = SUBDIRS =
.PHONY: clean all $(SUBDIRS) .PHONY: clean all doc $(SUBDIRS)
all: $(SUBDIRS) doc all: $(SUBDIRS) doc
echo "Build doxygen doc and graphs: make graphs" echo "Build doxygen doc: make doc"
# Regular doxygen documentation # Regular doxygen documentation
doc: doc:
doxygen Doxyfile # generates html dir doxygen Doxyfile # generates html dir
echo "Build doxygen graphs: make graphs" echo "Build doxygen graphs: make graphs"

View file

@ -40,28 +40,36 @@
/* These include signatures for plugin and transaction callbacks. */ /* These include signatures for plugin and transaction callbacks. */
#include <clicon/clicon_backend.h> #include <clicon/clicon_backend.h>
/* /*! This is called on validate (and commit). Check validity of candidate
* Commit callback. */
* We do nothing here but simply create the config based on the current int
* db once everything is done as if will then contain the new config. transaction_validate(clicon_handle h,
transaction_data td)
{
if (debug)
transaction_print(stderr, td);
return 0;
}
/*! This is called on commit. Identify modifications and adjust machine state
*/ */
int int
transaction_commit(clicon_handle h, transaction_commit(clicon_handle h,
transaction_data td) transaction_data td)
{ {
fprintf(stderr, "%s\n", __FUNCTION__); cxobj *target = transaction_target(td); /* wanted XML tree */
transaction_print(stderr, td); cxobj **vec;
int i;
size_t len;
/* Get all added i/fs */
vec = xpath_vec_flag(target, "//interface", XML_FLAG_ADD, &len);
for (i=0; i<len; i++) /* Loop over added i/fs */
clicon_xml2file(stdout, vec[i], 0, 1); /* Print the added interface */
return 0; return 0;
} }
int
transaction_validate(clicon_handle h,
transaction_data td)
{
fprintf(stderr, "%s\n", __FUNCTION__);
transaction_print(stderr, td);
return 0;
}
/* /*
* Plugin initialization * Plugin initialization

View file

@ -106,6 +106,6 @@ cxobj *xml_insert(cxobj *xt, char *tag);
int cxvec_dup(cxobj **vec0, size_t len0, cxobj ***vec1, size_t *len1); int cxvec_dup(cxobj **vec0, size_t len0, cxobj ***vec1, size_t *len1);
int cxvec_append(cxobj *x, cxobj ***vec, size_t *len); int cxvec_append(cxobj *x, cxobj ***vec, size_t *len);
int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg); int xml_apply(cxobj *xn, enum cxobj_type type, xml_applyfn_t fn, void *arg);
int xml_apply_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg);
#endif /* _CLICON_XML_H */ #endif /* _CLICON_XML_H */

View file

@ -32,8 +32,8 @@ int xmlkeyfmt2key2(char *xkfmt, cvec *cvv, char **xk);
int xmlkey2xml(char *xkey, yang_spec *yspec, char **xml); int xmlkey2xml(char *xkey, yang_spec *yspec, char **xml);
int xmldb_get(char *dbname, char *xpath, int xmldb_get(char *dbname, char *xpath,
yang_spec *yspec, cxobj **xtop); yang_spec *yspec, cxobj **xtop);
int xmldb_get_xpath(char *dbname, char *xpath, yang_spec *yspec, int xmldb_get_vec(char *dbname, char *xpath, yang_spec *yspec,
cxobj **xtop, cxobj ***xvec, int *xlen); cxobj **xtop, cxobj ***xvec, size_t *xlen);
int xmldb_put( char *dbname, cxobj *xt, int xmldb_put( char *dbname, cxobj *xt,
yang_spec *yspec, enum operation_type op); yang_spec *yspec, enum operation_type op);
int xmldb_put_xkey(char *dbname, char *xkey, char *val, yang_spec *yspec, int xmldb_put_xkey(char *dbname, char *xkey, char *val, yang_spec *yspec,

View file

@ -28,6 +28,7 @@
*/ */
cxobj *xpath_first(cxobj *xn_top, char *xpath); cxobj *xpath_first(cxobj *xn_top, char *xpath);
cxobj *xpath_each(cxobj *xn_top, char *xpath, cxobj *prev); cxobj *xpath_each(cxobj *xn_top, char *xpath, cxobj *prev);
cxobj **xpath_vec(cxobj *xn_top, char *xpath, int *xv_len); cxobj **xpath_vec(cxobj *xn_top, char *xpath, size_t *xv_len);
cxobj **xpath_vec_flag(cxobj *cxtop, char *xpath, uint16_t flags, size_t *veclen);
#endif /* _CLICON_XSL_H */ #endif /* _CLICON_XSL_H */

View file

@ -36,6 +36,7 @@
/* clicon */ /* clicon */
#include "clicon_err.h" #include "clicon_err.h"
#include "clicon_log.h"
#include "clicon_queue.h" #include "clicon_queue.h"
#include "clicon_chunk.h" #include "clicon_chunk.h"
#include "clicon_xml.h" #include "clicon_xml.h"
@ -998,7 +999,7 @@ cxvec_append(cxobj *x,
return retval; return retval;
} }
/*! Apply a function call recursively on all xml node recursively /*! Apply a function call recursively on all xml node children recursively
* Recursively traverse all xml nodes in a parse-tree and apply fn(arg) for * Recursively traverse all xml nodes in a parse-tree and apply fn(arg) for
* each object found. The function is called with the xml node and an * each object found. The function is called with the xml node and an
* argument as args. * argument as args.
@ -1037,3 +1038,40 @@ xml_apply(cxobj *xn,
done: done:
return retval; return retval;
} }
/*! Apply a function call recursively on all ancestors
* Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for
* each object found. The function is called with the xml node and an
* argument as args.
* @param[in] xn XML node
* @param[in] fn Callback
* @param[in] arg Argument
* @code
* int x_fn(cxobj *x, void *arg)
* {
* return 0;
* }
* xml_apply_ancestor(xn, x_fn, NULL);
* @endcode
* @see xml_apply
* @note do not delete or move around any children during this function
* @note It does not apply fn to the root node,..
*/
int
xml_apply_ancestor(cxobj *xn,
xml_applyfn_t fn,
void *arg)
{
int retval = -1;
cxobj *xp = NULL;
while ((xp = xml_parent(xn)) != NULL) {
if (fn(xp, arg) < 0)
goto done;
if (xml_apply_ancestor(xp, fn, arg) < 0)
goto done;
}
retval = 0;
done:
return retval;
}

View file

@ -667,7 +667,7 @@ xmldb_get(char *dbname,
struct db_pair *pairs; struct db_pair *pairs;
cxobj *xt = NULL; cxobj *xt = NULL;
cxobj **xvec=NULL; cxobj **xvec=NULL;
int len; size_t len;
/* Read in complete database (this can be optimized) */ /* Read in complete database (this can be optimized) */
if ((npairs = db_regexp(dbname, "", __FUNCTION__, &pairs, 0)) < 0) if ((npairs = db_regexp(dbname, "", __FUNCTION__, &pairs, 0)) < 0)
@ -735,10 +735,10 @@ xmldb_get(char *dbname,
* @code * @code
* cxobj *xt; * cxobj *xt;
* cxobj **xvec; * cxobj **xvec;
* int xlen; * size_t xlen;
* yang_spec *yspec = clicon_dbspec_yang(h); * yang_spec *yspec = clicon_dbspec_yang(h);
* if (xmldb_get_xpath(dbname, "/interfaces/interface[name="eth*"]", yspec, * if (xmldb_get_vec(dbname, "/interfaces/interface[name="eth*"]", yspec,
* &xt, &xvec, &xlen) < 0) * &xt, &xvec, &xlen) < 0)
* err; * err;
* for (i=0; i<xlen; i++){ * for (i=0; i<xlen; i++){
* xn = xv[i]; * xn = xv[i];
@ -751,12 +751,12 @@ xmldb_get(char *dbname,
* @see xmldb_get * @see xmldb_get
*/ */
int int
xmldb_get_xpath(char *dbname, xmldb_get_vec(char *dbname,
char *xpath, char *xpath,
yang_spec *yspec, yang_spec *yspec,
cxobj **xtop, cxobj **xtop,
cxobj ***xvec, cxobj ***xvec,
int *xlen) size_t *xlen)
{ {
int retval = -1; int retval = -1;
int i; int i;

View file

@ -74,6 +74,7 @@ to the xml standards:
/* clicon */ /* clicon */
#include "clicon_err.h" #include "clicon_err.h"
#include "clicon_log.h"
#include "clicon_xml.h" #include "clicon_xml.h"
#include "clicon_xsl.h" #include "clicon_xsl.h"
@ -304,6 +305,7 @@ static int
recursive_find(cxobj *xn, recursive_find(cxobj *xn,
char *pattern, char *pattern,
int node_type, int node_type,
uint16_t flags,
cxobj ***vec0, cxobj ***vec0,
size_t *vec0len) size_t *vec0len)
{ {
@ -315,11 +317,13 @@ recursive_find(cxobj *xn,
xsub = NULL; xsub = NULL;
while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) { while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
if (fnmatch(pattern, xml_name(xsub), 0) == 0){ if (fnmatch(pattern, xml_name(xsub), 0) == 0){
if (cxvec_append(xsub, &vec, &veclen) < 0) clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
goto done; if (flags==0x0 || xml_flag(xsub, flags))
if (cxvec_append(xsub, &vec, &veclen) < 0)
goto done;
// continue; /* Dont go deeper */ // continue; /* Dont go deeper */
} }
if (recursive_find(xsub, pattern, node_type, &vec, &veclen) < 0) if (recursive_find(xsub, pattern, node_type, flags, &vec, &veclen) < 0)
goto done; goto done;
} }
retval = 0; retval = 0;
@ -331,8 +335,9 @@ recursive_find(cxobj *xn,
static int static int
xpath_expr(char *e, xpath_expr(char *e,
cxobj ***vec0, uint16_t flags,
size_t *vec0len) cxobj ***vec0,
size_t *vec0len)
{ {
char *e_a; char *e_a;
char *e_v; char *e_v;
@ -359,9 +364,12 @@ xpath_expr(char *e,
xv = (*vec0)[i]; xv = (*vec0)[i];
if ((x = xml_find(xv, e_a)) != NULL && if ((x = xml_find(xv, e_a)) != NULL &&
(xml_type(x) == CX_ATTR)){ (xml_type(x) == CX_ATTR)){
if (!e_v || strcmp(xml_value(x), e_v) == 0) if (!e_v || strcmp(xml_value(x), e_v) == 0){
if (cxvec_append(xv, &vec, &veclen) < 0) clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
goto done; if (flags==0x0 || xml_flag(xv, flags))
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
}
} }
} }
} }
@ -371,8 +379,10 @@ xpath_expr(char *e,
if (sscanf(e, "%d", &i) == 1){ /* number */ if (sscanf(e, "%d", &i) == 1){ /* number */
if (i < *vec0len){ if (i < *vec0len){
xv = (*vec0)[i]; /* XXX: cant compress: gcc breaks */ xv = (*vec0)[i]; /* XXX: cant compress: gcc breaks */
if (cxvec_append(xv, &vec, &veclen) < 0) clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
goto done; if (flags==0x0 || xml_flag(xv, flags))
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
} }
} }
else{ else{
@ -393,8 +403,10 @@ xpath_expr(char *e,
(xml_type(x) == CX_ELMNT)){ (xml_type(x) == CX_ELMNT)){
if ((val = xml_body(x)) != NULL && if ((val = xml_body(x)) != NULL &&
strcmp(val, e) == 0){ strcmp(val, e) == 0){
if (cxvec_append(xv, &vec, &veclen) < 0) clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
goto done; if (flags==0x0 || xml_flag(xv, flags))
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
} }
} }
} }
@ -428,6 +440,7 @@ xpath_find(struct xpath_element *xe,
int descendants0, int descendants0,
cxobj **vec0, cxobj **vec0,
size_t vec0len, size_t vec0len,
uint16_t flags,
cxobj ***vec2, cxobj ***vec2,
size_t *vec2len size_t *vec2len
) )
@ -442,10 +455,12 @@ xpath_find(struct xpath_element *xe,
size_t vec1len = 0; size_t vec1len = 0;
if (xe == NULL){ if (xe == NULL){
// append /* append */
for (i=0; i<vec0len; i++){ for (i=0; i<vec0len; i++){
xv = vec0[i]; xv = vec0[i];
cxvec_append(xv, vec2, vec2len); clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
if (flags==0x0 || xml_flag(xv, flags))
cxvec_append(xv, vec2, vec2len);
} }
free(vec0); free(vec0);
return 0; return 0;
@ -480,7 +495,7 @@ xpath_find(struct xpath_element *xe,
if (descendants0){ if (descendants0){
for (i=0; i<vec0len; i++){ for (i=0; i<vec0len; i++){
xv = vec0[i]; xv = vec0[i];
if (recursive_find(xv, xe->xe_str, CX_ELMNT, &vec1, &vec1len) < 0) if (recursive_find(xv, xe->xe_str, CX_ELMNT, flags, &vec1, &vec1len) < 0)
goto done; goto done;
} }
} }
@ -490,8 +505,10 @@ xpath_find(struct xpath_element *xe,
x = NULL; x = NULL;
while ((x = xml_child_each(xv, x, -1)) != NULL) { while ((x = xml_child_each(xv, x, -1)) != NULL) {
if (fnmatch(xe->xe_str, xml_name(x), 0) == 0){ if (fnmatch(xe->xe_str, xml_name(x), 0) == 0){
if (cxvec_append(x, &vec1, &vec1len) < 0) clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(x, flags));
goto done; if (flags==0x0 || xml_flag(x, flags))
if (cxvec_append(x, &vec1, &vec1len) < 0)
goto done;
} }
} }
} }
@ -517,10 +534,10 @@ xpath_find(struct xpath_element *xe,
} }
} }
if (xe->xe_predicate) if (xe->xe_predicate)
if (xpath_expr(xe->xe_predicate, &vec0, &vec0len) < 0) if (xpath_expr(xe->xe_predicate, flags, &vec0, &vec0len) < 0)
goto done; goto done;
if (xpath_find(xe->xe_next, descendants, if (xpath_find(xe->xe_next, descendants,
vec0, vec0len, vec0, vec0len, flags,
vec2, vec2len) < 0) vec2, vec2len) < 0)
goto done; goto done;
retval = 0; retval = 0;
@ -574,6 +591,7 @@ static int
xpath_exec(char *xpath, xpath_exec(char *xpath,
cxobj **vec0, cxobj **vec0,
size_t vec0len, size_t vec0len,
uint16_t flags,
cxobj ***vec2, cxobj ***vec2,
size_t *vec2len) size_t *vec2len)
{ {
@ -587,7 +605,7 @@ xpath_exec(char *xpath,
goto done; goto done;
if (0) if (0)
xpath_print(stderr, xplist); xpath_print(stderr, xplist);
if (xpath_find(xplist, 0, vec1, vec1len, vec2, vec2len) < 0) if (xpath_find(xplist, 0, vec1, vec1len, flags, vec2, vec2len) < 0)
goto done; goto done;
if (xpath_free(xplist) < 0) if (xpath_free(xplist) < 0)
goto done; goto done;
@ -607,6 +625,7 @@ xpath_exec(char *xpath,
static int static int
xpath_choice(cxobj *xtop, xpath_choice(cxobj *xtop,
char *xpath0, char *xpath0,
uint16_t flags,
cxobj ***vec1, cxobj ***vec1,
size_t *vec1len) size_t *vec1len)
{ {
@ -637,7 +656,7 @@ xpath_choice(cxobj *xtop,
} }
xpath = s1; xpath = s1;
s1 = s2; s1 = s2;
if (xpath_exec(xpath, vec0, vec0len, vec1, vec1len) < 0) if (xpath_exec(xpath, vec0, vec0len, flags, vec1, vec1len) < 0)
goto done; goto done;
} }
retval = 0; retval = 0;
@ -667,13 +686,14 @@ xpath_choice(cxobj *xtop,
* @see also xpath_vec. * @see also xpath_vec.
*/ */
cxobj * cxobj *
xpath_first(cxobj *cxtop, char *xpath) xpath_first(cxobj *cxtop,
char *xpath)
{ {
cxobj **vec0 = NULL; cxobj **vec0 = NULL;
size_t vec0len = 0; size_t vec0len = 0;
cxobj *xn = NULL; cxobj *xn = NULL;
if (xpath_choice(cxtop, xpath, &vec0, &vec0len) < 0) if (xpath_choice(cxtop, xpath, 0, &vec0, &vec0len) < 0)
goto done; goto done;
if (vec0len) if (vec0len)
xn = vec0[0]; xn = vec0[0];
@ -707,7 +727,9 @@ xpath_first(cxobj *cxtop, char *xpath)
* NOTE: uses a static variable: consider replacing with xpath_vec() instead * NOTE: uses a static variable: consider replacing with xpath_vec() instead
*/ */
cxobj * cxobj *
xpath_each(cxobj *cxtop, char *xpath, cxobj *xprev) xpath_each(cxobj *cxtop,
char *xpath,
cxobj *xprev)
{ {
static cxobj **vec0 = NULL; /* XXX */ static cxobj **vec0 = NULL; /* XXX */
static size_t vec0len = 0; static size_t vec0len = 0;
@ -718,7 +740,7 @@ xpath_each(cxobj *cxtop, char *xpath, cxobj *xprev)
if (vec0) // XXX if (vec0) // XXX
free(vec0); // XXX free(vec0); // XXX
vec0len = 0; vec0len = 0;
if (xpath_choice(cxtop, xpath, &vec0, &vec0len) < 0) if (xpath_choice(cxtop, xpath, 0, &vec0, &vec0len) < 0)
goto done; goto done;
} }
if (vec0len){ if (vec0len){
@ -765,14 +787,31 @@ xpath_each(cxobj *cxtop, char *xpath, cxobj *xprev)
* @see also xpath_first, xpath_each. * @see also xpath_first, xpath_each.
*/ */
cxobj ** cxobj **
xpath_vec(cxobj *cxtop, xpath_vec(cxobj *cxtop,
char *xpath, char *xpath,
int *veclen) size_t *veclen)
{ {
cxobj **vec=NULL; cxobj **vec=NULL;
*veclen = 0; *veclen = 0;
if (xpath_choice(cxtop, xpath, &vec, (size_t*)veclen) < 0) if (xpath_choice(cxtop, xpath, 0, &vec, (size_t*)veclen) < 0)
return NULL;
return vec;
}
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
* @param[in] flags Set of flags that return nodes must match (0 if all)
*/
cxobj **
xpath_vec_flag(cxobj *cxtop,
char *xpath,
uint16_t flags,
size_t *veclen)
{
cxobj **vec=NULL;
*veclen = 0;
if (xpath_choice(cxtop, xpath, flags, &vec, (size_t*)veclen) < 0)
return NULL; return NULL;
return vec; return vec;
} }