4 # Copyright (C) 2009, Google Inc.
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.
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.
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
22 """Ganeti configuration daemon server library.
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.
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
39 class ConfdProcessor(object):
40 """A processor for confd requests.
46 def __init__(self, reader):
47 """Constructor for ConfdAsyncUDPServer
49 @type reader: L{ssconf.SimpleConfigReader}
50 @param reader: ConfigReader to use to access the config
54 self.hmac_key = utils.ReadFile(constants.HMAC_CLUSTER_KEY)
56 def ExecQuery(self, payload_in, ip, port):
57 """Process a single UDP request from a client.
59 @type payload_in: string
60 @param payload_in: request raw data
62 @param ip: source ip address
64 @type port: source port
68 request = self.ExtractRequest(payload_in)
69 reply, rsalt = self.ProcessRequest(request)
70 payload_out = self.PackReply(reply, rsalt)
72 except errors.ConfdRequestError, err:
73 logging.info('Ignoring broken query from %s:%d: %s' % (ip, port, err))
76 def ExtractRequest(self, payload):
77 """Extracts a ConfdRequest object from a serialized hmac signed string.
79 This functions also performs signature/timestamp validation.
82 current_time = time.time()
83 logging.debug("Extracting request with size: %d" % (len(payload)))
85 (message, salt) = serializer.LoadSigned(payload, self.hmac_key)
86 except errors.SignatureError, err:
87 msg = "invalid signature: %s" % err
88 raise errors.ConfdRequestError(msg)
90 message_timestamp = int(salt)
91 except (ValueError, TypeError):
92 msg = "non-integer timestamp: %s" % salt
93 raise errors.ConfdRequestError(msg)
95 skew = abs(current_time - message_timestamp)
96 if skew > constants.CONFD_MAX_CLOCK_SKEW:
97 msg = "outside time range (skew: %d)" % skew
98 raise errors.ConfdRequestError(msg)
101 request = objects.ConfdRequest.FromDict(message)
102 except AttributeError, err:
103 raise errors.ConfdRequestError('%s' % err)
107 def ProcessRequest(self, request):
108 """Process one ConfdRequest request, and produce an answer
110 @type request: L{objects.ConfdRequest}
111 @rtype: (L{objects.ConfdReply}, string)
112 @return: tuple of reply and salt to add to the signature
115 logging.debug("Processing request: %s" % request)
116 if request.protocol != constants.CONFD_PROTOCOL_VERSION:
117 msg = "wrong protocol version %d" % request.protocol
118 raise errors.ConfdRequestError(msg)
120 if request.type not in constants.CONFD_REQS:
121 msg = "wrong request type %d" % request.type
122 raise errors.ConfdRequestError(msg)
124 rsalt = request.rsalt
126 msg = "missing requested salt"
127 raise errors.ConfdRequestError(msg)
129 if request.type not in self.DISPATCH_TABLE:
130 answer = 'not implemented'
131 status = constants.CONFD_REPL_STATUS_NOTIMPLEMENTED
132 reply = objects.ConfdReply(
133 protocol=constants.CONFD_PROTOCOL_VERSION,
138 # TODO: actually dispatch queries to some classes to handle them
139 assert False, "DISPATCH_TABLE is populated but handler is not"
141 logging.debug("Sending reply: %s" % reply)
143 return (reply, rsalt)
145 def PackReply(self, reply, rsalt):
146 """Serialize and sign the given reply, with salt rsalt
148 @type reply: L{objects.ConfdReply}
152 return serializer.DumpSigned(reply.ToDict(), self.hmac_key, rsalt)