X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/b459a848dc62e314e61e8ae14edd3ff6cc2b2822..385d4574da52964a4588552dda2f3d28414d3f0f:/lib/qlang.py diff --git a/lib/qlang.py b/lib/qlang.py index bd2a3fd..2352391 100644 --- a/lib/qlang.py +++ b/lib/qlang.py @@ -1,7 +1,7 @@ # # -# Copyright (C) 2010, 2011 Google Inc. +# Copyright (C) 2010, 2011, 2012 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -38,7 +38,6 @@ import logging import pyparsing as pyp from ganeti import errors -from ganeti import netutils from ganeti import utils from ganeti import compat @@ -58,12 +57,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 +168,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) @@ -250,20 +257,6 @@ def ParseFilter(text, parser=None): " '%s': %s" % (text, err), err) -def _IsHostname(text): - """Checks if a string could be a hostname. - - @rtype: bool - - """ - try: - netutils.Hostname.GetNormalizedName(text) - except errors.OpPrereqError: - return False - else: - return True - - def _CheckFilter(text): """CHecks if a string could be a filter. @@ -282,17 +275,24 @@ def _CheckGlobbing(text): return bool(frozenset(text) & GLOB_DETECTION_CHARS) -def _MakeFilterPart(namefield, text): +def _MakeFilterPart(namefield, text, isnumeric=False): """Generates filter for one argument. """ - if _CheckGlobbing(text): + if isnumeric: + try: + number = int(text) + except (TypeError, ValueError), err: + raise errors.OpPrereqError("Invalid job ID passed: %s" % str(err), + errors.ECODE_INVAL) + return [OP_EQUAL, namefield, number] + elif _CheckGlobbing(text): return [OP_REGEXP, namefield, utils.DnsNameGlobPattern(text)] else: return [OP_EQUAL, namefield, text] -def MakeFilter(args, force_filter): +def MakeFilter(args, force_filter, namefield=None, isnumeric=False): """Try to make a filter from arguments to a command. If the name could be a filter it is parsed as such. If it's just a globbing @@ -303,21 +303,31 @@ def MakeFilter(args, force_filter): @param args: Arguments to command @type force_filter: bool @param force_filter: Whether to force treatment as a full-fledged filter + @type namefield: string + @param namefield: Name of field to use for simple filters (use L{None} for + a default of "name") + @type isnumeric: bool + @param isnumeric: Whether the namefield type is numeric, as opposed to + the default string type; this influences how the filter is built @rtype: list @return: Query filter """ + if namefield is None: + namefield = "name" + if (force_filter or (args and len(args) == 1 and _CheckFilter(args[0]))): try: (filter_text, ) = args except (TypeError, ValueError): raise errors.OpPrereqError("Exactly one argument must be given as a" - " filter") + " filter", errors.ECODE_INVAL) result = ParseFilter(filter_text) elif args: - result = [OP_OR] + map(compat.partial(_MakeFilterPart, "name"), args) + result = [OP_OR] + map(compat.partial(_MakeFilterPart, namefield, + isnumeric=isnumeric), args) else: result = None