Statistics
| Branch: | Tag: | Revision:

root / lib / confd / server.py @ 6daf26a0

History | View | Annotate | Download (4.8 kB)

1 71f27d19 Guido Trotter
#!/usr/bin/python
2 71f27d19 Guido Trotter
#
3 71f27d19 Guido Trotter
4 71f27d19 Guido Trotter
# Copyright (C) 2009, Google Inc.
5 71f27d19 Guido Trotter
#
6 71f27d19 Guido Trotter
# This program is free software; you can redistribute it and/or modify
7 71f27d19 Guido Trotter
# it under the terms of the GNU General Public License as published by
8 71f27d19 Guido Trotter
# the Free Software Foundation; either version 2 of the License, or
9 71f27d19 Guido Trotter
# (at your option) any later version.
10 71f27d19 Guido Trotter
#
11 71f27d19 Guido Trotter
# This program is distributed in the hope that it will be useful, but
12 71f27d19 Guido Trotter
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 71f27d19 Guido Trotter
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 71f27d19 Guido Trotter
# General Public License for more details.
15 71f27d19 Guido Trotter
#
16 71f27d19 Guido Trotter
# You should have received a copy of the GNU General Public License
17 71f27d19 Guido Trotter
# along with this program; if not, write to the Free Software
18 71f27d19 Guido Trotter
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 71f27d19 Guido Trotter
# 02110-1301, USA.
20 71f27d19 Guido Trotter
21 71f27d19 Guido Trotter
22 71f27d19 Guido Trotter
"""Ganeti configuration daemon server library.
23 71f27d19 Guido Trotter

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

27 71f27d19 Guido Trotter
"""
28 71f27d19 Guido Trotter
29 71f27d19 Guido Trotter
import logging
30 71f27d19 Guido Trotter
import time
31 71f27d19 Guido Trotter
32 71f27d19 Guido Trotter
from ganeti import constants
33 71f27d19 Guido Trotter
from ganeti import objects
34 71f27d19 Guido Trotter
from ganeti import errors
35 71f27d19 Guido Trotter
from ganeti import utils
36 71f27d19 Guido Trotter
from ganeti import serializer
37 71f27d19 Guido Trotter
38 e16e4824 Guido Trotter
from ganeti.confd import querylib
39 e16e4824 Guido Trotter
40 71f27d19 Guido Trotter
41 71f27d19 Guido Trotter
class ConfdProcessor(object):
42 71f27d19 Guido Trotter
  """A processor for confd requests.
43 71f27d19 Guido Trotter

44 71f27d19 Guido Trotter
  """
45 71f27d19 Guido Trotter
  DISPATCH_TABLE = {
46 e16e4824 Guido Trotter
      constants.CONFD_REQ_PING: querylib.PingQuery,
47 6daf26a0 Guido Trotter
      constants.CONFD_REQ_NODE_ROLE_BYNAME: querylib.NodeRoleQuery,
48 e16e4824 Guido Trotter
      constants.CONFD_REQ_NODE_PIP_BY_INSTANCE_IP: querylib.ConfdQuery,
49 71f27d19 Guido Trotter
  }
50 71f27d19 Guido Trotter
51 71f27d19 Guido Trotter
  def __init__(self, reader):
52 12ce965f Guido Trotter
    """Constructor for ConfdProcessor
53 71f27d19 Guido Trotter

54 71f27d19 Guido Trotter
    @type reader: L{ssconf.SimpleConfigReader}
55 71f27d19 Guido Trotter
    @param reader: ConfigReader to use to access the config
56 71f27d19 Guido Trotter

57 71f27d19 Guido Trotter
    """
58 71f27d19 Guido Trotter
    self.reader = reader
59 71f27d19 Guido Trotter
    self.hmac_key = utils.ReadFile(constants.HMAC_CLUSTER_KEY)
60 d21eda27 Guido Trotter
    assert \
61 d21eda27 Guido Trotter
      not constants.CONFD_REQS.symmetric_difference(self.DISPATCH_TABLE), \
62 d21eda27 Guido Trotter
      "DISPATCH_TABLE is unaligned with CONFD_REQS"
63 71f27d19 Guido Trotter
64 71f27d19 Guido Trotter
  def ExecQuery(self, payload_in, ip, port):
65 71f27d19 Guido Trotter
    """Process a single UDP request from a client.
66 71f27d19 Guido Trotter

67 71f27d19 Guido Trotter
    @type payload_in: string
68 71f27d19 Guido Trotter
    @param payload_in: request raw data
69 71f27d19 Guido Trotter
    @type ip: string
70 71f27d19 Guido Trotter
    @param ip: source ip address
71 71f27d19 Guido Trotter
    @param port: integer
72 71f27d19 Guido Trotter
    @type port: source port
73 71f27d19 Guido Trotter

74 71f27d19 Guido Trotter
    """
75 71f27d19 Guido Trotter
    try:
76 71f27d19 Guido Trotter
      request = self.ExtractRequest(payload_in)
77 71f27d19 Guido Trotter
      reply, rsalt = self.ProcessRequest(request)
78 71f27d19 Guido Trotter
      payload_out = self.PackReply(reply, rsalt)
79 71f27d19 Guido Trotter
      return payload_out
80 71f27d19 Guido Trotter
    except errors.ConfdRequestError, err:
81 71f27d19 Guido Trotter
      logging.info('Ignoring broken query from %s:%d: %s' % (ip, port, err))
82 71f27d19 Guido Trotter
      return None
