Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / baserlib.py @ 6c881c52

History | View | Annotate | Download (7.7 kB)

1 c2dca9af Oleksiy Mishchenko
#
2 c2dca9af Oleksiy Mishchenko
#
3 c2dca9af Oleksiy Mishchenko
4 c2dca9af Oleksiy Mishchenko
# Copyright (C) 2006, 2007, 2008 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 59b4eeef Iustin Pop
import logging
27 59b4eeef Iustin Pop
28 441e7cfd Oleksiy Mishchenko
from ganeti import luxi
29 b5b67ef9 Michael Hanselmann
from ganeti import rapi
30 3d103742 Iustin Pop
from ganeti import http
31 25e39bfa Iustin Pop
from ganeti import ssconf
32 25e39bfa Iustin Pop
from ganeti import constants
33 59b4eeef Iustin Pop
from ganeti import opcodes
34 59b4eeef Iustin Pop
from ganeti import errors
35 441e7cfd Oleksiy Mishchenko
36 c2dca9af Oleksiy Mishchenko
37 c2dca9af Oleksiy Mishchenko
def BuildUriList(ids, uri_format, uri_fields=("name", "uri")):
38 c2dca9af Oleksiy Mishchenko
  """Builds a URI list as used by index resources.
39 c2dca9af Oleksiy Mishchenko

40 c41eea6e Iustin Pop
  @param ids: list of ids as strings
41 c41eea6e Iustin Pop
  @param uri_format: format to be applied for URI
42 c41eea6e Iustin Pop
  @param uri_fields: optional parameter for field IDs
43 c2dca9af Oleksiy Mishchenko

44 c2dca9af Oleksiy Mishchenko
  """
45 c2dca9af Oleksiy Mishchenko
  (field_id, field_uri) = uri_fields
46 dca1764e Iustin Pop
47 c2dca9af Oleksiy Mishchenko
  def _MapId(m_id):
48 c2dca9af Oleksiy Mishchenko
    return { field_id: m_id, field_uri: uri_format % m_id, }
49 c2dca9af Oleksiy Mishchenko
50 c2dca9af Oleksiy Mishchenko
  # Make sure the result is sorted, makes it nicer to look at and simplifies
51 c2dca9af Oleksiy Mishchenko
  # unittests.
52 c2dca9af Oleksiy Mishchenko
  ids.sort()
53 c2dca9af Oleksiy Mishchenko
54 c2dca9af Oleksiy Mishchenko
  return map(_MapId, ids)
55 c2dca9af Oleksiy Mishchenko
56 c2dca9af Oleksiy Mishchenko
57 c2dca9af Oleksiy Mishchenko
def ExtractField(sequence, index):
58 c2dca9af Oleksiy Mishchenko
  """Creates a list containing one column out of a list of lists.
59 c2dca9af Oleksiy Mishchenko

60 c41eea6e Iustin Pop
  @param sequence: sequence of lists
61 c41eea6e Iustin Pop
  @param index: index of field
62 c2dca9af Oleksiy Mishchenko

63 c2dca9af Oleksiy Mishchenko
  """
64 c2dca9af Oleksiy Mishchenko
  return map(lambda item: item[index], sequence)
65 c2dca9af Oleksiy Mishchenko
66 c2dca9af Oleksiy Mishchenko
67 c2dca9af Oleksiy Mishchenko
def MapFields(names, data):
68 c2dca9af Oleksiy Mishchenko
  """Maps two lists into one dictionary.
69 c2dca9af Oleksiy Mishchenko

70 c41eea6e Iustin Pop
  Example::
71 c41eea6e Iustin Pop
      >>> MapFields(["a", "b"], ["foo", 123])
72 c41eea6e Iustin Pop
      {'a': 'foo', 'b': 123}
73 c2dca9af Oleksiy Mishchenko

74 c41eea6e Iustin Pop
  @param names: field names (list of strings)
75 c41eea6e Iustin Pop
  @param data: field data (list)
76 c2dca9af Oleksiy Mishchenko

77 c2dca9af Oleksiy Mishchenko
  """
