summaryrefslogtreecommitdiffstats
path: root/ipalib/plugins/dns.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib/plugins/dns.py')
-rw-r--r--ipalib/plugins/dns.py159
1 files changed, 139 insertions, 20 deletions
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 3f8753d11..42ca498c9 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -1,5 +1,6 @@
# Authors:
# Pavel Zuna <pzuna@redhat.com>
+# Martin Kosek <mkosek@redhat.com>
#
# Copyright (C) 2010 Red Hat
# see file 'COPYING' for use and warranty information
@@ -49,6 +50,28 @@ EXAMPLES:
ipa dnsrecord-add example.com _ldap._tcp --srv-rec="0 1 389 slow.example.com"
ipa dnsrecord-add example.com _ldap._tcp --srv-rec="1 1 389 backup.example.com"
+ When dnsrecord-add command is executed with no option to add a specific record
+ an interactive mode is started. The mode interactively prompts for the most
+ typical record types for the respective zone:
+ ipa dnsrecord-add example.com www
+ [A record]: 1.2.3.4,11.22.33.44 (2 interactively entered random IPs)
+ [AAAA record]: (no AAAA address entered)
+ Record name: www
+ A record: 1.2.3.4, 11.22.33.44
+
+ The interactive mode can also be used for deleting the DNS records:
+ ipa dnsrecord-del example.com www
+ No option to delete specific record provided.
+ Delete all? Yes/No (default No): (do not delete all records)
+ Current DNS record contents:
+
+ A record: 1.2.3.4, 11.22.33.44
+
+ Delete A record '1.2.3.4'? Yes/No (default No):
+ Delete A record '11.22.33.44'? Yes/No (default No): y
+ Record name: www
+ A record: 1.2.3.4 (A record 11.22.33.44 has been deleted)
+
Show zone example.com:
ipa dnszone-show example.com
@@ -71,7 +94,6 @@ EXAMPLES:
if one is not included):
ipa dns-resolve www.example.com
ipa dns-resolve www
-
"""
import netaddr
@@ -93,6 +115,14 @@ _record_types = (
u'TSIG', u'TXT',
)
+# DNS zone record identificator
+_dns_zone_record = u'@'
+
+# most used record types, always ask for those in interactive prompt
+_top_record_types = ('A', 'AAAA', )
+_rev_top_record_types = ('PTR', )
+_zone_top_record_types = ('NS', 'MX', 'LOC', )
+
# attributes derived from record types
_record_attributes = [str('%srecord' % t.lower()) for t in _record_types]
@@ -195,6 +225,14 @@ _valid_reverse_zones = {
'.ip6.arpa.' : 32,
}
+def zone_is_reverse(zone_name):
+ for rev_zone_name in _valid_reverse_zones.keys():
+ if zone_name.endswith(rev_zone_name):
+ return True
+
+ return False
+
+
def has_cli_options(entry, no_option_msg):
entry = dict((t, entry.get(t, [])) for t in _record_attributes)
numattr = reduce(lambda x,y: x+y,
@@ -505,7 +543,7 @@ class dnsrecord(LDAPObject):
def is_pkey_zone_record(self, *keys):
idnsname = keys[-1]
- if idnsname == '@' or idnsname == ('%s.' % keys[-2]):
+ if idnsname == str(_dns_zone_record) or idnsname == ('%s.' % keys[-2]):
return True
return False
@@ -533,25 +571,40 @@ class dnsrecord_cmd_w_record_options(Command):
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
- )
+ yield self.get_record_option(t)
def record_options_2_entry(self, **options):
entries = dict((t, options.get(t, [])) for t in _record_attributes)
entries.update(dict((k, []) for (k,v) in entries.iteritems() if v == None ))
return entries
+ def get_record_option(self, rec_type):
+ doc = self.record_param_doc % rec_type
+ validator = _record_validators.get(rec_type)
+ if validator:
+ return List(
+ '%srecord?' % rec_type.lower(), validator,
+ cli_name='%s_rec' % rec_type.lower(), doc=doc,
+ label='%s record' % rec_type, attribute=True
+ )
+ else:
+ return List(
+ '%srecord?' % rec_type.lower(), cli_name='%s_rec' % rec_type.lower(),
+ doc=doc, label='%s record' % rec_type, attribute=True
+ )
+
+ def prompt_record_options(self, rec_type_list):
+ user_options = {}
+ # ask for all usual record types
+ for rec_type in rec_type_list:
+ rec_option = self.get_record_option(rec_type)
+ raw = self.Backend.textui.prompt(rec_option.label,optional=True)
+ rec_value = rec_option(raw)
+ if rec_value is not None:
+ user_options[rec_option.name] = rec_value
+
+ return user_options
+
class dnsrecord_mod_record(LDAPQuery, dnsrecord_cmd_w_record_options):
"""
@@ -599,7 +652,7 @@ class dnsrecord_mod_record(LDAPQuery, dnsrecord_cmd_w_record_options):
self.obj.handle_not_found(*keys)
if self.obj.is_pkey_zone_record(*keys):
- entry_attrs[self.obj.primary_key.name] = [u'@']
+ entry_attrs[self.obj.primary_key.name] = [_dns_zone_record]
retval = self.post_callback(keys, entry_attrs)
if retval:
@@ -637,7 +690,8 @@ class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options):
"""
Add new DNS resource record.
"""
- no_option_msg = 'No options to add a specific record provided.'
+ no_option_msg = 'No options to add a specific record provided.\n' \
+ "Command help may be consulted for all supported record types."
takes_options = LDAPCreate.takes_options + (
Flag('force',
label=_('Force'),
@@ -693,6 +747,24 @@ class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options):
return dn
+ def interactive_prompt_callback(self, kw):
+ for param in kw.keys():
+ if param in _record_attributes:
+ # some record type entered, skip this helper
+ return
+
+ # check zone type
+ if kw['idnsname'] == _dns_zone_record:
+ top_record_types = _zone_top_record_types
+ elif zone_is_reverse(kw['dnszoneidnsname']):
+ top_record_types = _rev_top_record_types
+ else:
+ top_record_types = _top_record_types
+
+ # ask for all usual record types
+ user_options = self.prompt_record_options(top_record_types)
+ kw.update(user_options)
+
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
for rtype in options:
rtype_cb = '_%s_pre_callback' % rtype
@@ -727,7 +799,8 @@ class dnsrecord_del(dnsrecord_mod_record):
"""
Delete DNS resource record.
"""
- no_option_msg = _('Neither --del-all nor options to delete a specific record provided.')
+ no_option_msg = _('Neither --del-all nor options to delete a specific record provided.\n'\
+ "Command help may be consulted for all supported record types.")
takes_options = (
Flag('del_all',
default=False,
@@ -745,6 +818,52 @@ class dnsrecord_del(dnsrecord_mod_record):
entry = super(dnsrecord_del, self).record_options_2_entry(**options)
return has_cli_options(entry, self.no_option_msg)
+ def interactive_prompt_callback(self, kw):
+ if kw.get('del_all', False):
+ return
+ for param in kw.keys():
+ if param in _record_attributes:
+ # we have something to delete, skip this helper
+ return
+
+ # get DNS record first so that the NotFound exception is raised
+ # before the helper would start
+ dns_record = api.Command['dnsrecord_show'](kw['dnszoneidnsname'], kw['idnsname'])['result']
+ rec_types = [rec_type for rec_type in dns_record if rec_type in _record_attributes]
+
+ self.Backend.textui.print_plain(_("No option to delete specific record provided."))
+ user_del_all = self.Backend.textui.prompt_yesno(_("Delete all?"), default=False)
+
+ if user_del_all is True:
+ kw['del_all'] = True
+ return
+
+ # ask user for records to be removed
+ dns_record = api.Command['dnsrecord_show'](kw['dnszoneidnsname'], kw['idnsname'])['result']
+ rec_types = [rec_type for rec_type in dns_record if rec_type in _record_attributes]
+
+ self.Backend.textui.print_plain(_(u'Current DNS record contents:\n'))
+ present_params = []
+ for param in self.params():
+ if param.name in _record_attributes and param.name in dns_record:
+ present_params.append(param)
+ rec_type_content = u', '.join(dns_record[param.name])
+ self.Backend.textui.print_plain(u'%s: %s' % (param.label, rec_type_content))
+ self.Backend.textui.print_plain(u'')
+
+ # ask what records to remove
+ for param in present_params:
+ deleted_values = []
+ for rec_value in dns_record[param.name]:
+ user_del_value = self.Backend.textui.prompt_yesno(
+ _(u"Delete %s '%s'?"
+ % (param.label, rec_value)), default=False)
+ if user_del_value is True:
+ deleted_values.append(rec_value)
+ if deleted_values:
+ deleted_list = u','.join(deleted_values)
+ kw[param.name] = param(deleted_list)
+
def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
for (a, v) in entry_attrs.iteritems():
if not isinstance(v, (list, tuple)):
@@ -776,7 +895,7 @@ class dnsrecord_show(LDAPRetrieve, dnsrecord_cmd_w_record_options):
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'@']
+ entry_attrs[self.obj.primary_key.name] = [_dns_zone_record]
return dn
api.register(dnsrecord_show)
@@ -805,7 +924,7 @@ class dnsrecord_find(LDAPSearch, dnsrecord_cmd_w_record_options):
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'@']
+ entries[0][1][zone_obj.primary_key.name] = [_dns_zone_record]
api.register(dnsrecord_find)