rapi: make tags query not use jobs
[ganeti-local] / lib / rapi / baserlib.py
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 ganeti.cli
27 import ganeti.opcodes
28
29 from ganeti import luxi
30 from ganeti import rapi
31 from ganeti import http
32 from ganeti import ssconf
33 from ganeti import constants
34
35
36 def BuildUriList(ids, uri_format, uri_fields=("name", "uri")):
37   """Builds a URI list as used by index resources.
38
39   @param ids: list of ids as strings
40   @param uri_format: format to be applied for URI
41   @param uri_fields: optional parameter for field IDs
42
43   """
44   (field_id, field_uri) = uri_fields
45
46   def _MapId(m_id):
47     return { field_id: m_id, field_uri: uri_format % m_id, }
48
49   # Make sure the result is sorted, makes it nicer to look at and simplifies
50   # unittests.
51   ids.sort()
52
53   return map(_MapId, ids)
54
55
56 def ExtractField(sequence, index):
57   """Creates a list containing one column out of a list of lists.
58
59   @param sequence: sequence of lists
60   @param index: index of field
61
62   """
63   return map(lambda item: item[index], sequence)
64
65
66 def MapFields(names, data):
67   """Maps two lists into one dictionary.
68
69   Example::
70       >>> MapFields(["a", "b"], ["foo", 123])
71       {'a': 'foo', 'b': 123}
72
73   @param names: field names (list of strings)
74   @param data: field data (list)
75
76   """
77   if len(names) != len(data):
78     raise AttributeError("Names and data must have the same length")
79   return dict(zip(names, data))
80
81
82 def _Tags_GET(kind, name=""):
83   """Helper function to retrieve tags.
84
85   """
86   if kind == constants.TAG_INSTANCE or kind == constants.TAG_NODE:
87     if not name:
88       raise HttpBadRequest("Missing name on tag request")
89     cl = luxi.Client()
90     if kind == constants.TAG_INSTANCE:
91       fn = cl.QueryInstances
92     else:
93       fn = cl.QueryNodes
94     result = fn(names=[name], fields=["tags"], use_locking=False)
95     if not result or not result[0]:
96       raise http.HttpBadGateway("Invalid response from tag query")
97     tags = result[0][0]
98   elif kind == constants.TAG_CLUSTER:
99     ssc = ssconf.SimpleStore()
100     tags = ssc.GetClusterTags()
101
102   return list(tags)
103
104
105 def _Tags_PUT(kind, tags, name=""):
106   """Helper function to set tags.
107
108   """
109   cl = luxi.Client()
110   return cl.SubmitJob([ganeti.opcodes.OpAddTags(kind=kind, name=name,
111                                                 tags=tags)])
112
113
114 def _Tags_DELETE(kind, tags, name=""):
115   """Helper function to delete tags.
116
117   """
118   cl = luxi.Client()
119   return cl.SubmitJob([ganeti.opcodes.OpDelTags(kind=kind, name=name,
120                                                 tags=tags)])
121
122
123 def MapBulkFields(itemslist, fields):
124   """Map value to field name in to one dictionary.
125
126   @param itemslist: a list of items values
127   @param fields: a list of items names
128
129   @return: a list of mapped dictionaries
130
131   """
132   items_details = []
133   for item in itemslist:
134     mapped = MapFields(fields, item)
135     items_details.append(mapped)
136   return items_details
137
138
139 def MakeParamsDict(opts, params):
140   """Makes params dictionary out of a option set.
141
142   This function returns a dictionary needed for hv or be parameters. But only
143   those fields which provided in the option set. Takes parameters frozensets
144   from constants.
145
146   @type opts: dict
147   @param opts: selected options
148   @type params: frozenset
149   @param params: subset of options
150   @rtype: dict
151   @return: dictionary of options, filtered by given subset.
152
153   """
154   result = {}
155
156   for p in params:
157     try:
158       value = opts[p]
159     except KeyError:
160       continue
161     result[p] = value
162
163   return result
164
165
166 class R_Generic(object):
167   """Generic class for resources.
168
169   """
170   # Default permission requirements
171   GET_ACCESS = []
172   PUT_ACCESS = [rapi.RAPI_ACCESS_WRITE]
173   POST_ACCESS = [rapi.RAPI_ACCESS_WRITE]
174   DELETE_ACCESS = [rapi.RAPI_ACCESS_WRITE]
175
176   def __init__(self, items, queryargs, req):
177     """Generic resource constructor.
178
179     @param items: a list with variables encoded in the URL
180     @param queryargs: a dictionary with additional options from URL
181
182     """
183     self.items = items
184     self.queryargs = queryargs
185     self.req = req
186     self.sn = None
187
188   def getSerialNumber(self):
189     """Get Serial Number.
190
191     """
192     return self.sn
193
194   def _checkIntVariable(self, name):
195     """Return the parsed value of an int argument.
196
197     """
198     val = self.queryargs.get(name, 0)
199     if isinstance(val, list):
200       if val:
201         val = val[0]
202       else:
203         val = 0
204     try:
205       val = int(val)
206     except (ValueError, TypeError), err:
207       raise http.HttpBadRequest("Invalid value for the"
208                                 " '%s' parameter" % (name,))
209     return val
210
211   def getBodyParameter(self, name, *args):
212     """Check and return the value for a given parameter.
213
214     If a second parameter is not given, an error will be returned,
215     otherwise this parameter specifies the default value.
216
217     @param name: the required parameter
218
219     """
220     if name in self.req.request_body:
221       return self.req.request_body[name]
222     elif args:
223       return args[0]
224     else:
225       raise http.HttpBadRequest("Required parameter '%s' is missing" %
226                                 name)
227
228   def useLocking(self):
229     """Check if the request specifies locking.
230
231     """
232     return self._checkIntVariable('lock')
233
234   def useBulk(self):
235     """Check if the request specifies bulk querying.
236
237     """
238     return self._checkIntVariable('bulk')