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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
'''This file provides the Hostbase plugin. It manages dns/dhcp/nis host information'''
__revision__ = '$Revision$'
from syslog import syslog, LOG_INFO
from lxml.etree import XML
from Cheetah.Template import Template
from Bcfg2.Server.Plugin import Plugin, PluginExecutionError, PluginInitError, DirectoryBacked
class DataNexus(DirectoryBacked):
'''DataNexus is an object that watches multiple files and
handles changes in an intelligent fashion.'''
__name__ = 'DataNexus'
def __init__(self, path, filelist, fam):
self.files = filelist
DirectoryBacked.__init__(self, path, fam)
def HandleEvent(self, event):
'''Trap adds and updates, else call baseclass HandleEvent'''
action = event.code2str()
if action in ['exists', 'created']:
if (event.filename != self.name) and (event.filename not in self.files):
syslog(LOG_INFO, "%s:Got event for unexpected file %s" % (self.__name__, event.filename))
return
DirectoryBacked.HandleEvent(self, event)
if action != 'endExist' and event.filename != self.name:
self.rebuildState(event)
def rebuildState(self, event):
'''This function is called when underlying data has changed'''
pass
class Hostbase(Plugin, DataNexus):
'''The Hostbase plugin handles host/network info'''
__name__ = 'Hostbase'
__version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
filepath = '/etc/bind'
def __init__(self, core, datastore):
self.ready = False
files = ['dnsdata.xml', 'hostbase.xml', 'networks.xml']
Plugin.__init__(self, core, datastore)
try:
DataNexus.__init__(self, datastore + '/Hostbase/data',
files, self.core.fam)
except:
self.LogError("Failed to load data directory")
raise PluginInitError
self.xdata = {}
self.filedata = {}
self.templates = {'zone':Template(open(self.data + '/templates/' + 'zonetemplate.tmpl').read()),
'reversesoa':Template(open(self.data + '/templates/' + 'reversesoa.tmpl').read()),
'named':Template(open(self.data + '/templates/' + 'namedtemplate.tmpl').read()),
'reverseapp':Template(open(self.data + '/templates/' + 'reverseappend.tmpl').read()),
'dhcp':Template(open(self.data + '/templates/' + 'dhcpd_template.tmpl').read())}
self.Entries['ConfigFile'] = {}
def FetchFile(self, entry, metadata):
'''Return prebuilt file data'''
fname = entry.get('name').split('/')[-1]
if not self.filedata.has_key(fname):
raise PluginExecutionError
perms = {'owner':'root', 'group':'root', 'perms':'644'}
[entry.attrib.__setitem__(key, value) for (key, value) in perms.iteritems()]
entry.text = self.filedata[fname]
def rebuildState(self, event):
'''Pre-cache all state information for hostbase config files'''
if self.entries.has_key(event.filename) and not self.xdata.has_key(event.filename):
self.xdata[event.filename] = XML(self.entries[event.filename].data)
if [item for item in self.files if not self.entries.has_key(item)]:
return
# we might be able to rebuild data more sparsely,
# but hostbase.xml is the only one that will really change often
# rebuild zoneinfo
iplist = []
for zone in self.xdata['dnsdata.xml']:
zonehosts = []
for host in [host for host in self.xdata['hostbase.xml']
if host.get('domain') == zone.get('domain')]:
hostname = host.get('hostname')
if zone.get('domain') == 'mcs.anl.gov':
## special cases for the mcs.anl.gov domain
## all machines have a "-eth" entry as well as an entry identifying their subnet
## they also have their mail exchangers after every address
ipnodes = host.findall("interface/ip")
zonehosts.append((hostname, ipnodes[0].attrib['ip'], ipnodes[0].findall("name/mx"), None))
[zonehosts.append(("-".join([hostname, ipnode.attrib['dnssuffix']]), \
ipnode.attrib['ip'], ipnode.findall("name/mx"), None))
for ipnode in ipnodes]
[zonehosts.append(("-".join([hostname, namenode.attrib['name']]), \
ipnode.attrib['ip'], namenode.findall("mx"), None))
for ipnode in ipnodes
for namenode in ipnode
if namenode.attrib['name'] != ""]
else:
ipnodes = host.findall("interface/ip")
zonehosts.append((host.attrib['hostname'], ipnodes[0].attrib['ip'], None, None))
[zonehosts.append(("-".join([host.attrib['hostname'], namenode.attrib['name']]),
ipnode.attrib['ip'], None, None))
for ipnode in ipnodes
for namenode in ipnode
if namenode.attrib['name'] != ""]
[zonehosts.append((host.attrib['hostname'], None, None, cnamenode.attrib['cname']))
for cnamenode in host.findall("interface/ip/name/cname")
if cnamenode.attrib['cname'] != ""]
[iplist.append(ipnode.attrib['ip']) for ipnode in host.findall("interface/ip")]
zonehosts.sort()
self.templates['zone'].zone = zone
self.templates['zone'].root = self.xdata['dnsdata.xml']
self.templates['zone'].hosts = zonehosts
self.filedata[zone.get('domain')] = str(self.templates['zone'])
# now all zone forward files are built
iplist.sort()
filelist = []
temp = None
for x in range(len(iplist)-1):
addressparts = iplist[x].split(".")
if addressparts[:3] != iplist[x+1].split(".")[:3] and addressparts[:2] == iplist[x+1].split(".")[:2] \
and ".".join([addressparts[1], addressparts[0]]) not in filelist:
filelist.append(".".join([addressparts[1], addressparts[0]]))
elif addressparts[:3] != iplist[x+1].split(".")[:3] and \
addressparts[:2] != iplist[x+1].split(".")[:2] and \
".".join([addressparts[1], addressparts[0]]) not in filelist:
filelist.append(".".join([addressparts[2], addressparts[1], addressparts[0]]))
if x+1 == len(iplist) - 1:
temp = iplist[x+1].split(".")
if ".".join([temp[2], temp[1], temp[0]]) not in filelist \
and ".".join([temp[1], temp[0]]) not in filelist:
filelist.append(".".join([temp[2], temp[1], temp[0]]))
for filename in filelist:
self.templates['reversesoa'].inaddr = filename
self.templates['reversesoa'].zone = zone
self.templates['reversesoa'].root = self.xdata['dnsdata.xml']
self.filedata["%s.rev" % filename] = str(self.templates['reversesoa'])
self.templates['named'].zones = self.xdata['dnsdata.xml']
self.templates['named'].reverses = filelist
self.filedata["named.conf"] = str(self.templates['named'])
for filename in filelist:
originlist = []
towrite = filename.split(".")
towrite.reverse()
if len(towrite) > 2:
self.templates['reverseapp'].hosts = [(ipnode.get('ip').split('.'), host.get('hostname'),
host.get('domain'), ipnode.get('num'), ipnode.get('dnssuffix'))
for host in self.xdata['hostbase.xml']
for ipnode in host.findall('interface/ip')
if ipnode.get('ip').split('.')[:3] == towrite]
self.templates['reverseapp'].inaddr = filename
self.templates['reverseapp'].fileorigin = None
self.filedata["%s.rev" % filename] += str(self.templates['reverseapp'])
else:
revhosts = [(ipnode.get('ip').split('.'), host.get('hostname'), host.get('domain'),
ipnode.get('num'), ipnode.get('dnssuffix'))
for host in self.xdata['hostbase.xml']
for ipnode in host.findall("interface/ip")
if ipnode.get('ip').split(".")[:2] == towrite]
[originlist.append(".".join([reversehost[0][2], reversehost[0][1], reversehost[0][0]]))
for reversehost in revhosts
if ".".join([reversehost[0][2], reversehost[0][1], reversehost[0][0]]) not in originlist]
revhosts.sort()
originlist.sort()
for origin in originlist:
outputlist = [rhost for rhost in revhosts
if ".".join([rhost[0][2], rhost[0][1], rhost[0][0]]) == origin]
self.templates['reverseapp'].fileorigin = filename
self.templates['reverseapp'].hosts = outputlist
self.templates['reverseapp'].inaddr = origin
self.filedata["%s.rev" % filename] += str(self.templates['reverseapp'])
self.buildDHCP()
for key in self.filedata:
self.Entries['ConfigFile']["%s/%s" % (self.filepath, key)] = self.FetchFile
def buildDHCP(self):
'''Pre-build dhcpd.conf and stash in the filedata table'''
if 'networks.xml' not in self.xdata.keys():
print "not running before networks is cached"
return
networkroot = self.xdata['networks.xml']
if 'hostbase.xml' not in self.xdata.keys():
print "not running before hostbase is cached"
return
hostbase = self.xdata['hostbase.xml']
vlanandsublist = []
subnets = networkroot.findall("subnet")
for vlan in networkroot.findall("vlan"):
vlansubs = vlan.findall("subnet")
vlansubs.sort(lambda x, y: cmp(x.get("address"), y.get("address")))
vlanandsublist.append((vlan, vlansubs))
subnets140 = [subnet for subnet in subnets if subnet.attrib['address'].split(".")[0] == "140"]
privatesubnets = [subnet for subnet in subnets if subnet.attrib['address'].split(".")[0] != "140"]
subnets140.sort(lambda x, y: cmp(x.get("address"), y.get("address")))
privatesubnets.sort(lambda x, y: cmp(x.get("address"), y.get("address")))
dhcphosts = [host for host in hostbase if host.get('dhcp') == 'y' \
and host.find("interface").get('mac') != 'float' \
and host.find("interface").get('mac') != ""]
hosts = []
for host in dhcphosts:
if len(host.findall("interface")) == 1 and len(host.findall("interface/ip")) == 1:
hosts.append([host.get('hostname'), host.get('domain'), \
host.find("interface").get('mac'), \
host.find("interface/ip").get('ip')])
elif len(host.findall("interface")) > 1:
count = 0
for interface in host.findall("interface"):
if count == 0 and interface.find("ip") is not None:
hostdata = [host.get('hostname'), host.get('domain'), \
interface.get('mac'), interface.find("ip").get('ip')]
elif count != 0 and interface.find("ip") is not None:
hostdata = [host.get('hostname'), "-".join([host.get('domain'), str(count)]), \
interface.get('mac'), interface.find("ip").get('ip')]
if len(interface.findall("ip")) > 1:
for ipnode in interface.findall("ip")[1:]:
hostdata[3] = ", ".join([hostdata[3], ipnode.get('ip')])
count += 1
hosts.append(hostdata)
hosts.sort(lambda x, y: cmp(x[0], y[0]))
self.templates['dhcp'].hosts = hosts
self.templates['dhcp'].privatesubnets = privatesubnets
self.templates['dhcp'].subnets140 = subnets140
self.templates['dhcp'].vlans = vlanandsublist
self.templates['dhcp'].networkroot = networkroot
self.filedata['/etc/dhcpd.conf'] = str(self.templates['dhcp'])
|