Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / baserlib.py @ c3a5176f

History | View | Annotate | Download (14.4 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 b8028dcf Michael Hanselmann
_SUPPORTED_METHODS = compat.UniqueFrozenset([
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 c3a5176f 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 MapFields(names, data):
89 c2dca9af Oleksiy Mishchenko
  """Maps two lists into one dictionary.
90 c2dca9af Oleksiy Mishchenko

91 c41eea6e Iustin Pop
  Example::
92 c41eea6e Iustin Pop
      >>> MapFields(["a", "b"], ["foo", 123])
93 c41eea6e Iustin Pop
      {'a': 'foo', 'b': 123}
94 c2dca9af Oleksiy Mishchenko

95 c41eea6e Iustin Pop
  @param names: field names (list of strings)
96 c41eea6e Iustin Pop
  @param data: field data (list)
97 c2dca9af Oleksiy Mishchenko

98 c2dca9af Oleksiy Mishchenko
  """
99 c2dca9af Oleksiy Mishchenko
  if len(names) != len(data):
100 c2dca9af Oleksiy Mishchenko
    raise AttributeError("Names and data must have the same length")
101 dca1764e Iustin Pop
  return dict(zip(names, data))
102 c2dca9af Oleksiy Mishchenko
103 c2dca9af Oleksiy Mishchenko
104 51ee2f49 Oleksiy Mishchenko
def MapBulkFields(itemslist, fields):
105 51ee2f49 Oleksiy Mishchenko
  """Map value to field name in to one dictionary.
106 51ee2f49 Oleksiy Mishchenko

107 c41eea6e Iustin Pop
  @param itemslist: a list of items values
108 c41eea6e Iustin Pop
  @param fields: a list of items names
109 c41eea6e Iustin Pop

110 c41eea6e Iustin Pop
  @return: a list of mapped dictionaries
111 51ee2f49 Oleksiy Mishchenko

112 51ee2f49 Oleksiy Mishchenko
  """
113 51ee2f49 Oleksiy Mishchenko
  items_details = []
114 51ee2f49 Oleksiy Mishchenko
  for item in itemslist:
115 51ee2f49 Oleksiy Mishchenko
    mapped = MapFields(fields, item)
116 51ee2f49 Oleksiy Mishchenko
    items_details.append(mapped)
117 51ee2f49 Oleksiy Mishchenko
  return items_details
118 51ee2f49 Oleksiy Mishchenko
119 51ee2f49 Oleksiy Mishchenko
120 b166ef84 Michael Hanselmann
def FillOpcode(opcls, body, static, rename=None):
121 cfaeaaf7 Michael Hanselmann
  """Fills an opcode with body parameters.
122 cfaeaaf7 Michael Hanselmann

123 cfaeaaf7 Michael Hanselmann
  Parameter types are checked.
124 cfaeaaf7 Michael Hanselmann

125 cfaeaaf7 Michael Hanselmann
  @type opcls: L{opcodes.OpCode}
126 cfaeaaf7 Michael Hanselmann
  @param opcls: Opcode class
127 cfaeaaf7 Michael Hanselmann
  @type body: dict
128 cfaeaaf7 Michael Hanselmann
  @param body: Body parameters as received from client
129 cfaeaaf7 Michael Hanselmann
  @type static: dict
130 cfaeaaf7 Michael Hanselmann
  @param static: Static parameters which can't be modified by client
131 b166ef84 Michael Hanselmann
  @type rename: dict
132 b166ef84 Michael Hanselmann
  @param rename: Renamed parameters, key as old name, value as new name
133 cfaeaaf7 Michael Hanselmann
  @return: Opcode object
134 cfaeaaf7 Michael Hanselmann

135 cfaeaaf7 Michael Hanselmann
  """
136 c6e1a3ee Michael Hanselmann
  if body is None:
137 c6e1a3ee Michael Hanselmann
    params = {}
138 c6e1a3ee Michael Hanselmann
  else:
139 c6e1a3ee Michael Hanselmann
    CheckType(body, dict, "Body contents")
140 cfaeaaf7 Michael Hanselmann
141 c6e1a3ee Michael Hanselmann
    # Make copy to be modified
142 c6e1a3ee Michael Hanselmann
    params = body.copy()
143 b166ef84 Michael Hanselmann
144 b166ef84 Michael Hanselmann
  if rename:
145 b166ef84 Michael Hanselmann
    for old, new in rename.items():
146 b166ef84 Michael Hanselmann
      if new in params and old in params:
147 b166ef84 Michael Hanselmann
        raise http.HttpBadRequest("Parameter '%s' was renamed to '%s', but"
148 b166ef84 Michael Hanselmann
                                  " both are specified" %
149 b166ef84 Michael Hanselmann
                                  (old, new))
150 b166ef84 Michael Hanselmann
      if old in params:
151 b166ef84 Michael Hanselmann
        assert new not in params
152 b166ef84 Michael Hanselmann
        params[new] = params.pop(old)
153 b166ef84 Michael Hanselmann
154 cfaeaaf7 Michael Hanselmann
  if static:
155 b166ef84 Michael Hanselmann
    overwritten = set(params.keys()) & set(static.keys())
156 cfaeaaf7 Michael Hanselmann
    if overwritten:
157 cfaeaaf7 Michael Hanselmann
      raise http.HttpBadRequest("Can't overwrite static parameters %r" %
158 cfaeaaf7 Michael Hanselmann
                                overwritten)
159 cfaeaaf7 Michael Hanselmann
160 cfaeaaf7 Michael Hanselmann
    params.update(static)
161 cfaeaaf7 Michael Hanselmann
162 cfaeaaf7 Michael Hanselmann
  # Convert keys to strings (simplejson decodes them as unicode)
163 cfaeaaf7 Michael Hanselmann
  params = dict((str(key), value) for (key, value) in params.items())
164 cfaeaaf7 Michael Hanselmann
165 cfaeaaf7 Michael Hanselmann
  try:
166 b459a848 Andrea Spadaccini
    op = opcls(**params) # pylint: disable=W0142
167 cfaeaaf7 Michael Hanselmann
    op.Validate(False)
168 cfaeaaf7 Michael Hanselmann
  except (errors.OpPrereqError, TypeError), err:
169 cfaeaaf7 Michael Hanselmann
    raise http.HttpBadRequest("Invalid body parameters: %s" % err)
170 cfaeaaf7 Michael Hanselmann
171 cfaeaaf7 Michael Hanselmann
  return op
172 cfaeaaf7 Michael Hanselmann
173 cfaeaaf7 Michael Hanselmann
174 e8ebbd2b Michael Hanselmann
def HandleItemQueryErrors(fn, *args, **kwargs):
175 e8ebbd2b Michael Hanselmann
  """Converts errors when querying a single item.
176 e8ebbd2b Michael Hanselmann

177 e8ebbd2b Michael Hanselmann
  """
178 e8ebbd2b Michael Hanselmann
  try:
179 e8ebbd2b Michael Hanselmann
    return fn(*args, **kwargs)
180 e8ebbd2b Michael Hanselmann
  except errors.OpPrereqError, err:
181 e8ebbd2b Michael Hanselmann
    if len(err.args) == 2 and err.args[1] == errors.ECODE_NOENT:
182 e8ebbd2b Michael Hanselmann
      raise http.HttpNotFound()
183 e8ebbd2b Michael Hanselmann
184 e8ebbd2b Michael Hanselmann
    raise
185 e8ebbd2b Michael Hanselmann
186 e8ebbd2b Michael Hanselmann
187 e51ca051 Michael Hanselmann
def FeedbackFn(msg):
188 e51ca051 Michael Hanselmann
  """Feedback logging function for jobs.
189 59b4eeef Iustin Pop

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

193 e51ca051 Michael Hanselmann
  @param msg: the message
194 2d54e29c Iustin Pop

195 59b4eeef Iustin Pop
  """
196 e51ca051 Michael Hanselmann
  (_, log_type, log_msg) = msg
197 59b4eeef Iustin Pop
  logging.info("%s: %s", log_type, log_msg)
198 59b4eeef Iustin Pop
199 59b4eeef Iustin Pop
200 af6433c6 Michael Hanselmann
def CheckType(value, exptype, descr):
201 af6433c6 Michael Hanselmann
  """Abort request if value type doesn't match expected type.
202 af6433c6 Michael Hanselmann

203 af6433c6 Michael Hanselmann
  @param value: Value
204 af6433c6 Michael Hanselmann
  @type exptype: type
205 af6433c6 Michael Hanselmann
  @param exptype: Expected type
206 af6433c6 Michael Hanselmann
  @type descr: string
207 af6433c6 Michael Hanselmann
  @param descr: Description of value
208 af6433c6 Michael Hanselmann
  @return: Value (allows inline usage)
209 af6433c6 Michael Hanselmann

210 af6433c6 Michael Hanselmann
  """
211 af6433c6 Michael Hanselmann
  if not isinstance(value, exptype):
212 af6433c6 Michael Hanselmann
    raise http.HttpBadRequest("%s: Type is '%s', but '%s' is expected" %
213 af6433c6 Michael Hanselmann
                              (descr, type(value).__name__, exptype.__name__))
214 af6433c6 Michael Hanselmann
215 af6433c6 Michael Hanselmann
  return value
216 af6433c6 Michael Hanselmann
217 af6433c6 Michael Hanselmann
218 af6433c6 Michael Hanselmann
def CheckParameter(data, name, default=_DEFAULT, exptype=_DEFAULT):
219 af6433c6 Michael Hanselmann
  """Check and return the value for a given parameter.
220 af6433c6 Michael Hanselmann

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

224 af6433c6 Michael Hanselmann
  @type data: dict
225 af6433c6 Michael Hanselmann
  @param data: Dictionary containing input data
226 af6433c6 Michael Hanselmann
  @type name: string
227 af6433c6 Michael Hanselmann
  @param name: Parameter name
228 af6433c6 Michael Hanselmann
  @param default: Default value (can be None)
229 af6433c6 Michael Hanselmann
  @param exptype: Expected type (can be None)
230 af6433c6 Michael Hanselmann

231 af6433c6 Michael Hanselmann
  """
232 af6433c6 Michael Hanselmann
  try:
233 af6433c6 Michael Hanselmann
    value = data[name]
234 af6433c6 Michael Hanselmann
  except KeyError:
235 af6433c6 Michael Hanselmann
    if default is not _DEFAULT:
236 af6433c6 Michael Hanselmann
      return default
237 af6433c6 Michael Hanselmann
238 af6433c6 Michael Hanselmann
    raise http.HttpBadRequest("Required parameter '%s' is missing" %
239 af6433c6 Michael Hanselmann
                              name)
240 af6433c6 Michael Hanselmann
241 af6433c6 Michael Hanselmann
  if exptype is _DEFAULT:
242 af6433c6 Michael Hanselmann
    return value
243 af6433c6 Michael Hanselmann
244 af6433c6 Michael Hanselmann
  return CheckType(value, exptype, "'%s' parameter" % name)
245 af6433c6 Michael Hanselmann
246 af6433c6 Michael Hanselmann
247 26ff6ee2 Michael Hanselmann
class ResourceBase(object):
248 c2dca9af Oleksiy Mishchenko
  """Generic class for resources.
249 c2dca9af Oleksiy Mishchenko

250 c2dca9af Oleksiy Mishchenko
  """
251 b5b67ef9 Michael Hanselmann
  # Default permission requirements
252 b5b67ef9 Michael Hanselmann
  GET_ACCESS = []
253 b5b67ef9 Michael Hanselmann
  PUT_ACCESS = [rapi.RAPI_ACCESS_WRITE]
254 b5b67ef9 Michael Hanselmann
  POST_ACCESS = [rapi.RAPI_ACCESS_WRITE]
255 b5b67ef9 Michael Hanselmann
  DELETE_ACCESS = [rapi.RAPI_ACCESS_WRITE]
256 b5b67ef9 Michael Hanselmann
257 da04c447 Michael Hanselmann
  def __init__(self, items, queryargs, req, _client_cls=None):
258 c2dca9af Oleksiy Mishchenko
    """Generic resource constructor.
259 c2dca9af Oleksiy Mishchenko

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

265 c2dca9af Oleksiy Mishchenko
    """
266 c2dca9af Oleksiy Mishchenko
    self.items = items
267 c2dca9af Oleksiy Mishchenko
    self.queryargs = queryargs
268 627ad739 Michael Hanselmann
    self._req = req
269 da04c447 Michael Hanselmann
270 da04c447 Michael Hanselmann
    if _client_cls is None:
271 da04c447 Michael Hanselmann
      _client_cls = luxi.Client
272 da04c447 Michael Hanselmann
273 ab85ce39 Michael Hanselmann
    self._client_cls = _client_cls
274 627ad739 Michael Hanselmann
275 2feecf12 Michael Hanselmann
  def _GetRequestBody(self):
276 2feecf12 Michael Hanselmann
    """Returns the body data.
277 2feecf12 Michael Hanselmann

278 2feecf12 Michael Hanselmann
    """
279 2feecf12 Michael Hanselmann
    return self._req.private.body_data
280 2feecf12 Michael Hanselmann
281 2feecf12 Michael Hanselmann
  request_body = property(fget=_GetRequestBody)
282 3d103742 Iustin Pop
283 3fb8680a Michael Hanselmann
  def _checkIntVariable(self, name, default=0):
284 3d103742 Iustin Pop
    """Return the parsed value of an int argument.
285 3d103742 Iustin Pop

286 3d103742 Iustin Pop
    """
287 3fb8680a Michael Hanselmann
    val = self.queryargs.get(name, default)
288 3d103742 Iustin Pop
    if isinstance(val, list):
289 3d103742 Iustin Pop
      if val:
290 3d103742 Iustin Pop
        val = val[0]
291 3d103742 Iustin Pop
      else:
292 3fb8680a Michael Hanselmann
        val = default
293 3d103742 Iustin Pop
    try:
294 3d103742 Iustin Pop
      val = int(val)
295 7c4d6c7b Michael Hanselmann
    except (ValueError, TypeError):
296 6e99c5a0 Iustin Pop
      raise http.HttpBadRequest("Invalid value for the"
297 3d103742 Iustin Pop
                                " '%s' parameter" % (name,))
298 3d103742 Iustin Pop
    return val
299 3d103742 Iustin Pop
300 e5b7c4ca Iustin Pop
  def _checkStringVariable(self, name, default=None):
301 20ba96f5 Michele Tartara
    """Return the parsed value of a string argument.
302 e5b7c4ca Iustin Pop

303 e5b7c4ca Iustin Pop
    """
304 e5b7c4ca Iustin Pop
    val = self.queryargs.get(name, default)
305 e5b7c4ca Iustin Pop
    if isinstance(val, list):
306 e5b7c4ca Iustin Pop
      if val:
307 e5b7c4ca Iustin Pop
        val = val[0]
308 e5b7c4ca Iustin Pop
      else:
309 e5b7c4ca Iustin Pop
        val = default
310 e5b7c4ca Iustin Pop
    return val
311 3d103742 Iustin Pop
312 6e99c5a0 Iustin Pop
  def getBodyParameter(self, name, *args):
313 6e99c5a0 Iustin Pop
    """Check and return the value for a given parameter.
314 6e99c5a0 Iustin Pop

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

318 6e99c5a0 Iustin Pop
    @param name: the required parameter
319 6e99c5a0 Iustin Pop

320 6e99c5a0 Iustin Pop
    """
321 af6433c6 Michael Hanselmann
    if args:
322 1c54156d Luca Bigliardi
      return CheckParameter(self.request_body, name, default=args[0])
323 ab221ddf Michael Hanselmann
324 1c54156d Luca Bigliardi
    return CheckParameter(self.request_body, name)
325 6e99c5a0 Iustin Pop
326 3d103742 Iustin Pop
  def useLocking(self):
327 3d103742 Iustin Pop
    """Check if the request specifies locking.
328 3d103742 Iustin Pop

329 3d103742 Iustin Pop
    """
330 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("lock"))
331 3d103742 Iustin Pop
332 3d103742 Iustin Pop
  def useBulk(self):
333 3d103742 Iustin Pop
    """Check if the request specifies bulk querying.
334 3d103742 Iustin Pop

335 3d103742 Iustin Pop
    """
336 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("bulk"))
337 6f59b964 Iustin Pop
338 3427d34f Michael Hanselmann
  def useForce(self):
339 3427d34f Michael Hanselmann
    """Check if the request specifies a forced operation.
340 3427d34f Michael Hanselmann

341 3427d34f Michael Hanselmann
    """
342 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("force"))
343 3427d34f Michael Hanselmann
344 6f59b964 Iustin Pop
  def dryRun(self):
