From 3711261802db1f6ce09de6371a9ea59e542e3d63 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Wed, 12 Jan 2011 21:02:05 +0100 Subject: Port installer and host plugin to the new DNS plugin * move ipa dns-resolve to the new plugin * port the installer and the host plugin to the new interface * remove the old plugin --- ipalib/plugins/dns.py | 941 -------------------------------------- ipalib/plugins/dns2.py | 54 ++- ipalib/plugins/host.py | 35 +- ipaserver/install/bindinstance.py | 30 +- 4 files changed, 81 insertions(+), 979 deletions(-) delete mode 100644 ipalib/plugins/dns.py diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py deleted file mode 100644 index ced13efc9..000000000 --- a/ipalib/plugins/dns.py +++ /dev/null @@ -1,941 +0,0 @@ -# Authors: -# Pavel Zuna -# -# Copyright (C) 2009 Red Hat -# see file 'COPYING' for use and warranty information -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -""" -Domain Name System (DNS) plug-in - -Implements a set of commands useful for manipulating DNS records used by -the BIND LDAP plug-in. - -EXAMPLES: - - Add new zone: - ipa dns-add example.com nameserver.example.com admin@example.com - - Add second nameserver for example.com: - ipa dns-add-rr example.com @ NS nameserver2.example.com - - Delete previously added nameserver from example.com: - ipa dns-del-rr example.com @ NS nameserver2.example.com - - Add new A record for www.example.com: (random IP) - ipa dns-add-rr example.com www A 80.142.15.2 - - Add new PTR record for www.example.com - ipa dns-add-rr 15.142.80.in-addr.arpa 2 PTR www.example.com. - - Show zone example.com: - ipa dns-show example.com - - Find zone with "example" in it's domain name: - ipa dns-find example - - Find records for resources with "www" in their name in zone example.com: - ipa dns-find-rr example.com www - - Find A records for resource www in zone example.com - ipa dns-find-rr example.com --resource www --type A - - Show records for resource www in zone example.com - ipa dns-show-rr example.com www - - Delete zone example.com with all resource records: - ipa dns-delete example.com - - Resolve a host name to see if it exists (will add default IPA domain - if one is not included): - ipa dns-resolve www.example.com - ipa dns-resolve www - -""" - -# A few notes about the LDAP schema to make this plugin more understandable: -# - idnsRecord object is a HOSTNAME with one or more resource records -# - idnsZone object is a idnsRecord object with mandatory SOA record -# it basically makes the assumption that ZONE == DOMAINNAME + SOA record -# resource records can be stored in both idnsZone and idnsRecord objects - -import time - -from ipalib import api, crud, errors, output -from ipalib import Object, Command -from ipalib import Flag, Int, Str, StrEnum -from ipalib import _, ngettext -from ipalib.output import Output, standard_entry, standard_list_of_entries -from ipapython import dnsclient - -# parent DN -_zone_container_dn = api.env.container_dns - -# supported resource record types -_record_types = ( - u'A', u'AAAA', u'A6', u'AFSDB', u'CERT', u'CNAME', u'DNAME', - u'DS', u'HINFO', u'KEY', u'KX', u'LOC', u'MD', u'MINFO', u'MX', - u'NAPTR', u'NS', u'NSEC', u'NXT', u'PTR', u'RRSIG', u'SSHFP', - u'SRV', u'TXT', -) - -# mapping from attribute to resource record type -_attribute_types = dict( - arecord=u'A', aaaarecord=u'AAAA', a6record=u'A6', - afsdbrecord=u'AFSDB', certrecord=u'CERT', cnamerecord=u'CNAME', - dnamerecord=u'DNAME', dsrecord=u'DS', hinforecord=u'HINFO', - keyrecord=u'KEY', kxrecord=u'KX', locrecord='LOC', - mdrecord=u'MD', minforecord=u'MINFO', mxrecord=u'MX', - naptrrecord=u'NAPTR', nsrecord=u'NS', nsecrecord=u'NSEC', - ntxtrecord=u'NTXT', ptrrecord=u'PTR', rrsigrecord=u'RRSIG', - sshfprecord=u'SSHFP', srvrecord=u'SRV', txtrecord=u'TXT', -) - -# supported DNS classes, IN = internet, rest is almost never used -_record_classes = (u'IN', u'CS', u'CH', u'HS') - -# attributes displayed by default for resource records -_record_default_attributes = ['%srecord' % r for r in _record_types] -_record_default_attributes.append('idnsname') - -# attributes displayed by default for zones -_zone_default_attributes = [ - 'idnsname', 'idnszoneactive', 'idnssoamname', 'idnssoarname', - 'idnssoaserial', 'idnssoarefresh', 'idnssoaretry', 'idnssoaexpire', - 'idnssoaminimum' -] - - -# normalizer for admin email -def _rname_normalizer(value): - value = value.replace('@', '.') - if not value.endswith('.'): - value += '.' - return value - -# build zone dn -def _get_zone_dn(ldap, idnsname): - rdn = ldap.make_rdn_from_attr('idnsname', idnsname) - return ldap.make_dn_from_rdn(rdn, _zone_container_dn) - -# build dn for entry with record -def _get_record_dn(ldap, zone, idnsname): - parent_dn = _get_zone_dn(ldap, zone) - if idnsname == '@' or idnsname == zone: - return parent_dn - rdn = ldap.make_rdn_from_attr('idnsname', idnsname) - return ldap.make_dn_from_rdn(rdn, parent_dn) - - -def dns_container_exists(ldap): - """ - See if the dns container exists. If not raise an exception. - """ - basedn = 'cn=dns,%s' % api.env.basedn - try: - ret = ldap.find_entries('(objectclass=*)', None, basedn, - ldap.SCOPE_BASE) - except errors.NotFound: - raise errors.NotFound(reason=_('DNS is not configured')) - - return True - -class dns(Object): - """DNS zone/SOA record object.""" - label = _('DNS') - - takes_params = ( - Str('idnsname', - cli_name='name', - label=_('Zone'), - doc=_('Zone name (FQDN)'), - normalizer=lambda value: value.lower(), - primary_key=True, - ), - Str('idnssoamname', - cli_name='name_server', - label=_('Authoritative name server'), - ), - Str('idnssoarname', - cli_name='admin_email', - label=_('administrator e-mail address'), - default_from=lambda idnsname: 'root.%s' % idnsname, - normalizer=_rname_normalizer, - ), - Int('idnssoaserial?', - cli_name='serial', - label=_('SOA serial'), - ), - Int('idnssoarefresh?', - cli_name='refresh', - label=_('SOA refresh'), - ), - Int('idnssoaretry?', - cli_name='retry', - label=_('SOA retry'), - ), - Int('idnssoaexpire?', - cli_name='expire', - label=_('SOA expire'), - ), - Int('idnssoaminimum?', - cli_name='minimum', - label=_('SOA minimum'), - ), - Int('dnsttl?', - cli_name='ttl', - label=_('SOA time to live'), - ), - StrEnum('dnsclass?', - cli_name='class', - label=_('SOA class'), - values=_record_classes, - ), - Flag('idnsallowdynupdate', - cli_name='allow_dynupdate', - label=_('allow dynamic update?'), - ), - Str('idnsupdatepolicy?', - cli_name='update_policy', - label=_('BIND update policy'), - ), - ) - - default_attributes = _zone_default_attributes - - json_friendly_attributes = ( - 'default_attributes', 'label', 'name', 'takes_params' ) - - def __json__(self): - json_dict = dict( - (a, getattr(self, a)) for a in self.json_friendly_attributes - ) - if self.primary_key: - json_dict['primary_key'] = self.primary_key.name - json_dict['methods'] = [m for m in self.methods] - return json_dict - - -api.register(dns) - - -class dns_add(crud.Create): - """ - Create new DNS zone/SOA record. - """ - def execute(self, *args, **options): - ldap = self.Backend.ldap2 - idnsname = args[0] - - dns_container_exists(ldap) - - # build entry attributes - entry_attrs = self.args_options_2_entry(*args, **options) - - # build entry DN - dn = _get_zone_dn(ldap, idnsname) - - # fill in required attributes - entry_attrs['objectclass'] = ['top', 'idnsrecord', 'idnszone'] - entry_attrs['idnszoneactive'] = 'TRUE' - entry_attrs['idnsallowdynupdate'] = str( - entry_attrs['idnsallowdynupdate'] - ).upper() - - # fill default values, build SOA serial from current date - soa_serial = int('%s01' % time.strftime('%Y%d%m')) - entry_attrs.setdefault('idnssoaserial', soa_serial) - entry_attrs.setdefault('idnssoarefresh', 3600) - entry_attrs.setdefault('idnssoaretry', 900) - entry_attrs.setdefault('idnssoaexpire', 1209600) - entry_attrs.setdefault('idnssoaminimum', 3600) - - # create zone entry - ldap.add_entry(dn, entry_attrs) - - # get zone entry with created attributes for output - (dn, entry_attrs) = ldap.get_entry(dn, entry_attrs.keys()) - entry_attrs['dn'] = dn - - return dict(result=entry_attrs, value=idnsname) - - def output_for_cli(self, textui, result, *args, **options): - entry_attrs = result['result'] - idnsname = result['value'] - - textui.print_name(self.name) - textui.print_attribute('dn', entry_attrs['dn']) - del entry_attrs['dn'] - textui.print_entry(entry_attrs) - textui.print_dashed('Created DNS zone "%s".' % idnsname) - -api.register(dns_add) - - -class dns_del(crud.Delete): - """ - Delete existing DNS zone/SOA record. - """ - def execute(self, *args, **options): - ldap = self.api.Backend.ldap2 - idnsname = args[0] - - dns_container_exists(ldap) - - # build zone entry DN - dn = _get_zone_dn(ldap, idnsname) - # just check if zone exists for now - ldap.get_entry(dn, ['']) - - # retrieve all subentries of zone - records - try: - (entries, truncated) = ldap.find_entries( - None, [''], dn, ldap.SCOPE_ONELEVEL - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - # kill'em all, records first - for e in entries: - ldap.delete_entry(e[0]) - ldap.delete_entry(dn) - - return dict(result=True, value=u'') - - def output_for_cli(self, textui, result, *args, **options): - textui.print_name(self.name) - textui.print_dashed('Deleted DNS zone "%s".' % args[0]) - -api.register(dns_del) - - -class dns_mod(crud.Update): - """ - Modify DNS zone/SOA record. - """ - def execute(self, *args, **options): - ldap = self.api.Backend.ldap2 - idnsname = args[0] - - dns_container_exists(ldap) - - # build entry attributes, don't include idnsname! - entry_attrs = self.args_options_2_entry(*tuple(), **options) - entry_attrs['idnsallowdynupdate'] = str( - entry_attrs['idnsallowdynupdate'] - ).upper() - - # build entry DN - dn = _get_zone_dn(ldap, idnsname) - - # update zone entry - ldap.update_entry(dn, entry_attrs) - - # get zone entry with modified + default attributes for output - (dn, entry_attrs) = ldap.get_entry( - dn, (entry_attrs.keys() + _zone_default_attributes) - ) - entry_attrs['dn'] = dn - - return dict(result=entry_attrs, value=idnsname) - - def output_for_cli(self, textui, result, *args, **options): - entry_attrs = result['result'] - idnsname = result['value'] - - textui.print_name(self.name) - textui.print_attribute('dn', entry_attrs['dn']) - del entry_attrs['dn'] - textui.print_entry(entry_attrs) - textui.print_dashed('Modified DNS zone "%s".' % idnsname) - -api.register(dns_mod) - - -class dns_find(crud.Search): - """ - Search for DNS zones/SOA records. - """ - def execute(self, term, **options): - ldap = self.api.Backend.ldap2 - - dns_container_exists(ldap) - - # build search filter - filter = ldap.make_filter_from_attr('idnsname', term, exact=False) - - # select attributes we want to retrieve - if options.get('all', False): - attrs_list = ['*'] - else: - attrs_list = _zone_default_attributes - - # get matching entries - try: - (entries, truncated) = ldap.find_entries( - filter, attrs_list, _zone_container_dn, ldap.SCOPE_ONELEVEL - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - for e in entries: - e[1]['dn'] = e[0] - entries = tuple(e for (dn, e) in entries) - - return dict(result=entries, count=len(entries), truncated=truncated) - - def output_for_cli(self, textui, result, term, **options): - entries = result['result'] - truncated = result['truncated'] - - textui.print_name(self.name) - for entry_attrs in entries: - textui.print_attribute('dn', entry_attrs['dn']) - del entry_attrs['dn'] - textui.print_entry(entry_attrs) - textui.print_plain('') - textui.print_count( - len(entries), '%i DNS zone matched.', '%i DNS zones matched.' - ) - if truncated: - textui.print_dashed('These results are truncated.', below=False) - textui.print_dashed( - 'Please refine your search and try again.', above=False - ) - -api.register(dns_find) - - -class dns_show(crud.Retrieve): - """ - Display DNS zone/SOA record. - """ - def execute(self, idnsname, **options): - ldap = self.api.Backend.ldap2 - - dns_container_exists(ldap) - - # build entry DN - dn = _get_zone_dn(ldap, idnsname) - - # select attributes we want to retrieve - if options.get('all', False): - attrs_list = ['*'] - else: - attrs_list = _zone_default_attributes - - (dn, entry_attrs) = ldap.get_entry(dn, attrs_list) - entry_attrs['dn'] = dn - - return dict(result=entry_attrs, value=idnsname) - - def output_for_cli(self, textui, result, *args, **options): - entry_attrs = result['result'] - - textui.print_name(self.name) - textui.print_attribute('dn', entry_attrs['dn']) - del entry_attrs['dn'] - textui.print_entry(entry_attrs) - -api.register(dns_show) - - -class dns_enable(Command): - """ - Activate DNS zone. - """ - takes_args = ( - Str('zone', - cli_name='zone', - label=_('Zone name'), - normalizer=lambda value: value.lower(), - ), - ) - - has_output = output.standard_value - - def execute(self, zone): - ldap = self.api.Backend.ldap2 - - dns_container_exists(ldap) - - # build entry DN - dn = _get_zone_dn(ldap, zone) - - # activate! - try: - ldap.update_entry(dn, {'idnszoneactive': 'TRUE'}) - except errors.EmptyModlist: - pass - - return dict(result=True, value=zone) - - def output_for_cli(self, textui, result, zone): - textui.print_name(self.name) - textui.print_dashed('Activated DNS zone "%s".' % zone) - -api.register(dns_enable) - - -class dns_disable(Command): - """ - Deactivate DNS zone. - """ - takes_args = ( - Str('zone', - label=_('Zone name'), - normalizer=lambda value: value.lower(), - ), - ) - - has_output = output.standard_value - - def execute(self, zone): - ldap = self.api.Backend.ldap2 - - dns_container_exists(ldap) - - # build entry DN - dn = _get_zone_dn(ldap, zone) - - # deactivate! - try: - ldap.update_entry(dn, {'idnszoneactive': 'FALSE'}) - except errors.EmptyModlist: - pass - - return dict(result=True, value=zone) - - def output_for_cli(self, textui, result, zone): - textui.print_name(self.name) - textui.print_dashed('Deactivated DNS zone "%s".' % zone) - -api.register(dns_disable) - - -class dns_add_rr(Command): - """ - Add new DNS resource record. - """ - - takes_args = ( - Str('zone', - label=_('Zone name'), - normalizer=lambda value: value.lower(), - ), - Str('idnsname', - cli_name='resource', - label=_('resource name'), - default_from=lambda zone: zone.lower(), - attribute=True, - ), - StrEnum('type', - label=_('Record type'), - values=_record_types, - ), - Str('data', - label=_('Data'), - doc=_('Type-specific data'), - ), - ) - - takes_options = ( - Int('dnsttl?', - cli_name='ttl', - label=_('Time to live'), - attribute=True, - ), - StrEnum('dnsclass?', - cli_name='class', - label=_('Class'), - values=_record_classes, - attribute=True, - ), - ) - - has_output = standard_entry - - def execute(self, zone, idnsname, type, data, **options): - ldap = self.api.Backend.ldap2 - attr = ('%srecord' % type).lower() - - dns_container_exists(ldap) - - # build entry DN - dn = _get_record_dn(ldap, zone, idnsname) - - # get resource entry where to store the new record - try: - (dn, entry_attrs) = ldap.get_entry(dn, [attr]) - except errors.NotFound: - if idnsname != '@' and idnsname != zone: - # resource entry doesn't exist, check if zone exists - zone_dn = _get_zone_dn(ldap, zone) - ldap.get_entry(zone_dn, ['']) - # it does, create new resource entry - - # build entry attributes - entry_attrs = self.args_options_2_entry( - (idnsname, ), **options - ) - - # fill in required attributes - entry_attrs['objectclass'] = ['top', 'idnsrecord'] - - # fill in the record - entry_attrs[attr] = data - - # create the entry - ldap.add_entry(dn, entry_attrs) - - # get entry with created attributes for output - (dn, entry_attrs) = ldap.get_entry(dn, entry_attrs.keys()) - entry_attrs['dn'] = dn - - return dict(result=entry_attrs, value=idnsname) - - # zone doesn't exist - raise - # resource entry already exists, create a modlist for the new record - - # convert entry_attrs keys to lowercase - #entry_attrs = dict( - # (k.lower(), v) for (k, v) in entry_attrs.iteritems() - #) - - # get new value for record attribute - attr_value = entry_attrs.get(attr, []) - attr_value.append(data) - - ldap.update_entry(dn, {attr: attr_value}) - # get entry with updated attribute for output - (dn, entry_attrs) = ldap.get_entry(dn, ['idnsname', attr]) - entry_attrs['dn'] = dn - - return dict(result=entry_attrs, value=idnsname) - - def output_for_cli(self, textui, result, zone, idnsname, type, data, - **options): - entry_attrs = result['result'] - output = '"%s %s %s" to zone "%s"' % ( - idnsname, type, data, zone, - ) - - textui.print_name(self.name) - textui.print_attribute('dn', entry_attrs['dn']) - del entry_attrs['dn'] - textui.print_entry(entry_attrs) - textui.print_dashed('Added DNS resource record %s.' % output) - -api.register(dns_add_rr) - - -class dns_del_rr(Command): - """ - Delete DNS resource record. - """ - - takes_args = ( - Str('zone', - label=_('Zone name'), - normalizer=lambda value: value.lower(), - ), - Str('idnsname', - cli_name='resource', - label=_('Resource name'), - default_from=lambda zone: zone.lower(), - attribute=True, - ), - StrEnum('type', - label=_('Record type'), - values=_record_types, - ), - Str('data', - label=_('Data'), - doc=_('Type-specific data'), - ), - ) - - has_output = standard_entry - - def execute(self, zone, idnsname, type, data, **options): - ldap = self.api.Backend.ldap2 - attr = ('%srecord' % type).lower() - - dns_container_exists(ldap) - - # build entry DN - dn = _get_record_dn(ldap, zone, idnsname) - - # get resource entry with the record we're trying to delete - (dn, entry_attrs) = ldap.get_entry(dn) - - # convert entry_attrs keys to lowercase - entry_attrs = dict( - (k.lower(), v) for (k, v) in entry_attrs.iteritems() - ) - - # get new value for record attribute - attr_value = entry_attrs.get(attr.lower(), []) - try: - attr_value.remove(data) - except ValueError: - raise errors.NotFound(reason=u'resource record not found') - - # check if it's worth to keep this entry in LDAP - if 'idnszone' not in entry_attrs['objectclass']: - # get a list of all meaningful record attributes - record_attrs = [] - for (k, v) in entry_attrs.iteritems(): - if k.endswith('record') and v: - record_attrs.append(k) - # check if the list is empty - if not record_attrs: - # it's not - ldap.delete_entry(dn) - return dict(result={}, value=idnsname) - - ldap.update_entry(dn, {attr: attr_value}) - # get entry with updated attribute for output - (dn, entry_attrs) = ldap.get_entry(dn, ['idnsname', attr]) - entry_attrs['dn'] = dn - - return dict(result=entry_attrs, value=idnsname) - - def output_for_cli(self, textui, result, zone, idnsname, type, data, **options): - output = '"%s %s %s" from zone "%s"' % ( - idnsname, type, data, zone, - ) - entry_attrs = result['result'] - - textui.print_name(self.name) - if entry_attrs: - textui.print_attribute('dn', entry_attrs['dn']) - del entry_attrs['dn'] - textui.print_entry(entry_attrs) - textui.print_dashed('Deleted DNS resource record %s' % output) - -api.register(dns_del_rr) - - -class dns_find_rr(Command): - """ - Search for DNS resource records. - """ - takes_args = ( - Str('zone', - label=_('Zone name'), - normalizer=lambda value: value.lower(), - ), - Str('criteria?', - cli_name='criteria', - label=_('Search criteria'), - ), - ) - - takes_options = ( - Str('idnsname?', - cli_name='resource', - label=_('Resource name'), - default_from=lambda zone: zone.lower(), - ), - StrEnum('type?', - label=_('Record type'), - values=_record_types, - ), - Str('data?', - label=_('type-specific data'), - ), - ) - - has_output = standard_list_of_entries - - def execute(self, zone, term, **options): - ldap = self.api.Backend.ldap2 - if 'type' in options: - attr = ('%srecord' % options['type']).lower() - else: - attr = None - - dns_container_exists(ldap) - - # build base dn for search - base_dn = _get_zone_dn(ldap, zone) - - # build search keywords - search_kw = {} - if 'data' in options: - if attr is not None: - # user is looking for a certain record type - search_kw[attr] = options['data'] - else: - # search in all record types - for a in _record_default_attributes: - search_kw[a] = term - if 'idnsname' in options: - idnsname = options['idnsname'] - if idnsname == '@': - search_kw['idnsname'] = zone - else: - search_kw['idnsname'] = idnsname - - # build search filter - filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL) - if term: - search_kw = {} - for a in _record_default_attributes: - search_kw[a] = term - term_filter = ldap.make_filter(search_kw, exact=False) - filter = ldap.combine_filters((filter, term_filter), ldap.MATCH_ALL) - - # select attributes we want to retrieve - if options.get('all', False): - attrs_list = ['*'] - elif attr is not None: - attrs_list = [attr] - else: - attrs_list = _record_default_attributes - - # get matching entries - try: - (entries, truncated) = ldap.find_entries( - filter, attrs_list, base_dn - ) - except errors.NotFound: - (entries, truncated) = (tuple(), False) - - # if the user is looking for a certain record type, don't display - # entries that do not contain it - if attr is not None: - related_entries = [] - for e in entries: - entry_attrs = e[1] - if attr in entry_attrs: - related_entries.append(e) - entries = related_entries - - for e in entries: - e[1]['dn'] = e[0] - entries = tuple(e for (dn, e) in entries) - - return dict(result=entries, count=len(entries), truncated=truncated) - - def output_for_cli(self, textui, result, zone, term, **options): - entries = result['result'] - truncated = result['truncated'] - - textui.print_name(self.name) - for entry_attrs in entries: - textui.print_attribute('dn', entry_attrs['dn']) - del entry_attrs['dn'] - textui.print_entry(entry_attrs) - textui.print_plain('') - textui.print_count( - len(entries), '%i DNS resource record matched.', - '%i DNS resource records matched.' - ) - if truncated: - textui.print_dashed('These results are truncated.', below=False) - textui.print_dashed( - 'Please refine your search and try again.', above=False - ) - -api.register(dns_find_rr) - - -class dns_show_rr(Command): - """ - Show existing DNS resource records. - """ - - takes_args = ( - Str('zone', - label=_('Zone name'), - normalizer=lambda value: value.lower(), - ), - Str('idnsname', - cli_name='resource', - label=_('Resource name'), - normalizer=lambda value: value.lower(), - ), - ) - - has_output = standard_entry - - def execute(self, zone, idnsname, **options): - # shows all records associated with resource - ldap = self.api.Backend.ldap2 - - dns_container_exists(ldap) - - # build entry DN - dn = _get_record_dn(ldap, zone, idnsname) - - # select attributes we want to retrieve - if options.get('all', False): - attrs_list = ['*'] - else: - attrs_list = _record_default_attributes - - (dn, entry_attrs) = ldap.get_entry(dn, attrs_list) - entry_attrs['dn'] = dn - - return dict(result=entry_attrs, value=idnsname) - - def output_for_cli(self, textui, result, zone, idnsname, **options): - entry_attrs = result['result'] - - textui.print_name(self.name) - textui.print_attribute('dn', entry_attrs['dn']) - del entry_attrs['dn'] - textui.print_entry(entry_attrs) - -api.register(dns_show_rr) - - -class dns_resolve(Command): - """ - Resolve a host name in DNS - """ - has_output = output.standard_value - msg_summary = _('Found \'%(value)s\'') - - takes_args = ( - Str('hostname', - label=_('Hostname'), - ), - ) - - def execute(self, *args, **options): - query=args[0] - if query.find(api.env.domain) == -1 and query.find('.') == -1: - query = '%s.%s.' % (query, api.env.domain) - if query[-1] != '.': - query = query + '.' - reca = dnsclient.query(query, dnsclient.DNS_C_IN, dnsclient.DNS_T_A) - rec6 = dnsclient.query(query, dnsclient.DNS_C_IN, dnsclient.DNS_T_AAAA) - records = reca + rec6 - found = False - for rec in records: - if rec.dns_type == dnsclient.DNS_T_A or \ - rec.dns_type == dnsclient.DNS_T_AAAA: - found = True - break - - if not found: - raise errors.NotFound(reason=_('Host \'%(host)s\' not found' % {'host':query})) - - return dict(result=True, value=query) - -api.register(dns_resolve) diff --git a/ipalib/plugins/dns2.py b/ipalib/plugins/dns2.py index 9254f1df9..cf5809803 100644 --- a/ipalib/plugins/dns2.py +++ b/ipalib/plugins/dns2.py @@ -37,7 +37,7 @@ EXAMPLES: ipa dnsrecord-add example.com www --a-rec 80.142.15.2 Add new PTR record for www.example.com - ipa dnsrecord 15.142.80.in-addr.arpa 2 --ptr-rec www.example.com. + ipa dnsrecord-add 15.142.80.in-addr.arpa 2 --ptr-rec www.example.com. Show zone example.com: ipa dnszone-show example.com @@ -121,6 +121,13 @@ _record_validators = { } +def dns_container_exists(ldap): + try: + ldap.get_entry(api.env.container_dns, []) + except errors.NotFound: + return False + return True + class dnszone(LDAPObject): """ DNS Zone, container for resource records. @@ -227,12 +234,6 @@ class dnszone(LDAPObject): ), ) - def check_container_exists(self): - try: - self.backend.get_entry(self.container_dn, []) - except errors.NotFound: - raise errors.NotFound(reason=_('DNS is not configured')) - api.register(dnszone) @@ -241,7 +242,9 @@ class dnszone_add(LDAPCreate): Create new DNS zone (SOA record). """ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options): - self.obj.check_container_exists() + if not dns_container_exists(self.api.Backend.ldap2): + raise errors.NotFound(reason=_('DNS is not configured')) + entry_attrs['idnszoneactive'] = 'TRUE' entry_attrs['idnsallowdynupdate'] = str( entry_attrs.get('idnsallowdynupdate', False) @@ -583,3 +586,38 @@ class dnsrecord_find(LDAPSearch, dnsrecord_cmd_w_record_options): api.register(dnsrecord_find) +class dns_resolve(Command): + """ + Resolve a host name in DNS + """ + has_output = output.standard_value + msg_summary = _('Found \'%(value)s\'') + + takes_args = ( + Str('hostname', + label=_('Hostname'), + ), + ) + + def execute(self, *args, **options): + query=args[0] + if query.find(api.env.domain) == -1 and query.find('.') == -1: + query = '%s.%s.' % (query, api.env.domain) + if query[-1] != '.': + query = query + '.' + reca = dnsclient.query(query, dnsclient.DNS_C_IN, dnsclient.DNS_T_A) + rec6 = dnsclient.query(query, dnsclient.DNS_C_IN, dnsclient.DNS_T_AAAA) + records = reca + rec6 + found = False + for rec in records: + if rec.dns_type == dnsclient.DNS_T_A or \ + rec.dns_type == dnsclient.DNS_T_AAAA: + found = True + break + + if not found: + raise errors.NotFound(reason=_('Host \'%(host)s\' not found' % {'host':query})) + + return dict(result=True, value=query) + +api.register(dns_resolve) diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py index 88ac0bcb7..d60f63776 100644 --- a/ipalib/plugins/host.py +++ b/ipalib/plugins/host.py @@ -84,7 +84,7 @@ from ipalib.plugins.service import normalize_certificate from ipalib.plugins.service import set_certificate_attrs from ipalib.plugins.service import make_pem, check_writable_file from ipalib.plugins.service import write_certificate -from ipalib.plugins.dns import dns_container_exists, _attribute_types +from ipalib.plugins.dns2 import dns_container_exists, _record_types from ipalib import _, ngettext from ipalib import x509 from ipapython.ipautil import ipa_generate_password @@ -282,7 +282,7 @@ class host_add(LDAPCreate): if 'ip_address' in options and dns_container_exists(ldap): parts = keys[-1].split('.') domain = unicode('.'.join(parts[1:])) - result = api.Command['dns_find']()['result'] + result = api.Command['dnszone_find']()['result'] match = False for zone in result: if domain == zone['idnsname'][0]: @@ -290,7 +290,7 @@ class host_add(LDAPCreate): break if not match: raise errors.NotFound(reason=_('DNS zone %(zone)s not found' % dict(zone=domain))) - if not options.get('no_reverse',False): + if not options.get('no_reverse', False): # we prefer lookup of the IP through the reverse zone revzone, revname = get_reverse_zone(options['ip_address']) # Verify that our reverse zone exists @@ -302,7 +302,7 @@ class host_add(LDAPCreate): if not match: raise errors.NotFound(reason=_('Reverse DNS zone %(zone)s not found' % dict(zone=revzone))) try: - reverse = api.Command['dns_find_rr'](revzone, revname) + reverse = api.Command['dnsrecord_find'](revzone, idnsname=revname) if reverse['count'] > 0: raise errors.DuplicateEntry(message=u'This IP address is already assigned.') except errors.NotFound: @@ -344,17 +344,18 @@ class host_add(LDAPCreate): parts = keys[-1].split('.') domain = unicode('.'.join(parts[1:])) if ':' in options['ip_address']: - type = u'AAAA' + addkw = { u'aaaarecord' : options['ip_address'] } else: - type = u'A' + addkw = { u'arecord' : options['ip_address'] } try: - api.Command['dns_add_rr'](domain, parts[0], type, options['ip_address']) + api.Command['dnsrecord_add'](domain, parts[0], **addkw) except errors.EmptyModlist: # the entry already exists and matches pass revzone, revname = get_reverse_zone(options['ip_address']) try: - api.Command['dns_add_rr'](revzone, revname, u'PTR', keys[-1]+'.') + addkw = { u'ptrrecord' : keys[-1]+'.' } + api.Command['dnsrecord_add'](revzone, revname, **addkw) except errors.EmptyModlist: # the entry already exists and matches pass @@ -424,7 +425,7 @@ class host_del(LDAPDelete): # Remove DNS entries parts = fqdn.split('.') domain = unicode('.'.join(parts[1:])) - result = api.Command['dns_find']()['result'] + result = api.Command['dnszone_find']()['result'] match = False for zone in result: if domain == zone['idnsname'][0]: @@ -434,30 +435,34 @@ class host_del(LDAPDelete): raise errors.NotFound(reason=_('DNS zone %(zone)s not found' % dict(zone=domain))) raise e # Get all forward resources for this host - records = api.Command['dns_find_rr'](domain, parts[0])['result'] + records = api.Command['dnsrecord_find'](domain, idnsname=parts[0])['result'] for record in records: if 'arecord' in record: ipaddr = record['arecord'][0] self.debug('deleting ipaddr %s' % ipaddr) revzone, revname = get_reverse_zone(ipaddr) try: - api.Command['dns_del_rr'](revzone, revname, u'PTR', fqdn+'.') + delkw = { u'ptrrecord' : fqdn+'.' } + api.Command['dnsrecord_del'](revzone, revname, **delkw) except errors.NotFound: pass try: - api.Command['dns_del_rr'](domain, parts[0], u'A', ipaddr) + delkw = { u'arecord' : ipaddr } + api.Command['dnsrecord_del'](domain, parts[0], **delkw) except errors.NotFound: pass else: # Try to delete all other record types too + _attribute_types = [str('%srecord' % t.lower()) for t in _record_types] for attr in _attribute_types: if attr != 'arecord' and attr in record: for i in xrange(len(record[attr])): if (record[attr][i].endswith(parts[0]) or record[attr][i].endswith(fqdn+'.')): - api.Command['dns_del_rr'](domain, - record['idnsname'][0], - _attribute_types[attr], record[attr][i]) + delkw = { unicode(attr) : record[attr][i] } + api.Command['dnsrecord_del'](domain, + record['idnsname'][0], + **delkw) break try: diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index e1a5810f4..4cf9f94c3 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -107,8 +107,8 @@ def get_reverse_zone(ip_address): def dns_zone_exists(name): try: - zone = api.Command.dns_show(unicode(name)) - except Exception: + zone = api.Command.dnszone_show(unicode(name)) + except ipalib.errors.NotFound: return False if len(zone) == 0: @@ -121,11 +121,11 @@ def add_zone(name, update_policy=None, zonemgr=None, dns_backup=None): update_policy = "grant %s krb5-self * A;" % api.env.realm try: - api.Command.dns_add(unicode(name), - idnssoamname=unicode(api.env.host+"."), - idnssoarname=unicode(zonemgr), - idnsallowdynupdate=True, - idnsupdatepolicy=unicode(update_policy)) + api.Command.dnszone_add(unicode(name), + idnssoamname=unicode(api.env.host+"."), + idnssoarname=unicode(zonemgr), + idnsallowdynupdate=True, + idnsupdatepolicy=unicode(update_policy)) except (errors.DuplicateEntry, errors.EmptyModlist): pass @@ -138,10 +138,10 @@ def add_reverze_zone(ip_address, update_policy=None, dns_backup=None): if not update_policy: update_policy = "grant %s krb5-subdomain %s. PTR;" % (api.env.realm, zone) try: - api.Command.dns_add(unicode(zone), - idnssoamname=unicode(api.env.host+"."), - idnsallowdynupdate=True, - idnsupdatepolicy=unicode(update_policy)) + api.Command.dnszone_add(unicode(zone), + idnssoamname=unicode(api.env.host+"."), + idnsallowdynupdate=True, + idnsupdatepolicy=unicode(update_policy)) except (errors.DuplicateEntry, errors.EmptyModlist): pass @@ -150,9 +150,9 @@ def add_reverze_zone(ip_address, update_policy=None, dns_backup=None): return zone def add_rr(zone, name, type, rdata, dns_backup=None): + addkw = { '%srecord' % unicode(type.lower()) : unicode(rdata) } try: - api.Command.dns_add_rr(unicode(zone), unicode(name), - unicode(type), unicode(rdata)) + api.Command.dnsrecord_add(unicode(zone), unicode(name), **addkw) except (errors.DuplicateEntry, errors.EmptyModlist): pass if dns_backup: @@ -201,8 +201,8 @@ class DnsBackup(object): if have_ldap: type, host, rdata = dns_record.split(" ", 2) try: - api.Command.dns_del_rr(unicode(zone), unicode(host), - unicode(type), unicode(rdata)) + delkw = { '%srecord' % unicode(type.lower()) : unicode(rdata) } + api.Command.dnsrecord_del(unicode(zone), unicode(host), **delkw) except: pass j += 1 -- cgit