78 c2dca9af Oleksiy Mishchenko
  if len(names) != len(data):
79 c2dca9af Oleksiy Mishchenko
    raise AttributeError("Names and data must have the same length")
80 dca1764e Iustin Pop
  return dict(zip(names, data))
81 c2dca9af Oleksiy Mishchenko
82 c2dca9af Oleksiy Mishchenko
83 64246438 Iustin Pop
def _Tags_GET(kind, name):
84 c2dca9af Oleksiy Mishchenko
  """Helper function to retrieve tags.
85 c2dca9af Oleksiy Mishchenko

86 c2dca9af Oleksiy Mishchenko
  """
87 25e39bfa Iustin Pop
  if kind == constants.TAG_INSTANCE or kind == constants.TAG_NODE:
88 25e39bfa Iustin Pop
    if not name:
89 59b4eeef Iustin Pop
      raise http.HttpBadRequest("Missing name on tag request")
90 59b4eeef Iustin Pop
    cl = GetClient()
91 25e39bfa Iustin Pop
    if kind == constants.TAG_INSTANCE:
92 25e39bfa Iustin Pop
      fn = cl.QueryInstances
93 25e39bfa Iustin Pop
    else:
94 25e39bfa Iustin Pop
      fn = cl.QueryNodes
95 25e39bfa Iustin Pop
    result = fn(names=[name], fields=["tags"], use_locking=False)
96 25e39bfa Iustin Pop
    if not result or not result[0]:
97 25e39bfa Iustin Pop
      raise http.HttpBadGateway("Invalid response from tag query")
98 25e39bfa Iustin Pop
    tags = result[0][0]
99 25e39bfa Iustin Pop
  elif kind == constants.TAG_CLUSTER:
100 25e39bfa Iustin Pop
    ssc = ssconf.SimpleStore()
101 25e39bfa Iustin Pop
    tags = ssc.GetClusterTags()
102 25e39bfa Iustin Pop
103 c2dca9af Oleksiy Mishchenko
  return list(tags)
104 c2dca9af Oleksiy Mishchenko
105 c2dca9af Oleksiy Mishchenko
106 64246438 Iustin Pop
def _Tags_PUT(kind, tags, name, dry_run):
107 441e7cfd Oleksiy Mishchenko
  """Helper function to set tags.
108 441e7cfd Oleksiy Mishchenko

109 441e7cfd Oleksiy Mishchenko
  """
110 64246438 Iustin Pop
  return SubmitJob([opcodes.OpAddTags(kind=kind, name=name,
111 64246438 Iustin Pop
                                      tags=tags, dry_run=dry_run)])
112 441e7cfd Oleksiy Mishchenko
113 441e7cfd Oleksiy Mishchenko
114 64246438 Iustin Pop
def _Tags_DELETE(kind, tags, name, dry_run):
115 15fd9fd5 Oleksiy Mishchenko
  """Helper function to delete tags.
116 15fd9fd5 Oleksiy Mishchenko

117 15fd9fd5 Oleksiy Mishchenko
  """
118 64246438 Iustin Pop
  return SubmitJob([opcodes.OpDelTags(kind=kind, name=name,
119 64246438 Iustin Pop
                                      tags=tags, dry_run=dry_run)])
120 15fd9fd5 Oleksiy Mishchenko
121 15fd9fd5 Oleksiy Mishchenko
122 51ee2f49 Oleksiy Mishchenko
def MapBulkFields(itemslist, fields):
123 51ee2f49 Oleksiy Mishchenko
  """Map value to field name in to one dictionary.
124 51ee2f49 Oleksiy Mishchenko

125 c41eea6e Iustin Pop
  @param itemslist: a list of items values
126 c41eea6e Iustin Pop
  @param fields: a list of items names
127 c41eea6e Iustin Pop

128 c41eea6e Iustin Pop
  @return: a list of mapped dictionaries
129 51ee2f49 Oleksiy Mishchenko

130 51ee2f49 Oleksiy Mishchenko
  """
