From baf31fdeeed3226cb54474c1d42f543d4d0929bf Mon Sep 17 00:00:00 2001 From: Darragh O'Reilly Date: Tue, 10 Sep 2013 09:06:28 +0000 Subject: [PATCH] Ensure pid file is removed when metadata ns daemon receives SIGTERM These files from the metadata namespace proxy are not being removed because delete_pid() is registered with atexit. This means it only runs when a process exits normally and won't run when a process receives a signal. This patch registers a signal handler for SIGTERM that calls exit() to make the process exit normally so delete_pid() gets called. Fixes bug: 1223250 Change-Id: I6309802e2109359560ccc084559ec8e4d310cce2 --- neutron/agent/linux/daemon.py | 5 +++++ neutron/tests/unit/test_linux_daemon.py | 21 +++++++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/neutron/agent/linux/daemon.py b/neutron/agent/linux/daemon.py index 2825679a3a..3df12d580e 100644 --- a/neutron/agent/linux/daemon.py +++ b/neutron/agent/linux/daemon.py @@ -19,6 +19,7 @@ import atexit import fcntl import os +import signal import sys from neutron.agent.linux import utils @@ -123,11 +124,15 @@ class Daemon(object): # write pidfile atexit.register(self.delete_pid) + signal.signal(signal.SIGTERM, self.handle_sigterm) self.pidfile.write(os.getpid()) def delete_pid(self): os.remove(str(self.pidfile)) + def handle_sigterm(self, signum, frame): + sys.exit(0) + def start(self): """Start the daemon.""" diff --git a/neutron/tests/unit/test_linux_daemon.py b/neutron/tests/unit/test_linux_daemon.py index 2fc27a4aab..c4cf3223ce 100644 --- a/neutron/tests/unit/test_linux_daemon.py +++ b/neutron/tests/unit/test_linux_daemon.py @@ -160,13 +160,16 @@ class TestDaemon(base.BaseTestCase): d = daemon.Daemon('pidfile') with mock.patch.object(d, '_fork') as fork: with mock.patch.object(daemon, 'atexit') as atexit: - with mock.patch.object(daemon, 'sys') as sys: - sys.stdin.fileno.return_value = 0 - sys.stdout.fileno.return_value = 1 - sys.stderr.fileno.return_value = 2 - d.daemonize() - atexit.register.assert_called_once_with(d.delete_pid) + with mock.patch.object(daemon, 'signal') as signal: + signal.SIGTERM = 15 + with mock.patch.object(daemon, 'sys') as sys: + sys.stdin.fileno.return_value = 0 + sys.stdout.fileno.return_value = 1 + sys.stderr.fileno.return_value = 2 + d.daemonize() + signal.signal.assert_called_once_with(15, d.handle_sigterm) + atexit.register.assert_called_once_with(d.delete_pid) fork.assert_has_calls([mock.call(), mock.call()]) self.os.assert_has_calls([ @@ -185,6 +188,12 @@ class TestDaemon(base.BaseTestCase): d.delete_pid() self.os.remove.assert_called_once_with('pidfile') + def test_handle_sigterm(self): + d = daemon.Daemon('pidfile') + with mock.patch.object(daemon, 'sys') as sys: + d.handle_sigterm(15, 1234) + sys.exit.assert_called_once_with(0) + def test_start(self): self.pidfile.return_value.is_running.return_value = False d = daemon.Daemon('pidfile')