summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/FileMonitor/__init__.py
blob: fd0cb66f1fceda01ace9a508767bf1fd74507fb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
"""Bcfg2.Server.FileMonitor provides the support for monitoring files."""

import os
import sys
import fnmatch
import logging
import pkgutil
from time import sleep, time

logger = logging.getLogger(__name__)

class Event(object):
    def __init__(self, request_id, filename, code):
        self.requestID = request_id
        self.filename = filename
        self.action = code

    def code2str(self):
        """return static code for event"""
        return self.action

    def __str__(self):
        return "%s: %s %s" % (self.__class__.__name__,
                              self.filename, self.action)

    def __repr__(self):
        return "%s (request ID %s)" % (str(self), self.requestID)


class FileMonitor(object):
    """File Monitor baseclass."""
    def __init__(self, ignore=None, debug=False):
        object.__init__(self)
        self.debug = debug
        self.handles = dict()
        self.events = []
        if ignore is None:
            ignore = []
        self.ignore = ignore
        self.started = False

    def __str__(self):
        return "%s: %s" % (__name__, self.__class__.__name__)

    def __repr__(self):
        return "%s (%s events, fd %s)" % (self.__class__.__name__,
                                          len(self.events),
                                          self.fileno())

    def start(self):
        """ start threads or anything else that needs to be done after
        the server forks and daemonizes """
        self.started = True

    def debug_log(self, msg):
        if self.debug:
            logger.info(msg)

    def should_ignore(self, event):
        for pattern in self.ignore:
            if (fnmatch.fnmatch(event.filename, pattern) or 
                fnmatch.fnmatch(os.path.split(event.filename)[-1], pattern)):
                self.debug_log("Ignoring %s" % event)
                return True
        return False

    def pending(self):
        return bool(self.events)

    def get_event(self):
        return self.events.pop(0)

    def fileno(self):
        return 0

    def handle_one_event(self, event):
        if not self.started:
            self.start()
        if self.should_ignore(event):
            return
        if event.requestID not in self.handles:
            logger.info("Got event for unexpected id %s, file %s" %
                        (event.requestID, event.filename))
            return
        self.debug_log("Dispatching event %s %s to obj %s" %
                       (event.code2str(), event.filename,
                        self.handles[event.requestID]))
        try:
            self.handles[event.requestID].HandleEvent(event)
        except:
            err = sys.exc_info()[1]
            logger.error("Error in handling of event %s for %s: %s" %
                         (event.code2str(), event.filename, err))

    def handle_event_set(self, lock=None):
        if not self.started:
            self.start()
        count = 1
        event = self.get_event()
        start = time()
        if lock:
            lock.acquire()
        try:
            self.handle_one_event(event)
            while self.pending():
                self.handle_one_event(self.get_event())
                count += 1
        except:
            pass
        if lock:
            lock.release()
        end = time()
        logger.info("Handled %d events in %.03fs" % (count, (end - start)))

    def handle_events_in_interval(self, interval):
        if not self.started:
            self.start()
        end = time() + interval
        while time() < end:
            if self.pending():
                self.handle_event_set()
                end = time() + interval
            else:
                sleep(0.5)

    def shutdown(self):
        self.started = False


available = dict()

# todo: loading the monitor drivers should be automatic
from Bcfg2.Server.FileMonitor.Pseudo import Pseudo
available['pseudo'] = Pseudo

try:
    from Bcfg2.Server.FileMonitor.Fam import Fam
    available['fam'] = Fam
except ImportError:
    pass

try:
    from Bcfg2.Server.FileMonitor.Gamin import Gamin
    available['gamin'] = Gamin
except ImportError:
    pass

try:
    from Bcfg2.Server.FileMonitor.Inotify import Inotify
    available['inotify'] = Inotify
except ImportError:
    pass    

for fdrv in sorted(available.keys(), key=lambda k: available[k].__priority__):
    if fdrv in available:
        available['default'] = available[fdrv]
        break