diff --git a/lib/qlang.py b/lib/qlang.py
index 839a615baaf9fd0d1605a571410039161257750a..2923194f93d43dac948639390d13e569c5657821 100644
--- a/lib/qlang.py
+++ b/lib/qlang.py
@@ -58,12 +58,16 @@ OP_TRUE = "?"
 # operator-specific value
 OP_EQUAL = "="
 OP_NOT_EQUAL = "!="
+OP_LT = "<"
+OP_LE = "<="
+OP_GT = ">"
+OP_GE = ">="
 OP_REGEXP = "=~"
 OP_CONTAINS = "=[]"
 
 
 #: Characters used for detecting user-written filters (see L{_CheckFilter})
-FILTER_DETECTION_CHARS = frozenset("()=/!~'\"\\" + string.whitespace)
+FILTER_DETECTION_CHARS = frozenset("()=/!~'\"\\<>" + string.whitespace)
 
 #: Characters used to detect globbing filters (see L{_CheckGlobbing})
 GLOB_DETECTION_CHARS = frozenset("*?")
@@ -165,6 +169,10 @@ def BuildFilterParser():
   binopstbl = {
     "==": OP_EQUAL,
     "!=": OP_NOT_EQUAL,
+    "<": OP_LT,
+    "<=": OP_LE,
+    ">": OP_GT,
+    ">=": OP_GE,
     }
 
   binary_cond = (field_name + pyp.oneOf(binopstbl.keys()) + rval)
diff --git a/lib/query.py b/lib/query.py
index ffd4d204ce0b5b230d7828bce3bd8c3a28054e23..a8f19f0a5629cb429c0b5b4705c8fad8037af50c 100644
--- a/lib/query.py
+++ b/lib/query.py
@@ -403,6 +403,18 @@ class _FilterCompilerHelper:
     qlang.OP_NOT_EQUAL:
       (_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
                         for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
+    qlang.OP_LT: (_OPTYPE_BINARY, [
+      (None, operator.lt, None),
+      ]),
+    qlang.OP_GT: (_OPTYPE_BINARY, [
+      (None, operator.gt, None),
+      ]),
+    qlang.OP_LE: (_OPTYPE_BINARY, [
+      (None, operator.le, None),
+      ]),
+    qlang.OP_GE: (_OPTYPE_BINARY, [
+      (None, operator.ge, None),
+      ]),
     qlang.OP_REGEXP: (_OPTYPE_BINARY, [
       (None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
       ]),
diff --git a/man/ganeti.rst b/man/ganeti.rst
index 2d5c82f729f8fd49e6a76c493b3d9fe43f370d7e..d31adfca1bad7494b4354567e4225136e13a6a6c 100644
--- a/man/ganeti.rst
+++ b/man/ganeti.rst
@@ -362,7 +362,7 @@ Syntax in pseudo-BNF::
 
   <condition> ::=
     { /* Value comparison */
-      <field> { == | != } <value>
+      <field> { == | != | < | <= | >= | > } <value>
 
       /* Collection membership */
       | <value> [ not ] in <field>
@@ -389,6 +389,14 @@ Operators:
   Equality
 *!=*
   Inequality
+*<*
+  Less than
+*<=*
+  Less than or equal
+*>*
+  Greater than
+*>=*
+  Greater than or equal
 *=~*
   Pattern match using regular expression
 *!~*
diff --git a/test/ganeti.qlang_unittest.py b/test/ganeti.qlang_unittest.py
index 02b953406655efa29cf6b7120507c0e7e8752e06..df249ec68915a8118d674ba14f17caad92df5ac4 100755
--- a/test/ganeti.qlang_unittest.py
+++ b/test/ganeti.qlang_unittest.py
@@ -147,6 +147,11 @@ class TestParseFilter(unittest.TestCase):
                [qlang.OP_NOT, [qlang.OP_REGEXP, "field",
                                utils.DnsNameGlobPattern("*.example.*")]])
 
+    self._Test("ctime < 1234", [qlang.OP_LT, "ctime", 1234])
+    self._Test("ctime > 1234", [qlang.OP_GT, "ctime", 1234])
+    self._Test("mtime <= 9999", [qlang.OP_LE, "mtime", 9999])
+    self._Test("mtime >= 9999", [qlang.OP_GE, "mtime", 9999])
+
   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"])
@@ -167,6 +172,11 @@ class TestParseFilter(unittest.TestCase):
     # Non-matching regexp delimiters
     tests.append("name =~ /foobarbaz#")
 
+    # Invalid operators
+    tests.append("name <> value")
+    tests.append("name => value")
+    tests.append("name =< value")
+
     for qfilter in tests:
       try:
         qlang.ParseFilter(qfilter, parser=self.parser)
diff --git a/test/ganeti.query_unittest.py b/test/ganeti.query_unittest.py
index 87034436a03c4a9827da54b445aeaff9b182c219..ca83d1cc9c99ddecb45553730af2af5e98b8a64f 100755
--- a/test/ganeti.query_unittest.py
+++ b/test/ganeti.query_unittest.py
@@ -1745,6 +1745,38 @@ class TestQueryFilter(unittest.TestCase):
     self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
                       qfilter=["=~", "name", r"["])
 
+  def testFilterLessGreater(self):
+    fielddefs = query._PrepareFieldList([
+      (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
+       None, 0, lambda ctx, item: item),
+      ], [])
+
+    data = range(100)
+
+    q = query.Query(fielddefs, ["value"],
+                    qfilter=["<", "value", 20])
+    self.assertTrue(q.RequestedNames() is None)
+    self.assertEqual(q.Query(data),
+                     [[(constants.RS_NORMAL, i)] for i in range(20)])
+
+    q = query.Query(fielddefs, ["value"],
+                    qfilter=["<=", "value", 30])
+    self.assertTrue(q.RequestedNames() is None)
+    self.assertEqual(q.Query(data),
+                     [[(constants.RS_NORMAL, i)] for i in range(31)])
+
+    q = query.Query(fielddefs, ["value"],
+                    qfilter=[">", "value", 40])
+    self.assertTrue(q.RequestedNames() is None)
+    self.assertEqual(q.Query(data),
+                     [[(constants.RS_NORMAL, i)] for i in range(41, 100)])
+
+    q = query.Query(fielddefs, ["value"],
+                    qfilter=[">=", "value", 50])
+    self.assertTrue(q.RequestedNames() is None)
+    self.assertEqual(q.Query(data),
+                     [[(constants.RS_NORMAL, i)] for i in range(50, 100)])
+
 
 if __name__ == "__main__":
   testutils.GanetiTestProgram()