Statistics
| Branch: | Tag: | Revision:

root / lib / confd / querylib.py @ 23057d29

History | View | Annotate | Download (7.9 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 query is None:
102
      status = constants.CONFD_REPL_STATUS_OK
103
      answer = self.reader.GetMasterNode()
104
    else:
105
      status = constants.CONFD_REPL_STATUS_ERROR
106
      answer = 'master query accepts no query argument'
107

    
108
    return status, answer
109

    
110

    
111
class NodeRoleQuery(ConfdQuery):
112
  """A query for the role of a node.
113

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

116
  """
117
  def Exec(self, query):
118
    """EmptyQuery main execution
119

120
    """
121
    node = query
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)
127
    if flags is None:
128
      return QUERY_UNKNOWN_ENTRY_ERROR
129

    
130
    master_candidate, drained, offline = flags
131
    if master_candidate:
132
      answer = constants.CONFD_NODE_ROLE_CANDIDATE
133
    elif drained:
134
      answer = constants.CONFD_NODE_ROLE_DRAINED
135
    elif offline:
136
      answer = constants.CONFD_NODE_ROLE_OFFLINE
137
    else:
138
      answer = constants.CONFD_NODE_ROLE_REGULAR
139

    
140
    return constants.CONFD_REPL_STATUS_OK, answer
141

    
142

    
143
class InstanceIpToNodePrimaryIpQuery(ConfdQuery):
144
  """A query for the location of one or more instance's ips.
145

146
  """
147
  def Exec(self, query):
148
    """InstanceIpToNodePrimaryIpQuery main execution.
149

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)...])
158

159
    """
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
167
      else:
168
        status = constants.CONFD_REPL_STATUS_ERROR
169
        logging.debug("missing IP or IPLIST in query dict")
170
        return QUERY_ARGUMENT_ERROR
171

    
172
      if constants.CONFD_REQQ_LINK in query:
173
        network_link = query[constants.CONFD_REQQ_LINK]
174
      else:
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]
179
      network_link = None
180
      mode = constants.CONFD_REQQ_IP
181
    else:
182
      logging.debug("Invalid query argument type for: %s" % query)
183
      return QUERY_ARGUMENT_ERROR
184

    
185
    pnodes_list = []
186

    
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
191

    
192
      instance = self.reader.GetInstanceByLinkIp(instance_ip, network_link)
193
      if not instance:
194
        logging.debug("Unknown instance IP: %s" % instance_ip)
195
        pnodes_list.append(QUERY_UNKNOWN_ENTRY_ERROR)
196
        continue
197

    
198
      pnode = self.reader.GetInstancePrimaryNode(instance)
199
      if not pnode:
200
        logging.error("Instance '%s' doesn't have an associated primary"
201
                      " node" % instance)
202
        pnodes_list.append(QUERY_INTERNAL_ERROR)
203
        continue
204

    
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)
210
        continue
211

    
212
      pnodes_list.append((constants.CONFD_REPL_STATUS_OK, pnode_primary_ip))
213

    
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]
218

    
219
    return constants.CONFD_REPL_STATUS_OK, pnodes_list
220

    
221

    
222
class NodesPipsQuery(ConfdQuery):
223
  """A query for nodes primary IPs.
224

225
  It returns the list of nodes primary IPs.
226

227
  """
228
  def Exec(self, query):
229
    """NodesPipsQuery main execution.
230

231
    """
232
    if query is None:
233
      status = constants.CONFD_REPL_STATUS_OK
234
      answer = self.reader.GetNodesPrimaryIps()
235
    else:
236
      status = constants.CONFD_REPL_STATUS_ERROR
237
      answer = "non-empty node primary IPs query"
238

    
239
    return status, answer
240

    
241

    
242
class MasterCandidatesPipsQuery(ConfdQuery):
243
  """A query for master candidates primary IPs.
244

245
  It returns the list of master candidates primary IPs.
246

247
  """
248
  def Exec(self, query):
249
    """MasterCandidatesPipsQuery main execution.
250

251
    """
252
    if query is None:
253
      status = constants.CONFD_REPL_STATUS_OK
254
      answer = self.reader.GetMasterCandidatesPrimaryIps()
255
    else:
256
      status = constants.CONFD_REPL_STATUS_ERROR
257
      answer = "non-empty master candidates primary IPs query"
258

    
259
    return status, answer
260

    
261

    
262
class InstancesIpsQuery(ConfdQuery):
263
  """A query for instances IPs.
264

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.
267

268
  """
269
  def Exec(self, query):
270
    """InstancesIpsQuery main execution.
271

272
    """
273
    link = query
274
    status = constants.CONFD_REPL_STATUS_OK
275
    answer = self.reader.GetInstancesIps(link)
276

    
277
    return status, answer