
Fix H404 error and start enforcing it. Trivialfix Change-Id: Iaa6fb4f1f07dee32a944259ab65204360d9db7ea
184 lines
5.9 KiB
Python
184 lines
5.9 KiB
Python
# Copyright 2016 Red Hat, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import os
|
|
import random
|
|
import signal
|
|
import socket
|
|
import sys
|
|
import time
|
|
|
|
from neutron_lib import constants as n_const
|
|
from oslo_config import cfg
|
|
|
|
from neutron.agent.linux import daemon
|
|
|
|
UNIX_FAMILY = 'UNIX'
|
|
|
|
OPTS = [
|
|
cfg.IntOpt('num_children',
|
|
short='n',
|
|
default=0,
|
|
help='Number of children to spawn',
|
|
required=False),
|
|
cfg.StrOpt('family',
|
|
short='f',
|
|
default=n_const.IPv4,
|
|
choices=[n_const.IPv4, n_const.IPv6, UNIX_FAMILY],
|
|
help='Listen socket family (%(v4)s, %(v6)s or %(unix)s)' %
|
|
{
|
|
'v4': n_const.IPv4,
|
|
'v6': n_const.IPv6,
|
|
'unix': UNIX_FAMILY
|
|
},
|
|
required=False),
|
|
cfg.StrOpt('proto',
|
|
short='p',
|
|
default=n_const.PROTO_NAME_TCP,
|
|
choices=[n_const.PROTO_NAME_TCP, n_const.PROTO_NAME_UDP],
|
|
help='Protocol (%(tcp)s or %(udp)s)' %
|
|
{
|
|
'tcp': n_const.PROTO_NAME_TCP,
|
|
'udp': n_const.PROTO_NAME_UDP
|
|
},
|
|
required=False),
|
|
cfg.BoolOpt('parent_listen',
|
|
short='pl',
|
|
default=True,
|
|
help='Parent process must listen too',
|
|
required=False),
|
|
cfg.BoolOpt('ignore_sigterm',
|
|
short='i',
|
|
default=False,
|
|
help='Ignore SIGTERM',
|
|
required=False)
|
|
]
|
|
|
|
|
|
class ProcessSpawn(daemon.Daemon):
|
|
"""This class is part of the functional test of the netns_cleanup module.
|
|
|
|
It allows spawning processes that listen on random ports either on
|
|
tcp(6), udp(6) or unix sockets. Also it allows handling or ignoring
|
|
SIGTERM to check whether the cleanup works as expected by getting rid
|
|
of the spawned processes.
|
|
"""
|
|
|
|
MAX_BIND_RETRIES = 64
|
|
|
|
DCT_FAMILY = {
|
|
n_const.IPv4: socket.AF_INET,
|
|
n_const.IPv6: socket.AF_INET6,
|
|
UNIX_FAMILY: socket.AF_UNIX
|
|
}
|
|
DCT_PROTO = {
|
|
n_const.PROTO_NAME_TCP: socket.SOCK_STREAM,
|
|
n_const.PROTO_NAME_UDP: socket.SOCK_DGRAM,
|
|
}
|
|
|
|
def __init__(self, pidfile=None,
|
|
family=n_const.IPv4,
|
|
proto=n_const.PROTO_NAME_TCP,
|
|
ignore_sigterm=False, num_children=0,
|
|
parent_must_listen=True):
|
|
self.family = family
|
|
self.proto = proto
|
|
self.ignore_sigterm = ignore_sigterm
|
|
self.num_children = num_children
|
|
self.listen_socket = None
|
|
self.parent_must_listen = parent_must_listen
|
|
self.child_pids = []
|
|
|
|
super(ProcessSpawn, self).__init__(pidfile)
|
|
|
|
def start_listening(self):
|
|
socket_family = self.DCT_FAMILY[self.family]
|
|
socket_type = self.DCT_PROTO[self.proto]
|
|
|
|
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:
|
|
# NOTE(dalvarez): not finding a free port on a freshly created
|
|
# namespace is very unlikely but if problems show up, retries can
|
|
# be increased to avoid tests failing
|
|
try:
|
|
if self.family == UNIX_FAMILY:
|
|
self.listen_socket.bind('')
|
|
else:
|
|
# Pick a non privileged port
|
|
port = random.randint(1024, 65535)
|
|
self.listen_socket.bind(('', port))
|
|
except socket.error:
|
|
retries += 1
|
|
else:
|
|
if n_const.PROTO_NAME_TCP in self.proto:
|
|
self.listen_socket.listen(0)
|
|
break
|
|
|
|
def do_sleep(self):
|
|
while True:
|
|
time.sleep(10)
|
|
|
|
def run(self):
|
|
# Spawn as many children as requested
|
|
children = []
|
|
while len(children) != self.num_children:
|
|
child_pid = os.fork()
|
|
if child_pid == 0:
|
|
# Listen and do nothing else
|
|
self.start_listening()
|
|
self.do_sleep()
|
|
return
|
|
children.append(child_pid)
|
|
|
|
# Install a SIGTERM handler if requested
|
|
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:
|
|
self.start_listening()
|
|
self.do_sleep()
|
|
|
|
def sigterm_handler(self, signum, frame):
|
|
if self.listen_socket:
|
|
self.listen_socket.close()
|
|
for child in self.child_pids:
|
|
try:
|
|
os.kill(child, signal.SIGTERM)
|
|
except OSError:
|
|
pass
|
|
sys.exit(0)
|
|
|
|
|
|
def main():
|
|
cfg.CONF.register_cli_opts(OPTS)
|
|
cfg.CONF(project='neutron', default_config_files=[])
|
|
proc_spawn = ProcessSpawn(num_children=cfg.CONF.num_children,
|
|
family=cfg.CONF.family,
|
|
proto=cfg.CONF.proto,
|
|
parent_must_listen=cfg.CONF.parent_listen,
|
|
ignore_sigterm=cfg.CONF.ignore_sigterm)
|
|
proc_spawn.start()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|