Merge lp:~mandel/ubuntuone-client/daemon-options into lp:ubuntuone-client

Proposed by Manuel de la Peña
Status: Merged
Approved by: Manuel de la Peña
Approved revision: 1320
Merged at revision: 1280
Proposed branch: lp:~mandel/ubuntuone-client/daemon-options
Merge into: lp:ubuntuone-client
Prerequisite: lp:~mandel/ubuntuone-client/unify-filemonitors
Diff against target: 789 lines (+254/-225)
17 files modified
bin/ubuntuone-syncdaemon (+6/-1)
data/syncdaemon.conf (+6/-0)
tests/platform/filesystem_notifications/test_darwin.py (+7/-0)
tests/platform/filesystem_notifications/test_fsevents_daemon.py (+51/-7)
tests/platform/filesystem_notifications/test_linux.py (+8/-0)
tests/platform/filesystem_notifications/test_windows.py (+7/-0)
tests/syncdaemon/test_config.py (+13/-0)
tests/syncdaemon/test_eventqueue.py (+17/-0)
ubuntuone/platform/__init__.py (+6/-0)
ubuntuone/platform/filesystem_notifications/__init__.py (+1/-0)
ubuntuone/platform/filesystem_notifications/monitor/__init__.py (+59/-6)
ubuntuone/platform/filesystem_notifications/monitor/common.py (+7/-0)
ubuntuone/platform/filesystem_notifications/monitor/darwin/fsevents_daemon.py (+51/-206)
ubuntuone/platform/filesystem_notifications/monitor/linux.py (+6/-0)
ubuntuone/syncdaemon/event_queue.py (+6/-2)
ubuntuone/syncdaemon/filesystem_notifications.py (+0/-1)
ubuntuone/syncdaemon/main.py (+3/-2)
To merge this branch: bzr merge lp:~mandel/ubuntuone-client/daemon-options
Reviewer Review Type Date Requested Status
Mike McCracken (community) Approve
Diego Sarmentero (community) Approve
Review via email: mp+115354@code.launchpad.net

Commit message

- Provides an extra command line that allows to choose which filemonitor implementation to use with the ubuntuone sync daemon (LP: 1025689).

Description of the change

Provides an extra command line that allows to choose which filemonitor implementation to use with the ubuntuone sync daemon. Right now on Windows and Linux there is only one implementation while on Darwin there are two of them.

The test results should be:

Linux => all pass
Windows => atm one failure (currently present in trunk).
Darwin => tests/platform/filesystem_notifications/test_darwin.py (all pass but got stuck at the end just like trunk).
          tests/platform/filesystem_notifications/test_fsevents_daemon.py (all pass).

To post a comment you must log in.
Revision history for this message
Mike McCracken (mikemc) wrote :

A couple minor things I think should change and a few comments about
importing at the end that you can feel free to argue with...

# typos:
- "test test" in a couple docstrings
- get_filemonitor_class exception strings say "Not active monitor", I think you mean "no active…"?
(or "no available", see below)

# naming issue:
- is_active_monitor is a little confusing in english. It's intended to
tell us if the monitor is usable (ie, installed, for the daemon),
right?
"Is active monitor" sounds like it's telling us if this monitor is
currently active, which it isn't doing.

How about "is_available" or "is_installed" instead?
Since it's a method on the monitor class, I left out "monitor" in
those suggestions intentionally.

-----> up for discussion: lots of assignments in __init__ files from
       submodules that don't seem platform-specific:

