diff --git a/neutron/agent/linux/keepalived.py b/neutron/agent/linux/keepalived.py index da757a8416b..f47a27f1d1c 100644 --- a/neutron/agent/linux/keepalived.py +++ b/neutron/agent/linux/keepalived.py @@ -15,6 +15,7 @@ import errno import itertools import os +import signal import netaddr from neutron_lib import constants @@ -39,6 +40,7 @@ GARP_MASTER_DELAY = 60 HEALTH_CHECK_NAME = 'ha_health_check' LOG = logging.getLogger(__name__) +SIGTERM_TIMEOUT = 5 def get_free_range(parent_range, excluded_ranges, size=PRIMARY_VIP_RANGE_SIZE): @@ -457,7 +459,15 @@ class KeepalivedManager(object): service_name=KEEPALIVED_SERVICE_NAME) pm = self.get_process() - pm.disable(sig='15') + pm.disable(sig=str(int(signal.SIGTERM))) + try: + utils.wait_until_true(lambda: not pm.active, + timeout=SIGTERM_TIMEOUT) + except utils.WaitTimeout: + LOG.warning('Keepalived process %s did not finish after SIGTERM ' + 'signal in %s seconds, sending SIGKILL signal', + pm.pid, SIGTERM_TIMEOUT) + pm.disable(sig=str(int(signal.SIGKILL))) def check_processes(self): keepalived_pm = self.get_process() diff --git a/neutron/tests/unit/agent/linux/test_keepalived.py b/neutron/tests/unit/agent/linux/test_keepalived.py index bd4d619ff0a..87410d640ad 100644 --- a/neutron/tests/unit/agent/linux/test_keepalived.py +++ b/neutron/tests/unit/agent/linux/test_keepalived.py @@ -14,13 +14,16 @@ # import os +import signal import textwrap import mock from neutron_lib import constants as n_consts from oslo_config import cfg +from oslo_utils import uuidutils import testtools +from neutron.agent.linux import external_process from neutron.agent.linux import keepalived from neutron.conf.agent.l3 import config as l3_config from neutron.tests import base @@ -543,3 +546,40 @@ ping6 -c 1 -w 1 2001:db8::1 1>/dev/null || exit 1""", with mock.patch.object(keepalived, 'file_utils') as patched_utils: ts.write_check_script() patched_utils.replace_file.assert_not_called() + + +class KeepalivedManagerTestCase(base.BaseTestCase): + + def setUp(self): + super(KeepalivedManagerTestCase, self).setUp() + self.mock_config = mock.Mock() + self.mock_config.AGENT.check_child_processes_interval = False + self.process_monitor = external_process.ProcessMonitor( + self.mock_config, mock.ANY) + self.uuid = uuidutils.generate_uuid() + self.process_monitor.register( + self.uuid, keepalived.KEEPALIVED_SERVICE_NAME, mock.ANY) + self.keepalived_manager = keepalived.KeepalivedManager( + self.uuid, self.mock_config, self.process_monitor, mock.ANY) + self.mock_get_process = mock.patch.object(self.keepalived_manager, + 'get_process') + + def test_destroy(self): + mock_get_process = self.mock_get_process.start() + process = mock.Mock() + mock_get_process.return_value = process + process.active = False + self.keepalived_manager.disable() + process.disable.assert_called_once_with( + sig=str(int(signal.SIGTERM))) + + def test_destroy_force(self): + mock_get_process = self.mock_get_process.start() + with mock.patch.object(keepalived, 'SIGTERM_TIMEOUT', 0): + process = mock.Mock() + mock_get_process.return_value = process + process.active = True + self.keepalived_manager.disable() + process.disable.assert_has_calls([ + mock.call(sig=str(int(signal.SIGTERM))), + mock.call(sig=str(int(signal.SIGKILL)))])