Adds protocol options for test_cross_tenant_traffic

Hyper-V cannot create stateful ICMP / ICMPv6 rules, but can create
stateful rules for any other protocol, thus, the test
test_cross_tenant_traffic cannot pass in the Hyper-V CI at the moment.
Testing other protocols instead can solve this issue.

Adds configuration option scenario.protocols, which contains the protocols
tested by the mentioned test. Its default option is 'icmp', in order
to maintain existing behaviour.

Change-Id: Ia304b81ec60a4fb06730f297e732a6b19a183f7f
Closes-Bug: #1363986
This commit is contained in:
Claudiu Belu 2014-08-28 16:38:01 +03:00
parent 7c8dd48f75
commit 33c3e60a3d
4 changed files with 108 additions and 24 deletions

View File

@ -155,3 +155,53 @@ class RemoteClient(remote_client.RemoteClient):
cmd_why = 'sudo ls -lR /dev'
LOG.info("Contents of /dev: %s", self.exec_command(cmd_why))
raise
def nc_listen_host(self, port=80, protocol='tcp'):
"""Creates persistent nc server listening on the given TCP / UDP port
:port: the port to start listening on.
:protocol: the protocol used by the server. TCP by default.
"""
udp = '-u' if protocol.lower() == 'udp' else ''
cmd = "sudo nc %(udp)s -p %(port)s -lk -e echo foolish &" % {
'udp': udp, 'port': port}
return self.exec_command(cmd)
def nc_host(self, host, port=80, protocol='tcp', expected_response=None):
"""Check connectivity to TCP / UDP port at host via nc
:host: an IP against which the connectivity will be tested.
:port: the port to check connectivity against.
:protocol: the protocol used by nc to send packets. TCP by default.
:expected_response: string representing the expected response
from server.
:raises SSHExecCommandFailed: if an expected response is given and it
does not match the actual server response.
"""
udp = '-u' if protocol.lower() == 'udp' else ''
cmd = 'echo "bar" | nc -w 1 %(udp)s %(host)s %(port)s' % {
'udp': udp, 'host': host, 'port': port}
response = self.exec_command(cmd)
# sending an UDP packet will always succeed. we need to check
# the response.
if (expected_response is not None and
expected_response != response.strip()):
raise tempest.lib.exceptions.SSHExecCommandFailed(
command=cmd, exit_status=0, stdout=response, stderr='')
return response
def icmp_check(self, host, nic=None):
"""Wrapper for icmp connectivity checks"""
return self.ping_host(host, nic=nic)
def udp_check(self, host, **kwargs):
"""Wrapper for udp connectivity checks."""
kwargs.pop('nic', None)
return self.nc_host(host, protocol='udp', expected_response='foolish',
**kwargs)
def tcp_check(self, host, **kwargs):
"""Wrapper for tcp connectivity checks."""
kwargs.pop('nic', None)
return self.nc_host(host, **kwargs)

View File

@ -1020,7 +1020,12 @@ ScenarioGroup = [
choices=["udhcpc", "dhclient", ""],
help='DHCP client used by images to renew DCHP lease. '
'If left empty, update operation will be skipped. '
'Supported clients: "udhcpc", "dhclient"')
'Supported clients: "udhcpc", "dhclient"'),
cfg.StrOpt('protocol',
default='icmp',
choices=('icmp', 'tcp', 'udp'),
help='The protocol used in security groups tests to check '
'connectivity.'),
]

View File

