Revision c666f1f4

b/daemons/ganeti-confd
88 88
        logging.error("Reply too big to fit in an udp packet.")
89 89

  
90 90

  
91
class ConfdInotifyEventHandler(pyinotify.ProcessEvent):
92

  
93
  def __init__(self, watch_manager, callback,
94
               filename=constants.CLUSTER_CONF_FILE):
95
    """Constructor for ConfdInotifyEventHandler
96

  
97
    @type watch_manager: pyinotify.WatchManager
98
    @param watch_manager: ganeti-confd inotify watch manager
99
    @type callback: function accepting a boolean
100
    @param callback: function to call when an inotify event happens
101
    @type filename: string
102
    @param filename: config file to watch
103

  
104
    """
105
    # pylint: disable-msg=W0231
106
    # no need to call the parent's constructor
107
    self.watch_manager = watch_manager
108
    self.callback = callback
109
    self.mask = pyinotify.EventsCodes.ALL_FLAGS["IN_IGNORED"] | \
110
                pyinotify.EventsCodes.ALL_FLAGS["IN_MODIFY"]
111
    self.file = filename
112
    self.watch_handle = None
113

  
114
  def enable(self):
115
    """Watch the given file
116

  
117
    """
118
    if self.watch_handle is None:
119
      result = self.watch_manager.add_watch(self.file, self.mask)
120
      if not self.file in result or result[self.file] <= 0:
121
        raise errors.InotifyError("Could not add inotify watcher")
122
      else:
123
        self.watch_handle = result[self.file]
124

  
125
  def disable(self):
126
    """Stop watching the given file
127

  
128
    """
129
    if self.watch_handle is not None:
130
      result = self.watch_manager.rm_watch(self.watch_handle)
131
      if result[self.watch_handle]:
132
        self.watch_handle = None
133

  
134
  def process_IN_IGNORED(self, event):
135
    # Due to the fact that we monitor just for the cluster config file (rather
136
    # than for the whole data dir) when the file is replaced with another one
137
    # (which is what happens normally in ganeti) we're going to receive an
138
    # IN_IGNORED event from inotify, because of the file removal (which is
139
    # contextual with the replacement). In such a case we need to create
140
    # another watcher for the "new" file.
141
    logging.debug("Received 'ignored' inotify event for %s", event.path)
142
    self.watch_handle = None
143

  
144
    try:
145
      # Since the kernel believes the file we were interested in is gone, it's
146
      # not going to notify us of any other events, until we set up, here, the
147
      # new watch. This is not a race condition, though, since we're anyway
148
      # going to realod the file after setting up the new watch.
149
      self.callback(False)
150
    except:
151
      # we need to catch any exception here, log it, but proceed, because even
152
      # if we failed handling a single request, we still want the confd to
153
      # continue working.
154
      logging.error("Unexpected exception", exc_info=True)
155

  
156
  def process_IN_MODIFY(self, event):
157
    # This gets called when the config file is modified. Note that this doesn't
158
    # usually happen in Ganeti, as the config file is normally replaced by a
159
    # new one, at filesystem level, rather than actually modified (see
160
    # utils.WriteFile)
161
    logging.debug("Received 'modify' inotify event for %s", event.path)
162

  
163
    try:
164
      self.callback(True)
165
    except:
166
      # we need to catch any exception here, log it, but proceed, because even
167
      # if we failed handling a single request, we still want the confd to
168
      # continue working.
169
      logging.error("Unexpected exception", exc_info=True)
170

  
171
  def process_default(self, event):
172
    logging.error("Received unhandled inotify event: %s", event)
173

  
174

  
175 91
class ConfdConfigurationReloader(object):
176 92
  """Logic to control when to reload the ganeti configuration
177 93

  
......
196 112
    self.last_notification = 0
197 113

  
198 114
    # Asyncronous inotify handler for config changes
115
    cfg_file = constants.CLUSTER_CONF_FILE
199 116
    self.wm = pyinotify.WatchManager()
200
    self.inotify_handler = ConfdInotifyEventHandler(self.wm, self.OnInotify)
117
    self.inotify_handler = asyncnotifier.SingleFileEventHandler(self.wm,
118
                                                                self.OnInotify,
119
                                                                cfg_file)
201 120
    self.notifier = asyncnotifier.AsyncNotifier(self.wm, self.inotify_handler)
202 121

  
203 122
    self.timer_handle = None
b/lib/asyncnotifier.py
23 23

  
24 24

  
25 25
import asyncore
26
import logging
26 27

  
27 28
try:
28 29
  # pylint: disable-msg=E0611
......
30 31
except ImportError:
31 32
  import pyinotify
32 33

  
34
from ganeti import errors
33 35

  
34 36
# We contributed the AsyncNotifier class back to python-pyinotify, and it's
35 37
# part of their codebase since version 0.8.7. This code can be removed once
......
61 63
  def handle_read(self):
62 64
    self.notifier.read_events()
63 65
    self.notifier.process_events()
66

  
67

  
68
class SingleFileEventHandler(pyinotify.ProcessEvent):
69
  """Handle modify events for a single file.
