DeprecationWarning fixes for pylint
[ganeti-local] / lib / confd / querylib.py
1 #
2 #
3
4 # Copyright (C) 2009 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 """Ganeti configuration daemon queries library.
23
24 """
25
26 import logging
27
28 from ganeti import constants
29
30
31 # constants for some common errors to return from a query
32 QUERY_UNKNOWN_ENTRY_ERROR = (constants.CONFD_REPL_STATUS_ERROR,
33                              constants.CONFD_ERROR_UNKNOWN_ENTRY)
34 QUERY_INTERNAL_ERROR = (constants.CONFD_REPL_STATUS_ERROR,
35                         constants.CONFD_ERROR_INTERNAL)
36 QUERY_ARGUMENT_ERROR = (constants.CONFD_REPL_STATUS_ERROR,
37                         constants.CONFD_ERROR_ARGUMENT)
38
39
40 class ConfdQuery(object):
41   """Confd Query base class.
42
43   """
44   def __init__(self, reader):
45     """Constructor for Confd Query
46
47     @type reader: L{ssconf.SimpleConfigReader}
48     @param reader: ConfigReader to use to access the config
49
50     """
51     self.reader = reader
52
53   def Exec(self, query): # pylint: disable=R0201,W0613
54     """Process a single UDP request from a client.
55
56     Different queries should override this function, which by defaults returns
57     a "non-implemented" answer.
58
59     @type query: (undefined)
60     @param query: ConfdRequest 'query' field
61     @rtype: (integer, undefined)
62     @return: status and answer to give to the client
63
64     """
65     status = constants.CONFD_REPL_STATUS_NOTIMPLEMENTED
66     answer = 'not implemented'
67     return status, answer
68
69
70 class PingQuery(ConfdQuery):
71   """An empty confd query.
72
73   It will return success on an empty argument, and an error on any other
74   argument.
75
76   """
77   def Exec(self, query):
78     """PingQuery main execution.
79
80     """
81     if query is None:
82       status = constants.CONFD_REPL_STATUS_OK
83       answer = 'ok'
84     else:
85       status = constants.CONFD_REPL_STATUS_ERROR
86       answer = 'non-empty ping query'
87
88     return status, answer
89
90
91 class ClusterMasterQuery(ConfdQuery):
92   """Cluster master query.
93
94   It accepts no arguments, and returns the current cluster master.
95
96   """
97   def _GetMasterNode(self):
98     return self.reader.GetMasterNode()
99
100   def Exec(self, query):
101     """ClusterMasterQuery main execution
102
103     """
104     if isinstance(query, dict):
105       if constants.CONFD_REQQ_FIELDS in query:
106         status = constants.CONFD_REPL_STATUS_OK
107         req_fields = query[constants.CONFD_REQQ_FIELDS]
108         if not isinstance(req_fields, (list, tuple)):
109           logging.debug("FIELDS request should be a list")
110           return QUERY_ARGUMENT_ERROR
111
112         answer = []
113         for field in req_fields:
114           if field == constants.CONFD_REQFIELD_NAME:
115             answer.append(self._GetMasterNode())
116           elif field == constants.CONFD_REQFIELD_IP:
117             answer.append(self.reader.GetMasterIP())
118           elif field == constants.CONFD_REQFIELD_MNODE_PIP:
119             answer.append(self.reader.GetNodePrimaryIp(self._GetMasterNode()))
120       else:
121         logging.debug("missing FIELDS in query dict")
122         return QUERY_ARGUMENT_ERROR
123     elif not query:
124       status = constants.CONFD_REPL_STATUS_OK
125       answer = self.reader.GetMasterNode()
126     else:
127       logging.debug("Invalid master query argument: not dict or empty")
128       return QUERY_ARGUMENT_ERROR
129
130     return status, answer
131
132
133 class NodeRoleQuery(ConfdQuery):
134   """A query for the role of a node.
135
136   It will return one of CONFD_NODE_ROLE_*, or an error for non-existing nodes.
137
138   """
139   def Exec(self, query):
140     """EmptyQuery main execution
141
142     """
143     node = query
144     if self.reader.GetMasterNode() == node:
145       status = constants.CONFD_REPL_STATUS_OK
146       answer = constants.CONFD_NODE_ROLE_MASTER
147       return status, answer
148     flags = self.reader.GetNodeStatusFlags(node)
149     if flags is None:
150       return QUERY_UNKNOWN_ENTRY_ERROR
151
152     master_candidate, drained, offline = flags
153     if master_candidate:
154       answer = constants.CONFD_NODE_ROLE_CANDIDATE
155     elif drained:
156       answer = constants.CONFD_NODE_ROLE_DRAINED
157     elif offline:
158       answer = constants.CONFD_NODE_ROLE_OFFLINE
159     else:
160       answer = constants.CONFD_NODE_ROLE_REGULAR
161
162     return constants.CONFD_REPL_STATUS_OK, answer
163
164
165 class InstanceIpToNodePrimaryIpQuery(ConfdQuery):
166   """A query for the location of one or more instance's ips.
167
168   """
169   def Exec(self, query):
170     """InstanceIpToNodePrimaryIpQuery main execution.
171
172     @type query: string or dict
173     @param query: instance ip or dict containing:
174                   constants.CONFD_REQQ_LINK: nic link (optional)
175                   constants.CONFD_REQQ_IPLIST: list of ips
176                   constants.CONFD_REQQ_IP: single ip
177                   (one IP type request is mandatory)
178     @rtype: (integer, ...)
179     @return: ((status, answer) or (success, [(status, answer)...])
180
181     """
182     if isinstance(query, dict):
183       if constants.CONFD_REQQ_IP in query:
184         instances_list = [query[constants.CONFD_REQQ_IP]]
185         mode = constants.CONFD_REQQ_IP
186       elif constants.CONFD_REQQ_IPLIST in query:
187         instances_list = query[constants.CONFD_REQQ_IPLIST]
188         mode = constants.CONFD_REQQ_IPLIST
189       else:
190         logging.debug("missing IP or IPLIST in query dict")
191         return QUERY_ARGUMENT_ERROR
192
193       if constants.CONFD_REQQ_LINK in query:
194         network_link = query[constants.CONFD_REQQ_LINK]
195       else:
196         network_link = None # default will be used
197     else:
198       logging.debug("Invalid query argument type for: %s", query)
199       return QUERY_ARGUMENT_ERROR
200
201     pnodes_list = []
202
203     for instance_ip in instances_list:
204       if not isinstance(instance_ip, basestring):
205         logging.debug("Invalid IP type for: %s", instance_ip)
206         return QUERY_ARGUMENT_ERROR
207
208       instance = self.reader.GetInstanceByLinkIp(instance_ip, network_link)
209       if not instance:
210         logging.debug("Unknown instance IP: %s", instance_ip)
211         pnodes_list.append(QUERY_UNKNOWN_ENTRY_ERROR)
212         continue
213
214       pnode = self.reader.GetInstancePrimaryNode(instance)
215       if not pnode:
216         logging.error("Instance '%s' doesn't have an associated primary"
217                       " node", instance)
218         pnodes_list.append(QUERY_INTERNAL_ERROR)
219         continue
220
221       pnode_primary_ip = self.reader.GetNodePrimaryIp(pnode)
222       if not pnode_primary_ip:
223         logging.error("Primary node '%s' doesn't have an associated"
224                       " primary IP", pnode)
225         pnodes_list.append(QUERY_INTERNAL_ERROR)
226         continue
227
228       pnodes_list.append((constants.CONFD_REPL_STATUS_OK, pnode_primary_ip))
229
230     # If a single ip was requested, return a single answer, otherwise
231     # the whole list, with a success status (since each entry has its
232     # own success/failure)
233     if mode == constants.CONFD_REQQ_IP:
234       return pnodes_list[0]
235
236     return constants.CONFD_REPL_STATUS_OK, pnodes_list
237
238
239 class NodesPipsQuery(ConfdQuery):
240   """A query for nodes primary IPs.
241
242   It returns the list of nodes primary IPs.
243
244   """
245   def Exec(self, query):
246     """NodesPipsQuery main execution.
247
248     """
249     if query is None:
250       status = constants.CONFD_REPL_STATUS_OK
251       answer = self.reader.GetNodesPrimaryIps()
252     else:
253       status = constants.CONFD_REPL_STATUS_ERROR
254       answer = "non-empty node primary IPs query"
255
256     return status, answer
257
258
259 class MasterCandidatesPipsQuery(ConfdQuery):
260   """A query for master candidates primary IPs.
261
262   It returns the list of master candidates primary IPs.
263
264   """
265   def Exec(self, query):
266     """MasterCandidatesPipsQuery main execution.
267
268     """
269     if query is None:
270       status = constants.CONFD_REPL_STATUS_OK
271       answer = self.reader.GetMasterCandidatesPrimaryIps()
272     else:
273       status = constants.CONFD_REPL_STATUS_ERROR
274       answer = "non-empty master candidates primary IPs query"
275
276     return status, answer
277
278
279 class InstancesIpsQuery(ConfdQuery):
280   """A query for instances IPs.
281
282   It returns the list of IPs of NICs connected to the requested link or all the
283   instances IPs if no link is submitted.
284
285   """
286   def Exec(self, query):
287     """InstancesIpsQuery main execution.
288
289     """
290     link = query
291     status = constants.CONFD_REPL_STATUS_OK
292     answer = self.reader.GetInstancesIps(link)
293
294     return status, answer