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
=== modified file 'tests/platform/linux/test_filesystem_notifications.py'
--- tests/platform/linux/test_filesystem_notifications.py 2011-08-25 17:31:02 +0000
+++ tests/platform/linux/test_filesystem_notifications.py 2011-09-05 13:52:23 +0000
@@ -37,6 +37,17 @@
37# pylint: disable=W021237# pylint: disable=W0212
3838
3939
40class FakeVolume(object):
41 """A fake volume."""
42
43 def __init__(self, path, ancestors):
44 """Create a new instance."""
45 super(FakeVolume, self).__init__()
46 self.volume_id = path
47 self.path = path
48 self.ancestors = ancestors
49
50
40class ShutdownTestCase(testcase.BaseTwistedTestCase):51class ShutdownTestCase(testcase.BaseTwistedTestCase):
41 """Test the monitor shutdown."""52 """Test the monitor shutdown."""
4253
@@ -288,6 +299,41 @@
288 self.assertIn(self.root_dir, self.monitor._general_watchs)299 self.assertIn(self.root_dir, self.monitor._general_watchs)
289300
290 @defer.inlineCallbacks301 @defer.inlineCallbacks
302 def test_add_watches_to_udf_ancestors(self):
303 """Test that the ancestor watches are added."""
304 ancestors = ['~', '~/Picture', '~/Pictures/Work']
305 path = 'test_path'
306 volume = FakeVolume(path, ancestors)
307 self.patch(filesystem_notifications, 'access', lambda path: True)
308 self.patch(self.monitor, '_is_udf_ancestor', lambda path: True)
309 # lets add the watches, ensure that we do return true and that the new
310 # watches are indeed present.
311 added = yield self.monitor.add_watches_to_udf_ancestors(volume)
312 self.assertTrue(added, 'Watches should have been added.')
313 for path in ancestors:
314 self.assertTrue(self.log_handler.check_debug(
315 "Adding ancestors inotify watch", path))
316 self.assertIn(path, self.monitor._ancestors_watchs)
317
318 @defer.inlineCallbacks
319 def test_add_watches_to_udf_ancestors_reverted(self):
320 """Test that the ancestor watches are reverted."""
321 ancestors = ['~', '~/Picture', '~/Pictures/Work']
322 path = 'test_path'
323 volume = FakeVolume(path, ancestors)
324 self.patch(filesystem_notifications, 'access',
325 lambda path: path != ancestors[2])
326 self.patch(self.monitor, '_is_udf_ancestor', lambda path: True)
327 # lets ensure that we did not added any of the watches.
328 added = yield self.monitor.add_watches_to_udf_ancestors(volume)
329 self.assertFalse(added, 'Watches should NOT have been added.')
330 for path in ancestors:
331 if path != ancestors[2]:
332 self.assertTrue(self.log_handler.check_debug(
333 "Adding ancestors inotify watch", path))
334 self.assertNotIn(path, self.monitor._ancestors_watchs)
335
336 @defer.inlineCallbacks
291 def test_add_watch_on_udf_ancestor(self):337 def test_add_watch_on_udf_ancestor(self):
292 """Test that ancestors watchs can be added."""338 """Test that ancestors watchs can be added."""
293 # create the udf and add the watch339 # create the udf and add the watch
294340
=== modified file 'tests/platform/windows/test_filesystem_notifications.py'
--- tests/platform/windows/test_filesystem_notifications.py 2011-08-17 19:24:52 +0000
+++ tests/platform/windows/test_filesystem_notifications.py 2011-09-05 13:52:23 +0000
@@ -1315,3 +1315,23 @@
13151315
1316 self.assertTrue(d1.called, "Should already be called.")1316 self.assertTrue(d1.called, "Should already be called.")
1317 self.assertTrue(d2.called, "Should already be called.")1317 self.assertTrue(d2.called, "Should already be called.")
1318
1319 @defer.inlineCallbacks
1320 def test_add_watches_to_udf_ancestors(self):
1321 """Test that the ancestor watches are not added."""
1322
1323 class FakeVolume(object):
1324 """A fake UDF."""
1325
1326 def __init__(self, ancestors):
1327 """Create a new instance."""
1328 self.ancestors = ancestors
1329
1330 ancestors = ['~', '~\\Pictures', '~\\Pictures\\Home',]
1331 volume = FakeVolume(ancestors)
1332 monitor = FilesystemMonitor(None, None)
1333 added = yield monitor.add_watches_to_udf_ancestors(volume)
1334 self.assertTrue(added, 'We should always return true.')
1335 # lets ensure that we never added the watches
1336 self.assertEqual(0, len(monitor._watch_manager._wdm.values()),
1337 'No watches should have been added.')
13181338
=== modified file 'tests/syncdaemon/test_eq_inotify.py'
--- tests/syncdaemon/test_eq_inotify.py 2011-08-25 18:27:25 +0000
+++ tests/syncdaemon/test_eq_inotify.py 2011-09-05 13:52:23 +0000
@@ -1,6 +1,7 @@
1# tests.syncdaemon.test_eq_inotify1# tests.syncdaemon.test_eq_inotify
2#2#
3# Author: Facundo Batista <facundo@canonical.com>3# Authors: Facundo Batista <facundo@canonical.com>
4# Manuel de la Pena <manuel@canonical.com>
4#5#
5# Copyright 2009-2011 Canonical Ltd.6# Copyright 2009-2011 Canonical Ltd.
6#7#
@@ -45,7 +46,7 @@
45 set_file_readwrite,46 set_file_readwrite,
46 set_dir_readwrite,47 set_dir_readwrite,
47)48)
48from ubuntuone.syncdaemon import volume_manager49from ubuntuone.syncdaemon import event_queue, volume_manager
4950
50# our logging level51# our logging level
51TRACE = logging.getLevelName('TRACE')52TRACE = logging.getLevelName('TRACE')
@@ -83,6 +84,41 @@
83 self.assertEqual(called, [method_arg])84 self.assertEqual(called, [method_arg])
84 self.assertEqual(res, method_resp)85 self.assertEqual(res, method_resp)
8586
87 def test_add_watches_to_udf_ancestors(self):
88 """Test that ancestors watches can be added."""
89 called = []
90 method_resp = True
91 method_arg = object()
92
93 def add_watches_to_udf_ancestors(path):
94 """Fake it."""
95 called.append(path)
96 return defer.succeed(method_resp)
97
98 self.patch(self.eq.monitor, 'add_watches_to_udf_ancestors',
99 add_watches_to_udf_ancestors)
100 res = yield self.eq.add_watches_to_udf_ancestors(method_arg)
101 self.assertEqual(called, [method_arg])
102 self.assertEqual(res, method_resp)
103
104 def test_add_watches_to_udf_ancestors_no_access(self):
105 """Test that ancestors watches are not added."""
106 called = []
107 method_resp = True
108 method_arg = object()
109
110 def add_watches_to_udf_ancestors(path):
111 """Fake it."""
112 called.append(path)
113 return defer.succeed(method_resp)
114
115 self.patch(event_queue, 'access', lambda path: False)
116 self.pathc(self.eq.monitor, 'add_watches_to_udf_ancestors',
117 add_watches_to_udf_ancestors)
118 added = yield self.eq.add_watches_to_udf_ancestors(method_arg)
119 self.assertFalse(added, 'Watches should have not been added.')
120 self.assertEqual(0, len(called))
121
86 def test_rm_watch(self):122 def test_rm_watch(self):
87 """Test that watchs can be removed."""123 """Test that watchs can be removed."""
88 called = []124 called = []
89125
=== modified file 'tests/syncdaemon/test_localrescan.py'
--- tests/syncdaemon/test_localrescan.py 2011-08-30 18:04:23 +0000
+++ tests/syncdaemon/test_localrescan.py 2011-09-05 13:52:23 +0000
@@ -1,5 +1,6 @@
1# Author: Facundo Batista <facundo@canonical.com>1# Author: Facundo Batista <facundo@canonical.com>
2# Author: Natalia Bidart <natalia.bidart@canonical.com>2# Author: Natalia Bidart <natalia.bidart@canonical.com>
3# Author: Manuel de la Pena <manuel@canonical.com>
3#4#
4# Copyright 2009-2011 Canonical Ltd.5# Copyright 2009-2011 Canonical Ltd.
5#6#
@@ -76,6 +77,10 @@
76 add_watch = freeze_rollback = is_frozen = _fake77 add_watch = freeze_rollback = is_frozen = _fake
77 rm_watch = freeze_begin = add_to_mute_filter = _fake78 rm_watch = freeze_begin = add_to_mute_filter = _fake
7879
80 def add_watches_to_udf_ancestors(self, volume):
81 """Fake ancestors addition."""
82 return defer.succeed(True)
83
79 def freeze_commit(self, events):84 def freeze_commit(self, events):
80 """just store events"""85 """just store events"""
81 self.pushed.extend(events)86 self.pushed.extend(events)
@@ -2237,7 +2242,17 @@
2237 self.watches.append(path)2242 self.watches.append(path)
2238 return defer.succeed(True)2243 return defer.succeed(True)
22392244
2245 @defer.inlineCallbacks
2246 def fake_add_watches_to_udf_ancestors(volume):
2247 """Fake the addition of the ancestors watches."""
2248 for ancestor in volume.ancestors:
2249 self._logger.debug("Adding watch to UDF's %r", ancestor)
2250 yield fake_add(ancestor)
2251 defer.returnValue(True)
2252
2240 self.patch(self.eq, 'add_watch', fake_add)2253 self.patch(self.eq, 'add_watch', fake_add)
2254 self.patch(self.eq, 'add_watches_to_udf_ancestors',
2255 fake_add_watches_to_udf_ancestors)
22412256
2242 self.lr = local_rescan.LocalRescan(self.vm, self.fsm, self.eq, self.aq)2257 self.lr = local_rescan.LocalRescan(self.vm, self.fsm, self.eq, self.aq)
22432258
22442259
=== modified file 'ubuntuone/platform/linux/filesystem_notifications.py'
--- ubuntuone/platform/linux/filesystem_notifications.py 2011-08-25 17:31:02 +0000
+++ ubuntuone/platform/linux/filesystem_notifications.py 2011-09-05 13:52:23 +0000
@@ -1,6 +1,7 @@
1# ubuntuone.syncdaemon.event_queue - Event queuing1# ubuntuone.syncdaemon.event_queue - Event queuing
2#2#
3# Author: Facundo Batista <facundo@canonical.com>3# Authors: Facundo Batista <facundo@canonical.com>
4# Manuel de la Pena <manuel@canonical.com>
4#5#
5# Copyright 2009-2011 Canonical Ltd.6# Copyright 2009-2011 Canonical Ltd.
6#7#
@@ -23,6 +24,7 @@
2324
24import pyinotify25import pyinotify
25from twisted.internet import abstract, reactor, error, defer26from twisted.internet import abstract, reactor, error, defer
27from ubuntuone.platform.linux.os_helper import access
2628
2729
28# translates quickly the event and it's is_dir state to our standard events30# translates quickly the event and it's is_dir state to our standard events
@@ -472,6 +474,32 @@
472 w_dict[dirpath] = result[dirpath]474 w_dict[dirpath] = result[dirpath]
473 return defer.succeed(True)475 return defer.succeed(True)
474476
477 @defer.inlineCallbacks
478 def add_watches_to_udf_ancestors(self, volume):
479 """Add a inotify watch to volume's ancestors if it's an UDF."""
480 added_watches = []
481
482 def revert_watches():
483 """Restore the just added watches and unsubscribe volume."""
484 for path in added_watches:
485 self.eq.rm_watch(path)
486
487 for ancestor in volume.ancestors:
488 # check that ancestor is still there
489 if not access(ancestor):
490 self.log.info("Tree broken at path: %r", volume.path)
491 revert_watches()
492 defer.returnValue(False)
493
494 self.log.debug("Adding watch to UDF's ancestor %r", ancestor)
495 really_added = yield self.eq.add_watch(ancestor)
496 # only note it for the revert if the watch was not there before
497 if really_added:
498 added_watches.append(ancestor)
499
500 # all is ok
501 defer.returnValue(True)
502
475 def inotify_watch_fix(self, pathfrom, pathto):503 def inotify_watch_fix(self, pathfrom, pathto):
476 """Fix the path in inotify structures."""504 """Fix the path in inotify structures."""
477 if pathfrom in self._general_watchs:505 if pathfrom in self._general_watchs:
478506
=== modified file 'ubuntuone/platform/windows/filesystem_notifications.py'
--- ubuntuone/platform/windows/filesystem_notifications.py 2011-08-15 20:00:36 +0000
+++ ubuntuone/platform/windows/filesystem_notifications.py 2011-09-05 13:52:23 +0000
@@ -734,6 +734,14 @@
734 return self._watch_manager.add_watch(dirpath,734 return self._watch_manager.add_watch(dirpath,
735 FILESYSTEM_MONITOR_MASK, auto_add=True)735 FILESYSTEM_MONITOR_MASK, auto_add=True)
736736
737 def add_watches_to_udf_ancestors(self, volume):
738 """Add a inotify watch to volume's ancestors if it's an UDF."""
739 # On windows there is no need to add the watches to the ancestors
740 # so we will always return true. The reason for this is that the
741 # handles that we open stop the user from renaming the ancestors of
742 # the UDF, for a user to do that he has to unsync the udf first
743 return defer.succeed(True)
744
737 def is_frozen(self):745 def is_frozen(self):
738 """Checks if there's something frozen."""746 """Checks if there's something frozen."""
739 return self._processor.frozen_path is not None747 return self._processor.frozen_path is not None
740748
=== modified file 'ubuntuone/syncdaemon/event_queue.py'
--- ubuntuone/syncdaemon/event_queue.py 2011-08-17 13:11:06 +0000
+++ ubuntuone/syncdaemon/event_queue.py 2011-09-05 13:52:23 +0000
@@ -1,6 +1,7 @@
1# ubuntuone.syncdaemon.event_queue - Event queuing1# ubuntuone.syncdaemon.event_queue - Event queuing
2#2#
3# Author: Facundo Batista <facundo@canonical.com>3# Authors: Facundo Batista <facundo@canonical.com>
4# Manuel de la Pena <manuel@canonical.com>
4#5#
5# Copyright 2009-2011 Canonical Ltd.6# Copyright 2009-2011 Canonical Ltd.
6#7#
@@ -23,7 +24,7 @@
2324
24from twisted.internet import defer25from twisted.internet import defer
2526
26from ubuntuone.platform import FilesystemMonitor27from ubuntuone.platform import access, FilesystemMonitor
2728
28# these are our internal events, what is inserted into the whole system29# these are our internal events, what is inserted into the whole system
29EVENTS = {30EVENTS = {
@@ -222,6 +223,18 @@
222 """Add watch to a dir."""223 """Add watch to a dir."""
223 return self.monitor.add_watch(dirpath)224 return self.monitor.add_watch(dirpath)
224225
226 def add_watches_to_udf_ancestors(self, volume):
227 """Add a inotify watch to volume's ancestors if it's an UDF."""
228 # This is a platform dependent operation since there are cases in
229 # which the watches do not have to be added (On windows we do not
230 # have to add them since we have an opened handle.)
231 # finally, check that UDF is ok in disk
232 if not access(volume.path):
233 # if we cannot access the UDF lets return false and do
234 # nothing
235 return defer.succeed(False)
236 return self.monitor.add_watches_to_udf_ancestors(volume)
237
225 def unsubscribe(self, obj):238 def unsubscribe(self, obj):
226 """Remove the callback object from the listener queue.239 """Remove the callback object from the listener queue.
227240
228241
=== modified file 'ubuntuone/syncdaemon/local_rescan.py'
--- ubuntuone/syncdaemon/local_rescan.py 2011-08-22 14:20:30 +0000
+++ ubuntuone/syncdaemon/local_rescan.py 2011-09-05 13:52:23 +0000
@@ -259,40 +259,6 @@
259 self._process_next_queue(None)259 self._process_next_queue(None)
260 return self._previous_deferred260 return self._previous_deferred
261261
262 @defer.inlineCallbacks
263 def _add_watches_to_udf_ancestors(self, volume):
264 """Add a inotify watch to volume's ancestors if it's an UDF."""
265 added_watches = []
266
267 def revert_watches():
268 """Restore the just added watches and unsubscribe volume."""
269 for path in added_watches:
270 self.eq.rm_watch(path)
271 m = "Unsubscribing UDF %r because not in disk: %r"
272 log_info(m, volume.volume_id, volume.path)
273 self.vm.unsubscribe_udf(volume.volume_id)
274
275 for ancestor in volume.ancestors:
276 # check that ancestor is still there
277 if not access(ancestor):
278 log_info("Tree broken at path: %r", volume.path)
279 revert_watches()
280 defer.returnValue(False)
281
282 log_debug("Adding watch to UDF's ancestor %r", ancestor)
283 really_added = yield self.eq.add_watch(ancestor)
284 # only note it for the revert if the watch was not there before
285 if really_added:
286 added_watches.append(ancestor)
287
288 # finally, check that UDF is ok in disk
289 if not access(volume.path):
290 revert_watches()
291 defer.returnValue(False)
292
293 # all is ok
294 defer.returnValue(True)
295
296 def _process_next_queue(self, _):262 def _process_next_queue(self, _):
297 """Process the next item in the queue, if any."""263 """Process the next item in the queue, if any."""
298 log_debug("process next in queue (len %d)", len(self._queue))264 log_debug("process next in queue (len %d)", len(self._queue))
@@ -312,8 +278,13 @@
312 # add watches to UDF ancestors and check UDF is ok278 # add watches to UDF ancestors and check UDF is ok
313 volume = scan_info[0]279 volume = scan_info[0]
314 if isinstance(volume, volume_manager.UDF):280 if isinstance(volume, volume_manager.UDF):
315 udf_ok = yield self._add_watches_to_udf_ancestors(volume)281 udf_ok = yield self.eq.add_watches_to_udf_ancestors(volume)
316 if not udf_ok:282 if not udf_ok:
283 # we need to ensure that the udf is not subscribed
284 # when an error happens while adding the parent watches.
285 m = "Unsubscribing UDF %r because not in disk: %r"
286 log_info(m, volume.volume_id, volume.path)
287 self.vm.unsubscribe_udf(volume.volume_id)
317 self._process_next_queue(None)288 self._process_next_queue(None)
318 return289 return
319290

Subscribers

People subscribed via source and target branches