Enhance the test_multicast_between_vms_on_same_network test

This patch is enhancing the test_multicast_between_vms_on_same_network
test to fit the different scenarios that can be encountered when using
multicast:

1) When IGMP snooping is enabled and the multicast group address *is not*
   in the 224.0.0.X range [0], asserts that the multicast traffic *is not*
   flooded.

2) When IGMP snooping is not enabled, asserts that the multicast traffic
   is flooded and the unregistered VM gets it.

3) When IGMP snooping is enabled and the multicast group addres *is* in
   the 224.0.0.X range [0], asserts that the multicast traffic *is*
   flooded.

In order to make those assertions, a new VM is being launched as part of
the test running tcpdump to verify whether the traffic is reaching it or
not.

A new configuration option called "is_igmp_snooping_enabled" has been
added.

[0] https://tools.ietf.org/html/rfc4541 (See section 2.1.2)

Change-Id: I8af041925119463c7199238988f0133e8d993a8f
Signed-off-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
This commit is contained in:
Lucas Alvares Gomes 2020-02-04 13:36:46 +00:00
parent 4dea8060f0
commit b5114e078f
3 changed files with 82 additions and 8 deletions

View File

@ -123,6 +123,12 @@ NeutronPluginOptions = [
default=False,
help='Allow creation of shared resources.'
'The default value is false.'),
cfg.BoolOpt('is_igmp_snooping_enabled',
default=False,
help='Indicates whether IGMP snooping is enabled or not. '
'If True, multicast test(s) will assert that multicast '
'traffic is not being flooded to all ports. Defaults '
'to False.'),
]
# TODO(amuller): Redo configuration options registration as part of the planned

View File

@ -111,6 +111,14 @@ with open('%(result_file)s', 'w') as f:
'result_file': result_file}
def get_unregistered_script(group, result_file):
return """#!/bin/bash
export LC_ALL=en_US.UTF-8
tcpdump -i any -s0 -vv host %(group)s -vvneA -s0 -l &> %(result_file)s &
""" % {'group': group,
'result_file': result_file}
class BaseMulticastTest(object):
credentials = ['primary']
@ -125,6 +133,7 @@ class BaseMulticastTest(object):
multicast_message = "Big Bang"
receiver_output_file = "/tmp/receiver_mcast_out"
sender_output_file = "/tmp/sender_mcast_out"
unregistered_output_file = "/tmp/unregistered_mcast_out"
@classmethod
def skip_checks(cls):
@ -198,16 +207,16 @@ class BaseMulticastTest(object):
server['ssh_client'] = ssh.Client(server['fip']['floating_ip_address'],
self.username,
pkey=self.keypair['private_key'])
self._check_python_installed_on_server(server['ssh_client'],
server['id'])
self._check_cmd_installed_on_server(server['ssh_client'],
server['id'], PYTHON3_BIN)
return server
def _check_python_installed_on_server(self, ssh_client, server_id):
def _check_cmd_installed_on_server(self, ssh_client, server_id, cmd):
try:
ssh_client.execute_script('which %s' % PYTHON3_BIN)
ssh_client.execute_script('which %s' % cmd)
except exceptions.SSHScriptFailed:
raise self.skipException(
"%s is not available on server %s" % (PYTHON3_BIN, server_id))
"%s is not available on server %s" % (cmd, server_id))
def _prepare_sender(self, server, mcast_address):
check_script = get_sender_script(
@ -226,10 +235,23 @@ class BaseMulticastTest(object):
server['fip']['floating_ip_address'],
self.username,
pkey=self.keypair['private_key'])
self._check_python_installed_on_server(ssh_client, server['id'])
self._check_cmd_installed_on_server(ssh_client, server['id'],
PYTHON3_BIN)
server['ssh_client'].execute_script(
'echo "%s" > ~/multicast_traffic_receiver.py' % check_script)
def _prepare_unregistered(self, server, mcast_address):
check_script = get_unregistered_script(
group=mcast_address, result_file=self.unregistered_output_file)
ssh_client = ssh.Client(
server['fip']['floating_ip_address'],
self.username,
pkey=self.keypair['private_key'])
self._check_cmd_installed_on_server(ssh_client, server['id'],
'tcpdump')
server['ssh_client'].execute_script(
'echo "%s" > ~/unregistered_traffic_receiver.sh' % check_script)
@test.unstable_test("bug 1850288")
@decorators.idempotent_id('113486fc-24c9-4be4-8361-03b1c9892867')
def test_multicast_between_vms_on_same_network(self):
@ -241,9 +263,26 @@ class BaseMulticastTest(object):
receivers = [self._create_server() for _ in range(1)]
# Sender can be also receiver of multicast traffic
receivers.append(sender)
self._check_multicast_conectivity(sender=sender, receivers=receivers)
unregistered = self._create_server()
self._check_multicast_conectivity(sender=sender, receivers=receivers,
unregistered=unregistered)
def _check_multicast_conectivity(self, sender, receivers):
def _is_multicast_traffic_expected(self, mcast_address):
"""Checks if multicast traffic is expected to arrive.
Checks if multicast traffic is expected to arrive to the
unregistered VM.
If IGMP snooping is enabled, multicast traffic should not be
flooded unless the destination IP is in the range of 224.0.0.X
[0].
[0] https://tools.ietf.org/html/rfc4541 (See section 2.1.2)
"""
return (mcast_address.startswith('224.0.0') or not
CONF.neutron_plugin_options.is_igmp_snooping_enabled)
def _check_multicast_conectivity(self, sender, receivers, unregistered):
"""Test multi-cast messaging between two servers
[Sender server] -> ... some network topology ... -> [Receiver server]
@ -257,6 +296,12 @@ class BaseMulticastTest(object):
path=file_path))
return msg in result
self._prepare_unregistered(unregistered, mcast_address)
# Run the unregistered node script
unregistered['ssh_client'].execute_script(
"bash ~/unregistered_traffic_receiver.sh", become_root=True)
self._prepare_sender(sender, mcast_address)
receiver_ids = []
for receiver in receivers:
@ -295,6 +340,18 @@ class BaseMulticastTest(object):
for receiver_id in receiver_ids:
self.assertIn(receiver_id, replies_result)
# Kill the tcpdump command running on the unregistered node so
# tcpdump flushes its output to the output file
unregistered['ssh_client'].execute_script(
"killall tcpdump && sleep 2", become_root=True)
unregistered_result = unregistered['ssh_client'].execute_script(
"cat {path} || echo '{path} not exists yet'".format(
path=self.unregistered_output_file))
num_of_pckt = (1 if self._is_multicast_traffic_expected(mcast_address)
else 0)
self.assertIn('%d packets captured' % num_of_pckt, unregistered_result)
class MulticastTestIPv4(BaseMulticastTest, base.BaseTempestTestCase):

View File

@ -0,0 +1,11 @@
---
features:
- |
Enhanced the ``test_multicast_between_vms_on_same_network`` adding
IGMP test coverage to it. A new VM running tcpdump is spawned as
part of the test to verify whether the traffic is reaching it or not.
upgrade:
- |
Add a new configuration option called ``is_igmp_snooping_enabled``
to enable/disable IGMP testing as part of the
``test_multicast_between_vms_on_same_network`` test case.