Refactor NetcatTester class
Following capabilities were added: - used transport protocol is passed as a constant instead of bool - src port for testing was added - connection can be established explicitly - change constructor parameters of NetcatTester As a part of removing bool for protocol definition get_free_namespace_port() was also modified to match the behavior. Change-Id: Id2ec322e7f731c05a3754a65411c9a5d8b258126
This commit is contained in:
parent
83e37980dc
commit
b20fd81dbd
|
@ -140,7 +140,7 @@ class NeutronConfigFixture(ConfigFixture):
|
|||
This might fail if some other process occupies this port after this
|
||||
function finished but before the neutron-server process started.
|
||||
"""
|
||||
return str(helpers.get_free_namespace_port())
|
||||
return str(helpers.get_free_namespace_port(constants.PROTO_NAME_TCP))
|
||||
|
||||
def _generate_api_paste(self):
|
||||
return c_helpers.find_sample_file('api-paste.ini')
|
||||
|
|
|
@ -23,6 +23,7 @@ import subprocess
|
|||
from neutron.agent.common import config
|
||||
from neutron.agent.linux import ip_lib
|
||||
from neutron.agent.linux import utils
|
||||
from neutron.common import constants
|
||||
from neutron.tests import tools
|
||||
|
||||
CHILD_PROCESS_TIMEOUT = os.environ.get('OS_TEST_CHILD_PROCESS_TIMEOUT', 20)
|
||||
|
@ -32,6 +33,8 @@ READ_TIMEOUT = os.environ.get('OS_TEST_READ_TIMEOUT', 5)
|
|||
SS_SOURCE_PORT_PATTERN = re.compile(
|
||||
r'^.*\s+\d+\s+.*:(?P<port>\d+)\s+[0-9:].*')
|
||||
|
||||
TRANSPORT_PROTOCOLS = (constants.PROTO_NAME_TCP, constants.PROTO_NAME_UDP)
|
||||
|
||||
|
||||
class RecursivePermDirFixture(tools.SafeFixture):
|
||||
"""Ensure at least perms permissions on directory and ancestors."""
|
||||
|
@ -53,7 +56,7 @@ class RecursivePermDirFixture(tools.SafeFixture):
|
|||
current_directory = os.path.dirname(current_directory)
|
||||
|
||||
|
||||
def get_free_namespace_port(tcp=True, namespace=None):
|
||||
def get_free_namespace_port(protocol, namespace=None):
|
||||
"""Return an unused port from given namespace
|
||||
|
||||
WARNING: This function returns a port that is free at the execution time of
|
||||
|
@ -61,13 +64,15 @@ def get_free_namespace_port(tcp=True, namespace=None):
|
|||
is a potential danger that port will be no longer free. It's up to
|
||||
the programmer to handle error if port is already in use.
|
||||
|
||||
:param tcp: Return free port for TCP protocol if set to True, return free
|
||||
port for UDP protocol if set to False
|
||||
:param protocol: Return free port for given protocol. Supported protocols
|
||||
are 'tcp' and 'udp'.
|
||||
"""
|
||||
if tcp:
|
||||
if protocol == constants.PROTO_NAME_TCP:
|
||||
param = '-tna'
|
||||
else:
|
||||
elif protocol == constants.PROTO_NAME_UDP:
|
||||
param = '-una'
|
||||
else:
|
||||
raise ValueError("Unsupported procotol %s" % protocol)
|
||||
|
||||
ip_wrapper = ip_lib.IPWrapper(namespace=namespace)
|
||||
output = ip_wrapper.netns.execute(['ss', param])
|
||||
|
@ -145,29 +150,47 @@ class RootHelperProcess(subprocess.Popen):
|
|||
|
||||
class NetcatTester(object):
|
||||
TESTING_STRING = 'foo'
|
||||
TCP = constants.PROTO_NAME_TCP
|
||||
UDP = constants.PROTO_NAME_UDP
|
||||
|
||||
def __init__(self, client_namespace, server_namespace, server_address,
|
||||
port, client_address=None, udp=False):
|
||||
def __init__(self, client_namespace, server_namespace, address,
|
||||
dst_port, protocol, server_address='0.0.0.0', src_port=None):
|
||||
"""
|
||||
Tool for testing connectivity on transport layer using netcat
|
||||
executable.
|
||||
|
||||
The processes are spawned lazily.
|
||||
|
||||
:param client_namespace: Namespace in which netcat process that
|
||||
connects to other netcat will be spawned
|
||||
:param server_namespace: Namespace in which listening netcat process
|
||||
will be spawned
|
||||
:param address: Server address from client point of view
|
||||
:param dst_port: Port on which netcat listens
|
||||
:param protocol: Transport protocol, either 'tcp' or 'udp'
|
||||
:param server_address: Address in server namespace on which netcat
|
||||
should listen
|
||||
:param src_port: Source port of netcat process spawned in client
|
||||
namespace - packet will have src_port in TCP/UDP
|
||||
header with this value
|
||||
|
||||
"""
|
||||
self.client_namespace = client_namespace
|
||||
self.server_namespace = server_namespace
|
||||
self._client_process = None
|
||||
self._server_process = None
|
||||
# Use client_address to specify an address to connect client to that is
|
||||
# different from the one that server side is going to listen on (useful
|
||||
# when testing floating IPs)
|
||||
self.client_address = client_address or server_address
|
||||
self.address = address
|
||||
self.server_address = server_address
|
||||
self.port = str(port)
|
||||
self.udp = udp
|
||||
self.dst_port = str(dst_port)
|
||||
self.src_port = str(src_port) if src_port else None
|
||||
if protocol not in TRANSPORT_PROTOCOLS:
|
||||
raise ValueError("Unsupported protocol %s" % protocol)
|
||||
self.protocol = protocol
|
||||
|
||||
@property
|
||||
def client_process(self):
|
||||
if not self._client_process:
|
||||
if not self._server_process:
|
||||
self._spawn_server_process()
|
||||
self._client_process = self._spawn_nc_in_namespace(
|
||||
self.client_namespace,
|
||||
address=self.client_address)
|
||||
self.establish_connection()
|
||||
return self._client_process
|
||||
|
||||
@property
|
||||
|
@ -182,6 +205,22 @@ class NetcatTester(object):
|
|||
address=self.server_address,
|
||||
listen=True)
|
||||
|
||||
def establish_connection(self):
|
||||
if self._client_process:
|
||||
raise RuntimeError('%(proto)s connection to $(ip_addr)s is already'
|
||||
' established' %
|
||||
{'proto': self.protocol,
|
||||
'ip_addr': self.address})
|
||||
|
||||
if not self._server_process:
|
||||
self._spawn_server_process()
|
||||
self._client_process = self._spawn_nc_in_namespace(
|
||||
self.client_namespace,
|
||||
address=self.address)
|
||||
if self.protocol == self.UDP:
|
||||
# Create an entry in conntrack table for UDP packets
|
||||
self.client_process.writeline(self.TESTING_STRING)
|
||||
|
||||
def test_connectivity(self, respawn=False):
|
||||
stop_required = (respawn and self._client_process and
|
||||
self._client_process.poll() is not None)
|
||||
|
@ -196,15 +235,17 @@ class NetcatTester(object):
|
|||
return message == self.TESTING_STRING
|
||||
|
||||
def _spawn_nc_in_namespace(self, namespace, address, listen=False):
|
||||
cmd = ['nc', address, self.port]
|
||||
if self.udp:
|
||||
cmd = ['nc', address, self.dst_port]
|
||||
if self.protocol == self.UDP:
|
||||
cmd.append('-u')
|
||||
if listen:
|
||||
cmd.append('-l')
|
||||
if not self.udp:
|
||||
if self.protocol == self.TCP:
|
||||
cmd.append('-k')
|
||||
else:
|
||||
cmd.extend(['-w', '20'])
|
||||
if self.src_port:
|
||||
cmd.extend(['-p', self.src_port])
|
||||
proc = RootHelperProcess(cmd, namespace=namespace)
|
||||
return proc
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import testtools
|
|||
|
||||
from neutron.agent.linux import iptables_manager
|
||||
from neutron.agent.linux import utils
|
||||
from neutron.common import constants
|
||||
from neutron.tests import base
|
||||
from neutron.tests.common import machine_fixtures
|
||||
from neutron.tests.common import net_helpers
|
||||
|
@ -43,7 +44,8 @@ class IptablesManagerTestCase(functional_base.BaseSudoTestCase):
|
|||
self.client_fw, self.server_fw = self.create_firewalls()
|
||||
# The port is used in isolated namespace that precludes possibility of
|
||||
# port conflicts
|
||||
self.port = helpers.get_free_namespace_port(self.server.namespace)
|
||||
self.port = helpers.get_free_namespace_port(constants.PROTO_NAME_TCP,
|
||||
self.server.namespace)
|
||||
|
||||
def create_firewalls(self):
|
||||
client_iptables = iptables_manager.IptablesManager(
|
||||
|
@ -77,14 +79,11 @@ class IptablesManagerTestCase(functional_base.BaseSudoTestCase):
|
|||
rule = self.PROTOCOL_BLOCK_RULE % protocol
|
||||
return chain, rule
|
||||
|
||||
def _test_with_nc(self, fw_manager, direction, port, udp):
|
||||
def _test_with_nc(self, fw_manager, direction, port, protocol):
|
||||
netcat = helpers.NetcatTester(
|
||||
self.client.namespace, self.server.namespace,
|
||||
self.server.ip, self.port, udp=udp)
|
||||
self.server.ip, self.port, protocol)
|
||||
self.addCleanup(netcat.stop_processes)
|
||||
protocol = 'tcp'
|
||||
if udp:
|
||||
protocol = 'udp'
|
||||
self.assertTrue(netcat.test_connectivity())
|
||||
self.filter_add_rule(
|
||||
fw_manager, self.server.ip, direction, protocol, port)
|
||||
|
@ -121,28 +120,36 @@ class IptablesManagerTestCase(functional_base.BaseSudoTestCase):
|
|||
self.client.assert_ping(self.server.ip)
|
||||
|
||||
def test_tcp_input_port(self):
|
||||
self._test_with_nc(self.server_fw, 'ingress', self.port, udp=False)
|
||||
self._test_with_nc(self.server_fw, 'ingress', self.port,
|
||||
protocol=helpers.NetcatTester.TCP)
|
||||
|
||||
def test_tcp_output_port(self):
|
||||
self._test_with_nc(self.client_fw, 'egress', self.port, udp=False)
|
||||
self._test_with_nc(self.client_fw, 'egress', self.port,
|
||||
protocol=helpers.NetcatTester.TCP)
|
||||
|
||||
def test_tcp_input(self):
|
||||
self._test_with_nc(self.server_fw, 'ingress', port=None, udp=False)
|
||||
self._test_with_nc(self.server_fw, 'ingress', port=None,
|
||||
protocol=helpers.NetcatTester.TCP)
|
||||
|
||||
def test_tcp_output(self):
|
||||
self._test_with_nc(self.client_fw, 'egress', port=None, udp=False)
|
||||
self._test_with_nc(self.client_fw, 'egress', port=None,
|
||||
protocol=helpers.NetcatTester.TCP)
|
||||
|
||||
def test_udp_input_port(self):
|
||||
self._test_with_nc(self.server_fw, 'ingress', self.port, udp=True)
|
||||
self._test_with_nc(self.server_fw, 'ingress', self.port,
|
||||
protocol=helpers.NetcatTester.UDP)
|
||||
|
||||
def test_udp_output_port(self):
|
||||
self._test_with_nc(self.client_fw, 'egress', self.port, udp=True)
|
||||
self._test_with_nc(self.client_fw, 'egress', self.port,
|
||||
protocol=helpers.NetcatTester.UDP)
|
||||
|
||||
def test_udp_input(self):
|
||||
self._test_with_nc(self.server_fw, 'ingress', port=None, udp=True)
|
||||
self._test_with_nc(self.server_fw, 'ingress', port=None,
|
||||
protocol=helpers.NetcatTester.UDP)
|
||||
|
||||
def test_udp_output(self):
|
||||
self._test_with_nc(self.client_fw, 'egress', port=None, udp=True)
|
||||
self._test_with_nc(self.client_fw, 'egress', port=None,
|
||||
protocol=helpers.NetcatTester.UDP)
|
||||
|
||||
|
||||
class IptablesManagerNonRootTestCase(base.BaseTestCase):
|
||||
|
|
|
@ -400,7 +400,8 @@ class L3AgentTestCase(L3AgentTestFramework):
|
|||
router_info = self.generate_router_info(enable_ha=False)
|
||||
router = self.manage_router(self.agent, router_info)
|
||||
|
||||
port = helpers.get_free_namespace_port(router.ns_name)
|
||||
port = helpers.get_free_namespace_port(l3_constants.PROTO_NAME_TCP,
|
||||
router.ns_name)
|
||||
client_address = '19.4.4.3'
|
||||
server_address = '35.4.0.4'
|
||||
|
||||
|
@ -413,9 +414,8 @@ class L3AgentTestCase(L3AgentTestFramework):
|
|||
|
||||
router_ns = ip_lib.IPWrapper(namespace=router.ns_name)
|
||||
netcat = helpers.NetcatTester(router.ns_name, router.ns_name,
|
||||
server_address, port,
|
||||
client_address=client_address,
|
||||
udp=False)
|
||||
client_address, port,
|
||||
protocol=helpers.NetcatTester.TCP)
|
||||
self.addCleanup(netcat.stop_processes)
|
||||
|
||||
def assert_num_of_conntrack_rules(n):
|
||||
|
@ -705,12 +705,13 @@ class L3AgentTestCase(L3AgentTestFramework):
|
|||
self._add_fip(router, dst_fip, fixed_address=dst_machine.ip)
|
||||
router.process(self.agent)
|
||||
|
||||
protocol_port = helpers.get_free_namespace_port(dst_machine.namespace)
|
||||
protocol_port = helpers.get_free_namespace_port(
|
||||
l3_constants.PROTO_NAME_TCP, dst_machine.namespace)
|
||||
# client sends to fip
|
||||
netcat = helpers.NetcatTester(
|
||||
src_machine.namespace, dst_machine.namespace,
|
||||
dst_machine.ip, protocol_port, client_address=dst_fip,
|
||||
udp=False)
|
||||
dst_fip, protocol_port,
|
||||
protocol=helpers.NetcatTester.TCP)
|
||||
self.addCleanup(netcat.stop_processes)
|
||||
self.assertTrue(netcat.test_connectivity())
|
||||
|
||||
|
|
Loading…
Reference in New Issue