70

  
71
  """
72

  
73
  def __init__(self, watch_manager, callback, filename):
74
    """Constructor for SingleFileEventHandler
75

  
76
    @type watch_manager: pyinotify.WatchManager
77
    @param watch_manager: inotify watch manager
78
    @type callback: function accepting a boolean
79
    @param callback: function to call when an inotify event happens
80
    @type filename: string
81
    @param filename: config file to watch
82

  
83
    """
84
    # pylint: disable-msg=W0231
85
    # no need to call the parent's constructor
86
    self.watch_manager = watch_manager
87
    self.callback = callback
88
    self.mask = pyinotify.EventsCodes.ALL_FLAGS["IN_IGNORED"] | \
89
                pyinotify.EventsCodes.ALL_FLAGS["IN_MODIFY"]
90
    self.file = filename
91
    self.watch_handle = None
92

  
93
  def enable(self):
94
    """Watch the given file
95

  
96
    """
97
    if self.watch_handle is None:
98
      result = self.watch_manager.add_watch(self.file, self.mask)
99
      if not self.file in result or result[self.file] <= 0:
100
        raise errors.InotifyError("Could not add inotify watcher")
101
      else:
102
        self.watch_handle = result[self.file]
103

  
104
  def disable(self):
105
    """Stop watching the given file
106

  
107
    """
108
    if self.watch_handle is not None:
109
      result = self.watch_manager.rm_watch(self.watch_handle)
110
      if result[self.watch_handle]:
111
        self.watch_handle = None
112

  
113
  # pylint: disable-msg=C0103
114
  # this overrides a method in pyinotify.ProcessEvent
115
  def process_IN_IGNORED(self, event):
116
    # Due to the fact that we monitor just for the cluster config file (rather
117
    # than for the whole data dir) when the file is replaced with another one
118
    # (which is what happens normally in ganeti) we're going to receive an
119
    # IN_IGNORED event from inotify, because of the file removal (which is
120
    # contextual with the replacement). In such a case we need to create
121
    # another watcher for the "new" file.
122
    logging.debug("Received 'ignored' inotify event for %s", event.path)
123
    self.watch_handle = None
124

  
125
    try:
126
      # Since the kernel believes the file we were interested in is gone, it's
127
      # not going to notify us of any other events, until we set up, here, the
128
      # new watch. This is not a race condition, though, since we're anyway
129
      # going to realod the file after setting up the new watch.
130
      self.callback(False)
131
    except: # pylint: disable-msg=W0702
132
      # we need to catch any exception here, log it, but proceed, because even
133
      # if we failed handling a single request, we still want our daemon to
134
      # proceed.
135
      logging.error("Unexpected exception", exc_info=True)
136

  
137
  # pylint: disable-msg=C0103
138
  # this overrides a method in pyinotify.ProcessEvent
139
  def process_IN_MODIFY(self, event):
140
    # This gets called when the config file is modified. Note that this doesn't
141
    # usually happen in Ganeti, as the config file is normally replaced by a
142
    # new one, at filesystem level, rather than actually modified (see
143
    # utils.WriteFile)
144
    logging.debug("Received 'modify' inotify event for %s", event.path)
145

  
146
    try:
147
      self.callback(True)
148
    except: # pylint: disable-msg=W0702
149
      # we need to catch any exception here, log it, but proceed, because even
150
      # if we failed handling a single request, we still want our daemon to
151
      # proceed.
152
      logging.error("Unexpected exception", exc_info=True)
153

  
154
  def process_default(self, event):
155
    logging.error("Received unhandled inotify event: %s", event)

Also available in: Unified diff