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
37 from ganeti import ssconf
39 from ganeti.confd import querylib
42 class ConfdProcessor(object):
43 """A processor for confd requests.
45 @ivar reader: confd SimpleConfigReader
46 @ivar disabled: whether confd serving is disabled
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 constants.CONFD_REQ_INSTANCES_IPS_LIST: querylib.InstancesIpsQuery,
61 """Constructor for ConfdProcessor
65 self.hmac_key = utils.ReadFile(constants.CONFD_HMAC_KEY)
68 not constants.CONFD_REQS.symmetric_difference(self.DISPATCH_TABLE), \
69 "DISPATCH_TABLE is unaligned with CONFD_REQS"
73 self.reader = ssconf.SimpleConfigReader()
75 except errors.ConfigurationError:
83 def ExecQuery(self, payload_in, ip, port):
84 """Process a single UDP request from a client.
86 @type payload_in: string
87 @param payload_in: request raw data
89 @param ip: source ip address
91 @type port: source port
95 logging.debug('Confd is disabled. Ignoring query.')
98 request = self.ExtractRequest(payload_in)
99 reply, rsalt = self.ProcessRequest(request)
100 payload_out = self.PackReply(reply, rsalt)
102 except errors.ConfdRequestError, err:
103 logging.info('Ignoring broken query from %s:%d: %s', ip, port, err)
106 def ExtractRequest(self, payload):
107 """Extracts a ConfdRequest object from a serialized hmac signed string.
109 This functions also performs signature/timestamp validation.
112 current_time = time.time()
113 logging.debug("Extracting request with size: %d", len(payload))
115 (message, salt) = serializer.LoadSigned(payload, self.hmac_key)
116 except errors.SignatureError, err:
117 msg = "invalid signature: %s" % err
118 raise errors.ConfdRequestError(msg)
120 message_timestamp = int(salt)
121 except (ValueError, TypeError):
122 msg = "non-integer timestamp: %s" % salt
123 raise errors.ConfdRequestError(msg)
125 skew = abs(current_time - message_timestamp)
126 if skew > constants.CONFD_MAX_CLOCK_SKEW:
127 msg = "outside time range (skew: %d)" % skew
128 raise errors.ConfdRequestError(msg)
131 request = objects.ConfdRequest.FromDict(message)
132 except AttributeError, err:
133 raise errors.ConfdRequestError('%s' % err)
137 def ProcessRequest(self, request):
138 """Process one ConfdRequest request, and produce an answer
140 @type request: L{objects.ConfdRequest}
141 @rtype: (L{objects.ConfdReply}, string)
142 @return: tuple of reply and salt to add to the signature
145 logging.debug("Processing request: %s", request)
146 if request.protocol != constants.CONFD_PROTOCOL_VERSION:
147 msg = "wrong protocol version %d" % request.protocol
148 raise errors.ConfdRequestError(msg)
150 if request.type not in constants.CONFD_REQS:
151 msg = "wrong request type %d" % request.type
152 raise errors.ConfdRequestError(msg)
154 rsalt = request.rsalt
156 msg = "missing requested salt"
157 raise errors.ConfdRequestError(msg)
159 query_object = self.DISPATCH_TABLE[request.type](self.reader)
160 status, answer = query_object.Exec(request.query)
161 reply = objects.ConfdReply(
162 protocol=constants.CONFD_PROTOCOL_VERSION,
165 serial=self.reader.GetConfigSerialNo(),
168 logging.debug("Sending reply: %s", reply)
170 return (reply, rsalt)
172 def PackReply(self, reply, rsalt):
173 """Serialize and sign the given reply, with salt rsalt
175 @type reply: L{objects.ConfdReply}
179 return serializer.DumpSigned(reply.ToDict(), self.hmac_key, rsalt)