131 51ee2f49 Oleksiy Mishchenko
  items_details = []
132 51ee2f49 Oleksiy Mishchenko
  for item in itemslist:
133 51ee2f49 Oleksiy Mishchenko
    mapped = MapFields(fields, item)
134 51ee2f49 Oleksiy Mishchenko
    items_details.append(mapped)
135 51ee2f49 Oleksiy Mishchenko
  return items_details
136 51ee2f49 Oleksiy Mishchenko
137 51ee2f49 Oleksiy Mishchenko
138 d50b3059 Oleksiy Mishchenko
def MakeParamsDict(opts, params):
139 c41eea6e Iustin Pop
  """Makes params dictionary out of a option set.
140 d50b3059 Oleksiy Mishchenko

141 d50b3059 Oleksiy Mishchenko
  This function returns a dictionary needed for hv or be parameters. But only
142 d50b3059 Oleksiy Mishchenko
  those fields which provided in the option set. Takes parameters frozensets
143 d50b3059 Oleksiy Mishchenko
  from constants.
144 d50b3059 Oleksiy Mishchenko

145 d50b3059 Oleksiy Mishchenko
  @type opts: dict
146 d50b3059 Oleksiy Mishchenko
  @param opts: selected options
147 d50b3059 Oleksiy Mishchenko
  @type params: frozenset
148 d50b3059 Oleksiy Mishchenko
  @param params: subset of options
149 d50b3059 Oleksiy Mishchenko
  @rtype: dict
150 d50b3059 Oleksiy Mishchenko
  @return: dictionary of options, filtered by given subset.
151 d50b3059 Oleksiy Mishchenko

152 d50b3059 Oleksiy Mishchenko
  """
153 d50b3059 Oleksiy Mishchenko
  result = {}
154 d50b3059 Oleksiy Mishchenko
155 d50b3059 Oleksiy Mishchenko
  for p in params:
156 d50b3059 Oleksiy Mishchenko
    try:
157 d50b3059 Oleksiy Mishchenko
      value = opts[p]
158 d50b3059 Oleksiy Mishchenko
    except KeyError:
159 d50b3059 Oleksiy Mishchenko
      continue
160 d50b3059 Oleksiy Mishchenko
    result[p] = value
161 d50b3059 Oleksiy Mishchenko
162 d50b3059 Oleksiy Mishchenko
  return result
163 d50b3059 Oleksiy Mishchenko
164 d50b3059 Oleksiy Mishchenko
165 59b4eeef Iustin Pop
def SubmitJob(op, cl=None):
166 59b4eeef Iustin Pop
  """Generic wrapper for submit job, for better http compatibility.
167 59b4eeef Iustin Pop

168 59b4eeef Iustin Pop
  @type op: list
169 59b4eeef Iustin Pop
  @param op: the list of opcodes for the job
170 59b4eeef Iustin Pop
  @type cl: None or luxi.Client
171 59b4eeef Iustin Pop
  @param cl: optional luxi client to use
172 59b4eeef Iustin Pop
  @rtype: string
173 59b4eeef Iustin Pop
  @return: the job ID
174 59b4eeef Iustin Pop

175 59b4eeef Iustin Pop
  """
176 59b4eeef Iustin Pop
  try:
177 59b4eeef Iustin Pop
    if cl is None:
178 59b4eeef Iustin Pop
      cl = GetClient()
179 59b4eeef Iustin Pop
    return cl.SubmitJob(op)
180 59b4eeef Iustin Pop
  except errors.JobQueueFull:
181 59b4eeef Iustin Pop
    raise http.HttpServiceUnavailable("Job queue is full, needs archiving")
182 59b4eeef Iustin Pop
  except errors.JobQueueDrainError:
183 59b4eeef Iustin Pop
    raise http.HttpServiceUnavailable("Job queue is drained, cannot submit")
184 59b4eeef Iustin Pop
  except luxi.NoMasterError, err:
185 59b4eeef Iustin Pop
    raise http.HttpBadGateway("Master seems to unreachable: %s" % str(err))
186 59b4eeef Iustin Pop
  except luxi.TimeoutError, err:
187 59b4eeef Iustin Pop
    raise http.HttpGatewayTimeout("Timeout while talking to the master"
188 59b4eeef Iustin Pop
                                  " daemon. Error: %s" % str(err))
189 59b4eeef Iustin Pop
190 59b4eeef Iustin Pop
def GetClient():
191 59b4eeef Iustin Pop
  """Geric wrapper for luxi.Client(), for better http compatiblity.
192 59b4eeef Iustin Pop

193 59b4eeef Iustin Pop
  """
194 59b4eeef Iustin Pop
  try:
195 59b4eeef Iustin Pop
    return luxi.Client()
196 59b4eeef Iustin Pop
  except luxi.NoMasterError, err:
197 59b4eeef Iustin Pop
    raise http.HttpBadGateway("Master seems to unreachable: %s" % str(err))
198 59b4eeef Iustin Pop
199 59b4eeef Iustin Pop
200 59b4eeef Iustin Pop
def FeedbackFn(ts, log_type, log_msg):
201 59b4eeef Iustin Pop
  """Feedback logging function for http case.
202 59b4eeef Iustin Pop

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

206 59b4eeef Iustin Pop
  """
207 59b4eeef Iustin Pop
  logging.info("%s: %s", log_type, log_msg)
208 59b4eeef Iustin Pop
209 59b4eeef Iustin Pop
210 c2dca9af Oleksiy Mishchenko
class R_Generic(object):
211 c2dca9af Oleksiy Mishchenko
  """Generic class for resources.
212 c2dca9af Oleksiy Mishchenko

213 c2dca9af Oleksiy Mishchenko
  """
214 b5b67ef9 Michael Hanselmann
  # Default permission requirements
215 b5b67ef9 Michael Hanselmann
  GET_ACCESS = []
216 b5b67ef9 Michael Hanselmann
  PUT_ACCESS = [rapi.RAPI_ACCESS_WRITE]
217 b5b67ef9 Michael Hanselmann
  POST_ACCESS = [rapi.RAPI_ACCESS_WRITE]
218 b5b67ef9 Michael Hanselmann
  DELETE_ACCESS = [rapi.RAPI_ACCESS_WRITE]
219 b5b67ef9 Michael Hanselmann
220 7a8f64da Oleksiy Mishchenko
  def __init__(self, items, queryargs, req):
221 c2dca9af Oleksiy Mishchenko
    """Generic resource constructor.
222 c2dca9af Oleksiy Mishchenko

223 c41eea6e Iustin Pop
    @param items: a list with variables encoded in the URL
224 c41eea6e Iustin Pop
    @param queryargs: a dictionary with additional options from URL
225 c2dca9af Oleksiy Mishchenko

226 c2dca9af Oleksiy Mishchenko
    """
227 c2dca9af Oleksiy Mishchenko
    self.items = items
228 c2dca9af Oleksiy Mishchenko
    self.queryargs = queryargs
229 7a8f64da Oleksiy Mishchenko
    self.req = req
230 713faea6 Oleksiy Mishchenko
    self.sn = None
231 713faea6 Oleksiy Mishchenko
232 713faea6 Oleksiy Mishchenko
  def getSerialNumber(self):
233 713faea6 Oleksiy Mishchenko
    """Get Serial Number.
234 713faea6 Oleksiy Mishchenko

235 713faea6 Oleksiy Mishchenko
    """
236 713faea6 Oleksiy Mishchenko
    return self.sn
237 3d103742 Iustin Pop
238 3fb8680a Michael Hanselmann
  def _checkIntVariable(self, name, default=0):
239 3d103742 Iustin Pop
    """Return the parsed value of an int argument.
240 3d103742 Iustin Pop

241 3d103742 Iustin Pop
    """
