Added support for inverted regexps
This commit is contained in:
parent
2fe185d683
commit
5706703ab4
9 changed files with 68 additions and 50 deletions
|
|
@ -1,10 +1,11 @@
|
||||||
# Clixon Changelog
|
# Clixon Changelog
|
||||||
|
|
||||||
## 3.10.0/4.0.0 (Upcoming)
|
## 4.0.0 (Upcoming)
|
||||||
|
|
||||||
### Major New features
|
### Major New features
|
||||||
* Regexp improvements: Libxml2 XSD, multiple patterns, optimization
|
* Regexp improvements: Libxml2 XSD, multiple patterns, optimization
|
||||||
* Support for multiple patterns as described in RFC7950 Section 9.4.7
|
* Support for multiple patterns as described in RFC7950 Section 9.4.7
|
||||||
|
* Support for inverted patterns as described in RFC7950 Section 9.4.6
|
||||||
* Libxml2 support for full XSD matching as alternative to Posix translation
|
* Libxml2 support for full XSD matching as alternative to Posix translation
|
||||||
* Configure with: `./configure --with-libxml2`
|
* Configure with: `./configure --with-libxml2`
|
||||||
* Set `CLICON_YANG_REGEXP` to libxml2 (default is posix)
|
* Set `CLICON_YANG_REGEXP` to libxml2 (default is posix)
|
||||||
|
|
|
||||||
18
README.md
18
README.md
|
|
@ -115,11 +115,27 @@ However, the following YANG syntax modules are not implemented (reference to RFC
|
||||||
- refine (7.13.2)
|
- refine (7.13.2)
|
||||||
- status (7.21.2)
|
- status (7.21.2)
|
||||||
- extension (7.19)
|
- extension (7.19)
|
||||||
- modifier (9.4.6)
|
|
||||||
- YIN (13)
|
- YIN (13)
|
||||||
- Yang extended Xpath functions: re-match(), deref)(), derived-from(), derived-from-or-self(), enum-value(), bit-is-set() (10.2-10.6)
|
- Yang extended Xpath functions: re-match(), deref)(), derived-from(), derived-from-or-self(), enum-value(), bit-is-set() (10.2-10.6)
|
||||||
- Default values on leaf-lists are not supported (7.7.2)
|
- Default values on leaf-lists are not supported (7.7.2)
|
||||||
|
|
||||||
|
### Yang patterns
|
||||||
|
Yang type patterns use regexps defined in [W3C XML XSD](http://www.w3.org/TR/2004/REC-xmlschema-2-20041028). XSD regexp:s are
|
||||||
|
slightly different from POSIX regexp.
|
||||||
|
|
||||||
|
Clixon supports two regular expressions engines:
|
||||||
|
* "Posix" which is the default method, which _translates_ XSD regexp:s to posix before matching with the standard Linux regex engine. This translation is not complete but can be considered "good-enough" for most yang use-cases. For reference, all standard Yang models in [https://github.com/YangModels/yang] have been tested.
|
||||||
|
* "Libxml2" which uses the XSD regex engine in Libxml2. This is a complete XSD engine but you need to compile and link with libxml2 which may add overhead.
|
||||||
|
|
||||||
|
To use libxml2 in clixon you need enable libxml2 in both cligen and clixon:
|
||||||
|
```
|
||||||
|
> ./configure --with-libxml2 # both cligen and clixon
|
||||||
|
```
|
||||||
|
You then need to set the following configure option:
|
||||||
|
```
|
||||||
|
<CLICON_YANG_REGEXP>libxml2</CLICON_YANG_REGEXP>
|
||||||
|
```
|
||||||
|
|
||||||
## XML
|
## XML
|
||||||
|
|
||||||
Clixon has its own implementation of XML and XPATH implementation.
|
Clixon has its own implementation of XML and XPATH implementation.
|
||||||
|
|
|
||||||
|
|
@ -287,21 +287,30 @@ yang2cli_var_pattern(clicon_handle h,
|
||||||
char *mode;
|
char *mode;
|
||||||
cg_var *cvp;
|
cg_var *cvp;
|
||||||
char *pattern;
|
char *pattern;
|
||||||
|
int invert;
|
||||||
|
char *posix;
|
||||||
|
|
||||||
mode = clicon_yang_regexp(h);
|
mode = clicon_yang_regexp(h);
|
||||||
cvp = NULL; /* Loop over compiled regexps */
|
cvp = NULL; /* Loop over compiled regexps */
|
||||||
while ((cvp = cvec_each(patterns, cvp)) != NULL){
|
while ((cvp = cvec_each(patterns, cvp)) != NULL){
|
||||||
pattern = cv_string_get(cvp);
|
pattern = cv_string_get(cvp);
|
||||||
|
invert = cv_flag(cvp, V_INVERT);
|
||||||
if (strcmp(mode, "posix") == 0){
|
if (strcmp(mode, "posix") == 0){
|
||||||
char *posix = NULL;
|
posix = NULL;
|
||||||
if (regexp_xsd2posix(pattern, &posix) < 0)
|
if (regexp_xsd2posix(pattern, &posix) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
cprintf(cb, " regexp:\"%s\"", posix);
|
cprintf(cb, " regexp:%s\"%s\"",
|
||||||
if (posix)
|
invert?"!":"",
|
||||||
|
posix);
|
||||||
|
if (posix){
|
||||||
free(posix);
|
free(posix);
|
||||||
|
posix = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
cprintf(cb, " regexp:\"%s\"", pattern);
|
cprintf(cb, " regexp:%s\"%s\"",
|
||||||
|
invert?"!":"",
|
||||||
|
pattern);
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
|
|
||||||
21
doc/FAQ.md
21
doc/FAQ.md
|
|
@ -27,7 +27,6 @@
|
||||||
* [How should I start the backend daemon?](#how-should-i-start-the-backend-daemon)
|
* [How should I start the backend daemon?](#how-should-i-start-the-backend-daemon)
|
||||||
* [Can I use systemd with Clixon?](#can-i-use-systemd-with-clixon)
|
* [Can I use systemd with Clixon?](#can-i-use-systemd-with-clixon)
|
||||||
* [How can I add extra XML?](#how-can-i-add-extra-xml)
|
* [How can I add extra XML?](#how-can-i-add-extra-xml)
|
||||||
* [What about Regexps?](#what-about-regexps)
|
|
||||||
* [I want to program. How do I extend the example?](#i-want-to-program-how-do-i-extend-the-example)
|
* [I want to program. How do I extend the example?](#i-want-to-program-how-do-i-extend-the-example)
|
||||||
* [How is a plugin initiated?](#how-is-a-plugin-initiated)
|
* [How is a plugin initiated?](#how-is-a-plugin-initiated)
|
||||||
* [How do I write a commit function?](#how-do-i-write-a-commit-function)
|
* [How do I write a commit function?](#how-do-i-write-a-commit-function)
|
||||||
|
|
@ -439,26 +438,6 @@ You add this via the -c option:
|
||||||
The second way is by programming the plugin_reset() in the backend
|
The second way is by programming the plugin_reset() in the backend
|
||||||
plugin. The example code contains an example on how to do this (see plugin_reset() in example_backend.c).
|
plugin. The example code contains an example on how to do this (see plugin_reset() in example_backend.c).
|
||||||
|
|
||||||
## What about Regexps?
|
|
||||||
|
|
||||||
Yang type patterns use regexps defined in [W3C XML XSD
|
|
||||||
](http://www.w3.org/TR/2004/REC-xmlschema-2-20041028). XSD regexp:s are
|
|
||||||
slightly different from POSIX regexp.
|
|
||||||
|
|
||||||
lixon supports two regular expressions engines:
|
|
||||||
* "Posix" which is the default method, _translates_ XSD regexp:s to posix before matching with the standard Linux regex engine. This translation is not complete but considered
|
|
||||||
"good-enough" for most yang use-cases. For reference, all standard
|
|
||||||
Yang models in [https://github.com/YangModels/yang] have been tested.
|
|
||||||
* "Libxml2" which uses the XSD regex engine in Libxml2. This is a complete XSD engine but you need to compile and link with libxml2 which may add overhead.
|
|
||||||
|
|
||||||
To use libxml2 in clixon you need enable libxml2 in both cligen and clixon:
|
|
||||||
```
|
|
||||||
> ./configure --with-libxml2 # both cligen and clixon
|
|
||||||
```
|
|
||||||
You then need to set the following configure option:
|
|
||||||
```
|
|
||||||
<CLICON_YANG_REGEXP>libxml2</CLICON_YANG_REGEXP>
|
|
||||||
```
|
|
||||||
|
|
||||||
## I want to program. How do I extend the example?
|
## I want to program. How do I extend the example?
|
||||||
See [../example/main](../example/main)
|
See [../example/main](../example/main)
|
||||||
|
|
|
||||||
|
|
@ -1368,8 +1368,6 @@ ys_populate_leaf(clicon_handle h,
|
||||||
if (yparent && yparent->ys_keyword == Y_LIST){
|
if (yparent && yparent->ys_keyword == Y_LIST){
|
||||||
if ((ret = yang_key_match(yparent, ys->ys_argument)) < 0)
|
if ((ret = yang_key_match(yparent, ys->ys_argument)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 1)
|
|
||||||
cv_flag_set(cv, V_UNIQUE);
|
|
||||||
}
|
}
|
||||||
ys->ys_cv = cv;
|
ys->ys_cv = cv;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
|
|
||||||
|
|
@ -41,10 +41,12 @@
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Actually cligen variable stuff XXX
|
* Clixon-specific cligen variable (cv) flags
|
||||||
|
* CLIgen flags defined are in the range 0x01 -0x0f
|
||||||
|
* An application can use any flags above that
|
||||||
|
* @see cv_flag
|
||||||
*/
|
*/
|
||||||
#define V_UNIQUE 0x01 /* Variable flag */
|
#define V_UNSET 0x10 /* Used by XML code to denote a value is not default */
|
||||||
#define V_UNSET 0x08 /* Variable is unset, ie no default */
|
|
||||||
|
|
||||||
|
|
||||||
#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
|
#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
|
||||||
|
|
|
||||||
|
|
@ -33,8 +33,9 @@
|
||||||
|
|
||||||
* Yang type related functions
|
* Yang type related functions
|
||||||
* Part of this is type resolving which is pretty complex
|
* Part of this is type resolving which is pretty complex
|
||||||
*
|
* +--> yang_type_cache_set
|
||||||
* (called at parse / set cache)
|
* +--> compile_pattern2regexp
|
||||||
|
* (called at parse) |
|
||||||
* ys_resolve_type --+ ys_populate_range, yang_enum_int_value(NULL)
|
* ys_resolve_type --+ ys_populate_range, yang_enum_int_value(NULL)
|
||||||
* \ | cml
|
* \ | cml
|
||||||
* v v v
|
* v v v
|
||||||
|
|
@ -43,9 +44,10 @@
|
||||||
* ^ ^ ^ ^
|
* ^ ^ ^ ^
|
||||||
* | | | |
|
* | | | |
|
||||||
* | yang2cli_var | yang2cli_var_union_one
|
* | yang2cli_var | yang2cli_var_union_one
|
||||||
* ys_cv_validate ys_cv_validate_union_one
|
* ys_cv_validate----+ ys_cv_validate_union_one
|
||||||
* |
|
* | \ /
|
||||||
* ys_populate_leaf, xml_cv_cache (NULL)
|
* ys_populate_leaf, +--> cv_validate1 --> cv_validate_pattern (exec regexps)
|
||||||
|
* xml_cv_cache (NULL)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
|
|
@ -278,13 +280,15 @@ yang_type_cache_free(yang_type_cache *ycache)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile yang patterns in string form to regex compiled void* form
|
/*! Compile yang patterns in string form to regex compiled void* form
|
||||||
* and re-store into "patterns" cvec.
|
* and re-store into "patterns" cvec.
|
||||||
* This is done here instead of deep in resolve code (resolve_restrictions)
|
* This is done here instead of deep in resolve code (resolve_restrictions)
|
||||||
* since it id dependent on clicon_handle.
|
* since it id dependent on clicon_handle.
|
||||||
* The downside is that all accesses to "patterns" must pass via the cache.
|
* The downside is that all accesses to "patterns" must pass via the cache.
|
||||||
* If calls to yang_type_resolve is made without the cache is set, will be
|
* If calls to yang_type_resolve is made without the cache is set, will be
|
||||||
* wrong.
|
* wrong.
|
||||||
|
* @see match_regexp in cligen code
|
||||||
|
* @see yang_type_resolve_restrictions where patterns is set
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_pattern2regexp(clicon_handle h,
|
compile_pattern2regexp(clicon_handle h,
|
||||||
|
|
@ -317,6 +321,9 @@ compile_pattern2regexp(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
cv_void_set(rcv, re);
|
cv_void_set(rcv, re);
|
||||||
|
/* invert pattern check */
|
||||||
|
if (cv_flag(pcv, V_INVERT))
|
||||||
|
cv_flag_set(rcv, V_INVERT);
|
||||||
}
|
}
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
|
|
@ -515,6 +522,8 @@ cv_validate_pattern(clicon_handle h,
|
||||||
re = cv_void_get(cvr);
|
re = cv_void_get(cvr);
|
||||||
if ((ret = regex_exec(h, re, str?str:"")) < 0)
|
if ((ret = regex_exec(h, re, str?str:"")) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
if (cv_flag(cvr, V_INVERT))
|
||||||
|
ret = !ret; /* swap 0 and 1 */
|
||||||
if (ret == 0){
|
if (ret == 0){
|
||||||
if (reason)
|
if (reason)
|
||||||
*reason = cligen_reason("regexp match fail: pattern does not match %s",
|
*reason = cligen_reason("regexp match fail: pattern does not match %s",
|
||||||
|
|
@ -1062,7 +1071,7 @@ yang_find_identity(yang_stmt *ys,
|
||||||
* @retval 0 OK.
|
* @retval 0 OK.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
resolve_restrictions(yang_stmt *ytype,
|
yang_type_resolve_restrictions(yang_stmt *ytype,
|
||||||
int *options,
|
int *options,
|
||||||
cvec **cvv,
|
cvec **cvv,
|
||||||
cvec *regexps,
|
cvec *regexps,
|
||||||
|
|
@ -1094,6 +1103,9 @@ resolve_restrictions(yang_stmt *ytype,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
pattern = ys->ys_argument; /* clear text pattern */
|
pattern = ys->ys_argument; /* clear text pattern */
|
||||||
|
/* Check 1.1 invert pattern */
|
||||||
|
if (yang_find(ys, Y_MODIFIER, "invert-match") != NULL)
|
||||||
|
cv_flag_set(cv, V_INVERT);
|
||||||
cv_string_set(cv, pattern);
|
cv_string_set(cv, pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1165,7 +1177,7 @@ yang_type_resolve(yang_stmt *yorig,
|
||||||
/* Check if type is basic type. If so, return that */
|
/* Check if type is basic type. If so, return that */
|
||||||
if ((prefix == NULL && yang_builtin(type))){
|
if ((prefix == NULL && yang_builtin(type))){
|
||||||
*yrestype = ytype;
|
*yrestype = ytype;
|
||||||
if (resolve_restrictions(ytype, options, cvv, patterns, fraction) < 0)
|
if (yang_type_resolve_restrictions(ytype, options, cvv, patterns, fraction) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
|
|
@ -1210,7 +1222,7 @@ yang_type_resolve(yang_stmt *yorig,
|
||||||
fraction) < 0)
|
fraction) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
/* appends patterns, overwrites others if any */
|
/* appends patterns, overwrites others if any */
|
||||||
if (resolve_restrictions(ytype, options, cvv, patterns, fraction) < 0)
|
if (yang_type_resolve_restrictions(ytype, options, cvv, patterns, fraction) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
ok:
|
ok:
|
||||||
|
|
|
||||||
|
|
@ -64,11 +64,9 @@ module pattern{
|
||||||
type string {
|
type string {
|
||||||
length "1..max";
|
length "1..max";
|
||||||
pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
|
pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
|
||||||
/*
|
|
||||||
pattern '[xX][mM][lL].*' {
|
pattern '[xX][mM][lL].*' {
|
||||||
modifier invert-match;
|
modifier invert-match;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
typedef twomatchtype {
|
typedef twomatchtype {
|
||||||
|
|
@ -409,7 +407,7 @@ testrun rfc2 0 'xx00'
|
||||||
new "Test for RFC7950 Sec 9.4.7 pattern example 3 (invert match)"
|
new "Test for RFC7950 Sec 9.4.7 pattern example 3 (invert match)"
|
||||||
testrun rfc3 1 'enabled'
|
testrun rfc3 1 'enabled'
|
||||||
testrun rfc3 0 '10-mbit'
|
testrun rfc3 0 '10-mbit'
|
||||||
#testrun rfc3 0 'xml-element' # invert-match dont work
|
testrun rfc3 0 'xml-element' # invert: dont match xml
|
||||||
|
|
||||||
new "Test for two patterns"
|
new "Test for two patterns"
|
||||||
testrun 'twomatch' 1 'gksdhfsakjhdksa'
|
testrun 'twomatch' 1 'gksdhfsakjhdksa'
|
||||||
|
|
@ -717,6 +715,9 @@ expectfn "$clixon_cli -1f $cfg -l o set c rfc3 enabled" 0 '^$'
|
||||||
new "CLI tests for RFC7950 Sec 9.4.7 ex 3 10-mbit (should fail)"
|
new "CLI tests for RFC7950 Sec 9.4.7 ex 3 10-mbit (should fail)"
|
||||||
expectfn "$clixon_cli -1f $cfg -l o set c rfc3 10-mbit" 255 '^CLI syntax error:'
|
expectfn "$clixon_cli -1f $cfg -l o set c rfc3 10-mbit" 255 '^CLI syntax error:'
|
||||||
|
|
||||||
|
new "CLI tests for RFC7950 Sec 9.4.7 ex 3 xml-element (should fail)"
|
||||||
|
expectfn "$clixon_cli -1f $cfg -l o set c rfc3 xml-element" 255 '^CLI syntax error:'
|
||||||
|
|
||||||
new "CLI tests for two patterns gksdhfsakjhdks"
|
new "CLI tests for two patterns gksdhfsakjhdks"
|
||||||
expectfn "$clixon_cli -1f $cfg -l o set c twomatch gksdhfsakjhdks" 0 '^$'
|
expectfn "$clixon_cli -1f $cfg -l o set c twomatch gksdhfsakjhdks" 0 '^$'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue