Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / baserlib.py @ 4c864b55

History | View | Annotate | Download (14.4 kB)

1 c2dca9af Oleksiy Mishchenko
#
2 c2dca9af Oleksiy Mishchenko
#
3 c2dca9af Oleksiy Mishchenko
4 c2dca9af Oleksiy Mishchenko
# Copyright (C) 2006, 2007, 2008 Google Inc.
5 c2dca9af Oleksiy Mishchenko
#
6 c2dca9af Oleksiy Mishchenko
# This program is free software; you can redistribute it and/or modify
7 c2dca9af Oleksiy Mishchenko
# it under the terms of the GNU General Public License as published by
8 c2dca9af Oleksiy Mishchenko
# the Free Software Foundation; either version 2 of the License, or
9 c2dca9af Oleksiy Mishchenko
# (at your option) any later version.
10 c2dca9af Oleksiy Mishchenko
#
11 c2dca9af Oleksiy Mishchenko
# This program is distributed in the hope that it will be useful, but
12 c2dca9af Oleksiy Mishchenko
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 c2dca9af Oleksiy Mishchenko
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 c2dca9af Oleksiy Mishchenko
# General Public License for more details.
15 c2dca9af Oleksiy Mishchenko
#
16 c2dca9af Oleksiy Mishchenko
# You should have received a copy of the GNU General Public License
17 c2dca9af Oleksiy Mishchenko
# along with this program; if not, write to the Free Software
18 c2dca9af Oleksiy Mishchenko
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 c2dca9af Oleksiy Mishchenko
# 02110-1301, USA.
20 c2dca9af Oleksiy Mishchenko
21 c2dca9af Oleksiy Mishchenko
22 c2dca9af Oleksiy Mishchenko
"""Remote API base resources library.
23 c2dca9af Oleksiy Mishchenko

24 c2dca9af Oleksiy Mishchenko
"""
25 c2dca9af Oleksiy Mishchenko
26 b459a848 Andrea Spadaccini
# pylint: disable=C0103
27 fe267188 Iustin Pop
28 fe267188 Iustin Pop
# C0103: Invalid name, since the R_* names are not conforming
29 fe267188 Iustin Pop
30 59b4eeef Iustin Pop
import logging
31 59b4eeef Iustin Pop
32 441e7cfd Oleksiy Mishchenko
from ganeti import luxi
33 b5b67ef9 Michael Hanselmann
from ganeti import rapi
34 3d103742 Iustin Pop
from ganeti import http
35 59b4eeef Iustin Pop
from ganeti import errors
36 c08fd0d6 Michael Hanselmann
from ganeti import compat
37 441e7cfd Oleksiy Mishchenko
38 c2dca9af Oleksiy Mishchenko
39 af6433c6 Michael Hanselmann
# Dummy value to detect unchanged parameters
40 af6433c6 Michael Hanselmann
_DEFAULT = object()
41 af6433c6 Michael Hanselmann
42 c08fd0d6 Michael Hanselmann
#: Supported HTTP methods
43 c08fd0d6 Michael Hanselmann
_SUPPORTED_METHODS = frozenset([
44 c08fd0d6 Michael Hanselmann
  http.HTTP_DELETE,
45 c08fd0d6 Michael Hanselmann
  http.HTTP_GET,
46 c08fd0d6 Michael Hanselmann
  http.HTTP_POST,
47 c08fd0d6 Michael Hanselmann
  http.HTTP_PUT,
48 c08fd0d6 Michael Hanselmann
  ])
49 c08fd0d6 Michael Hanselmann
50 af6433c6 Michael Hanselmann
51 b8ab1c7f Michael Hanselmann
def _BuildOpcodeAttributes():
52 b8ab1c7f Michael Hanselmann
  """Builds list of attributes used for per-handler opcodes.
53 b8ab1c7f Michael Hanselmann

54 b8ab1c7f Michael Hanselmann
  """
55 b8ab1c7f Michael Hanselmann
  return [(method, "%s_OPCODE" % method, "%s_RENAME" % method,
56 b8ab1c7f Michael Hanselmann
           "Get%sOpInput" % method.capitalize())
57 b8ab1c7f Michael Hanselmann
          for method in _SUPPORTED_METHODS]
58 b8ab1c7f Michael Hanselmann
59 b8ab1c7f Michael Hanselmann
60 b8ab1c7f Michael Hanselmann
_OPCODE_ATTRS = _BuildOpcodeAttributes()
61 b8ab1c7f Michael Hanselmann
62 b8ab1c7f Michael Hanselmann
63 c2dca9af Oleksiy Mishchenko
def BuildUriList(ids, uri_format, uri_fields=("name", "uri")):
64 c2dca9af Oleksiy Mishchenko
  """Builds a URI list as used by index resources.
65 c2dca9af Oleksiy Mishchenko

66 c41eea6e Iustin Pop
  @param ids: list of ids as strings
67 c41eea6e Iustin Pop
  @param uri_format: format to be applied for URI
68 c41eea6e Iustin Pop
  @param uri_fields: optional parameter for field IDs
69 c2dca9af Oleksiy Mishchenko

70 c2dca9af Oleksiy Mishchenko
  """
