Merge lp:~mandel/ubuntuone-client/eq-add-ancestors-watches into lp:ubuntuone-client

Proposed by Manuel de la Peña
Status: Merged
Approved by: Alejandro J. Cura
Approved revision: 1139
Merged at revision: 1128
Proposed branch: lp:~mandel/ubuntuone-client/eq-add-ancestors-watches
Merge into: lp:ubuntuone-client
Diff against target: 368 lines (+177/-40)
8 files modified
tests/platform/linux/test_filesystem_notifications.py (+46/-0)
tests/platform/windows/test_filesystem_notifications.py (+20/-0)
tests/syncdaemon/test_eq_inotify.py (+38/-2)
tests/syncdaemon/test_localrescan.py (+15/-0)
ubuntuone/platform/linux/filesystem_notifications.py (+29/-1)
ubuntuone/platform/windows/filesystem_notifications.py (+8/-0)
ubuntuone/syncdaemon/event_queue.py (+15/-2)
ubuntuone/syncdaemon/local_rescan.py (+6/-35)
To merge this branch: bzr merge lp:~mandel/ubuntuone-client/eq-add-ancestors-watches
Reviewer Review Type Date Requested Status
Alejandro J. Cura (community) Approve
Roberto Alsina (community) Approve
Review via email: mp+74102@code.launchpad.net

Commit message

Fixes lp:838111

The code moves the addition of the watches for the UDF ancestor to the FilesystemMonitor which is a platform dependent class. The new API is exposed through the eq so that the local rescan can use it. This branch also provides the implementation of Windows which simply ignores the addition of the watches.

Description of the change

Fixes lp:838111

The code moves the addition of the watches for the UDF ancestor to the FilesystemMonitor which is a platform dependent class. The new API is exposed through the eq so that the local rescan can use it. This branch also provides the implementation of Windows which simply ignores the addition of the watches.

This is a new MP so that the code is not blocked du to team holidays. Previous MP can be found here: https://code.launchpad.net/~mandel/ubuntuone-client/eq-add-ancestors-watches/+merge/73775

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) wrote :

+1

review: Approve
Revision history for this message
Alejandro J. Cura (alecu) wrote :

