4 # Copyright (C) 2009, 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 """Ganeti configuration daemon queries library.
28 from ganeti import constants
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)
40 class ConfdQuery(object):
41 """Confd Query base class.
44 def __init__(self, reader):
45 """Constructor for Confd Query
47 @type reader: L{ssconf.SimpleConfigReader}
48 @param reader: ConfigReader to use to access the config
53 def Exec(self, query):
54 """Process a single UDP request from a client.
56 Different queries should override this function, which by defaults returns
57 a "non-implemented" answer.
59 @type query: (undefined)
60 @param query: ConfdRequest 'query' field
61 @rtype: (integer, undefined)
62 @return: status and answer to give to the client
65 status = constants.CONFD_REPL_STATUS_NOTIMPLEMENTED
66 answer = 'not implemented'
70 class PingQuery(ConfdQuery):
71 """An empty confd query.
73 It will return success on an empty argument, and an error on any other
77 def Exec(self, query):
78 """PingQuery main execution.
82 status = constants.CONFD_REPL_STATUS_OK
85 status = constants.CONFD_REPL_STATUS_ERROR
86 answer = 'non-empty ping query'
91 class ClusterMasterQuery(ConfdQuery):
92 """Cluster master query.
94 It accepts no arguments, and returns the current cluster master.
97 def Exec(self, query):
98 """ClusterMasterQuery main execution
102 status = constants.CONFD_REPL_STATUS_OK
103 answer = self.reader.GetMasterNode()
105 status = constants.CONFD_REPL_STATUS_ERROR
106 answer = 'master query accepts no query argument'
108 return status, answer
111 class NodeRoleQuery(ConfdQuery):
112 """A query for the role of a node.
114 It will return one of CONFD_NODE_ROLE_*, or an error for non-existing nodes.
117 def Exec(self, query):
118 """EmptyQuery main execution
122 if self.reader.GetMasterNode() == node:
123 status = constants.CONFD_REPL_STATUS_OK
124 answer = constants.CONFD_NODE_ROLE_MASTER
125 return status, answer
126 flags = self.reader.GetNodeStatusFlags(node)
128 return QUERY_UNKNOWN_ENTRY_ERROR
130 master_candidate, drained, offline = flags
132 answer = constants.CONFD_NODE_ROLE_CANDIDATE
134 answer = constants.CONFD_NODE_ROLE_DRAINED
136 answer = constants.CONFD_NODE_ROLE_OFFLINE
138 answer = constants.CONFD_NODE_ROLE_REGULAR
140 return constants.CONFD_REPL_STATUS_OK, answer
143 class InstanceIpToNodePrimaryIpQuery(ConfdQuery):
144 """A query for the location of one or more instance's ips.
147 def Exec(self, query):
148 """InstanceIpToNodePrimaryIpQuery main execution.
150 @type query: string or dict
151 @param query: instance ip or dict containing:
152 constants.CONFD_REQQ_LINK: nic link (optional)
153 constants.CONFD_REQQ_IPLIST: list of ips
154 constants.CONFD_REQQ_IP: single ip
155 (one IP type request is mandatory)
156 @rtype: (integer, ...)
157 @return: ((status, answer) or (success, [(status, answer)...])
160 if isinstance(query, dict):
161 if constants.CONFD_REQQ_IP in query:
162 instances_list = [query[constants.CONFD_REQQ_IP]]
163 mode = constants.CONFD_REQQ_IP
164 elif constants.CONFD_REQQ_IPLIST in query:
165 instances_list = query[constants.CONFD_REQQ_IPLIST]
166 mode = constants.CONFD_REQQ_IPLIST
168 status = constants.CONFD_REPL_STATUS_ERROR
169 logging.debug("missing IP or IPLIST in query dict")
170 return QUERY_ARGUMENT_ERROR
172 if constants.CONFD_REQQ_LINK in query:
173 network_link = query[constants.CONFD_REQQ_LINK]
175 network_link = None # default will be used
176 elif isinstance(query, basestring):
177 # 2.1 beta1 and beta2 mode, to be deprecated for 2.2
178 instances_list = [query]
180 mode = constants.CONFD_REQQ_IP
182 logging.debug("Invalid query argument type for: %s" % query)
183 return QUERY_ARGUMENT_ERROR
187 for instance_ip in instances_list:
188 if not isinstance(instance_ip, basestring):
189 logging.debug("Invalid IP type for: %s" % instance_ip)
190 return QUERY_ARGUMENT_ERROR
192 instance = self.reader.GetInstanceByLinkIp(instance_ip, network_link)
194 logging.debug("Unknown instance IP: %s" % instance_ip)
195 pnodes_list.append(QUERY_UNKNOWN_ENTRY_ERROR)
198 pnode = self.reader.GetInstancePrimaryNode(instance)
200 logging.error("Instance '%s' doesn't have an associated primary"
202 pnodes_list.append(QUERY_INTERNAL_ERROR)
205 pnode_primary_ip = self.reader.GetNodePrimaryIp(pnode)
206 if not pnode_primary_ip:
207 logging.error("Primary node '%s' doesn't have an associated"
208 " primary IP" % pnode)
209 pnodes_list.append(QUERY_INTERNAL_ERROR)
212 pnodes_list.append((constants.CONFD_REPL_STATUS_OK, pnode_primary_ip))
214 # If a single ip was requested, return a single answer, otherwise the whole
215 # list, with a success status (since each entry has its own success/failure)
216 if mode == constants.CONFD_REQQ_IP:
217 return pnodes_list[0]
219 return constants.CONFD_REPL_STATUS_OK, pnodes_list
222 class NodesPipsQuery(ConfdQuery):
223 """A query for nodes primary IPs.
225 It returns the list of nodes primary IPs.
228 def Exec(self, query):
229 """NodesPipsQuery main execution.
233 status = constants.CONFD_REPL_STATUS_OK
234 answer = self.reader.GetNodesPrimaryIps()
236 status = constants.CONFD_REPL_STATUS_ERROR
237 answer = "non-empty node primary IPs query"
239 return status, answer
242 class MasterCandidatesPipsQuery(ConfdQuery):
243 """A query for master candidates primary IPs.
245 It returns the list of master candidates primary IPs.
248 def Exec(self, query):
249 """MasterCandidatesPipsQuery main execution.
253 status = constants.CONFD_REPL_STATUS_OK
254 answer = self.reader.GetMasterCandidatesPrimaryIps()
256 status = constants.CONFD_REPL_STATUS_ERROR
257 answer = "non-empty master candidates primary IPs query"
259 return status, answer
262 class InstancesIpsQuery(ConfdQuery):
263 """A query for instances IPs.
265 It returns the list of IPs of NICs connected to the requested link or all the
266 instances IPs if no link is submitted.
269 def Exec(self, query):
270 """InstancesIpsQuery main execution.
274 status = constants.CONFD_REPL_STATUS_OK
275 answer = self.reader.GetInstancesIps(link)
277 return status, answer