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