I looked at the assignment to get_filemonitor_class, and poked around
a bit - some of this isn't new in this diff, but might be
nice to fix here, instead of adding code that does the same thing:
(Feel free to disagree if you think it's not productive)

# why is get_filemonitor_class being added to the namespace in platform/filesystem_notifications/__init__.py?
it looks like it's never accessed as
filesystem_notifications.get_filemonitor_class, but always as
filesystem_notifications.monitor.get_filemonitor_class, (eg in
platform/__init__.py).

So, that assignment is unnecessary, and

# importing filesystemMonitor - there are a lot of ways to import
  it. I think we could remove the assignments in platform/__init__.py,
  and filesystem_notifications/__init__.py, since it's possible to
  just get it from platform.filesystem_notifications.monitor
  everywhere - we already do this in a few places.

- syncdaemon/event_queue.py imports it from
  platform.filesystem_notifications, while test_eventqueue imports it
  from platform. (seems like those should be the same)

- test_windows and test_darwin import it from
  u.platform.filesystem_notifications.monitor.common, while test_linux
  gets it from platform.filesystem_notifications.

# related to the above: _GeneralINotifyProcessor seems like it's only
  used in test_filesystem_notifications. maybe we should change that and
  remove the assignment in filesystem_notifications/__init__.py:36

review: Needs Fixing
Revision history for this message
Manuel de la Peña (mandel) wrote :

> A couple minor things I think should change and a few comments about
> importing at the end that you can feel free to argue with...
>
> # typos:
> - "test test" in a couple docstrings
> - get_filemonitor_class exception strings say "Not active monitor", I think
> you mean "no active…"?
> (or "no available", see below)
>
>

Fixed!

> # naming issue:
> - is_active_monitor is a little confusing in english. It's intended to
> tell us if the monitor is usable (ie, installed, for the daemon),
> right?
> "Is active monitor" sounds like it's telling us if this monitor is
> currently active, which it isn't doing.
>
> How about "is_available" or "is_installed" instead?
> Since it's a method on the monitor class, I left out "monitor" in
> those suggestions intentionally.
>

Fixed !

> -----> up for discussion: lots of assignments in __init__ files from
> submodules that don't seem platform-specific:
>
> I looked at the assignment to get_filemonitor_class, and poked around
> a bit - some of this isn't new in this diff, but might be
> nice to fix here, instead of adding code that does the same thing:
> (Feel free to disagree if you think it's not productive)
>
> # why is get_filemonitor_class being added to the namespace in
> platform/filesystem_notifications/__init__.py?
> it looks like it's never accessed as
> filesystem_notifications.get_filemonitor_class, but always as
> filesystem_notifications.monitor.get_filemonitor_class, (eg in
> platform/__init__.py).
>
> So, that assignment is unnecessary, and
>
> # importing filesystemMonitor - there are a lot of ways to import
> it. I think we could remove the assignments in platform/__init__.py,
> and filesystem_notifications/__init__.py, since it's possible to
> just get it from platform.filesystem_notifications.monitor
> everywhere - we already do this in a few places.
>
> - syncdaemon/event_queue.py imports it from
> platform.filesystem_notifications, while test_eventqueue imports it
> from platform. (seems like those should be the same)
>
> - test_windows and test_darwin import it from
> u.platform.filesystem_notifications.monitor.common, while test_linux
> gets it from platform.filesystem_notifications.
>
> # related to the above: _GeneralINotifyProcessor seems like it's only
> used in test_filesystem_notifications. maybe we should change that and
> remove the assignment in filesystem_notifications/__init__.py:36

Completely agree with this but is maybe better to create a bug and do it in a diff branch to keep things easier, what do you think?

Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

== Python Lint Notices ==

./tests/platform/filesystem_notifications/test_linux.py:
    399: local variable 'is_available' is assigned to but never used
    400: undefined name 'is_availabe'

make: *** [lint] Error 2

review: Needs Fixing
Revision history for this message
Diego Sarmentero (diegosarmentero) wrote :

+1

review: Approve
Revision history for this message
Mike McCracken (mikemc) wrote :

A couple more places where s/active/available/ needs to happen:
- the NoActiveMonitorError Exception in monitor/__init__ should be NoAvailableMonitorError

- the assertion messages in filesystem_notifications/test_* say "Should always be active" - should be "always be available".

review: Needs Fixing
Revision history for this message
Mike McCracken (mikemc) wrote :

Thanks!

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :
1318. By Manuel de la Peña

Merged with parent.

1319. By Manuel de la Peña

Merged with changes from other platforms work.

1320. By Manuel de la Peña

Merged unify-filemonitors into daemon-options.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/ubuntuone-syncdaemon'
2--- bin/ubuntuone-syncdaemon 2012-07-02 15:39:40 +0000
3+++ bin/ubuntuone-syncdaemon 2012-07-19 14:25:29 +0000
4@@ -48,6 +48,7 @@
5
6 from ubuntuone.platform import (
7 can_write,
8+ get_filemonitor_class,
9 set_dir_readwrite,
10 is_already_running,
11 is_root,
12@@ -212,6 +213,9 @@
13 "[CONSUMER_KEY:CONSUMER_SECRET:]KEY:SECRET"
14 parser.error(msg)
15
16+ # check which file monitor to use
17+ monitor_class = yield get_filemonitor_class(options.fs_monitor)
18+
19 main = Main(options.root_dir, options.shares_dir, options.data_dir,
20 partials_dir, host=options.host, port=int(options.port),
21 dns_srv=options.dns_srv, ssl=True,
22@@ -224,7 +228,8 @@
23 write_limit=options.bandwidth_throttling_write_limit,
24 throttling_enabled=options.bandwidth_throttling_on,
25 ignore_files=options.ignore,
26- oauth_credentials=oauth_credentials)
27+ oauth_credentials=oauth_credentials,
28+ monitor_class=monitor_class)
29
30 # override the reactor default signal handlers in order to
31 # shutdown properly
32
33=== modified file 'data/syncdaemon.conf'
34--- data/syncdaemon.conf 2012-06-20 15:19:12 +0000
35+++ data/syncdaemon.conf 2012-07-19 14:25:29 +0000
36@@ -106,6 +106,11 @@
37 memory_pool_limit.parser = int
38 memory_pool_limit.help = How many AQ Commands will be kept in memory to execute.
39
40+fs_monitor.default = default
41+fs_monitor.metavar = MONITOR_TYPE
42+fs_monitor.help = Set the file monitor to be used to get the events from the
43+ file system.
44+
45
46 [notifications]
47 show_all_notifications.default = True
48@@ -130,6 +135,7 @@
49 write_limit.metavar = UPLOAD_LIMIT
50 write_limit.help = Set the upload limit (bytes/sec).
51
52+
53 [debug]
54 lsprof_file.metavar = FILE
55 lsprof_file.help = Profile execution using the lsprof profiler, and write the
56
57=== modified file 'tests/platform/filesystem_notifications/test_darwin.py'
58--- tests/platform/filesystem_notifications/test_darwin.py 2012-07-19 14:25:29 +0000
59+++ tests/platform/filesystem_notifications/test_darwin.py 2012-07-19 14:25:29 +0000
60@@ -1387,3 +1387,10 @@
61
62 self.assertTrue(d1.result, "Should not be called yet.")
63 self.assertTrue(d2, "Should not be called yet.")
64+
65+ @defer.inlineCallbacks
66+ def test_is_available_monitor(self):
67+ """Test test the is_available_monitor method."""
68+ # we should always return true
69+ is_available = yield common.FilesystemMonitor.is_available_monitor()
70+ self.assertTrue(is_available, 'Should always be available.')
71
72=== modified file 'tests/platform/filesystem_notifications/test_fsevents_daemon.py'
73--- tests/platform/filesystem_notifications/test_fsevents_daemon.py 2012-07-19 14:25:29 +0000
74+++ tests/platform/filesystem_notifications/test_fsevents_daemon.py 2012-07-19 14:25:29 +0000
75@@ -30,10 +30,11 @@
76
77 import os
78
79-from twisted.internet import defer
80+from twisted.internet import defer, protocol
81
82 from contrib.testing.testcase import BaseTwistedTestCase
83 from ubuntuone.darwin import fsevents
84+from ubuntuone.devtools.testcases.txsocketserver import TidyUnixServer
85 from ubuntuone.platform.filesystem_notifications.monitor.darwin import (
86 fsevents_daemon,
87 )
88@@ -44,6 +45,20 @@
89 IN_MOVED_TO,
90 )
91
92+class FakeServerProtocol(protocol.Protocol):
93+ """A test protocol."""
94+
95+ def dataReceived(self, data):
96+ """Echo the data received."""
97+ self.transport.write(data)
98+
99+
100+class FakeServerFactory(protocol.ServerFactory):
101+ """A factory for the test server."""
102+
103+ protocol = FakeServerProtocol
104+
105+
106 class FakeDaemonEvent(object):
107 """A fake daemon event."""
108
109@@ -56,7 +71,7 @@
110 class FakeProcessor(object):
111 """A fake processor."""
112
113- def __init__(self):
114+ def __init__(self, *args):
115 """Create a new instance."""
116 self.processed_events = []
117
118@@ -68,9 +83,9 @@
119 class FakePyInotifyEventsFactory(object):
120 """Fake factory."""
121
122- def __init__(self, processor):
123+ def __init__(self):
124 """Create a new instance."""
125- self.processor = processor
126+ self.processor = FakeProcessor()
127 self.called = []
128 self.watched_paths = []
129 self.ignored_paths = []
130@@ -369,13 +384,13 @@
131 def setUp(self):
132 """Set the tests."""
133 yield super(FilesystemMonitorTestCase, self).setUp()
134- self.processor = FakeProcessor()
135- self.factory = FakePyInotifyEventsFactory(self.processor)
136+ self.patch(fsevents_daemon, 'NotifyProcessor', FakeProcessor)
137+ self.factory = FakePyInotifyEventsFactory()
138 self.protocol = FakeProtocol()
139 self.monitor = fsevents_daemon.FilesystemMonitor(None, None)
140+ self.processor = self.monitor._processor
141
142 # override default objects
143- self.monitor._processor = self.processor
144 self.monitor._factory = self.factory
145
146 # patch the connect
147@@ -437,3 +452,32 @@
148 self.factory.ignored_paths.append(dirpath)
149 yield self.monitor.add_watch(dirpath)
150 self.assertNotIn('add_path', self.protocol.called)
151+
152+ @defer.inlineCallbacks
153+ def test_is_available_monitor_running(self):
154+ """Test the method when it is indeed running."""
155+ monitor_cls = fsevents_daemon.FilesystemMonitor
156+
157+ # start a fake server for the test
158+ server = TidyUnixServer()
159+ yield server.listen_server(FakeServerFactory)
160+ self.addCleanup(server.clean_up)
161+
162+ # set the path
163+ old_socket = fsevents_daemon.DAEMON_SOCKET
164+ fsevents_daemon.DAEMON_SOCKET = server.path
165+ self.addCleanup(setattr, fsevents_daemon, 'DAEMON_SOCKET', old_socket)
166+
167+ result = yield monitor_cls.is_available_monitor()
168+ self.assertTrue(result)
169+
170+ @defer.inlineCallbacks
171+ def test_is_available_monitor_fail(self):
172+ """Test the method when the daemon is not running."""
173+ monitor_cls = fsevents_daemon.FilesystemMonitor
174+ old_socket = fsevents_daemon.DAEMON_SOCKET
175+ fsevents_daemon.DAEMON_SOCKET += 'test'
176+ self.addCleanup(setattr, fsevents_daemon, 'DAEMON_SOCKET', old_socket)
177+
178+ result = yield monitor_cls.is_available_monitor()
179+ self.assertFalse(result)
180
181=== modified file 'tests/platform/filesystem_notifications/test_linux.py'
182--- tests/platform/filesystem_notifications/test_linux.py 2012-07-19 14:25:29 +0000
183+++ tests/platform/filesystem_notifications/test_linux.py 2012-07-19 14:25:29 +0000
184@@ -391,6 +391,14 @@
185 self.assertEqual(self.monitor._general_watchs, {})
186 self.assertEqual(self.monitor._ancestors_watchs, {})
187
188+ @defer.inlineCallbacks
189+ def test_is_available_monitor(self):
190+ """Test test the is_available_monitor method."""
191+ # we should always return true
192+ monitor_cls = filesystem_notifications.FilesystemMonitor
193+ is_available = yield monitor_cls.is_available_monitor()
194+ self.assertTrue(is_available, 'Should always be available.')
195+
196
197 class FakeEvent(object):
198 """A fake event."""
199
200=== modified file 'tests/platform/filesystem_notifications/test_windows.py'
201--- tests/platform/filesystem_notifications/test_windows.py 2012-07-19 14:25:29 +0000
202+++ tests/platform/filesystem_notifications/test_windows.py 2012-07-19 14:25:29 +0000
203@@ -1446,3 +1446,10 @@
204 # lets ensure that we never added the watches
205 self.assertEqual(0, len(monitor._watch_manager._wdm.values()),
206 'No watches should have been added.')
207+
208+ @defer.inlineCallbacks
209+ def test_is_available_monitor(self):
210+ """Test test the is_available_monitor method."""
211+ # we should always return true
212+ is_available = yield FilesystemMonitor.is_available_monitor()
213+ self.assertTrue(is_available, 'Should always be available.')
214
215=== modified file 'tests/syncdaemon/test_config.py'
216--- tests/syncdaemon/test_config.py 2012-07-17 18:36:13 +0000
217+++ tests/syncdaemon/test_config.py 2012-07-19 14:25:29 +0000
218@@ -700,6 +700,19 @@
219 self.assertEquals(self.cp.get('__main__', 'ignore').value,
220 ['.*\\.pyc', '.*\\.sw[opnx]'])
221
222+ def test_fs_monitor_not_default(self):
223+ """Test get monitor."""
224+ monitor_id = 'my_monitor'
225+ conf_file = os.path.join(self.test_root, 'test_new_config.conf')
226+ with open_file(conf_file, 'w') as fd:
227+ fd.write('[__main__]\n')
228+ fd.write('fs_monitor = %s\n' % monitor_id)
229+ self.assertTrue(path_exists(conf_file))
230+ self.cp.read([conf_file])
231+ self.cp.parse_all()
232+ self.assertEquals(self.cp.get('__main__', 'fs_monitor').value,
233+ monitor_id)
234+
235 def test_use_trash_default(self):
236 """Test default configuration for use_trash."""
237 self.cp.parse_all()
238
239=== modified file 'tests/syncdaemon/test_eventqueue.py'
240--- tests/syncdaemon/test_eventqueue.py 2012-04-09 20:07:05 +0000
241+++ tests/syncdaemon/test_eventqueue.py 2012-07-19 14:25:29 +0000
242@@ -37,6 +37,7 @@
243 from twisted.trial.unittest import TestCase
244
245 from contrib.testing.testcase import BaseTwistedTestCase, FakeVolumeManager
246+from ubuntuone.platform import FilesystemMonitor
247 from ubuntuone.syncdaemon import (
248 event_queue,
249 filesystem_manager,
250@@ -406,6 +407,22 @@
251 return self.shutdown_d
252
253
254+class EventQueueInitTestCase(TestCase):
255+ """Test the init of the EQ."""
256+
257+ def test_default_monitor(self):
258+ """Test the init with the default monitor."""
259+ eq = event_queue.EventQueue(None)
260+ self.assertIsInstance(eq.monitor, FilesystemMonitor)
261+ return eq.shutdown()
262+
263+ def test_passed_monitor(self):
264+ """Test the init with a custom monitor."""
265+ eq = event_queue.EventQueue(None, monitor_class=FakeMonitor)
266+ self.assertIsInstance(eq.monitor, FakeMonitor)
267+
268+
269+
270 class EventQueueShutdownTestCase(TestCase):
271 """Test the shutdown method in EQ."""
272
273
274=== modified file 'ubuntuone/platform/__init__.py'
275--- ubuntuone/platform/__init__.py 2012-06-28 10:26:50 +0000
276+++ ubuntuone/platform/__init__.py 2012-07-19 14:25:29 +0000
277@@ -101,6 +101,12 @@
278 stat_path = os_helper.stat_path
279 walk = os_helper.walk
280
281+# From Monitor
282+from ubuntuone.platform.filesystem_notifications import monitor
283+
284+get_filemonitor_class = monitor.get_filemonitor_class
285+FilesystemMonitor = monitor.FilesystemMonitor
286+
287 # From Logger
288 setup_filesystem_logging = logger.setup_filesystem_logging
289 get_filesystem_logger = logger.get_filesystem_logger
290
291=== modified file 'ubuntuone/platform/filesystem_notifications/__init__.py'
292--- ubuntuone/platform/filesystem_notifications/__init__.py 2012-07-19 14:25:29 +0000
293+++ ubuntuone/platform/filesystem_notifications/__init__.py 2012-07-19 14:25:29 +0000
294@@ -35,3 +35,4 @@
295
296 _GeneralINotifyProcessor = notify_processor.NotifyProcessor
297 FilesystemMonitor = monitor.FilesystemMonitor
298+get_filemonitor_class = monitor.get_filemonitor_class
299
300=== added directory 'ubuntuone/platform/filesystem_notifications/filesystem_monitor'
301=== added file 'ubuntuone/platform/filesystem_notifications/filesystem_monitor/__init__.py'
302=== modified file 'ubuntuone/platform/filesystem_notifications/monitor/__init__.py'
303--- ubuntuone/platform/filesystem_notifications/monitor/__init__.py 2012-07-19 14:25:29 +0000
304+++ ubuntuone/platform/filesystem_notifications/monitor/__init__.py 2012-07-19 14:25:29 +0000
305@@ -30,13 +30,66 @@
306
307 import sys
308
309-if sys.platform in ('win32', 'darwin'):
310- from ubuntuone.platform.filesystem_notifications.monitor import (
311- common,
312- )
313- FilesystemMonitor = common.FilesystemMonitor
314+from twisted.internet import defer
315+
316+DEFAULT_MONITOR = 'default'
317+
318+
319+class NoAvailableMonitorError(Exception):
320+ """Raised if there are no available monitors in the system."""
321+
322+
323+if sys.platform == 'win32':
324+ from ubuntuone.platform.filesystem_notifications.monitor import (
325+ common,
326+ )
327+
328+ FILEMONITOR_IDS = {
329+ DEFAULT_MONITOR: common.FilesystemMonitor,
330+ }
331+
332+elif sys.platform == 'darwin':
333+ from ubuntuone.platform.filesystem_notifications.monitor import darwin
334+ from ubuntuone.platform.filesystem_notifications.monitor import (
335+ common,
336+ )
337+
338+ FILEMONITOR_IDS = {
339+ DEFAULT_MONITOR: common.FilesystemMonitor,
340+ 'daemon': darwin.fsevents_daemon.FilesystemMonitor,
341+ }
342 else:
343 from ubuntuone.platform.filesystem_notifications.monitor import (
344 linux,
345 )
346- FilesystemMonitor = linux.FilesystemMonitor
347+
348+ FILEMONITOR_IDS = {
349+ DEFAULT_MONITOR: linux.FilesystemMonitor,
350+ }
351+
352+# mantain old API
353+FilesystemMonitor = FILEMONITOR_IDS[DEFAULT_MONITOR]
354+
355+
356+@defer.inlineCallbacks
357+def get_filemonitor_class(monitor_id=None):
358+ """Return the class to be used."""
359+ if monitor_id is None:
360+ # use default
361+ monitor_id = 'default'
362+
363+ if monitor_id not in FILEMONITOR_IDS:
364+ raise NoAvailableMonitorError(
365+ 'No available monitor with id "%s"could be found.' % monitor_id)
366+
367+ # retrieve the correct class and assert it can be used
368+ cls = FILEMONITOR_IDS[monitor_id]
369+ is_available = yield cls.is_available_monitor()
370+
371+ if is_available:
372+ defer.returnValue(cls)
373+ elif not is_available and monitor_id != DEFAULT_MONITOR:
374+ cls = yield get_filemonitor_class(DEFAULT_MONITOR)
375+ defer.returnValue(cls)
376+ else:
377+ raise NoAvailableMonitorError('No available monitor could be found.')
378
379=== modified file 'ubuntuone/platform/filesystem_notifications/monitor/common.py'
380--- ubuntuone/platform/filesystem_notifications/monitor/common.py 2012-07-19 14:25:29 +0000
381+++ ubuntuone/platform/filesystem_notifications/monitor/common.py 2012-07-19 14:25:29 +0000
382@@ -53,6 +53,7 @@
383 os_path,
384 )
385
386+
387 if sys.platform == 'darwin':
388 from ubuntuone.platform.filesystem_notifications.monitor.darwin import (
389 fsevents_client,
390@@ -371,6 +372,12 @@
391 self._processor = notify_processor.NotifyProcessor(self, ignore_config)
392 self._watch_manager = WatchManager(self._processor)
393
394+ @classmethod
395+ def is_available_monitor(cls):
396+ """Return if the monitor can be used in the platform."""
397+ # we can always use this monitor
398+ return defer.succeed(True)
399+
400 def add_to_mute_filter(self, event, **info):
401 """Add info to mute filter in the processor."""
402 self._processor.add_to_mute_filter(event, info)
403
404=== modified file 'ubuntuone/platform/filesystem_notifications/monitor/darwin/fsevents_daemon.py'
405--- ubuntuone/platform/filesystem_notifications/monitor/darwin/fsevents_daemon.py 2012-07-19 14:25:29 +0000
406+++ ubuntuone/platform/filesystem_notifications/monitor/darwin/fsevents_daemon.py 2012-07-19 14:25:29 +0000
407@@ -35,21 +35,29 @@
408
409 from twisted.internet import defer, endpoints, reactor
410
411+from ubuntu_sso.utils.tcpactivation import (
412+ ActivationConfig,
413+ ActivationInstance,
414+ AlreadyStartedError,
415+)
416+
417 from ubuntuone import logger
418 from ubuntuone.darwin import fsevents
419+from ubuntuone.platform.filesystem_notifications.notify_processor import (
420+ NotifyProcessor,
421+)
422 from ubuntuone.platform.filesystem_notifications.pyinotify_agnostic import (
423 Event,
424- ProcessEvent,
425 IN_OPEN,
426 IN_CLOSE_NOWRITE,
427 IN_CLOSE_WRITE,
428 IN_CREATE,
429- IN_IGNORED,
430 IN_ISDIR,
431 IN_DELETE,
432 IN_MOVED_FROM,
433 IN_MOVED_TO,
434- IN_MODIFY)
435+ IN_MODIFY,
436+)
437
438 TRACE = logger.TRACE
439
440@@ -91,6 +99,36 @@
441 # Path to the socket used by the daemon
442 DAEMON_SOCKET = '/var/run/ubuntuone_fsevents_daemon'
443
444+
445+class DescriptionFactory(object):
446+ """Factory that provides the server and client descriptions."""
447+
448+ client_description_pattern = 'unix:path=%s'
449+ server_description_pattern = 'unix:%s'
450+
451+ def __init__(self):
452+ """Create a new instance."""
453+ self.server = self.server_description_pattern % DAEMON_SOCKET
454+ self.client = self.client_description_pattern % DAEMON_SOCKET
455+
456+
457+def get_activation_config():
458+ """Get the configuration to activate the sso service."""
459+ description = DescriptionFactory()
460+ return ActivationConfig(None, None, description)
461+
462+
463+@defer.inlineCallbacks
464+def is_daemon_running():
465+ """Return if the sd is running by trying to get the port."""
466+ ai = ActivationInstance(get_activation_config())
467+ try:
468+ yield ai.get_server_description()
469+ defer.returnValue(False)
470+ except AlreadyStartedError:
471+ defer.returnValue(True)
472+
473+
474 def get_syncdaemon_valid_path(path):
475 """Return a valid encoded path."""
476 return unicodedata.normalize('NFC', path).encode('utf-8')
477@@ -99,7 +137,8 @@
478 class PyInotifyEventsFactory(fsevents.FsEventsFactory):
479 """Factory that process events and converts them in pyinotify ones."""
480
481- def __init__(self, processor, ignored_events=DARWIN_IGNORED_ACTIONS):
482+ def __init__(self, processor,
483+ ignored_events=DARWIN_IGNORED_ACTIONS):
484 """Create a new instance."""
485 # old style class
486 fsevents.FsEventsFactory.__init__(self)
487@@ -253,218 +292,24 @@
488 self._processor(pyinotify_event)
489
490
491-class NotifyProcessor(ProcessEvent):
492- """Mac OS X implementation of NotifyProcessor"""
493-
494- def __init__(self, monitor, ignore_config=None):
495- # XXX: avoid circular imports.
496- from ubuntuone.syncdaemon.filesystem_notifications import (
497- GeneralINotifyProcessor)
498- self.general_processor = GeneralINotifyProcessor(monitor,
499- self.handle_dir_delete, NAME_TRANSLATIONS,
500- self.platform_is_ignored, IN_IGNORED, ignore_config=ignore_config)
501- self.held_event = None
502-
503- def rm_from_mute_filter(self, event, paths):
504- """Remove event from the mute filter."""
505- self.general_processor.rm_from_mute_filter(event, paths)
506-
507- def add_to_mute_filter(self, event, paths):
508- """Add an event and path(s) to the mute filter."""
509- self.general_processor.add_to_mute_filter(event, paths)
510-
511- def platform_is_ignored(self, path):
512- """Should we ignore this path in the current platform.?"""
513- # don't support links yet
514- if os.path.islink(path):
515- return True
516- return False
517-
518- def is_ignored(self, path):
519- """Should we ignore this path?"""
520- return self.general_processor.is_ignored(path)
521-
522- def release_held_event(self, timed_out=False):
523- """Release the event on hold to fulfill its destiny."""
524- self.general_processor.push_event(self.held_event)
525- self.held_event = None
526-
527- def process_IN_MODIFY(self, event):
528- """Capture a modify event and fake an open ^ close write events."""
529- # lets ignore dir changes
530- if event.dir:
531- return
532- # on darwin we just get IN_MODIFY, lets always fake
533- # an OPEN & CLOSE_WRITE couple
534- raw_open = raw_close = {
535- 'wd': event.wd,
536- 'dir': event.dir,
537- 'name': event.name,
538- 'path': event.path}
539- # calculate the open mask
540- raw_open['mask'] = IN_OPEN
541- # create the event using the raw data, then fix the pathname param
542- open_event = Event(raw_open)
543- open_event.pathname = event.pathname
544- # push the open
545- self.general_processor.push_event(open_event)
546- raw_close['mask'] = IN_CLOSE_WRITE
547- close_event = Event(raw_close)
548- close_event.pathname = event.pathname
549- # push the close event
550- self.general_processor.push_event(close_event)
551-
552- def process_IN_MOVED_FROM(self, event):
553- """Capture the MOVED_FROM to maybe syntethize FILE_MOVED."""
554- if self.held_event is not None:
555- self.general_processor.log.warn('Lost pair event of %s',
556- self.held_event)
557- self.held_event = event
558-
559- def _fake_create_event(self, event):
560- """Fake the creation of an event."""
561- # this is the case of a MOVE from an ignored path (links for example)
562- # to a valid path
563- if event.dir:
564- evtname = "FS_DIR_"
565- else:
566- evtname = "FS_FILE_"
567- self.general_processor.eq_push(evtname + "CREATE", path=event.pathname)
568- if not event.dir:
569- self.general_processor.eq_push('FS_FILE_CLOSE_WRITE',
570- path=event.pathname)
571-
572- def _fake_delete_create_event(self, event):
573- """Fake the deletion and the creation."""
574- # this is the case of a MOVE from a watch UDF to a diff UDF which
575- # means that we have to copy the way linux works.
576- if event.dir:
577- evtname = "FS_DIR_"
578- else:
579- evtname = "FS_FILE_"
580- m = "Delete because of different shares: %r"
581- self.log.info(m, self.held_event.pathname)
582- self.general_processor.eq_push(evtname + "DELETE",
583- path=self.held_event.pathname)
584- self.general_processor.eq_push(evtname + "CREATE", path=event.pathname)
585- if not event.dir:
586- self.general_processor.eq_push('FS_FILE_CLOSE_WRITE',
587- path=event.pathname)
588-
589- def process_IN_MOVED_TO(self, event):
590- """Capture the MOVED_TO to maybe syntethize FILE_MOVED."""
591- if self.held_event is not None:
592- if event.cookie == self.held_event.cookie:
593- f_path_dir = os.path.split(self.held_event.pathname)[0]
594- t_path_dir = os.path.split(event.pathname)[0]
595-
596- is_from_forreal = not self.is_ignored(self.held_event.pathname)
597- is_to_forreal = not self.is_ignored(event.pathname)
598- if is_from_forreal and is_to_forreal:
599- f_share_id = self.general_processor.get_path_share_id(
600- f_path_dir)
601- t_share_id = self.general_processor.get_path_share_id(
602- t_path_dir)
603- if f_share_id != t_share_id:
604- # if the share_id are != push a delete/create
605- self._fake_delete_create_event(event)
606- else:
607- if event.dir:
608- evtname = "FS_DIR_"
609- else:
610- evtname = "FS_FILE_"
611- self.general_processor.eq_push(evtname + "MOVE",
612- path_from=self.held_event.pathname,
613- path_to=event.pathname)
614- elif is_to_forreal:
615- # this is the case of a MOVE from something ignored
616- # to a valid filename
617- self._fake_create_event(event)
618-
619- self.held_event = None
620- return
621- else:
622- # we should never get to this point on darwin, never ever!
623- self.release_held_event()
624- self.general_processor.push_event(event)
625- else:
626- # we should never get to this point on darwin, never ever!
627- self.general_processor.log.warn(
628- 'Cookie does not match the previous held event!')
629- self.general_processor.log.warn('Ignoring %s', event)
630-
631- def process_default(self, event):
632- """Push the event into the EventQueue."""
633- if self.held_event is not None:
634- self.release_held_event()
635- self.general_processor.push_event(event)
636-
637- def handle_dir_delete(self, fullpath):
638- """Some special work when a directory is deleted."""
639- self.general_processor.rm_watch(fullpath)
640-
641- # handle the case of move a dir to a non-watched directory
642- paths = self.general_processor.get_paths_starting_with(fullpath,
643- include_base=False)
644-
645- paths.sort(reverse=True)
646- for path, is_dir in paths:
647- m = "Pushing deletion because of parent dir move: (is_dir=%s) %r"
648- self.general_processor.log.info(m, is_dir, path)
649- if is_dir:
650- # same as the above remove
651- self.general_processor.rm_watch(path)
652-
653- self.general_processor.eq_push('FS_DIR_DELETE', path=path)
654- else:
655- self.general_processor.eq_push('FS_FILE_DELETE', path=path)
656-
657- def freeze_begin(self, path):
658- """Puts in hold all the events for this path."""
659- self.general_processor.freeze_begin(path)
660-
661- def freeze_rollback(self):
662- """Unfreezes the frozen path, reseting to idle state."""
663- self.general_processor.freeze_rollback()
664-
665- def freeze_commit(self, events):
666- """Unfreezes the frozen path, sending received events if not dirty.
667-
668- If events for that path happened:
669- - return True
670- else:
671- - push the here received events, return False
672- """
673- return self.general_processor.freeze_commit(events)
674-
675- @property
676- def mute_filter(self):
677- """Return the mute filter used by the processor."""
678- return self.general_processor.filter
679-
680- @property
681- def frozen_path(self):
682- """Return the frozen path."""
683- return self.general_processor.frozen_path
684-
685- @property
686- def log(self):
687- """Return the logger of the instance."""
688- return self.general_processor.log
689-
690-
691 class FilesystemMonitor(object):
692- """Implementation that allows to receive events from the sustem."""
693+ """Implementation that allows to receive events from the system."""
694
695 def __init__(self, eq, fs, ignore_config=None, timeout=1):
696 self.log = logging.getLogger('ubuntuone.SyncDaemon.FSMonitor')
697 self.log.setLevel(TRACE)
698+ self._processor = NotifyProcessor(self, ignore_config)
699 self.fs = fs
700 self.eq = eq
701- self._processor = NotifyProcessor(self, ignore_config)
702 self._factory = PyInotifyEventsFactory(self._processor)
703 self._protocol = None
704
705+ @classmethod
706+ def is_available_monitor(cls):
707+ """Return if the monitor can be used in the platform."""
708+ # can only be used if the daemon is running
709+ return is_daemon_running()
710+
711 @defer.inlineCallbacks
712 def _connect_to_daemon(self):
713 """Connect to the daemon so that we can receive events."""
714
715=== modified file 'ubuntuone/platform/filesystem_notifications/monitor/linux.py'
716--- ubuntuone/platform/filesystem_notifications/monitor/linux.py 2012-07-19 14:25:29 +0000
717+++ ubuntuone/platform/filesystem_notifications/monitor/linux.py 2012-07-19 14:25:29 +0000
718@@ -161,6 +161,12 @@
719 wm, self._inotify_notifier_antr)
720 self._ancestors_watchs = {}
721
722+ @classmethod
723+ def is_available_monitor(cls):
724+ """Return if the monitor can be used in the platform."""
725+ # we can always use this monitor
726+ return defer.succeed(True)
727+
728 def add_to_mute_filter(self, event, **info):
729 """Add info to mute filter in the processor."""
730 self._processor.add_to_mute_filter(event, info)
731
732=== modified file 'ubuntuone/syncdaemon/event_queue.py'
733--- ubuntuone/syncdaemon/event_queue.py 2012-06-28 10:26:50 +0000
734+++ ubuntuone/syncdaemon/event_queue.py 2012-07-19 14:25:29 +0000
735@@ -183,13 +183,17 @@
736 class EventQueue(object):
737 """Manages the events from different sources and distributes them."""
738
739- def __init__(self, fs, ignore_config=None):
740+ def __init__(self, fs, ignore_config=None, monitor_class=None):
741 self.listener_map = {}
742
743 self.log = logging.getLogger('ubuntuone.SyncDaemon.EQ')
744 self.fs = fs
745
746- self.monitor = FilesystemMonitor(self, fs, ignore_config)
747+ if monitor_class is None:
748+ # use the default class returned by platform
749+ self.monitor = FilesystemMonitor(self, fs, ignore_config)
750+ else:
751+ self.monitor = monitor_class(self, fs, ignore_config)
752
753 self.dispatching = False
754 self.dispatch_queue = collections.deque()
755
756=== modified file 'ubuntuone/syncdaemon/filesystem_notifications.py'
757--- ubuntuone/syncdaemon/filesystem_notifications.py 2012-04-09 20:07:05 +0000
758+++ ubuntuone/syncdaemon/filesystem_notifications.py 2012-07-19 14:25:29 +0000
759@@ -44,7 +44,6 @@
760
761 def __init__(self, monitor, handle_dir_delete, name_translations,
762 platform_is_ignored, ignore_mask, ignore_config=None):
763- super(GeneralINotifyProcessor, self).__init__()
764 self.log = logging.getLogger('ubuntuone.SyncDaemon.'
765 + 'filesystem_notifications.GeneralProcessor')
766 self.log.setLevel(TRACE)
767
768=== modified file 'ubuntuone/syncdaemon/main.py'
769--- ubuntuone/syncdaemon/main.py 2012-06-21 18:58:50 +0000
770+++ ubuntuone/syncdaemon/main.py 2012-07-19 14:25:29 +0000
771@@ -90,7 +90,7 @@
772 handshake_timeout=30,
773 shares_symlink_name='Shared With Me',
774 read_limit=None, write_limit=None, throttling_enabled=False,
775- ignore_files=None, oauth_credentials=None):
776+ ignore_files=None, oauth_credentials=None, monitor_class=None):
777 self.root_dir = root_dir
778 self.shares_dir = shares_dir
779 self.shares_dir_link = os.path.join(self.root_dir, shares_symlink_name)
780@@ -115,7 +115,8 @@
781 self.vm = volume_manager.VolumeManager(self)
782 self.fs = filesystem_manager.FileSystemManager(
783 data_dir, partials_dir, self.vm, self.db)
784- self.event_q = event_queue.EventQueue(self.fs, ignore_files)
785+ self.event_q = event_queue.EventQueue(self.fs, ignore_files,
786+ monitor_class=monitor_class)
787 self.fs.register_eq(self.event_q)
788
789 # subscribe VM to EQ, to be unsubscribed in shutdown

Subscribers

People subscribed via source and target branches