Statistics
| Branch: | Tag: | Revision:

root / lib / confd / server.py @ 48166551

History | View | Annotate | Download (5.2 kB)

1
#!/usr/bin/python
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
  }
56

    
57
  def __init__(self):
58
    """Constructor for ConfdProcessor
59

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

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

    
76
  def Disable(self):
77
    self.disabled = True
78
    self.reader = None
79

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

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

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

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

106
    This functions also performs signature/timestamp validation.
107

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

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

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

    
132
    return request
133

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

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

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

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

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

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

    
165
    logging.debug("Sending reply: %s" % reply)
166

    
167
    return (reply, rsalt)
168

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

172
    @type reply: L{objects.ConfdReply}
173
    @type rsalt: string
174

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