This patch is build on top of my DNS patches 218-220 ---- Add 2 new features to DNS record interactive help to increase its usability and also make its behavior more consistent with standard parameter interactive help:
1) Ask for missing DNS parts When a required part of a newly added DNS record was missing, we just returned a ValidationError. Now, the interactive help rather asks for all missing required parts of all DNS records that were being added by its parts. 2) Let user amend invalid part When an interactive help asked for a DNS record part value and user enters an invalid value, the entire interactive help exits with an error. This may upset a user if he already entered several correct DNS record part values. Now, the help rather tells user what's wrong and give him an opportunity to amend the value. https://fedorahosted.org/freeipa/ticket/2386 ------------- A demonstration of the new features: # ipa dnsrecord-add example.com foo --mx-exchanger=mx.example.com. MX Preference: 0 <<< we don't fail now Record name: foo MX record: 0 mx.example.com. # ipa dnsrecord-add example.com foo Please choose a type of DNS resource record to be added The most common types for this type of zone are: A, AAAA DNS resource record type: LOC LOC Degrees Latitude: 1 [LOC Minutes Latitude]: 1000 <<< we don't fail with invalid values! >>> LOC Minutes Latitude: can be at most 59 [LOC Minutes Latitude]: 50 [LOC Seconds Latitude]: LOC Direction Latitude: E >>> LOC Direction Latitude: must be one of (u'N', u'S') LOC Direction Latitude: N LOC Degrees Longtitude: 2 [LOC Minutes Longtitude]: [LOC Seconds Longtitude]: LOC Direction Longtitude: E LOC Altitude: 123 [LOC Size]: [LOC Horizontal Precision]: [LOC Vertical Precision]: Record name: foo LOC record: 1 50 N 2 E 123.00 MX record: 0 mx.example.com.
>From 2373c2e30b99ee3f344bc9ed2daba6b90373bc0c Mon Sep 17 00:00:00 2001 From: Martin Kosek <mko...@redhat.com> Date: Tue, 28 Feb 2012 15:04:05 +0100 Subject: [PATCH] Improve dnsrecord interactive help Add 2 new features to DNS record interactive help to increase its usability and also make its behavior more consistent with standard parameter interactive help: 1) Ask for missing DNS parts When a required part of a newly added DNS record was missing, we just returned a ValidationError. Now, the interactive help rather asks for all missing required parts of all DNS records that were being added by its parts. 2) Let user amend invalid part When an interactive help asked for a DNS record part value and user enters an invalid value, the entire interactive help exits with an error. This may upset a user if he already entered several correct DNS record part values. Now, the help rather tells user what's wrong and give him an opportunity to amend the value. https://fedorahosted.org/freeipa/ticket/2386 --- ipalib/cli.py | 6 ++- ipalib/plugins/dns.py | 123 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 109 insertions(+), 20 deletions(-) diff --git a/ipalib/cli.py b/ipalib/cli.py index e232c3ed24c3b35d4db5180e302b4e0e29c89f08..737ae001573af0f614783fe69add5711362da21e 100644 --- a/ipalib/cli.py +++ b/ipalib/cli.py @@ -529,6 +529,9 @@ class textui(backend.Backend): print raise PromptFailed(name=label) + def print_prompt_attribute_error(self, attribute, error): + self.print_plain('>>> %s: %s' % (attribute, error)) + def prompt(self, label, default=None, get_values=None, optional=False): """ Prompt user for input. @@ -1160,7 +1163,8 @@ class cli(backend.Executioner): error = None while True: if error is not None: - print '>>> %s: %s' % (unicode(param.label), unicode(error)) + self.Backend.textui.print_prompt_attribute_error(unicode(param.label), + unicode(error)) raw = self.Backend.textui.prompt(param.label, default, optional=param.alwaysask or not param.required) try: value = param(raw, **kw) diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py index 07d44dde76d513881eeb5762626af229549f8e52..8ffc2b4ba17c3ec883e044fd2ad815974984f162 100644 --- a/ipalib/plugins/dns.py +++ b/ipalib/plugins/dns.py @@ -672,6 +672,25 @@ class DNSRecord(Str): return tuple(self._convert_dnsrecord_extra(extra) for extra in self.extra) + def __get_part_param(self, backend, part, output_kw, default=None): + name = self.part_name_format % (self.rrtype.lower(), part.name) + label = self.part_label_format % (self.rrtype, unicode(part.label)) + optional = not part.required + + while True: + try: + raw = backend.textui.prompt(label, + optional=optional, + default=default) + if not raw.strip(): + raw = default + + output_kw[name] = part(raw) + break + except (errors.ValidationError, errors.ConversionError), e: + backend.textui.print_prompt_attribute_error( + unicode(label), unicode(e.error)) + def prompt_parts(self, backend, mod_dnsvalue=None): mod_parts = None if mod_dnsvalue is not None: @@ -682,21 +701,33 @@ class DNSRecord(Str): return user_options for part_id, part in enumerate(self.parts): - name = self.part_name_format % (self.rrtype.lower(), part.name) - label = self.part_label_format % (self.rrtype, unicode(part.label)) - optional = not part.required if mod_parts: default = mod_parts[part_id] else: default = None - raw = backend.textui.prompt(label, - optional=optional, - default=default) - if not raw.strip(): - raw = default + self.__get_part_param(backend, part, user_options, default) - user_options[name] = part(raw) + return user_options + + def prompt_missing_parts(self, backend, kw, prompt_optional=False): + user_options = {} + if self.parts is None: + return user_options + + for part in self.parts: + name = self.part_name_format % (self.rrtype.lower(), part.name) + label = self.part_label_format % (self.rrtype, unicode(part.label)) + + if name in kw: + continue + + optional = not part.required + if optional and not prompt_optional: + continue + + default = part.get_default(**kw) + self.__get_part_param(backend, part, user_options, default) return user_options @@ -1919,6 +1950,51 @@ class dnsrecord(LDAPObject): record.setdefault('dnsrecords', []).append(dnsentry) del record[attr] + def get_rrparam_from_part(self, part_name): + """ + Get an instance of DNSRecord parameter that has part_name as its part. + If such parameter is not found, None is returned + + :param part_name Part parameter name + """ + try: + param = self.params[part_name] + + if not any(flag in param.flags for flag in \ + ('dnsrecord_part', 'dnsrecord_extra')): + return None + + # All DNS record part or extra parameters contain a name of its + # parent RR parameter in its hint attribute + rrparam = self.params[param.hint] + except (KeyError, AttributeError): + return None + + return rrparam + + def iterate_rrparams_by_parts(self, kw, skip_extra=False): + """ + Iterates through all DNSRecord instances that has at least one of its + parts or extra options in given dictionary. It returns the DNSRecord + instance only for the first occurence of part/extra option. + + :param kw Dictionary with DNS record parts or extra options + :param skip_extra Skip DNS record extra options, yield only DNS records + with a real record part + """ + processed = [] + for opt in kw: + rrparam = self.get_rrparam_from_part(opt) + if rrparam is None: + continue + + if skip_extra and 'dnsrecord_extra' in self.params[opt].flags: + continue + + if rrparam.name not in processed: + processed.append(rrparam) + yield rrparam + api.register(dnsrecord) @@ -1943,11 +2019,22 @@ class dnsrecord_add(LDAPCreate): def interactive_prompt_callback(self, kw): try: self.obj.has_cli_options(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 self.obj.iterate_rrparams_by_parts(kw, skip_extra=True): + user_options = rrparam.prompt_missing_parts(self.Backend, kw, + prompt_optional=False) + new_kw.update(user_options) + kw.update(new_kw) + return except errors.OptionError: pass - else: - # some record type entered, skip this helper - return # check zone type if kw['idnsname'] == _dns_zone_record: @@ -1999,13 +2086,11 @@ class dnsrecord_add(LDAPCreate): except KeyError: continue + rrparam = self.obj.get_rrparam_from_part(option) + if rrparam is None: + continue + if 'dnsrecord_part' in param.flags: - # check if any record part was added - try: - rrparam = self.params[param.hint] - except (KeyError, AttributeError): - continue - if rrparam.name in entry_attrs: # this record was already entered continue @@ -2020,7 +2105,7 @@ class dnsrecord_add(LDAPCreate): if isinstance(param, Flag) and not options[option]: continue # extra option is passed, run per-type pre_callback for given RR type - precallback_attrs.append(param.hint) + precallback_attrs.append(rrparam.name) # run precallback also for all new RR type attributes in entry_attrs for attr in entry_attrs: -- 1.7.7.6
_______________________________________________ Freeipa-devel mailing list Freeipa-devel@redhat.com https://www.redhat.com/mailman/listinfo/freeipa-devel