Skip to content
Snippets Groups Projects
Commit 3f2f55bb authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

qlang: Add function to distinguish filters from names


Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent 3802f3cf
No related branches found
No related tags found
No related merge requests found
......@@ -32,10 +32,12 @@ converted to callable functions by L{query._CompileFilter}.
"""
import re
import string # pylint: disable-msg=W0402
import pyparsing as pyp
from ganeti import errors
from ganeti import netutils
# Logic operators with one or more operands, each of which is a filter on its
......@@ -57,6 +59,10 @@ OP_REGEXP = "=~"
OP_CONTAINS = "=[]"
#: Characters used for detecting user-written filters (see L{MaybeFilter})
FILTER_DETECTION_CHARS = frozenset("()=/!~" + string.whitespace)
def MakeSimpleFilter(namefield, values):
"""Builds simple a filter.
......@@ -220,3 +226,27 @@ def ParseFilter(text, parser=None):
except pyp.ParseBaseException, err:
raise errors.QueryFilterParseError("Failed to parse query filter"
" '%s': %s" % (text, err), err)
def MaybeFilter(text):
"""Try to determine if a string is a filter or a name.
If in doubt, this function treats a text as a name.
@type text: string
@param text: String to be examined
@rtype: bool
"""
# Quick check for punctuation and whitespace
if frozenset(text) & FILTER_DETECTION_CHARS:
return True
try:
netutils.Hostname.GetNormalizedName(text)
except errors.OpPrereqError:
# Not a valid hostname, treat as filter
return True
# Most probably a name
return False
......@@ -22,6 +22,7 @@
"""Script for testing ganeti.qlang"""
import unittest
import string
from ganeti import utils
from ganeti import errors
......@@ -52,7 +53,14 @@ class TestParseFilter(unittest.TestCase):
def setUp(self):
self.parser = qlang.BuildFilterParser()
def _Test(self, filter_, expected):
def _Test(self, filter_, expected, expect_filter=True):
if expect_filter:
self.assertTrue(qlang.MaybeFilter(filter_),
msg="'%s' was not recognized as a filter" % filter_)
else:
self.assertFalse(qlang.MaybeFilter(filter_),
msg=("'%s' should not be recognized as a filter" %
filter_))
self.assertEqual(qlang.ParseFilter(filter_, parser=self.parser), expected)
def test(self):
......@@ -90,10 +98,12 @@ class TestParseFilter(unittest.TestCase):
[qlang.OP_TRUE, "field"]])
self._Test("mem == 128", [qlang.OP_EQUAL, "mem", 128])
self._Test("negfield != -1", [qlang.OP_NOT_EQUAL, "negfield", -1])
self._Test("master", [qlang.OP_TRUE, "master"])
self._Test("master", [qlang.OP_TRUE, "master"],
expect_filter=False)
self._Test("not master", [qlang.OP_NOT, [qlang.OP_TRUE, "master"]])
for op in ["not", "and", "or"]:
self._Test("%sxyz" % op, [qlang.OP_TRUE, "%sxyz" % op])
self._Test("%sxyz" % op, [qlang.OP_TRUE, "%sxyz" % op],
expect_filter=False)
self._Test("not %sxyz" % op,
[qlang.OP_NOT, [qlang.OP_TRUE, "%sxyz" % op]])
self._Test(" not \t%sfoo" % op,
......@@ -166,5 +176,22 @@ class TestParseFilter(unittest.TestCase):
self.fail("Invalid filter '%s' did not raise exception" % filter_)
class TestMaybeFilter(unittest.TestCase):
def test(self):
self.assertTrue(qlang.MaybeFilter(""))
self.assertTrue(qlang.MaybeFilter("foo/bar"))
self.assertTrue(qlang.MaybeFilter("foo==bar"))
for i in set("()!~" + string.whitespace) | qlang.FILTER_DETECTION_CHARS:
self.assertTrue(qlang.MaybeFilter(i),
msg="%r not recognized as filter" % i)
self.assertFalse(qlang.MaybeFilter("node1"))
self.assertFalse(qlang.MaybeFilter("n-o-d-e"))
self.assertFalse(qlang.MaybeFilter("n_o_d_e"))
self.assertFalse(qlang.MaybeFilter("node1.example.com"))
self.assertFalse(qlang.MaybeFilter("node1.example.com."))
if __name__ == "__main__":
testutils.GanetiTestProgram()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment