Skip to content

Commit 7c7a99a

Browse files
Remove obsolete PID files before start
External processes, such as radvd, can refuse to start and throw an exception such as: "Unable to convert value in $pidfile" because the given pidfile has more than one PID in it. The situation can happen when the neutron node is reset and the obsolete PID files are not cleaned before neutron is started. This commit adds PID file cleanup before external process start. Closes-bug: #2033980 Change-Id: Id62bf18067d0b144c3e8825c7603cc1e51dca052 (cherry picked from commit c3b855a) Signed-off-by: Sven Kieske <[email protected]>
1 parent 98a3eae commit 7c7a99a

File tree

8 files changed

+77
-37
lines changed

8 files changed

+77
-37
lines changed

neutron/agent/linux/external_process.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,19 @@ def enable(self, cmd_callback=None, reload_cfg=False, ensure_active=False):
8787
if not self.active:
8888
if not cmd_callback:
8989
cmd_callback = self.default_cmd_callback
90-
cmd = cmd_callback(self.get_pid_file_name())
90+
# Always try and remove the pid file, as it's existence could
91+
# stop the process from starting
92+
pid_file = self.get_pid_file_name()
93+
try:
94+
utils.delete_if_exists(pid_file, run_as_root=self.run_as_root)
95+
except Exception as e:
96+
LOG.error("Could not delete file %(pid_file)s, %(service)s "
97+
"could fail to start. Exception: %(exc)s",
98+
{'pid_file': pid_file,
99+
'service': self.service,
100+
'exc': e})
101+
102+
cmd = cmd_callback(pid_file)
91103

92104
ip_wrapper = ip_lib.IPWrapper(namespace=self.namespace)
93105
ip_wrapper.netns.execute(cmd, addl_env=self.cmd_addl_env,
@@ -99,12 +111,14 @@ def enable(self, cmd_callback=None, reload_cfg=False, ensure_active=False):
99111

100112
def reload_cfg(self):
101113
if self.custom_reload_callback:
102-
self.disable(get_stop_command=self.custom_reload_callback)
114+
self.disable(get_stop_command=self.custom_reload_callback,
115+
delete_pid_file=False)
103116
else:
104-
self.disable('HUP')
117+
self.disable('HUP', delete_pid_file=False)
105118

106-
def disable(self, sig='9', get_stop_command=None):
119+
def disable(self, sig='9', get_stop_command=None, delete_pid_file=True):
107120
pid = self.pid
121+
delete_pid_file = delete_pid_file or sig == '9'
108122

109123
if self.active:
110124
if get_stop_command:
@@ -118,10 +132,10 @@ def disable(self, sig='9', get_stop_command=None):
118132
utils.execute(cmd, addl_env=self.cmd_addl_env,
119133
run_as_root=self.run_as_root,
120134
privsep_exec=True)
121-
# In the case of shutting down, remove the pid file
122-
if sig == '9':
123-
utils.delete_if_exists(self.get_pid_file_name(),
124-
run_as_root=self.run_as_root)
135+
136+
if delete_pid_file:
137+
utils.delete_if_exists(self.get_pid_file_name(),
138+
run_as_root=self.run_as_root)
125139
elif pid:
126140
LOG.debug('%(service)s process for %(uuid)s pid %(pid)d is stale, '
127141
'ignoring signal %(signal)s',

neutron/agent/linux/keepalived.py

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -427,15 +427,6 @@ def _output_config_file(self):
427427

428428
return config_path
429429

430-
@staticmethod
431-
def _safe_remove_pid_file(pid_file):
432-
try:
433-
os.remove(pid_file)
434-
except OSError as e:
435-
if e.errno != errno.ENOENT:
436-
LOG.error("Could not delete file %s, keepalived can "
437-
"refuse to start.", pid_file)
438-
439430
def get_vrrp_pid_file_name(self, base_pid_file):
440431
return '%s-vrrp' % base_pid_file
441432

@@ -516,9 +507,6 @@ def callback(pid_file):
516507
if vrrp_pm.active:
517508
vrrp_pm.disable()
518509

519-
self._safe_remove_pid_file(pid_file)
520-
self._safe_remove_pid_file(self.get_vrrp_pid_file_name(pid_file))
521-
522510
cmd = ['keepalived', '-P',
523511
'-f', config_path,
524512
'-p', pid_file,

neutron/agent/metadata/driver.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,8 @@ def destroy_monitored_metadata_proxy(cls, monitor, uuid, conf, ns_name):
288288
pm.pid, SIGTERM_TIMEOUT)
289289
pm.disable(sig=str(int(signal.SIGKILL)))
290290

291-
# Delete metadata proxy config and PID files.
291+
# Delete metadata proxy config.
292292
HaproxyConfigurator.cleanup_config_file(uuid, cfg.CONF.state_path)
293-
linux_utils.delete_if_exists(pm.get_pid_file_name(), run_as_root=True)
294293

295294
cls.monitors.pop(uuid, None)
296295

neutron/tests/unit/agent/l3/test_agent.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3267,8 +3267,9 @@ def test_external_gateway_removed_ext_gw_port_no_fip_ns(self):
32673267
self.assertFalse(ri.remove_floating_ip.called)
32683268

32693269
@mock.patch.object(os, 'geteuid', return_value=mock.ANY)
3270+
@mock.patch.object(linux_utils, 'delete_if_exists')
32703271
@mock.patch.object(pwd, 'getpwuid')
3271-
def test_spawn_radvd(self, mock_getpwuid, *args):
3272+
def test_spawn_radvd(self, mock_getpwuid, mock_delete, *args):
32723273
router = l3_test_common.prepare_router_data(
32733274
ip_version=lib_constants.IP_VERSION_6)
32743275

@@ -3304,6 +3305,8 @@ def test_spawn_radvd(self, mock_getpwuid, *args):
33043305
self.conf.set_override('radvd_user', radvd_user)
33053306
mock_getpwuid.return_value = FakeUser(os_user)
33063307
radvd.enable(router['_interfaces'])
3308+
mock_delete.assert_called_once_with(pidfile, run_as_root=True)
3309+
mock_delete.reset_mock()
33073310
cmd = execute.call_args[0][0]
33083311
_join = lambda *args: ' '.join(args)
33093312
cmd = _join(*cmd)

neutron/tests/unit/agent/l3/test_ha_router.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def setUp(self):
3535
self.device_exists_p = mock.patch(
3636
'neutron.agent.linux.ip_lib.device_exists')
3737
self.device_exists = self.device_exists_p.start()
38+
self.delete_if_exists_p = mock.patch(
39+
'neutron.agent.linux.utils.delete_if_exists')
40+
self.delete_if_exists = self.delete_if_exists_p.start()
3841

3942
def _create_router(self, router=None, **kwargs):
4043
if not router:

neutron/tests/unit/agent/linux/test_external_process.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,11 @@ def setUp(self):
120120
self.execute_p = mock.patch('neutron.agent.common.utils.execute')
121121
self.execute = self.execute_p.start()
122122
self.delete_if_exists = mock.patch(
123-
'oslo_utils.fileutils.delete_if_exists').start()
123+
'neutron.agent.linux.utils.delete_if_exists').start()
124124
self.ensure_dir = mock.patch.object(
125125
fileutils, 'ensure_tree').start()
126+
self.error_log = mock.patch("neutron.agent.linux.external_process."
127+
"LOG.error").start()
126128

127129
self.conf = mock.Mock()
128130
self.conf.external_pids = '/var/path'
@@ -167,6 +169,8 @@ def test_enable_with_namespace(self):
167169
with mock.patch.object(ep, 'ip_lib') as ip_lib:
168170
manager.enable(callback)
169171
callback.assert_called_once_with('pidfile')
172+
self.delete_if_exists.assert_called_once_with(
173+
'pidfile', run_as_root=True)
170174
env = {ep.PROCESS_TAG: ep.DEFAULT_SERVICE_NAME + '-uuid'}
171175
ip_lib.assert_has_calls([
172176
mock.call.IPWrapper(namespace='ns'),
@@ -253,11 +257,34 @@ def _create_cmd(*args):
253257
(service_name, uuid))
254258
self.assertEqual(expected_value, ret_value)
255259

260+
def test_enable_delete_pid_file_raises(self):
261+
callback = mock.Mock()
262+
cmd = ['the', 'cmd']
263+
callback.return_value = cmd
264+
self.delete_if_exists.side_effect = OSError
265+
266+
with mock.patch.object(ep.ProcessManager, 'get_pid_file_name') as name:
267+
name.return_value = 'pidfile'
268+
with mock.patch.object(ep.ProcessManager, 'active') as active:
269+
active.__get__ = mock.Mock(return_value=False)
270+
271+
manager = ep.ProcessManager(self.conf, 'uuid')
272+
manager.enable(callback)
273+
callback.assert_called_once_with('pidfile')
274+
cmd = ['env', DEFAULT_ENVVAR + '-uuid'] + cmd
275+
self.execute.assert_called_once_with(cmd,
276+
check_exit_code=True,
277+
extra_ok_codes=None,
278+
run_as_root=False,
279+
log_fail_as_error=True,
280+
privsep_exec=False)
281+
self.error_log.assert_called_once()
282+
256283
def test_reload_cfg_without_custom_reload_callback(self):
257284
with mock.patch.object(ep.ProcessManager, 'disable') as disable:
258285
manager = ep.ProcessManager(self.conf, 'uuid', namespace='ns')
259286
manager.reload_cfg()
260-
disable.assert_called_once_with('HUP')
287+
disable.assert_called_once_with('HUP', delete_pid_file=False)
261288

262289
def test_reload_cfg_with_custom_reload_callback(self):
263290
reload_callback = mock.sentinel.callback
@@ -266,7 +293,8 @@ def test_reload_cfg_with_custom_reload_callback(self):
266293
self.conf, 'uuid', namespace='ns',
267294
custom_reload_callback=reload_callback)
268295
manager.reload_cfg()
269-
disable.assert_called_once_with(get_stop_command=reload_callback)
296+
disable.assert_called_once_with(get_stop_command=reload_callback,
297+
delete_pid_file=False)
270298

271299
def test_disable_get_stop_command(self):
272300
cmd = ['the', 'cmd']

neutron/tests/unit/agent/metadata/test_driver.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def setUp(self):
9393
mock.patch('neutron.agent.l3.ha.AgentMixin'
9494
'._init_ha_conf_path').start()
9595
self.delete_if_exists = mock.patch.object(linux_utils,
96-
'delete_if_exists')
96+
'delete_if_exists').start()
9797
self.mock_get_process = mock.patch.object(
9898
metadata_driver.MetadataDriver,
9999
'_get_metadata_proxy_process_manager')
@@ -239,6 +239,9 @@ def _test_spawn_metadata_proxy(self, dad_failed=False):
239239
router_id, metadata_driver.METADATA_SERVICE_NAME,
240240
mock.ANY)
241241

242+
self.delete_if_exists.assert_called_once_with(
243+
mock.ANY, run_as_root=True)
244+
242245
def test_spawn_metadata_proxy(self):
243246
self._test_spawn_metadata_proxy()
244247

@@ -270,23 +273,19 @@ def test_create_config_file_wrong_group(self):
270273
config.create_config_file)
271274

272275
def test_destroy_monitored_metadata_proxy(self):
273-
delete_if_exists = self.delete_if_exists.start()
274-
mproxy_process = mock.Mock(
275-
active=False, get_pid_file_name=mock.Mock(return_value='pid_file'))
276+
mproxy_process = mock.Mock(active=False)
276277
mock_get_process = self.mock_get_process.start()
277278
mock_get_process.return_value = mproxy_process
278279
driver = metadata_driver.MetadataDriver(FakeL3NATAgent())
279280
driver.destroy_monitored_metadata_proxy(mock.Mock(), 'uuid', 'conf',
280281
'ns_name')
281282
mproxy_process.disable.assert_called_once_with(
282283
sig=str(int(signal.SIGTERM)))
283-
delete_if_exists.assert_has_calls([
284-
mock.call('pid_file', run_as_root=True)])
284+
self.delete_if_exists.assert_called_once_with(
285+
mock.ANY, run_as_root=True)
285286

286287
def test_destroy_monitored_metadata_proxy_force(self):
287-
delete_if_exists = self.delete_if_exists.start()
288-
mproxy_process = mock.Mock(
289-
active=True, get_pid_file_name=mock.Mock(return_value='pid_file'))
288+
mproxy_process = mock.Mock(active=True)
290289
mock_get_process = self.mock_get_process.start()
291290
mock_get_process.return_value = mproxy_process
292291
driver = metadata_driver.MetadataDriver(FakeL3NATAgent())
@@ -296,5 +295,5 @@ def test_destroy_monitored_metadata_proxy_force(self):
296295
mproxy_process.disable.assert_has_calls([
297296
mock.call(sig=str(int(signal.SIGTERM))),
298297
mock.call(sig=str(int(signal.SIGKILL)))])
299-
delete_if_exists.assert_has_calls([
300-
mock.call('pid_file', run_as_root=True)])
298+
self.delete_if_exists.assert_called_once_with(
299+
mock.ANY, run_as_root=True)

neutron/tests/unit/agent/ovn/metadata/test_driver.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from oslo_utils import uuidutils
2222

2323
from neutron.agent.linux import external_process as ep
24+
from neutron.agent.linux import utils as linux_utils
2425
from neutron.agent.ovn.metadata import agent as metadata_agent
2526
from neutron.agent.ovn.metadata import driver as metadata_driver
2627
from neutron.common import metadata as comm_meta
@@ -45,6 +46,8 @@ class TestMetadataDriverProcess(base.BaseTestCase):
4546
def setUp(self):
4647
super(TestMetadataDriverProcess, self).setUp()
4748
mock.patch('eventlet.spawn').start()
49+
self.delete_if_exists = mock.patch.object(linux_utils,
50+
'delete_if_exists').start()
4851

4952
ovn_meta_conf.register_meta_conf_opts(meta_conf.SHARED_OPTS, cfg.CONF)
5053
ovn_conf.register_opts()
@@ -117,6 +120,9 @@ def test_spawn_metadata_proxy(self):
117120
run_as_root=True)
118121
])
119122

123+
self.delete_if_exists.assert_called_once_with(
124+
mock.ANY, run_as_root=True)
125+
120126
def test_create_config_file_wrong_user(self):
121127
with mock.patch('pwd.getpwnam', side_effect=KeyError):
122128
config = metadata_driver.HaproxyConfigurator(mock.ANY, mock.ANY,

0 commit comments

Comments
 (0)