From 55a25bdfaf066523e1faa4e8124e3c1c40add506 Mon Sep 17 00:00:00 2001 From: Daniel Alvarez Date: Thu, 22 Dec 2016 19:07:16 +0000 Subject: [PATCH] Fix a bug in process_spawn binding on ports process_spawn module is used in netns-cleanup functional tests. This module forks processes which will listen on random ports/sockets. Due to the lack of randomness on how get_free_namespace_port() from net_helpers was used, all forked processes got the same sequence of ports to bind on. This led to a race condition and some stayed alive without actually listening on any port/socket. Also, the ignore_sigterm feature wasn't handled properly and parent always died after a SIGTERM which caused the test to fail when the above condition happened since netns-cleanup wasn't able to find its parent and kill the process which failed to bind. Change-Id: I2f6e1f99eae595e3ea8c10998a7ad3c861a694f4 Closes-Bug: #1652124 --- neutron/tests/functional/cmd/process_spawn.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/neutron/tests/functional/cmd/process_spawn.py b/neutron/tests/functional/cmd/process_spawn.py index 7eddd1322e2..b3718c37997 100644 --- a/neutron/tests/functional/cmd/process_spawn.py +++ b/neutron/tests/functional/cmd/process_spawn.py @@ -13,6 +13,7 @@ # under the License. import os +import random import signal import socket import sys @@ -23,7 +24,6 @@ from oslo_config import cfg from neutron._i18n import _ from neutron.agent.linux import daemon -from neutron.tests.common import net_helpers UNIX_FAMILY = 'UNIX' @@ -77,7 +77,7 @@ class ProcessSpawn(daemon.Daemon): of the spawned processes. """ - MAX_BIND_RETRIES = 5 + MAX_BIND_RETRIES = 64 DCT_FAMILY = { n_const.IPv4: socket.AF_INET, @@ -110,6 +110,9 @@ class ProcessSpawn(daemon.Daemon): self.listen_socket = socket.socket(socket_family, socket_type) + # Set a different seed per process to increase randomness + random.seed(os.getpid()) + # Try to listen in a random port which is not currently in use retries = 0 while retries < ProcessSpawn.MAX_BIND_RETRIES: @@ -120,7 +123,8 @@ class ProcessSpawn(daemon.Daemon): if self.family == UNIX_FAMILY: self.listen_socket.bind('') else: - port = net_helpers.get_free_namespace_port(self.proto) + # Pick a non privileged port + port = random.randint(1024, 65535) self.listen_socket.bind(('', port)) except socket.error: retries += 1 @@ -146,8 +150,9 @@ class ProcessSpawn(daemon.Daemon): children.append(child_pid) # Install a SIGTERM handler if requested - if not self.ignore_sigterm: - signal.signal(signal.SIGTERM, self.sigterm_handler) + handler = ( + signal.SIG_IGN if self.ignore_sigterm else self.sigterm_handler) + signal.signal(signal.SIGTERM, handler) self.child_pids = children if self.parent_must_listen: