DNS Locations: command dns-update-system-records

command dns-update-system-records updates/fixes DNS records for IPA
services:
* updating A, AAAA records for CA
* updating SRV records for LDAP, kerberos and AD trust
* updating TXT record in _kerberos with proper realm
* updating dns locations if used

https://fedorahosted.org/freeipa/ticket/2008

Reviewed-By: Petr Spacek <pspacek@redhat.com>
Reviewed-By: Jan Cholasta <jcholast@redhat.com>
This commit is contained in:
Martin Basti 2016-06-10 17:03:25 +02:00
parent cf634a4ff8
commit e23159596e
6 changed files with 187 additions and 4 deletions

View File

@ -1054,6 +1054,12 @@ option: Str('version?')
output: Output('result', type=[<type 'bool'>])
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: Output('value', type=[<type 'unicode'>])
command: dns_update_system_records
args: 0,2,2
option: Flag('dry_run', autofill=True, default=False)
option: Str('version?')
output: Output('result', type=[<type 'dict'>])
output: Output('value', type=[<type 'bool'>])
command: dnsconfig_mod
args: 0,11,3
option: Str('addattr*', cli_name='addattr')

View File

@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
IPA_API_VERSION_MINOR=184
# Last change: ftweedal - add issuer options to cert-show and cert-find
IPA_API_VERSION_MINOR=185
# Last change: mbasti - added command dns-update-system-records

View File

@ -21,8 +21,9 @@
from __future__ import print_function
import six
import copy
from ipaclient.frontend import MethodOverride
from ipaclient.frontend import MethodOverride, CommandOverride
from ipalib import errors
from ipalib.dns import (get_record_rrtype,
has_cli_options,
@ -342,3 +343,29 @@ class dnsforwardzone_mod(MethodOverride):
_("Server will check DNS forwarder(s)."))
self.Backend.textui.print_plain(
_("This may take some time, please wait ..."))
@register(override=True)
class dns_update_system_records(CommandOverride):
def output_for_cli(self, textui, output, *args, **options):
output_super = copy.deepcopy(output)
super_res = output_super.get('result', {})
super_res.pop('ipa_records', None)
super_res.pop('location_records', None)
super(dns_update_system_records, self).output_for_cli(
textui, output_super, *args, **options)
labels = {
p.name: unicode(p.label) for p in self.output_params()
}
result = output.get('result', {})
for key in ('ipa_records', 'location_records'):
if result.get(key):
textui.print_indented(u'{}:'.format(labels[key]), indent=1)
for val in sorted(result[key]):
textui.print_indented(val, indent=2)
textui.print_line(u'')
return int(not output['value'])

View File

@ -395,6 +395,29 @@ class DNSForwardPolicyConflictWithEmptyZone(PublicMessage):
)
class DNSUpdateOfSystemRecordFailed(PublicMessage):
"""
** 13022 ** Update of a DNS system record failed
"""
errno = 13022
type = "warning"
format = _(
"Update of system record '%(record)s' failed with error: %(error)s"
)
class DNSUpdateNotIPAManagedZone(PublicMessage):
"""
** 13023 ** Zone for system records is not managed by IPA
"""
errno = 13023
type = "warning"
format = _(
"IPA does not manage the zone %(zone)s, please add records "
"to your DNS server manually"
)
def iter_messages(variables, base):
"""Return a tuple with all subclasses
"""

View File

@ -4,6 +4,8 @@
from __future__ import absolute_import
import six
from collections import defaultdict
from dns import (
rdataclass,
@ -17,6 +19,10 @@ from ipalib import errors
from ipalib.dns import record_name_format
from ipapython.dnsutil import DNSName, resolve_rrsets
if six.PY3:
unicode=str
IPA_DEFAULT_MASTER_SRV_REC = (
# srv record name, port
(DNSName(u'_ldap._tcp'), 389),
@ -214,7 +220,7 @@ class IPASystemRecords(object):
for rdata in rdataset:
option_name = (record_name_format % rdatatype.to_text(
rdata.rdtype).lower())
update_dict[option_name].append(rdata.to_text())
update_dict[option_name].append(unicode(rdata.to_text()))
return update_dict
def __update_dns_records(
@ -378,3 +384,19 @@ class IPASystemRecords(object):
self.update_base_records(),
self.update_locations_records()
)
@classmethod
def records_list_from_node(cls, name, node):
records = []
for rdataset in node:
for rd in rdataset:
records.append(
u'{name} {ttl} {rdclass} {rdtype} {rdata}'.format(
name=name.ToASCII(),
ttl=rdataset.ttl,
rdclass=rdataclass.to_text(rd.rdclass),
rdtype=rdatatype.to_text(rd.rdtype),
rdata=rd.to_text()
)
)
return records

View File

@ -72,6 +72,10 @@ from ipapython.ipautil import CheckedIPAddress
from ipapython.dnsutil import check_zone_overlap
from ipapython.dnsutil import DNSName
from ipapython.dnsutil import related_to_auto_empty_zone
from ipaserver.dns_data_management import (
IPASystemRecords,
IPADomainIsNotManagedByIPAError,
)
if six.PY3:
unicode = str
@ -4430,3 +4434,104 @@ class dnsforwardzone_add_permission(DNSZoneBase_add_permission):
@register()
class dnsforwardzone_remove_permission(DNSZoneBase_remove_permission):
__doc__ = _('Remove a permission for per-forward zone access delegation.')
@register()
class dns_update_system_records(Command):
__doc__ = _('Update location and IPA server DNS records')
has_output_params = (
Str(
'ipa_records*',
label=_('IPA DNS records')
),
Str(
'location_records*',
label=_('IPA location records')
)
)
has_output = (
output.Output(
'result',
type=dict,
doc=_('Dictionary mapping variable name to value'),
),
output.Output(
'value', bool,
_('Result of the command'), ['no_display']
)
)
takes_options = (
Flag(
'dry_run',
label=_('Dry run'),
doc=_('Do not update recors only return expected records')
)
)
def execute(self, *args, **options):
def output_to_list(iterable):
rec_list = []
for name, node in iterable:
rec_list.extend(IPASystemRecords.records_list_from_node(
name, node))
return rec_list
def output_to_list_with_failed(iterable):
err_rec_list = []
for name, node, error in iterable:
err_rec_list.extend([
(v, unicode(error)) for v in
IPASystemRecords.records_list_from_node(name, node)
])
return err_rec_list
result = {
'result': {},
'value': True,
}
system_records = IPASystemRecords(self.api)
if options.get('dry_run'):
result['result']['ipa_records'] = output_to_list(
system_records.get_base_records().items())
result['result']['location_records'] = output_to_list(
system_records.get_locations_records().items())
else:
try:
(
(success_base, failed_base),
(success_loc, failed_loc),
) = system_records.update_dns_records()
except IPADomainIsNotManagedByIPAError:
result['value'] = False
self.add_message(
messages.DNSUpdateNotIPAManagedZone(
zone=self.api.env.domain)
)
result['result']['ipa_records'] = output_to_list(
system_records.get_base_records().items())
else:
if success_base:
result['result']['ipa_records'] = output_to_list(
success_base)
if success_loc:
result['result']['location_records'] = output_to_list(
success_loc)
for failed in (failed_base, failed_loc):
for record, error in output_to_list_with_failed(failed):
self.add_message(
messages.DNSUpdateOfSystemRecordFailed(
record=record,
error=error
)
)
if failed_base or failed_loc:
result['value'] = False
return result