This file is indexed.

/usr/lib/python3/dist-packages/provisioningserver/rackdservices/ntp.py is in python3-maas-provisioningserver 2.4.0~beta2-6865-gec43e47e6-0ubuntu1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# Copyright 2016 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).

"""NTP service for the rack controller."""

__all__ = [
    "RackNetworkTimeProtocolService",
]

from datetime import timedelta

import attr
from provisioningserver.logger import LegacyLogger
from provisioningserver.ntp.config import configure_rack
from provisioningserver.rpc import exceptions
from provisioningserver.rpc.region import (
    GetControllerType,
    GetTimeConfiguration,
)
from provisioningserver.service_monitor import service_monitor
from provisioningserver.utils.twisted import callOut
from twisted.application.internet import TimerService
from twisted.internet.defer import (
    inlineCallbacks,
    maybeDeferred,
)
from twisted.internet.threads import deferToThread


log = LegacyLogger()


class RackNetworkTimeProtocolService(TimerService):

    interval = timedelta(seconds=30).total_seconds()

    _configuration = None
    _rpc_service = None

    def __init__(self, rpc_service, reactor):
        super().__init__(self.interval, self._tryUpdate)
        self._rpc_service = rpc_service
        self.clock = reactor

    def _tryUpdate(self):
        """Update the NTP server running on this host."""
        d = maybeDeferred(self._getConfiguration)
        d.addCallback(self._maybeApplyConfiguration)
        d.addErrback(self._updateFailed)
        return d

    def _updateFailed(self, failure):
        """Top-level error handler for the periodic task."""
        if failure.check(exceptions.NoSuchNode):
            pass  # This node is not yet recognised by the region.
        elif failure.check(exceptions.NoConnectionsAvailable):
            pass  # The region is not yet available.
        else:
            log.err(failure, "Failed to update NTP configuration.")

    @inlineCallbacks
    def _getConfiguration(self):
        """Return NTP server configuration.

        The configuration object returned is comparable with previous and
        subsequently obtained configuration objects, allowing this service to
        determine whether a change needs to be applied to the NTP server.
        """
        client = yield self._rpc_service.getClientNow()
        time_configuation = yield client(
            GetTimeConfiguration, system_id=client.localIdent)
        controller_type = yield client(
            GetControllerType, system_id=client.localIdent)
        return _Configuration(
            references=time_configuation["servers"],
            peers=time_configuation["peers"],
            is_region=controller_type["is_region"],
            is_rack=controller_type["is_rack"])

    def _maybeApplyConfiguration(self, configuration):
        """Reconfigure the NTP server if the configuration changes.

        Reconfigure and restart `chrony` if the current configuration differs
        from a previously applied configuration, otherwise do nothing.

        :param configuration: The configuration object obtained from
            `_getConfiguration`.
        """
        if configuration != self._configuration:
            d = maybeDeferred(self._applyConfiguration, configuration)
            d.addCallback(callOut, self._configurationApplied, configuration)
            return d

    def _applyConfiguration(self, configuration):
        """Configure the NTP server.

        :param configuration: The configuration object obtained from
            `_getConfiguration`.
        """
        if configuration.is_rack and not configuration.is_region:
            d = deferToThread(
                configure_rack, configuration.references, configuration.peers)
            d.addCallback(callOut, service_monitor.restartService, "ntp_rack")
            return d

    def _configurationApplied(self, configuration):
        """Record the currently applied NTP server configuration.

        :param configuration: The configuration object obtained from
            `_getConfiguration`.
        """
        self._configuration = configuration


@attr.s
class _Configuration:
    """Configuration for the rack's NTP servers."""

    # Addresses or hostnames of reference time servers.
    references = attr.ib(converter=frozenset)
    # Addresses of peer time servers.
    peers = attr.ib(converter=frozenset)

    # The type of this controller. It's fair to assume that is_rack is true,
    # but check nevertheless before applying this configuration.
    is_region = attr.ib(converter=bool)
    is_rack = attr.ib(converter=bool)