345 6f59b964 Iustin Pop
    """Check if the request specifies dry-run mode.
346 6f59b964 Iustin Pop

347 6f59b964 Iustin Pop
    """
348 b939de46 Michael Hanselmann
    return bool(self._checkIntVariable("dry-run"))
349 be1ddd09 Michael Hanselmann
350 303bc802 Iustin Pop
  def GetClient(self, query=False):
351 be1ddd09 Michael Hanselmann
    """Wrapper for L{luxi.Client} with HTTP-specific error handling.
352 be1ddd09 Michael Hanselmann

353 303bc802 Iustin Pop
    @param query: this signifies that the client will only be used for
354 303bc802 Iustin Pop
        queries; if the build-time parameter enable-split-queries is
355 303bc802 Iustin Pop
        enabled, then the client will be connected to the query socket
356 303bc802 Iustin Pop
        instead of the masterd socket
357 303bc802 Iustin Pop

358 be1ddd09 Michael Hanselmann
    """
359 303bc802 Iustin Pop
    if query and constants.ENABLE_SPLIT_QUERY:
360 4e55af74 Michael Hanselmann
      address = pathutils.QUERY_SOCKET
361 303bc802 Iustin Pop
    else:
362 303bc802 Iustin Pop
      address = None
363 be1ddd09 Michael Hanselmann
    # Could be a function, pylint: disable=R0201
