Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / baserlib.py @ e5b7c4ca

History | View | Annotate | Download (7.3 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
"""Remote API base resources library.
23

24
"""
25

    
26
import logging
27

    
28
import ganeti.cli
29

    
30
from ganeti import luxi
31
from ganeti import rapi
32
from ganeti import http
33
from ganeti import ssconf
34
from ganeti import constants
35
from ganeti import opcodes
36
from ganeti import errors
37

    
38

    
39
def BuildUriList(ids, uri_format, uri_fields=("name", "uri")):
40
  """Builds a URI list as used by index resources.
41

42
  @param ids: list of ids as strings
43
  @param uri_format: format to be applied for URI
44
  @param uri_fields: optional parameter for field IDs
45

46
  """
47
  (field_id, field_uri) = uri_fields
48

    
49
  def _MapId(m_id):
50
    return { field_id: m_id, field_uri: uri_format % m_id, }
51

    
52
  # Make sure the result is sorted, makes it nicer to look at and simplifies
53
  # unittests.
54
  ids.sort()
55

    
56
  return map(_MapId, ids)
57

    
58

    
59
def ExtractField(sequence, index):
60
  """Creates a list containing one column out of a list of lists.
61

62
  @param sequence: sequence of lists
63
  @param index: index of field
64

65
  """
66
  return map(lambda item: item[index], sequence)
67

    
68

    
69
def MapFields(names, data):
70
  """Maps two lists into one dictionary.
71

72
  Example::
73
      >>> MapFields(["a", "b"], ["foo", 123])
74
      {'a': 'foo', 'b': 123}
75

76
  @param names: field names (list of strings)
77
  @param data: field data (list)
78

79
  """
80
  if len(names) != len(data):
81
    raise AttributeError("Names and data must have the same length")
82
  return dict(zip(names, data))
83

    
84

    
85
def _Tags_GET(kind, name=""):
86
  """Helper function to retrieve tags.
87

88
  """
89
  if kind == constants.TAG_INSTANCE or kind == constants.TAG_NODE:
90
    if not name:
91
      raise http.HttpBadRequest("Missing name on tag request")
92
    cl = GetClient()
93
    if kind == constants.TAG_INSTANCE:
94
      fn = cl.QueryInstances
95
    else:
96
      fn = cl.QueryNodes
97
    result = fn(names=[name], fields=["tags"], use_locking=False)
98
    if not result or not result[0]:
99
      raise http.HttpBadGateway("Invalid response from tag query")
100
    tags = result[0][0]
101
  elif kind == constants.TAG_CLUSTER:
102
    ssc = ssconf.SimpleStore()
103
    tags = ssc.GetClusterTags()
104

    
105
  return list(tags)
106

    
107

    
108
def _Tags_PUT(kind, tags, name=""):
109
  """Helper function to set tags.
110

111
  """
112
  return SubmitJob([opcodes.OpAddTags(kind=kind, name=name, tags=tags)])
113

    
114

    
115
def _Tags_DELETE(kind, tags, name=""):
116
  """Helper function to delete tags.
117

118
  """
119
  return SubmitJob([opcodes.OpDelTags(kind=kind, name=name, tags=tags)])
120

    
121

    
122
def MapBulkFields(itemslist, fields):
123
  """Map value to field name in to one dictionary.
124

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

128
  @return: a list of mapped dictionaries
129

130
  """
131
  items_details = []
132
  for item in itemslist:
133
    mapped = MapFields(fields, item)
134
    items_details.append(mapped)
135
  return items_details
136

    
137

    
138
def MakeParamsDict(opts, params):
139
  """Makes params dictionary out of a option set.
140

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

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

152
  """
153
  result = {}
154

    
155
  for p in params:
156
    try:
157
      value = opts[p]
158
    except KeyError:
159
      continue
160
    result[p] = value
161

    
162
  return result
163

    
164

    
165
def SubmitJob(op, cl=None):
166
  """Generic wrapper for submit job, for better http compatibility.
167

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

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

    
190
def GetClient():
191
  """Geric wrapper for luxi.Client(), for better http compatiblity.
192

193
  """
194
  try:
195
    return luxi.Client()
196
  except luxi.NoMasterError, err:
197
    raise http.HttpBadGateway("Master seems to unreachable: %s" % str(err))
198

    
199

    
200
def FeedbackFn(ts, log_type, log_msg):
201
  """Feedback logging function for http case.
202

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

206
  """
207
  logging.info("%s: %s", log_type, log_msg)
208

    
209

    
210
class R_Generic(object):
211
  """Generic class for resources.
212

213
  """
214
  # Default permission requirements
215
  GET_ACCESS = []
216
  PUT_ACCESS = [rapi.RAPI_ACCESS_WRITE]
217
  POST_ACCESS = [rapi.RAPI_ACCESS_WRITE]
218
  DELETE_ACCESS = [rapi.RAPI_ACCESS_WRITE]
219

    
220
  def __init__(self, items, queryargs, req):
221
    """Generic resource constructor.
222

223
    @param items: a list with variables encoded in the URL
224
    @param queryargs: a dictionary with additional options from URL
225

226
    """
227
    self.items = items
228
    self.queryargs = queryargs
229
    self.req = req
230
    self.sn = None
231

    
232
  def getSerialNumber(self):
233
    """Get Serial Number.
234

235
    """
236
    return self.sn
237

    
238
  def _checkIntVariable(self, name):
239
    """Return the parsed value of an int argument.
240

241
    """
242
    val = self.queryargs.get(name, 0)
243
    if isinstance(val, list):
244
      if val:
245
        val = val[0]
246
      else:
247
        val = 0
248
    try:
249
      val = int(val)
250
    except (ValueError, TypeError), err:
251
      raise http.HttpBadRequest("Invalid value for the"
252
                                " '%s' parameter" % (name,))
253
    return val
254

    
255
  def _checkStringVariable(self, name, default=None):
256
    """Return the parsed value of an int argument.
257

258
    """
259
    val = self.queryargs.get(name, default)
260
    if isinstance(val, list):
261
      if val:
262
        val = val[0]
263
      else:
264
        val = default
265
    return val
266

    
267
  def getBodyParameter(self, name, *args):
268
    """Check and return the value for a given parameter.
269

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

273
    @param name: the required parameter
274

275
    """
276
    if name in self.req.request_body:
277
      return self.req.request_body[name]
278
    elif args:
279
      return args[0]
280
    else:
281
      raise http.HttpBadRequest("Required parameter '%s' is missing" %
282
                                name)
283

    
284
  def useLocking(self):
285
    """Check if the request specifies locking.
286

287
    """
288
    return self._checkIntVariable('lock')
289

    
290
  def useBulk(self):
291
    """Check if the request specifies bulk querying.
292

293
    """
294
    return self._checkIntVariable('bulk')