Statistics
| Branch: | Tag: | Revision:

root / lib / rapi / baserlib.py @ 3427d34f

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
from ganeti import luxi
29
from ganeti import rapi
30
from ganeti import http
31
from ganeti import ssconf
32
from ganeti import constants
33
from ganeti import opcodes
34
from ganeti import errors
35

    
36

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

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

44
  """
45
  (field_id, field_uri) = uri_fields
46

    
47
  def _MapId(m_id):
48
    return { field_id: m_id, field_uri: uri_format % m_id, }
49

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

    
54
  return map(_MapId, ids)
55

    
56

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

60
  @param sequence: sequence of lists
61
  @param index: index of field
62

63
  """
64
  return map(lambda item: item[index], sequence)
65

    
66

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

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

74
  @param names: field names (list of strings)
75
  @param data: field data (list)
76

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

    
82

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

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

    
103
  return list(tags)
104

    
105

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

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

    
112

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

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

    
119

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

123
  @param itemslist: a list of items values
124
  @param fields: a list of items names
125

126
  @return: a list of mapped dictionaries
127

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

    
135

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

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

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

150
  """
151
  result = {}
152

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

    
160
  return result
161

    
162

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

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

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

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

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

    
197

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

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

204
  """
205
  logging.info("%s: %s", log_type, log_msg)
206

    
207

    
208
class R_Generic(object):
209
  """Generic class for resources.
210

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

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

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

224
    """
225
    self.items = items
226
    self.queryargs = queryargs
227
    self.req = req
228
    self.sn = None
229

    
230
  def getSerialNumber(self):
231
    """Get Serial Number.
232

233
    """
234
    return self.sn
235

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

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

    
253
  def getBodyParameter(self, name, *args):
254
    """Check and return the value for a given parameter.
255

256
    If a second parameter is not given, an error will be returned,
257
    otherwise this parameter specifies the default value.
258

259
    @param name: the required parameter
260

261
    """
262
    if name in self.req.request_body:
263
      return self.req.request_body[name]
264
    elif args:
265
      return args[0]
266
    else:
267
      raise http.HttpBadRequest("Required parameter '%s' is missing" %
268
                                name)
269

    
270
  def useLocking(self):
271
    """Check if the request specifies locking.
272

273
    """
274
    return self._checkIntVariable('lock')
275

    
276
  def useBulk(self):
277
    """Check if the request specifies bulk querying.
278

279
    """
280
    return self._checkIntVariable('bulk')
281

    
282
  def useForce(self):
283
    """Check if the request specifies a forced operation.
284

285
    """
286
    return self._checkIntVariable('force')
287

    
288
  def dryRun(self):
289
    """Check if the request specifies dry-run mode.
290

291
    """
292
    return self._checkIntVariable('dry-run')