Skip to content

Instantly share code, notes, and snippets.

@ernestom
Created October 18, 2013 04:34
Show Gist options
  • Save ernestom/7036541 to your computer and use it in GitHub Desktop.
Save ernestom/7036541 to your computer and use it in GitHub Desktop.
inotify watcher (using python bindings) to process new and modified files.
#!/usr/bin/env python
"""
(c) Menta Network - www.menta.mx
Example usage:
$LOG=/var/log/inotifywait.log
python inotifywait.py \
/srv/www/images \
scp -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
-i ~/.ssh/private_key \
%s [email protected]:/123456/images/ \
2>&1 >> $LOG
IMPORTANT:
The number in /proc/sys/fs/inotify/max_user_watches must be greater
than the total files being watched per instance.
"""
import os
import sys
import subprocess
import logging
try:
from pyinotify import (
WatchManager, Notifier, ProcessEvent,
IN_CLOSE_WRITE, IN_MOVED_TO, IN_DELETE
)
except ImportError:
print 'Error:\nThe `pyinotify` module must be installed.\nRun `pip install pyinotify`.\n'
sys.exit(-1)
logger = logging.getLogger(sys.argv[0])
class EventManager(ProcessEvent):
"""Main event processor/callback."""
callback_command = None
def process_default(self, event):
"""Process the watched events."""
if event.dir:
logger.debug('Directory event ignored: %s' % event)
elif event.mask & IN_DELETE:
logger.warning('Deletion event ignored: %s' % event)
elif self.execute_callback_command(event.pathname):
logger.info('File: %s processed from event: %s' % (event.pathname, event))
else:
logger.error('Event failed: %s' % event)
def execute_callback_command(self, path):
if not self.callback_command:
raise Exception('EventManager.callback_command not set')
command = self.callback_command % path
output = None
try:
output = subprocess.check_output(command.split(), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
logger.critical('Command: %s failed: %s', (command, output))
return False
logger.debug('Command: %s done' % command)
return True
def monitor(path, callback_command):
"""Recursively monitor new files or updates to the existing ones,
including renames, but not deletions."""
full_path = os.path.abspath(path)
if not os.path.exists(full_path):
raise Exception('The path: %s does not exist')
events = IN_CLOSE_WRITE | IN_MOVED_TO | IN_DELETE
event_manager = EventManager()
event_manager.callback_command = callback_command
watch_manager = WatchManager()
watch_manager.add_watch(full_path, events, rec=True, auto_add=False)
notifier = Notifier(watch_manager, default_proc_fun=event_manager)
notifier.loop(callback=None)
def start_logging():
"""Configure and start the logger."""
console_handler = logging.StreamHandler()
format = '[%(asctime)s %(name)s %(levelname)s] %(message)s'
console_handler.setFormatter(logging.Formatter(format))
logger.addHandler(console_handler)
logger.setLevel(logging.DEBUG)
logger.info('Process started')
def usage():
print 'Usage: %s /dir/to/monitor scp %%s ' % sys.argv[0]
sys.exit(-2)
def main():
try:
path = sys.argv[1]
callback_command = ' '.join(sys.argv[2:])
except IndexError:
usage()
if not path or not callback_command:
usage()
start_logging()
monitor(path, callback_command)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment