commit xpath
This commit is contained in:
parent
554c1cb9de
commit
9bd941ab76
14 changed files with 306 additions and 130 deletions
19
Makefile.in
19
Makefile.in
|
|
@ -42,7 +42,7 @@ LIBS = @LIBS@
|
|||
INCLUDES = -I. -I@srcdir@ @INCLUDES@
|
||||
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
|
||||
|
||||
|
|
@ -52,7 +52,7 @@ $(SUBDIRS):
|
|||
(cd $@ && $(MAKE) $(MFLAGS) all)
|
||||
|
||||
depend:
|
||||
for i in $(SUBDIRS) example; \
|
||||
for i in $(SUBDIRS) doc example; \
|
||||
do (cd $$i && $(MAKE) $(MFLAGS) depend); done
|
||||
|
||||
# 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) $< > $@
|
||||
|
||||
install: clicon.conf.cpp clicon.mk
|
||||
for i in $(SUBDIRS); \
|
||||
for i in $(SUBDIRS) doc; \
|
||||
do (cd $$i && $(MAKE) $(MFLAGS) $@); done; \
|
||||
install -d -m 755 $(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"
|
||||
|
||||
install-include:
|
||||
for i in $(SUBDIRS); \
|
||||
for i in $(SUBDIRS) doc; \
|
||||
do (cd $$i && $(MAKE) $(MFLAGS) $@); done; \
|
||||
echo "To install example app: cd example; make; make install"
|
||||
|
||||
uninstall:
|
||||
for i in $(SUBDIRS) example; \
|
||||
for i in $(SUBDIRS) doc example; \
|
||||
do (cd $$i && $(MAKE) $(MFLAGS) $@); done;
|
||||
rm -f $(datadir)/clicon/clicon.conf.cpp
|
||||
rm -f $(datadir)/clicon/clicon.mk
|
||||
|
||||
# Overrides SUBDIRS above
|
||||
#doc:
|
||||
# cd $@; $(MAKE) $(MFLAGS) $@
|
||||
doc:
|
||||
cd $@; $(MAKE) $(MFLAGS) $@
|
||||
|
||||
config.status: configure
|
||||
$(SHELL) config.status --recheck
|
||||
|
|
@ -92,14 +91,14 @@ configure: configure.ac
|
|||
cd $(srcdir) && autoconf
|
||||
|
||||
clean:
|
||||
for i in $(SUBDIRS) example; \
|
||||
for i in $(SUBDIRS) doc example; \
|
||||
do (cd $$i && $(MAKE) $(MFLAGS) $@); done;
|
||||
|
||||
distclean:
|
||||
rm -f Makefile TAGS config.status config.log *~ .depend
|
||||
rm -rf Makefile autom4te.cache
|
||||
rm -rf clicon.conf.cpp clicon.mk
|
||||
for i in $(SUBDIRS) example; \
|
||||
for i in $(SUBDIRS) doc example; \
|
||||
do (cd $$i && $(MAKE) $(MFLAGS) $@); done
|
||||
|
||||
# Lines of code
|
||||
|
|
|
|||
|
|
@ -126,8 +126,8 @@ candidate_commit(clicon_handle h,
|
|||
char *running)
|
||||
{
|
||||
int retval = -1;
|
||||
// int i, j;
|
||||
// int failed = 0;
|
||||
int i;
|
||||
cxobj *xn;
|
||||
struct stat sb;
|
||||
void *firsterr = NULL;
|
||||
yang_spec *yspec;
|
||||
|
|
@ -171,6 +171,25 @@ candidate_commit(clicon_handle h,
|
|||
goto done;
|
||||
if (debug)
|
||||
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 */
|
||||
if (plugin_transaction_begin(h, td) < 0)
|
||||
|
|
@ -236,6 +255,8 @@ candidate_validate(clicon_handle h,
|
|||
struct stat sb;
|
||||
yang_spec *yspec;
|
||||
transaction_data_t *td = NULL;
|
||||
int i;
|
||||
cxobj *xn;
|
||||
|
||||
if ((yspec = clicon_dbspec_yang(h)) == NULL){
|
||||
clicon_err(OE_FATAL, 0, "No DB_SPEC");
|
||||
|
|
@ -276,6 +297,22 @@ candidate_validate(clicon_handle h,
|
|||
|
||||
if (debug)
|
||||
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 */
|
||||
if (plugin_transaction_begin(h, td) < 0)
|
||||
|
|
|
|||
|
|
@ -1492,7 +1492,7 @@ show_conf_xpath(clicon_handle h, cvec *cvv, cg_var *arg)
|
|||
cg_var *cv;
|
||||
cxobj *xt = NULL;
|
||||
cxobj **xv = NULL;
|
||||
int xlen;
|
||||
size_t xlen;
|
||||
int i;
|
||||
|
||||
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");
|
||||
xpath = cv_string_get(cv);
|
||||
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;
|
||||
for (i=0; i<xlen; i++)
|
||||
clicon_xml2file(stdout, xv[i], 0, 1);
|
||||
|
|
|
|||
|
|
@ -219,11 +219,11 @@ netconf_xpath(cxobj *xsearch,
|
|||
cxobj *xorig)
|
||||
{
|
||||
cxobj *x;
|
||||
int retval = -1;
|
||||
char *selector;
|
||||
int retval = -1;
|
||||
char *selector;
|
||||
cxobj **xv;
|
||||
int xlen;
|
||||
int i;
|
||||
size_t xlen;
|
||||
int i;
|
||||
|
||||
if ((selector = xml_find_value(xfilter, "select")) == NULL){
|
||||
netconf_create_rpc_error(cb_err, xorig,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8
|
|||
# title of most generated pages and in a few other places.
|
||||
# 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
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
|
|
|
|||
154
doc/FAQ.txt
154
doc/FAQ.txt
|
|
@ -1,86 +1,140 @@
|
|||
Frequently Asked Questions - Clixon
|
||||
Frequently Asked Questions - CliXon
|
||||
===================================
|
||||
|
||||
Q: What is the best way of understanding Clixon?
|
||||
A: Run the ietf yang routing example.
|
||||
Q: What is CliXon?
|
||||
------------------
|
||||
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)?
|
||||
A: Clixon: ./configure; make; sudo make install; sudo make install-include
|
||||
Example: cd example; make; sudo make install
|
||||
Q: Why should you use CliXon?
|
||||
-----------------------------
|
||||
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?
|
||||
A: Clixon uses Doxygen for reference documentation.
|
||||
Q: What license is available?
|
||||
-----------------------------
|
||||
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
|
||||
|
||||
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 netconf session: clicon_netconf -f /usr/local/etc/routing.conf
|
||||
------------------------------
|
||||
- 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 netconf session: clicon_netconf -f /usr/local/etc/routing.conf
|
||||
|
||||
Q: How does Clixon differ from Clicon?
|
||||
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?
|
||||
Q: How do you change the example?
|
||||
---------------------------------
|
||||
- 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.c - New cli commands may need to write a new C-funtion.
|
||||
- routing_backend.c - What happens at commit.
|
||||
- routing_cli.c - Cli C-commands are placed here.
|
||||
- routing_backend.c - Commit and validate functions.
|
||||
- routing_netconf.c - Modify semantics of netconf commands.
|
||||
|
||||
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
|
||||
configuration file, eg /usr/local/var/routing/candidate_db
|
||||
------------------------------------------
|
||||
Use clicon_dbctrl. The name of the running or candidate databases are found in the
|
||||
configuration file.
|
||||
Example:
|
||||
/interfaces/interface/eth0/ipv4
|
||||
/interfaces/interface/eth0/type bgp
|
||||
/interfaces/interface/eth0
|
||||
/interfaces/interface/eth0/name eth0
|
||||
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.
|
||||
> clicon_dbctrl -d /usr/local/var/routing/candidate_db / -p
|
||||
/interfaces/interface/eth0/ipv4
|
||||
/interfaces/interface/eth0/type bgp
|
||||
/interfaces/interface/eth0
|
||||
/interfaces/interface/eth0/name eth0
|
||||
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?
|
||||
A: You write a commit function in routing_backend.c.
|
||||
Q: How do you write a commit function?
|
||||
--------------------------------------
|
||||
You write a commit function in routing_backend.c.
|
||||
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
|
||||
information on added, deleted and changed entries. You access this
|
||||
information using access functions as defined in clicon_backend_transaction.h
|
||||
|
||||
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:
|
||||
cxobj *target = transaction_target(td); # wanted XML tree
|
||||
# Get all 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
|
||||
clicon_xml2file(stdout, vec[i]) # Print the added interface
|
||||
The flags you can check for are: XML_FLAG_ADD, _DEL and _CHANGE, and their combinations.
|
||||
cxobj *target = transaction_target(td); # wanted XML tree
|
||||
vec = xpath_vec_flag(target, "//interface", &len, XML_FLAG_ADD); /* Get 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 */
|
||||
You can look for added, deleted and changed entries in this way.
|
||||
|
||||
Q How do I access the XML tree?
|
||||
A: Using XPATH, find and iteration functions defined in the XML library. Example
|
||||
Q: How do I access the XML tree?
|
||||
--------------------------------
|
||||
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?
|
||||
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");
|
||||
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)
|
||||
where 'cvv' contains the value of the variable and 'arg' contains the
|
||||
function parameter 'myarg'.
|
||||
|
||||
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
|
||||
|
||||
Q: How do you write a validation rule?
|
||||
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.
|
||||
Q: How do you write a validation function?
|
||||
------------------------------------------
|
||||
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);
|
||||
return -1;
|
||||
The validation or commit will then be aborted.
|
||||
|
|
|
|||
|
|
@ -30,13 +30,13 @@ pdflatex = @PDFLATEX@
|
|||
|
||||
SUBDIRS =
|
||||
|
||||
.PHONY: clean all $(SUBDIRS)
|
||||
.PHONY: clean all doc $(SUBDIRS)
|
||||
|
||||
all: $(SUBDIRS) doc
|
||||
echo "Build doxygen doc and graphs: make graphs"
|
||||
echo "Build doxygen doc: make doc"
|
||||
|
||||
# Regular doxygen documentation
|
||||
doc:
|
||||
doc:
|
||||
doxygen Doxyfile # generates html dir
|
||||
echo "Build doxygen graphs: make graphs"
|
||||
|
||||
|
|
|
|||
|
|
@ -40,28 +40,36 @@
|
|||
/* These include signatures for plugin and transaction callbacks. */
|
||||
#include <clicon/clicon_backend.h>
|
||||
|
||||
/*
|
||||
* Commit callback.
|
||||
* We do nothing here but simply create the config based on the current
|
||||
* db once everything is done as if will then contain the new config.
|
||||
/*! This is called on validate (and commit). Check validity of candidate
|
||||
*/
|
||||
int
|
||||
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
|
||||
transaction_commit(clicon_handle h,
|
||||
transaction_data td)
|
||||
{
|
||||
fprintf(stderr, "%s\n", __FUNCTION__);
|
||||
transaction_print(stderr, td);
|
||||
cxobj *target = transaction_target(td); /* wanted XML tree */
|
||||
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;
|
||||
}
|
||||
|
||||
int
|
||||
transaction_validate(clicon_handle h,
|
||||
transaction_data td)
|
||||
{
|
||||
fprintf(stderr, "%s\n", __FUNCTION__);
|
||||
transaction_print(stderr, td);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Plugin initialization
|
||||
|
|
|
|||
|
|
@ -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_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_ancestor(cxobj *xn, xml_applyfn_t fn, void *arg);
|
||||
|
||||
#endif /* _CLICON_XML_H */
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ int xmlkeyfmt2key2(char *xkfmt, cvec *cvv, char **xk);
|
|||
int xmlkey2xml(char *xkey, yang_spec *yspec, char **xml);
|
||||
int xmldb_get(char *dbname, char *xpath,
|
||||
yang_spec *yspec, cxobj **xtop);
|
||||
int xmldb_get_xpath(char *dbname, char *xpath, yang_spec *yspec,
|
||||
cxobj **xtop, cxobj ***xvec, int *xlen);
|
||||
int xmldb_get_vec(char *dbname, char *xpath, yang_spec *yspec,
|
||||
cxobj **xtop, cxobj ***xvec, size_t *xlen);
|
||||
int xmldb_put( char *dbname, cxobj *xt,
|
||||
yang_spec *yspec, enum operation_type op);
|
||||
int xmldb_put_xkey(char *dbname, char *xkey, char *val, yang_spec *yspec,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
*/
|
||||
cxobj *xpath_first(cxobj *xn_top, char *xpath);
|
||||
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 */
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
/* clicon */
|
||||
#include "clicon_err.h"
|
||||
#include "clicon_log.h"
|
||||
#include "clicon_queue.h"
|
||||
#include "clicon_chunk.h"
|
||||
#include "clicon_xml.h"
|
||||
|
|
@ -998,7 +999,7 @@ cxvec_append(cxobj *x,
|
|||
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
|
||||
* each object found. The function is called with the xml node and an
|
||||
* argument as args.
|
||||
|
|
@ -1037,3 +1038,40 @@ xml_apply(cxobj *xn,
|
|||
done:
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -667,7 +667,7 @@ xmldb_get(char *dbname,
|
|||
struct db_pair *pairs;
|
||||
cxobj *xt = NULL;
|
||||
cxobj **xvec=NULL;
|
||||
int len;
|
||||
size_t len;
|
||||
|
||||
/* Read in complete database (this can be optimized) */
|
||||
if ((npairs = db_regexp(dbname, "", __FUNCTION__, &pairs, 0)) < 0)
|
||||
|
|
@ -735,10 +735,10 @@ xmldb_get(char *dbname,
|
|||
* @code
|
||||
* cxobj *xt;
|
||||
* cxobj **xvec;
|
||||
* int xlen;
|
||||
* size_t xlen;
|
||||
* yang_spec *yspec = clicon_dbspec_yang(h);
|
||||
* if (xmldb_get_xpath(dbname, "/interfaces/interface[name="eth*"]", yspec,
|
||||
* &xt, &xvec, &xlen) < 0)
|
||||
* if (xmldb_get_vec(dbname, "/interfaces/interface[name="eth*"]", yspec,
|
||||
* &xt, &xvec, &xlen) < 0)
|
||||
* err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xv[i];
|
||||
|
|
@ -751,12 +751,12 @@ xmldb_get(char *dbname,
|
|||
* @see xmldb_get
|
||||
*/
|
||||
int
|
||||
xmldb_get_xpath(char *dbname,
|
||||
char *xpath,
|
||||
yang_spec *yspec,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec,
|
||||
int *xlen)
|
||||
xmldb_get_vec(char *dbname,
|
||||
char *xpath,
|
||||
yang_spec *yspec,
|
||||
cxobj **xtop,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ to the xml standards:
|
|||
|
||||
/* clicon */
|
||||
#include "clicon_err.h"
|
||||
#include "clicon_log.h"
|
||||
#include "clicon_xml.h"
|
||||
#include "clicon_xsl.h"
|
||||
|
||||
|
|
@ -304,6 +305,7 @@ static int
|
|||
recursive_find(cxobj *xn,
|
||||
char *pattern,
|
||||
int node_type,
|
||||
uint16_t flags,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
{
|
||||
|
|
@ -315,11 +317,13 @@ recursive_find(cxobj *xn,
|
|||
xsub = NULL;
|
||||
while ((xsub = xml_child_each(xn, xsub, node_type)) != NULL) {
|
||||
if (fnmatch(pattern, xml_name(xsub), 0) == 0){
|
||||
if (cxvec_append(xsub, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xsub, flags));
|
||||
if (flags==0x0 || xml_flag(xsub, flags))
|
||||
if (cxvec_append(xsub, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
// 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;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -331,8 +335,9 @@ recursive_find(cxobj *xn,
|
|||
|
||||
static int
|
||||
xpath_expr(char *e,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
uint16_t flags,
|
||||
cxobj ***vec0,
|
||||
size_t *vec0len)
|
||||
{
|
||||
char *e_a;
|
||||
char *e_v;
|
||||
|
|
@ -359,9 +364,12 @@ xpath_expr(char *e,
|
|||
xv = (*vec0)[i];
|
||||
if ((x = xml_find(xv, e_a)) != NULL &&
|
||||
(xml_type(x) == CX_ATTR)){
|
||||
if (!e_v || strcmp(xml_value(x), e_v) == 0)
|
||||
if (cxvec_append(xv, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
if (!e_v || strcmp(xml_value(x), e_v) == 0){
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
|
||||
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 (i < *vec0len){
|
||||
xv = (*vec0)[i]; /* XXX: cant compress: gcc breaks */
|
||||
if (cxvec_append(xv, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
|
||||
if (flags==0x0 || xml_flag(xv, flags))
|
||||
if (cxvec_append(xv, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else{
|
||||
|
|
@ -393,8 +403,10 @@ xpath_expr(char *e,
|
|||
(xml_type(x) == CX_ELMNT)){
|
||||
if ((val = xml_body(x)) != NULL &&
|
||||
strcmp(val, e) == 0){
|
||||
if (cxvec_append(xv, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
|
||||
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,
|
||||
cxobj **vec0,
|
||||
size_t vec0len,
|
||||
uint16_t flags,
|
||||
cxobj ***vec2,
|
||||
size_t *vec2len
|
||||
)
|
||||
|
|
@ -442,10 +455,12 @@ xpath_find(struct xpath_element *xe,
|
|||
size_t vec1len = 0;
|
||||
|
||||
if (xe == NULL){
|
||||
// append
|
||||
/* append */
|
||||
for (i=0; i<vec0len; 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);
|
||||
return 0;
|
||||
|
|
@ -480,7 +495,7 @@ xpath_find(struct xpath_element *xe,
|
|||
if (descendants0){
|
||||
for (i=0; i<vec0len; 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -490,8 +505,10 @@ xpath_find(struct xpath_element *xe,
|
|||
x = NULL;
|
||||
while ((x = xml_child_each(xv, x, -1)) != NULL) {
|
||||
if (fnmatch(xe->xe_str, xml_name(x), 0) == 0){
|
||||
if (cxvec_append(x, &vec1, &vec1len) < 0)
|
||||
goto done;
|
||||
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(x, flags));
|
||||
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 (xpath_expr(xe->xe_predicate, &vec0, &vec0len) < 0)
|
||||
if (xpath_expr(xe->xe_predicate, flags, &vec0, &vec0len) < 0)
|
||||
goto done;
|
||||
if (xpath_find(xe->xe_next, descendants,
|
||||
vec0, vec0len,
|
||||
vec0, vec0len, flags,
|
||||
vec2, vec2len) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
|
|
@ -574,6 +591,7 @@ static int
|
|||
xpath_exec(char *xpath,
|
||||
cxobj **vec0,
|
||||
size_t vec0len,
|
||||
uint16_t flags,
|
||||
cxobj ***vec2,
|
||||
size_t *vec2len)
|
||||
{
|
||||
|
|
@ -587,7 +605,7 @@ xpath_exec(char *xpath,
|
|||
goto done;
|
||||
if (0)
|
||||
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;
|
||||
if (xpath_free(xplist) < 0)
|
||||
goto done;
|
||||
|
|
@ -607,6 +625,7 @@ xpath_exec(char *xpath,
|
|||
static int
|
||||
xpath_choice(cxobj *xtop,
|
||||
char *xpath0,
|
||||
uint16_t flags,
|
||||
cxobj ***vec1,
|
||||
size_t *vec1len)
|
||||
{
|
||||
|
|
@ -637,7 +656,7 @@ xpath_choice(cxobj *xtop,
|
|||
}
|
||||
xpath = s1;
|
||||
s1 = s2;
|
||||
if (xpath_exec(xpath, vec0, vec0len, vec1, vec1len) < 0)
|
||||
if (xpath_exec(xpath, vec0, vec0len, flags, vec1, vec1len) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -667,13 +686,14 @@ xpath_choice(cxobj *xtop,
|
|||
* @see also xpath_vec.
|
||||
*/
|
||||
cxobj *
|
||||
xpath_first(cxobj *cxtop, char *xpath)
|
||||
xpath_first(cxobj *cxtop,
|
||||
char *xpath)
|
||||
{
|
||||
cxobj **vec0 = NULL;
|
||||
size_t vec0len = 0;
|
||||
cxobj *xn = NULL;
|
||||
|
||||
if (xpath_choice(cxtop, xpath, &vec0, &vec0len) < 0)
|
||||
if (xpath_choice(cxtop, xpath, 0, &vec0, &vec0len) < 0)
|
||||
goto done;
|
||||
if (vec0len)
|
||||
xn = vec0[0];
|
||||
|
|
@ -707,7 +727,9 @@ xpath_first(cxobj *cxtop, char *xpath)
|
|||
* NOTE: uses a static variable: consider replacing with xpath_vec() instead
|
||||
*/
|
||||
cxobj *
|
||||
xpath_each(cxobj *cxtop, char *xpath, cxobj *xprev)
|
||||
xpath_each(cxobj *cxtop,
|
||||
char *xpath,
|
||||
cxobj *xprev)
|
||||
{
|
||||
static cxobj **vec0 = NULL; /* XXX */
|
||||
static size_t vec0len = 0;
|
||||
|
|
@ -718,7 +740,7 @@ xpath_each(cxobj *cxtop, char *xpath, cxobj *xprev)
|
|||
if (vec0) // XXX
|
||||
free(vec0); // XXX
|
||||
vec0len = 0;
|
||||
if (xpath_choice(cxtop, xpath, &vec0, &vec0len) < 0)
|
||||
if (xpath_choice(cxtop, xpath, 0, &vec0, &vec0len) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (vec0len){
|
||||
|
|
@ -765,14 +787,31 @@ xpath_each(cxobj *cxtop, char *xpath, cxobj *xprev)
|
|||
* @see also xpath_first, xpath_each.
|
||||
*/
|
||||
cxobj **
|
||||
xpath_vec(cxobj *cxtop,
|
||||
char *xpath,
|
||||
int *veclen)
|
||||
xpath_vec(cxobj *cxtop,
|
||||
char *xpath,
|
||||
size_t *veclen)
|
||||
{
|
||||
cxobj **vec=NULL;
|
||||
|
||||
*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 vec;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue