|
Packit |
bc9a3a |
# Copyright (C) 2016 Canonical Ltd.
|
|
Packit |
bc9a3a |
#
|
|
Packit |
bc9a3a |
# Author: Ryan Harper <ryan.harper@canonical.com>
|
|
Packit |
bc9a3a |
#
|
|
Packit |
bc9a3a |
# This file is part of cloud-init. See LICENSE file for license information.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
"""NTP: enable and configure ntp"""
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
from cloudinit.config.schema import (
|
|
Packit |
bc9a3a |
get_schema_doc, validate_cloudconfig_schema)
|
|
Packit |
bc9a3a |
from cloudinit import log as logging
|
|
Packit |
bc9a3a |
from cloudinit.settings import PER_INSTANCE
|
|
Packit |
bc9a3a |
from cloudinit import temp_utils
|
|
Packit |
bc9a3a |
from cloudinit import templater
|
|
Packit |
bc9a3a |
from cloudinit import type_utils
|
|
Packit |
bc9a3a |
from cloudinit import util
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
import copy
|
|
Packit |
bc9a3a |
import os
|
|
Packit |
bc9a3a |
import six
|
|
Packit |
bc9a3a |
from textwrap import dedent
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
LOG = logging.getLogger(__name__)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
frequency = PER_INSTANCE
|
|
Packit |
bc9a3a |
NTP_CONF = '/etc/ntp.conf'
|
|
Packit |
bc9a3a |
NR_POOL_SERVERS = 4
|
|
Packit |
bc9a3a |
distros = ['centos', 'debian', 'fedora', 'opensuse', 'rhel', 'sles', 'ubuntu']
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
NTP_CLIENT_CONFIG = {
|
|
Packit |
bc9a3a |
'chrony': {
|
|
Packit |
bc9a3a |
'check_exe': 'chronyd',
|
|
Packit |
bc9a3a |
'confpath': '/etc/chrony.conf',
|
|
Packit |
bc9a3a |
'packages': ['chrony'],
|
|
Packit |
bc9a3a |
'service_name': 'chrony',
|
|
Packit |
bc9a3a |
'template_name': 'chrony.conf.{distro}',
|
|
Packit |
bc9a3a |
'template': None,
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'ntp': {
|
|
Packit |
bc9a3a |
'check_exe': 'ntpd',
|
|
Packit |
bc9a3a |
'confpath': NTP_CONF,
|
|
Packit |
bc9a3a |
'packages': ['ntp'],
|
|
Packit |
bc9a3a |
'service_name': 'ntp',
|
|
Packit |
bc9a3a |
'template_name': 'ntp.conf.{distro}',
|
|
Packit |
bc9a3a |
'template': None,
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'ntpdate': {
|
|
Packit |
bc9a3a |
'check_exe': 'ntpdate',
|
|
Packit |
bc9a3a |
'confpath': NTP_CONF,
|
|
Packit |
bc9a3a |
'packages': ['ntpdate'],
|
|
Packit |
bc9a3a |
'service_name': 'ntpdate',
|
|
Packit |
bc9a3a |
'template_name': 'ntp.conf.{distro}',
|
|
Packit |
bc9a3a |
'template': None,
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'systemd-timesyncd': {
|
|
Packit |
bc9a3a |
'check_exe': '/lib/systemd/systemd-timesyncd',
|
|
Packit |
bc9a3a |
'confpath': '/etc/systemd/timesyncd.conf.d/cloud-init.conf',
|
|
Packit |
bc9a3a |
'packages': [],
|
|
Packit |
bc9a3a |
'service_name': 'systemd-timesyncd',
|
|
Packit |
bc9a3a |
'template_name': 'timesyncd.conf',
|
|
Packit |
bc9a3a |
'template': None,
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
}
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# This is Distro-specific configuration overrides of the base config
|
|
Packit |
bc9a3a |
DISTRO_CLIENT_CONFIG = {
|
|
Packit |
bc9a3a |
'debian': {
|
|
Packit |
bc9a3a |
'chrony': {
|
|
Packit |
bc9a3a |
'confpath': '/etc/chrony/chrony.conf',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'opensuse': {
|
|
Packit |
bc9a3a |
'chrony': {
|
|
Packit |
bc9a3a |
'service_name': 'chronyd',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'ntp': {
|
|
Packit |
bc9a3a |
'confpath': '/etc/ntp.conf',
|
|
Packit |
bc9a3a |
'service_name': 'ntpd',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'systemd-timesyncd': {
|
|
Packit |
bc9a3a |
'check_exe': '/usr/lib/systemd/systemd-timesyncd',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'sles': {
|
|
Packit |
bc9a3a |
'chrony': {
|
|
Packit |
bc9a3a |
'service_name': 'chronyd',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'ntp': {
|
|
Packit |
bc9a3a |
'confpath': '/etc/ntp.conf',
|
|
Packit |
bc9a3a |
'service_name': 'ntpd',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'systemd-timesyncd': {
|
|
Packit |
bc9a3a |
'check_exe': '/usr/lib/systemd/systemd-timesyncd',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'ubuntu': {
|
|
Packit |
bc9a3a |
'chrony': {
|
|
Packit |
bc9a3a |
'confpath': '/etc/chrony/chrony.conf',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
}
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# The schema definition for each cloud-config module is a strict contract for
|
|
Packit |
bc9a3a |
# describing supported configuration parameters for each cloud-config section.
|
|
Packit |
bc9a3a |
# It allows cloud-config to validate and alert users to invalid or ignored
|
|
Packit |
bc9a3a |
# configuration options before actually attempting to deploy with said
|
|
Packit |
bc9a3a |
# configuration.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
schema = {
|
|
Packit |
bc9a3a |
'id': 'cc_ntp',
|
|
Packit |
bc9a3a |
'name': 'NTP',
|
|
Packit |
bc9a3a |
'title': 'enable and configure ntp',
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
Handle ntp configuration. If ntp is not installed on the system and
|
|
Packit |
bc9a3a |
ntp configuration is specified, ntp will be installed. If there is a
|
|
Packit |
bc9a3a |
default ntp config file in the image or one is present in the
|
|
Packit |
bc9a3a |
distro's ntp package, it will be copied to ``/etc/ntp.conf.dist``
|
|
Packit |
bc9a3a |
before any changes are made. A list of ntp pools and ntp servers can
|
|
Packit |
bc9a3a |
be provided under the ``ntp`` config key. If no ntp ``servers`` or
|
|
Packit |
bc9a3a |
``pools`` are provided, 4 pools will be used in the format
|
|
Packit |
bc9a3a |
``{0-3}.{distro}.pool.ntp.org``."""),
|
|
Packit |
bc9a3a |
'distros': distros,
|
|
Packit |
bc9a3a |
'examples': [
|
|
Packit |
bc9a3a |
dedent("""\
|
|
Packit |
bc9a3a |
# Override ntp with chrony configuration on Ubuntu
|
|
Packit |
bc9a3a |
ntp:
|
|
Packit |
bc9a3a |
enabled: true
|
|
Packit |
bc9a3a |
ntp_client: chrony # Uses cloud-init default chrony configuration
|
|
Packit |
bc9a3a |
"""),
|
|
Packit |
bc9a3a |
dedent("""\
|
|
Packit |
bc9a3a |
# Provide a custom ntp client configuration
|
|
Packit |
bc9a3a |
ntp:
|
|
Packit |
bc9a3a |
enabled: true
|
|
Packit |
bc9a3a |
ntp_client: myntpclient
|
|
Packit |
bc9a3a |
config:
|
|
Packit |
bc9a3a |
confpath: /etc/myntpclient/myntpclient.conf
|
|
Packit |
bc9a3a |
check_exe: myntpclientd
|
|
Packit |
bc9a3a |
packages:
|
|
Packit |
bc9a3a |
- myntpclient
|
|
Packit |
bc9a3a |
service_name: myntpclient
|
|
Packit |
bc9a3a |
template: |
|
|
Packit |
bc9a3a |
## template:jinja
|
|
Packit |
bc9a3a |
# My NTP Client config
|
|
Packit |
bc9a3a |
{% if pools -%}# pools{% endif %}
|
|
Packit |
bc9a3a |
{% for pool in pools -%}
|
|
Packit |
bc9a3a |
pool {{pool}} iburst
|
|
Packit |
bc9a3a |
{% endfor %}
|
|
Packit |
bc9a3a |
{%- if servers %}# servers
|
|
Packit |
bc9a3a |
{% endif %}
|
|
Packit |
bc9a3a |
{% for server in servers -%}
|
|
Packit |
bc9a3a |
server {{server}} iburst
|
|
Packit |
bc9a3a |
{% endfor %}
|
|
Packit |
bc9a3a |
pools: [0.int.pool.ntp.org, 1.int.pool.ntp.org, ntp.myorg.org]
|
|
Packit |
bc9a3a |
servers:
|
|
Packit |
bc9a3a |
- ntp.server.local
|
|
Packit |
bc9a3a |
- ntp.ubuntu.com
|
|
Packit |
bc9a3a |
- 192.168.23.2""")],
|
|
Packit |
bc9a3a |
'frequency': PER_INSTANCE,
|
|
Packit |
bc9a3a |
'type': 'object',
|
|
Packit |
bc9a3a |
'properties': {
|
|
Packit |
bc9a3a |
'ntp': {
|
|
Packit |
bc9a3a |
'type': ['object', 'null'],
|
|
Packit |
bc9a3a |
'properties': {
|
|
Packit |
bc9a3a |
'pools': {
|
|
Packit |
bc9a3a |
'type': 'array',
|
|
Packit |
bc9a3a |
'items': {
|
|
Packit |
bc9a3a |
'type': 'string',
|
|
Packit |
bc9a3a |
'format': 'hostname'
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'uniqueItems': True,
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
List of ntp pools. If both pools and servers are
|
|
Packit |
bc9a3a |
empty, 4 default pool servers will be provided of
|
|
Packit |
bc9a3a |
the format ``{0-3}.{distro}.pool.ntp.org``.""")
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'servers': {
|
|
Packit |
bc9a3a |
'type': 'array',
|
|
Packit |
bc9a3a |
'items': {
|
|
Packit |
bc9a3a |
'type': 'string',
|
|
Packit |
bc9a3a |
'format': 'hostname'
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'uniqueItems': True,
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
List of ntp servers. If both pools and servers are
|
|
Packit |
bc9a3a |
empty, 4 default pool servers will be provided with
|
|
Packit |
bc9a3a |
the format ``{0-3}.{distro}.pool.ntp.org``.""")
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'ntp_client': {
|
|
Packit |
bc9a3a |
'type': 'string',
|
|
Packit |
bc9a3a |
'default': 'auto',
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
Name of an NTP client to use to configure system NTP.
|
|
Packit |
bc9a3a |
When unprovided or 'auto' the default client preferred
|
|
Packit |
bc9a3a |
by the distribution will be used. The following
|
|
Packit |
bc9a3a |
built-in client names can be used to override existing
|
|
Packit |
bc9a3a |
configuration defaults: chrony, ntp, ntpdate,
|
|
Packit |
bc9a3a |
systemd-timesyncd."""),
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'enabled': {
|
|
Packit |
bc9a3a |
'type': 'boolean',
|
|
Packit |
bc9a3a |
'default': True,
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
Attempt to enable ntp clients if set to True. If set
|
|
Packit |
bc9a3a |
to False, ntp client will not be configured or
|
|
Packit |
bc9a3a |
installed"""),
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'config': {
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
Configuration settings or overrides for the
|
|
Packit |
bc9a3a |
``ntp_client`` specified."""),
|
|
Packit |
bc9a3a |
'type': ['object'],
|
|
Packit |
bc9a3a |
'properties': {
|
|
Packit |
bc9a3a |
'confpath': {
|
|
Packit |
bc9a3a |
'type': 'string',
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
The path to where the ``ntp_client``
|
|
Packit |
bc9a3a |
configuration is written."""),
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'check_exe': {
|
|
Packit |
bc9a3a |
'type': 'string',
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
The executable name for the ``ntp_client``.
|
|
Packit |
bc9a3a |
For example, ntp service ``check_exe`` is
|
|
Packit |
bc9a3a |
'ntpd' because it runs the ntpd binary."""),
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'packages': {
|
|
Packit |
bc9a3a |
'type': 'array',
|
|
Packit |
bc9a3a |
'items': {
|
|
Packit |
bc9a3a |
'type': 'string',
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'uniqueItems': True,
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
List of packages needed to be installed for the
|
|
Packit |
bc9a3a |
selected ``ntp_client``."""),
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'service_name': {
|
|
Packit |
bc9a3a |
'type': 'string',
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
The systemd or sysvinit service name used to
|
|
Packit |
bc9a3a |
start and stop the ``ntp_client``
|
|
Packit |
bc9a3a |
service."""),
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'template': {
|
|
Packit |
bc9a3a |
'type': 'string',
|
|
Packit |
bc9a3a |
'description': dedent("""\
|
|
Packit |
bc9a3a |
Inline template allowing users to define their
|
|
Packit |
bc9a3a |
own ``ntp_client`` configuration template.
|
|
Packit |
bc9a3a |
The value must start with '## template:jinja'
|
|
Packit |
bc9a3a |
to enable use of templating support.
|
|
Packit |
bc9a3a |
"""),
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
# Don't use REQUIRED_NTP_CONFIG_KEYS to allow for override
|
|
Packit |
bc9a3a |
# of builtin client values.
|
|
Packit |
bc9a3a |
'required': [],
|
|
Packit |
bc9a3a |
'minProperties': 1, # If we have config, define something
|
|
Packit |
bc9a3a |
'additionalProperties': False
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
},
|
|
Packit |
bc9a3a |
'required': [],
|
|
Packit |
bc9a3a |
'additionalProperties': False
|
|
Packit |
bc9a3a |
}
|
|
Packit |
bc9a3a |
}
|
|
Packit |
bc9a3a |
}
|
|
Packit |
bc9a3a |
REQUIRED_NTP_CONFIG_KEYS = frozenset([
|
|
Packit |
bc9a3a |
'check_exe', 'confpath', 'packages', 'service_name'])
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
__doc__ = get_schema_doc(schema) # Supplement python help()
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def distro_ntp_client_configs(distro):
|
|
Packit |
bc9a3a |
"""Construct a distro-specific ntp client config dictionary by merging
|
|
Packit |
bc9a3a |
distro specific changes into base config.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@param distro: String providing the distro class name.
|
|
Packit |
bc9a3a |
@returns: Dict of distro configurations for ntp clients.
|
|
Packit |
bc9a3a |
"""
|
|
Packit |
bc9a3a |
dcfg = DISTRO_CLIENT_CONFIG
|
|
Packit |
bc9a3a |
cfg = copy.copy(NTP_CLIENT_CONFIG)
|
|
Packit |
bc9a3a |
if distro in dcfg:
|
|
Packit |
bc9a3a |
cfg = util.mergemanydict([cfg, dcfg[distro]], reverse=True)
|
|
Packit |
bc9a3a |
return cfg
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def select_ntp_client(ntp_client, distro):
|
|
Packit |
bc9a3a |
"""Determine which ntp client is to be used, consulting the distro
|
|
Packit |
bc9a3a |
for its preference.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@param ntp_client: String name of the ntp client to use.
|
|
Packit |
bc9a3a |
@param distro: Distro class instance.
|
|
Packit |
bc9a3a |
@returns: Dict of the selected ntp client or {} if none selected.
|
|
Packit |
bc9a3a |
"""
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# construct distro-specific ntp_client_config dict
|
|
Packit |
bc9a3a |
distro_cfg = distro_ntp_client_configs(distro.name)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# user specified client, return its config
|
|
Packit |
bc9a3a |
if ntp_client and ntp_client != 'auto':
|
|
Packit |
bc9a3a |
LOG.debug('Selected NTP client "%s" via user-data configuration',
|
|
Packit |
bc9a3a |
ntp_client)
|
|
Packit |
bc9a3a |
return distro_cfg.get(ntp_client, {})
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# default to auto if unset in distro
|
|
Packit |
bc9a3a |
distro_ntp_client = distro.get_option('ntp_client', 'auto')
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
clientcfg = {}
|
|
Packit |
bc9a3a |
if distro_ntp_client == "auto":
|
|
Packit |
bc9a3a |
for client in distro.preferred_ntp_clients:
|
|
Packit |
bc9a3a |
cfg = distro_cfg.get(client)
|
|
Packit |
bc9a3a |
if util.which(cfg.get('check_exe')):
|
|
Packit |
bc9a3a |
LOG.debug('Selected NTP client "%s", already installed',
|
|
Packit |
bc9a3a |
client)
|
|
Packit |
bc9a3a |
clientcfg = cfg
|
|
Packit |
bc9a3a |
break
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
if not clientcfg:
|
|
Packit |
bc9a3a |
client = distro.preferred_ntp_clients[0]
|
|
Packit |
bc9a3a |
LOG.debug(
|
|
Packit |
bc9a3a |
'Selected distro preferred NTP client "%s", not yet installed',
|
|
Packit |
bc9a3a |
client)
|
|
Packit |
bc9a3a |
clientcfg = distro_cfg.get(client)
|
|
Packit |
bc9a3a |
else:
|
|
Packit |
bc9a3a |
LOG.debug('Selected NTP client "%s" via distro system config',
|
|
Packit |
bc9a3a |
distro_ntp_client)
|
|
Packit |
bc9a3a |
clientcfg = distro_cfg.get(distro_ntp_client, {})
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
return clientcfg
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def install_ntp_client(install_func, packages=None, check_exe="ntpd"):
|
|
Packit |
bc9a3a |
"""Install ntp client package if not already installed.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@param install_func: function. This parameter is invoked with the contents
|
|
Packit |
bc9a3a |
of the packages parameter.
|
|
Packit |
bc9a3a |
@param packages: list. This parameter defaults to ['ntp'].
|
|
Packit |
bc9a3a |
@param check_exe: string. The name of a binary that indicates the package
|
|
Packit |
bc9a3a |
the specified package is already installed.
|
|
Packit |
bc9a3a |
"""
|
|
Packit |
bc9a3a |
if util.which(check_exe):
|
|
Packit |
bc9a3a |
return
|
|
Packit |
bc9a3a |
if packages is None:
|
|
Packit |
bc9a3a |
packages = ['ntp']
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
install_func(packages)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def rename_ntp_conf(confpath=None):
|
|
Packit |
bc9a3a |
"""Rename any existing ntp client config file
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@param confpath: string. Specify a path to an existing ntp client
|
|
Packit |
bc9a3a |
configuration file.
|
|
Packit |
bc9a3a |
"""
|
|
Packit |
bc9a3a |
if os.path.exists(confpath):
|
|
Packit |
bc9a3a |
util.rename(confpath, confpath + ".dist")
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def generate_server_names(distro):
|
|
Packit |
bc9a3a |
"""Generate a list of server names to populate an ntp client configuration
|
|
Packit |
bc9a3a |
file.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@param distro: string. Specify the distro name
|
|
Packit |
bc9a3a |
@returns: list: A list of strings representing ntp servers for this distro.
|
|
Packit |
bc9a3a |
"""
|
|
Packit |
bc9a3a |
names = []
|
|
Packit |
bc9a3a |
pool_distro = distro
|
|
Packit |
bc9a3a |
# For legal reasons x.pool.sles.ntp.org does not exist,
|
|
Packit |
bc9a3a |
# use the opensuse pool
|
|
Packit |
bc9a3a |
if distro == 'sles':
|
|
Packit |
bc9a3a |
pool_distro = 'opensuse'
|
|
Packit |
bc9a3a |
for x in range(0, NR_POOL_SERVERS):
|
|
Packit |
bc9a3a |
name = "%d.%s.pool.ntp.org" % (x, pool_distro)
|
|
Packit |
bc9a3a |
names.append(name)
|
|
Packit |
bc9a3a |
return names
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def write_ntp_config_template(distro_name, servers=None, pools=None,
|
|
Packit |
bc9a3a |
path=None, template_fn=None, template=None):
|
|
Packit |
bc9a3a |
"""Render a ntp client configuration for the specified client.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@param distro_name: string. The distro class name.
|
|
Packit |
bc9a3a |
@param servers: A list of strings specifying ntp servers. Defaults to empty
|
|
Packit |
bc9a3a |
list.
|
|
Packit |
bc9a3a |
@param pools: A list of strings specifying ntp pools. Defaults to empty
|
|
Packit |
bc9a3a |
list.
|
|
Packit |
bc9a3a |
@param path: A string to specify where to write the rendered template.
|
|
Packit |
bc9a3a |
@param template_fn: A string to specify the template source file.
|
|
Packit |
bc9a3a |
@param template: A string specifying the contents of the template. This
|
|
Packit |
bc9a3a |
content will be written to a temporary file before being used to render
|
|
Packit |
bc9a3a |
the configuration file.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@raises: ValueError when path is None.
|
|
Packit |
bc9a3a |
@raises: ValueError when template_fn is None and template is None.
|
|
Packit |
bc9a3a |
"""
|
|
Packit |
bc9a3a |
if not servers:
|
|
Packit |
bc9a3a |
servers = []
|
|
Packit |
bc9a3a |
if not pools:
|
|
Packit |
bc9a3a |
pools = []
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
if len(servers) == 0 and len(pools) == 0:
|
|
Packit |
bc9a3a |
pools = generate_server_names(distro_name)
|
|
Packit |
bc9a3a |
LOG.debug(
|
|
Packit |
bc9a3a |
'Adding distro default ntp pool servers: %s', ','.join(pools))
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
if not path:
|
|
Packit |
bc9a3a |
raise ValueError('Invalid value for path parameter')
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
if not template_fn and not template:
|
|
Packit |
bc9a3a |
raise ValueError('Not template_fn or template provided')
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
params = {'servers': servers, 'pools': pools}
|
|
Packit |
bc9a3a |
if template:
|
|
Packit |
bc9a3a |
tfile = temp_utils.mkstemp(prefix='template_name-', suffix=".tmpl")
|
|
Packit |
bc9a3a |
template_fn = tfile[1] # filepath is second item in tuple
|
|
Packit |
bc9a3a |
util.write_file(template_fn, content=template)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
templater.render_to_file(template_fn, path, params)
|
|
Packit |
bc9a3a |
# clean up temporary template
|
|
Packit |
bc9a3a |
if template:
|
|
Packit |
bc9a3a |
util.del_file(template_fn)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def reload_ntp(service, systemd=False):
|
|
Packit |
bc9a3a |
"""Restart or reload an ntp system service.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@param service: A string specifying the name of the service to be affected.
|
|
Packit |
bc9a3a |
@param systemd: A boolean indicating if the distro uses systemd, defaults
|
|
Packit |
bc9a3a |
to False.
|
|
Packit |
bc9a3a |
@returns: A tuple of stdout, stderr results from executing the action.
|
|
Packit |
bc9a3a |
"""
|
|
Packit |
bc9a3a |
if systemd:
|
|
Packit |
bc9a3a |
cmd = ['systemctl', 'reload-or-restart', service]
|
|
Packit |
bc9a3a |
else:
|
|
Packit |
bc9a3a |
cmd = ['service', service, 'restart']
|
|
Packit |
bc9a3a |
util.subp(cmd, capture=True)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def supplemental_schema_validation(ntp_config):
|
|
Packit |
bc9a3a |
"""Validate user-provided ntp:config option values.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
This function supplements flexible jsonschema validation with specific
|
|
Packit |
bc9a3a |
value checks to aid in triage of invalid user-provided configuration.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@param ntp_config: Dictionary of configuration value under 'ntp'.
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
@raises: ValueError describing invalid values provided.
|
|
Packit |
bc9a3a |
"""
|
|
Packit |
bc9a3a |
errors = []
|
|
Packit |
bc9a3a |
missing = REQUIRED_NTP_CONFIG_KEYS.difference(set(ntp_config.keys()))
|
|
Packit |
bc9a3a |
if missing:
|
|
Packit |
bc9a3a |
keys = ', '.join(sorted(missing))
|
|
Packit |
bc9a3a |
errors.append(
|
|
Packit |
bc9a3a |
'Missing required ntp:config keys: {keys}'.format(keys=keys))
|
|
Packit |
bc9a3a |
elif not any([ntp_config.get('template'),
|
|
Packit |
bc9a3a |
ntp_config.get('template_name')]):
|
|
Packit |
bc9a3a |
errors.append(
|
|
Packit |
bc9a3a |
'Either ntp:config:template or ntp:config:template_name values'
|
|
Packit |
bc9a3a |
' are required')
|
|
Packit |
bc9a3a |
for key, value in sorted(ntp_config.items()):
|
|
Packit |
bc9a3a |
keypath = 'ntp:config:' + key
|
|
Packit |
bc9a3a |
if key == 'confpath':
|
|
Packit |
bc9a3a |
if not all([value, isinstance(value, six.string_types)]):
|
|
Packit |
bc9a3a |
errors.append(
|
|
Packit |
bc9a3a |
'Expected a config file path {keypath}.'
|
|
Packit |
bc9a3a |
' Found ({value})'.format(keypath=keypath, value=value))
|
|
Packit |
bc9a3a |
elif key == 'packages':
|
|
Packit |
bc9a3a |
if not isinstance(value, list):
|
|
Packit |
bc9a3a |
errors.append(
|
|
Packit |
bc9a3a |
'Expected a list of required package names for {keypath}.'
|
|
Packit |
bc9a3a |
' Found ({value})'.format(keypath=keypath, value=value))
|
|
Packit |
bc9a3a |
elif key in ('template', 'template_name'):
|
|
Packit |
bc9a3a |
if value is None: # Either template or template_name can be none
|
|
Packit |
bc9a3a |
continue
|
|
Packit |
bc9a3a |
if not isinstance(value, six.string_types):
|
|
Packit |
bc9a3a |
errors.append(
|
|
Packit |
bc9a3a |
'Expected a string type for {keypath}.'
|
|
Packit |
bc9a3a |
' Found ({value})'.format(keypath=keypath, value=value))
|
|
Packit |
bc9a3a |
elif not isinstance(value, six.string_types):
|
|
Packit |
bc9a3a |
errors.append(
|
|
Packit |
bc9a3a |
'Expected a string type for {keypath}.'
|
|
Packit |
bc9a3a |
' Found ({value})'.format(keypath=keypath, value=value))
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
if errors:
|
|
Packit |
bc9a3a |
raise ValueError(r'Invalid ntp configuration:\n{errors}'.format(
|
|
Packit |
bc9a3a |
errors='\n'.join(errors)))
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
def handle(name, cfg, cloud, log, _args):
|
|
Packit |
bc9a3a |
"""Enable and configure ntp."""
|
|
Packit |
bc9a3a |
if 'ntp' not in cfg:
|
|
Packit |
bc9a3a |
LOG.debug(
|
|
Packit |
bc9a3a |
"Skipping module named %s, not present or disabled by cfg", name)
|
|
Packit |
bc9a3a |
return
|
|
Packit |
bc9a3a |
ntp_cfg = cfg['ntp']
|
|
Packit |
bc9a3a |
if ntp_cfg is None:
|
|
Packit |
bc9a3a |
ntp_cfg = {} # Allow empty config which will install the package
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# TODO drop this when validate_cloudconfig_schema is strict=True
|
|
Packit |
bc9a3a |
if not isinstance(ntp_cfg, (dict)):
|
|
Packit |
bc9a3a |
raise RuntimeError(
|
|
Packit |
bc9a3a |
"'ntp' key existed in config, but not a dictionary type,"
|
|
Packit |
bc9a3a |
" is a {_type} instead".format(_type=type_utils.obj_name(ntp_cfg)))
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
validate_cloudconfig_schema(cfg, schema)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# Allow users to explicitly enable/disable
|
|
Packit |
bc9a3a |
enabled = ntp_cfg.get('enabled', True)
|
|
Packit |
bc9a3a |
if util.is_false(enabled):
|
|
Packit |
bc9a3a |
LOG.debug("Skipping module named %s, disabled by cfg", name)
|
|
Packit |
bc9a3a |
return
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# Select which client is going to be used and get the configuration
|
|
Packit |
bc9a3a |
ntp_client_config = select_ntp_client(ntp_cfg.get('ntp_client'),
|
|
Packit |
bc9a3a |
cloud.distro)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# Allow user ntp config to override distro configurations
|
|
Packit |
bc9a3a |
ntp_client_config = util.mergemanydict(
|
|
Packit |
bc9a3a |
[ntp_client_config, ntp_cfg.get('config', {})], reverse=True)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
supplemental_schema_validation(ntp_client_config)
|
|
Packit |
bc9a3a |
rename_ntp_conf(confpath=ntp_client_config.get('confpath'))
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
template_fn = None
|
|
Packit |
bc9a3a |
if not ntp_client_config.get('template'):
|
|
Packit |
bc9a3a |
template_name = (
|
|
Packit |
bc9a3a |
ntp_client_config.get('template_name').replace('{distro}',
|
|
Packit |
bc9a3a |
cloud.distro.name))
|
|
Packit |
bc9a3a |
template_fn = cloud.get_template_filename(template_name)
|
|
Packit |
bc9a3a |
if not template_fn:
|
|
Packit |
bc9a3a |
msg = ('No template found, not rendering %s' %
|
|
Packit |
bc9a3a |
ntp_client_config.get('template_name'))
|
|
Packit |
bc9a3a |
raise RuntimeError(msg)
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
write_ntp_config_template(cloud.distro.name,
|
|
Packit |
bc9a3a |
servers=ntp_cfg.get('servers', []),
|
|
Packit |
bc9a3a |
pools=ntp_cfg.get('pools', []),
|
|
Packit |
bc9a3a |
path=ntp_client_config.get('confpath'),
|
|
Packit |
bc9a3a |
template_fn=template_fn,
|
|
Packit |
bc9a3a |
template=ntp_client_config.get('template'))
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
install_ntp_client(cloud.distro.install_packages,
|
|
Packit |
bc9a3a |
packages=ntp_client_config['packages'],
|
|
Packit |
bc9a3a |
check_exe=ntp_client_config['check_exe'])
|
|
Packit |
bc9a3a |
try:
|
|
Packit |
bc9a3a |
reload_ntp(ntp_client_config['service_name'],
|
|
Packit |
bc9a3a |
systemd=cloud.distro.uses_systemd())
|
|
Packit |
bc9a3a |
except util.ProcessExecutionError as e:
|
|
Packit |
bc9a3a |
LOG.exception("Failed to reload/start ntp service: %s", e)
|
|
Packit |
bc9a3a |
raise
|
|
Packit |
bc9a3a |
|
|
Packit |
bc9a3a |
# vi: ts=4 expandtab
|