[Freeipa-devel] [PATCH 0095-0098] NTP: use augeas, configure chronyd, do not overwrite config

Martin Basti mbasti at redhat.com
Mon Mar 14 13:01:53 UTC 2016



On 14.03.2016 13:46, Martin Babinsky wrote:
> On 03/11/2016 09:16 AM, David Kupka wrote:
>> Current version (0.5.0) of python-augeas is missing copy() method. Use
>> dkupka/python-augeas copr repo before new version it's build and
>> available in the official repos.
>>
>>
>>
> Hi David,
>
> TLDR: NACK :D.
>
> Here are high-level remarks to discuss:
>
> Maybe it would be a good idea to move ipaaugeas/changeconf and ntp to 
> ipaplatform since it is dealing with (sorta) platform specific 
> behavior (ntp vs. chrony vs. whatever we will have for timesync in the 
> future). CC'ing Jan for thoughts.
>
> Also regarding patches 0096-0097, we could have platform specific 
> TimeDateService object that could wrap NTP/chrony management. for 
> example, the task namespace functions in Pathc 0096 could be 
> reimplemented as a methods of the service (RedhatTimeDateService, 
> FedoraTimeDateService etc.) and then called in a platform-agnostic 
> manner.
>
> Here are some comments regarding code:
>
> Patch 0095:
>
> 1.)
> +    IPA_CUSTOM_AUGEAS_LENSES_DIR = '/usr/share/augeas/lenses/ipa/'
>
> Do not forget to add this directory to %install and %files spection of 
> the spec file so that it is correctly added to RPM build.
>
> 2.)
>
> please separate import of system-wide and IPA-specific modules by 
> blank line
>
> +import collections
> +from augeas import Augeas
> +from ipaplatform.paths import paths
> +from ipapython.ipa_log_manager import root_logger
>
> 3.) the call to parent's __new__ should have signature 'super(aug_obj, 
> cls).__new__(*args, **kwargs)'
>
> +            cls._instance = super(aug_obj, cls).__new__(cls, *args, 
> **kwargs)
>
> 4.)
>
> +            raise RuntimeError('Augeas lenses was loaded. Could not 
> add more'
> +                               'lenses.')
>
> Should be 'Augeas lenses _were_ loaded'
>
> 5.)
>
> +        if lens in self.lenses:
> +            raise RuntimeError('Lens %s already added.' % lens)
> +        self.lenses.append(lens)
> +        load_path = '/augeas/load/{0}'.format(lens
>
>
> Shouldn't the following code use os.path,join to construct the load_path?
>
> 6.) I would prefer the following indentation style in the add_lens() 
> method
>
> @@ -65,9 +65,9 @@ class aug_obj(object):
>          for conf_file in conf_files:
>              self._aug.set(os.path.join(load_path, 'incl[0]'), conf_file)
>              self.tree['old'] = self.tree.get(conf_file, None)
> -            self.tree[conf_file] = aug_node(self._aug,
> - os.path.join('/files',
> - conf_file[1:]))
> +            self.tree[conf_file] = aug_node(
> +                self._aug, os.path.join('/files', conf_file[1:])
> +            )
>
> 7.) I would also prefer if the hardcoded paths like '/augeas/load', 
> 'files', and '//error' would be made into either module variables or 
> class members.
>
> 8.)
>
> +    def load(self):
> +        if self._loaded:
> +            raise RuntimeError('Augeas lenses was loaded. Could not 
> add more'
> +                               'lenses.')
>
> Fix the excpetion text in the same way as in 4.)
>
> 9.)
>
> +        errors = self._aug.match(os.path.join('//error'))
>
> is the os.path.join necessary here?
>
> 10.) I guess you can rewrite the error message in load() method using 
> list comprehension:
>
> @@ -76,9 +76,9 @@ class aug_obj(object):
>          self._aug.load()
>          errors = self._aug.match(os.path.join('//error'))
>          if errors:
> -            err_msg = ""
> -            for error in errors:
> -                err_msg += ("{}: {}".format(error, 
> self._aug.get(error)))
> +            err_msg = '\n'.join(
> +                ["{}: {}".format(e, self._aug.get(e)) for e in errors]
> +            )
>              raise RuntimeError(err_msg)
>          self._loaded = True
>
> 11.)
>
> +class aug_node(collections.MutableMapping):
> +    """ Single augeas node.
> +    Can be handled as python dict().
> +    """
> +    def __init__(self, aug, path):
> +        self._aug = aug
> +        if path and os.path.isabs(path):
> +            self._path = path
> +        else:
> +            self._tmp = _tmp_path(aug, path)
> +            self._path = self._tmp.path
>
> Isn't it better to change signature of __init__ to:
>
>     def __init__(self, aug, path=None):
>
> and then test whether path is None?
>
> 12.)
>
>     def __setitem__(self, key, node):
> +        target = os.path.join(self._path, key)
> +        end = '{0}[0]'.format(os.path.join(self._path, key))
> +        if self._aug.match(target):
> +            self._aug.remove(target)
> +        target_list = aug_list(self._aug, target)
> +        for src_node in aug_list(node._aug, node._path):
> +            target_list.append(src_node)
>
> The 'end' variable is declared but not used.
>
> 13.)
>
> +
> +    def __len__(self):
> +        return len(self._aug.match('{0}/*'.format(self._path)))
> +
> +    def __iter__(self):
> +        for key in self._aug.match('{0}/*'.format(self._path)):
> +            yield self._aug.label(key)
> +        raise StopIteration()
> +
>
> Shouldn't we construct paths using os.path.join for the sake of 
> consistency?
>
> 14.)
>
> +    def __bool__(self):
> +        return (bool(len(self)) or bool(self.value))
>
> The parentheses around the boolean expression are not needed
>
> 15.)
>
> +class aug_list(collections.MutableSequence):
> +    """Augeas NODESET.
> +    Can be handled as a list().
> +    """
> +    def __init__(self, aug, path):
> +        self._aug = aug
> +        if path and os.path.isabs(path):
> +            self._path = path
> +        else:
> +            self._tmp = _tmp_path(aug, path)
> +            self._path = self._tmp.path
>
> I would use 'path=None' int he signature and then test whether 'path 
> is not None'.
>
> 16.)
>
> +        if not self._aug.match(target):
> +            raise IndexError()
>
> It would be nice if you could put some basic error message into the 
> raised exceptions, like "node index out of range" or something like that
>
> 17.)
>
> +        elif isinstance(index, slice):
> +            label = self._path.split('/')[-1]
>
> you could use os.path.basename() to get the leaf node.
>
>
> 18.)
>
> +            replace = range_target[:len(node)]
> +            delete = create = []
>
> Be careful here as you create two references to the same empty list 
> object, which is probably not what you wanted.
>
> 19.)
> +                try:
> +                    create_start = range_target[-1]+1
> +                except IndexError:
> +                    create_start = self._idx_pos(index.start)
> +                create_stop = create_start+len(node)-len(replace)
> +                create = list(range(create_start, create_stop))
>
> Please respect PEP8 and put spaces around arithmetic operators in 
> assignments.
>
> Also it would be nice to have at least a minimal test suite for this 
> module, but that may be a separate ticket.
>
> patch 0096:
>
> 1.) please fix the commit message
> 2.) please use new-style license header in ipapython/ntp.py
> 3.)
>
> +        return ("Conflicting Time&Date synchroniztion service '%s' is "
> +                "currently enabled and/or running on the system."
> +                % self.conflicting_service)
>
> Please fix the typo in the error message.
>
> 4.)
> +        service = services.service(self.service_name)
> +        if sstore:
> +            if sstore.get_state('ntp', 'enabled') is None:
> +                sstore.backup_state('ntp', 'enabled', 
> service.is_enabled())
> +
> +        if fstore:
> +            if not fstore.has_file(self.conf_file):
> +                fstore.backup_file(self.conf_file)
>
> the conditions in the 'if' statement can be merged into single AND 
> expression
>
> 5.)
> +        self._store_service_state(sstore, fstore)
> +        if sstore:
> +            sstore.backup_state('ntp', "enabled", service.is_enabled())
> +
> +        if fstore:
> +            fstore.backup_file(self.conf_file)
>
> I think these checks are redundant here.
>
> 6.)
> +        # In such case it is OK to fail
> +        try:
> +            restored = fstore.restore_file(self.conf_file)
> +        except Exception:
> +            pass
>
> Instead of 'pass' it would be better to set restored to False so that 
> you don't hit NameError later.
>
> 7.)
> +
> +    def configure_client(self, ntp_servers=[], sstore=None, 
> fstore=None):
> +        self.server_options['burst'] = None
> +        self.server_options['iburst'] = None
>
> I would rather set these instance variables in __init__() than here.
>
> 8.)
>
> +    def configure_client(self, ntp_servers=[], sstore=None, 
> fstore=None):
> +        self.conf_file = paths.CHRONY_CONF
> self.conf_file is already defined in constructor.
>
> 9.)
>
> +        self.server_options['iburst'] = None
> this should be moved to __init__()
>
> 10.)
> +        with ipaaugeas.aug_obj() as aug:
> +            try:
> +                aug.add_lens(self.lens, [self.conf_file])
> +                aug.load()
> +            except RuntimeError as e:
> +                raise NTPConfigurationError(e)
>
> This code is repeated quite a few times, maybe it would be a good idea 
> to factor it out to a method of NTPService object.
>
>
> Patch 0097:
>
> 1.) please fix a typo here:
>
> +        description="Disble any other Time synchronization services."
>
> 2.)
>
> +    installutils, kra, krbinstance, memcacheinstance, ntpinstance,
> you have 2 spaces before 'ntpinstance'
>

I'm adding my nitpicks too :)

1)
+#!/usr/bin/python

This should not be in modules, only in executable files

2)
Missing license in ipaaugeas.py

Martin^2




More information about the Freeipa-devel mailing list