[Freeipa-devel] [PATCH] 037 Remove the original DNS plugin
Jakub Hrozek
jhrozek at redhat.com
Wed Jan 12 21:40:40 UTC 2011
On Wed, Jan 12, 2011 at 08:58:15PM +0100, Jakub Hrozek wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 01/12/2011 08:56 PM, Jakub Hrozek wrote:
> > I didn't find a related ticket, but I think this needs to be done. At
> > the very least it caused confusion for QA.
> >
> > This patch
> > - removes the obsolete DNS plugin
> > - renames the new plugin to dns
> > - moves ipa dns-resolve to the new plugin
> > - ports the installer and the host plugin to the new interface
> >
> > I didn't touch the UI at all. Adam, Endi, do I need to tweak it somehow
> > (esp. because the plugin is renamed).
> >
> > Jakub
>
> Attached is another version of the same patch, just formatted with -M
> - -C, so it should hopefully look better.
>
OK, that was still not very readable so I splitted the patches into two
to ease the review:
1) jhrozek-freeipa-037-03-dont-use-legacy-dns.patch:
Port installer and host plugin to the new DNS plugin
* moves ipa dns-resolve to the new plugin
* ports the installer and the host plugin to the new interface
2) jhrozek-freeipa-038-rename-dns2-to-dns.patch
No functionality change, just renames the old plugin to the new one.
I used "git format-patch -M -C --patience --full-index" to format the
patch but git still didn't detect the replace, it seems. Is there
anything else I can do in order to get a prettier patch? I created the
patch with "git rm ipalib/plugins/dns.py" and then "git mv
ipalib/plugins/dns2.py ipalib/plugins/dns.py" -- without performing rm
first, git would complain about renaming file to another which is
tracked.
-------------- next part --------------
>From a244add5fcfd74415c537074f456bce2adb1160e Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 12 Jan 2011 21:02:05 +0100
Subject: [PATCH 1/2] 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
---
ipalib/plugins/dns2.py | 52 ++++++++++++++++++++++++++++++++-----
ipalib/plugins/host.py | 35 ++++++++++++++----------
ipaserver/install/bindinstance.py | 30 ++++++++++----------
3 files changed, 80 insertions(+), 37 deletions(-)
diff --git a/ipalib/plugins/dns2.py b/ipalib/plugins/dns2.py
index 9254f1df9184a04fd5b6940eb3c2198b092b0c1d..d8e0ad657ef45085258cfec018647d83455eb94c 100644
--- a/ipalib/plugins/dns2.py
+++ b/ipalib/plugins/dns2.py
@@ -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 88ac0bcb780d44d1619d3eb2d91c74a3b2ed3a25..d60f63776714689e754c3f1c18e972e062304edc 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 e1a5810f44d342fd1031efaf40da55684e833825..976b69541588d4dc157ae42fc69a9c2f09f3b71c 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) : unicode(rdata) }
+ api.Command.dnsrecord_del(unicode(zone), unicode(host), **delkw)
except:
pass
j += 1
--
1.7.3.4
-------------- next part --------------
>From 4257cc21718380674bd1f0c20b44f16984afe316 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Wed, 12 Jan 2011 22:09:02 +0100
Subject: [PATCH 2/2] Rename dns2 to dns
---
ipalib/plugins/dns.py | 1032 +++++++++++++++++-------------------------------
ipalib/plugins/dns2.py | 623 -----------------------------
ipalib/plugins/host.py | 2 +-
3 files changed, 358 insertions(+), 1299 deletions(-)
delete mode 100644 ipalib/plugins/dns2.py
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index ced13efc92b2480bbe15675233903edd8387fa16..cf58098036f7056d20337b4d3b5f02b158b41360 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -1,7 +1,7 @@
# Authors:
# Pavel Zuna <pzuna at redhat.com>
#
-# Copyright (C) 2009 Red Hat
+# 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
@@ -17,45 +17,45 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-Domain Name System (DNS) plug-in
+Domain Name System (DNS)
-Implements a set of commands useful for manipulating DNS records used by
-the BIND LDAP plug-in.
+Manage DNS zone and resource records.
EXAMPLES:
Add new zone:
- ipa dns-add example.com nameserver.example.com admin at example.com
+ ipa dnszone-add example.com --name-server nameserver.example.com
+ --admin-email admin at example.com
Add second nameserver for example.com:
- ipa dns-add-rr example.com @ NS nameserver2.example.com
+ ipa dnsrecord-add example.com @ --ns-rec nameserver2.example.com
Delete previously added nameserver from example.com:
- ipa dns-del-rr example.com @ NS nameserver2.example.com
+ ipa dnsrecord-del example.com @ --ns-rec 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
+ ipa dnsrecord-add example.com www --a-rec 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.
+ ipa dnsrecord-add 15.142.80.in-addr.arpa 2 --ptr-rec www.example.com.
Show zone example.com:
- ipa dns-show example.com
+ ipa dnszone-show example.com
Find zone with "example" in it's domain name:
- ipa dns-find example
+ ipa dnszone-find example
Find records for resources with "www" in their name in zone example.com:
- ipa dns-find-rr example.com www
+ ipa dnsrecord-find example.com www
- Find A records for resource www in zone example.com
- ipa dns-find-rr example.com --resource www --type A
+ 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 dns-show-rr example.com www
+ ipa dnsrecord-show example.com www
Delete zone example.com with all resource records:
- ipa dns-delete example.com
+ ipa dnszone-del example.com
Resolve a host name to see if it exists (will add default IPA domain
if one is not included):
@@ -64,59 +64,31 @@ EXAMPLES:
"""
-# 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 netaddr
import time
-from ipalib import api, crud, errors, output
-from ipalib import Object, Command
-from ipalib import Flag, Int, Str, StrEnum
+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 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',
+ 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',
)
-# 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',
-)
+# 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')
-# 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('@', '.')
@@ -124,41 +96,57 @@ def _rname_normalizer(value):
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)
+def _create_zone_serial(**kwargs):
+ """Generate serial number for zones."""
+ return int('%s01' % time.strftime('%Y%d%m'))
-# 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 _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,
+}
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)
+ ldap.get_entry(api.env.container_dns, [])
except errors.NotFound:
- raise errors.NotFound(reason=_('DNS is not configured'))
-
+ return False
return True
-class dns(Object):
- """DNS zone/SOA record object."""
+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')
takes_params = (
Str('idnsname',
cli_name='name',
- label=_('Zone'),
+ label=_('Zone name'),
doc=_('Zone name (FQDN)'),
normalizer=lambda value: value.lower(),
primary_key=True,
@@ -166,743 +154,437 @@ class dns(Object):
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'),
+ 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,
),
- Flag('idnsallowdynupdate',
- cli_name='allow_dynupdate',
- label=_('allow dynamic update?'),
- ),
Str('idnsupdatepolicy?',
cli_name='update_policy',
label=_('BIND update policy'),
+ doc=_('BIND update policy'),
+ ),
+ Flag('idnszoneactive?',
+ cli_name='zone_active',
+ label=_('Active zone'),
+ doc=_('Is zone active?'),
+ flags=['no_create', 'no_update'],
+ attribute=True,
+ ),
+ Flag('idnsallowdynupdate',
+ cli_name='allow_dynupdate',
+ label=_('Dynamic update'),
+ doc=_('Allow dynamic update?'),
+ attribute=True,
),
)
- default_attributes = _zone_default_attributes
+api.register(dnszone)
- 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):
+class dnszone_add(LDAPCreate):
"""
- Create new DNS zone/SOA record.
+ 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)
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+ if not dns_container_exists(self.api.Backend.ldap2):
+ raise errors.NotFound(reason=_('DNS is not configured'))
- # fill in required attributes
- entry_attrs['objectclass'] = ['top', 'idnsrecord', 'idnszone']
entry_attrs['idnszoneactive'] = 'TRUE'
entry_attrs['idnsallowdynupdate'] = str(
- entry_attrs['idnsallowdynupdate']
+ entry_attrs.get('idnsallowdynupdate', False)
).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)
+ nameserver = entry_attrs['idnssoamname']
+ if nameserver[-1] != '.':
+ nameserver += '.'
+ entry_attrs['nsrecord'] = nameserver
+ entry_attrs['idnssoamname'] = nameserver
+ return dn
- # create zone entry
- ldap.add_entry(dn, entry_attrs)
+api.register(dnszone_add)
- # 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):
+class dnszone_del(LDAPDelete):
"""
- Delete existing DNS zone/SOA record.
+ Delete 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)
+api.register(dnszone_del)
- # 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):
+class dnszone_mod(LDAPUpdate):
"""
- Modify DNS zone/SOA record.
+ 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)
+ def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
entry_attrs['idnsallowdynupdate'] = str(
- entry_attrs['idnsallowdynupdate']
+ entry_attrs.get('idnsallowdynupdate', False)
).upper()
+ return dn
- # build entry DN
- dn = _get_zone_dn(ldap, idnsname)
+api.register(dnszone_mod)
- # 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):
+class dnszone_find(LDAPSearch):
"""
- Display DNS zone/SOA record.
+ Search for DNS zones (SOA records).
"""
- 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)
+api.register(dnszone_find)
- 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):
+class dnszone_show(LDAPRetrieve):
"""
- Activate DNS zone.
+ Display information about a DNS zone (SOA record).
"""
- 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)
+api.register(dnszone_show)
- 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):
+class dnszone_disable(LDAPQuery):
"""
- Deactivate DNS zone.
+ Disable DNS Zone.
"""
- takes_args = (
- Str('zone',
- label=_('Zone name'),
- normalizer=lambda value: value.lower(),
- ),
- )
-
has_output = output.standard_value
+ msg_summary = _('Disabled DNS zone "%(value)s"')
- def execute(self, zone):
- ldap = self.api.Backend.ldap2
-
- dns_container_exists(ldap)
+ def execute(self, *keys, **options):
+ ldap = self.obj.backend
- # build entry DN
- dn = _get_zone_dn(ldap, zone)
+ dn = self.obj.get_dn(*keys, **options)
- # deactivate!
try:
ldap.update_entry(dn, {'idnszoneactive': 'FALSE'})
except errors.EmptyModlist:
pass
- return dict(result=True, value=zone)
+ return dict(result=True, value=keys[-1])
- def output_for_cli(self, textui, result, zone):
- textui.print_name(self.name)
- textui.print_dashed('Deactivated DNS zone "%s".' % zone)
+api.register(dnszone_disable)
-api.register(dns_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
-class dns_add_rr(Command):
+ return dict(result=True, value=keys[-1])
+
+api.register(dnszone_enable)
+
+
+class dnsrecord(LDAPObject):
"""
- Add new DNS resource record.
+ 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_args = (
- Str('zone',
- label=_('Zone name'),
- normalizer=lambda value: value.lower(),
- ),
+ takes_params = (
Str('idnsname',
- cli_name='resource',
- label=_('resource name'),
- default_from=lambda zone: zone.lower(),
- attribute=True,
+ cli_name='name',
+ label=_('Record name'),
+ doc=_('Record name'),
+ primary_key=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,
+ doc=_('Time to live'),
),
StrEnum('dnsclass?',
cli_name='class',
label=_('Class'),
+ doc=_('DNS class'),
values=_record_classes,
- attribute=True,
),
)
- has_output = standard_entry
+ 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 execute(self, zone, idnsname, type, data, **options):
- ldap = self.api.Backend.ldap2
- attr = ('%srecord' % type).lower()
+ 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
- dns_container_exists(ldap)
+ def execute(self, *keys, **options):
+ ldap = self.obj.backend
- # build entry DN
- dn = _get_record_dn(ldap, zone, idnsname)
+ dn = self.obj.get_dn(*keys, **options)
+
+ entry_attrs = self.record_options_2_entry(**options)
- # get resource entry where to store the new record
try:
- (dn, entry_attrs) = ldap.get_entry(dn, [attr])
+ (dn, old_entry_attrs) = ldap.get_entry(dn, entry_attrs.keys())
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
- )
+ 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)
- # fill in required attributes
- entry_attrs['objectclass'] = ['top', 'idnsrecord']
+ if self.obj.is_pkey_zone_record(*keys):
+ entry_attrs[self.obj.primary_key.name] = [u'@']
- # fill in the record
- entry_attrs[attr] = data
+ self.post_callback(keys, entry_attrs)
- # create the entry
- ldap.add_entry(dn, entry_attrs)
+ return dict(result=entry_attrs, value=keys[-1])
- # get entry with created attributes for output
- (dn, entry_attrs) = ldap.get_entry(dn, entry_attrs.keys())
- entry_attrs['dn'] = dn
+ def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
+ pass
- return dict(result=entry_attrs, value=idnsname)
+ def post_callback(self, keys, entry_attrs):
+ pass
- # 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()
- #)
+class dnsrecord_add_record(dnsrecord_mod_record):
+ """
+ Add records to DNS resource.
+ """
+ INTERNAL = True
- # get new value for record attribute
- attr_value = entry_attrs.get(attr, [])
- attr_value.append(data)
+ 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
- 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
+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
- return dict(result=entry_attrs, value=idnsname)
+api.register(dnsrecord_add)
- 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)
+class dnsrecord_delentry(LDAPDelete):
+ """
+ Delete DNS record entry.
+ """
+ INTERNAL = True
-api.register(dns_add_rr)
+api.register(dnsrecord_delentry)
-class dns_del_rr(Command):
+class dnsrecord_del(dnsrecord_mod_record):
"""
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):
+ 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):
"""
- Search for DNS resource records.
+ Display DNS resource.
"""
- 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)
+ def has_output_params(self):
+ for option in self.get_record_options():
+ yield option
- # build base dn for search
- base_dn = _get_zone_dn(ldap, zone)
+ 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
- # 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
+api.register(dnsrecord_show)
- # 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):
+class dnsrecord_find(LDAPSearch, dnsrecord_cmd_w_record_options):
"""
- Show existing DNS resource records.
+ 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)
- 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)
+ 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)
class dns_resolve(Command):
"""
diff --git a/ipalib/plugins/dns2.py b/ipalib/plugins/dns2.py
deleted file mode 100644
index d8e0ad657ef45085258cfec018647d83455eb94c..0000000000000000000000000000000000000000
--- a/ipalib/plugins/dns2.py
+++ /dev/null
@@ -1,623 +0,0 @@
-# Authors:
-# Pavel Zuna <pzuna at 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, 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 <http://www.gnu.org/licenses/>.
-"""
-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 at example.com
-
- Add 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,
-}
-
-
-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.
- """
- 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')
-
- 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'),
- ),
- Flag('idnszoneactive?',
- cli_name='zone_active',
- label=_('Active zone'),
- doc=_('Is zone active?'),
- flags=['no_create', 'no_update'],
- attribute=True,
- ),
- Flag('idnsallowdynupdate',
- cli_name='allow_dynupdate',
- label=_('Dynamic update'),
- doc=_('Allow dynamic update?'),
- attribute=True,
- ),
- )
-
-api.register(dnszone)
-
-
-class dnszone_add(LDAPCreate):
- """
- Create new DNS zone (SOA record).
- """
- def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
- 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)
- ).upper()
-
- nameserver = entry_attrs['idnssoamname']
- if nameserver[-1] != '.':
- nameserver += '.'
- entry_attrs['nsrecord'] = nameserver
- entry_attrs['idnssoamname'] = nameserver
- 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).
- """
- 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)
-
-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 d60f63776714689e754c3f1c18e972e062304edc..8639ce5a07e1b0d8f3707633ba184a7bee3ad51c 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.dns2 import dns_container_exists, _record_types
+from ipalib.plugins.dns import dns_container_exists, _record_types
from ipalib import _, ngettext
from ipalib import x509
from ipapython.ipautil import ipa_generate_password
--
1.7.3.4
More information about the Freeipa-devel
mailing list