71 c2dca9af Oleksiy Mishchenko
  (field_id, field_uri) = uri_fields
72 dca1764e Iustin Pop
73 c2dca9af Oleksiy Mishchenko
  def _MapId(m_id):
74 e687ec01 Michael Hanselmann
    return {
75 e687ec01 Michael Hanselmann
      field_id: m_id,
76 e687ec01 Michael Hanselmann
      field_uri: uri_format % m_id,
77 e687ec01 Michael Hanselmann
      }
78 c2dca9af Oleksiy Mishchenko
79 c2dca9af Oleksiy Mishchenko
  # Make sure the result is sorted, makes it nicer to look at and simplifies
80 c2dca9af Oleksiy Mishchenko
  # unittests.
81 c2dca9af Oleksiy Mishchenko
  ids.sort()
82 c2dca9af Oleksiy Mishchenko
83 c2dca9af Oleksiy Mishchenko
  return map(_MapId, ids)
84 c2dca9af Oleksiy Mishchenko
85 c2dca9af Oleksiy Mishchenko
86 c2dca9af Oleksiy Mishchenko
def ExtractField(sequence, index):
87 c2dca9af Oleksiy Mishchenko
  """Creates a list containing one column out of a list of lists.
88 c2dca9af Oleksiy Mishchenko

89 c41eea6e Iustin Pop
  @param sequence: sequence of lists
90 c41eea6e Iustin Pop
  @param index: index of field
91 c2dca9af Oleksiy Mishchenko

92 c2dca9af Oleksiy Mishchenko
  """
93 c2dca9af Oleksiy Mishchenko
  return map(lambda item: item[index], sequence)
94 c2dca9af Oleksiy Mishchenko
95 c2dca9af Oleksiy Mishchenko
96 c2dca9af Oleksiy Mishchenko
def MapFields(names, data):
97 c2dca9af Oleksiy Mishchenko
  """Maps two lists into one dictionary.
98 c2dca9af Oleksiy Mishchenko

99 c41eea6e Iustin Pop
  Example::
100 c41eea6e Iustin Pop
      >>> MapFields(["a", "b"], ["foo", 123])
101 c41eea6e Iustin Pop
      {'a': 'foo', 'b': 123}
102 c2dca9af Oleksiy Mishchenko

103 c41eea6e Iustin Pop
  @param names: field names (list of strings)
104 c41eea6e Iustin Pop
  @param data: field data (list)
105 c2dca9af Oleksiy Mishchenko

106 c2dca9af Oleksiy Mishchenko
  """
107 c2dca9af Oleksiy Mishchenko
  if len(names) != len(data):
108 c2dca9af Oleksiy Mishchenko
    raise AttributeError("Names and data must have the same length")
109 dca1764e Iustin Pop
  return dict(zip(names, data))
110 c2dca9af Oleksiy Mishchenko
111 c2dca9af Oleksiy Mishchenko
112 51ee2f49 Oleksiy Mishchenko
def MapBulkFields(itemslist, fields):
113 51ee2f49 Oleksiy Mishchenko
  """Map value to field name in to one dictionary.
114 51ee2f49 Oleksiy Mishchenko

115 c41eea6e Iustin Pop
  @param itemslist: a list of items values
116 c41eea6e Iustin Pop
  @param fields: a list of items names
117 c41eea6e Iustin Pop

118 c41eea6e Iustin Pop
  @return: a list of mapped dictionaries
119 51ee2f49 Oleksiy Mishchenko

120 51ee2f49 Oleksiy Mishchenko
  """
121 51ee2f49 Oleksiy Mishchenko
  items_details = []
122 51ee2f49 Oleksiy Mishchenko
  for item in itemslist:
123 51ee2f49 Oleksiy Mishchenko
    mapped = MapFields(fields, item)
124 51ee2f49 Oleksiy Mishchenko
    items_details.append(mapped)
125 51ee2f49 Oleksiy Mishchenko
  return items_details
