Revision e543a42f

b/lib/asyncnotifier.py
76 76
  """
77 77

  
78 78

  
79
class SingleFileEventHandler(pyinotify.ProcessEvent):
80
  """Handle modify events for a single file.
79
class FileEventHandlerBase(pyinotify.ProcessEvent):
80
  """Base class for file event handlers.
81

  
82
  @ivar watch_manager: Inotify watch manager
81 83

  
82 84
  """
85
  def __init__(self, watch_manager):
86
    """Initializes this class.
87

  
88
    @type watch_manager: pyinotify.WatchManager
89
    @param watch_manager: inotify watch manager
90

  
91
    """
92
    # pylint: disable-msg=W0231
93
    # no need to call the parent's constructor
94
    self.watch_manager = watch_manager
95

  
96
  def process_default(self, event):
97
    logging.error("Received unhandled inotify event: %s", event)
98

  
99
  def AddWatch(self, filename, mask):
100
    """Adds a file watch.
101

  
102
    @param filename: Path to file
103
    @param mask: Inotify event mask
104
    @return: Result
105

  
106
    """
107
    result = self.watch_manager.add_watch(filename, mask)
108

  
109
    ret = result.get(filename, -1)
110
    if ret <= 0:
111
      raise errors.InotifyError("Could not add inotify watcher (%s)" % ret)
112

  
113
    return result[filename]
83 114

  
115
  def RemoveWatch(self, handle):
116
    """Removes a handle from the watcher.
117

  
118
    @param handle: Inotify handle
119
    @return: Whether removal was successful
120

  
121
    """
122
    result = self.watch_manager.rm_watch(handle)
123

  
124
    return result[handle]
125

  
126

  
127
class SingleFileEventHandler(FileEventHandlerBase):
128
  """Handle modify events for a single file.
129

  
130
  """
84 131
  def __init__(self, watch_manager, callback, filename):
85 132
    """Constructor for SingleFileEventHandler
86 133

  
......
92 139
    @param filename: config file to watch
93 140

  
94 141
    """
95
    # pylint: disable-msg=W0231
96
    # no need to call the parent's constructor
97
    self.watch_manager = watch_manager
98
    self.callback = callback
99
    self.mask = pyinotify.EventsCodes.ALL_FLAGS["IN_IGNORED"] | \
100
                pyinotify.EventsCodes.ALL_FLAGS["IN_MODIFY"]
101
    self.file = filename
102
    self.watch_handle = None
142
    FileEventHandlerBase.__init__(self, watch_manager)
143

  
144
    self._callback = callback
145
    self._filename = filename
146

  
147
    self._watch_handle = None
103 148

  
104 149
  def enable(self):
105
    """Watch the given file
150
    """Watch the given file.
106 151

  
107 152
    """
108
    if self.watch_handle is None:
109
      result = self.watch_manager.add_watch(self.file, self.mask)
110
      if not self.file in result or result[self.file] <= 0:
111
        raise errors.InotifyError("Could not add inotify watcher")
112
      else:
113
        self.watch_handle = result[self.file]
153
    if self._watch_handle is not None:
154
      return
155

  
156
    # Class '...' has no 'IN_...' member, pylint: disable-msg=E1103
157
    mask = (pyinotify.EventsCodes.IN_MODIFY |
158
            pyinotify.EventsCodes.IN_IGNORED)
159

  
160
    self._watch_handle = self.AddWatch(self._filename, mask)
114 161

  
115 162
  def disable(self):
116
    """Stop watching the given file
163
    """Stop watching the given file.
117 164

  
118 165
    """
119
    if self.watch_handle is not None:
120
      result = self.watch_manager.rm_watch(self.watch_handle)
121
      if result[self.watch_handle]:
122
        self.watch_handle = None
166
    if self._watch_handle is not None and self.RemoveWatch(self._watch_handle):
167
      self._watch_handle = None
123 168

  
124 169
  # pylint: disable-msg=C0103
125 170
  # this overrides a method in pyinotify.ProcessEvent
......
132 177
    # case we'll need to create a watcher for the "new" file. This can be done
133 178
    # by the callback by calling "enable" again on us.
134 179
    logging.debug("Received 'ignored' inotify event for %s", event.path)
135
    self.watch_handle = None
136
    self.callback(False)
180
    self._watch_handle = None
181
    self._callback(False)
137 182

  
138 183
  # pylint: disable-msg=C0103
139 184
  # this overrides a method in pyinotify.ProcessEvent
......
143 188
    # replacing any file with a new one, at filesystem level, rather than
144 189
    # actually changing it. (see utils.WriteFile)
145 190
    logging.debug("Received 'modify' inotify event for %s", event.path)
146
    self.callback(True)
147

  
148
  def process_default(self, event):
149
    logging.error("Received unhandled inotify event: %s", event)
191
    self._callback(True)
b/test/ganeti.asyncnotifier_unittest.py
24 24
import unittest
25 25
import signal
26 26
import os
27
import tempfile
28
import shutil
27 29

  
28 30
try:
29 31
  # pylint: disable-msg=E0611
......
149 151
    self.assertEquals(self.notifiers[self.NOTIFIER_TERM].error_count, 0)
150 152

  
151 153

  
154
class TestSingleFileEventHandlerError(unittest.TestCase):
155
  def setUp(self):
156
    self.tmpdir = tempfile.mkdtemp()
157

  
158
  def tearDown(self):
159
    shutil.rmtree(self.tmpdir)
160

  
161
  def test(self):
162
    wm = pyinotify.WatchManager()
163
    handler = asyncnotifier.SingleFileEventHandler(wm, None,
164
                                                   utils.PathJoin(self.tmpdir,
165
                                                                  "nonexist"))
166
    self.assertRaises(errors.InotifyError, handler.enable)
167
    self.assertRaises(errors.InotifyError, handler.enable)
168
    handler.disable()
169
    self.assertRaises(errors.InotifyError, handler.enable)
170

  
171

  
152 172
if __name__ == "__main__":
153 173
  testutils.GanetiTestProgram()

Also available in: Unified diff