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
158
159
160
161
162
163
164
165
166
167
168
169
170
|
"""Bcfg2.Server.FileMonitor provides the support for monitoring files."""
import os
import sys
import fnmatch
import logging
from time import sleep, time
LOGGER = logging.getLogger(__name__)
class Event(object):
""" Base class for all FAM events """
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):
""" log a debug message """
if self.debug:
LOGGER.info(msg)
def should_ignore(self, event):
""" returns true if an event should be ignored """
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):
""" returns True if there are pending events """
return bool(self.events)
def get_event(self):
""" get the oldest pending event """
return self.events.pop(0)
def fileno(self):
""" get the file descriptor of the file monitor thread """
return 0
def handle_one_event(self, event):
""" handle the given event by dispatching it to the object
that handles events for the path """
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: # pylint: disable=W0702
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):
""" Handle all pending events """
if not self.started:
self.start()
count = 1
event = self.get_event()
start = time()
if lock:
lock.acquire()
self.handle_one_event(event)
while self.pending():
self.handle_one_event(self.get_event())
count += 1
if lock:
lock.release()
end = time()
LOGGER.info("Handled %d events in %.03fs" % (count, (end - start)))
def handle_events_in_interval(self, interval):
""" handle events for the specified period of time (in
seconds) """
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):
""" shutdown the monitor """
self.started = False
def AddMonitor(self, path, obj, handleID=None):
""" watch the specified path, alerting obj to events """
raise NotImplementedError
available = dict() # pylint: disable=C0103
# 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
|