126 51ee2f49 Oleksiy Mishchenko
127 51ee2f49 Oleksiy Mishchenko
128 d50b3059 Oleksiy Mishchenko
def MakeParamsDict(opts, params):
129 c41eea6e Iustin Pop
  """Makes params dictionary out of a option set.
130 d50b3059 Oleksiy Mishchenko

131 d50b3059 Oleksiy Mishchenko
  This function returns a dictionary needed for hv or be parameters. But only
132 d50b3059 Oleksiy Mishchenko
  those fields which provided in the option set. Takes parameters frozensets
133 d50b3059 Oleksiy Mishchenko
  from constants.
134 d50b3059 Oleksiy Mishchenko

135 d50b3059 Oleksiy Mishchenko
  @type opts: dict
136 d50b3059 Oleksiy Mishchenko
  @param opts: selected options
137 d50b3059 Oleksiy Mishchenko
  @type params: frozenset
138 d50b3059 Oleksiy Mishchenko
  @param params: subset of options
139 d50b3059 Oleksiy Mishchenko
  @rtype: dict
140 d50b3059 Oleksiy Mishchenko
  @return: dictionary of options, filtered by given subset.
141 d50b3059 Oleksiy Mishchenko

142 d50b3059 Oleksiy Mishchenko
  """
143 d50b3059 Oleksiy Mishchenko
  result = {}
144 d50b3059 Oleksiy Mishchenko
145 d50b3059 Oleksiy Mishchenko
  for p in params:
146 d50b3059 Oleksiy Mishchenko
    try:
147 d50b3059 Oleksiy Mishchenko
      value = opts[p]
148 d50b3059 Oleksiy Mishchenko
    except KeyError:
149 d50b3059 Oleksiy Mishchenko
      continue
150 d50b3059 Oleksiy Mishchenko
    result[p] = value
151 d50b3059 Oleksiy Mishchenko
152 d50b3059 Oleksiy Mishchenko
  return result
153 d50b3059 Oleksiy Mishchenko
154 d50b3059 Oleksiy Mishchenko
155 b166ef84 Michael Hanselmann
def FillOpcode(opcls, body, static, rename=None):
156 cfaeaaf7 Michael Hanselmann
  """Fills an opcode with body parameters.
157 cfaeaaf7 Michael Hanselmann

158 cfaeaaf7 Michael Hanselmann
  Parameter types are checked.
159 cfaeaaf7 Michael Hanselmann

160 cfaeaaf7 Michael Hanselmann
  @type opcls: L{opcodes.OpCode}
161 cfaeaaf7 Michael Hanselmann
  @param opcls: Opcode class
162 cfaeaaf7 Michael Hanselmann
  @type body: dict
163 cfaeaaf7 Michael Hanselmann
  @param body: Body parameters as received from client
164 cfaeaaf7 Michael Hanselmann
  @type static: dict
165 cfaeaaf7 Michael Hanselmann
  @param static: Static parameters which can't be modified by client
166 b166ef84 Michael Hanselmann
  @type rename: dict
167 b166ef84 Michael Hanselmann
  @param rename: Renamed parameters, key as old name, value as new name
168 cfaeaaf7 Michael Hanselmann
  @return: Opcode object
169 cfaeaaf7 Michael Hanselmann

170 cfaeaaf7 Michael Hanselmann
  """
171 c6e1a3ee Michael Hanselmann
  if body is None:
172 c6e1a3ee Michael Hanselmann
    params = {}
173 c6e1a3ee Michael Hanselmann
  else:
174 c6e1a3ee Michael Hanselmann
    CheckType(body, dict, "Body contents")
175 cfaeaaf7 Michael Hanselmann
176 c6e1a3ee Michael Hanselmann
    # Make copy to be modified
177 c6e1a3ee Michael Hanselmann
    params = body.copy()
178 b166ef84 Michael Hanselmann
179 b166ef84 Michael Hanselmann
  if rename:
180 b166ef84 Michael Hanselmann
    for old, new in rename.items():
181 b166ef84 Michael Hanselmann
      if new in params and old in params:
182 b166ef84 Michael Hanselmann
        raise http.HttpBadRequest("Parameter '%s' was renamed to '%s', but"
183 b166ef84 Michael Hanselmann
                                  " both are specified" %
184 b166ef84 Michael Hanselmann
                                  (old, new))
185 b166ef84 Michael Hanselmann
      if old in params:
186 b166ef84 Michael Hanselmann
        assert new not in params
187 b166ef84 Michael Hanselmann
        params[new] = params.pop(old)
188 b166ef84 Michael Hanselmann
189 cfaeaaf7 Michael Hanselmann
  if static:
190 b166ef84 Michael Hanselmann
    overwritten = set(params.keys()) & set(static.keys())
191 cfaeaaf7 Michael Hanselmann
    if overwritten:
192 cfaeaaf7 Michael Hanselmann
      raise http.HttpBadRequest("Can't overwrite static parameters %r" %
193 cfaeaaf7 Michael Hanselmann
                                overwritten)
