/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)
|