Merge lp:~mandel/ubuntuone-client/daemon-options into lp:ubuntuone-client
- daemon-options
- Merge into trunk
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 |
Related bugs: |
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/
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
> 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
> 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
> platform/
> it looks like it's never accessed as
> filesystem_
> filesystem_
> platform/
>
> 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/
> and filesystem_
> just get it from platform.
> everywhere - we already do this in a few places.
>
> - syncdaemon/
> platform.
> from platform. (seems like those should be the same)
>
> - test_windows and test_darwin import it from
> u.platform.
> gets it from platform.
>
> # related to the above: _GeneralINotify
> used in test_filesystem
> remove the assignment in filesystem_
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?
Diego Sarmentero (diegosarmentero) wrote : | # |
== Python Lint Notices ==
./tests/
399: local variable 'is_available' is assigned to but never used
400: undefined name 'is_availabe'
make: *** [lint] Error 2
Mike McCracken (mikemc) wrote : | # |
A couple more places where s/active/available/ needs to happen:
- the NoActiveMonitor
- the assertion messages in filesystem_
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The prerequisite https:/
- 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
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 |
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: _class exception strings say "Not active monitor", I think you mean "no active…"?
- "test test" in a couple docstrings
- get_filemonitor
(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? notifications. get_filemonitor _class, but always as notifications. monitor. get_filemonitor _class, (eg in __init_ _.py).
it looks like it's never accessed as
filesystem_
filesystem_
platform/
So, that assignment is unnecessary, and
# importing filesystemMonitor - there are a lot of ways to import __init_ _.py, notifications/ __init_ _.py, since it's possible to filesystem_ notifications. monitor
it. I think we could remove the assignments in platform/
and filesystem_
just get it from platform.
everywhere - we already do this in a few places.
- syncdaemon/ event_queue. py imports it from filesystem_ notifications, while test_eventqueue imports it
platform.
from platform. (seems like those should be the same)
- test_windows and test_darwin import it from filesystem_ notifications. monitor. common, while test_linux filesystem_ notifications.
u.platform.
gets it from platform.
# related to the above: _GeneralINotify Processor seems like it's only _notifications. maybe we should change that and notifications/ __init_ _.py:36
used in test_filesystem
remove the assignment in filesystem_