Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / baserlib.py @ 31d827d1

History | View | Annotate | Download (14.5 kB)

1 c2dca9af Oleksiy Mishchenko
#
2 c2dca9af Oleksiy Mishchenko
#
3 c2dca9af Oleksiy Mishchenko
4 3bd0f3d8 Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2012 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 da04c447 Michael Hanselmann
  def __init__(self, items, queryargs, req, _client_cls=None):
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 da04c447 Michael Hanselmann
305 da04c447 Michael Hanselmann
    if _client_cls is None:
306 da04c447 Michael Hanselmann
      _client_cls = luxi.Client
307 da04c447 Michael Hanselmann
308 ab85ce39 Michael Hanselmann
    self._client_cls = _client_cls
309 627ad739 Michael Hanselmann
310 2feecf12 Michael Hanselmann
  def _GetRequestBody(self):
311 2feecf12 Michael Hanselmann
    """Returns the body data.
312 2feecf12 Michael Hanselmann

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

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

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

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

353 6e99c5a0 Iustin Pop
    @param name: the required parameter
354 6e99c5a0 Iustin Pop

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

364 3d103742 Iustin Pop
    """
365 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("lock"))
366 3d103742 Iustin Pop
367 3d103742 Iustin Pop
  def useBulk(self):
368 3d103742 Iustin Pop
    """Check if the request specifies bulk querying.
369 3d103742 Iustin Pop

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

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

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

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

401 be1ddd09 Michael Hanselmann
    @type op: list
402 be1ddd09 Michael Hanselmann
    @param op: the list of opcodes for the job
403 be1ddd09 Michael Hanselmann
    @type cl: None or luxi.Client
404 be1ddd09 Michael Hanselmann
    @param cl: optional luxi client to use
405 be1ddd09 Michael Hanselmann
    @rtype: string
406 be1ddd09 Michael Hanselmann
    @return: the job ID
407 be1ddd09 Michael Hanselmann

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

430 b8ab1c7f Michael Hanselmann
  """
431 b8ab1c7f Michael Hanselmann
  return frozenset(filter(None, (getattr(cls, op_attr, None)
432 b8ab1c7f Michael Hanselmann
                                 for (_, op_attr, _, _) in _OPCODE_ATTRS)))
433 b8ab1c7f Michael Hanselmann
434 b8ab1c7f Michael Hanselmann
435 c08fd0d6 Michael Hanselmann
class _MetaOpcodeResource(type):
436 c08fd0d6 Michael Hanselmann
  """Meta class for RAPI resources.
437 c08fd0d6 Michael Hanselmann

438 c08fd0d6 Michael Hanselmann
  """
439 c08fd0d6 Michael Hanselmann
  def __call__(mcs, *args, **kwargs):
440 c08fd0d6 Michael Hanselmann
    """Instantiates class and patches it for use by the RAPI daemon.
441 c08fd0d6 Michael Hanselmann

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

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

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

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

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

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

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