Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / baserlib.py @ 364c350f

History | View | Annotate | Download (15.6 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 9b4cb29d Michele Tartara
from ganeti import utils
40 441e7cfd Oleksiy Mishchenko
41 c2dca9af Oleksiy Mishchenko
42 af6433c6 Michael Hanselmann
# Dummy value to detect unchanged parameters
43 af6433c6 Michael Hanselmann
_DEFAULT = object()
44 af6433c6 Michael Hanselmann
45 c08fd0d6 Michael Hanselmann
#: Supported HTTP methods
46 b8028dcf Michael Hanselmann
_SUPPORTED_METHODS = compat.UniqueFrozenset([
47 c08fd0d6 Michael Hanselmann
  http.HTTP_DELETE,
48 c08fd0d6 Michael Hanselmann
  http.HTTP_GET,
49 c08fd0d6 Michael Hanselmann
  http.HTTP_POST,
50 c08fd0d6 Michael Hanselmann
  http.HTTP_PUT,
51 c08fd0d6 Michael Hanselmann
  ])
52 c08fd0d6 Michael Hanselmann
53 af6433c6 Michael Hanselmann
54 b8ab1c7f Michael Hanselmann
def _BuildOpcodeAttributes():
55 b8ab1c7f Michael Hanselmann
  """Builds list of attributes used for per-handler opcodes.
56 b8ab1c7f Michael Hanselmann

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

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

73 c2dca9af Oleksiy Mishchenko
  """
74 c2dca9af Oleksiy Mishchenko
  (field_id, field_uri) = uri_fields
75 dca1764e Iustin Pop
76 c2dca9af Oleksiy Mishchenko
  def _MapId(m_id):
77 e687ec01 Michael Hanselmann
    return {
78 e687ec01 Michael Hanselmann
      field_id: m_id,
79 e687ec01 Michael Hanselmann
      field_uri: uri_format % m_id,
80 e687ec01 Michael Hanselmann
      }
81 c2dca9af Oleksiy Mishchenko
82 c2dca9af Oleksiy Mishchenko
  # Make sure the result is sorted, makes it nicer to look at and simplifies
83 c2dca9af Oleksiy Mishchenko
  # unittests.
84 c2dca9af Oleksiy Mishchenko
  ids.sort()
85 c2dca9af Oleksiy Mishchenko
86 c2dca9af Oleksiy Mishchenko
  return map(_MapId, ids)
87 c2dca9af Oleksiy Mishchenko
88 c2dca9af Oleksiy Mishchenko
89 c2dca9af Oleksiy Mishchenko
def MapFields(names, data):
90 c2dca9af Oleksiy Mishchenko
  """Maps two lists into one dictionary.
91 c2dca9af Oleksiy Mishchenko

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

266 c2dca9af Oleksiy Mishchenko
    """
267 61f8fda4 Michele Tartara
    assert isinstance(queryargs, dict)
268 61f8fda4 Michele Tartara
269 c2dca9af Oleksiy Mishchenko
    self.items = items
270 c2dca9af Oleksiy Mishchenko
    self.queryargs = queryargs
271 627ad739 Michael Hanselmann
    self._req = req
272 da04c447 Michael Hanselmann
273 da04c447 Michael Hanselmann
    if _client_cls is None:
274 da04c447 Michael Hanselmann
      _client_cls = luxi.Client
275 da04c447 Michael Hanselmann
276 ab85ce39 Michael Hanselmann
    self._client_cls = _client_cls
277 627ad739 Michael Hanselmann
278 2feecf12 Michael Hanselmann
  def _GetRequestBody(self):
279 2feecf12 Michael Hanselmann
    """Returns the body data.
280 2feecf12 Michael Hanselmann

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

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

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

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

321 6e99c5a0 Iustin Pop
    @param name: the required parameter
322 6e99c5a0 Iustin Pop

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

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

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

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

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

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

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

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

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

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

415 5224c9bf Michael Hanselmann
  @type handler: L{ResourceBase}
416 5224c9bf Michael Hanselmann
  @type method: string
417 5224c9bf Michael Hanselmann
  @rtype: string or None
418 5224c9bf Michael Hanselmann

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

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

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

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

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

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

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

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

494 c08fd0d6 Michael Hanselmann
  """
495 c08fd0d6 Michael Hanselmann
  __metaclass__ = _MetaOpcodeResource
496 c08fd0d6 Michael Hanselmann
497 c08fd0d6 Michael Hanselmann
  def _GetDefaultData(self):
498 c08fd0d6 Michael Hanselmann
    return (self.request_body, None)
499 c08fd0d6 Michael Hanselmann
500 9b4cb29d Michele Tartara
  def _GetRapiOpName(self):
501 9b4cb29d Michele Tartara
    """Extracts the name of the RAPI operation from the class name
502 9b4cb29d Michele Tartara

503 9b4cb29d Michele Tartara
    """
504 9b4cb29d Michele Tartara
    if self.__class__.__name__.startswith("R_2_"):
505 9b4cb29d Michele Tartara
      return self.__class__.__name__[4:]
506 9b4cb29d Michele Tartara
    return self.__class__.__name__
507 9b4cb29d Michele Tartara
508 da3faf9d Michele Tartara
  def _GetCommonStatic(self):
509 da3faf9d Michele Tartara
    """Return the static parameters common to all the RAPI calls
510 da3faf9d Michele Tartara

511 9b4cb29d Michele Tartara
    The reason is a parameter present in all the RAPI calls, and the reason
512 9b4cb29d Michele Tartara
    trail has to be build for all of them, so the parameter is read here and
513 9b4cb29d Michele Tartara
    used to build the reason trail, that is the actual parameter passed
514 9b4cb29d Michele Tartara
    forward.
515 9b4cb29d Michele Tartara

516 da3faf9d Michele Tartara
    """
517 9b4cb29d Michele Tartara
    trail = []
518 9b4cb29d Michele Tartara
    usr_reason = self._checkStringVariable("reason", default=None)
519 9b4cb29d Michele Tartara
    if usr_reason:
520 9b4cb29d Michele Tartara
      trail.append((constants.OPCODE_REASON_SRC_USER,
521 9b4cb29d Michele Tartara
                    usr_reason,
522 9b4cb29d Michele Tartara
                    utils.EpochNano()))
523 9b4cb29d Michele Tartara
    reason_src = "%s:%s" % (constants.OPCODE_REASON_SRC_RLIB2,
524 9b4cb29d Michele Tartara
                            self._GetRapiOpName())
525 9b4cb29d Michele Tartara
    trail.append((reason_src, "", utils.EpochNano()))
526 9b4cb29d Michele Tartara
    common_static = {
527 9b4cb29d Michele Tartara
      "reason": trail,
528 9b4cb29d Michele Tartara
      }
529 da3faf9d Michele Tartara
    return common_static
530 da3faf9d Michele Tartara
531 c08fd0d6 Michael Hanselmann
  def _GenericHandler(self, opcode, rename, fn):
532 da3faf9d Michele Tartara
    (body, specific_static) = fn()
533 da3faf9d Michele Tartara
    static = self._GetCommonStatic()
534 da3faf9d Michele Tartara
    if specific_static:
535 da3faf9d Michele Tartara
      static.update(specific_static)
536 c08fd0d6 Michael Hanselmann
    op = FillOpcode(opcode, body, static, rename=rename)
537 c08fd0d6 Michael Hanselmann
    return self.SubmitJob([op])