Statistics
| Branch: | Tag: | Revision:

root / lib / confd / querylib.py @ 250554a9

History | View | Annotate | Download (8.6 kB)

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):
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 Exec(self, query):
98
    """ClusterMasterQuery main execution
99

100
    """
101
    if isinstance(query, dict):
102
      if constants.CONFD_REQQ_FIELDS in query:
103
        status = constants.CONFD_REPL_STATUS_OK
104
        req_fields = query[constants.CONFD_REQQ_FIELDS]
105
        if not isinstance(req_fields, (list, tuple)):
106
          logging.debug("FIELDS request should be a list")
107
          return QUERY_ARGUMENT_ERROR
108

    
109
        answer = []
110
        for field in req_fields:
111
          if field == constants.CONFD_REQFIELD_NAME:
112
            answer.append(self.reader.GetMasterNode())
113
          elif field == constants.CONFD_REQFIELD_IP:
114
            answer.append(self.reader.GetMasterIP())
115
      else:
116
        logging.debug("missing FIELDS in query dict")
117
        return QUERY_ARGUMENT_ERROR
118
    elif not query:
119
      status = constants.CONFD_REPL_STATUS_OK
120
      answer = self.reader.GetMasterNode()
121
    else:
122
      logging.debug("Invalid master query argument: not dict or empty")
123
      return QUERY_ARGUMENT_ERROR
124

    
125
    return status, answer
126

    
127

    
128
class NodeRoleQuery(ConfdQuery):
129
  """A query for the role of a node.
130

131
  It will return one of CONFD_NODE_ROLE_*, or an error for non-existing nodes.
132

133
  """
134
  def Exec(self, query):
135
    """EmptyQuery main execution
136

137
    """
138
    node = query
139
    if self.reader.GetMasterNode() == node:
140
      status = constants.CONFD_REPL_STATUS_OK
141
      answer = constants.CONFD_NODE_ROLE_MASTER
142
      return status, answer
143
    flags = self.reader.GetNodeStatusFlags(node)
144
    if flags is None:
145
      return QUERY_UNKNOWN_ENTRY_ERROR
146

    
147
    master_candidate, drained, offline = flags
148
    if master_candidate:
149
      answer = constants.CONFD_NODE_ROLE_CANDIDATE
150
    elif drained:
151
      answer = constants.CONFD_NODE_ROLE_DRAINED
152
    elif offline:
153
      answer = constants.CONFD_NODE_ROLE_OFFLINE
154
    else:
155
      answer = constants.CONFD_NODE_ROLE_REGULAR
156

    
157
    return constants.CONFD_REPL_STATUS_OK, answer
158

    
159

    
160
class InstanceIpToNodePrimaryIpQuery(ConfdQuery):
161
  """A query for the location of one or more instance's ips.
162

163
  """
164
  def Exec(self, query):
165
    """InstanceIpToNodePrimaryIpQuery main execution.
166

167
    @type query: string or dict
168
    @param query: instance ip or dict containing:
169
                  constants.CONFD_REQQ_LINK: nic link (optional)
170
                  constants.CONFD_REQQ_IPLIST: list of ips
171
                  constants.CONFD_REQQ_IP: single ip
172
                  (one IP type request is mandatory)
173
    @rtype: (integer, ...)
174
    @return: ((status, answer) or (success, [(status, answer)...])
175

176
    """
177
    if isinstance(query, dict):
178
      if constants.CONFD_REQQ_IP in query:
179
        instances_list = [query[constants.CONFD_REQQ_IP]]
180
        mode = constants.CONFD_REQQ_IP
181
      elif constants.CONFD_REQQ_IPLIST in query:
182
        instances_list = query[constants.CONFD_REQQ_IPLIST]
183
        mode = constants.CONFD_REQQ_IPLIST
184
      else:
185
        status = constants.CONFD_REPL_STATUS_ERROR
186
        logging.debug("missing IP or IPLIST in query dict")
187
        return QUERY_ARGUMENT_ERROR
188

    
189
      if constants.CONFD_REQQ_LINK in query:
190
        network_link = query[constants.CONFD_REQQ_LINK]
191
      else:
192
        network_link = None # default will be used
193
    elif isinstance(query, basestring):
194
      # 2.1 beta1 and beta2 mode, to be deprecated for 2.2
195
      instances_list = [query]
196
      network_link = None
197
      mode = constants.CONFD_REQQ_IP
198
    else:
199
      logging.debug("Invalid query argument type for: %s" % query)
200
      return QUERY_ARGUMENT_ERROR
201

    
202
    pnodes_list = []
203

    
204
    for instance_ip in instances_list:
205
      if not isinstance(instance_ip, basestring):
206
        logging.debug("Invalid IP type for: %s" % instance_ip)
207
        return QUERY_ARGUMENT_ERROR
208

    
209
      instance = self.reader.GetInstanceByLinkIp(instance_ip, network_link)
210
      if not instance:
211
        logging.debug("Unknown instance IP: %s" % instance_ip)
212
        pnodes_list.append(QUERY_UNKNOWN_ENTRY_ERROR)
213
        continue
214

    
215
      pnode = self.reader.GetInstancePrimaryNode(instance)
216
      if not pnode:
217
        logging.error("Instance '%s' doesn't have an associated primary"
218
                      " node" % instance)
219
        pnodes_list.append(QUERY_INTERNAL_ERROR)
220
        continue
221

    
222
      pnode_primary_ip = self.reader.GetNodePrimaryIp(pnode)
223
      if not pnode_primary_ip:
224
        logging.error("Primary node '%s' doesn't have an associated"
225
                      " primary IP" % pnode)
226
        pnodes_list.append(QUERY_INTERNAL_ERROR)
227
        continue
228

    
229
      pnodes_list.append((constants.CONFD_REPL_STATUS_OK, pnode_primary_ip))
230

    
231
    # If a single ip was requested, return a single answer, otherwise the whole
232
    # list, with a success status (since each entry has its 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