Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / baserlib.py @ 0232b768

History | View | Annotate | Download (14.9 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 303bc802 Iustin Pop
from ganeti import constants
38 4e55af74 Michael Hanselmann
from ganeti import pathutils
39 441e7cfd Oleksiy Mishchenko
40 c2dca9af Oleksiy Mishchenko
41 af6433c6 Michael Hanselmann
# Dummy value to detect unchanged parameters
42 af6433c6 Michael Hanselmann
_DEFAULT = object()
43 af6433c6 Michael Hanselmann
44 c08fd0d6 Michael Hanselmann
#: Supported HTTP methods
45 c08fd0d6 Michael Hanselmann
_SUPPORTED_METHODS = frozenset([
46 c08fd0d6 Michael Hanselmann
  http.HTTP_DELETE,
47 c08fd0d6 Michael Hanselmann
  http.HTTP_GET,
48 c08fd0d6 Michael Hanselmann
  http.HTTP_POST,
49 c08fd0d6 Michael Hanselmann
  http.HTTP_PUT,
50 c08fd0d6 Michael Hanselmann
  ])
51 c08fd0d6 Michael Hanselmann
52 af6433c6 Michael Hanselmann
53 b8ab1c7f Michael Hanselmann
def _BuildOpcodeAttributes():
54 b8ab1c7f Michael Hanselmann
  """Builds list of attributes used for per-handler opcodes.
55 b8ab1c7f Michael Hanselmann

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

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

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

91 c41eea6e Iustin Pop
  @param sequence: sequence of lists
92 c41eea6e Iustin Pop
  @param index: index of field
93 c2dca9af Oleksiy Mishchenko

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

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

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

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

117 c41eea6e Iustin Pop
  @param itemslist: a list of items values
118 c41eea6e Iustin Pop
  @param fields: a list of items names
119 c41eea6e Iustin Pop

120 c41eea6e Iustin Pop
  @return: a list of mapped dictionaries
121 51ee2f49 Oleksiy Mishchenko

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

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

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

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

160 cfaeaaf7 Michael Hanselmann
  Parameter types are checked.
161 cfaeaaf7 Michael Hanselmann

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

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

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

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

230 e51ca051 Michael Hanselmann
  @param msg: the message
231 2d54e29c Iustin Pop

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

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

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

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

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

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

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

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

302 c2dca9af Oleksiy Mishchenko
    """
303 c2dca9af Oleksiy Mishchenko
    self.items = items
304 c2dca9af Oleksiy Mishchenko
    self.queryargs = queryargs
305 627ad739 Michael Hanselmann
    self._req = req
306 da04c447 Michael Hanselmann
307 da04c447 Michael Hanselmann
    if _client_cls is None:
308 da04c447 Michael Hanselmann
      _client_cls = luxi.Client
309 da04c447 Michael Hanselmann
310 ab85ce39 Michael Hanselmann
    self._client_cls = _client_cls
311 627ad739 Michael Hanselmann
312 2feecf12 Michael Hanselmann
  def _GetRequestBody(self):
313 2feecf12 Michael Hanselmann
    """Returns the body data.
314 2feecf12 Michael Hanselmann

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

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

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

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

355 6e99c5a0 Iustin Pop
    @param name: the required parameter
356 6e99c5a0 Iustin Pop

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

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

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

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

384 6f59b964 Iustin Pop
    """
385 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("dry-run"))
386 be1ddd09 Michael Hanselmann
387 303bc802 Iustin Pop
  def GetClient(self, query=False):
388 be1ddd09 Michael Hanselmann
    """Wrapper for L{luxi.Client} with HTTP-specific error handling.
389 be1ddd09 Michael Hanselmann

390 303bc802 Iustin Pop
    @param query: this signifies that the client will only be used for
391 303bc802 Iustin Pop
        queries; if the build-time parameter enable-split-queries is
392 303bc802 Iustin Pop
        enabled, then the client will be connected to the query socket
393 303bc802 Iustin Pop
        instead of the masterd socket
394 303bc802 Iustin Pop

395 be1ddd09 Michael Hanselmann
    """
396 303bc802 Iustin Pop
    if query and constants.ENABLE_SPLIT_QUERY:
397 4e55af74 Michael Hanselmann
      address = pathutils.QUERY_SOCKET
398 303bc802 Iustin Pop
    else:
399 303bc802 Iustin Pop
      address = None
400 be1ddd09 Michael Hanselmann
    # Could be a function, pylint: disable=R0201
401 be1ddd09 Michael Hanselmann
    try:
402 303bc802 Iustin Pop
      return self._client_cls(address=address)
403 be1ddd09 Michael Hanselmann
    except luxi.NoMasterError, err:
404 be1ddd09 Michael Hanselmann
      raise http.HttpBadGateway("Can't connect to master daemon: %s" % err)
405 be1ddd09 Michael Hanselmann
    except luxi.PermissionError:
406 be1ddd09 Michael Hanselmann
      raise http.HttpInternalServerError("Internal error: no permission to"
407 be1ddd09 Michael Hanselmann
                                         " connect to the master daemon")
408 be1ddd09 Michael Hanselmann
409 be1ddd09 Michael Hanselmann
  def SubmitJob(self, op, cl=None):
410 be1ddd09 Michael Hanselmann
    """Generic wrapper for submit job, for better http compatibility.
411 be1ddd09 Michael Hanselmann

412 be1ddd09 Michael Hanselmann
    @type op: list
413 be1ddd09 Michael Hanselmann
    @param op: the list of opcodes for the job
414 be1ddd09 Michael Hanselmann
    @type cl: None or luxi.Client
415 be1ddd09 Michael Hanselmann
    @param cl: optional luxi client to use
416 be1ddd09 Michael Hanselmann
    @rtype: string
417 be1ddd09 Michael Hanselmann
    @return: the job ID
418 be1ddd09 Michael Hanselmann

419 be1ddd09 Michael Hanselmann
    """
