Watchdog (osx) not notifying on remote network changes
Asked Answered
T

2

8

I'm using Watchdog to monitor a network directory, non-recursive, for a specific pattern of files to be created over time. The issue I am seeing is that while it works fantastic when I test locally, if I make changes to the monitored directory from a remote machine, the events are not triggered.

Here are the specific details of my configuration:

  • OSX
  • monitoring a single directory, non-recursive, on an NFS mount
  • python 2.6

An example of my problem can be easily reproduced by using the stock example snippet:

import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler

if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO,
                        format='%(asctime)s - %(message)s',
                        datefmt='%Y-%m-%d %H:%M:%S')
    event_handler = LoggingEventHandler()
    observer = Observer()
    observer.schedule(event_handler, path=sys.argv[1], recursive=False)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

If you start this on a network directory, and then make changes from the same system, the events are dispatched. But if you then make changes to the directory from another machine on the network, no events are dispatched.

Am I missing something regarding limitations of kqueue (or could be FSEvents on OSX, since it says its preferred first by Watchdog)?

I was stoked on this python package, and was going to start using it for other scripts to replace filesystem polling, but I can't seem to find any information regarding why I am seeing this issue.

Update

I also tested MacFSEvents and got the same problem. Then I modified the above test script to forcibly try different observers:

# does not work with remote changes
from watchdog.observers.fsevents import FSEventsObserver as Observer
# does not work with remote changes
from watchdog.observers.kqueue import KqueueObserver as Observer
# only option that works because its actually polling every second
from watchdog.observers.polling import PollingObserver as Observer

So at least for now, I can use the polling observer and not have to modify my code, until someone can shed some light on the real problem I am having.

Telemotor answered 28/2, 2012 at 23:7 Comment(0)
I
8

I'm pretty sure filesystem events don't work over NFS - the reason being that the way the kernel manages file system events normally is that it has a layer in the kernel that triggers on activity - with NFS there's no facility for notifying of changes, you can only get a list of inodes, write some blocks, read some blocks.. etc. It's pretty minimal.

For filesystem events to work over NFS, you would have to be constantly polling the NFS server.

There may be some facility for this with AFP, which if you've got the juice you can just install netatalk and try that.

If you absolutely need to do something like this and netatalk doesn't do it - your best bet is to get OSXFuse up and running and write an NFS overlay that actually does just sit there and poll for changes. But, you'll be limited to 'truncated,modified,appended,deleted', etc.

See Also: Use libfuse in a project, without root access (for installation)? FTP mounts & inotify/kqueue/FSEvents

Israelitish answered 29/2, 2012 at 12:18 Comment(3)
Really good info so far! Do you have any idea why nfs.server.fsevents is a configurable nfs.conf setting? developer.apple.com/library/mac/documentation/Darwin/Reference/…Telemotor
nfs.server.fsevents works on NFS filesystems that the mac is exporting to the world, not the mounts it's mounting on remote machines.Israelitish
Perfect. Thanks! Upvoted and accepted. I will just stick with the Polling observer for watchdogTelemotor
G
0

in case that you want to have a Watchdog in a mounted directory PollingObserver should be used instead of Observer

import time
from watchdog.observers import PollingObserver
from watchdog.events import FileSystemEventHandler

class MyHandler(FileSystemEventHandler):
    def on_any_event(self, event):
        print(event)

if __name__ == "__main__":
    event_handler = MyHandler()
    observer = PollingObserver()
    observer.schedule(event_handler, path='.', recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()
Gazetteer answered 28/6, 2023 at 12:52 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Lamellirostral

© 2022 - 2025 — McMap. All rights reserved.