364 be1ddd09 Michael Hanselmann
    try:
365 303bc802 Iustin Pop
      return self._client_cls(address=address)
366 be1ddd09 Michael Hanselmann
    except luxi.NoMasterError, err:
367 be1ddd09 Michael Hanselmann
      raise http.HttpBadGateway("Can't connect to master daemon: %s" % err)
368 be1ddd09 Michael Hanselmann
    except luxi.PermissionError:
369 be1ddd09 Michael Hanselmann
      raise http.HttpInternalServerError("Internal error: no permission to"
370 be1ddd09 Michael Hanselmann
                                         " connect to the master daemon")
371 be1ddd09 Michael Hanselmann
372 be1ddd09 Michael Hanselmann
  def SubmitJob(self, op, cl=None):
373 be1ddd09 Michael Hanselmann
    """Generic wrapper for submit job, for better http compatibility.
374 be1ddd09 Michael Hanselmann

375 be1ddd09 Michael Hanselmann
    @type op: list
376 be1ddd09 Michael Hanselmann
    @param op: the list of opcodes for the job
377 be1ddd09 Michael Hanselmann
    @type cl: None or luxi.Client
378 be1ddd09 Michael Hanselmann
    @param cl: optional luxi client to use
379 be1ddd09 Michael Hanselmann
    @rtype: string
380 be1ddd09 Michael Hanselmann
    @return: the job ID
381 be1ddd09 Michael Hanselmann

382 be1ddd09 Michael Hanselmann
    """