Looks fine!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/platform/linux/test_filesystem_notifications.py'
2--- tests/platform/linux/test_filesystem_notifications.py 2011-08-25 17:31:02 +0000
3+++ tests/platform/linux/test_filesystem_notifications.py 2011-09-05 13:52:23 +0000
4@@ -37,6 +37,17 @@
5 # pylint: disable=W0212
6
7
8+class FakeVolume(object):
9+ """A fake volume."""
10+
11+ def __init__(self, path, ancestors):
12+ """Create a new instance."""
13+ super(FakeVolume, self).__init__()
14+ self.volume_id = path
15+ self.path = path
16+ self.ancestors = ancestors
17+
18+
19 class ShutdownTestCase(testcase.BaseTwistedTestCase):
20 """Test the monitor shutdown."""
21
22@@ -288,6 +299,41 @@
23 self.assertIn(self.root_dir, self.monitor._general_watchs)
24
25 @defer.inlineCallbacks
26+ def test_add_watches_to_udf_ancestors(self):
27+ """Test that the ancestor watches are added."""
28+ ancestors = ['~', '~/Picture', '~/Pictures/Work']
29+ path = 'test_path'
30+ volume = FakeVolume(path, ancestors)
31+ self.patch(filesystem_notifications, 'access', lambda path: True)
32+ self.patch(self.monitor, '_is_udf_ancestor', lambda path: True)
33+ # lets add the watches, ensure that we do return true and that the new
34+ # watches are indeed present.
35+ added = yield self.monitor.add_watches_to_udf_ancestors(volume)
36+ self.assertTrue(added, 'Watches should have been added.')
37+ for path in ancestors:
38+ self.assertTrue(self.log_handler.check_debug(
39+ "Adding ancestors inotify watch", path))
40+ self.assertIn(path, self.monitor._ancestors_watchs)
41+
42+ @defer.inlineCallbacks
43+ def test_add_watches_to_udf_ancestors_reverted(self):
44+ """Test that the ancestor watches are reverted."""
45+ ancestors = ['~', '~/Picture', '~/Pictures/Work']
46+ path = 'test_path'
47+ volume = FakeVolume(path, ancestors)
48+ self.patch(filesystem_notifications, 'access',
49+ lambda path: path != ancestors[2])
50+ self.patch(self.monitor, '_is_udf_ancestor', lambda path: True)
51+ # lets ensure that we did not added any of the watches.
52+ added = yield self.monitor.add_watches_to_udf_ancestors(volume)
53+ self.assertFalse(added, 'Watches should NOT have been added.')
54+ for path in ancestors:
55+ if path != ancestors[2]:
56+ self.assertTrue(self.log_handler.check_debug(
57+ "Adding ancestors inotify watch", path))
58+ self.assertNotIn(path, self.monitor._ancestors_watchs)
59+
60+ @defer.inlineCallbacks
61 def test_add_watch_on_udf_ancestor(self):
62 """Test that ancestors watchs can be added."""
63 # create the udf and add the watch
64
65=== modified file 'tests/platform/windows/test_filesystem_notifications.py'
66--- tests/platform/windows/test_filesystem_notifications.py 2011-08-17 19:24:52 +0000
67+++ tests/platform/windows/test_filesystem_notifications.py 2011-09-05 13:52:23 +0000
68@@ -1315,3 +1315,23 @@
69
70 self.assertTrue(d1.called, "Should already be called.")
71 self.assertTrue(d2.called, "Should already be called.")
72+
73+ @defer.inlineCallbacks
74+ def test_add_watches_to_udf_ancestors(self):
75+ """Test that the ancestor watches are not added."""
76+
77+ class FakeVolume(object):
78+ """A fake UDF."""
79+
80+ def __init__(self, ancestors):
81+ """Create a new instance."""
82+ self.ancestors = ancestors
83+
84+ ancestors = ['~', '~\\Pictures', '~\\Pictures\\Home',]
85+ volume = FakeVolume(ancestors)
86+ monitor = FilesystemMonitor(None, None)
87+ added = yield monitor.add_watches_to_udf_ancestors(volume)
88+ self.assertTrue(added, 'We should always return true.')
89+ # lets ensure that we never added the watches
90+ self.assertEqual(0, len(monitor._watch_manager._wdm.values()),
91+ 'No watches should have been added.')
92
93=== modified file 'tests/syncdaemon/test_eq_inotify.py'
94--- tests/syncdaemon/test_eq_inotify.py 2011-08-25 18:27:25 +0000
95+++ tests/syncdaemon/test_eq_inotify.py 2011-09-05 13:52:23 +0000
96@@ -1,6 +1,7 @@
97 # tests.syncdaemon.test_eq_inotify
98 #
99-# Author: Facundo Batista <facundo@canonical.com>
100+# Authors: Facundo Batista <facundo@canonical.com>
101+# Manuel de la Pena <manuel@canonical.com>
102 #
103 # Copyright 2009-2011 Canonical Ltd.
104 #
105@@ -45,7 +46,7 @@
106 set_file_readwrite,
107 set_dir_readwrite,
108 )
109-from ubuntuone.syncdaemon import volume_manager
110+from ubuntuone.syncdaemon import event_queue, volume_manager
111
112 # our logging level
113 TRACE = logging.getLevelName('TRACE')
114@@ -83,6 +84,41 @@
115 self.assertEqual(called, [method_arg])
116 self.assertEqual(res, method_resp)
117
118+ def test_add_watches_to_udf_ancestors(self):
119+ """Test that ancestors watches can be added."""
120+ called = []
121+ method_resp = True
122+ method_arg = object()
123+
124+ def add_watches_to_udf_ancestors(path):
125+ """Fake it."""
126+ called.append(path)
127+ return defer.succeed(method_resp)
128+
129+ self.patch(self.eq.monitor, 'add_watches_to_udf_ancestors',
130+ add_watches_to_udf_ancestors)
131+ res = yield self.eq.add_watches_to_udf_ancestors(method_arg)
132+ self.assertEqual(called, [method_arg])
133+ self.assertEqual(res, method_resp)
134+
135+ def test_add_watches_to_udf_ancestors_no_access(self):
136+ """Test that ancestors watches are not added."""
137+ called = []
138+ method_resp = True
139+ method_arg = object()
140+
141+ def add_watches_to_udf_ancestors(path):
142+ """Fake it."""
143+ called.append(path)
144+ return defer.succeed(method_resp)
145+
146+ self.patch(event_queue, 'access', lambda path: False)
147+ self.pathc(self.eq.monitor, 'add_watches_to_udf_ancestors',
148+ add_watches_to_udf_ancestors)
149+ added = yield self.eq.add_watches_to_udf_ancestors(method_arg)
150+ self.assertFalse(added, 'Watches should have not been added.')
151+ self.assertEqual(0, len(called))
152+
153 def test_rm_watch(self):
154 """Test that watchs can be removed."""
155 called = []
156
157=== modified file 'tests/syncdaemon/test_localrescan.py'
158--- tests/syncdaemon/test_localrescan.py 2011-08-30 18:04:23 +0000
159+++ tests/syncdaemon/test_localrescan.py 2011-09-05 13:52:23 +0000
160@@ -1,5 +1,6 @@
161 # Author: Facundo Batista <facundo@canonical.com>
162 # Author: Natalia Bidart <natalia.bidart@canonical.com>
163+# Author: Manuel de la Pena <manuel@canonical.com>
164 #
165 # Copyright 2009-2011 Canonical Ltd.
166 #
167@@ -76,6 +77,10 @@
168 add_watch = freeze_rollback = is_frozen = _fake
169 rm_watch = freeze_begin = add_to_mute_filter = _fake
170
171+ def add_watches_to_udf_ancestors(self, volume):
172+ """Fake ancestors addition."""
173+ return defer.succeed(True)
174+
175 def freeze_commit(self, events):
176 """just store events"""
177 self.pushed.extend(events)
178@@ -2237,7 +2242,17 @@
179 self.watches.append(path)
180 return defer.succeed(True)
181
182+ @defer.inlineCallbacks
183+ def fake_add_watches_to_udf_ancestors(volume):
184+ """Fake the addition of the ancestors watches."""
185+ for ancestor in volume.ancestors:
186+ self._logger.debug("Adding watch to UDF's %r", ancestor)
187+ yield fake_add(ancestor)
188+ defer.returnValue(True)
189+
190 self.patch(self.eq, 'add_watch', fake_add)
191+ self.patch(self.eq, 'add_watches_to_udf_ancestors',
192+ fake_add_watches_to_udf_ancestors)
193
194 self.lr = local_rescan.LocalRescan(self.vm, self.fsm, self.eq, self.aq)
195
196
197=== modified file 'ubuntuone/platform/linux/filesystem_notifications.py'
198--- ubuntuone/platform/linux/filesystem_notifications.py 2011-08-25 17:31:02 +0000
199+++ ubuntuone/platform/linux/filesystem_notifications.py 2011-09-05 13:52:23 +0000
200@@ -1,6 +1,7 @@
201 # ubuntuone.syncdaemon.event_queue - Event queuing
202 #
203-# Author: Facundo Batista <facundo@canonical.com>
204+# Authors: Facundo Batista <facundo@canonical.com>
205+# Manuel de la Pena <manuel@canonical.com>
206 #
207 # Copyright 2009-2011 Canonical Ltd.
208 #
209@@ -23,6 +24,7 @@
210
211 import pyinotify
212 from twisted.internet import abstract, reactor, error, defer
213+from ubuntuone.platform.linux.os_helper import access
214
215
216 # translates quickly the event and it's is_dir state to our standard events
217@@ -472,6 +474,32 @@
218 w_dict[dirpath] = result[dirpath]
219 return defer.succeed(True)
220
221+ @defer.inlineCallbacks
222+ def add_watches_to_udf_ancestors(self, volume):
223+ """Add a inotify watch to volume's ancestors if it's an UDF."""
224+ added_watches = []
225+
226+ def revert_watches():
227+ """Restore the just added watches and unsubscribe volume."""
228+ for path in added_watches:
229+ self.eq.rm_watch(path)
230+
231+ for ancestor in volume.ancestors:
232+ # check that ancestor is still there
233+ if not access(ancestor):
234+ self.log.info("Tree broken at path: %r", volume.path)
235+ revert_watches()
236+ defer.returnValue(False)
237+
238+ self.log.debug("Adding watch to UDF's ancestor %r", ancestor)
239+ really_added = yield self.eq.add_watch(ancestor)
240+ # only note it for the revert if the watch was not there before
241+ if really_added:
242+ added_watches.append(ancestor)
243+
244+ # all is ok
245+ defer.returnValue(True)
246+
247 def inotify_watch_fix(self, pathfrom, pathto):
248 """Fix the path in inotify structures."""
249 if pathfrom in self._general_watchs:
250
251=== modified file 'ubuntuone/platform/windows/filesystem_notifications.py'
252--- ubuntuone/platform/windows/filesystem_notifications.py 2011-08-15 20:00:36 +0000
253+++ ubuntuone/platform/windows/filesystem_notifications.py 2011-09-05 13:52:23 +0000
254@@ -734,6 +734,14 @@
255 return self._watch_manager.add_watch(dirpath,
256 FILESYSTEM_MONITOR_MASK, auto_add=True)
257
258+ def add_watches_to_udf_ancestors(self, volume):
259+ """Add a inotify watch to volume's ancestors if it's an UDF."""
260+ # On windows there is no need to add the watches to the ancestors
261+ # so we will always return true. The reason for this is that the
262+ # handles that we open stop the user from renaming the ancestors of
263+ # the UDF, for a user to do that he has to unsync the udf first
264+ return defer.succeed(True)
265+
266 def is_frozen(self):
267 """Checks if there's something frozen."""
268 return self._processor.frozen_path is not None
269
270=== modified file 'ubuntuone/syncdaemon/event_queue.py'
271--- ubuntuone/syncdaemon/event_queue.py 2011-08-17 13:11:06 +0000
272+++ ubuntuone/syncdaemon/event_queue.py 2011-09-05 13:52:23 +0000
273@@ -1,6 +1,7 @@
274 # ubuntuone.syncdaemon.event_queue - Event queuing
275 #
276-# Author: Facundo Batista <facundo@canonical.com>
277+# Authors: Facundo Batista <facundo@canonical.com>
278+# Manuel de la Pena <manuel@canonical.com>
279 #
280 # Copyright 2009-2011 Canonical Ltd.
281 #
282@@ -23,7 +24,7 @@
283
284 from twisted.internet import defer
285
286-from ubuntuone.platform import FilesystemMonitor
287+from ubuntuone.platform import access, FilesystemMonitor
288
289 # these are our internal events, what is inserted into the whole system
290 EVENTS = {
291@@ -222,6 +223,18 @@
292 """Add watch to a dir."""
293 return self.monitor.add_watch(dirpath)
294
295+ def add_watches_to_udf_ancestors(self, volume):
296+ """Add a inotify watch to volume's ancestors if it's an UDF."""
297+ # This is a platform dependent operation since there are cases in
298+ # which the watches do not have to be added (On windows we do not
299+ # have to add them since we have an opened handle.)
300+ # finally, check that UDF is ok in disk
301+ if not access(volume.path):
302+ # if we cannot access the UDF lets return false and do
303+ # nothing
304+ return defer.succeed(False)
305+ return self.monitor.add_watches_to_udf_ancestors(volume)
306+
307 def unsubscribe(self, obj):
308 """Remove the callback object from the listener queue.
309
310
311=== modified file 'ubuntuone/syncdaemon/local_rescan.py'
312--- ubuntuone/syncdaemon/local_rescan.py 2011-08-22 14:20:30 +0000
313+++ ubuntuone/syncdaemon/local_rescan.py 2011-09-05 13:52:23 +0000
314@@ -259,40 +259,6 @@
315 self._process_next_queue(None)
316 return self._previous_deferred
317
318- @defer.inlineCallbacks
319- def _add_watches_to_udf_ancestors(self, volume):
320- """Add a inotify watch to volume's ancestors if it's an UDF."""
321- added_watches = []
322-
323- def revert_watches():
324- """Restore the just added watches and unsubscribe volume."""
325- for path in added_watches:
326- self.eq.rm_watch(path)
327- m = "Unsubscribing UDF %r because not in disk: %r"
328- log_info(m, volume.volume_id, volume.path)
329- self.vm.unsubscribe_udf(volume.volume_id)
330-
331- for ancestor in volume.ancestors:
332- # check that ancestor is still there
333- if not access(ancestor):
334- log_info("Tree broken at path: %r", volume.path)
335- revert_watches()
336- defer.returnValue(False)
337-
338- log_debug("Adding watch to UDF's ancestor %r", ancestor)
339- really_added = yield self.eq.add_watch(ancestor)
340- # only note it for the revert if the watch was not there before
341- if really_added:
342- added_watches.append(ancestor)
343-
344- # finally, check that UDF is ok in disk
345- if not access(volume.path):
346- revert_watches()
347- defer.returnValue(False)
348-
349- # all is ok
350- defer.returnValue(True)
351-
352 def _process_next_queue(self, _):
353 """Process the next item in the queue, if any."""
354 log_debug("process next in queue (len %d)", len(self._queue))
355@@ -312,8 +278,13 @@
356 # add watches to UDF ancestors and check UDF is ok
357 volume = scan_info[0]
358 if isinstance(volume, volume_manager.UDF):
359- udf_ok = yield self._add_watches_to_udf_ancestors(volume)
360+ udf_ok = yield self.eq.add_watches_to_udf_ancestors(volume)
361 if not udf_ok:
362+ # we need to ensure that the udf is not subscribed
363+ # when an error happens while adding the parent watches.
364+ m = "Unsubscribing UDF %r because not in disk: %r"
365+ log_info(m, volume.volume_id, volume.path)
366+ self.vm.unsubscribe_udf(volume.volume_id)
367 self._process_next_queue(None)
368 return
369

Subscribers

People subscribed via source and target branches