83 71f27d19 Guido Trotter
84 71f27d19 Guido Trotter
  def ExtractRequest(self, payload):
85 71f27d19 Guido Trotter
    """Extracts a ConfdRequest object from a serialized hmac signed string.
86 71f27d19 Guido Trotter

87 71f27d19 Guido Trotter
    This functions also performs signature/timestamp validation.
88 71f27d19 Guido Trotter

89 71f27d19 Guido Trotter
    """
90 71f27d19 Guido Trotter
    current_time = time.time()
91 71f27d19 Guido Trotter
    logging.debug("Extracting request with size: %d" % (len(payload)))
92 71f27d19 Guido Trotter
    try:
93 71f27d19 Guido Trotter
      (message, salt) = serializer.LoadSigned(payload, self.hmac_key)
94 71f27d19 Guido Trotter
    except errors.SignatureError, err:
95 71f27d19 Guido Trotter
      msg = "invalid signature: %s" % err
96 71f27d19 Guido Trotter
      raise errors.ConfdRequestError(msg)
97 71f27d19 Guido Trotter
    try:
98 71f27d19 Guido Trotter
      message_timestamp = int(salt)
99 71f27d19 Guido Trotter
    except (ValueError, TypeError):
100 71f27d19 Guido Trotter
      msg = "non-integer timestamp: %s" % salt
101 71f27d19 Guido Trotter
      raise errors.ConfdRequestError(msg)
102 71f27d19 Guido Trotter
103 71f27d19 Guido Trotter
    skew = abs(current_time - message_timestamp)
104 71f27d19 Guido Trotter
    if skew > constants.CONFD_MAX_CLOCK_SKEW:
105 71f27d19 Guido Trotter
      msg = "outside time range (skew: %d)" % skew
106 71f27d19 Guido Trotter
      raise errors.ConfdRequestError(msg)
107 71f27d19 Guido Trotter
108 71f27d19 Guido Trotter
    try:
109 71f27d19 Guido Trotter
      request = objects.ConfdRequest.FromDict(message)
110 71f27d19 Guido Trotter
    except AttributeError, err:
111 71f27d19 Guido Trotter
      raise errors.ConfdRequestError('%s' % err)
112 71f27d19 Guido Trotter
113 71f27d19 Guido Trotter
    return request
114 71f27d19 Guido Trotter
115 71f27d19 Guido Trotter
  def ProcessRequest(self, request):
116 71f27d19 Guido Trotter
    """Process one ConfdRequest request, and produce an answer
117 71f27d19 Guido Trotter

118 71f27d19 Guido Trotter
    @type request: L{objects.ConfdRequest}
119 71f27d19 Guido Trotter
    @rtype: (L{objects.ConfdReply}, string)
120 71f27d19 Guido Trotter
    @return: tuple of reply and salt to add to the signature
121 71f27d19 Guido Trotter

122 71f27d19 Guido Trotter
    """
123 71f27d19 Guido Trotter
    logging.debug("Processing request: %s" % request)
124 71f27d19 Guido Trotter
    if request.protocol != constants.CONFD_PROTOCOL_VERSION:
125 71f27d19 Guido Trotter
      msg = "wrong protocol version %d" % request.protocol
126 71f27d19 Guido Trotter
      raise errors.ConfdRequestError(msg)
127 71f27d19 Guido Trotter
128 71f27d19 Guido Trotter
    if request.type not in constants.CONFD_REQS:
129 71f27d19 Guido Trotter
      msg = "wrong request type %d" % request.type
130 71f27d19 Guido Trotter
      raise errors.ConfdRequestError(msg)
131 71f27d19 Guido Trotter
132 71f27d19 Guido Trotter
    rsalt = request.rsalt
133 71f27d19 Guido Trotter
    if not rsalt:
134 71f27d19 Guido Trotter
      msg = "missing requested salt"
135 71f27d19 Guido Trotter
      raise errors.ConfdRequestError(msg)
136 71f27d19 Guido Trotter
137 e16e4824 Guido Trotter
    query_object = self.DISPATCH_TABLE[request.type](self.reader)
138 e16e4824 Guido Trotter
    status, answer = query_object.Exec(request.query)
139 e16e4824 Guido Trotter
    reply = objects.ConfdReply(
140 e16e4824 Guido Trotter
              protocol=constants.CONFD_PROTOCOL_VERSION,
141 e16e4824 Guido Trotter
              status=status,
142 e16e4824 Guido Trotter
              answer=answer,
143 e16e4824 Guido Trotter
              serial=self.reader.GetConfigSerialNo(),
144 e16e4824 Guido Trotter
              )
145 71f27d19 Guido Trotter
146 71f27d19 Guido Trotter
    logging.debug("Sending reply: %s" % reply)
147 71f27d19 Guido Trotter
148 71f27d19 Guido Trotter
    return (reply, rsalt)
149 71f27d19 Guido Trotter
150 71f27d19 Guido Trotter
  def PackReply(self, reply, rsalt):
151 71f27d19 Guido Trotter
    """Serialize and sign the given reply, with salt rsalt
152 71f27d19 Guido Trotter

153 71f27d19 Guido Trotter
    @type reply: L{objects.ConfdReply}
154 71f27d19 Guido Trotter
    @type rsalt: string
155 71f27d19 Guido Trotter

156 71f27d19 Guido Trotter
    """
157 71f27d19 Guido Trotter
    return serializer.DumpSigned(reply.ToDict(), self.hmac_key, rsalt)