383 be1ddd09 Michael Hanselmann
    if cl is None:
384 be1ddd09 Michael Hanselmann
      cl = self.GetClient()
385 be1ddd09 Michael Hanselmann
    try:
386 be1ddd09 Michael Hanselmann
      return cl.SubmitJob(op)
387 be1ddd09 Michael Hanselmann
    except errors.JobQueueFull:
388 be1ddd09 Michael Hanselmann
      raise http.HttpServiceUnavailable("Job queue is full, needs archiving")
389 be1ddd09 Michael Hanselmann
    except errors.JobQueueDrainError:
390 be1ddd09 Michael Hanselmann
      raise http.HttpServiceUnavailable("Job queue is drained, cannot submit")
391 be1ddd09 Michael Hanselmann
    except luxi.NoMasterError, err:
392 be1ddd09 Michael Hanselmann
      raise http.HttpBadGateway("Master seems to be unreachable: %s" % err)
393 be1ddd09 Michael Hanselmann
    except luxi.PermissionError:
394 be1ddd09 Michael Hanselmann
      raise http.HttpInternalServerError("Internal error: no permission to"
395 be1ddd09 Michael Hanselmann
                                         " connect to the master daemon")
396 be1ddd09 Michael Hanselmann
    except luxi.TimeoutError, err:
