Commit 16629d10 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

Implement globbing operator for filters



The operators “=*” and “!*” do globbing in filters, e.g.:

$ gnt-instance list --no-headers -o name 'name =* "*.site"'
inst1.site.example.com
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent 18e4dee6
......@@ -38,6 +38,7 @@ import pyparsing as pyp
from ganeti import errors
from ganeti import netutils
from ganeti import utils
# Logic operators with one or more operands, each of which is a filter on its
......@@ -146,8 +147,10 @@ def BuildFilterParser():
number = pyp.Combine(pyp.Optional(num_sign) + pyp.Word(pyp.nums))
number.setParseAction(lambda toks: int(toks[0]))
quoted_string = pyp.quotedString.copy().setParseAction(pyp.removeQuotes)
# Right-hand-side value
rval = (number | pyp.quotedString.setParseAction(pyp.removeQuotes))
rval = (number | quoted_string)
# Boolean condition
bool_cond = field_name.copy()
......@@ -184,10 +187,22 @@ def BuildFilterParser():
not_regexp_cond.setParseAction(lambda (field, value):
[[OP_NOT, [OP_REGEXP, field, value]]])
# Globbing, e.g. name =* "*.site"
glob_cond = (field_name + pyp.Suppress("=*") + quoted_string)
glob_cond.setParseAction(lambda (field, value):
[[OP_REGEXP, field,
utils.DnsNameGlobPattern(value)]])
not_glob_cond = (field_name + pyp.Suppress("!*") + quoted_string)
not_glob_cond.setParseAction(lambda (field, value):
[[OP_NOT, [OP_REGEXP, field,
utils.DnsNameGlobPattern(value)]]])
# All possible conditions
condition = (binary_cond ^ bool_cond ^
in_cond ^ not_in_cond ^
regexp_cond ^ not_regexp_cond)
regexp_cond ^ not_regexp_cond ^
glob_cond ^ not_glob_cond)
# Associativity operators
filter_expr = pyp.operatorPrecedence(condition, [
......
......@@ -265,6 +265,9 @@ Syntax in pseudo-BNF::
*/
| <field> { =~ | !~ } m/<re>/<re-modifiers>
/* Globbing */
| <field> { =* | !* } <quoted-string>
/* Boolean */
| <field>
}
......@@ -283,6 +286,10 @@ Operators:
Pattern match using regular expression
*!~*
Logically negated from *=~*
*=\**
Globbing, see **glob**(7), though only * and ? are supported
*!\**
Logically negated from *=\**
*in*, *not in*
Collection membership and negation
......
......@@ -147,6 +147,12 @@ class TestParseFilter(unittest.TestCase):
self._Test("notname =~ m%stest%s" % (i, i),
[qlang.OP_REGEXP, "notname", "test"])
self._Test("name =* '*.site'",
[qlang.OP_REGEXP, "name", utils.DnsNameGlobPattern("*.site")])
self._Test("field !* '*.example.*'",
[qlang.OP_NOT, [qlang.OP_REGEXP, "field",
utils.DnsNameGlobPattern("*.example.*")]])
def testAllFields(self):
for name in frozenset(i for d in query.ALL_FIELD_LISTS for i in d.keys()):
self._Test("%s == \"value\"" % name, [qlang.OP_EQUAL, name, "value"])
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment