4 # Copyright (C) 2010 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Module for query operations"""
27 from ganeti import constants
28 from ganeti import errors
29 from ganeti import utils
30 from ganeti import compat
31 from ganeti import objects
35 FIELD_NAME_RE = re.compile(r"^[a-z0-9/._]+$")
36 TITLE_RE = re.compile(r"^[^\s]+$")
38 #: Verification function for each field type
40 constants.QFT_UNKNOWN: ht.TNone,
41 constants.QFT_TEXT: ht.TString,
42 constants.QFT_BOOL: ht.TBool,
43 constants.QFT_NUMBER: ht.TInt,
44 constants.QFT_UNIT: ht.TInt,
45 constants.QFT_TIMESTAMP: ht.TOr(ht.TInt, ht.TFloat),
46 constants.QFT_OTHER: lambda _: True,
50 def _GetUnknownField(ctx, item): # pylint: disable-msg=W0613
51 """Gets the contents of an unknown field.
54 return (constants.QRFS_UNKNOWN, None)
57 def _GetQueryFields(fielddefs, selected):
58 """Calculates the internal list of selected fields.
60 Unknown fields are returned as L{constants.QFT_UNKNOWN}.
63 @param fielddefs: Field definitions
64 @type selected: list of strings
65 @param selected: List of selected fields
72 fdef = fielddefs[name]
74 fdef = (_MakeField(name, name, constants.QFT_UNKNOWN),
75 None, _GetUnknownField)
84 def GetAllFields(fielddefs):
85 """Extract L{objects.QueryFieldDefinition} from field definitions.
87 @rtype: list of L{objects.QueryFieldDefinition}
90 return [fdef for (fdef, _, _) in fielddefs]
94 def __init__(self, fieldlist, selected):
95 """Initializes this class.
97 The field definition is a dictionary with the field's name as a key and a
98 tuple containing, in order, the field definition object
99 (L{objects.QueryFieldDefinition}, the data kind to help calling code
100 collect data and a retrieval function. The retrieval function is called
101 with two parameters, in order, the data container and the item in container
102 (see L{Query.Query}).
104 Users of this class can call L{RequestedData} before preparing the data
105 container to determine what data is needed.
107 @type fieldlist: dictionary
108 @param fieldlist: Field definitions
109 @type selected: list of strings
110 @param selected: List of selected fields
113 self._fields = _GetQueryFields(fieldlist, selected)
115 def RequestedData(self):
116 """Gets requested kinds of data.
121 return frozenset(datakind
122 for (_, datakind, _) in self._fields
123 if datakind is not None)
126 """Returns the list of fields for this query.
128 Includes unknown fields.
130 @rtype: List of L{objects.QueryFieldDefinition}
133 return GetAllFields(self._fields)
135 def Query(self, ctx):
138 @param ctx: Data container passed to field retrieval functions, must
139 support iteration using C{__iter__}
142 result = [[fn(ctx, item) for (_, _, fn) in self._fields]
147 for (idx, row) in enumerate(result):
148 assert _VerifyResultRow(self._fields, row), \
149 ("Inconsistent result for fields %s in row %s: %r" %
150 (self._fields, idx, row))
154 def OldStyleQuery(self, ctx):
155 """Query with "old" query result format.
157 See L{Query.Query} for arguments.
160 unknown = set(fdef.name
161 for (fdef, _, _) in self._fields
162 if fdef.kind == constants.QFT_UNKNOWN)
164 raise errors.OpPrereqError("Unknown output fields selected: %s" %
165 (utils.CommaJoin(unknown), ),
168 return [[value for (_, value) in row]
169 for row in self.Query(ctx)]
172 def _VerifyResultRow(fields, row):
173 """Verifies the contents of a query result row.
176 @param fields: Field definitions for result
177 @type row: list of tuples
181 return (len(row) == len(fields) and
182 compat.all((status == constants.QRFS_NORMAL and
183 _VERIFY_FN[fdef.kind](value)) or
184 # Value for an abnormal status must be None
185 (status != constants.QRFS_NORMAL and value is None)
186 for ((status, value), (fdef, _, _)) in zip(row, fields)))
189 def _PrepareFieldList(fields):
190 """Prepares field list for use by L{Query}.
192 Converts the list to a dictionary and does some verification.
194 @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind,
196 @param fields: List of fields
198 @return: Field dictionary for L{Query}
201 assert len(set(fdef.title.lower()
202 for (fdef, _, _) in fields)) == len(fields), \
203 "Duplicate title found"
208 (fdef, _, fn) = field
210 assert fdef.name and fdef.title, "Name and title are required"
211 assert FIELD_NAME_RE.match(fdef.name)
212 assert TITLE_RE.match(fdef.title)
214 assert fdef.name not in result, "Duplicate field name found"
216 result[fdef.name] = field
218 assert len(result) == len(fields)
219 assert compat.all(name == fdef.name
220 for (name, (fdef, _, _)) in result.items())
225 def _MakeField(name, title, kind):
226 """Wrapper for creating L{objects.QueryFieldDefinition} instances.
228 @param name: Field name as a regular expression
229 @param title: Human-readable title
230 @param kind: Field type
233 return objects.QueryFieldDefinition(name=name, title=title, kind=kind)