mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Add new version of DNS plugin: complete rework with baseldap + unit tests.
Ticket #36 Ticket #450
This commit is contained in:
parent
4ad8055341
commit
5db7c4ec34
@ -178,6 +178,7 @@ Requires: gnupg
|
|||||||
Requires: pyOpenSSL
|
Requires: pyOpenSSL
|
||||||
Requires: python-nss >= 0.9-8
|
Requires: python-nss >= 0.9-8
|
||||||
Requires: python-lxml
|
Requires: python-lxml
|
||||||
|
Requires: python-netaddr
|
||||||
|
|
||||||
%description python
|
%description python
|
||||||
IPA is an integrated solution to provide centrally managed Identity (machine,
|
IPA is an integrated solution to provide centrally managed Identity (machine,
|
||||||
|
584
ipalib/plugins/dns2.py
Normal file
584
ipalib/plugins/dns2.py
Normal file
@ -0,0 +1,584 @@
|
|||||||
|
# Authors:
|
||||||
|
# Pavel Zuna <pzuna@redhat.com>
|
||||||
|
#
|
||||||
|
# Copyright (C) 2010 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; version 2 only
|
||||||
|
#
|
||||||
|
# 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, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
"""
|
||||||
|
Domain Name System (DNS)
|
||||||
|
|
||||||
|
Manage DNS zone and resource records.
|
||||||
|
|
||||||
|
EXAMPLES:
|
||||||
|
|
||||||
|
Add new zone:
|
||||||
|
ipa dnszone-add example.com --name_server nameserver.example.com
|
||||||
|
--admin_email admin@example.com
|
||||||
|
|
||||||
|
edd second nameserver for example.com:
|
||||||
|
ipa dnsrecord-add example.com @ --ns-rec nameserver2.example.com
|
||||||
|
|
||||||
|
Delete previously added nameserver from example.com:
|
||||||
|
ipa dnsrecord-del example.com @ --ns-rec nameserver2.example.com
|
||||||
|
|
||||||
|
Add new A record for www.example.com: (random IP)
|
||||||
|
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.
|
||||||
|
|
||||||
|
Show zone example.com:
|
||||||
|
ipa dnszone-show example.com
|
||||||
|
|
||||||
|
Find zone with "example" in it's domain name:
|
||||||
|
ipa dnszone-find example
|
||||||
|
|
||||||
|
Find records for resources with "www" in their name in zone example.com:
|
||||||
|
ipa dnsrecord-find example.com www
|
||||||
|
|
||||||
|
Find A records with value 10.10.0.1 in zone example.com
|
||||||
|
ipa dnsrecord-find example.com --a-rec 10.10.0.1
|
||||||
|
|
||||||
|
Show records for resource www in zone example.com
|
||||||
|
ipa dnsrecord-show example.com www
|
||||||
|
|
||||||
|
Delete zone example.com with all resource records:
|
||||||
|
ipa dnszone-del 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
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import netaddr
|
||||||
|
import time
|
||||||
|
|
||||||
|
from ipalib import api, errors, output
|
||||||
|
from ipalib import Command
|
||||||
|
from ipalib import Flag, Int, List, Str, StrEnum
|
||||||
|
from ipalib.plugins.baseldap import *
|
||||||
|
from ipalib import _, ngettext
|
||||||
|
from ipapython import dnsclient
|
||||||
|
|
||||||
|
# supported resource record types
|
||||||
|
_record_types = (
|
||||||
|
u'A', u'AAAA', u'A6', u'AFSDB', u'APL', u'CERT', u'CNAME', u'DHCID', u'DLV',
|
||||||
|
u'DNAME', u'DNSKEY', u'DS', u'HINFO', u'HIP', u'IPSECKEY', u'KEY', u'KX',
|
||||||
|
u'LOC', u'MD', u'MINFO', u'MX', u'NAPTR', u'NS', u'NSEC', u'NSEC3',
|
||||||
|
u'NSEC3PARAM', u'NXT', u'PTR', u'RRSIG', u'RP', u'SIG', u'SPF', u'SRV',
|
||||||
|
u'SSHFP', u'TA', u'TKEY', u'TSIG', u'TXT',
|
||||||
|
)
|
||||||
|
|
||||||
|
# attributes derived from record types
|
||||||
|
_record_attributes = [str('%srecord' % t.lower()) for t in _record_types]
|
||||||
|
|
||||||
|
# supported DNS classes, IN = internet, rest is almost never used
|
||||||
|
_record_classes = (u'IN', u'CS', u'CH', u'HS')
|
||||||
|
|
||||||
|
# normalizer for admin email
|
||||||
|
def _rname_normalizer(value):
|
||||||
|
value = value.replace('@', '.')
|
||||||
|
if not value.endswith('.'):
|
||||||
|
value += '.'
|
||||||
|
return value
|
||||||
|
|
||||||
|
def _create_zone_serial(**kwargs):
|
||||||
|
"""Generate serial number for zones."""
|
||||||
|
return int('%s01' % time.strftime('%Y%d%m'))
|
||||||
|
|
||||||
|
def _validate_ipaddr(ugettext, ipaddr):
|
||||||
|
try:
|
||||||
|
ip = netaddr.IPAddress(ipaddr)
|
||||||
|
except netaddr.AddrFormatError:
|
||||||
|
return u'invalid address format'
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _validate_ipnet(ugettext, ipnet):
|
||||||
|
try:
|
||||||
|
net = netaddr.IPNetwork(ipnet)
|
||||||
|
except (UnboundLocalError, ValueError):
|
||||||
|
return u'invalid format'
|
||||||
|
return None
|
||||||
|
|
||||||
|
_record_validators = {
|
||||||
|
u'A': _validate_ipaddr,
|
||||||
|
u'AAAA': _validate_ipaddr,
|
||||||
|
u'APL': _validate_ipnet,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class dnszone(LDAPObject):
|
||||||
|
"""
|
||||||
|
DNS Zone, container for resource records.
|
||||||
|
"""
|
||||||
|
container_dn = api.env.container_dns
|
||||||
|
object_name = 'DNS zone'
|
||||||
|
object_name_plural = 'DNS zones'
|
||||||
|
object_class = ['top', 'idnsrecord', 'idnszone']
|
||||||
|
default_attributes = [
|
||||||
|
'idnsname', 'idnszoneactive', 'idnssoamname', 'idnssoarname',
|
||||||
|
'idnssoaserial', 'idnssoarefresh', 'idnssoaretry', 'idnssoaexpire',
|
||||||
|
'idnssoaminimum'
|
||||||
|
] + _record_attributes
|
||||||
|
label = _('DNS zone')
|
||||||
|
|
||||||
|
takes_params = (
|
||||||
|
Str('idnsname',
|
||||||
|
cli_name='name',
|
||||||
|
label=_('Zone name'),
|
||||||
|
doc=_('Zone name (FQDN)'),
|
||||||
|
normalizer=lambda value: value.lower(),
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
Str('idnssoamname',
|
||||||
|
cli_name='name_server',
|
||||||
|
label=_('Authoritative name server'),
|
||||||
|
doc=_('Authoritative name server'),
|
||||||
|
),
|
||||||
|
Str('idnssoarname',
|
||||||
|
cli_name='admin_email',
|
||||||
|
label=_('Administrator e-mail address'),
|
||||||
|
doc=_('Administrator e-mail address'),
|
||||||
|
default_from=lambda idnsname: 'root.%s' % idnsname,
|
||||||
|
normalizer=_rname_normalizer,
|
||||||
|
),
|
||||||
|
Int('idnssoaserial?',
|
||||||
|
cli_name='serial',
|
||||||
|
label=_('SOA serial'),
|
||||||
|
doc=_('SOA record serial number'),
|
||||||
|
create_default=_create_zone_serial,
|
||||||
|
autofill=True,
|
||||||
|
),
|
||||||
|
Int('idnssoarefresh?',
|
||||||
|
cli_name='refresh',
|
||||||
|
label=_('SOA refresh'),
|
||||||
|
doc=_('SOA record refresh time'),
|
||||||
|
default=3600,
|
||||||
|
autofill=True,
|
||||||
|
),
|
||||||
|
Int('idnssoaretry?',
|
||||||
|
cli_name='retry',
|
||||||
|
label=_('SOA retry'),
|
||||||
|
doc=_('SOA record retry time'),
|
||||||
|
default=900,
|
||||||
|
autofill=True,
|
||||||
|
),
|
||||||
|
Int('idnssoaexpire?',
|
||||||
|
cli_name='expire',
|
||||||
|
label=_('SOA expire'),
|
||||||
|
doc=_('SOA record expire time'),
|
||||||
|
default=1209600,
|
||||||
|
autofill=True,
|
||||||
|
),
|
||||||
|
Int('idnssoaminimum?',
|
||||||
|
cli_name='minimum',
|
||||||
|
label=_('SOA minimum'),
|
||||||
|
doc=_('SOA record minimum value'),
|
||||||
|
default=3600,
|
||||||
|
autofill=True,
|
||||||
|
),
|
||||||
|
Int('idnssoamaximum?',
|
||||||
|
cli_name='maximum',
|
||||||
|
label=_('SOA maximum'),
|
||||||
|
doc=_('SOA record maximum value'),
|
||||||
|
),
|
||||||
|
Int('dnsttl?',
|
||||||
|
cli_name='ttl',
|
||||||
|
label=_('SOA time to live'),
|
||||||
|
doc=_('SOA record time to live'),
|
||||||
|
),
|
||||||
|
StrEnum('dnsclass?',
|
||||||
|
cli_name='class',
|
||||||
|
label=_('SOA class'),
|
||||||
|
doc=_('SOA record class'),
|
||||||
|
values=_record_classes,
|
||||||
|
),
|
||||||
|
Str('idnsupdatepolicy?',
|
||||||
|
cli_name='update_policy',
|
||||||
|
label=_('BIND update policy'),
|
||||||
|
doc=_('BIND update policy'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
class dnszone_add(LDAPCreate):
|
||||||
|
"""
|
||||||
|
Create new DNS zone (SOA record).
|
||||||
|
"""
|
||||||
|
takes_options = (
|
||||||
|
Flag('idnsallowdynupdate',
|
||||||
|
cli_name='allow_dynupdate',
|
||||||
|
label=_('Dynamic update'),
|
||||||
|
doc=_('allow dynamic update?'),
|
||||||
|
attribute=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||||
|
self.obj.check_container_exists()
|
||||||
|
entry_attrs['idnszoneactive'] = 'TRUE'
|
||||||
|
entry_attrs['idnsallowdynupdate'] = str(
|
||||||
|
entry_attrs.get('idnsallowdynupdate', False)
|
||||||
|
).upper()
|
||||||
|
return dn
|
||||||
|
|
||||||
|
api.register(dnszone_add)
|
||||||
|
|
||||||
|
|
||||||
|
class dnszone_del(LDAPDelete):
|
||||||
|
"""
|
||||||
|
Delete DNS zone (SOA record).
|
||||||
|
"""
|
||||||
|
|
||||||
|
api.register(dnszone_del)
|
||||||
|
|
||||||
|
|
||||||
|
class dnszone_mod(LDAPUpdate):
|
||||||
|
"""
|
||||||
|
Modify DNS zone (SOA record).
|
||||||
|
"""
|
||||||
|
takes_options = (
|
||||||
|
Flag('idnsallowdynupdate',
|
||||||
|
cli_name='allow_dynupdate',
|
||||||
|
label=_('Dynamic update'),
|
||||||
|
doc=_('allow dynamic update?'),
|
||||||
|
attribute=True,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||||
|
entry_attrs['idnsallowdynupdate'] = str(
|
||||||
|
entry_attrs.get('idnsallowdynupdate', False)
|
||||||
|
).upper()
|
||||||
|
return dn
|
||||||
|
|
||||||
|
api.register(dnszone_mod)
|
||||||
|
|
||||||
|
|
||||||
|
class dnszone_find(LDAPSearch):
|
||||||
|
"""
|
||||||
|
Search for DNS zones (SOA records).
|
||||||
|
"""
|
||||||
|
|
||||||
|
api.register(dnszone_find)
|
||||||
|
|
||||||
|
|
||||||
|
class dnszone_show(LDAPRetrieve):
|
||||||
|
"""
|
||||||
|
Display information about a DNS zone (SOA record).
|
||||||
|
"""
|
||||||
|
|
||||||
|
api.register(dnszone_show)
|
||||||
|
|
||||||
|
|
||||||
|
class dnszone_disable(LDAPQuery):
|
||||||
|
"""
|
||||||
|
Disable DNS Zone.
|
||||||
|
"""
|
||||||
|
has_output = output.standard_value
|
||||||
|
msg_summary = _('Disabled DNS zone "%(value)s"')
|
||||||
|
|
||||||
|
def execute(self, *keys, **options):
|
||||||
|
ldap = self.obj.backend
|
||||||
|
|
||||||
|
dn = self.obj.get_dn(*keys, **options)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ldap.update_entry(dn, {'idnszoneactive': 'FALSE'})
|
||||||
|
except errors.EmptyModlist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return dict(result=True, value=keys[-1])
|
||||||
|
|
||||||
|
api.register(dnszone_disable)
|
||||||
|
|
||||||
|
|
||||||
|
class dnszone_enable(LDAPQuery):
|
||||||
|
"""
|
||||||
|
Enable DNS Zone.
|
||||||
|
"""
|
||||||
|
has_output = output.standard_value
|
||||||
|
msg_summary = _('Enabled DNS zone "%(value)s"')
|
||||||
|
|
||||||
|
def execute(self, *keys, **options):
|
||||||
|
ldap = self.obj.backend
|
||||||
|
|
||||||
|
dn = self.obj.get_dn(*keys, **options)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ldap.update_entry(dn, {'idnszoneactive': 'TRUE'})
|
||||||
|
except errors.EmptyModlist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return dict(result=True, value=keys[-1])
|
||||||
|
|
||||||
|
api.register(dnszone_enable)
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord(LDAPObject):
|
||||||
|
"""
|
||||||
|
DNS record.
|
||||||
|
"""
|
||||||
|
parent_object = 'dnszone'
|
||||||
|
container_dn = api.env.container_dns
|
||||||
|
object_name = 'DNS resource record'
|
||||||
|
object_name_plural = 'DNS resource records'
|
||||||
|
object_class = ['top', 'idnsrecord']
|
||||||
|
default_attributes = _record_attributes + ['idnsname']
|
||||||
|
|
||||||
|
label = _('DNS resource record')
|
||||||
|
|
||||||
|
takes_params = (
|
||||||
|
Str('idnsname',
|
||||||
|
cli_name='name',
|
||||||
|
label=_('Record name'),
|
||||||
|
doc=_('Record name'),
|
||||||
|
primary_key=True,
|
||||||
|
),
|
||||||
|
Int('dnsttl?',
|
||||||
|
cli_name='ttl',
|
||||||
|
label=_('Time to live'),
|
||||||
|
doc=_('Time to live'),
|
||||||
|
),
|
||||||
|
StrEnum('dnsclass?',
|
||||||
|
cli_name='class',
|
||||||
|
label=_('Class'),
|
||||||
|
doc=_('DNS class'),
|
||||||
|
values=_record_classes,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_pkey_zone_record(*keys):
|
||||||
|
idnsname = keys[-1]
|
||||||
|
if idnsname == '@' or idnsname == ('%s.' % keys[-2]):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_dn(self, *keys, **options):
|
||||||
|
if self.is_pkey_zone_record(*keys):
|
||||||
|
return self.api.Object[self.parent_object].get_dn(*keys[:-1], **options)
|
||||||
|
return super(dnsrecord, self).get_dn(*keys, **options)
|
||||||
|
|
||||||
|
api.register(dnsrecord)
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord_cmd_w_record_options(Command):
|
||||||
|
"""
|
||||||
|
Base class for DNS record commands with record options.
|
||||||
|
"""
|
||||||
|
record_param_doc = 'comma-separated list of %s records'
|
||||||
|
|
||||||
|
def get_record_options(self):
|
||||||
|
for t in _record_types:
|
||||||
|
t = t.encode('utf-8')
|
||||||
|
doc = self.record_param_doc % t
|
||||||
|
validator = _record_validators.get(t)
|
||||||
|
if validator:
|
||||||
|
yield List(
|
||||||
|
'%srecord?' % t.lower(), validator,
|
||||||
|
cli_name='%s_rec' % t.lower(), doc=doc,
|
||||||
|
label='%s record' % t, attribute=True
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
yield List(
|
||||||
|
'%srecord?' % t.lower(), cli_name='%s_rec' % t.lower(),
|
||||||
|
doc=doc, label='%s record' % t, attribute=True
|
||||||
|
)
|
||||||
|
|
||||||
|
def record_options_2_entry(self, **options):
|
||||||
|
return dict((t, options.get(t, [])) for t in _record_attributes)
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord_mod_record(LDAPQuery, dnsrecord_cmd_w_record_options):
|
||||||
|
"""
|
||||||
|
Base class for adding/removing records from DNS resource entries.
|
||||||
|
"""
|
||||||
|
has_output = output.standard_entry
|
||||||
|
|
||||||
|
def get_options(self):
|
||||||
|
for option in super(dnsrecord_mod_record, self).get_options():
|
||||||
|
yield option
|
||||||
|
for option in self.get_record_options():
|
||||||
|
yield option
|
||||||
|
|
||||||
|
def execute(self, *keys, **options):
|
||||||
|
ldap = self.obj.backend
|
||||||
|
|
||||||
|
dn = self.obj.get_dn(*keys, **options)
|
||||||
|
|
||||||
|
entry_attrs = self.record_options_2_entry(**options)
|
||||||
|
|
||||||
|
try:
|
||||||
|
(dn, old_entry_attrs) = ldap.get_entry(dn, entry_attrs.keys())
|
||||||
|
except errors.NotFound:
|
||||||
|
self.obj.handle_not_found(*keys)
|
||||||
|
|
||||||
|
self.update_old_entry_callback(entry_attrs, old_entry_attrs)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ldap.update_entry(dn, old_entry_attrs)
|
||||||
|
except errors.EmptyModlist:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if options.get('all', False):
|
||||||
|
attrs_list = ['*']
|
||||||
|
else:
|
||||||
|
attrs_list = list(
|
||||||
|
set(self.obj.default_attributes + entry_attrs.keys())
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
(dn, entry_attrs) = ldap.get_entry(dn, attrs_list)
|
||||||
|
except errors.NotFound:
|
||||||
|
self.obj.handle_not_found(*keys)
|
||||||
|
|
||||||
|
if self.obj.is_pkey_zone_record(*keys):
|
||||||
|
entry_attrs[self.obj.primary_key.name] = [u'@']
|
||||||
|
|
||||||
|
self.post_callback(keys, entry_attrs)
|
||||||
|
|
||||||
|
return dict(result=entry_attrs, value=keys[-1])
|
||||||
|
|
||||||
|
def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def post_callback(self, keys, entry_attrs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord_add_record(dnsrecord_mod_record):
|
||||||
|
"""
|
||||||
|
Add records to DNS resource.
|
||||||
|
"""
|
||||||
|
INTERNAL = True
|
||||||
|
|
||||||
|
def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
|
||||||
|
for (a, v) in entry_attrs.iteritems():
|
||||||
|
if not isinstance(v, (list, tuple)):
|
||||||
|
v = [v]
|
||||||
|
old_entry_attrs.setdefault(a, [])
|
||||||
|
old_entry_attrs[a] += v
|
||||||
|
|
||||||
|
api.register(dnsrecord_add_record)
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options):
|
||||||
|
"""
|
||||||
|
Add new DNS resource record.
|
||||||
|
"""
|
||||||
|
def get_options(self):
|
||||||
|
for option in super(dnsrecord_add, self).get_options():
|
||||||
|
yield option
|
||||||
|
for option in self.get_record_options():
|
||||||
|
yield option
|
||||||
|
|
||||||
|
def exc_callback(self, keys, options, exc, call_func, *call_args, **call_kwargs):
|
||||||
|
if call_func.func_name == 'add_entry':
|
||||||
|
if isinstance(exc, errors.DuplicateEntry):
|
||||||
|
self.obj.methods.add_record(
|
||||||
|
*keys, **self.record_options_2_entry(**options)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
api.register(dnsrecord_add)
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord_delentry(LDAPDelete):
|
||||||
|
"""
|
||||||
|
Delete DNS record entry.
|
||||||
|
"""
|
||||||
|
INTERNAL = True
|
||||||
|
|
||||||
|
api.register(dnsrecord_delentry)
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord_del(dnsrecord_mod_record):
|
||||||
|
"""
|
||||||
|
Delete DNS resource record.
|
||||||
|
"""
|
||||||
|
def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
|
||||||
|
for (a, v) in entry_attrs.iteritems():
|
||||||
|
if not isinstance(v, (list, tuple)):
|
||||||
|
v = [v]
|
||||||
|
for val in v:
|
||||||
|
try:
|
||||||
|
old_entry_attrs[a].remove(val)
|
||||||
|
except (KeyError, ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def post_callback(self, keys, entry_attrs):
|
||||||
|
if not self.obj.is_pkey_zone_record(*keys):
|
||||||
|
for a in _record_attributes:
|
||||||
|
if a in entry_attrs and entry_attrs[a]:
|
||||||
|
return
|
||||||
|
self.obj.methods.delentry(*keys)
|
||||||
|
|
||||||
|
api.register(dnsrecord_del)
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord_show(LDAPRetrieve, dnsrecord_cmd_w_record_options):
|
||||||
|
"""
|
||||||
|
Display DNS resource.
|
||||||
|
"""
|
||||||
|
def has_output_params(self):
|
||||||
|
for option in self.get_record_options():
|
||||||
|
yield option
|
||||||
|
|
||||||
|
def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
|
||||||
|
if self.obj.is_pkey_zone_record(*keys):
|
||||||
|
entry_attrs[self.obj.primary_key.name] = [u'@']
|
||||||
|
return dn
|
||||||
|
|
||||||
|
api.register(dnsrecord_show)
|
||||||
|
|
||||||
|
|
||||||
|
class dnsrecord_find(LDAPSearch, dnsrecord_cmd_w_record_options):
|
||||||
|
"""
|
||||||
|
Search for DNS resources.
|
||||||
|
"""
|
||||||
|
def get_options(self):
|
||||||
|
for option in super(dnsrecord_find, self).get_options():
|
||||||
|
yield option
|
||||||
|
for option in self.get_record_options():
|
||||||
|
yield option.clone(query=True)
|
||||||
|
|
||||||
|
def pre_callback(self, ldap, filter, attrs_list, base_dn, scope, *args, **options):
|
||||||
|
record_attrs = self.record_options_2_entry(**options)
|
||||||
|
record_filter = ldap.make_filter(record_attrs, rules=ldap.MATCH_ALL)
|
||||||
|
filter = ldap.combine_filters(
|
||||||
|
(filter, record_filter), rules=ldap.MATCH_ALL
|
||||||
|
)
|
||||||
|
return (filter, base_dn, ldap.SCOPE_SUBTREE)
|
||||||
|
|
||||||
|
def post_callback(self, ldap, entries, truncated, *args, **options):
|
||||||
|
if entries:
|
||||||
|
zone_obj = self.api.Object[self.obj.parent_object]
|
||||||
|
zone_dn = zone_obj.get_dn(args[0])
|
||||||
|
if entries[0][0] == zone_dn:
|
||||||
|
entries[0][1][zone_obj.primary_key.name] = [u'@']
|
||||||
|
|
||||||
|
api.register(dnsrecord_find)
|
||||||
|
|
341
tests/test_xmlrpc/test_dns_plugin.py
Normal file
341
tests/test_xmlrpc/test_dns_plugin.py
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
# Authors:
|
||||||
|
# Pavel Zuna <pzuna@redhat.com>
|
||||||
|
#
|
||||||
|
# Copyright (C) 2010 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; version 2 only
|
||||||
|
#
|
||||||
|
# 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, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
"""
|
||||||
|
Test the `ipalib/plugins/dns.py` module.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ipalib import api, errors
|
||||||
|
from tests.test_xmlrpc import objectclasses
|
||||||
|
from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid
|
||||||
|
|
||||||
|
dnszone1 = u'dnszone.test'
|
||||||
|
dnsres1 = u'testdnsres'
|
||||||
|
|
||||||
|
class test_dns(Declarative):
|
||||||
|
|
||||||
|
cleanup_commands = [
|
||||||
|
('dnszone_del', [dnszone1], {}),
|
||||||
|
('dnsrecord_del', [dnszone1, dnsres1], {}),
|
||||||
|
]
|
||||||
|
|
||||||
|
tests = [
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to retrieve non-existent zone %r' % dnszone1,
|
||||||
|
command=('dnszone_show', [dnszone1], {}),
|
||||||
|
expected=errors.NotFound(reason='DNS zone not found'),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to update non-existent zone %r' % dnszone1,
|
||||||
|
command=('dnszone_mod', [dnszone1], {'idnssoamname': u'foobar'}),
|
||||||
|
expected=errors.NotFound(reason='DNS zone not found'),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to delete non-existent zone %r' % dnszone1,
|
||||||
|
command=('dnszone_del', [dnszone1], {}),
|
||||||
|
expected=errors.NotFound(reason='DNS zone not found'),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create zone %r' % dnszone1,
|
||||||
|
command=(
|
||||||
|
'dnszone_add', [dnszone1], {
|
||||||
|
'idnssoamname': u'ns1.%s' % dnszone1,
|
||||||
|
'idnssoarname': u'root.%s' % dnszone1,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
expected={
|
||||||
|
'value': dnszone1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [dnszone1],
|
||||||
|
'idnszoneactive': [u'TRUE'],
|
||||||
|
'idnssoamname': [u'ns1.%s' % dnszone1],
|
||||||
|
'idnssoarname': [u'root.%s.' % dnszone1],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [fuzzy_digits],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
'idnsallowdynupdate': [u'FALSE'],
|
||||||
|
'objectclass': [u'top', u'idnsrecord', u'idnszone'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to create duplicate zone %r' % dnszone1,
|
||||||
|
command=(
|
||||||
|
'dnszone_add', [dnszone1], {
|
||||||
|
'idnssoamname': u'ns1.%s' % dnszone1,
|
||||||
|
'idnssoarname': u'root.%s' % dnszone1,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
expected=errors.DuplicateEntry(),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Retrieve zone %r' % dnszone1,
|
||||||
|
command=('dnszone_show', [dnszone1], {}),
|
||||||
|
expected={
|
||||||
|
'value': dnszone1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [dnszone1],
|
||||||
|
'idnszoneactive': [u'TRUE'],
|
||||||
|
'idnssoamname': [u'ns1.%s' % dnszone1],
|
||||||
|
'idnssoarname': [u'root.%s.' % dnszone1],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [fuzzy_digits],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Update zone %r' % dnszone1,
|
||||||
|
command=('dnszone_mod', [dnszone1], {'idnssoarefresh': 5478}),
|
||||||
|
expected={
|
||||||
|
'value': dnszone1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'idnsname': [dnszone1],
|
||||||
|
'idnszoneactive': [u'TRUE'],
|
||||||
|
'idnssoamname': [u'ns1.%s' % dnszone1],
|
||||||
|
'idnssoarname': [u'root.%s.' % dnszone1],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [u'5478'],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
'idnsallowdynupdate': [u'FALSE'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Search for zones with name server %r' % (u'ns1.%s' % dnszone1),
|
||||||
|
command=('dnszone_find', [], {'idnssoamname': u'ns1.%s' % dnszone1}),
|
||||||
|
expected={
|
||||||
|
'summary': None,
|
||||||
|
'count': 1,
|
||||||
|
'truncated': False,
|
||||||
|
'result': [{
|
||||||
|
'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [dnszone1],
|
||||||
|
'idnszoneactive': [u'TRUE'],
|
||||||
|
'idnssoamname': [u'ns1.%s' % dnszone1],
|
||||||
|
'idnssoarname': [u'root.%s.' % dnszone1],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [u'5478'],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Disable zone %r' % dnszone1,
|
||||||
|
command=('dnszone_disable', [dnszone1], {}),
|
||||||
|
expected={
|
||||||
|
'value': dnszone1,
|
||||||
|
'summary': u'Disabled DNS zone "%s"' % dnszone1,
|
||||||
|
'result': True,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Check if zone %r is really disabled' % dnszone1,
|
||||||
|
command=('dnszone_show', [dnszone1], {}),
|
||||||
|
expected={
|
||||||
|
'value': dnszone1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [dnszone1],
|
||||||
|
'idnszoneactive': [u'FALSE'],
|
||||||
|
'idnssoamname': [u'ns1.%s' % dnszone1],
|
||||||
|
'idnssoarname': [u'root.%s.' % dnszone1],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [fuzzy_digits],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Enable zone %r' % dnszone1,
|
||||||
|
command=('dnszone_enable', [dnszone1], {}),
|
||||||
|
expected={
|
||||||
|
'value': dnszone1,
|
||||||
|
'summary': u'Enabled DNS zone "%s"' % dnszone1,
|
||||||
|
'result': True,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Check if zone %r is really enabled' % dnszone1,
|
||||||
|
command=('dnszone_show', [dnszone1], {}),
|
||||||
|
expected={
|
||||||
|
'value': dnszone1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [dnszone1],
|
||||||
|
'idnszoneactive': [u'TRUE'],
|
||||||
|
'idnssoamname': [u'ns1.%s' % dnszone1],
|
||||||
|
'idnssoarname': [u'root.%s.' % dnszone1],
|
||||||
|
'idnssoaserial': [fuzzy_digits],
|
||||||
|
'idnssoarefresh': [fuzzy_digits],
|
||||||
|
'idnssoaretry': [fuzzy_digits],
|
||||||
|
'idnssoaexpire': [fuzzy_digits],
|
||||||
|
'idnssoaminimum': [fuzzy_digits],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to retrieve non-existent record %r in zone %r' % (dnsres1, dnszone1),
|
||||||
|
command=('dnsrecord_show', [dnszone1, dnsres1], {}),
|
||||||
|
expected=errors.NotFound(reason='DNS resource record not found'),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Try to delete non-existent record %r in zone %r' % (dnsres1, dnszone1),
|
||||||
|
command=('dnsrecord_del', [dnszone1, dnsres1], {}),
|
||||||
|
expected=errors.NotFound(reason='DNS resource record not found'),
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Create record %r in zone %r' % (dnszone1, dnsres1),
|
||||||
|
command=('dnsrecord_add', [dnszone1, dnsres1], {'arecord': u'127.0.0.1'}),
|
||||||
|
expected={
|
||||||
|
'value': dnsres1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': u'idnsname=%s,idnsname=%s,cn=dns,%s' % (dnsres1, dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [dnsres1],
|
||||||
|
'objectclass': [u'top', u'idnsrecord'],
|
||||||
|
'arecord': [u'127.0.0.1'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Search for all records in zone %r' % dnszone1,
|
||||||
|
command=('dnsrecord_find', [dnszone1], {}),
|
||||||
|
expected={
|
||||||
|
'summary': None,
|
||||||
|
'count': 2,
|
||||||
|
'truncated': False,
|
||||||
|
'result': [
|
||||||
|
{
|
||||||
|
'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [u'@'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'dn': u'idnsname=%s,idnsname=%s,cn=dns,%s' % (dnsres1, dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [dnsres1],
|
||||||
|
'arecord': [u'127.0.0.1'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Add A record to %r in zone %r' % (dnszone1, dnsres1),
|
||||||
|
command=('dnsrecord_add', [dnszone1, dnsres1], {'arecord': u'10.10.0.1'}),
|
||||||
|
expected={
|
||||||
|
'value': dnsres1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'dn': u'idnsname=%s,idnsname=%s,cn=dns,%s' % (dnsres1, dnszone1, api.env.basedn),
|
||||||
|
'idnsname': [dnsres1],
|
||||||
|
'arecord': [u'127.0.0.1', u'10.10.0.1'],
|
||||||
|
'objectclass': [u'top', u'idnsrecord'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Remove A record from %r in zone %r' % (dnszone1, dnsres1),
|
||||||
|
command=('dnsrecord_del', [dnszone1, dnsres1], {'arecord': u'127.0.0.1'}),
|
||||||
|
expected={
|
||||||
|
'value': dnsres1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'idnsname': [dnsres1],
|
||||||
|
'arecord': [u'10.10.0.1'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Delete record %r in zone %r' % (dnsres1, dnszone1),
|
||||||
|
command=('dnsrecord_del', [dnszone1, dnsres1], {}),
|
||||||
|
expected={
|
||||||
|
'value': dnsres1,
|
||||||
|
'summary': None,
|
||||||
|
'result': {
|
||||||
|
'idnsname': [dnsres1],
|
||||||
|
'arecord': [u'10.10.0.1'],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
dict(
|
||||||
|
desc='Delete zone %r' % dnszone1,
|
||||||
|
command=('dnszone_del', [dnszone1], {}),
|
||||||
|
expected={
|
||||||
|
'value': dnszone1,
|
||||||
|
'summary': None,
|
||||||
|
'result': True,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user