# License for the specific language governing permissions and limitations
# under the License.
+import signal
+
import eventlet
import eventlet.event
import eventlet.queue
if block:
utils.wait_until_true(self.is_active)
- def stop(self, block=False):
+ def stop(self, block=False, kill_signal=signal.SIGKILL):
"""Halt the process and watcher threads.
:param block: Block until the process has stopped.
+ :param kill_signal: Number of signal that will be sent to the process
+ when terminating the process
:raises eventlet.timeout.Timeout if blocking is True and the process
did not stop in time.
"""
if self._kill_event:
LOG.debug('Halting async process [%s].', self.cmd)
- self._kill()
+ self._kill(kill_signal)
else:
raise AsyncProcessException(_('Process is not running.'))
self._process.pid,
run_as_root=self.run_as_root)
- def _kill(self, respawning=False):
+ def _kill(self, kill_signal, respawning=False):
"""Kill the process and the associated watcher greenthreads.
:param respawning: Optional, whether respawn will be subsequently
pid = self.pid
if pid:
- self._kill_process(pid)
+ self._kill_process(pid, kill_signal)
if not respawning:
# Clear the kill event to ensure the process can be
# explicitly started again.
self._kill_event = None
- def _kill_process(self, pid):
+ def _kill_process(self, pid, kill_signal):
try:
# A process started by a root helper will be running as
# root and need to be killed via the same helper.
- utils.execute(['kill', '-9', pid], run_as_root=self.run_as_root)
+ utils.execute(['kill', '-%d' % kill_signal, pid],
+ run_as_root=self.run_as_root)
except Exception as ex:
stale_pid = (isinstance(ex, RuntimeError) and
'No such process' in str(ex))
respawning = True
else:
respawning = False
- self._kill(respawning=respawning)
+ self._kill(signal.SIGKILL, respawning=respawning)
if respawning:
eventlet.sleep(self.respawn_interval)
LOG.debug('Respawning async process [%s].', self.cmd)
# License for the specific language governing permissions and limitations
# under the License.
+import signal
+
import eventlet.event
import eventlet.queue
import eventlet.timeout
with mock.patch.object(self.proc, '_kill') as kill:
self.proc._handle_process_error()
- kill.assert_has_calls([mock.call(respawning=False)])
+ kill.assert_has_calls([mock.call(signal.SIGKILL, respawning=False)])
def test__handle_process_error_kills_without_respawn(self):
self.proc.respawn_interval = 1
with mock.patch('eventlet.sleep') as sleep:
self.proc._handle_process_error()
- kill.assert_has_calls([mock.call(respawning=True)])
+ kill.assert_has_calls([mock.call(signal.SIGKILL, respawning=True)])
sleep.assert_has_calls([mock.call(self.proc.respawn_interval)])
spawn.assert_called_once_with()
mock.patch.object(self.proc, '_kill_process'
) as mock_kill_process,\
mock.patch.object(self.proc, '_process'):
- self.proc._kill(respawning)
+ self.proc._kill(signal.SIGKILL, respawning)
if respawning:
self.assertIsNotNone(self.proc._kill_event)
mock_kill_event.send.assert_called_once_with()
if pid:
- mock_kill_process.assert_called_once_with(pid)
+ mock_kill_process.assert_called_once_with(pid, signal.SIGKILL)
def test__kill_when_respawning_does_not_clear_kill_event(self):
self._test__kill(True)
def test__kill_targets_process_for_pid(self):
self._test__kill(False, pid='1')
- def _test__kill_process(self, pid, expected, exception_message=None):
+ def _test__kill_process(self, pid, expected, exception_message=None,
+ kill_signal=signal.SIGKILL):
self.proc.run_as_root = True
if exception_message:
exc = RuntimeError(exception_message)
exc = None
with mock.patch.object(utils, 'execute',
side_effect=exc) as mock_execute:
- actual = self.proc._kill_process(pid)
+ actual = self.proc._kill_process(pid, kill_signal)
self.assertEqual(expected, actual)
- mock_execute.assert_called_with(['kill', '-9', pid],
+ mock_execute.assert_called_with(['kill', '-%d' % kill_signal, pid],
run_as_root=self.proc.run_as_root)
def test__kill_process_returns_true_for_valid_pid(self):
def test__kill_process_returns_false_for_execute_exception(self):
self._test__kill_process('1', False, 'Invalid')
- def test_stop_calls_kill(self):
+ def test_kill_process_with_different_signal(self):
+ self._test__kill_process('1', True, kill_signal=signal.SIGTERM)
+
+ def test_stop_calls_kill_with_provided_signal_number(self):
self.proc._kill_event = True
with mock.patch.object(self.proc, '_kill') as mock_kill:
- self.proc.stop()
- mock_kill.assert_called_once_with()
+ self.proc.stop(kill_signal=signal.SIGTERM)
+ mock_kill.assert_called_once_with(signal.SIGTERM)
def test_stop_raises_exception_if_already_started(self):
with testtools.ExpectedException(async_process.AsyncProcessException):