@ -881,24 +881,33 @@ class NetworkScenarioTest(ScenarioTest):
raise
def check_remote_connectivity(self, source, dest, should_succeed=True,
nic=None):
"""assert ping server via source ssh connection
nic=None, protocol='icmp'):
"""check server connectivity via source ssh connection
:param source: RemoteClient: an ssh connection from which to ping
:param dest: an IP to ping against
:param should_succeed: boolean: should ping succeed or not
:param nic: specific network interface to ping from
:param source: RemoteClient: an ssh connection from which to execute
the check
:param dest: an IP to check connectivity against
:param should_succeed: boolean should connection succeed or not
:param nic: specific network interface to test connectivity from
:param protocol: the protocol used to test connectivity with.
:returns: True, if the connection succeeded and it was expected to
succeed. False otherwise.
"""
def ping_remote():
method_name = '%s_check' % protocol
connectivity_checker = getattr(source, method_name)
def connect_remote():
try:
source.ping_host(dest, nic=nic)
connectivity_checker(dest, nic=nic)
except lib_exc.SSHExecCommandFailed:
LOG.warning('Failed to ping IP: %s via a ssh connection '
'from: %s.', dest, source.ssh_client.host)
LOG.warning('Failed to check %(protocol)s connectivity for '
'IP %(dest)s via a ssh connection from: %(src)s.',
dict(protocol=protocol, dest=dest,
src=source.ssh_client.host))
return not should_succeed
return should_succeed
result = test_utils.call_until_true(ping_remote,
result = test_utils.call_until_true(connect_remote,
CONF.validation.ping_timeout, 1)
source_host = source.ssh_client.host
if should_succeed:

View File

@ -395,24 +395,22 @@ class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
self.check_remote_connectivity(source=access_point_ssh,
dest=self._get_server_ip(server))
def _test_cross_tenant_block(self, source_tenant, dest_tenant):
def _test_cross_tenant_block(self, source_tenant, dest_tenant, ruleset):
# if public router isn't defined, then dest_tenant access is via
# floating-ip
protocol = ruleset['protocol']
access_point_ssh = self._connect_to_access_point(source_tenant)
ip = self._get_server_ip(dest_tenant.access_point,
floating=self.floating_ip_access)
self.check_remote_connectivity(source=access_point_ssh, dest=ip,
should_succeed=False)
should_succeed=False, protocol=protocol)
def _test_cross_tenant_allow(self, source_tenant, dest_tenant):
def _test_cross_tenant_allow(self, source_tenant, dest_tenant, ruleset):
"""check for each direction:
creating rule for tenant incoming traffic enables only 1way traffic
"""
ruleset = dict(
protocol='icmp',
direction='ingress'
)
protocol = ruleset['protocol']
sec_group_rules_client = (
dest_tenant.manager.security_group_rules_client)
self._create_security_group_rule(
@ -423,10 +421,10 @@ class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
access_point_ssh = self._connect_to_access_point(source_tenant)
ip = self._get_server_ip(dest_tenant.access_point,
floating=self.floating_ip_access)
self.check_remote_connectivity(access_point_ssh, ip)
self.check_remote_connectivity(access_point_ssh, ip, protocol=protocol)
# test that reverse traffic is still blocked
self._test_cross_tenant_block(dest_tenant, source_tenant)
self._test_cross_tenant_block(dest_tenant, source_tenant, ruleset)
# allow reverse traffic and check
sec_group_rules_client = (
@ -440,7 +438,8 @@ class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
access_point_ssh_2 = self._connect_to_access_point(dest_tenant)
ip = self._get_server_ip(source_tenant.access_point,
floating=self.floating_ip_access)
self.check_remote_connectivity(access_point_ssh_2, ip)
self.check_remote_connectivity(access_point_ssh_2, ip,
protocol=protocol)
def _verify_mac_addr(self, tenant):
"""Verify that VM has the same ip, mac as listed in port"""
@ -470,6 +469,17 @@ class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
self._log_console_output(
servers=[tenant.access_point], client=client)
def _create_protocol_ruleset(self, protocol, port=80):
if protocol == 'icmp':
ruleset = dict(protocol='icmp',
direction='ingress')
else:
ruleset = dict(protocol=protocol,
port_range_min=port,
port_range_max=port,
direction='ingress')
return ruleset
@decorators.idempotent_id('e79f879e-debb-440c-a7e4-efeda05b6848')
@utils.services('compute', 'network')
def test_cross_tenant_traffic(self):
@ -484,8 +494,18 @@ class TestSecurityGroupsBasicOps(manager.NetworkScenarioTest):
# cross tenant check
source_tenant = self.primary_tenant
dest_tenant = self.alt_tenant
self._test_cross_tenant_block(source_tenant, dest_tenant)
self._test_cross_tenant_allow(source_tenant, dest_tenant)
protocol = CONF.scenario.protocol
LOG.debug("Testing cross tenant traffic for %s protocol",
protocol)
if protocol in ['udp', 'tcp']:
for tenant in [source_tenant, dest_tenant]:
access_point = self._connect_to_access_point(tenant)
access_point.nc_listen_host(protocol=protocol)
ruleset = self._create_protocol_ruleset(protocol)
self._test_cross_tenant_block(source_tenant, dest_tenant, ruleset)
self._test_cross_tenant_allow(source_tenant, dest_tenant, ruleset)
except Exception:
self._log_console_output_for_all_tenants()
raise