194 cfaeaaf7 Michael Hanselmann
195 cfaeaaf7 Michael Hanselmann
    params.update(static)
196 cfaeaaf7 Michael Hanselmann
197 cfaeaaf7 Michael Hanselmann
  # Convert keys to strings (simplejson decodes them as unicode)
198 cfaeaaf7 Michael Hanselmann
  params = dict((str(key), value) for (key, value) in params.items())
199 cfaeaaf7 Michael Hanselmann
200 cfaeaaf7 Michael Hanselmann
  try:
201 b459a848 Andrea Spadaccini
    op = opcls(**params) # pylint: disable=W0142
202 cfaeaaf7 Michael Hanselmann
    op.Validate(False)
203 cfaeaaf7 Michael Hanselmann
  except (errors.OpPrereqError, TypeError), err:
204 cfaeaaf7 Michael Hanselmann
    raise http.HttpBadRequest("Invalid body parameters: %s" % err)
205 cfaeaaf7 Michael Hanselmann
206 cfaeaaf7 Michael Hanselmann
  return op
207 cfaeaaf7 Michael Hanselmann
208 cfaeaaf7 Michael Hanselmann
209 e8ebbd2b Michael Hanselmann
def HandleItemQueryErrors(fn, *args, **kwargs):
210 e8ebbd2b Michael Hanselmann
  """Converts errors when querying a single item.
211 e8ebbd2b Michael Hanselmann

212 e8ebbd2b Michael Hanselmann
  """
213 e8ebbd2b Michael Hanselmann
  try:
214 e8ebbd2b Michael Hanselmann
    return fn(*args, **kwargs)
215 e8ebbd2b Michael Hanselmann
  except errors.OpPrereqError, err:
216 e8ebbd2b Michael Hanselmann
    if len(err.args) == 2 and err.args[1] == errors.ECODE_NOENT:
217 e8ebbd2b Michael Hanselmann
      raise http.HttpNotFound()
218 e8ebbd2b Michael Hanselmann
219 e8ebbd2b Michael Hanselmann
    raise
220 e8ebbd2b Michael Hanselmann
221 e8ebbd2b Michael Hanselmann
222 e51ca051 Michael Hanselmann
def FeedbackFn(msg):
223 e51ca051 Michael Hanselmann
  """Feedback logging function for jobs.
224 59b4eeef Iustin Pop

225 59b4eeef Iustin Pop
  We don't have a stdout for printing log messages, so log them to the
226 59b4eeef Iustin Pop
  http log at least.
227 59b4eeef Iustin Pop

228 e51ca051 Michael Hanselmann
  @param msg: the message
229 2d54e29c Iustin Pop

230 59b4eeef Iustin Pop
  """
231 e51ca051 Michael Hanselmann
  (_, log_type, log_msg) = msg
232 59b4eeef Iustin Pop
  logging.info("%s: %s", log_type, log_msg)
233 59b4eeef Iustin Pop
234 59b4eeef Iustin Pop
235 af6433c6 Michael Hanselmann
def CheckType(value, exptype, descr):
236 af6433c6 Michael Hanselmann
  """Abort request if value type doesn't match expected type.
237 af6433c6 Michael Hanselmann

238 af6433c6 Michael Hanselmann
  @param value: Value
239 af6433c6 Michael Hanselmann
  @type exptype: type
240 af6433c6 Michael Hanselmann
  @param exptype: Expected type
241 af6433c6 Michael Hanselmann
  @type descr: string
242 af6433c6 Michael Hanselmann
  @param descr: Description of value
243 af6433c6 Michael Hanselmann
  @return: Value (allows inline usage)
244 af6433c6 Michael Hanselmann

245 af6433c6 Michael Hanselmann
  """
246 af6433c6 Michael Hanselmann
  if not isinstance(value, exptype):
247 af6433c6 Michael Hanselmann
    raise http.HttpBadRequest("%s: Type is '%s', but '%s' is expected" %
248 af6433c6 Michael Hanselmann
                              (descr, type(value).__name__, exptype.__name__))
249 af6433c6 Michael Hanselmann
250 af6433c6 Michael Hanselmann
  return value
251 af6433c6 Michael Hanselmann
252 af6433c6 Michael Hanselmann
253 af6433c6 Michael Hanselmann
def CheckParameter(data, name, default=_DEFAULT, exptype=_DEFAULT):
254 af6433c6 Michael Hanselmann
  """Check and return the value for a given parameter.
255 af6433c6 Michael Hanselmann

256 af6433c6 Michael Hanselmann
  If no default value was given and the parameter doesn't exist in the input
257 af6433c6 Michael Hanselmann
  data, an error is raise.
258 af6433c6 Michael Hanselmann

259 af6433c6 Michael Hanselmann
  @type data: dict
260 af6433c6 Michael Hanselmann
  @param data: Dictionary containing input data
261 af6433c6 Michael Hanselmann
  @type name: string
262 af6433c6 Michael Hanselmann
  @param name: Parameter name
263 af6433c6 Michael Hanselmann
  @param default: Default value (can be None)
264 af6433c6 Michael Hanselmann
  @param exptype: Expected type (can be None)
265 af6433c6 Michael Hanselmann

266 af6433c6 Michael Hanselmann
  """