397 be1ddd09 Michael Hanselmann
      raise http.HttpGatewayTimeout("Timeout while talking to the master"
398 be1ddd09 Michael Hanselmann
                                    " daemon: %s" % err)
399 c08fd0d6 Michael Hanselmann
400 c08fd0d6 Michael Hanselmann
401 b8ab1c7f Michael Hanselmann
def GetResourceOpcodes(cls):
402 b8ab1c7f Michael Hanselmann
  """Returns all opcodes used by a resource.
403 b8ab1c7f Michael Hanselmann

404 b8ab1c7f Michael Hanselmann
  """
405 b8ab1c7f Michael Hanselmann
  return frozenset(filter(None, (getattr(cls, op_attr, None)
406 c3a5176f Michael Hanselmann
                                 for (_, op_attr, _, _) in OPCODE_ATTRS)))
407 b8ab1c7f Michael Hanselmann
408 b8ab1c7f Michael Hanselmann
409 5224c9bf Michael Hanselmann
def GetHandlerAccess(handler, method):
410 5224c9bf Michael Hanselmann
  """Returns the access rights for a method on a handler.
411 5224c9bf Michael Hanselmann

412 5224c9bf Michael Hanselmann
  @type handler: L{ResourceBase}
413 5224c9bf Michael Hanselmann
  @type method: string
414 5224c9bf Michael Hanselmann
  @rtype: string or None
415 5224c9bf Michael Hanselmann

416 5224c9bf Michael Hanselmann
  """
