|
Line 0
Link Here
|
| 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(-) |