1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """Ganeti configuration daemon
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
30
31
32 import os
33 import sys
34 import logging
35 import time
36
37 try:
38
39 from pyinotify import pyinotify
40 except ImportError:
41 import pyinotify
42
43 from optparse import OptionParser
44
45 from ganeti import asyncnotifier
46 from ganeti import confd
47 from ganeti.confd import server as confd_server
48 from ganeti import constants
49 from ganeti import errors
50 from ganeti import daemon
51 from ganeti import netutils
52
53
55 """The confd udp server, suitable for use with asyncore.
56
57 """
58 - def __init__(self, bind_address, port, processor):
59 """Constructor for ConfdAsyncUDPServer
60
61 @type bind_address: string
62 @param bind_address: socket bind address
63 @type port: int
64 @param port: udp port
65 @type processor: L{confd.server.ConfdProcessor}
66 @param processor: ConfdProcessor to use to handle queries
67
68 """
69 family = netutils.IPAddress.GetAddressFamily(bind_address)
70 daemon.AsyncUDPSocket.__init__(self, family)
71 self.bind_address = bind_address
72 self.port = port
73 self.processor = processor
74 self.bind((bind_address, port))
75 logging.debug("listening on ('%s':%d)", bind_address, port)
76
77
91
92
94 """Logic to control when to reload the ganeti configuration
95
96 This class is able to alter between inotify and polling, to rate-limit the
97 number of reloads. When using inotify it also supports a fallback timed
98 check, to verify that the reload hasn't failed.
99
100 """
101 - def __init__(self, processor, mainloop):
102 """Constructor for ConfdConfigurationReloader
103
104 @type processor: L{confd.server.ConfdProcessor}
105 @param processor: ganeti-confd ConfdProcessor
106 @type mainloop: L{daemon.Mainloop}
107 @param mainloop: ganeti-confd mainloop
108
109 """
110 self.processor = processor
111 self.mainloop = mainloop
112
113 self.polling = True
114 self.last_notification = 0
115
116
117 cfg_file = constants.CLUSTER_CONF_FILE
118 self.wm = pyinotify.WatchManager()
119 self.inotify_handler = asyncnotifier.SingleFileEventHandler(self.wm,
120 self.OnInotify,
121 cfg_file)
122 notifier_class = asyncnotifier.ErrorLoggingAsyncNotifier
123 self.notifier = notifier_class(self.wm, self.inotify_handler)
124
125 self.timer_handle = None
126 self._EnableTimer()
127
129 """Receive an inotify notification.
130
131 @type notifier_enabled: boolean
132 @param notifier_enabled: whether the notifier is still enabled
133
134 """
135 current_time = time.time()
136 time_delta = current_time - self.last_notification
137 self.last_notification = current_time
138
139 if time_delta < constants.CONFD_CONFIG_RELOAD_RATELIMIT:
140 logging.debug("Moving from inotify mode to polling mode")
141 self.polling = True
142 if notifier_enabled:
143 self.inotify_handler.disable()
144
145 if not self.polling and not notifier_enabled:
146 try:
147 self.inotify_handler.enable()
148 except errors.InotifyError:
149 self.polling = True
150
151 try:
152 reloaded = self.processor.reader.Reload()
153 if reloaded:
154 logging.info("Reloaded ganeti config")
155 else:
156 logging.debug("Skipped double config reload")
157 except errors.ConfigurationError:
158 self.DisableConfd()
159 self.inotify_handler.disable()
160 return
161
162
163
164 self._ResetTimer()
165
167 if self.timer_handle is not None:
168 self.mainloop.scheduler.cancel(self.timer_handle)
169 self.timer_handle = None
170
180
184
186 """Function called when the timer fires
187
188 """
189 self.timer_handle = None
190 reloaded = False
191 was_disabled = False
192 try:
193 if self.processor.reader is None:
194 was_disabled = True
195 self.EnableConfd()
196 reloaded = True
197 else:
198 reloaded = self.processor.reader.Reload()
199 except errors.ConfigurationError:
200 self.DisableConfd(silent=was_disabled)
201 return
202
203 if self.polling and reloaded:
204 logging.info("Reloaded ganeti config")
205 elif reloaded:
206
207
208
209 if not self.notifier.check_events() and not was_disabled:
210 logging.warning("Config file reload at timeout (inotify failure)")
211 elif self.polling:
212
213
214 logging.debug("Moving from polling mode to inotify mode")
215 self.polling = False
216 try:
217 self.inotify_handler.enable()
218 except errors.InotifyError:
219 self.polling = True
220 else:
221 logging.debug("Performed configuration check")
222
223 self._EnableTimer()
224
226 """Puts confd in non-serving mode
227
228 """
229 if not silent:
230 logging.warning("Confd is being disabled")
231 self.processor.Disable()
232 self.polling = False
233 self._ResetTimer()
234
236 self.processor.Enable()
237 logging.warning("Confd is being enabled")
238 self.polling = True
239 self._ResetTimer()
240
241
255
256
257
258
259
260
286
287
289 """Main confd function, executed with PID file held
290
291 """
292 mainloop = prep_data
293 mainloop.Run()
294
295
297 """Main function for the confd daemon.
298
299 """
300 parser = OptionParser(description="Ganeti configuration daemon",
301 usage="%prog [-f] [-d] [-b ADDRESS]",
302 version="%%prog (ganeti) %s" %
303 constants.RELEASE_VERSION)
304
305 daemon.GenericMain(constants.CONFD, parser, CheckConfd, PrepConfd, ExecConfd)
306