Commits

Rodrigo Bistolfi committed 7a67e9b

Initial commit

Comments (0)

Files changed (9)

+*pyc
+*swp
+#*#
+*~
+dist
+build
+*.egg-info
+_trial_temp
+###############################
+VectorLinux repo monitor daemon
+###############################
+
+
+This is a Twisted/INotify daemon for listening to changes in the repository
+filesystem tree. Repository metadata needs to be updated each time a change is
+made in a package. With the repository monitor we can automate these tasks.
+
+
+Install
+=======
+
+    $ python setup.py install
+
+
+Running
+=======
+
+The install rutine will put service scripts in /etc/rc.d/rc.repomonitor
+
+    $ /etc/rc.d/rc.repomonitor {start|stop|restart}
+
+The repository location is read from the `VL_REPOSITORY_ROOT` environment
+variable, which should be an absolute path.
+
+
+Testing
+=======
+
+We use Twisted's Trial module for tests, from the source top directory run
+
+    $ trial repomonitor
+#!/bin/sh -e
+#
+# rc.repowatcher
+# Starts a daemon that listen for filesystem events in a repository root
+# rbistolfi - 2012
+#
+
+
+REPO_ROOT=/tmp
+TAC_FILE=/usr/lib/repowatcher/service.tac
+LOG_FILE=/var/log/repowatcher.log
+PID_FILE=/var/run/repowatcher.pid
+TWISTD=`which twistd`
+
+
+case $1 in
+	start)
+        VL_REPO_ROOT=$REPO_ROOT $TWISTD --logfile $LOG_FILE --pidfile $PID_FILE -y $TAC_FILE
+	;;
+	stop)
+	if [ -e $PID_FILE ]; then
+		PID=`cat $PID_FILE`
+	else
+		echo "VL-RepoWatcher is NOT running."
+		exit 1
+	fi
+        kill -INT $PID
+	;;
+	restart)
+        $0 stop
+        $0 start
+	;;
+	status)
+		if [ -e $PID_FILE ]; then
+			PID=`cat $PID_FILE`
+			echo "VL-RepoWatcher is running (pid $PID)."
+			exit 0
+		else
+			echo "VL-RepoWatcher is NOT running."
+			exit 1
+		fi	
+	;;
+	*)
+        echo "Usage: $0 {start|stop|restart}"
+		exit 1
+	;;
+esac
Add a comment to this file

repomonitor/__init__.py

Empty file added.

repomonitor/inotify.py

+# coding: utf8
+
+"""Provides a base class for handlers of inotify events
+
+"""
+
+
+__all__ = ["INotifyEventHandler"]
+
+
+import os, sys
+from twisted.internet import inotify, reactor
+from twisted.python import filepath, log
+from twisted.application import internet, service
+
+
+class INotifyEventHandler(object):
+    """A class handling INotify events.
+    Handlers are implemented as methods named with "on_" + the human readable
+    form of the mask, as defined in t.i.inotify
+
+    """
+    def __init__(self, root_path):
+        """Create an INotify object and expose the path to the root.
+
+        """
+        self.root = root_path
+        self.inotify = inotify.INotify()
+
+    def dispatch(self, ignored, filepath, mask):
+        """Dispatch an event to its handler if exists
+
+        """
+        for m in inotify.humanReadableMask(mask):
+            handler = "on_" + m
+            method = getattr(self, handler, None)
+            if method:
+                method(filepath, mask)
+
+    def register(self):
+        """Start reading events and add a watch
+
+        """
+        self.inotify.startReading()
+        self.inotify.watch(filepath.FilePath(self.root), callbacks=[self.dispatch])
+
+    def unregister(self):
+        """Stop reading and remove watchers
+
+        """
+        self.inotify.connectionLost("Shutting down")
+
+

repomonitor/repomonitor.py

