Remove unused imports from confd files
[ganeti-local] / lib / confd / server.py
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
38
39 class ConfdProcessor(object):
40   """A processor for confd requests.
41
42   """
43   DISPATCH_TABLE = {
44   }
45
46   def __init__(self, reader):
47     """Constructor for ConfdAsyncUDPServer
48
49     @type reader: L{ssconf.SimpleConfigReader}
50     @param reader: ConfigReader to use to access the config
51
52     """
53     self.reader = reader
54     self.hmac_key = utils.ReadFile(constants.HMAC_CLUSTER_KEY)
55
56   def ExecQuery(self, payload_in, ip, port):
57     """Process a single UDP request from a client.
58
59     @type payload_in: string
60     @param payload_in: request raw data
61     @type ip: string
62     @param ip: source ip address
63     @param port: integer
64     @type port: source port
65
66     """
67     try:
68       request = self.ExtractRequest(payload_in)
69       reply, rsalt = self.ProcessRequest(request)
70       payload_out = self.PackReply(reply, rsalt)
71       return payload_out
72     except errors.ConfdRequestError, err:
73       logging.info('Ignoring broken query from %s:%d: %s' % (ip, port, err))
74       return None
75
76   def ExtractRequest(self, payload):
77     """Extracts a ConfdRequest object from a serialized hmac signed string.
78
79     This functions also performs signature/timestamp validation.
80
81     """
82     current_time = time.time()
83     logging.debug("Extracting request with size: %d" % (len(payload)))
84     try:
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)
89     try:
90       message_timestamp = int(salt)
91     except (ValueError, TypeError):
92       msg = "non-integer timestamp: %s" % salt
93       raise errors.ConfdRequestError(msg)
94
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)
99
100     try:
101       request = objects.ConfdRequest.FromDict(message)
102     except AttributeError, err:
103       raise errors.ConfdRequestError('%s' % err)
104
105     return request
106
107   def ProcessRequest(self, request):
108     """Process one ConfdRequest request, and produce an answer
109
110     @type request: L{objects.ConfdRequest}
111     @rtype: (L{objects.ConfdReply}, string)
112     @return: tuple of reply and salt to add to the signature
113
114     """
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)
119
120     if request.type not in constants.CONFD_REQS:
121       msg = "wrong request type %d" % request.type
122       raise errors.ConfdRequestError(msg)
123
124     rsalt = request.rsalt
125     if not rsalt:
126       msg = "missing requested salt"
127       raise errors.ConfdRequestError(msg)
128
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,
134                 status=status,
135                 answer=answer,
136                 )
137     else:
138       # TODO: actually dispatch queries to some classes to handle them
139       assert False, "DISPATCH_TABLE is populated but handler is not"
140
141     logging.debug("Sending reply: %s" % reply)
142
143     return (reply, rsalt)
144
145   def PackReply(self, reply, rsalt):
146     """Serialize and sign the given reply, with salt rsalt
147
148     @type reply: L{objects.ConfdReply}
149     @type rsalt: string
150
151     """
152     return serializer.DumpSigned(reply.ToDict(), self.hmac_key, rsalt)
153