267 af6433c6 Michael Hanselmann
  try:
268 af6433c6 Michael Hanselmann
    value = data[name]
269 af6433c6 Michael Hanselmann
  except KeyError:
270 af6433c6 Michael Hanselmann
    if default is not _DEFAULT:
271 af6433c6 Michael Hanselmann
      return default
272 af6433c6 Michael Hanselmann
273 af6433c6 Michael Hanselmann
    raise http.HttpBadRequest("Required parameter '%s' is missing" %
274 af6433c6 Michael Hanselmann
                              name)
275 af6433c6 Michael Hanselmann
276 af6433c6 Michael Hanselmann
  if exptype is _DEFAULT:
277 af6433c6 Michael Hanselmann
    return value
278 af6433c6 Michael Hanselmann
279 af6433c6 Michael Hanselmann
  return CheckType(value, exptype, "'%s' parameter" % name)
280 af6433c6 Michael Hanselmann
281 af6433c6 Michael Hanselmann
282 26ff6ee2 Michael Hanselmann
class ResourceBase(object):
283 c2dca9af Oleksiy Mishchenko
  """Generic class for resources.
284 c2dca9af Oleksiy Mishchenko

285 c2dca9af Oleksiy Mishchenko
  """
286 b5b67ef9 Michael Hanselmann
  # Default permission requirements
287 b5b67ef9 Michael Hanselmann
  GET_ACCESS = []
288 b5b67ef9 Michael Hanselmann
  PUT_ACCESS = [rapi.RAPI_ACCESS_WRITE]
289 b5b67ef9 Michael Hanselmann
  POST_ACCESS = [rapi.RAPI_ACCESS_WRITE]
290 b5b67ef9 Michael Hanselmann
  DELETE_ACCESS = [rapi.RAPI_ACCESS_WRITE]
291 b5b67ef9 Michael Hanselmann
292 ab85ce39 Michael Hanselmann
  def __init__(self, items, queryargs, req, _client_cls=luxi.Client):
293 c2dca9af Oleksiy Mishchenko
    """Generic resource constructor.
294 c2dca9af Oleksiy Mishchenko

295 c41eea6e Iustin Pop
    @param items: a list with variables encoded in the URL
296 c41eea6e Iustin Pop
    @param queryargs: a dictionary with additional options from URL
297 ab85ce39 Michael Hanselmann
    @param req: Request context
298 ab85ce39 Michael Hanselmann
    @param _client_cls: L{luxi} client class (unittests only)
299 c2dca9af Oleksiy Mishchenko

300 c2dca9af Oleksiy Mishchenko
    """
301 c2dca9af Oleksiy Mishchenko
    self.items = items
302 c2dca9af Oleksiy Mishchenko
    self.queryargs = queryargs
303 627ad739 Michael Hanselmann
    self._req = req
304 ab85ce39 Michael Hanselmann
    self._client_cls = _client_cls
305 627ad739 Michael Hanselmann
306 2feecf12 Michael Hanselmann
  def _GetRequestBody(self):
307 2feecf12 Michael Hanselmann
    """Returns the body data.
308 2feecf12 Michael Hanselmann

309 2feecf12 Michael Hanselmann
    """
310 2feecf12 Michael Hanselmann
    return self._req.private.body_data
311 2feecf12 Michael Hanselmann
312 2feecf12 Michael Hanselmann
  request_body = property(fget=_GetRequestBody)
313 3d103742 Iustin Pop
314 3fb8680a Michael Hanselmann
  def _checkIntVariable(self, name, default=0):
315 3d103742 Iustin Pop
    """Return the parsed value of an int argument.
316 3d103742 Iustin Pop

317 3d103742 Iustin Pop
    """
318 3fb8680a Michael Hanselmann
    val = self.queryargs.get(name, default)
319 3d103742 Iustin Pop
    if isinstance(val, list):
320 3d103742 Iustin Pop
      if val:
321 3d103742 Iustin Pop
        val = val[0]
322 3d103742 Iustin Pop
      else:
323 3fb8680a Michael Hanselmann
        val = default
324 3d103742 Iustin Pop
    try:
325 3d103742 Iustin Pop
      val = int(val)
