Revision 526f866b
b/lib/jstore.py | ||
---|---|---|
203 | 203 |
@return: Directory name |
204 | 204 |
|
205 | 205 |
""" |
206 |
return str(int(job_id) / JOBS_PER_ARCHIVE_DIRECTORY) |
|
206 |
return str(ParseJobId(job_id) / JOBS_PER_ARCHIVE_DIRECTORY) |
|
207 |
|
|
208 |
|
|
209 |
def ParseJobId(job_id): |
|
210 |
"""Parses a job ID and converts it to integer. |
|
211 |
|
|
212 |
""" |
|
213 |
try: |
|
214 |
return int(job_id) |
|
215 |
except (ValueError, TypeError): |
|
216 |
raise errors.ParameterError("Invalid job ID '%s'" % job_id) |
b/lib/query.py | ||
---|---|---|
64 | 64 |
from ganeti import ht |
65 | 65 |
from ganeti import runtime |
66 | 66 |
from ganeti import qlang |
67 |
from ganeti import jstore |
|
67 | 68 |
|
68 | 69 |
from ganeti.constants import (QFT_UNKNOWN, QFT_TEXT, QFT_BOOL, QFT_NUMBER, |
69 | 70 |
QFT_UNIT, QFT_TIMESTAMP, QFT_OTHER, |
... | ... | |
103 | 104 |
# Query field flags |
104 | 105 |
QFF_HOSTNAME = 0x01 |
105 | 106 |
QFF_IP_ADDRESS = 0x02 |
106 |
# Next values: 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200 |
|
107 |
QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS) |
|
107 |
QFF_JOB_ID = 0x04 |
|
108 |
QFF_SPLIT_TIMESTAMP = 0x08 |
|
109 |
# Next values: 0x10, 0x20, 0x40, 0x80, 0x100, 0x200 |
|
110 |
QFF_ALL = (QFF_HOSTNAME | QFF_IP_ADDRESS | QFF_JOB_ID | QFF_SPLIT_TIMESTAMP) |
|
108 | 111 |
|
109 | 112 |
FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$") |
110 | 113 |
TITLE_RE = re.compile(r"^[^\s]+$") |
... | ... | |
345 | 348 |
raise errors.ParameterError("Invalid regex pattern (%s)" % err) |
346 | 349 |
|
347 | 350 |
|
351 |
def _PrepareSplitTimestamp(value): |
|
352 |
"""Prepares a value for comparison by L{_MakeSplitTimestampComparison}. |
|
353 |
|
|
354 |
""" |
|
355 |
if ht.TNumber(value): |
|
356 |
return value |
|
357 |
else: |
|
358 |
return utils.MergeTime(value) |
|
359 |
|
|
360 |
|
|
361 |
def _MakeSplitTimestampComparison(fn): |
|
362 |
"""Compares split timestamp values after converting to float. |
|
363 |
|
|
364 |
""" |
|
365 |
return lambda lhs, rhs: fn(utils.MergeTime(lhs), rhs) |
|
366 |
|
|
367 |
|
|
368 |
def _MakeComparisonChecks(fn): |
|
369 |
"""Prepares flag-specific comparisons using a comparison function. |
|
370 |
|
|
371 |
""" |
|
372 |
return [ |
|
373 |
(QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(fn), |
|
374 |
_PrepareSplitTimestamp), |
|
375 |
(QFF_JOB_ID, lambda lhs, rhs: fn(jstore.ParseJobId(lhs), rhs), |
|
376 |
jstore.ParseJobId), |
|
377 |
(None, fn, None), |
|
378 |
] |
|
379 |
|
|
380 |
|
|
348 | 381 |
class _FilterCompilerHelper: |
349 | 382 |
"""Converts a query filter to a callable usable for filtering. |
350 | 383 |
|
... | ... | |
363 | 396 |
|
364 | 397 |
List of tuples containing flags and a callable receiving the left- and |
365 | 398 |
right-hand side of the operator. The flags are an OR-ed value of C{QFF_*} |
366 |
(e.g. L{QFF_HOSTNAME}). |
|
399 |
(e.g. L{QFF_HOSTNAME} or L{QFF_SPLIT_TIMESTAMP}).
|
|
367 | 400 |
|
368 | 401 |
Order matters. The first item with flags will be used. Flags are checked |
369 | 402 |
using binary AND. |
... | ... | |
374 | 407 |
lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs], |
375 | 408 |
case_sensitive=False), |
376 | 409 |
None), |
410 |
(QFF_SPLIT_TIMESTAMP, _MakeSplitTimestampComparison(operator.eq), |
|
411 |
_PrepareSplitTimestamp), |
|
377 | 412 |
(None, operator.eq, None), |
378 | 413 |
] |
379 | 414 |
|
... | ... | |
403 | 438 |
qlang.OP_NOT_EQUAL: |
404 | 439 |
(_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn) |
405 | 440 |
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 |
]), |
|
441 |
qlang.OP_LT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.lt)), |
|
442 |
qlang.OP_LE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.le)), |
|
443 |
qlang.OP_GT: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.gt)), |
|
444 |
qlang.OP_GE: (_OPTYPE_BINARY, _MakeComparisonChecks(operator.ge)), |
|
418 | 445 |
qlang.OP_REGEXP: (_OPTYPE_BINARY, [ |
419 | 446 |
(None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex), |
420 | 447 |
]), |
... | ... | |
2239 | 2266 |
""" |
2240 | 2267 |
fields = [ |
2241 | 2268 |
(_MakeField("id", "ID", QFT_TEXT, "Job ID"), |
2242 |
None, 0, lambda _, (job_id, job): job_id),
|
|
2269 |
None, QFF_JOB_ID, lambda _, (job_id, job): job_id),
|
|
2243 | 2270 |
(_MakeField("status", "Status", QFT_TEXT, "Job status"), |
2244 | 2271 |
None, 0, _JobUnavail(lambda job: job.CalcStatus())), |
2245 | 2272 |
(_MakeField("priority", "Priority", QFT_NUMBER, |
... | ... | |
2270 | 2297 |
(_MakeField("oppriority", "OpCode_prio", QFT_OTHER, |
2271 | 2298 |
"List of opcode priorities"), |
2272 | 2299 |
None, 0, _PerJobOp(operator.attrgetter("priority"))), |
2273 |
(_MakeField("received_ts", "Received", QFT_OTHER, |
|
2274 |
"Timestamp of when job was received"), |
|
2275 |
None, 0, _JobTimestamp(operator.attrgetter("received_timestamp"))), |
|
2276 |
(_MakeField("start_ts", "Start", QFT_OTHER, |
|
2277 |
"Timestamp of job start"), |
|
2278 |
None, 0, _JobTimestamp(operator.attrgetter("start_timestamp"))), |
|
2279 |
(_MakeField("end_ts", "End", QFT_OTHER, |
|
2280 |
"Timestamp of job end"), |
|
2281 |
None, 0, _JobTimestamp(operator.attrgetter("end_timestamp"))), |
|
2282 | 2300 |
(_MakeField("summary", "Summary", QFT_OTHER, |
2283 | 2301 |
"List of per-opcode summaries"), |
2284 | 2302 |
None, 0, _PerJobOp(lambda op: op.input.Summary())), |
2285 | 2303 |
] |
2286 | 2304 |
|
2305 |
# Timestamp fields |
|
2306 |
for (name, attr, title, desc) in [ |
|
2307 |
("received_ts", "received_timestamp", "Received", |
|
2308 |
"Timestamp of when job was received"), |
|
2309 |
("start_ts", "start_timestamp", "Start", "Timestamp of job start"), |
|
2310 |
("end_ts", "end_timestamp", "End", "Timestamp of job end"), |
|
2311 |
]: |
|
2312 |
getter = operator.attrgetter(attr) |
|
2313 |
fields.extend([ |
|
2314 |
(_MakeField(name, title, QFT_OTHER, |
|
2315 |
"%s (tuple containing seconds and microseconds)" % desc), |
|
2316 |
None, QFF_SPLIT_TIMESTAMP, _JobTimestamp(getter)), |
|
2317 |
]) |
|
2318 |
|
|
2287 | 2319 |
return _PrepareFieldList(fields, []) |
2288 | 2320 |
|
2289 | 2321 |
|
b/test/ganeti.query_unittest.py | ||
---|---|---|
1777 | 1777 |
self.assertEqual(q.Query(data), |
1778 | 1778 |
[[(constants.RS_NORMAL, i)] for i in range(50, 100)]) |
1779 | 1779 |
|
1780 |
def testFilterLessGreaterJobId(self): |
|
1781 |
fielddefs = query._PrepareFieldList([ |
|
1782 |
(query._MakeField("id", "ID", constants.QFT_TEXT, "Job ID"), |
|
1783 |
None, query.QFF_JOB_ID, lambda ctx, item: item), |
|
1784 |
], []) |
|
1785 |
|
|
1786 |
data = ["1", "2", "3", "10", "102", "120", "125", "15", "100", "7"] |
|
1787 |
|
|
1788 |
assert data != utils.NiceSort(data), "Test data should not be sorted" |
|
1789 |
|
|
1790 |
q = query.Query(fielddefs, ["id"], qfilter=["<", "id", "20"]) |
|
1791 |
self.assertTrue(q.RequestedNames() is None) |
|
1792 |
self.assertEqual(q.Query(data), [ |
|
1793 |
[(constants.RS_NORMAL, "1")], |
|
1794 |
[(constants.RS_NORMAL, "2")], |
|
1795 |
[(constants.RS_NORMAL, "3")], |
|
1796 |
[(constants.RS_NORMAL, "10")], |
|
1797 |
[(constants.RS_NORMAL, "15")], |
|
1798 |
[(constants.RS_NORMAL, "7")], |
|
1799 |
]) |
|
1800 |
|
|
1801 |
q = query.Query(fielddefs, ["id"], qfilter=[">=", "id", "100"]) |
|
1802 |
self.assertTrue(q.RequestedNames() is None) |
|
1803 |
self.assertEqual(q.Query(data), [ |
|
1804 |
[(constants.RS_NORMAL, "102")], |
|
1805 |
[(constants.RS_NORMAL, "120")], |
|
1806 |
[(constants.RS_NORMAL, "125")], |
|
1807 |
[(constants.RS_NORMAL, "100")], |
|
1808 |
]) |
|
1809 |
|
|
1810 |
# Integers are no valid job IDs |
|
1811 |
self.assertRaises(errors.ParameterError, query.Query, |
|
1812 |
fielddefs, ["id"], qfilter=[">=", "id", 10]) |
|
1813 |
|
|
1814 |
def testFilterLessGreaterSplitTimestamp(self): |
|
1815 |
fielddefs = query._PrepareFieldList([ |
|
1816 |
(query._MakeField("ts", "Timestamp", constants.QFT_OTHER, "Timestamp"), |
|
1817 |
None, query.QFF_SPLIT_TIMESTAMP, lambda ctx, item: item), |
|
1818 |
], []) |
|
1819 |
|
|
1820 |
data = [ |
|
1821 |
utils.SplitTime(0), |
|
1822 |
utils.SplitTime(0.1), |
|
1823 |
utils.SplitTime(18224.7872), |
|
1824 |
utils.SplitTime(919896.12623), |
|
1825 |
utils.SplitTime(999), |
|
1826 |
utils.SplitTime(989.9999), |
|
1827 |
] |
|
1828 |
|
|
1829 |
for i in [0, [0, 0]]: |
|
1830 |
q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", i]) |
|
1831 |
self.assertTrue(q.RequestedNames() is None) |
|
1832 |
self.assertEqual(q.Query(data), []) |
|
1833 |
|
|
1834 |
q = query.Query(fielddefs, ["ts"], qfilter=["<", "ts", 1000]) |
|
1835 |
self.assertTrue(q.RequestedNames() is None) |
|
1836 |
self.assertEqual(q.Query(data), [ |
|
1837 |
[(constants.RS_NORMAL, (0, 0))], |
|
1838 |
[(constants.RS_NORMAL, (0, 100000))], |
|
1839 |
[(constants.RS_NORMAL, (999, 0))], |
|
1840 |
[(constants.RS_NORMAL, (989, 999900))], |
|
1841 |
]) |
|
1842 |
|
|
1843 |
q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", 5000.3]) |
|
1844 |
self.assertTrue(q.RequestedNames() is None) |
|
1845 |
self.assertEqual(q.Query(data), [ |
|
1846 |
[(constants.RS_NORMAL, (18224, 787200))], |
|
1847 |
[(constants.RS_NORMAL, (919896, 126230))], |
|
1848 |
]) |
|
1849 |
|
|
1850 |
for i in [18224.7772, utils.SplitTime(18224.7772)]: |
|
1851 |
q = query.Query(fielddefs, ["ts"], qfilter=[">=", "ts", i]) |
|
1852 |
self.assertTrue(q.RequestedNames() is None) |
|
1853 |
self.assertEqual(q.Query(data), [ |
|
1854 |
[(constants.RS_NORMAL, (18224, 787200))], |
|
1855 |
[(constants.RS_NORMAL, (919896, 126230))], |
|
1856 |
]) |
|
1857 |
|
|
1858 |
q = query.Query(fielddefs, ["ts"], qfilter=[">", "ts", 18224.7880]) |
|
1859 |
self.assertTrue(q.RequestedNames() is None) |
|
1860 |
self.assertEqual(q.Query(data), [ |
|
1861 |
[(constants.RS_NORMAL, (919896, 126230))], |
|
1862 |
]) |
|
1863 |
|
|
1780 | 1864 |
|
1781 | 1865 |
if __name__ == "__main__": |
1782 | 1866 |
testutils.GanetiTestProgram() |
Also available in: Unified diff