+# coding: utf8
+
+"""Watch the root of a repo and run a callback
+
+"""
+
+
+import os, sys, subprocess
+from .inotify import *
+
+
+REPO_ROOT = os.environ["VL_REPOSITORY_ROOT"]
+PACKAGE_EXTENSIONS = (".txz", ".tlz", ".tbz", ".tgz")
+META_EXTENSIONS = (".meta", ".info")
+
+
+class RepositoryINotifyHandler(INotifyEventHandler):
+    """Watch a repository root for fs changes
+
+    """
+    def on_create(self, filepath, mask):
+        if ispackage(filepath):
+            log.msg("Package written: %s" % filepath)
+        elif ismeta(filepath):
+            log.msg("Metadata file created: %s" % filepath)
+
+    def on_modify(self, filepath, mask):
+        if ispackagestxt(filepath):
+            log.msg("PACKAGES.TXT updated")
+
+
+def update_repository():
+    """Run the repository scripts with subprocess
+
+    """
+    script = "/usr/libexec/openssh/sftp-server_repo-wrapper"
+    try:
+        subprocess.check_call(script)
+    except:
+        log.err()
+
+
+def ispackage(filepath):
+    """Return True if filepath is a path to a package
+
+    """
+    file_extension = os.path.splitext(filepath.path)[1]
+    return file_extension in PACKAGE_EXTENSIONS
+
+
+def ismeta(filepath):
+    """Return True if filepath is a .meta
+
+    """
+    file_extension = os.path.splitext(filepath.path)[1]
+    return file_extension in META_EXTENSIONS
+
+
+def ispackagestxt(filepath):
+    """Return True if filepath is PACKAGES.TXT
+
+    """
+    return filepath.path.endswith("PACKAGES.TXT")
+
+
+def main():
+    """Create a handler and register it
+
+    """
+    handler = RepositoryINotifyHandler(REPO_ROOT)
+    handler.register()
+    return handler

repomonitor/tests.py

+# coding: utf8
+
+
+"""Tests for repowatcher
+
+"""
+
+
+from twisted.trial import unittest
+from twisted.python import filepath
+from twisted.internet import defer
+from .inotify import INotifyEventHandler
+import os
+
+
+class INotifyEventHandlerTest(unittest.TestCase):
+
+    def setUp(self):
+        self.tests_dir = filepath.FilePath(self.mktemp())
+        self.dir = self.tests_dir.path
+        self.tests_dir.createDirectory()
+        self.handler = None
+
+    def tearDown(self):
+        self.tests_dir.remove()
+        self.handler.unregister()
+
+    @defer.inlineCallbacks
+    def testEventHandler(self):
+
+        d1 = defer.Deferred()
+        d2 = defer.Deferred()
+
+        class INotifyHandler(INotifyEventHandler):
+
+            called = []
+
+            def on_create(self, filepath, mask):
+                self.called.append("on_create")
+                d1.callback(1)
+
+            def on_modify(self, filepath, mask):
+                self.called.append("on_modify")
+                d2.callback(2)
+
+        self.handler = INotifyHandler(self.dir)
+        self.handler.register()
+        with open(os.path.join(self.dir, "test.txt"), "w") as f:
+            f.write("test")
+        yield d1
+        yield d2
+        self.assertEqual(self.handler.called, ['on_create', 'on_modify'])
+
+# coding: utf8
+
+
+import os
+import repomonitor
+from twisted.application import service, internet
+from twisted.python.log import ILogObserver, FileLogObserver
+from twisted.python.logfile import DailyLogFile
+
+
+repomonitor.main()
+application = service.Application("VL-RepoMonitor")
+logfile = DailyLogFile("repomonitor.log", "/tmp")
+application.setComponent(ILogObserver, FileLogObserver(logfile).emit)
+#!/usr/bin/env python
+
+from distutils.core import setup
+
+setup(name='repowatcher',
+      version='0.1a',
+      description='Twisted-INotify daemon for reacting to repository fs changes',
+      author='Rodrigo Bistolfi',
+      author_email='moc.liamg@iflotsibr'[::-1],
+      packages=["repomonitor"],
+      data_files=[("/usr/lib/repomonitor/", ["service.tac"]),
+          ("/etc/rc.d", ["rc.repomonitor"])],
+      )
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.