|
0 |
- |
1 |
#!/usr/bin/python2.7 |
|
|
2 |
# coding: utf-8 |
3 |
# |
4 |
# Univention Management Console module: |
5 |
# System Diagnosis UMC module |
6 |
# |
7 |
# Copyright 2017 Univention GmbH |
8 |
# |
9 |
# http://www.univention.de/ |
10 |
# |
11 |
# All rights reserved. |
12 |
# |
13 |
# The source code of this program is made available |
14 |
# under the terms of the GNU Affero General Public License version 3 |
15 |
# (GNU AGPL V3) as published by the Free Software Foundation. |
16 |
# |
17 |
# Binary versions of this program provided by Univention to you as |
18 |
# well as other copyrighted, protected or trademarked materials like |
19 |
# Logos, graphics, fonts, specific documentations and configurations, |
20 |
# cryptographic keys etc. are subject to a license agreement between |
21 |
# you and Univention and not subject to the GNU AGPL V3. |
22 |
# |
23 |
# In the case you use this program under the terms of the GNU AGPL V3, |
24 |
# the program is provided in the hope that it will be useful, |
25 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
27 |
# GNU Affero General Public License for more details. |
28 |
# |
29 |
# You should have received a copy of the GNU Affero General Public |
30 |
# License with the Debian GNU/Linux or Univention distribution in file |
31 |
# /usr/share/common-licenses/AGPL-3; if not, see |
32 |
# <http://www.gnu.org/licenses/>. |
33 |
|
34 |
import socket |
35 |
import ldap.filter |
36 |
import itertools as it |
37 |
|
38 |
import univention.admin.uldap |
39 |
import univention.admin.modules as udm_modules |
40 |
import univention.admin.objects as udm_objects |
41 |
|
42 |
import univention.config_registry |
43 |
from univention.management.console.modules.diagnostic import Warning |
44 |
|
45 |
from univention.lib.i18n import Translation |
46 |
_ = Translation('univention-management-console-module-diagnostic').translate |
47 |
|
48 |
title = _('Check nameserver entries on DNS zones') |
49 |
description = _('All nameserver entries are ok.') |
50 |
links = [{ |
51 |
'name': 'sdb', |
52 |
'href': _('http://sdb.univention.de/1273'), |
53 |
'label': _('Univention Support Database - Bind: zone transfer failed') |
54 |
}] |
55 |
|
56 |
|
57 |
class RecordNotFound(Exception): |
58 |
pass |
59 |
|
60 |
|
61 |
class ZoneError(Exception): |
62 |
def __init__(self, nameserver): |
63 |
self.nameserver = nameserver |
64 |
|
65 |
@property |
66 |
def zone(self): |
67 |
return self.nameserver.zone |
68 |
|
69 |
|
70 |
class NoHostRecord(ZoneError): |
71 |
def __str__(self): |
72 |
msg = _('Found no host record (A/AAAA record) for nameserver {ns}.') |
73 |
return msg.format(ns=self.nameserver.nameserver()) |
74 |
|
75 |
|
76 |
class CnameAsNameServer(ZoneError): |
77 |
def __str__(self): |
78 |
msg = _('Found illegal alias record (CNAME record) for nameserver {ns}.') |
79 |
return msg.format(ns=self.nameserver.nameserver()) |
80 |
|
81 |
|
82 |
class Zone(object): |
83 |
def __init__(self, udm_zone, domainname): |
84 |
self.udm_zone = udm_zone |
85 |
self.domainname = domainname |
86 |
|
87 |
@property |
88 |
def kind(self): |
89 |
return self.udm_zone.module |
90 |
|
91 |
@property |
92 |
def zone(self): |
93 |
if self.kind == 'dns/forward_zone': |
94 |
return self.udm_zone.get('zone') |
95 |
return self.udm_zone.get('subnet') |
96 |
|
97 |
def base(self): |
98 |
if self.kind == 'dns/forward_zone': |
99 |
return self.zone |
100 |
return '{}.in-addr.arpa'.format(self.zone) |
101 |
|
102 |
def nameserver(self): |
103 |
for nameserver in self.udm_zone.get('nameserver'): |
104 |
yield NameServer(self, nameserver) |
105 |
|
106 |
def umc_link(self): |
107 |
text = 'udm:dns/dns' |
108 |
link = { |
109 |
'module': 'udm', |
110 |
'flavor': 'dns/dns', |
111 |
'props': { |
112 |
'openObject': { |
113 |
'objectDN': self.udm_zone.dn, |
114 |
'objectType': self.kind, |
115 |
} |
116 |
} |
117 |
} |
118 |
return (text, link) |
119 |
|
120 |
|
121 |
class NameServer(object): |
122 |
def __init__(self, zone, nameserver): |
123 |
self.zone = zone |
124 |
self._nameserver = nameserver |
125 |
|
126 |
def is_qualified(self): |
127 |
return self._nameserver.endswith('.') |
128 |
|
129 |
def nameserver(self): |
130 |
return self._nameserver.rstrip('.') |
131 |
|
132 |
def fqdn(self): |
133 |
if self.is_qualified(): |
134 |
return self.nameserver() |
135 |
return '{}.{}'.format(self.nameserver(), self.zone.base()) |
136 |
|
137 |
def is_in_zone(self): |
138 |
return not self.is_qualified() or \ |
139 |
self.nameserver().endswith(self.zone.domainname) |
140 |
|
141 |
def _generate_splits(self, fqdn): |
142 |
zn = fqdn |
143 |
while True: |
144 |
(rdn, zn) = zn.split('.', 1) |
145 |
if rdn and zn: |
146 |
yield (rdn, zn) |
147 |
if '.' not in zn or zn == self.zone.domainname: |
148 |
break |
149 |
|
150 |
def build_filter(self): |
151 |
template = '(&(relativeDomainName=%s)(zoneName=%s))' |
152 |
expressions = (ldap.filter.filter_format(template, (rdn, zn)) |
153 |
for (rdn, zn) in self._generate_splits(self.fqdn())) |
154 |
return '(|{})'.format(''.join(expressions)) |
155 |
|
156 |
|
157 |
class UDM(object): |
158 |
def __init__(self): |
159 |
univention.admin.modules.update() |
160 |
(self.ldap_connection, self.position) = univention.admin.uldap.getMachineConnection() |
161 |
self.configRegistry = univention.config_registry.ConfigRegistry() |
162 |
self.configRegistry.load() |
163 |
|
164 |
def lookup(self, module_name, filter_expression=''): |
165 |
module = udm_modules.get(module_name) |
166 |
for instance in module.lookup(None, self.ldap_connection, filter_expression): |
167 |
instance.open() |
168 |
yield instance |
169 |
|
170 |
def find(self, nameserver): |
171 |
filter_expression = nameserver.build_filter() |
172 |
for (dn, attr) in self.ldap_connection.search(filter_expression): |
173 |
if dn: |
174 |
for module in udm_modules.identify(dn, attr): |
175 |
record = udm_objects.get(module, None, |
176 |
self.ldap_connection, self.position, dn, attr=attr, |
177 |
attributes=attr) |
178 |
record.open() |
179 |
return record |
180 |
raise RecordNotFound() |
181 |
|
182 |
def all_zones(self): |
183 |
domainname = self.configRegistry.get('domainname') |
184 |
for zone in self.lookup('dns/forward_zone'): |
185 |
yield Zone(zone, domainname) |
186 |
for zone in self.lookup('dns/reverse_zone'): |
187 |
yield Zone(zone, domainname) |
188 |
|
189 |
def check_zone(self, zone): |
190 |
for nameserver in zone.nameserver(): |
191 |
try: |
192 |
record = self.find(nameserver) |
193 |
except RecordNotFound: |
194 |
if not nameserver.is_in_zone(): |
195 |
try: |
196 |
socket.getaddrinfo(nameserver.fqdn(), None) |
197 |
except socket.gaierror: |
198 |
yield NoHostRecord(nameserver) |
199 |
else: |
200 |
yield NoHostRecord(nameserver) |
201 |
|
202 |
else: |
203 |
if record.module == 'dns/alias': |
204 |
yield CnameAsNameServer(nameserver) |
205 |
elif record.module != 'dns/host_record': |
206 |
yield NoHostRecord(nameserver) |
207 |
|
208 |
|
209 |
def find_all_zone_problems(): |
210 |
udm = UDM() |
211 |
for zone in udm.all_zones(): |
212 |
for error in udm.check_zone(zone): |
213 |
yield error |
214 |
|
215 |
|
216 |
def run(): |
217 |
ed = [_('Found errors in the nameserver entries of the following zones.') + ' ' + |
218 |
_('Please refer to {sdb} for further information.')] |
219 |
modules = list() |
220 |
tmpl_forward = _('In forward zone {name} (see {{{link}}}):') |
221 |
tmpl_reverse = _('In reverse zone {name} (see {{{link}}}):') |
222 |
for (zone, group) in it.groupby(find_all_zone_problems(), lambda error: error.zone): |
223 |
(text, link) = zone.umc_link() |
224 |
ed.append('') |
225 |
if zone.kind == 'dns/forward_zone': |
226 |
ed.append(tmpl_forward.format(kind=zone.kind, name=zone.zone, link=text)) |
227 |
elif zone.kind == 'dns/reverse_zone': |
228 |
ed.append(tmpl_reverse.format(kind=zone.kind, name=zone.zone, link=text)) |
229 |
ed.extend(str(error) for error in group) |
230 |
modules.append(link) |
231 |
|
232 |
if modules: |
233 |
raise Warning(description='\n'.join(ed), umc_modules=modules) |
234 |
|
235 |
|
236 |
if __name__ == '__main__': |
237 |
from univention.management.console.modules.diagnostic import main |
238 |
main() |
1 |
(po) |
239 |
(po) |
2 |
-- |
|
|
3 |
.../umc/python/diagnostic/de.po | 50 +++++++++++++++++++++- |
240 |
.../umc/python/diagnostic/de.po | 50 +++++++++++++++++++++- |
4 |
1 file changed, 48 insertions(+), 2 deletions(-) |
241 |
1 file changed, 48 insertions(+), 2 deletions(-) |