Statistics
| Branch: | Tag: | Revision:

root / lib / confd / querylib.py @ cd195419

History | View | Annotate | Download (6.7 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

    
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