Statistics
| Branch: | Tag: | Revision:

root / lib / confd / server.py @ efbb4fd2

History | View | Annotate | Download (5.3 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 server library.
23

24
Ganeti-confd is a daemon to query master candidates for configuration values.
25
It uses UDP+HMAC for authentication with a global cluster key.
26

27
"""
28

    
29
import logging
30
import time
31

    
32
from ganeti import constants
33
from ganeti import objects
34
from ganeti import errors
35
from ganeti import utils
36
from ganeti import serializer
37
from ganeti import ssconf
38

    
39
from ganeti.confd import querylib
40

    
41

    
42
class ConfdProcessor(object):
43
  """A processor for confd requests.
44

45
  @ivar reader: confd SimpleConfigReader
46
  @ivar disabled: whether confd serving is disabled
47

48
  """
49
  DISPATCH_TABLE = {
50
    constants.CONFD_REQ_PING: querylib.PingQuery,
51
    constants.CONFD_REQ_NODE_ROLE_BYNAME: querylib.NodeRoleQuery,
52
    constants.CONFD_REQ_NODE_PIP_BY_INSTANCE_IP:
53
      querylib.InstanceIpToNodePrimaryIpQuery,
54
    constants.CONFD_REQ_CLUSTER_MASTER: querylib.ClusterMasterQuery,
55
    constants.CONFD_REQ_NODE_PIP_LIST: querylib.NodesPipsQuery,
56
    constants.CONFD_REQ_MC_PIP_LIST: querylib.MasterCandidatesPipsQuery,
57
    }
58

    
59
  def __init__(self):
60
    """Constructor for ConfdProcessor
61

62
    """
63
    self.disabled = True
64
    self.hmac_key = utils.ReadFile(constants.HMAC_CLUSTER_KEY)
65
    self.reader = None
66
    assert \
67
      not constants.CONFD_REQS.symmetric_difference(self.DISPATCH_TABLE), \
68
      "DISPATCH_TABLE is unaligned with CONFD_REQS"
69

    
70
  def Enable(self):
71
    try:
72
      self.reader = ssconf.SimpleConfigReader()
73
      self.disabled = False
74
    except errors.ConfigurationError:
75
      self.disabled = True
76
      raise
77

    
78
  def Disable(self):
79
    self.disabled = True
80
    self.reader = None
81

    
82
  def ExecQuery(self, payload_in, ip, port):
83
    """Process a single UDP request from a client.
84

85
    @type payload_in: string
86
    @param payload_in: request raw data
87
    @type ip: string
88
    @param ip: source ip address
89
    @param port: integer
90
    @type port: source port
91

92
    """
93
    if self.disabled:
94
      logging.debug('Confd is disabled. Ignoring query.')
95
      return
96
    try:
97
      request = self.ExtractRequest(payload_in)
98
      reply, rsalt = self.ProcessRequest(request)
99
      payload_out = self.PackReply(reply, rsalt)
100
      return payload_out
101
    except errors.ConfdRequestError, err:
102
      logging.info('Ignoring broken query from %s:%d: %s' % (ip, port, err))
103
      return None
104

    
105
  def ExtractRequest(self, payload):
106
    """Extracts a ConfdRequest object from a serialized hmac signed string.
107

108
    This functions also performs signature/timestamp validation.
109

110
    """
111
    current_time = time.time()
112
    logging.debug("Extracting request with size: %d" % (len(payload)))
113
    try:
114
      (message, salt) = serializer.LoadSigned(payload, self.hmac_key)
115
    except errors.SignatureError, err:
116
      msg = "invalid signature: %s" % err
117
      raise errors.ConfdRequestError(msg)
118
    try:
119
      message_timestamp = int(salt)
120
    except (ValueError, TypeError):
121
      msg = "non-integer timestamp: %s" % salt
122
      raise errors.ConfdRequestError(msg)
123

    
124
    skew = abs(current_time - message_timestamp)
125
    if skew > constants.CONFD_MAX_CLOCK_SKEW:
126
      msg = "outside time range (skew: %d)" % skew
127
      raise errors.ConfdRequestError(msg)
128

    
129
    try:
130
      request = objects.ConfdRequest.FromDict(message)
131
    except AttributeError, err:
132
      raise errors.ConfdRequestError('%s' % err)
133

    
134
    return request
135

    
136
  def ProcessRequest(self, request):
137
    """Process one ConfdRequest request, and produce an answer
138

139
    @type request: L{objects.ConfdRequest}
140
    @rtype: (L{objects.ConfdReply}, string)
141
    @return: tuple of reply and salt to add to the signature
142

143
    """
144
    logging.debug("Processing request: %s" % request)
145
    if request.protocol != constants.CONFD_PROTOCOL_VERSION:
146
      msg = "wrong protocol version %d" % request.protocol
147
      raise errors.ConfdRequestError(msg)
148

    
149
    if request.type not in constants.CONFD_REQS:
150
      msg = "wrong request type %d" % request.type
151
      raise errors.ConfdRequestError(msg)
152

    
153
    rsalt = request.rsalt
154
    if not rsalt:
155
      msg = "missing requested salt"
156
      raise errors.ConfdRequestError(msg)
157

    
158
    query_object = self.DISPATCH_TABLE[request.type](self.reader)
159
    status, answer = query_object.Exec(request.query)
160
    reply = objects.ConfdReply(
161
              protocol=constants.CONFD_PROTOCOL_VERSION,
162
              status=status,
163
              answer=answer,
164
              serial=self.reader.GetConfigSerialNo(),
165
              )
166

    
167
    logging.debug("Sending reply: %s" % reply)
168

    
169
    return (reply, rsalt)
170

    
171
  def PackReply(self, reply, rsalt):
172
    """Serialize and sign the given reply, with salt rsalt
173

174
    @type reply: L{objects.ConfdReply}
175
    @type rsalt: string
176

177
    """
178
    return serializer.DumpSigned(reply.ToDict(), self.hmac_key, rsalt)