5e7370e12709fc05612fd4c2eec55fe991a56ac6
[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
37
38 class ConfdQuery(object):
39   """Confd Query base class.
40
41   """
42   def __init__(self, reader):
43     """Constructor for Confd Query
44
45     @type reader: L{ssconf.SimpleConfigReader}
46     @param reader: ConfigReader to use to access the config
47
48     """
49     self.reader = reader
50
51   def Exec(self, query):
52     """Process a single UDP request from a client.
53
54     Different queries should override this function, which by defaults returns
55     a "non-implemented" answer.
56
57     @type query: (undefined)
58     @param query: ConfdRequest 'query' field
59     @rtype: (integer, undefined)
60     @return: status and answer to give to the client
61
62     """
63     status = constants.CONFD_REPL_STATUS_NOTIMPLEMENTED
64     answer = 'not implemented'
65     return status, answer
66
67
68 class PingQuery(ConfdQuery):
69   """An empty confd query.
70
71   It will return success on an empty argument, and an error on any other
72   argument.
73
74   """
75   def Exec(self, query):
76     """PingQuery main execution.
77
78     """
79     if query is None:
80       status = constants.CONFD_REPL_STATUS_OK
81       answer = 'ok'
82     else:
83       status = constants.CONFD_REPL_STATUS_ERROR
84       answer = 'non-empty ping query'
85
86     return status, answer
87
88
89 class ClusterMasterQuery(ConfdQuery):
90   """Cluster master query.
91
92   It accepts no arguments, and returns the current cluster master.
93
94   """
95   def Exec(self, query):
96     """ClusterMasterQuery main execution
97
98     """
99     if query is None:
100       status = constants.CONFD_REPL_STATUS_OK
101       answer = self.reader.GetMasterNode()
102     else:
103       status = constants.CONFD_REPL_STATUS_ERROR
104       answer = 'master query accepts no query argument'
105
106     return status, answer
107
108
109 class NodeRoleQuery(ConfdQuery):
110   """A query for the role of a node.
111
112   It will return one of CONFD_NODE_ROLE_*, or an error for non-existing nodes.
113
114   """
115   def Exec(self, query):
116     """EmptyQuery main execution
117
118     """
119     node = query
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)
125     if flags is None:
126       return QUERY_UNKNOWN_ENTRY_ERROR
127
128     master_candidate, drained, offline = flags
129     if master_candidate:
130       answer = constants.CONFD_NODE_ROLE_CANDIDATE
131     elif drained:
132       answer = constants.CONFD_NODE_ROLE_DRAINED
133     elif offline:
134       answer = constants.CONFD_NODE_ROLE_OFFLINE
135     else:
136       answer = constants.CONFD_NODE_ROLE_REGULAR
137
138     return constants.CONFD_REPL_STATUS_OK, answer
139
140
141 class InstanceIpToNodePrimaryIpQuery(ConfdQuery):
142   """A query for the location of one or more instance's ips.
143
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).
148
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.
151
152   """
153   def Exec(self, query):
154     """InstanceIpToNodePrimaryIpQuery main execution.
155
156     """
157     if isinstance(query, list):
158       instances_list = query
159     else:
160       instances_list = [query]
161     pnodes_list = []
162
163     for instance_ip in instances_list:
164       instance = self.reader.GetInstanceByLinkIp(instance_ip, None)
165       if not instance:
166         logging.debug("Invalid instance IP: %s" % instance)
167         pnodes_list.append(QUERY_UNKNOWN_ENTRY_ERROR)
168         continue
169
170       pnode = self.reader.GetInstancePrimaryNode(instance)
171       if not pnode:
172         logging.error("Instance '%s' doesn't have an associated primary"
173                       " node" % instance)
174         pnodes_list.append(QUERY_INTERNAL_ERROR)
175         continue
176
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)
182         continue
183
184       pnodes_list.append((constants.CONFD_REPL_STATUS_OK, pnode_primary_ip))
185
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]
189
190     return constants.CONFD_REPL_STATUS_OK, pnodes_list
191
192
193 class NodesPipsQuery(ConfdQuery):
194   """A query for nodes primary IPs.
195
196   It returns the list of nodes primary IPs.
197
198   """
199   def Exec(self, query):
200     """NodesPipsQuery main execution.
201
202     """
203     if query is None:
204       status = constants.CONFD_REPL_STATUS_OK
205       answer = self.reader.GetNodesPrimaryIps()
206     else:
207       status = constants.CONFD_REPL_STATUS_ERROR
208       answer = "non-empty node primary IPs query"
209
210     return status, answer
211
212
213 class MasterCandidatesPipsQuery(ConfdQuery):
214   """A query for master candidates primary IPs.
215
216   It returns the list of master candidates primary IPs.
217
218   """
219   def Exec(self, query):
220     """MasterCandidatesPipsQuery main execution.
221
222     """
223     if query is None:
224       status = constants.CONFD_REPL_STATUS_OK
225       answer = self.reader.GetMasterCandidatesPrimaryIps()
226     else:
227       status = constants.CONFD_REPL_STATUS_ERROR
228       answer = "non-empty master candidates primary IPs query"
229
230     return status, answer
231
232
233 class InstancesIpsQuery(ConfdQuery):
234   """A query for instances IPs.
235
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.
238
239   """
240   def Exec(self, query):
241     """InstancesIpsQuery main execution.
242
243     """
244     link = query
245     status = constants.CONFD_REPL_STATUS_OK
246     answer = self.reader.GetInstancesIps(link)
247
248     return status, answer