420 be1ddd09 Michael Hanselmann
    if cl is None:
421 be1ddd09 Michael Hanselmann
      cl = self.GetClient()
422 be1ddd09 Michael Hanselmann
    try:
423 be1ddd09 Michael Hanselmann
      return cl.SubmitJob(op)
424 be1ddd09 Michael Hanselmann
    except errors.JobQueueFull:
425 be1ddd09 Michael Hanselmann
      raise http.HttpServiceUnavailable("Job queue is full, needs archiving")
426 be1ddd09 Michael Hanselmann
    except errors.JobQueueDrainError:
427 be1ddd09 Michael Hanselmann
      raise http.HttpServiceUnavailable("Job queue is drained, cannot submit")
428 be1ddd09 Michael Hanselmann
    except luxi.NoMasterError, err:
429 be1ddd09 Michael Hanselmann
      raise http.HttpBadGateway("Master seems to be unreachable: %s" % err)
430 be1ddd09 Michael Hanselmann
    except luxi.PermissionError:
431 be1ddd09 Michael Hanselmann
      raise http.HttpInternalServerError("Internal error: no permission to"
432 be1ddd09 Michael Hanselmann
                                         " connect to the master daemon")
433 be1ddd09 Michael Hanselmann
    except luxi.TimeoutError, err:
434 be1ddd09 Michael Hanselmann
      raise http.HttpGatewayTimeout("Timeout while talking to the master"
435 be1ddd09 Michael Hanselmann
                                    " daemon: %s" % err)
436 c08fd0d6 Michael Hanselmann
437 c08fd0d6 Michael Hanselmann
438 b8ab1c7f Michael Hanselmann
def GetResourceOpcodes(cls):
439 b8ab1c7f Michael Hanselmann
  """Returns all opcodes used by a resource.
440 b8ab1c7f Michael Hanselmann

441 b8ab1c7f Michael Hanselmann
  """
442 b8ab1c7f Michael Hanselmann
  return frozenset(filter(None, (getattr(cls, op_attr, None)
443 b8ab1c7f Michael Hanselmann
                                 for (_, op_attr, _, _) in _OPCODE_ATTRS)))
444 b8ab1c7f Michael Hanselmann
445 b8ab1c7f Michael Hanselmann
446 c08fd0d6 Michael Hanselmann
class _MetaOpcodeResource(type):
447 c08fd0d6 Michael Hanselmann
  """Meta class for RAPI resources.
448 c08fd0d6 Michael Hanselmann

449 c08fd0d6 Michael Hanselmann
  """
450 c08fd0d6 Michael Hanselmann
  def __call__(mcs, *args, **kwargs):
451 c08fd0d6 Michael Hanselmann
    """Instantiates class and patches it for use by the RAPI daemon.
452 c08fd0d6 Michael Hanselmann

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

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

489 c08fd0d6 Michael Hanselmann
  @cvar GET_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
490 c08fd0d6 Michael Hanselmann
    automatically generate a GET handler submitting the opcode
491 c08fd0d6 Michael Hanselmann
  @cvar GET_RENAME: Set this to rename parameters in the GET handler (see
492 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
493 c08fd0d6 Michael Hanselmann
  @ivar GetGetOpInput: Define this to override the default method for
494 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
495 c08fd0d6 Michael Hanselmann

496 c08fd0d6 Michael Hanselmann
  @cvar PUT_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
497 c08fd0d6 Michael Hanselmann
    automatically generate a PUT handler submitting the opcode
498 c08fd0d6 Michael Hanselmann
  @cvar PUT_RENAME: Set this to rename parameters in the PUT handler (see
499 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
500 c08fd0d6 Michael Hanselmann
  @ivar GetPutOpInput: Define this to override the default method for
501 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
502 c08fd0d6 Michael Hanselmann

503 c08fd0d6 Michael Hanselmann
  @cvar POST_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
504 c08fd0d6 Michael Hanselmann
    automatically generate a POST handler submitting the opcode
505 c08fd0d6 Michael Hanselmann
  @cvar POST_RENAME: Set this to rename parameters in the DELETE handler (see
506 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
507 c08fd0d6 Michael Hanselmann
  @ivar GetPostOpInput: Define this to override the default method for
508 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
509 c08fd0d6 Michael Hanselmann

510 c08fd0d6 Michael Hanselmann
  @cvar DELETE_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
511 c08fd0d6 Michael Hanselmann
    automatically generate a GET handler submitting the opcode
512 c08fd0d6 Michael Hanselmann
  @cvar DELETE_RENAME: Set this to rename parameters in the DELETE handler (see
513 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
514 c08fd0d6 Michael Hanselmann
  @ivar GetDeleteOpInput: Define this to override the default method for
515 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
516 c08fd0d6 Michael Hanselmann

517 c08fd0d6 Michael Hanselmann
  """
518 c08fd0d6 Michael Hanselmann
  __metaclass__ = _MetaOpcodeResource
519 c08fd0d6 Michael Hanselmann
520 c08fd0d6 Michael Hanselmann
  def _GetDefaultData(self):
521 c08fd0d6 Michael Hanselmann
    return (self.request_body, None)
522 c08fd0d6 Michael Hanselmann
523 c08fd0d6 Michael Hanselmann
  def _GenericHandler(self, opcode, rename, fn):
524 c08fd0d6 Michael Hanselmann
    (body, static) = fn()
525 c08fd0d6 Michael Hanselmann
    op = FillOpcode(opcode, body, static, rename=rename)
526 c08fd0d6 Michael Hanselmann
    return self.SubmitJob([op])