Statistics
| Branch: | Tag: | Revision:

root / lib / confd / querylib.py @ 6b7d5878

History | View | Annotate | Download (8.8 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): # pylint: disable-msg=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
    elif isinstance(query, basestring):
198
      # 2.1 beta1 and beta2 mode, to be deprecated for 2.2
199
      instances_list = [query]
200
      network_link = None
201
      mode = constants.CONFD_REQQ_IP
202
    else:
203
      logging.debug("Invalid query argument type for: %s", query)
204
      return QUERY_ARGUMENT_ERROR
205

    
206
    pnodes_list = []
207

    
208
    for instance_ip in instances_list:
209
      if not isinstance(instance_ip, basestring):
210
        logging.debug("Invalid IP type for: %s", instance_ip)
211
        return QUERY_ARGUMENT_ERROR
212

    
213
      instance = self.reader.GetInstanceByLinkIp(instance_ip, network_link)
214
      if not instance:
215
        logging.debug("Unknown instance IP: %s", instance_ip)
216
        pnodes_list.append(QUERY_UNKNOWN_ENTRY_ERROR)
217
        continue
218

    
219
      pnode = self.reader.GetInstancePrimaryNode(instance)
220
      if not pnode:
221
        logging.error("Instance '%s' doesn't have an associated primary"
222
                      " node", instance)
223
        pnodes_list.append(QUERY_INTERNAL_ERROR)
224
        continue
225

    
226
      pnode_primary_ip = self.reader.GetNodePrimaryIp(pnode)
227
      if not pnode_primary_ip:
228
        logging.error("Primary node '%s' doesn't have an associated"
229
                      " primary IP", pnode)
230
        pnodes_list.append(QUERY_INTERNAL_ERROR)
231
        continue
232

    
233
      pnodes_list.append((constants.CONFD_REPL_STATUS_OK, pnode_primary_ip))
234

    
235
    # If a single ip was requested, return a single answer, otherwise
236
    # the whole list, with a success status (since each entry has its
237
    # own success/failure)
238
    if mode == constants.CONFD_REQQ_IP:
239
      return pnodes_list[0]
240

    
241
    return constants.CONFD_REPL_STATUS_OK, pnodes_list
242

    
243

    
244
class NodesPipsQuery(ConfdQuery):
245
  """A query for nodes primary IPs.
246

247
  It returns the list of nodes primary IPs.
248

249
  """
250
  def Exec(self, query):
251
    """NodesPipsQuery main execution.
252

253
    """
254
    if query is None:
255
      status = constants.CONFD_REPL_STATUS_OK
256
      answer = self.reader.GetNodesPrimaryIps()
257
    else:
258
      status = constants.CONFD_REPL_STATUS_ERROR
259
      answer = "non-empty node primary IPs query"
260

    
261
    return status, answer
262

    
263

    
264
class MasterCandidatesPipsQuery(ConfdQuery):
265
  """A query for master candidates primary IPs.
266

267
  It returns the list of master candidates primary IPs.
268

269
  """
270
  def Exec(self, query):
271
    """MasterCandidatesPipsQuery main execution.
272

273
    """
274
    if query is None:
275
      status = constants.CONFD_REPL_STATUS_OK
276
      answer = self.reader.GetMasterCandidatesPrimaryIps()
277
    else:
278
      status = constants.CONFD_REPL_STATUS_ERROR
279
      answer = "non-empty master candidates primary IPs query"
280

    
281
    return status, answer
282

    
283

    
284
class InstancesIpsQuery(ConfdQuery):
285
  """A query for instances IPs.
286

287
  It returns the list of IPs of NICs connected to the requested link or all the
288
  instances IPs if no link is submitted.
289

290
  """
291
  def Exec(self, query):
292
    """InstancesIpsQuery main execution.
293

294
    """
295
    link = query
296
    status = constants.CONFD_REPL_STATUS_OK
297
    answer = self.reader.GetInstancesIps(link)
298

    
299
    return status, answer