Created
October 18, 2013 04:34
-
-
Save ernestom/7036541 to your computer and use it in GitHub Desktop.
inotify watcher (using python bindings) to process new and modified files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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