326 7c4d6c7b Michael Hanselmann
    except (ValueError, TypeError):
327 6e99c5a0 Iustin Pop
      raise http.HttpBadRequest("Invalid value for the"
328 3d103742 Iustin Pop
                                " '%s' parameter" % (name,))
329 3d103742 Iustin Pop
    return val
330 3d103742 Iustin Pop
331 e5b7c4ca Iustin Pop
  def _checkStringVariable(self, name, default=None):
332 e5b7c4ca Iustin Pop
    """Return the parsed value of an int argument.
333 e5b7c4ca Iustin Pop

334 e5b7c4ca Iustin Pop
    """
335 e5b7c4ca Iustin Pop
    val = self.queryargs.get(name, default)
336 e5b7c4ca Iustin Pop
    if isinstance(val, list):
337 e5b7c4ca Iustin Pop
      if val:
338 e5b7c4ca Iustin Pop
        val = val[0]
339 e5b7c4ca Iustin Pop
      else:
340 e5b7c4ca Iustin Pop
        val = default
341 e5b7c4ca Iustin Pop
    return val
342 3d103742 Iustin Pop
343 6e99c5a0 Iustin Pop
  def getBodyParameter(self, name, *args):
344 6e99c5a0 Iustin Pop
    """Check and return the value for a given parameter.
345 6e99c5a0 Iustin Pop

346 6e99c5a0 Iustin Pop
    If a second parameter is not given, an error will be returned,
347 6e99c5a0 Iustin Pop
    otherwise this parameter specifies the default value.
348 6e99c5a0 Iustin Pop

349 6e99c5a0 Iustin Pop
    @param name: the required parameter
350 6e99c5a0 Iustin Pop

351 6e99c5a0 Iustin Pop
    """
352 af6433c6 Michael Hanselmann
    if args:
353 1c54156d Luca Bigliardi
      return CheckParameter(self.request_body, name, default=args[0])
354 ab221ddf Michael Hanselmann
355 1c54156d Luca Bigliardi
    return CheckParameter(self.request_body, name)
356 6e99c5a0 Iustin Pop
357 3d103742 Iustin Pop
  def useLocking(self):
358 3d103742 Iustin Pop
    """Check if the request specifies locking.
359 3d103742 Iustin Pop

360 3d103742 Iustin Pop
    """
361 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("lock"))
362 3d103742 Iustin Pop
363 3d103742 Iustin Pop
  def useBulk(self):
364 3d103742 Iustin Pop
    """Check if the request specifies bulk querying.
365 3d103742 Iustin Pop

366 3d103742 Iustin Pop
    """
367 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("bulk"))
368 6f59b964 Iustin Pop
369 3427d34f Michael Hanselmann
  def useForce(self):
370 3427d34f Michael Hanselmann
    """Check if the request specifies a forced operation.
371 3427d34f Michael Hanselmann

372 3427d34f Michael Hanselmann
    """
373 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("force"))
374 3427d34f Michael Hanselmann
375 6f59b964 Iustin Pop
  def dryRun(self):
376 6f59b964 Iustin Pop
    """Check if the request specifies dry-run mode.
377 6f59b964 Iustin Pop

378 6f59b964 Iustin Pop
    """
379 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("dry-run"))
380 be1ddd09 Michael Hanselmann
381 be1ddd09 Michael Hanselmann
  def GetClient(self):
382 be1ddd09 Michael Hanselmann
    """Wrapper for L{luxi.Client} with HTTP-specific error handling.
383 be1ddd09 Michael Hanselmann

384 be1ddd09 Michael Hanselmann
    """
385 be1ddd09 Michael Hanselmann
    # Could be a function, pylint: disable=R0201
386 be1ddd09 Michael Hanselmann
    try:
387 ab85ce39 Michael Hanselmann
      return self._client_cls()
388 be1ddd09 Michael Hanselmann
    except luxi.NoMasterError, err:
389 be1ddd09 Michael Hanselmann
      raise http.HttpBadGateway("Can't connect to master daemon: %s" % err)
390 be1ddd09 Michael Hanselmann
    except luxi.PermissionError:
391 be1ddd09 Michael Hanselmann
      raise http.HttpInternalServerError("Internal error: no permission to"
392 be1ddd09 Michael Hanselmann
                                         " connect to the master daemon")
393 be1ddd09 Michael Hanselmann
394 be1ddd09 Michael Hanselmann
  def SubmitJob(self, op, cl=None):
395 be1ddd09 Michael Hanselmann
    """Generic wrapper for submit job, for better http compatibility.
396 be1ddd09 Michael Hanselmann

397 be1ddd09 Michael Hanselmann
    @type op: list
398 be1ddd09 Michael Hanselmann
    @param op: the list of opcodes for the job
399 be1ddd09 Michael Hanselmann
    @type cl: None or luxi.Client
400 be1ddd09 Michael Hanselmann
    @param cl: optional luxi client to use
401 be1ddd09 Michael Hanselmann
    @rtype: string
402 be1ddd09 Michael Hanselmann
    @return: the job ID
403 be1ddd09 Michael Hanselmann

404 be1ddd09 Michael Hanselmann
    """
405 be1ddd09 Michael Hanselmann
    if cl is None:
406 be1ddd09 Michael Hanselmann
      cl = self.GetClient()
407 be1ddd09 Michael Hanselmann
    try:
408 be1ddd09 Michael Hanselmann
      return cl.SubmitJob(op)
409 be1ddd09 Michael Hanselmann
    except errors.JobQueueFull:
410 be1ddd09 Michael Hanselmann
      raise http.HttpServiceUnavailable("Job queue is full, needs archiving")
411 be1ddd09 Michael Hanselmann
    except errors.JobQueueDrainError:
412 be1ddd09 Michael Hanselmann
      raise http.HttpServiceUnavailable("Job queue is drained, cannot submit")
413 be1ddd09 Michael Hanselmann
    except luxi.NoMasterError, err:
414 be1ddd09 Michael Hanselmann
      raise http.HttpBadGateway("Master seems to be unreachable: %s" % err)
415 be1ddd09 Michael Hanselmann
    except luxi.PermissionError:
416 be1ddd09 Michael Hanselmann
      raise http.HttpInternalServerError("Internal error: no permission to"
417 be1ddd09 Michael Hanselmann
                                         " connect to the master daemon")
418 be1ddd09 Michael Hanselmann
    except luxi.TimeoutError, err:
419 be1ddd09 Michael Hanselmann
      raise http.HttpGatewayTimeout("Timeout while talking to the master"
420 be1ddd09 Michael Hanselmann
                                    " daemon: %s" % err)
421 c08fd0d6 Michael Hanselmann
422 c08fd0d6 Michael Hanselmann
423 b8ab1c7f Michael Hanselmann
def GetResourceOpcodes(cls):
424 b8ab1c7f Michael Hanselmann
  """Returns all opcodes used by a resource.
425 b8ab1c7f Michael Hanselmann

426 b8ab1c7f Michael Hanselmann
  """
427 b8ab1c7f Michael Hanselmann
  return frozenset(filter(None, (getattr(cls, op_attr, None)
428 b8ab1c7f Michael Hanselmann
                                 for (_, op_attr, _, _) in _OPCODE_ATTRS)))
429 b8ab1c7f Michael Hanselmann
430 b8ab1c7f Michael Hanselmann
431 c08fd0d6 Michael Hanselmann
class _MetaOpcodeResource(type):
432 c08fd0d6 Michael Hanselmann
  """Meta class for RAPI resources.
433 c08fd0d6 Michael Hanselmann

434 c08fd0d6 Michael Hanselmann
  """
435 c08fd0d6 Michael Hanselmann
  def __call__(mcs, *args, **kwargs):
436 c08fd0d6 Michael Hanselmann
    """Instantiates class and patches it for use by the RAPI daemon.
437 c08fd0d6 Michael Hanselmann

438 c08fd0d6 Michael Hanselmann
    """
439 c08fd0d6 Michael Hanselmann
    # Access to private attributes of a client class, pylint: disable=W0212
440 c08fd0d6 Michael Hanselmann
    obj = type.__call__(mcs, *args, **kwargs)
441 c08fd0d6 Michael Hanselmann
442 b8ab1c7f Michael Hanselmann
    for (method, op_attr, rename_attr, fn_attr) in _OPCODE_ATTRS:
443 f6ce0ba2 Michael Hanselmann
      if hasattr(obj, method):
444 f6ce0ba2 Michael Hanselmann
        # If the method handler is already defined, "*_RENAME" or "Get*OpInput"
445 f6ce0ba2 Michael Hanselmann
        # shouldn't be (they're only used by the automatically generated
446 f6ce0ba2 Michael Hanselmann
        # handler)
447 c08fd0d6 Michael Hanselmann
        assert not hasattr(obj, rename_attr)
448 c08fd0d6 Michael Hanselmann
        assert not hasattr(obj, fn_attr)
449 f6ce0ba2 Michael Hanselmann
      else:
450 f6ce0ba2 Michael Hanselmann
        # Try to generate handler method on handler instance
451 f6ce0ba2 Michael Hanselmann
        try:
452 f6ce0ba2 Michael Hanselmann
          opcode = getattr(obj, op_attr)
453 f6ce0ba2 Michael Hanselmann
        except AttributeError:
454 f6ce0ba2 Michael Hanselmann
          pass
