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
|
'''Bcfg2.Server.Core provides the runtime support for bcfg2 modules'''
__revision__ = '$Revision$'
from os import stat
from stat import ST_MODE, S_ISDIR
from sys import exc_info
from syslog import syslog, LOG_ERR
from traceback import extract_tb
from time import time
import _fam
from Bcfg2.Server.Generator import GeneratorError, GeneratorInitError
class PublishError(Exception):
'''This error is raised upon publication failures'''
pass
class fam(object):
'''The fam object is a set of callbacks for file alteration events'''
def __init__(self):
object.__init__(self)
self.fm = _fam.open()
self.users = {}
self.handles = {}
def fileno(self):
'''return fam file handle number'''
return self.fm.fileno()
def AddMonitor(self, path, obj=None):
'''add a monitor to path, installing a callback to obj.HandleEvent'''
mode = stat(path)[ST_MODE]
if S_ISDIR(mode):
handle = self.fm.monitorDirectory(path, None)
#print "adding callback for directory %s to %s, handle :%s:" % ( path, obj, handle.requestID())
else:
handle = self.fm.monitorFile(path, None)
self.handles[handle.requestID()] = handle
if obj != None:
self.users[handle.requestID()] = obj
return handle.requestID()
def HandleEvent(self):
'''Route a fam event to the proper callback'''
event = self.fm.nextEvent()
reqid = event.requestID
if self.users.has_key(reqid):
#print "dispatching event %s %s to obj %s handle :%s:" % (event.code2str(), event.filename, self.users[reqid], event.requestID)
self.users[reqid].HandleEvent(event)
class PublishedValue(object):
'''This is for data shared between generators'''
def __init__(self, owner, key, value):
object.__init__(self)
self.owner = owner
self.key = key
self.value = value
def Update(self, owner, value):
'''Update the value after an ownership check succeeds'''
if owner != self.owner:
raise PublishError, (self.key, owner)
self.value = value
class Core(object):
'''The Core object is the container for all Bcfg2 Server logic, and modules'''
def __init__(self, repository, structures, generators):
object.__init__(self)
self.datastore = repository
self.fam = fam()
self.pubspace = {}
self.structures = []
self.cron = {}
for structure in structures:
try:
mod = getattr(__import__("Bcfg2.Server.Structures.%s" %
(structure)).Server.Structures, structure)
except ImportError:
syslog(LOG_ERR, "Failed to load structure %s" % (structure))
continue
struct = getattr(mod, structure)
self.structures.append(struct(self, self.datastore))
self.generators = []
for generator in generators:
try:
mod = getattr(__import__("Bcfg2.Server.Generators.%s" %
(generator)).Server.Generators, generator)
except ImportError:
syslog(LOG_ERR, 'Failed to load generator %s' % (generator))
continue
gen = getattr(mod, generator)
try:
self.generators.append(gen(self, self.datastore))
except GeneratorInitError:
syslog(LOG_ERR, "Failed to instantiate generator %s" % gen.__name__)
except:
print "Unexpected initiantiation failure for generator %s" % gen.__name__
syslog(LOG_ERR, "Unexpected initiantiation failure for generator %s" % gen.__name__)
(trace, val, trb)=exc_info()
for line in extract_tb(trb):
syslog(LOG_ERR, ' File "%s", line %i, in %s\n %s\n'%line)
syslog(LOG_ERR, "%s: %s\n"%(trace, val))
del trace, val, trb
# we need to inventory and setup generators
# Process generator requirements
for gen in self.generators:
for prq in gen.__requires__:
if not self.pubspace.has_key(prq):
raise GeneratorError, (gen.name, prq)
gen.CompleteSetup()
def PublishValue(self, owner, key, value):
'''Publish a shared generator value'''
if not self.pubspace.has_key(key):
# This is a new entry
self.pubspace[key] = PublishedValue(owner, key, value)
else:
# This is an old entry. Update can fai
try:
self.pubspace[key].Update(owner, value)
except PublishError:
syslog(LOG_ERR, "Publish conflict for %s. Owner %s, Modifier %s"%
(key, self.pubspace[key].owner, owner))
def ReadValue(self, key):
'''Read a value published by another generator'''
if self.pubspace.has_key(key):
return self.pubspace[key].value
raise KeyError, key
def GetStructures(self, metadata):
'''Get all structures for client specified by metadata'''
return reduce(lambda x, y:x+y,
[struct.Construct(metadata) for struct in self.structures])
def BindStructure(self, structure, metadata):
'''Bind a complete structure'''
for entry in [child for child in structure.getchildren() if child.tag not in ['SymLink', 'Directory']]:
try:
self.Bind(entry, metadata)
except GeneratorError:
syslog(LOG_ERR, "Failed to bind entry: %s %s" % (entry.tag, entry.get('name')))
def Bind(self, entry, metadata):
'''Bind an entry using the appropriate generator'''
glist = [gen for gen in self.generators if
gen.__provides__.get(entry.tag, {}).has_key(entry.get('name'))]
if len(glist) == 1:
return glist[0].__provides__[entry.tag][entry.get('name')](entry, metadata)
elif len(glist) > 1:
print "Data Integrity error for %s %s" % (entry.tag, entry.get('name'))
else:
for gen in self.generators:
if hasattr(gen, "FindHandler"):
return gen.FindHandler(entry)(entry, metadata)
raise GeneratorError, (entry.tag, entry.get('name'))
def RunCronTasks(self):
'''Run periodic tasks for generators'''
generators = [gen for gen in self.generators if gen.__croninterval__]
for generator in generators:
current = time()
if ((current - self.cron.get(generator, 0)) > generator.__croninterval__):
generator.Cron()
self.cron[generator] = current
|