Force immediate NTP time sync with chronyd at IPA startup

In order to make sure we have the correct time early, e.g.
by the time we create a TLS certificate, this patch proposes
to force an immediate NTP update when using chronyd. While
the previous approach uses the passed NTP server as well, the
update may happen only after chronyd has performed measurements
(which may be too late).

Story: #2009058
Task: #42843

Change-Id: I6edafe8edeb8549f324959e7a1ec175c3049a515
This commit is contained in:
Arne Wiebalck 2021-07-15 18:09:30 +02:00
parent cacdd9bab3
commit 5531d5cee7
4 changed files with 19 additions and 46 deletions

View File

@ -1385,15 +1385,15 @@ class TestStandbyExtension(base.IronicAgentTest):
self.agent_extension._sync_clock() self.agent_extension._sync_clock()
calls = [mock.call('chronyd', check_exit_code=[0, 1]), calls = [mock.call('chronyc', 'shutdown', check_exit_code=[0, 1]),
mock.call('chronyc', 'add', 'server', '192.168.1.1'), mock.call("chronyd -q 'server 192.168.1.1 iburst'",
mock.call('chronyc', 'makestep'), shell=True),
mock.call('hwclock', '-v', '--systohc')] mock.call('hwclock', '-v', '--systohc')]
execute_mock.assert_has_calls(calls) execute_mock.assert_has_calls(calls)
execute_mock.reset_mock() execute_mock.reset_mock()
execute_mock.side_effect = [ execute_mock.side_effect = [
('', ''), ('', ''), ('', ''), ('', ''), ('', ''),
processutils.ProcessExecutionError('boop') processutils.ProcessExecutionError('boop')
] ]

View File

@ -856,27 +856,8 @@ class TestClockSyncUtils(ironic_agent_base.IronicAgentTest):
mock_time_method.return_value = 'chronyd' mock_time_method.return_value = 'chronyd'
utils.sync_clock() utils.sync_clock()
mock_execute.assert_has_calls([ mock_execute.assert_has_calls([
mock.call('chronyd', check_exit_code=[0, 1]), mock.call('chronyc', 'shutdown', check_exit_code=[0, 1]),
mock.call('chronyc', 'add', 'server', '192.168.1.1'), mock.call("chronyd -q 'server 192.168.1.1 iburst'", shell=True),
mock.call('chronyc', 'makestep'),
])
@mock.patch.object(utils, 'determine_time_method', autospec=True)
def test_sync_clock_chrony_already_present(self, mock_time_method,
mock_execute):
self.config(ntp_server='192.168.1.1')
mock_time_method.return_value = 'chronyd'
mock_execute.side_effect = [
('', ''),
processutils.ProcessExecutionError(
stderr='Source already present'),
('', ''),
]
utils.sync_clock()
mock_execute.assert_has_calls([
mock.call('chronyd', check_exit_code=[0, 1]),
mock.call('chronyc', 'add', 'server', '192.168.1.1'),
mock.call('chronyc', 'makestep'),
]) ])
@mock.patch.object(utils, 'determine_time_method', autospec=True) @mock.patch.object(utils, 'determine_time_method', autospec=True)
@ -889,12 +870,8 @@ class TestClockSyncUtils(ironic_agent_base.IronicAgentTest):
processutils.ProcessExecutionError(stderr='time verboten'), processutils.ProcessExecutionError(stderr='time verboten'),
] ]
self.assertRaisesRegex(errors.CommandExecutionError, self.assertRaisesRegex(errors.CommandExecutionError,
'Error occured adding ntp', 'Failed to sync time using chrony to ntp '
utils.sync_clock) 'server:', utils.sync_clock)
mock_execute.assert_has_calls([
mock.call('chronyd', check_exit_code=[0, 1]),
mock.call('chronyc', 'add', 'server', '192.168.1.1'),
])
@mock.patch.object(utils, 'determine_time_method', autospec=True) @mock.patch.object(utils, 'determine_time_method', autospec=True)
def test_sync_clock_none(self, mock_time_method, mock_execute): def test_sync_clock_none(self, mock_time_method, mock_execute):

View File

@ -835,21 +835,11 @@ def sync_clock(ignore_errors=False):
raise errors.CommandExecutionError(msg) raise errors.CommandExecutionError(msg)
elif method == 'chronyd': elif method == 'chronyd':
try: try:
# 0 should be if chronyd started # stop chronyd, ignore if it ran before or not
# 1 if already running execute('chronyc', 'shutdown', check_exit_code=[0, 1])
execute('chronyd', check_exit_code=[0, 1]) # force a time sync now
# NOTE(TheJulia): Once started, chronyd forks and stays in the query = "server " + CONF.ntp_server + " iburst"
# background as a server service, it will continue to keep the execute("chronyd -q \'%s\'" % query, shell=True)
# clock in sync.
try:
execute('chronyc', 'add', 'server', CONF.ntp_server)
except processutils.ProcessExecutionError as e:
if 'Source already present' not in str(e):
msg = 'Error occured adding ntp server: %s' % e
LOG.error(msg)
raise errors.CommandExecutionError(msg)
# Force the clock to sync now.
execute('chronyc', 'makestep')
LOG.debug('Set software clock using chrony') LOG.debug('Set software clock using chrony')
except (processutils.ProcessExecutionError, except (processutils.ProcessExecutionError,
errors.CommandExecutionError) as e: errors.CommandExecutionError) as e:

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fixes an issue where the NTP time sync at the IPA startup via chronyd is
not immediate (which can break time sensitive components such as the
generation of a TLS certificate).