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)
38 class ConfdQuery(object):
39 """Confd Query base class.
42 def __init__(self, reader):
43 """Constructor for Confd Query
45 @type reader: L{ssconf.SimpleConfigReader}
46 @param reader: ConfigReader to use to access the config
51 def Exec(self, query):
52 """Process a single UDP request from a client.
54 Different queries should override this function, which by defaults returns
55 a "non-implemented" answer.
57 @type query: (undefined)
58 @param query: ConfdRequest 'query' field
59 @rtype: (integer, undefined)
60 @return: status and answer to give to the client
63 status = constants.CONFD_REPL_STATUS_NOTIMPLEMENTED
64 answer = 'not implemented'
68 class PingQuery(ConfdQuery):
69 """An empty confd query.
71 It will return success on an empty argument, and an error on any other
75 def Exec(self, query):
76 """PingQuery main execution.
80 status = constants.CONFD_REPL_STATUS_OK
83 status = constants.CONFD_REPL_STATUS_ERROR
84 answer = 'non-empty ping query'
89 class ClusterMasterQuery(ConfdQuery):
90 """Cluster master query.
92 It accepts no arguments, and returns the current cluster master.
95 def Exec(self, query):
96 """ClusterMasterQuery main execution
100 status = constants.CONFD_REPL_STATUS_OK
101 answer = self.reader.GetMasterNode()
103 status = constants.CONFD_REPL_STATUS_ERROR
104 answer = 'master query accepts no query argument'
106 return status, answer
109 class NodeRoleQuery(ConfdQuery):
110 """A query for the role of a node.
112 It will return one of CONFD_NODE_ROLE_*, or an error for non-existing nodes.
115 def Exec(self, query):
116 """EmptyQuery main execution
120 if self.reader.GetMasterNode() == node:
121 status = constants.CONFD_REPL_STATUS_OK
122 answer = constants.CONFD_NODE_ROLE_MASTER
123 return status, answer
124 flags = self.reader.GetNodeStatusFlags(node)
126 return QUERY_UNKNOWN_ENTRY_ERROR
128 master_candidate, drained, offline = flags
130 answer = constants.CONFD_NODE_ROLE_CANDIDATE
132 answer = constants.CONFD_NODE_ROLE_DRAINED
134 answer = constants.CONFD_NODE_ROLE_OFFLINE
136 answer = constants.CONFD_NODE_ROLE_REGULAR
138 return constants.CONFD_REPL_STATUS_OK, answer
141 class InstanceIpToNodePrimaryIpQuery(ConfdQuery):
142 """A query for the location of one or more instance's ips.
144 Given a list of instance IPs, returns an ordered list with the same
145 number of elements as the input. Each element of the list is a tuple
146 containing the status (success or failure) and the content of the
147 query (IP of the primary node if successful, error constant if not).
149 If a string (instance's IP) is given instead of a list it will return
150 a single tuple, as opposed to a 1-element list containing that tuple.
153 def Exec(self, query):
154 """InstanceIpToNodePrimaryIpQuery main execution.
157 if isinstance(query, list):
158 instances_list = query
160 instances_list = [query]
163 for instance_ip in instances_list:
164 instance = self.reader.GetInstanceByLinkIp(instance_ip, None)
166 logging.debug("Invalid instance IP: %s" % instance)
167 pnodes_list.append(QUERY_UNKNOWN_ENTRY_ERROR)
170 pnode = self.reader.GetInstancePrimaryNode(instance)
172 logging.error("Instance '%s' doesn't have an associated primary"
174 pnodes_list.append(QUERY_INTERNAL_ERROR)
177 pnode_primary_ip = self.reader.GetNodePrimaryIp(pnode)
178 if not pnode_primary_ip:
179 logging.error("Primary node '%s' doesn't have an associated"
180 " primary IP" % pnode)
181 pnodes_list.append(QUERY_INTERNAL_ERROR)
184 pnodes_list.append((constants.CONFD_REPL_STATUS_OK, pnode_primary_ip))
186 # If input was a string, return a tuple instead of a 1-element list
187 if isinstance(query, basestring):
188 return pnodes_list[0]
190 return constants.CONFD_REPL_STATUS_OK, pnodes_list
193 class NodesPipsQuery(ConfdQuery):
194 """A query for nodes primary IPs.
196 It returns the list of nodes primary IPs.
199 def Exec(self, query):
200 """NodesPipsQuery main execution.
204 status = constants.CONFD_REPL_STATUS_OK
205 answer = self.reader.GetNodesPrimaryIps()
207 status = constants.CONFD_REPL_STATUS_ERROR
208 answer = "non-empty node primary IPs query"
210 return status, answer
213 class MasterCandidatesPipsQuery(ConfdQuery):
214 """A query for master candidates primary IPs.
216 It returns the list of master candidates primary IPs.
219 def Exec(self, query):
220 """MasterCandidatesPipsQuery main execution.
224 status = constants.CONFD_REPL_STATUS_OK
225 answer = self.reader.GetMasterCandidatesPrimaryIps()
227 status = constants.CONFD_REPL_STATUS_ERROR
228 answer = "non-empty master candidates primary IPs query"
230 return status, answer
233 class InstancesIpsQuery(ConfdQuery):
234 """A query for instances IPs.
236 It returns the list of IPs of NICs connected to the requested link or all the
237 instances IPs if no link is submitted.
240 def Exec(self, query):
241 """InstancesIpsQuery main execution.
245 status = constants.CONFD_REPL_STATUS_OK
246 answer = self.reader.GetInstancesIps(link)
248 return status, answer