Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (16.2 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 9ba38706 Petr Pudlak
import ganeti.rpc.errors as rpcerr
34 b5b67ef9 Michael Hanselmann
from ganeti import rapi
35 3d103742 Iustin Pop
from ganeti import http
36 59b4eeef Iustin Pop
from ganeti import errors
37 c08fd0d6 Michael Hanselmann
from ganeti import compat
38 303bc802 Iustin Pop
from ganeti import constants
39 4e55af74 Michael Hanselmann
from ganeti import pathutils
40 9b4cb29d Michele Tartara
from ganeti import utils
41 441e7cfd Oleksiy Mishchenko
42 c2dca9af Oleksiy Mishchenko
43 af6433c6 Michael Hanselmann
# Dummy value to detect unchanged parameters
44 af6433c6 Michael Hanselmann
_DEFAULT = object()
45 af6433c6 Michael Hanselmann
46 c08fd0d6 Michael Hanselmann
#: Supported HTTP methods
47 b8028dcf Michael Hanselmann
_SUPPORTED_METHODS = compat.UniqueFrozenset([
48 c08fd0d6 Michael Hanselmann
  http.HTTP_DELETE,
49 c08fd0d6 Michael Hanselmann
  http.HTTP_GET,
50 c08fd0d6 Michael Hanselmann
  http.HTTP_POST,
51 c08fd0d6 Michael Hanselmann
  http.HTTP_PUT,
52 c08fd0d6 Michael Hanselmann
  ])
53 c08fd0d6 Michael Hanselmann
54 af6433c6 Michael Hanselmann
55 b8ab1c7f Michael Hanselmann
def _BuildOpcodeAttributes():
56 b8ab1c7f Michael Hanselmann
  """Builds list of attributes used for per-handler opcodes.
57 b8ab1c7f Michael Hanselmann

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

460 c08fd0d6 Michael Hanselmann
  Instances of this class automatically gain handler functions through
461 c08fd0d6 Michael Hanselmann
  L{_MetaOpcodeResource} for any method for which a C{$METHOD$_OPCODE} variable
462 c08fd0d6 Michael Hanselmann
  is defined at class level. Subclasses can define a C{Get$Method$OpInput}
463 c08fd0d6 Michael Hanselmann
  method to do their own opcode input processing (e.g. for static values). The
464 c08fd0d6 Michael Hanselmann
  C{$METHOD$_RENAME} variable defines which values are renamed (see
465 3bd0f3d8 Iustin Pop
  L{baserlib.FillOpcode}).
466 508b9539 Dimitris Aragiorgis
  Still default behavior cannot be totally overriden. There are opcode params
467 508b9539 Dimitris Aragiorgis
  that are available to all opcodes, e.g. "depends". In case those params
468 508b9539 Dimitris Aragiorgis
  (currently only "depends") are found in the original request's body, they are
469 508b9539 Dimitris Aragiorgis
  added to the dictionary of parsed parameters and eventually passed to the
470 508b9539 Dimitris Aragiorgis
  opcode. If the parsed body is not represented as a dictionary object, the
471 508b9539 Dimitris Aragiorgis
  values are not added.
472 c08fd0d6 Michael Hanselmann

473 c08fd0d6 Michael Hanselmann
  @cvar GET_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
474 c08fd0d6 Michael Hanselmann
    automatically generate a GET handler submitting the opcode
475 c08fd0d6 Michael Hanselmann
  @cvar GET_RENAME: Set this to rename parameters in the GET handler (see
476 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
477 c08fd0d6 Michael Hanselmann
  @ivar GetGetOpInput: 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 PUT_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
481 c08fd0d6 Michael Hanselmann
    automatically generate a PUT handler submitting the opcode
482 c08fd0d6 Michael Hanselmann
  @cvar PUT_RENAME: Set this to rename parameters in the PUT handler (see
483 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
484 c08fd0d6 Michael Hanselmann
  @ivar GetPutOpInput: 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 POST_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
488 c08fd0d6 Michael Hanselmann
    automatically generate a POST handler submitting the opcode
489 11eb5cd0 Michael Hanselmann
  @cvar POST_RENAME: Set this to rename parameters in the POST handler (see
490 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
491 c08fd0d6 Michael Hanselmann
  @ivar GetPostOpInput: 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
  @cvar DELETE_OPCODE: Set this to a class derived from L{opcodes.OpCode} to
495 11eb5cd0 Michael Hanselmann
    automatically generate a DELETE handler submitting the opcode
496 c08fd0d6 Michael Hanselmann
  @cvar DELETE_RENAME: Set this to rename parameters in the DELETE handler (see
497 3bd0f3d8 Iustin Pop
    L{baserlib.FillOpcode})
498 c08fd0d6 Michael Hanselmann
  @ivar GetDeleteOpInput: Define this to override the default method for
499 c08fd0d6 Michael Hanselmann
    getting opcode parameters (see L{baserlib.OpcodeResource._GetDefaultData})
500 c08fd0d6 Michael Hanselmann

501 c08fd0d6 Michael Hanselmann
  """
502 c08fd0d6 Michael Hanselmann
  __metaclass__ = _MetaOpcodeResource
503 c08fd0d6 Michael Hanselmann
504 c08fd0d6 Michael Hanselmann
  def _GetDefaultData(self):
505 c08fd0d6 Michael Hanselmann
    return (self.request_body, None)
506 c08fd0d6 Michael Hanselmann
507 9b4cb29d Michele Tartara
  def _GetRapiOpName(self):
508 9b4cb29d Michele Tartara
    """Extracts the name of the RAPI operation from the class name
509 9b4cb29d Michele Tartara

510 9b4cb29d Michele Tartara
    """
511 9b4cb29d Michele Tartara
    if self.__class__.__name__.startswith("R_2_"):
512 9b4cb29d Michele Tartara
      return self.__class__.__name__[4:]
513 9b4cb29d Michele Tartara
    return self.__class__.__name__
514 9b4cb29d Michele Tartara
515 da3faf9d Michele Tartara
  def _GetCommonStatic(self):
516 da3faf9d Michele Tartara
    """Return the static parameters common to all the RAPI calls
517 da3faf9d Michele Tartara

518 9b4cb29d Michele Tartara
    The reason is a parameter present in all the RAPI calls, and the reason
519 9b4cb29d Michele Tartara
    trail has to be build for all of them, so the parameter is read here and
520 9b4cb29d Michele Tartara
    used to build the reason trail, that is the actual parameter passed
521 9b4cb29d Michele Tartara
    forward.
522 9b4cb29d Michele Tartara

523 da3faf9d Michele Tartara
    """
524 9b4cb29d Michele Tartara
    trail = []
525 9b4cb29d Michele Tartara
    usr_reason = self._checkStringVariable("reason", default=None)
526 9b4cb29d Michele Tartara
    if usr_reason:
527 9b4cb29d Michele Tartara
      trail.append((constants.OPCODE_REASON_SRC_USER,
528 9b4cb29d Michele Tartara
                    usr_reason,
529 9b4cb29d Michele Tartara
                    utils.EpochNano()))
530 9b4cb29d Michele Tartara
    reason_src = "%s:%s" % (constants.OPCODE_REASON_SRC_RLIB2,
531 9b4cb29d Michele Tartara
                            self._GetRapiOpName())
532 9b4cb29d Michele Tartara
    trail.append((reason_src, "", utils.EpochNano()))
533 9b4cb29d Michele Tartara
    common_static = {
534 9b4cb29d Michele Tartara
      "reason": trail,
535 9b4cb29d Michele Tartara
      }
536 da3faf9d Michele Tartara
    return common_static
537 da3faf9d Michele Tartara
538 508b9539 Dimitris Aragiorgis
  def _GetDepends(self):
539 508b9539 Dimitris Aragiorgis
    ret = {}
540 508b9539 Dimitris Aragiorgis
    if isinstance(self.request_body, dict):
541 508b9539 Dimitris Aragiorgis
      depends = self.getBodyParameter("depends", None)
542 508b9539 Dimitris Aragiorgis
      if depends:
543 508b9539 Dimitris Aragiorgis
        ret.update({"depends": depends})
544 508b9539 Dimitris Aragiorgis
    return ret
545 508b9539 Dimitris Aragiorgis
546 c08fd0d6 Michael Hanselmann
  def _GenericHandler(self, opcode, rename, fn):
547 da3faf9d Michele Tartara
    (body, specific_static) = fn()
548 508b9539 Dimitris Aragiorgis
    if isinstance(body, dict):
549 508b9539 Dimitris Aragiorgis
      body.update(self._GetDepends())
550 da3faf9d Michele Tartara
    static = self._GetCommonStatic()
551 da3faf9d Michele Tartara
    if specific_static:
552 da3faf9d Michele Tartara
      static.update(specific_static)
553 c08fd0d6 Michael Hanselmann
    op = FillOpcode(opcode, body, static, rename=rename)
554 c08fd0d6 Michael Hanselmann
    return self.SubmitJob([op])