summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cloudinit/config/cc_ntp.py106
-rw-r--r--config/cloud.cfg1
-rw-r--r--doc/examples/cloud-config-ntp.txt27
-rw-r--r--templates/ntp.conf.debian.tmpl63
-rw-r--r--templates/ntp.conf.fedora.tmpl66
-rw-r--r--templates/ntp.conf.rhel.tmpl61
-rw-r--r--templates/ntp.conf.sles.tmpl100
-rw-r--r--templates/ntp.conf.ubuntu.tmpl75
-rw-r--r--tests/unittests/test_handler/test_handler_ntp.py274
9 files changed, 773 insertions, 0 deletions
diff --git a/cloudinit/config/cc_ntp.py b/cloudinit/config/cc_ntp.py
new file mode 100644
index 00000000..ad69aa34
--- /dev/null
+++ b/cloudinit/config/cc_ntp.py
@@ -0,0 +1,106 @@
+# vi: ts=4 expandtab
+#
+# Copyright (C) 2016 Canonical Ltd.
+#
+# Author: Ryan Harper <ryan.harper@canonical.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+from cloudinit import log as logging
+from cloudinit.settings import PER_INSTANCE
+from cloudinit import templater
+from cloudinit import type_utils
+from cloudinit import util
+
+import os
+
+LOG = logging.getLogger(__name__)
+
+frequency = PER_INSTANCE
+NTP_CONF = '/etc/ntp.conf'
+NR_POOL_SERVERS = 4
+distros = ['centos', 'debian', 'fedora', 'opensuse', 'ubuntu']
+
+
+def handle(name, cfg, cloud, log, _args):
+ """
+ Enable and configure ntp
+
+ ntp:
+ pools: ['0.{{distro}}.pool.ntp.org', '1.{{distro}}.pool.ntp.org']
+ servers: ['192.168.2.1']
+
+ """
+
+ ntp_cfg = cfg.get('ntp', {})
+
+ if not isinstance(ntp_cfg, (dict)):
+ raise RuntimeError(("'ntp' key existed in config,"
+ " but not a dictionary type,"
+ " is a %s %instead"), type_utils.obj_name(ntp_cfg))
+
+ if 'ntp' not in cfg:
+ LOG.debug("Skipping module named %s,"
+ "not present or disabled by cfg", name)
+ return True
+
+ install_ntp(cloud.distro.install_packages, packages=['ntp'],
+ check_exe="ntpd")
+ rename_ntp_conf()
+ write_ntp_config_template(ntp_cfg, cloud)
+
+
+def install_ntp(install_func, packages=None, check_exe="ntpd"):
+ if util.which(check_exe):
+ return
+ if packages is None:
+ packages = ['ntp']
+
+ install_func(packages)
+
+
+def rename_ntp_conf(config=NTP_CONF):
+ if os.path.exists(config):
+ util.rename(config, config + ".dist")
+
+
+def generate_server_names(distro):
+ names = []
+ for x in range(0, NR_POOL_SERVERS):
+ name = "%d.%s.pool.ntp.org" % (x, distro)
+ names.append(name)
+ return names
+
+
+def write_ntp_config_template(cfg, cloud):
+ servers = cfg.get('servers', [])
+ pools = cfg.get('pools', [])
+
+ if len(servers) == 0 and len(pools) == 0:
+ LOG.debug('Adding distro default ntp pool servers')
+ pools = generate_server_names(cloud.distro.name)
+
+ params = {
+ 'servers': servers,
+ 'pools': pools,
+ }
+
+ template_fn = cloud.get_template_filename('ntp.conf.%s' %
+ (cloud.distro.name))
+ if not template_fn:
+ template_fn = cloud.get_template_filename('ntp.conf')
+ if not template_fn:
+ raise RuntimeError(("No template found, "
+ "not rendering %s"), NTP_CONF)
+
+ templater.render_to_file(template_fn, NTP_CONF, params)
diff --git a/config/cloud.cfg b/config/cloud.cfg
index a6afcc83..2d7fb473 100644
--- a/config/cloud.cfg
+++ b/config/cloud.cfg
@@ -45,6 +45,7 @@ cloud_config_modules:
- emit_upstart
- disk_setup
- mounts
+ - ntp
- ssh-import-id
- locale
- set-passwords
diff --git a/doc/examples/cloud-config-ntp.txt b/doc/examples/cloud-config-ntp.txt
new file mode 100644
index 00000000..2fc656e4
--- /dev/null
+++ b/doc/examples/cloud-config-ntp.txt
@@ -0,0 +1,27 @@
+#cloud-config
+
+# ntp: configure ntp services
+# servers: List of NTP servers with which to sync
+# pools: List of NTP pool servers with which to sync (pools are typically
+# DNS hostnames which resolve to different specific servers to load
+# balance a set of services)
+#
+# Each server in the list will be added in list-order in the following format:
+#
+# [pool|server] <server entry> iburst
+#
+#
+# If no servers or pools are defined but ntp is enabled, then cloud-init will
+# render the distro default list of pools
+#
+# pools = [
+# '0.{distro}.pool.ntp.org',
+# '1.{distro}.pool.ntp.org',
+# '2.{distro}.pool.ntp.org',
+# '3.{distro}.pool.ntp.org',
+# ]
+#
+
+ntp:
+ pools: ['0.company.pool.ntp.org', '1.company.pool.ntp.org', 'ntp.myorg.org']
+ servers: ['my.ntp.server.local', 'ntp.ubuntu.com', '192.168.23.2']
diff --git a/templates/ntp.conf.debian.tmpl b/templates/ntp.conf.debian.tmpl
new file mode 100644
index 00000000..3f07eeaa
--- /dev/null
+++ b/templates/ntp.conf.debian.tmpl
@@ -0,0 +1,63 @@
+## template:jinja
+
+# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
+
+driftfile /var/lib/ntp/ntp.drift
+
+# Enable this if you want statistics to be logged.
+#statsdir /var/log/ntpstats/
+
+statistics loopstats peerstats clockstats
+filegen loopstats file loopstats type day enable
+filegen peerstats file peerstats type day enable
+filegen clockstats file clockstats type day enable
+
+
+# You do need to talk to an NTP server or two (or three).
+#server ntp.your-provider.example
+
+# pool.ntp.org maps to about 1000 low-stratum NTP servers. Your server will
+# pick a different set every time it starts up. Please consider joining the
+# pool: <http://www.pool.ntp.org/join.html>
+{% if pools -%}# pools{% endif %}
+{% for pool in pools -%}
+pool {{pool}} iburst
+{% endfor %}
+{%- if servers %}# servers
+{% endif %}
+{% for server in servers -%}
+server {{server}} iburst
+{% endfor %}
+
+# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
+# details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
+# might also be helpful.
+#
+# Note that "restrict" applies to both servers and clients, so a configuration
+# that might be intended to block requests from certain clients could also end
+# up blocking replies from your own upstream servers.
+
+# By default, exchange time with everybody, but don't allow configuration.
+restrict -4 default kod notrap nomodify nopeer noquery limited
+restrict -6 default kod notrap nomodify nopeer noquery limited
+
+# Local users may interrogate the ntp server more closely.
+restrict 127.0.0.1
+restrict ::1
+
+# Needed for adding pool entries
+restrict source notrap nomodify noquery
+
+# Clients from this (example!) subnet have unlimited access, but only if
+# cryptographically authenticated.
+#restrict 192.168.123.0 mask 255.255.255.0 notrust
+
+
+# If you want to provide time to your local subnet, change the next line.
+# (Again, the address is an example only.)
+#broadcast 192.168.123.255
+
+# If you want to listen to time broadcasts on your local subnet, de-comment the
+# next lines. Please do this only if you trust everybody on the network!
+#disable auth
+#broadcastclient
diff --git a/templates/ntp.conf.fedora.tmpl b/templates/ntp.conf.fedora.tmpl
new file mode 100644
index 00000000..af7b1b09
--- /dev/null
+++ b/templates/ntp.conf.fedora.tmpl
@@ -0,0 +1,66 @@
+## template:jinja
+
+# For more information about this file, see the man pages
+# ntp.conf(5), ntp_acc(5), ntp_auth(5), ntp_clock(5), ntp_misc(5), ntp_mon(5).
+
+driftfile /var/lib/ntp/drift
+
+# Permit time synchronization with our time source, but do not
+# permit the source to query or modify the service on this system.
+restrict default nomodify notrap nopeer noquery
+
+# Permit all access over the loopback interface. This could
+# be tightened as well, but to do so would effect some of
+# the administrative functions.
+restrict 127.0.0.1
+restrict ::1
+
+# Hosts on local network are less restricted.
+#restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap
+
+# Use public servers from the pool.ntp.org project.
+# Please consider joining the pool (http://www.pool.ntp.org/join.html).
+{% if pools %}# pools
+{% endif %}
+{% for pool in pools -%}
+pool {{pool}} iburst
+{% endfor %}
+{%- if servers %}# servers
+{% endif %}
+{% for server in servers -%}
+server {{server}} iburst
+{% endfor %}
+
+#broadcast 192.168.1.255 autokey # broadcast server
+#broadcastclient # broadcast client
+#broadcast 224.0.1.1 autokey # multicast server
+#multicastclient 224.0.1.1 # multicast client
+#manycastserver 239.255.254.254 # manycast server
+#manycastclient 239.255.254.254 autokey # manycast client
+
+# Enable public key cryptography.
+#crypto
+
+includefile /etc/ntp/crypto/pw
+
+# Key file containing the keys and key identifiers used when operating
+# with symmetric key cryptography.
+keys /etc/ntp/keys
+
+# Specify the key identifiers which are trusted.
+#trustedkey 4 8 42
+
+# Specify the key identifier to use with the ntpdc utility.
+#requestkey 8
+
+# Specify the key identifier to use with the ntpq utility.
+#controlkey 8
+
+# Enable writing of statistics records.
+#statistics clockstats cryptostats loopstats peerstats
+
+# Disable the monitoring facility to prevent amplification attacks using ntpdc
+# monlist command when default restrict does not include the noquery flag. See
+# CVE-2013-5211 for more details.
+# Note: Monitoring will not be disabled with the limited restriction flag.
+disable monitor
diff --git a/templates/ntp.conf.rhel.tmpl b/templates/ntp.conf.rhel.tmpl
new file mode 100644
index 00000000..62b47764
--- /dev/null
+++ b/templates/ntp.conf.rhel.tmpl
@@ -0,0 +1,61 @@
+## template:jinja
+
+# For more information about this file, see the man pages
+# ntp.conf(5), ntp_acc(5), ntp_auth(5), ntp_clock(5), ntp_misc(5), ntp_mon(5).
+
+driftfile /var/lib/ntp/drift
+
+# Permit time synchronization with our time source, but do not
+# permit the source to query or modify the service on this system.
+restrict default kod nomodify notrap nopeer noquery
+restrict -6 default kod nomodify notrap nopeer noquery
+
+# Permit all access over the loopback interface. This could
+# be tightened as well, but to do so would effect some of
+# the administrative functions.
+restrict 127.0.0.1
+restrict -6 ::1
+
+# Hosts on local network are less restricted.
+#restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap
+
+# Use public servers from the pool.ntp.org project.
+# Please consider joining the pool (http://www.pool.ntp.org/join.html).
+{% if pools %}# pools
+{% endif %}
+{% for pool in pools -%}
+pool {{pool}} iburst
+{% endfor %}
+{%- if servers %}# servers
+{% endif %}
+{% for server in servers -%}
+server {{server}} iburst
+{% endfor %}
+
+#broadcast 192.168.1.255 autokey # broadcast server
+#broadcastclient # broadcast client
+#broadcast 224.0.1.1 autokey # multicast server
+#multicastclient 224.0.1.1 # multicast client
+#manycastserver 239.255.254.254 # manycast server
+#manycastclient 239.255.254.254 autokey # manycast client
+
+# Enable public key cryptography.
+#crypto
+
+includefile /etc/ntp/crypto/pw
+
+# Key file containing the keys and key identifiers used when operating
+# with symmetric key cryptography.
+keys /etc/ntp/keys
+
+# Specify the key identifiers which are trusted.
+#trustedkey 4 8 42
+
+# Specify the key identifier to use with the ntpdc utility.
+#requestkey 8
+
+# Specify the key identifier to use with the ntpq utility.
+#controlkey 8
+
+# Enable writing of statistics records.
+#statistics clockstats cryptostats loopstats peerstats
diff --git a/templates/ntp.conf.sles.tmpl b/templates/ntp.conf.sles.tmpl
new file mode 100644
index 00000000..5c5fc4db
--- /dev/null
+++ b/templates/ntp.conf.sles.tmpl
@@ -0,0 +1,100 @@
+## template:jinja
+
+################################################################################
+## /etc/ntp.conf
+##
+## Sample NTP configuration file.
+## See package 'ntp-doc' for documentation, Mini-HOWTO and FAQ.
+## Copyright (c) 1998 S.u.S.E. GmbH Fuerth, Germany.
+##
+## Author: Michael Andres, <ma@suse.de>
+## Michael Skibbe, <mskibbe@suse.de>
+##
+################################################################################
+
+##
+## Radio and modem clocks by convention have addresses in the
+## form 127.127.t.u, where t is the clock type and u is a unit
+## number in the range 0-3.
+##
+## Most of these clocks require support in the form of a
+## serial port or special bus peripheral. The particular
+## device is normally specified by adding a soft link
+## /dev/device-u to the particular hardware device involved,
+## where u correspond to the unit number above.
+##
+## Generic DCF77 clock on serial port (Conrad DCF77)
+## Address: 127.127.8.u
+## Serial Port: /dev/refclock-u
+##
+## (create soft link /dev/refclock-0 to the particular ttyS?)
+##
+# server 127.127.8.0 mode 5 prefer
+
+##
+## Undisciplined Local Clock. This is a fake driver intended for backup
+## and when no outside source of synchronized time is available.
+##
+# server 127.127.1.0 # local clock (LCL)
+# fudge 127.127.1.0 stratum 10 # LCL is unsynchronized
+
+##
+## Add external Servers using
+## # rcntpd addserver <yourserver>
+## The servers will only be added to the currently running instance, not
+## to /etc/ntp.conf.
+##
+{% if pools %}# pools
+{% endif %}
+{% for pool in pools -%}
+pool {{pool}} iburst
+{% endfor %}
+{%- if servers %}# servers
+{% endif %}
+{% for server in servers -%}
+server {{server}} iburst
+{% endfor %}
+
+# Access control configuration; see /usr/share/doc/packages/ntp/html/accopt.html for
+# details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
+# might also be helpful.
+#
+# Note that "restrict" applies to both servers and clients, so a configuration
+# that might be intended to block requests from certain clients could also end
+# up blocking replies from your own upstream servers.
+
+# By default, exchange time with everybody, but don't allow configuration.
+restrict -4 default notrap nomodify nopeer noquery
+restrict -6 default notrap nomodify nopeer noquery
+
+# Local users may interrogate the ntp server more closely.
+restrict 127.0.0.1
+restrict ::1
+
+# Clients from this (example!) subnet have unlimited access, but only if
+# cryptographically authenticated.
+#restrict 192.168.123.0 mask 255.255.255.0 notrust
+
+##
+## Miscellaneous stuff
+##
+
+driftfile /var/lib/ntp/drift/ntp.drift # path for drift file
+
+logfile /var/log/ntp # alternate log file
+# logconfig =syncstatus + sysevents
+# logconfig =all
+
+# statsdir /tmp/ # directory for statistics files
+# filegen peerstats file peerstats type day enable
+# filegen loopstats file loopstats type day enable
+# filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /etc/ntp.keys # path for keys file
+trustedkey 1 # define trusted keys
+requestkey 1 # key (7) for accessing server variables
+controlkey 1 # key (6) for accessing server variables
+
diff --git a/templates/ntp.conf.ubuntu.tmpl b/templates/ntp.conf.ubuntu.tmpl
new file mode 100644
index 00000000..862a4fbd
--- /dev/null
+++ b/templates/ntp.conf.ubuntu.tmpl
@@ -0,0 +1,75 @@
+## template:jinja
+
+# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help
+
+driftfile /var/lib/ntp/ntp.drift
+
+# Enable this if you want statistics to be logged.
+#statsdir /var/log/ntpstats/
+
+statistics loopstats peerstats clockstats
+filegen loopstats file loopstats type day enable
+filegen peerstats file peerstats type day enable
+filegen clockstats file clockstats type day enable
+
+# Specify one or more NTP servers.
+
+# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
+# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for
+# more information.
+{% if pools %}# pools
+{% endif %}
+{% for pool in pools -%}
+pool {{pool}} iburst
+{% endfor %}
+{%- if servers %}# servers
+{% endif %}
+{% for server in servers -%}
+server {{server}} iburst
+{% endfor %}
+
+# Use Ubuntu's ntp server as a fallback.
+# pool ntp.ubuntu.com
+
+# Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for
+# details. The web page <http://support.ntp.org/bin/view/Support/AccessRestrictions>
+# might also be helpful.
+#
+# Note that "restrict" applies to both servers and clients, so a configuration
+# that might be intended to block requests from certain clients could also end
+# up blocking replies from your own upstream servers.
+
+# By default, exchange time with everybody, but don't allow configuration.
+restrict -4 default kod notrap nomodify nopeer noquery limited
+restrict -6 default kod notrap nomodify nopeer noquery limited
+
+# Local users may interrogate the ntp server more closely.
+restrict 127.0.0.1
+restrict ::1
+
+# Needed for adding pool entries
+restrict source notrap nomodify noquery
+
+# Clients from this (example!) subnet have unlimited access, but only if
+# cryptographically authenticated.
+#restrict 192.168.123.0 mask 255.255.255.0 notrust
+
+
+# If you want to provide time to your local subnet, change the next line.
+# (Again, the address is an example only.)
+#broadcast 192.168.123.255
+
+# If you want to listen to time broadcasts on your local subnet, de-comment the
+# next lines. Please do this only if you trust everybody on the network!
+#disable auth
+#broadcastclient
+
+#Changes recquired to use pps synchonisation as explained in documentation:
+#http://www.ntp.org/ntpfaq/NTP-s-config-adv.htm#AEN3918
+
+#server 127.127.8.1 mode 135 prefer # Meinberg GPS167 with PPS
+#fudge 127.127.8.1 time1 0.0042 # relative to PPS for my hardware
+
+#server 127.127.22.1 # ATOM(PPS)
+#fudge 127.127.22.1 flag3 1 # enable PPS API
+
diff --git a/tests/unittests/test_handler/test_handler_ntp.py b/tests/unittests/test_handler/test_handler_ntp.py
new file mode 100644
index 00000000..1c7bb06a
--- /dev/null
+++ b/tests/unittests/test_handler/test_handler_ntp.py
@@ -0,0 +1,274 @@
+from cloudinit.config import cc_ntp
+from cloudinit.sources import DataSourceNone
+from cloudinit import templater
+from cloudinit import (distros, helpers, cloud, util)
+from ..helpers import FilesystemMockingTestCase, mock
+
+import logging
+import os
+import shutil
+import tempfile
+
+LOG = logging.getLogger(__name__)
+
+NTP_TEMPLATE = """
+## template: jinja
+
+{% if pools %}# pools
+{% endif %}
+{% for pool in pools -%}
+pool {{pool}} iburst
+{% endfor %}
+{%- if servers %}# servers
+{% endif %}
+{% for server in servers -%}
+server {{server}} iburst
+{% endfor %}
+
+"""
+
+
+NTP_EXPECTED_UBUNTU = """
+# pools
+pool 0.mycompany.pool.ntp.org iburst
+# servers
+server 192.168.23.3 iburst
+
+"""
+
+
+class TestNtp(FilesystemMockingTestCase):
+
+ def setUp(self):
+ super(TestNtp, self).setUp()
+ self.subp = util.subp
+ self.new_root = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.new_root)
+
+ def _get_cloud(self, distro, metadata=None):
+ self.patchUtils(self.new_root)
+ paths = helpers.Paths({})
+ cls = distros.fetch(distro)
+ mydist = cls(distro, {}, paths)
+ myds = DataSourceNone.DataSourceNone({}, mydist, paths)
+ if metadata:
+ myds.metadata.update(metadata)
+ return cloud.Cloud(myds, paths, {}, mydist, None)
+
+ @mock.patch("cloudinit.config.cc_ntp.util")
+ def test_ntp_install(self, mock_util):
+ cc = self._get_cloud('ubuntu')
+ cc.distro = mock.MagicMock()
+ cc.distro.name = 'ubuntu'
+ mock_util.which.return_value = None
+ install_func = mock.MagicMock()
+
+ cc_ntp.install_ntp(install_func, packages=['ntpx'], check_exe='ntpdx')
+
+ self.assertTrue(install_func.called)
+ mock_util.which.assert_called_with('ntpdx')
+ install_pkg = install_func.call_args_list[0][0][0]
+ self.assertEqual(sorted(install_pkg), ['ntpx'])
+
+ @mock.patch("cloudinit.config.cc_ntp.util")
+ def test_ntp_install_not_needed(self, mock_util):
+ cc = self._get_cloud('ubuntu')
+ cc.distro = mock.MagicMock()
+ cc.distro.name = 'ubuntu'
+ mock_util.which.return_value = ["/usr/sbin/ntpd"]
+ cc_ntp.install_ntp(cc)
+ self.assertFalse(cc.distro.install_packages.called)
+
+ def test_ntp_rename_ntp_conf(self):
+ with mock.patch.object(os.path, 'exists',
+ return_value=True) as mockpath:
+ with mock.patch.object(util, 'rename') as mockrename:
+ cc_ntp.rename_ntp_conf()
+
+ mockpath.assert_called_with('/etc/ntp.conf')
+ mockrename.assert_called_with('/etc/ntp.conf', '/etc/ntp.conf.dist')
+
+ def test_ntp_rename_ntp_conf_skip_missing(self):
+ with mock.patch.object(os.path, 'exists',
+ return_value=False) as mockpath:
+ with mock.patch.object(util, 'rename') as mockrename:
+ cc_ntp.rename_ntp_conf()
+
+ mockpath.assert_called_with('/etc/ntp.conf')
+ mockrename.assert_not_called()
+
+ def ntp_conf_render(self, distro):
+ """ntp_conf_render
+ Test rendering of a ntp.conf from template for a given distro
+ """
+
+ cfg = {'ntp': {}}
+ mycloud = self._get_cloud(distro)
+ distro_names = cc_ntp.generate_server_names(distro)
+
+ with mock.patch.object(templater, 'render_to_file') as mocktmpl:
+ with mock.patch.object(os.path, 'isfile', return_value=True):
+ with mock.patch.object(util, 'rename'):
+ cc_ntp.write_ntp_config_template(cfg, mycloud)
+
+ mocktmpl.assert_called_once_with(
+ ('/etc/cloud/templates/ntp.conf.%s.tmpl' % distro),
+ '/etc/ntp.conf',
+ {'servers': [], 'pools': distro_names})
+
+ def test_ntp_conf_render_rhel(self):
+ """Test templater.render_to_file() for rhel"""
+ self.ntp_conf_render('rhel')
+
+ def test_ntp_conf_render_debian(self):
+ """Test templater.render_to_file() for debian"""
+ self.ntp_conf_render('debian')
+
+ def test_ntp_conf_render_fedora(self):
+ """Test templater.render_to_file() for fedora"""
+ self.ntp_conf_render('fedora')
+
+ def test_ntp_conf_render_sles(self):
+ """Test templater.render_to_file() for sles"""
+ self.ntp_conf_render('sles')
+
+ def test_ntp_conf_render_ubuntu(self):
+ """Test templater.render_to_file() for ubuntu"""
+ self.ntp_conf_render('ubuntu')
+
+ def test_ntp_conf_servers_no_pools(self):
+ distro = 'ubuntu'
+ pools = []
+ servers = ['192.168.2.1']
+ cfg = {
+ 'ntp': {
+ 'pools': pools,
+ 'servers': servers,
+ }
+ }
+ mycloud = self._get_cloud(distro)
+
+ with mock.patch.object(templater, 'render_to_file') as mocktmpl:
+ with mock.patch.object(os.path, 'isfile', return_value=True):
+ with mock.patch.object(util, 'rename'):
+ cc_ntp.write_ntp_config_template(cfg.get('ntp'), mycloud)
+
+ mocktmpl.assert_called_once_with(
+ ('/etc/cloud/templates/ntp.conf.%s.tmpl' % distro),
+ '/etc/ntp.conf',
+ {'servers': servers, 'pools': pools})
+
+ def test_ntp_conf_custom_pools_no_server(self):
+ distro = 'ubuntu'
+ pools = ['0.mycompany.pool.ntp.org']
+ servers = []
+ cfg = {
+ 'ntp': {
+ 'pools': pools,
+ 'servers': servers,
+ }
+ }
+ mycloud = self._get_cloud(distro)
+
+ with mock.patch.object(templater, 'render_to_file') as mocktmpl:
+ with mock.patch.object(os.path, 'isfile', return_value=True):
+ with mock.patch.object(util, 'rename'):
+ cc_ntp.write_ntp_config_template(cfg.get('ntp'), mycloud)
+
+ mocktmpl.assert_called_once_with(
+ ('/etc/cloud/templates/ntp.conf.%s.tmpl' % distro),
+ '/etc/ntp.conf',
+ {'servers': servers, 'pools': pools})
+
+ def test_ntp_conf_custom_pools_and_server(self):
+ distro = 'ubuntu'
+ pools = ['0.mycompany.pool.ntp.org']
+ servers = ['192.168.23.3']
+ cfg = {
+ 'ntp': {
+ 'pools': pools,
+ 'servers': servers,
+ }
+ }
+ mycloud = self._get_cloud(distro)
+
+ with mock.patch.object(templater, 'render_to_file') as mocktmpl:
+ with mock.patch.object(os.path, 'isfile', return_value=True):
+ with mock.patch.object(util, 'rename'):
+ cc_ntp.write_ntp_config_template(cfg.get('ntp'), mycloud)
+
+ mocktmpl.assert_called_once_with(
+ ('/etc/cloud/templates/ntp.conf.%s.tmpl' % distro),
+ '/etc/ntp.conf',
+ {'servers': servers, 'pools': pools})
+
+ def test_ntp_conf_contents_match(self):
+ """Test rendered contents of /etc/ntp.conf for ubuntu"""
+ pools = ['0.mycompany.pool.ntp.org']
+ servers = ['192.168.23.3']
+ cfg = {
+ 'ntp': {
+ 'pools': pools,
+ 'servers': servers,
+ }
+ }
+ mycloud = self._get_cloud('ubuntu')
+ side_effect = [NTP_TEMPLATE.lstrip()]
+
+ # work backwards from util.write_file and mock out call path
+ # write_ntp_config_template()
+ # cloud.get_template_filename()
+ # os.path.isfile()
+ # templater.render_to_file()
+ # templater.render_from_file()
+ # util.load_file()
+ # util.write_file()
+ #
+ with mock.patch.object(util, 'write_file') as mockwrite:
+ with mock.patch.object(util, 'load_file', side_effect=side_effect):
+ with mock.patch.object(os.path, 'isfile', return_value=True):
+ with mock.patch.object(util, 'rename'):
+ cc_ntp.write_ntp_config_template(cfg.get('ntp'),
+ mycloud)
+
+ mockwrite.assert_called_once_with(
+ '/etc/ntp.conf',
+ NTP_EXPECTED_UBUNTU,
+ mode=420)
+
+ def test_ntp_handler(self):
+ """Test ntp handler renders ubuntu ntp.conf template"""
+ pools = ['0.mycompany.pool.ntp.org']
+ servers = ['192.168.23.3']
+ cfg = {
+ 'ntp': {
+ 'pools': pools,
+ 'servers': servers,
+ }
+ }
+ mycloud = self._get_cloud('ubuntu')
+ side_effect = [NTP_TEMPLATE.lstrip()]
+
+ with mock.patch.object(util, 'which', return_value=None):
+ with mock.patch.object(os.path, 'exists'):
+ with mock.patch.object(util, 'write_file') as mockwrite:
+ with mock.patch.object(util, 'load_file',
+ side_effect=side_effect):
+ with mock.patch.object(os.path, 'isfile',
+ return_value=True):
+ with mock.patch.object(util, 'rename'):
+ cc_ntp.handle("notimportant", cfg,
+ mycloud, LOG, None)
+
+ mockwrite.assert_called_once_with(
+ '/etc/ntp.conf',
+ NTP_EXPECTED_UBUNTU,
+ mode=420)
+
+ @mock.patch("cloudinit.config.cc_ntp.util")
+ def test_no_ntpcfg_does_nothing(self, mock_util):
+ cc = self._get_cloud('ubuntu')
+ cc.distro = mock.MagicMock()
+ cc_ntp.handle('cc_ntp', {}, cc, LOG, [])
+ self.assertFalse(cc.distro.install_packages.called)
+ self.assertFalse(mock_util.subp.called)