4 # Copyright (C) 2006, 2007, 2008 Google Inc.
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.
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.
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
22 """Remote API base resources library.
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
37 def BuildUriList(ids, uri_format, uri_fields=("name", "uri")):
38 """Builds a URI list as used by index resources.
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
45 (field_id, field_uri) = uri_fields
48 return { field_id: m_id, field_uri: uri_format % m_id, }
50 # Make sure the result is sorted, makes it nicer to look at and simplifies
54 return map(_MapId, ids)
57 def ExtractField(sequence, index):
58 """Creates a list containing one column out of a list of lists.
60 @param sequence: sequence of lists
61 @param index: index of field
64 return map(lambda item: item[index], sequence)
67 def MapFields(names, data):
68 """Maps two lists into one dictionary.
71 >>> MapFields(["a", "b"], ["foo", 123])
72 {'a': 'foo', 'b': 123}
74 @param names: field names (list of strings)
75 @param data: field data (list)
78 if len(names) != len(data):
79 raise AttributeError("Names and data must have the same length")
80 return dict(zip(names, data))
83 def _Tags_GET(kind, name=""):
84 """Helper function to retrieve tags.
87 if kind == constants.TAG_INSTANCE or kind == constants.TAG_NODE:
89 raise http.HttpBadRequest("Missing name on tag request")
91 if kind == constants.TAG_INSTANCE:
92 fn = cl.QueryInstances
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")
99 elif kind == constants.TAG_CLUSTER:
100 ssc = ssconf.SimpleStore()
101 tags = ssc.GetClusterTags()
106 def _Tags_PUT(kind, tags, name=""):
107 """Helper function to set tags.
110 return SubmitJob([opcodes.OpAddTags(kind=kind, name=name, tags=tags)])
113 def _Tags_DELETE(kind, tags, name=""):
114 """Helper function to delete tags.
117 return SubmitJob([opcodes.OpDelTags(kind=kind, name=name, tags=tags)])
120 def MapBulkFields(itemslist, fields):
121 """Map value to field name in to one dictionary.
123 @param itemslist: a list of items values
124 @param fields: a list of items names
126 @return: a list of mapped dictionaries
130 for item in itemslist:
131 mapped = MapFields(fields, item)
132 items_details.append(mapped)
136 def MakeParamsDict(opts, params):
137 """Makes params dictionary out of a option set.
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
144 @param opts: selected options
145 @type params: frozenset
146 @param params: subset of options
148 @return: dictionary of options, filtered by given subset.
163 def SubmitJob(op, cl=None):
164 """Generic wrapper for submit job, for better http compatibility.
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
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))
189 """Geric wrapper for luxi.Client(), for better http compatiblity.
194 except luxi.NoMasterError, err:
195 raise http.HttpBadGateway("Master seems to unreachable: %s" % str(err))
198 def FeedbackFn(ts, log_type, log_msg):
199 """Feedback logging function for http case.
201 We don't have a stdout for printing log messages, so log them to the
205 logging.info("%s: %s", log_type, log_msg)
208 class R_Generic(object):
209 """Generic class for resources.
212 # Default permission requirements
214 PUT_ACCESS = [rapi.RAPI_ACCESS_WRITE]
215 POST_ACCESS = [rapi.RAPI_ACCESS_WRITE]
216 DELETE_ACCESS = [rapi.RAPI_ACCESS_WRITE]
218 def __init__(self, items, queryargs, req):
219 """Generic resource constructor.
221 @param items: a list with variables encoded in the URL
222 @param queryargs: a dictionary with additional options from URL
226 self.queryargs = queryargs
230 def getSerialNumber(self):
231 """Get Serial Number.
236 def _checkIntVariable(self, name):
237 """Return the parsed value of an int argument.
240 val = self.queryargs.get(name, 0)
241 if isinstance(val, list):
248 except (ValueError, TypeError):
249 raise http.HttpBadRequest("Invalid value for the"
250 " '%s' parameter" % (name,))
253 def _checkStringVariable(self, name, default=None):
254 """Return the parsed value of an int argument.
257 val = self.queryargs.get(name, default)
258 if isinstance(val, list):
265 def getBodyParameter(self, name, *args):
266 """Check and return the value for a given parameter.
268 If a second parameter is not given, an error will be returned,
269 otherwise this parameter specifies the default value.
271 @param name: the required parameter
274 if name in self.req.request_body:
275 return self.req.request_body[name]
279 raise http.HttpBadRequest("Required parameter '%s' is missing" %
282 def useLocking(self):
283 """Check if the request specifies locking.
286 return self._checkIntVariable('lock')
289 """Check if the request specifies bulk querying.
292 return self._checkIntVariable('bulk')
295 """Check if the request specifies a forced operation.
298 return self._checkIntVariable('force')
301 """Check if the request specifies dry-run mode.
304 return self._checkIntVariable('dry-run')