summaryrefslogtreecommitdiffstats
path: root/ipaclient/plugins/dns.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipaclient/plugins/dns.py')
-rw-r--r--ipaclient/plugins/dns.py325
1 files changed, 325 insertions, 0 deletions
diff --git a/ipaclient/plugins/dns.py b/ipaclient/plugins/dns.py
new file mode 100644
index 000000000..6fca7cd24
--- /dev/null
+++ b/ipaclient/plugins/dns.py
@@ -0,0 +1,325 @@
+# Authors:
+# Martin Kosek <mkosek@redhat.com>
+# 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, 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/>.
+
+from __future__ import print_function
+
+import six
+
+from ipaclient.frontend import MethodOverride
+from ipalib import errors
+from ipalib.dns import (get_record_rrtype,
+ has_cli_options,
+ iterate_rrparams_by_parts,
+ record_name_format)
+from ipalib.plugable import Registry
+from ipalib import _, ngettext
+from ipapython.dnsutil import DNSName
+
+if six.PY3:
+ unicode = str
+
+register = Registry()
+
+# 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', )
+
+
+def __get_part_param(cmd, part, output_kw, default=None):
+ name = part.name
+ label = unicode(part.label)
+ optional = not part.required
+
+ output_kw[name] = cmd.prompt_param(part,
+ optional=optional,
+ label=label)
+
+
+def prompt_parts(rrtype, cmd, mod_dnsvalue=None):
+ mod_parts = None
+ if mod_dnsvalue is not None:
+ name = record_name_format % rrtype.lower()
+ mod_parts = cmd.api.Command.dnsrecord_split_parts(
+ name, mod_dnsvalue)['result']
+
+ user_options = {}
+ parts = [p for p in cmd.params() if 'dnsrecord_part' in p.flags]
+ if not parts:
+ return user_options
+
+ for part_id, part in enumerate(parts):
+ if mod_parts:
+ default = mod_parts[part_id]
+ else:
+ default = None
+
+ __get_part_param(cmd, part, user_options, default)
+
+ return user_options
+
+
+def prompt_missing_parts(rrtype, cmd, kw, prompt_optional=False):
+ user_options = {}
+ parts = [p for p in cmd.params() if 'dnsrecord_part' in p.flags]
+ if not parts:
+ return user_options
+
+ for part in parts:
+ name = part.name
+
+ if name in kw:
+ continue
+
+ optional = not part.required
+ if optional and not prompt_optional:
+ continue
+
+ default = part.get_default(**kw)
+ __get_part_param(cmd, part, user_options, default)
+
+ return user_options
+
+
+@register(override=True)
+class dnsrecord_add(MethodOverride):
+ no_option_msg = 'No options to add a specific record provided.\n' \
+ "Command help may be consulted for all supported record types."
+
+ def interactive_prompt_callback(self, kw):
+ try:
+ has_cli_options(self, kw, self.no_option_msg)
+
+ # Some DNS records were entered, do not use full interactive help
+ # We should still ask user for required parts of DNS parts he is
+ # trying to add in the same way we do for standard LDAP parameters
+ #
+ # Do not ask for required parts when any "extra" option is used,
+ # it can be used to fill all required params by itself
+ new_kw = {}
+ for rrparam in iterate_rrparams_by_parts(self, kw,
+ skip_extra=True):
+ rrtype = get_record_rrtype(rrparam.name)
+ user_options = prompt_missing_parts(rrtype, self, kw,
+ prompt_optional=False)
+ new_kw.update(user_options)
+ kw.update(new_kw)
+ return
+ except errors.OptionError:
+ pass
+
+ try:
+ idnsname = DNSName(kw['idnsname'])
+ except Exception as e:
+ raise errors.ValidationError(name='idnsname', error=unicode(e))
+
+ try:
+ zonename = DNSName(kw['dnszoneidnsname'])
+ except Exception as e:
+ raise errors.ValidationError(name='dnszoneidnsname', error=unicode(e))
+
+ # check zone type
+ if idnsname.is_empty():
+ common_types = u', '.join(_zone_top_record_types)
+ elif zonename.is_reverse():
+ common_types = u', '.join(_rev_top_record_types)
+ else:
+ common_types = u', '.join(_top_record_types)
+
+ self.Backend.textui.print_plain(_(u'Please choose a type of DNS resource record to be added'))
+ self.Backend.textui.print_plain(_(u'The most common types for this type of zone are: %s\n') %\
+ common_types)
+
+ ok = False
+ while not ok:
+ rrtype = self.Backend.textui.prompt(_(u'DNS resource record type'))
+
+ if rrtype is None:
+ return
+
+ try:
+ name = record_name_format % rrtype.lower()
+ param = self.params[name]
+
+ if 'no_option' in param.flags:
+ raise ValueError()
+ except (KeyError, ValueError):
+ all_types = u', '.join(get_record_rrtype(p.name)
+ for p in self.params()
+ if (get_record_rrtype(p.name) and
+ 'no_option' not in p.flags))
+ self.Backend.textui.print_plain(_(u'Invalid or unsupported type. Allowed values are: %s') % all_types)
+ continue
+ ok = True
+
+ user_options = prompt_parts(rrtype, self)
+ kw.update(user_options)
+
+
+@register(override=True)
+class dnsrecord_mod(MethodOverride):
+ no_option_msg = 'No options to modify a specific record provided.'
+
+ def interactive_prompt_callback(self, kw):
+ try:
+ has_cli_options(self, kw, self.no_option_msg, True)
+ except errors.OptionError:
+ pass
+ else:
+ # some record type entered, skip this helper
+ return
+
+ # get DNS record first so that the NotFound exception is raised
+ # before the helper would start
+ dns_record = self.api.Command['dnsrecord_show'](kw['dnszoneidnsname'], kw['idnsname'])['result']
+
+ self.Backend.textui.print_plain(_("No option to modify specific record provided."))
+
+ # ask user for records to be removed
+ self.Backend.textui.print_plain(_(u'Current DNS record contents:\n'))
+ record_params = []
+
+ for attr in dns_record:
+ try:
+ param = self.params[attr]
+ except KeyError:
+ continue
+ rrtype = get_record_rrtype(param.name)
+ if not rrtype:
+ continue
+
+ record_params.append((param, rrtype))
+ 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, rrtype in record_params:
+ rec_values = list(dns_record[param.name])
+ for rec_value in dns_record[param.name]:
+ rec_values.remove(rec_value)
+ mod_value = self.Backend.textui.prompt_yesno(
+ _("Modify %(name)s '%(value)s'?") % dict(name=param.label, value=rec_value), default=False)
+ if mod_value is True:
+ user_options = prompt_parts(rrtype, self,
+ mod_dnsvalue=rec_value)
+ kw[param.name] = [rec_value]
+ kw.update(user_options)
+
+ if rec_values:
+ self.Backend.textui.print_plain(ngettext(
+ u'%(count)d %(type)s record skipped. Only one value per DNS record type can be modified at one time.',
+ u'%(count)d %(type)s records skipped. Only one value per DNS record type can be modified at one time.',
+ 0) % dict(count=len(rec_values), type=rrtype))
+ break
+
+
+@register(override=True)
+class dnsrecord_del(MethodOverride):
+ 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.")
+
+ def interactive_prompt_callback(self, kw):
+ if kw.get('del_all', False):
+ return
+ try:
+ has_cli_options(self, kw, self.no_option_msg)
+ except errors.OptionError:
+ pass
+ else:
+ # some record type entered, skip this helper
+ return
+
+ # get DNS record first so that the NotFound exception is raised
+ # before the helper would start
+ dns_record = self.api.Command['dnsrecord_show'](kw['dnszoneidnsname'], kw['idnsname'])['result']
+
+ 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
+ self.Backend.textui.print_plain(_(u'Current DNS record contents:\n'))
+ present_params = []
+
+ for attr in dns_record:
+ try:
+ param = self.params[attr]
+ except KeyError:
+ continue
+ if not get_record_rrtype(param.name):
+ continue
+
+ 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(
+ _("Delete %(name)s '%(value)s'?")
+ % dict(name=param.label, value=rec_value), default=False)
+ if user_del_value is True:
+ deleted_values.append(rec_value)
+ if deleted_values:
+ kw[param.name] = tuple(deleted_values)
+
+
+@register(override=True)
+class dnsconfig_mod(MethodOverride):
+ def interactive_prompt_callback(self, kw):
+
+ # show informative message on client side
+ # server cannot send messages asynchronous
+ if kw.get('idnsforwarders', False):
+ self.Backend.textui.print_plain(
+ _("Server will check DNS forwarder(s)."))
+ self.Backend.textui.print_plain(
+ _("This may take some time, please wait ..."))
+
+
+@register(override=True)
+class dnsforwardzone_add(MethodOverride):
+ def interactive_prompt_callback(self, kw):
+ # show informative message on client side
+ # server cannot send messages asynchronous
+ if kw.get('idnsforwarders', False):
+ self.Backend.textui.print_plain(
+ _("Server will check DNS forwarder(s)."))
+ self.Backend.textui.print_plain(
+ _("This may take some time, please wait ..."))
+
+
+@register(override=True)
+class dnsforwardzone_mod(MethodOverride):
+ def interactive_prompt_callback(self, kw):
+ # show informative message on client side
+ # server cannot send messages asynchronous
+ if kw.get('idnsforwarders', False):
+ self.Backend.textui.print_plain(
+ _("Server will check DNS forwarder(s)."))
+ self.Backend.textui.print_plain(
+ _("This may take some time, please wait ..."))