417 5224c9bf Michael Hanselmann
  return getattr(handler, "%s_ACCESS" % method, None)
418 5224c9bf Michael Hanselmann
419 5224c9bf Michael Hanselmann
420 c08fd0d6 Michael Hanselmann
class _MetaOpcodeResource(type):
421 c08fd0d6 Michael Hanselmann
  """Meta class for RAPI resources.
422 c08fd0d6 Michael Hanselmann

423 c08fd0d6 Michael Hanselmann
  """
424 c08fd0d6 Michael Hanselmann
  def __call__(mcs, *args, **kwargs):
425 c08fd0d6 Michael Hanselmann
    """Instantiates class and patches it for use by the RAPI daemon.
426 c08fd0d6 Michael Hanselmann

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

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

463 c08fd0d6 Michael Hanselmann
  @cvar GET_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
464 c08fd0d6 Michael Hanselmann
    automatically generate a GET handler submitting the opcode
465 c08fd0d6 Michael Hanselmann
  @cvar GET_RENAME: Set this to rename parameters in the GET handler (see
466 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
467 c08fd0d6 Michael Hanselmann
  @ivar GetGetOpInput: Define this to override the default method for
468 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
469 c08fd0d6 Michael Hanselmann

470 c08fd0d6 Michael Hanselmann
  @cvar PUT_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
471 c08fd0d6 Michael Hanselmann
    automatically generate a PUT handler submitting the opcode
472 c08fd0d6 Michael Hanselmann
  @cvar PUT_RENAME: Set this to rename parameters in the PUT handler (see
473 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
474 c08fd0d6 Michael Hanselmann
  @ivar GetPutOpInput: Define this to override the default method for
475 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
476 c08fd0d6 Michael Hanselmann

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

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

491 c08fd0d6 Michael Hanselmann
  """
492 c08fd0d6 Michael Hanselmann
  __metaclass__ = _MetaOpcodeResource
493 c08fd0d6 Michael Hanselmann
494 c08fd0d6 Michael Hanselmann
  def _GetDefaultData(self):
495 c08fd0d6 Michael Hanselmann
    return (self.request_body, None)
496 c08fd0d6 Michael Hanselmann
497 c08fd0d6 Michael Hanselmann
  def _GenericHandler(self, opcode, rename, fn):
498 c08fd0d6 Michael Hanselmann
    (body, static) = fn()
499 c08fd0d6 Michael Hanselmann
    op = FillOpcode(opcode, body, static, rename=rename)
500 c08fd0d6 Michael Hanselmann
    return self.SubmitJob([op])