455 f6ce0ba2 Michael Hanselmann
        else:
456 f6ce0ba2 Michael Hanselmann
          setattr(obj, method,
457 f6ce0ba2 Michael Hanselmann
                  compat.partial(obj._GenericHandler, opcode,
458 f6ce0ba2 Michael Hanselmann
                                 getattr(obj, rename_attr, None),
459 f6ce0ba2 Michael Hanselmann
                                 getattr(obj, fn_attr, obj._GetDefaultData)))
460 c08fd0d6 Michael Hanselmann
461 c08fd0d6 Michael Hanselmann
    return obj
462 c08fd0d6 Michael Hanselmann
463 c08fd0d6 Michael Hanselmann
464 c08fd0d6 Michael Hanselmann
class OpcodeResource(ResourceBase):
465 c08fd0d6 Michael Hanselmann
  """Base class for opcode-based RAPI resources.
466 c08fd0d6 Michael Hanselmann

467 c08fd0d6 Michael Hanselmann
  Instances of this class automatically gain handler functions through
468 c08fd0d6 Michael Hanselmann
  L{_MetaOpcodeResource} for any method for which a C{$METHOD$_OPCODE} variable
469 c08fd0d6 Michael Hanselmann
  is defined at class level. Subclasses can define a C{Get$Method$OpInput}
470 c08fd0d6 Michael Hanselmann
  method to do their own opcode input processing (e.g. for static values). The
471 c08fd0d6 Michael Hanselmann
  C{$METHOD$_RENAME} variable defines which values are renamed (see
472 c08fd0d6 Michael Hanselmann
  L{FillOpcode}).
473 c08fd0d6 Michael Hanselmann

474 c08fd0d6 Michael Hanselmann
  @cvar GET_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
475 c08fd0d6 Michael Hanselmann
    automatically generate a GET handler submitting the opcode
476 c08fd0d6 Michael Hanselmann
  @cvar GET_RENAME: Set this to rename parameters in the GET handler (see
477 c08fd0d6 Michael Hanselmann
    L{FillOpcode})
478 c08fd0d6 Michael Hanselmann
  @ivar GetGetOpInput: Define this to override the default method for
479 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
480 c08fd0d6 Michael Hanselmann

481 c08fd0d6 Michael Hanselmann
  @cvar PUT_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
482 c08fd0d6 Michael Hanselmann
    automatically generate a PUT handler submitting the opcode
483 c08fd0d6 Michael Hanselmann
  @cvar PUT_RENAME: Set this to rename parameters in the PUT handler (see
484 c08fd0d6 Michael Hanselmann
    L{FillOpcode})
485 c08fd0d6 Michael Hanselmann
  @ivar GetPutOpInput: Define this to override the default method for
486 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
487 c08fd0d6 Michael Hanselmann

488 c08fd0d6 Michael Hanselmann
  @cvar POST_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
489 c08fd0d6 Michael Hanselmann
    automatically generate a POST handler submitting the opcode
490 c08fd0d6 Michael Hanselmann
  @cvar POST_RENAME: Set this to rename parameters in the DELETE handler (see
491 c08fd0d6 Michael Hanselmann
    L{FillOpcode})
492 c08fd0d6 Michael Hanselmann
  @ivar GetPostOpInput: Define this to override the default method for
493 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
494 c08fd0d6 Michael Hanselmann

495 c08fd0d6 Michael Hanselmann
  @cvar DELETE_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
496 c08fd0d6 Michael Hanselmann
    automatically generate a GET handler submitting the opcode
497 c08fd0d6 Michael Hanselmann
  @cvar DELETE_RENAME: Set this to rename parameters in the DELETE handler (see
498 c08fd0d6 Michael Hanselmann
    L{FillOpcode})
499 c08fd0d6 Michael Hanselmann
  @ivar GetDeleteOpInput: Define this to override the default method for
500 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
501 c08fd0d6 Michael Hanselmann

502 c08fd0d6 Michael Hanselmann
  """
503 c08fd0d6 Michael Hanselmann
  __metaclass__ = _MetaOpcodeResource
504 c08fd0d6 Michael Hanselmann
505 c08fd0d6 Michael Hanselmann
  def _GetDefaultData(self):
506 c08fd0d6 Michael Hanselmann
    return (self.request_body, None)
507 c08fd0d6 Michael Hanselmann
508 c08fd0d6 Michael Hanselmann
  def _GenericHandler(self, opcode, rename, fn):
509 c08fd0d6 Michael Hanselmann
    (body, static) = fn()
510 c08fd0d6 Michael Hanselmann
    op = FillOpcode(opcode, body, static, rename=rename)
511 c08fd0d6 Michael Hanselmann
    return self.SubmitJob([op])