From: Jakub Libosvar Date: Fri, 5 Jun 2015 14:32:51 +0000 (+0000) Subject: Refactor NetcatTester class X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=b20fd81dbd497e058384a0af065dd0f1fdc4c728;p=openstack-build%2Fneutron-build.git 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 --- diff --git a/neutron/tests/fullstack/config_fixtures.py b/neutron/tests/fullstack/config_fixtures.py index b887b5ae1..ec1248d1b 100644 --- a/neutron/tests/fullstack/config_fixtures.py +++ b/neutron/tests/fullstack/config_fixtures.py @@ -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') diff --git a/neutron/tests/functional/agent/linux/helpers.py b/neutron/tests/functional/agent/linux/helpers.py index 83caf7984..92ac9c92b 100644 --- a/neutron/tests/functional/agent/linux/helpers.py +++ b/neutron/tests/functional/agent/linux/helpers.py @@ -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\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' - - def __init__(self, client_namespace, server_namespace, server_address, - port, client_address=None, udp=False): + TCP = constants.PROTO_NAME_TCP + UDP = constants.PROTO_NAME_UDP + + 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 diff --git a/neutron/tests/functional/agent/linux/test_iptables.py b/neutron/tests/functional/agent/linux/test_iptables.py index ebbf74ce3..3d78459ae 100644 --- a/neutron/tests/functional/agent/linux/test_iptables.py +++ b/neutron/tests/functional/agent/linux/test_iptables.py @@ -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): diff --git a/neutron/tests/functional/agent/test_l3_agent.py b/neutron/tests/functional/agent/test_l3_agent.py index 74d2e486f..b35fb074a 100644 --- a/neutron/tests/functional/agent/test_l3_agent.py +++ b/neutron/tests/functional/agent/test_l3_agent.py @@ -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())