Revision ad48eacc
b/lib/qlang.py | ||
---|---|---|
58 | 58 |
# operator-specific value |
59 | 59 |
OP_EQUAL = "=" |
60 | 60 |
OP_NOT_EQUAL = "!=" |
61 |
OP_LT = "<" |
|
62 |
OP_LE = "<=" |
|
63 |
OP_GT = ">" |
|
64 |
OP_GE = ">=" |
|
61 | 65 |
OP_REGEXP = "=~" |
62 | 66 |
OP_CONTAINS = "=[]" |
63 | 67 |
|
64 | 68 |
|
65 | 69 |
#: Characters used for detecting user-written filters (see L{_CheckFilter}) |
66 |
FILTER_DETECTION_CHARS = frozenset("()=/!~'\"\\" + string.whitespace) |
|
70 |
FILTER_DETECTION_CHARS = frozenset("()=/!~'\"\\<>" + string.whitespace)
|
|
67 | 71 |
|
68 | 72 |
#: Characters used to detect globbing filters (see L{_CheckGlobbing}) |
69 | 73 |
GLOB_DETECTION_CHARS = frozenset("*?") |
... | ... | |
165 | 169 |
binopstbl = { |
166 | 170 |
"==": OP_EQUAL, |
167 | 171 |
"!=": OP_NOT_EQUAL, |
172 |
"<": OP_LT, |
|
173 |
"<=": OP_LE, |
|
174 |
">": OP_GT, |
|
175 |
">=": OP_GE, |
|
168 | 176 |
} |
169 | 177 |
|
170 | 178 |
binary_cond = (field_name + pyp.oneOf(binopstbl.keys()) + rval) |
b/lib/query.py | ||
---|---|---|
403 | 403 |
qlang.OP_NOT_EQUAL: |
404 | 404 |
(_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn) |
405 | 405 |
for (flags, fn, valprepfn) in _EQUALITY_CHECKS]), |
406 |
qlang.OP_LT: (_OPTYPE_BINARY, [ |
|
407 |
(None, operator.lt, None), |
|
408 |
]), |
|
409 |
qlang.OP_GT: (_OPTYPE_BINARY, [ |
|
410 |
(None, operator.gt, None), |
|
411 |
]), |
|
412 |
qlang.OP_LE: (_OPTYPE_BINARY, [ |
|
413 |
(None, operator.le, None), |
|
414 |
]), |
|
415 |
qlang.OP_GE: (_OPTYPE_BINARY, [ |
|
416 |
(None, operator.ge, None), |
|
417 |
]), |
|
406 | 418 |
qlang.OP_REGEXP: (_OPTYPE_BINARY, [ |
407 | 419 |
(None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex), |
408 | 420 |
]), |
b/man/ganeti.rst | ||
---|---|---|
362 | 362 |
|
363 | 363 |
<condition> ::= |
364 | 364 |
{ /* Value comparison */ |
365 |
<field> { == | != } <value> |
|
365 |
<field> { == | != | < | <= | >= | > } <value>
|
|
366 | 366 |
|
367 | 367 |
/* Collection membership */ |
368 | 368 |
| <value> [ not ] in <field> |
... | ... | |
389 | 389 |
Equality |
390 | 390 |
*!=* |
391 | 391 |
Inequality |
392 |
*<* |
|
393 |
Less than |
|
394 |
*<=* |
|
395 |
Less than or equal |
|
396 |
*>* |
|
397 |
Greater than |
|
398 |
*>=* |
|
399 |
Greater than or equal |
|
392 | 400 |
*=~* |
393 | 401 |
Pattern match using regular expression |
394 | 402 |
*!~* |
b/test/ganeti.qlang_unittest.py | ||
---|---|---|
147 | 147 |
[qlang.OP_NOT, [qlang.OP_REGEXP, "field", |
148 | 148 |
utils.DnsNameGlobPattern("*.example.*")]]) |
149 | 149 |
|
150 |
self._Test("ctime < 1234", [qlang.OP_LT, "ctime", 1234]) |
|
151 |
self._Test("ctime > 1234", [qlang.OP_GT, "ctime", 1234]) |
|
152 |
self._Test("mtime <= 9999", [qlang.OP_LE, "mtime", 9999]) |
|
153 |
self._Test("mtime >= 9999", [qlang.OP_GE, "mtime", 9999]) |
|
154 |
|
|
150 | 155 |
def testAllFields(self): |
151 | 156 |
for name in frozenset(i for d in query.ALL_FIELD_LISTS for i in d.keys()): |
152 | 157 |
self._Test("%s == \"value\"" % name, [qlang.OP_EQUAL, name, "value"]) |
... | ... | |
167 | 172 |
# Non-matching regexp delimiters |
168 | 173 |
tests.append("name =~ /foobarbaz#") |
169 | 174 |
|
175 |
# Invalid operators |
|
176 |
tests.append("name <> value") |
|
177 |
tests.append("name => value") |
|
178 |
tests.append("name =< value") |
|
179 |
|
|
170 | 180 |
for qfilter in tests: |
171 | 181 |
try: |
172 | 182 |
qlang.ParseFilter(qfilter, parser=self.parser) |
b/test/ganeti.query_unittest.py | ||
---|---|---|
1745 | 1745 |
self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"], |
1746 | 1746 |
qfilter=["=~", "name", r"["]) |
1747 | 1747 |
|
1748 |
def testFilterLessGreater(self): |
|
1749 |
fielddefs = query._PrepareFieldList([ |
|
1750 |
(query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"), |
|
1751 |
None, 0, lambda ctx, item: item), |
|
1752 |
], []) |
|
1753 |
|
|
1754 |
data = range(100) |
|
1755 |
|
|
1756 |
q = query.Query(fielddefs, ["value"], |
|
1757 |
qfilter=["<", "value", 20]) |
|
1758 |
self.assertTrue(q.RequestedNames() is None) |
|
1759 |
self.assertEqual(q.Query(data), |
|
1760 |
[[(constants.RS_NORMAL, i)] for i in range(20)]) |
|
1761 |
|
|
1762 |
q = query.Query(fielddefs, ["value"], |
|
1763 |
qfilter=["<=", "value", 30]) |
|
1764 |
self.assertTrue(q.RequestedNames() is None) |
|
1765 |
self.assertEqual(q.Query(data), |
|
1766 |
[[(constants.RS_NORMAL, i)] for i in range(31)]) |
|
1767 |
|
|
1768 |
q = query.Query(fielddefs, ["value"], |
|
1769 |
qfilter=[">", "value", 40]) |
|
1770 |
self.assertTrue(q.RequestedNames() is None) |
|
1771 |
self.assertEqual(q.Query(data), |
|
1772 |
[[(constants.RS_NORMAL, i)] for i in range(41, 100)]) |
|
1773 |
|
|
1774 |
q = query.Query(fielddefs, ["value"], |
|
1775 |
qfilter=[">=", "value", 50]) |
|
1776 |
self.assertTrue(q.RequestedNames() is None) |
|
1777 |
self.assertEqual(q.Query(data), |
|
1778 |
[[(constants.RS_NORMAL, i)] for i in range(50, 100)]) |
|
1779 |
|
|
1748 | 1780 |
|
1749 | 1781 |
if __name__ == "__main__": |
1750 | 1782 |
testutils.GanetiTestProgram() |
Also available in: Unified diff