242 3fb8680a Michael Hanselmann
    val = self.queryargs.get(name, default)
243 3d103742 Iustin Pop
    if isinstance(val, list):
244 3d103742 Iustin Pop
      if val:
245 3d103742 Iustin Pop
        val = val[0]
246 3d103742 Iustin Pop
      else:
247 3fb8680a Michael Hanselmann
        val = default
248 3d103742 Iustin Pop
    try:
249 3d103742 Iustin Pop
      val = int(val)
250 7c4d6c7b Michael Hanselmann
    except (ValueError, TypeError):
251 6e99c5a0 Iustin Pop
      raise http.HttpBadRequest("Invalid value for the"
252 3d103742 Iustin Pop
                                " '%s' parameter" % (name,))
253 3d103742 Iustin Pop
    return val
254 3d103742 Iustin Pop
255 e5b7c4ca Iustin Pop
  def _checkStringVariable(self, name, default=None):
256 e5b7c4ca Iustin Pop
    """Return the parsed value of an int argument.
257 e5b7c4ca Iustin Pop

258 e5b7c4ca Iustin Pop
    """
259 e5b7c4ca Iustin Pop
    val = self.queryargs.get(name, default)
260 e5b7c4ca Iustin Pop
    if isinstance(val, list):
261 e5b7c4ca Iustin Pop
      if val:
262 e5b7c4ca Iustin Pop
        val = val[0]
263 e5b7c4ca Iustin Pop
      else:
264 e5b7c4ca Iustin Pop
        val = default
265 e5b7c4ca Iustin Pop
    return val
266 3d103742 Iustin Pop
267 6e99c5a0 Iustin Pop
  def getBodyParameter(self, name, *args):
268 6e99c5a0 Iustin Pop
    """Check and return the value for a given parameter.
269 6e99c5a0 Iustin Pop

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

273 6e99c5a0 Iustin Pop
    @param name: the required parameter
274 6e99c5a0 Iustin Pop

275 6e99c5a0 Iustin Pop
    """
276 6e99c5a0 Iustin Pop
    if name in self.req.request_body:
277 6e99c5a0 Iustin Pop
      return self.req.request_body[name]
278 6e99c5a0 Iustin Pop
    elif args:
279 6e99c5a0 Iustin Pop
      return args[0]
280 6e99c5a0 Iustin Pop
    else:
281 6e99c5a0 Iustin Pop
      raise http.HttpBadRequest("Required parameter '%s' is missing" %
282 6e99c5a0 Iustin Pop
                                name)
283 6e99c5a0 Iustin Pop
284 3d103742 Iustin Pop
  def useLocking(self):
285 3d103742 Iustin Pop
    """Check if the request specifies locking.
286 3d103742 Iustin Pop

287 3d103742 Iustin Pop
    """
288 3d103742 Iustin Pop
    return self._checkIntVariable('lock')
289 3d103742 Iustin Pop
290 3d103742 Iustin Pop
  def useBulk(self):
291 3d103742 Iustin Pop
    """Check if the request specifies bulk querying.
292 3d103742 Iustin Pop

293 3d103742 Iustin Pop
    """
294 3d103742 Iustin Pop
    return self._checkIntVariable('bulk')
295 6f59b964 Iustin Pop
296 3427d34f Michael Hanselmann
  def useForce(self):
297 3427d34f Michael Hanselmann
    """Check if the request specifies a forced operation.
298 3427d34f Michael Hanselmann

299 3427d34f Michael Hanselmann
    """
300 3427d34f Michael Hanselmann
    return self._checkIntVariable('force')
301 3427d34f Michael Hanselmann
302 6f59b964 Iustin Pop
  def dryRun(self):
303 6f59b964 Iustin Pop
    """Check if the request specifies dry-run mode.
304 6f59b964 Iustin Pop

305 6f59b964 Iustin Pop
    """
306 6f59b964 Iustin Pop
    return self._checkIntVariable('dry-run')