diff options
| author | Julia Kreger <juliaashleykreger@gmail.com> | 2020-02-14 13:45:12 -0800 |
|---|---|---|
| committer | Julia Kreger <juliaashleykreger@gmail.com> | 2020-03-07 09:16:19 -0800 |
| commit | cee4bfc4bc2b038ca5c092aa73b1f9f670b58f66 (patch) | |
| tree | 654228ecd4c566e6088c441257bc6a7ad1793d22 /ironic_python_agent/tests | |
| parent | 48ef7c918898b7ce679d500270018b831a81ae03 (diff) | |
| download | ironic-python-agent-cee4bfc4bc2b038ca5c092aa73b1f9f670b58f66.tar.gz | |
Add NTP time sync
Attempt to sync the clock and save it to the hardware clock.
This feature supports use of chrony or ntpdate.
Sem-Ver: feature
Change-Id: I178d7614429d582e742d9cba6d0fa3ae099775e3
Story: 1619054
Task: 11591
Diffstat (limited to 'ironic_python_agent/tests')
| -rw-r--r-- | ironic_python_agent/tests/unit/extensions/test_standby.py | 65 | ||||
| -rw-r--r-- | ironic_python_agent/tests/unit/test_utils.py | 105 |
2 files changed, 167 insertions, 3 deletions
diff --git a/ironic_python_agent/tests/unit/extensions/test_standby.py b/ironic_python_agent/tests/unit/extensions/test_standby.py index 6a2fa440..e2650354 100644 --- a/ironic_python_agent/tests/unit/extensions/test_standby.py +++ b/ironic_python_agent/tests/unit/extensions/test_standby.py @@ -953,12 +953,13 @@ class TestStandbyExtension(base.IronicAgentTest): @mock.patch('ironic_python_agent.utils.execute', autospec=True) def test_run_shutdown_command_valid_poweroff_sysrq(self, execute_mock): - execute_mock.side_effect = [('', ''), ('', + execute_mock.side_effect = [('', ''), ('', ''), ('', 'Running in chroot, ignoring request.'), ('', '')] self.agent_extension._run_shutdown_command('poweroff') - calls = [mock.call('sync'), + calls = [mock.call('hwclock', '-v', '--systohc'), + mock.call('sync'), mock.call('poweroff', use_standard_locale=True, check_exit_code=[0]), mock.call("echo o > /proc/sysrq-trigger", shell=True)] @@ -966,7 +967,7 @@ class TestStandbyExtension(base.IronicAgentTest): @mock.patch('ironic_python_agent.utils.execute', autospec=True) def test_run_shutdown_command_valid_reboot_sysrq(self, execute_mock): - execute_mock.side_effect = [('', ''), ('', + execute_mock.side_effect = [('', ''), ('', ''), ('', 'Running in chroot, ignoring request.'), ('', '')] @@ -1022,6 +1023,37 @@ class TestStandbyExtension(base.IronicAgentTest): execute_mock.assert_any_call('sync') self.assertEqual('FAILED', failed_result.command_status) + @mock.patch('ironic_python_agent.utils.determine_time_method', + autospec=True) + @mock.patch('ironic_python_agent.utils.execute', autospec=True) + def test_power_off_with_ntp_server(self, execute_mock, mock_timemethod): + self.config(fail_if_clock_not_set=False) + self.config(ntp_server='192.168.1.1') + execute_mock.return_value = ('', '') + mock_timemethod.return_value = 'ntpdate' + + success_result = self.agent_extension.power_off() + success_result.join() + + calls = [mock.call('ntpdate', '192.168.1.1'), + mock.call('hwclock', '-v', '--systohc'), + mock.call('sync'), + mock.call('poweroff', use_standard_locale=True, + check_exit_code=[0])] + execute_mock.assert_has_calls(calls) + self.assertEqual('SUCCEEDED', success_result.command_status) + + self.config(fail_if_clock_not_set=True) + execute_mock.reset_mock() + execute_mock.return_value = ('', '') + execute_mock.side_effect = processutils.ProcessExecutionError + + failed_result = self.agent_extension.power_off() + failed_result.join() + + execute_mock.assert_any_call('ntpdate', '192.168.1.1') + self.assertEqual('FAILED', failed_result.command_status) + @mock.patch('ironic_python_agent.utils.execute', autospec=True) def test_sync(self, execute_mock): result = self.agent_extension.sync() @@ -1162,6 +1194,33 @@ class TestStandbyExtension(base.IronicAgentTest): '/dev/fake') self.assertEqual(expected_msg, result_msg) + @mock.patch('ironic_python_agent.utils.determine_time_method', + autospec=True) + @mock.patch('ironic_python_agent.utils.execute', autospec=True) + def test__sync_clock(self, execute_mock, mock_timemethod): + self.config(ntp_server='192.168.1.1') + self.config(fail_if_clock_not_set=True) + execute_mock.return_value = ('', '') + mock_timemethod.return_value = 'chronyd' + + self.agent_extension._sync_clock() + + calls = [mock.call('chronyd', check_exit_code=[0, 1]), + mock.call('chronyc', 'add', 'server', '192.168.1.1'), + mock.call('chronyc', 'makestep'), + mock.call('hwclock', '-v', '--systohc')] + execute_mock.assert_has_calls(calls) + + execute_mock.reset_mock() + execute_mock.side_effect = [ + ('', ''), ('', ''), ('', ''), + processutils.ProcessExecutionError('boop') + ] + + self.assertRaises(errors.ClockSyncError, + self.agent_extension._sync_clock) + execute_mock.assert_any_call('hwclock', '-v', '--systohc') + @mock.patch('hashlib.md5', autospec=True) @mock.patch('requests.get', autospec=True) diff --git a/ironic_python_agent/tests/unit/test_utils.py b/ironic_python_agent/tests/unit/test_utils.py index 8eb505ef..eb6eacd3 100644 --- a/ironic_python_agent/tests/unit/test_utils.py +++ b/ironic_python_agent/tests/unit/test_utils.py @@ -674,3 +674,108 @@ class TestRemoveKeys(testtools.TestCase): 'key': 'value', 'other': [{'configdrive': '<...>'}, 'string', 0]} self.assertEqual(expected, utils.remove_large_keys(value)) + + +@mock.patch.object(utils, 'execute', autospec=True) +class TestClockSyncUtils(ironic_agent_base.IronicAgentTest): + + def test_determine_time_method_none(self, mock_execute): + mock_execute.side_effect = OSError + self.assertIsNone(utils.determine_time_method()) + + def test_determine_time_method_ntpdate(self, mock_execute): + mock_execute.side_effect = [ + OSError, # No chronyd found + ('', ''), # Returns nothing on ntpdate call + ] + calls = [mock.call('chronyd', '-h'), + mock.call('ntpdate', '-v', check_exit_code=[0, 1])] + return_value = utils.determine_time_method() + self.assertEqual('ntpdate', return_value) + mock_execute.assert_has_calls(calls) + + def test_determine_time_method_chronyd(self, mock_execute): + mock_execute.side_effect = [ + ('', ''), # Returns nothing on ntpdate call + ] + calls = [mock.call('chronyd', '-h')] + return_value = utils.determine_time_method() + self.assertEqual('chronyd', return_value) + mock_execute.assert_has_calls(calls) + + @mock.patch.object(utils, 'determine_time_method', autospec=True) + def test_sync_clock_ntp(self, mock_time_method, mock_execute): + self.config(ntp_server='192.168.1.1') + mock_time_method.return_value = 'ntpdate' + utils.sync_clock() + mock_execute.assert_has_calls([mock.call('ntpdate', '192.168.1.1')]) + + @mock.patch.object(utils, 'determine_time_method', autospec=True) + def test_sync_clock_ntp_raises_exception(self, mock_time_method, + mock_execute): + self.config(ntp_server='192.168.1.1') + self.config(fail_if_clock_not_set=True) + mock_time_method.return_value = 'ntpdate' + mock_execute.side_effect = processutils.ProcessExecutionError() + self.assertRaises(errors.CommandExecutionError, utils.sync_clock) + + @mock.patch.object(utils, 'determine_time_method', autospec=True) + def test_sync_clock_chrony(self, mock_time_method, mock_execute): + self.config(ntp_server='192.168.1.1') + mock_time_method.return_value = 'chronyd' + 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) + 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) + def test_sync_clock_chrony_failure(self, mock_time_method, mock_execute): + self.config(ntp_server='192.168.1.1') + self.config(fail_if_clock_not_set=True) + mock_time_method.return_value = 'chronyd' + mock_execute.side_effect = [ + ('', ''), + processutils.ProcessExecutionError(stderr='time verboten'), + ] + self.assertRaisesRegex(errors.CommandExecutionError, + 'Error occured adding ntp', + 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) + def test_sync_clock_none(self, mock_time_method, mock_execute): + self.config(ntp_server='192.168.1.1') + mock_time_method.return_value = None + utils.sync_clock(ignore_errors=True) + self.assertEqual(0, mock_execute.call_count) + + @mock.patch.object(utils, 'determine_time_method', autospec=True) + def test_sync_clock_ntp_server_is_none(self, mock_time_method, + mock_execute): + self.config(ntp_server=None) + mock_time_method.return_value = None + utils.sync_clock() + self.assertEqual(0, mock_execute.call_count) |
