
Fixes some new PEP8 errors that appear with jobs running on new ubuntu version, and temporarily filters out the larger I202 error ("Additional newline in a group of imports"). This patch updates the hacking and flake8-import-order versions. Copied from: https://review.opendev.org/c/openstack/ovn-octavia-provider/+/936855 Change-Id: Ice4513eedc4fd6f054c19d1854eff00aeb5c35a1
1111 lines
49 KiB
Python
1111 lines
49 KiB
Python
# Copyright 2020 Red Hat, Inc.
|
|
# All Rights Reserved.
|
|
#
|
|
# 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 re
|
|
import time
|
|
|
|
import netaddr
|
|
from neutron_lib import constants
|
|
from neutron_lib.utils import test
|
|
from neutron_tempest_plugin.common import ip
|
|
from neutron_tempest_plugin.common import ssh
|
|
from neutron_tempest_plugin.common import utils
|
|
from neutron_tempest_plugin import exceptions
|
|
from oslo_log import log
|
|
from tempest import config
|
|
from tempest.lib.common.utils import data_utils
|
|
from tempest.lib import decorators
|
|
from tempest.lib import exceptions as temp_exc
|
|
|
|
from whitebox_neutron_tempest_plugin.tests.scenario import base
|
|
|
|
|
|
CONF = config.CONF
|
|
WB_CONF = CONF.whitebox_neutron_plugin_options
|
|
LOG = log.getLogger(__name__)
|
|
PYTHON3_BIN = "python3"
|
|
INSTANCE_INTERFACE = WB_CONF.default_instance_interface
|
|
|
|
|
|
def get_receiver_script(
|
|
group, port, hello_message, ack_message, result_file, interface=None):
|
|
if interface:
|
|
bind_to_dev_cmd = """
|
|
sock.setsockopt(socket.SOL_SOCKET,
|
|
socket.SO_BINDTODEVICE,
|
|
str('%(interface)s').encode('utf-8'))""" % {
|
|
'interface': interface}
|
|
else:
|
|
bind_to_dev_cmd = ''
|
|
|
|
return """
|
|
import socket
|
|
import struct
|
|
import sys
|
|
|
|
multicast_group = '%(group)s'
|
|
server_address = ('', %(port)s)
|
|
|
|
# Create the socket
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
|
%(bind_to_dev_cmd)s
|
|
|
|
# Bind to the server address
|
|
sock.bind(server_address)
|
|
|
|
# Tell the operating system to add the socket to the multicast group
|
|
# on all interfaces.
|
|
group = socket.inet_aton(multicast_group)
|
|
mreq = struct.pack('4sL', group, socket.INADDR_ANY)
|
|
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
|
|
|
|
# Receive/respond loop
|
|
with open('%(result_file)s', 'w') as f:
|
|
f.write('%(hello_message)s')
|
|
f.flush()
|
|
data, address = sock.recvfrom(1024)
|
|
f.write('received ' + str(len(data)) + ' bytes from ' + str(address))
|
|
f.write(str(data))
|
|
sock.sendto(b'%(ack_message)s', address)
|
|
""" % {'group': group,
|
|
'port': port,
|
|
'hello_message': hello_message,
|
|
'ack_message': ack_message,
|
|
'result_file': result_file,
|
|
'bind_to_dev_cmd': bind_to_dev_cmd}
|
|
|
|
|
|
def get_sender_script(group, port, message, result_file, ttl):
|
|
|
|
return """
|
|
import socket
|
|
import sys
|
|
|
|
message = b'%(message)s'
|
|
multicast_group = ('%(group)s', %(port)s)
|
|
|
|
# Create the datagram socket
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
|
# Set the time-to-live for messages to 1 so they do not go past the
|
|
# local network segment. For east-west or north-south tests recommended
|
|
# TTL is 2.
|
|
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, %(ttl)s)
|
|
|
|
# Set a timeout so the socket does not block indefinitely when trying
|
|
# to receive data.
|
|
sock.settimeout(1)
|
|
|
|
with open('%(result_file)s', 'w') as f:
|
|
try:
|
|
# Send data to the multicast group
|
|
sent = sock.sendto(message, multicast_group)
|
|
|
|
# Look for responses from all recipients
|
|
while True:
|
|
try:
|
|
data, server = sock.recvfrom(1024)
|
|
except socket.timeout:
|
|
f.write('timed out, no more responses')
|
|
break
|
|
else:
|
|
f.write('received reply ' + str(data) + ' from ' + str(server))
|
|
finally:
|
|
sys.stdout.write('closing socket')
|
|
sock.close()
|
|
""" % {'group': group,
|
|
'port': port,
|
|
'message': message,
|
|
'result_file': result_file,
|
|
'ttl': ttl}
|
|
|
|
|
|
def get_capture_script(result_file, mcast_port, interface):
|
|
return """#!/bin/bash
|
|
export LC_ALL=en_US.UTF-8
|
|
tcpdump -Qin -i %(interface)s -vvneA -s0 -l port %(mcast_port)s \
|
|
&> %(result_file)s &
|
|
""" % {'interface': interface,
|
|
'mcast_port': mcast_port,
|
|
'result_file': result_file}
|
|
|
|
|
|
class BaseMulticastTest(object):
|
|
|
|
credentials = ['primary', 'admin']
|
|
force_tenant_isolation = False
|
|
vlan_transparent = False
|
|
|
|
# Import configuration options
|
|
receivers_count = WB_CONF.mcast_receivers_count
|
|
mcast_groups_count = WB_CONF.mcast_groups_count
|
|
external_querier_period = WB_CONF.external_igmp_querier_period
|
|
|
|
hello_message = "I am waiting..."
|
|
multicast_port = 5007
|
|
multicast_message = "group %s"
|
|
receiver_output_file = "/tmp/receiver_mcast_out"
|
|
sender_output_file = "/tmp/sender_mcast_out_%s"
|
|
capture_output_file = "/tmp/capture_mcast_out"
|
|
|
|
@classmethod
|
|
def skip_checks(cls):
|
|
super(BaseMulticastTest, cls).skip_checks()
|
|
advanced_image_available = (
|
|
CONF.neutron_plugin_options.advanced_image_ref or
|
|
CONF.neutron_plugin_options.default_image_is_advanced)
|
|
if not advanced_image_available:
|
|
skip_reason = "This test require advanced tools for this test"
|
|
raise cls.skipException(skip_reason)
|
|
|
|
@classmethod
|
|
def resource_setup(cls):
|
|
super(BaseMulticastTest, cls).resource_setup()
|
|
|
|
if CONF.neutron_plugin_options.default_image_is_advanced:
|
|
cls.flavor_ref = CONF.compute.flavor_ref
|
|
cls.image_ref = CONF.compute.image_ref
|
|
cls.username = CONF.validation.image_ssh_user
|
|
else:
|
|
cls.flavor_ref = (
|
|
CONF.neutron_plugin_options.advanced_image_flavor_ref)
|
|
cls.image_ref = CONF.neutron_plugin_options.advanced_image_ref
|
|
cls.username = CONF.neutron_plugin_options.advanced_image_ssh_user
|
|
|
|
# setup basic topology for servers we can log into it
|
|
if cls.has_ovn_support:
|
|
try:
|
|
cls.network = cls.create_network(
|
|
vlan_transparent=cls.vlan_transparent)
|
|
except temp_exc.ServerFault as exc:
|
|
msg = 'Backend does not support VLAN Transparency.'
|
|
if exc.resp_body['message'] == msg:
|
|
raise cls.skipException(msg)
|
|
else:
|
|
raise exc
|
|
else:
|
|
cls.network = cls.create_network()
|
|
|
|
cls.subnet = cls.create_subnet(cls.network)
|
|
cls.router = cls.create_router_by_client()
|
|
cls.create_router_interface(cls.router['id'], cls.subnet['id'])
|
|
|
|
cls.keypair = cls.create_keypair()
|
|
|
|
cls.secgroup = cls.os_primary.network_client.create_security_group(
|
|
name='secgroup_mcast')
|
|
cls.security_groups.append(cls.secgroup['security_group'])
|
|
cls.create_loginable_secgroup_rule(
|
|
secgroup_id=cls.secgroup['security_group']['id'])
|
|
cls.create_pingable_secgroup_rule(
|
|
secgroup_id=cls.secgroup['security_group']['id'])
|
|
# Allow receiving of IGMP queries by VMs
|
|
cls.create_security_group_rule(
|
|
security_group_id=cls.secgroup['security_group']['id'],
|
|
protocol=constants.PROTO_NAME_IGMP,
|
|
direction=constants.INGRESS_DIRECTION)
|
|
# Create security group rule for UDP (multicast traffic)
|
|
cls.create_secgroup_rules(
|
|
rule_list=[dict(protocol=constants.PROTO_NAME_UDP,
|
|
direction=constants.INGRESS_DIRECTION,
|
|
remote_ip_prefix=cls.any_addresses,
|
|
ethertype=cls.ethertype)],
|
|
secgroup_id=cls.secgroup['security_group']['id'])
|
|
|
|
# Multicast IP range to be used for multicast group IP asignement
|
|
if '-' in cls.multicast_group_range:
|
|
multicast_group_range = netaddr.IPRange(
|
|
*cls.multicast_group_range.split('-'))
|
|
else:
|
|
multicast_group_range = netaddr.IPNetwork(
|
|
cls.multicast_group_range)
|
|
cls.multicast_group_iter = iter(multicast_group_range)
|
|
|
|
# For external or north-south topologies external network
|
|
# need to be defined
|
|
cls.external_network = cls.client.show_network(
|
|
CONF.network.public_network_id)['network']
|
|
|
|
def _check_cmd_installed_on_server(self, ssh_client, server_id, cmd):
|
|
try:
|
|
ssh_client.execute_script('which %s' % cmd)
|
|
except exceptions.SSHScriptFailed:
|
|
raise self.skipException(
|
|
"%s is not available on server %s" % (cmd, server_id))
|
|
|
|
def _prepare_sender(self, server, mcast_groups, ttl):
|
|
for mcast_group in mcast_groups:
|
|
output_file = self.sender_output_file % mcast_group
|
|
check_script = get_sender_script(
|
|
group=mcast_group, port=self.multicast_port,
|
|
message=self.multicast_message % mcast_group,
|
|
result_file=output_file,
|
|
ttl=ttl)
|
|
server['ssh_client'].execute_script(
|
|
'echo "%s" > /tmp/multicast_traffic_sender_%s.py' %
|
|
(check_script, mcast_group))
|
|
|
|
def _prepare_capture_script(self, server):
|
|
interface = (server['vlan_device'] if self.vlan_transparent
|
|
else INSTANCE_INTERFACE)
|
|
capture_script = get_capture_script(
|
|
result_file=self.capture_output_file,
|
|
mcast_port=self.multicast_port, interface=interface)
|
|
self._check_cmd_installed_on_server(
|
|
server['ssh_client'], server['id'], 'tcpdump')
|
|
server['ssh_client'].execute_script(
|
|
'echo "%s" > /tmp/capture_script.sh' % capture_script)
|
|
|
|
def _prepare_receiver(self, server, mcast_address):
|
|
interface = server['vlan_device'] if self.vlan_transparent else None
|
|
check_script = get_receiver_script(
|
|
group=mcast_address, port=self.multicast_port,
|
|
hello_message=self.hello_message, ack_message=server['id'],
|
|
result_file=self.receiver_output_file, interface=interface)
|
|
server['ssh_client'].execute_script(
|
|
'echo "%s" > /tmp/multicast_traffic_receiver.py' % check_script)
|
|
self._prepare_capture_script(server)
|
|
|
|
def _prepare_unregistered(self, server):
|
|
# We need to kill multicast receiver script if receiver becomes
|
|
# unsubscribed and the server moves to the pool of unregistered VMs
|
|
server['ssh_client'].exec_command(
|
|
"pids=$(ps ax | grep multicast | grep -v grep | awk '{print $1}')"
|
|
";kill -9 $pids && sleep 2 || true")
|
|
self._prepare_capture_script(server)
|
|
|
|
def _prepare_igmp_snooping_test(
|
|
self, mcast_groups, receivers_count=1, topology='internal',
|
|
port_type=None):
|
|
"""Function for creating desired topology for the test
|
|
|
|
Available topologies:
|
|
* internal(default): sender and receivers are on tenant network
|
|
* external: sender and receivers are on external(public) network
|
|
* east-west: sender and receivers are on different tenant networks
|
|
* north-south: sender is on external and receivers on tenant network
|
|
|
|
:param mcast_groups(list): list of multicast groups to use. Note,
|
|
for each group at least one receiver will be created.
|
|
:param receivers_count(int): Number of receivers to create for each
|
|
multicast group. Note, first receiver is always created on a
|
|
different compute node than sender is running.
|
|
:param topology(str): one of 4 available topologies to use (see list
|
|
above)
|
|
:param port_type(str): type of port to use. If omitted, default port
|
|
type will be used. Can be set to 'direct' or 'direct-physical'
|
|
for SR-IOV environments.
|
|
|
|
:returns: List of the following objects, sender, list of receivers,
|
|
list of unregistered, id of destination network
|
|
"""
|
|
# Note, for now we support port_type other than 'normal' only
|
|
# when using SR-IOV environement and therefore only external network
|
|
if topology == 'external' or topology == 'north-south':
|
|
self.ensure_external_network_is_shared()
|
|
sender = self._create_server_for_topology(
|
|
network_id=self.external_network['id'],
|
|
port_type=port_type)
|
|
elif topology == 'east-west':
|
|
network2 = self.create_network()
|
|
subnet2 = self.create_subnet(network2, cidr="10.100.1.0/24")
|
|
self.create_router_interface(self.router['id'], subnet2['id'])
|
|
sender = self._create_server_for_topology(
|
|
network_id=network2['id'], port_type=port_type)
|
|
else:
|
|
sender = self._create_server_for_topology(port_type=port_type)
|
|
|
|
if topology == 'external':
|
|
dst_network_id = self.external_network['id']
|
|
else:
|
|
dst_network_id = self.network['id']
|
|
|
|
receivers = []
|
|
for group_id in range(len(mcast_groups)):
|
|
if receivers_count > 0:
|
|
# At least one receiver in the group we need to be launched
|
|
# on different host
|
|
receivers.append(
|
|
[self._create_server_for_topology(
|
|
different_host=sender, network_id=dst_network_id,
|
|
port_type=port_type)])
|
|
for _ in range(receivers_count - 1):
|
|
receivers[group_id].append(
|
|
self._create_server_for_topology(
|
|
network_id=dst_network_id, port_type=port_type))
|
|
else:
|
|
# For some tests we do not create receivers
|
|
receivers.append([])
|
|
|
|
servers = [item for sublist in receivers for item in sublist]
|
|
servers.append(sender)
|
|
|
|
unregistered = self._create_server_for_topology(
|
|
network_id=dst_network_id, port_type=port_type)
|
|
servers.append(unregistered)
|
|
# For some tests we need to support more than one unregistered server
|
|
unregistered = [unregistered]
|
|
|
|
for server in servers:
|
|
self._check_cmd_installed_on_server(server['ssh_client'],
|
|
server['id'], PYTHON3_BIN)
|
|
return [sender, receivers, unregistered, dst_network_id]
|
|
|
|
def restart_openvswitch_on_compute_nodes(self):
|
|
for node in self.nodes:
|
|
if node['is_compute'] is True and node['is_controller'] is False:
|
|
self.reset_node_service('ovs vswitchd', node['client'])
|
|
|
|
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 (str(mcast_address).startswith('224.0.0') or not
|
|
CONF.neutron_plugin_options.is_igmp_snooping_enabled)
|
|
|
|
def _check_multicast_conectivity(
|
|
self, mcast_groups, sender, receivers, unregistered,
|
|
test_unsubscribe=False, start_delay=None, pre_action=None, ttl=1):
|
|
"""Test multicast messaging between servers
|
|
|
|
[Sender server] -> ... some network topology ... -> [Receiver server]
|
|
|
|
:param mcast_groups(list): list of multicast groups to use.
|
|
Note: for each group will be created a sender script and at least
|
|
one receiver.
|
|
:param sender(server): server that will send multicast traffic
|
|
:param receivers(list): list of lists of servers. There is ia list of
|
|
receivers per group.
|
|
:param unregistered(list): list of servers that are not subscribed
|
|
to receive multicast traffic.
|
|
:param test_unsubscribe(boolean): whether to test unsubsribe step or
|
|
not. Default is False.
|
|
:param start_delay(int): whether to include delay waiting step before
|
|
sending packets and how long (in seconds). Default is no delay.
|
|
:param pre_action(callable): run additional arbitrary action before
|
|
sending multicast packets.
|
|
:param ttl(int): time-to-live for multicast packages. Default is 1 fits
|
|
well when all VMs are on the same network. For testing multicast
|
|
between 2 different network TTL should be set to 2.
|
|
|
|
Scenario is the following:
|
|
1. Install required scripts on all VMs according their roles.
|
|
2. Send multicast packets from sender to each multicast group.
|
|
Note: by default a single group is used and single receiver,
|
|
but this can be adjusted with config options.
|
|
3. Verify that multicast traffic reached each receiver and
|
|
does not reach unregistgered host. Number of received packets
|
|
is the same as sent number.
|
|
4. Unsubscribe one receiver in each group and repeat steps 2 and 3.
|
|
Note: in case more than one receiver configured the step will
|
|
repeat until no subscribed hosts left.
|
|
|
|
"""
|
|
def _message_received(client, msg, file_path):
|
|
result = client.execute_script(
|
|
"cat {path} || echo '{path} not exists yet'".format(
|
|
path=file_path))
|
|
return msg in result
|
|
|
|
def _validate_number_of_messages(
|
|
client, mcast_groups, file_path, allowed_group=None):
|
|
"""This function validates number of packets that reached a VM
|
|
|
|
The function compares actual received number of packets per group
|
|
with the expected number.
|
|
"""
|
|
result = client.execute_script(
|
|
"cat {path} || echo '{path} not exists yet'".format(
|
|
path=file_path))
|
|
# We need to make sure that exactly the expected count
|
|
# of messages reached receiver, no more and no less
|
|
LOG.debug('result = {}'. format(result))
|
|
for mcast_group in mcast_groups:
|
|
LOG.debug('Validating group %s', mcast_group)
|
|
if allowed_group:
|
|
LOG.debug('Allowed group %s', allowed_group)
|
|
if ((allowed_group and mcast_group == allowed_group) or
|
|
self._is_multicast_traffic_expected(mcast_group)):
|
|
expected_count = 1
|
|
else:
|
|
expected_count = 0
|
|
count = len(
|
|
re.findall(self.multicast_message % mcast_group, result))
|
|
self.assertEqual(
|
|
count, expected_count,
|
|
'Received number of messages ({}) to group {} differs '
|
|
'from the expected ({})'.format(
|
|
count, mcast_group, expected_count))
|
|
|
|
def _test_traffic_between_servers():
|
|
for server in unregistered:
|
|
self._prepare_unregistered(server)
|
|
server['ssh_client'].execute_script(
|
|
"bash /tmp/capture_script.sh", become_root=True)
|
|
|
|
receiver_ids = []
|
|
# receivers is a list of separate lists for each mcast group
|
|
group_ids = range(len(mcast_groups))
|
|
for group_id in group_ids:
|
|
receiver_ids.append([])
|
|
for receiver in receivers[group_id]:
|
|
self._prepare_receiver(
|
|
receiver, mcast_groups[group_id])
|
|
# We run the capture script on each receiver as well
|
|
# in order to check that there is no traffic from any
|
|
# group that the receiver did not subscribe to
|
|
receiver['ssh_client'].execute_script(
|
|
"bash /tmp/capture_script.sh", become_root=True)
|
|
|
|
# receiver script needs to be executed as root user for
|
|
# vlan_transparency in order to bind the vlan interface
|
|
receiver['ssh_client'].execute_script(
|
|
"%s /tmp/multicast_traffic_receiver.py &" %
|
|
PYTHON3_BIN, shell="bash",
|
|
become_root=self.vlan_transparent)
|
|
utils.wait_until_true(
|
|
lambda: _message_received(
|
|
receiver['ssh_client'], self.hello_message,
|
|
self.receiver_output_file),
|
|
exception=RuntimeError(
|
|
"Receiver script didn't start properly on server "
|
|
"{!r}.".format(receiver['id'])))
|
|
receiver_ids[group_id].append(receiver['id'])
|
|
|
|
if pre_action:
|
|
pre_action()
|
|
|
|
if start_delay and start_delay > 5:
|
|
LOG.debug(
|
|
"Waiting %s seconds for start delay to expire",
|
|
start_delay)
|
|
time.sleep(start_delay)
|
|
else:
|
|
# (rsafrono) Note, this delay is needed to make sure all
|
|
# receivers are ready. In some cases (e.g. SR-IOV) test result
|
|
# is unstable if we start to send traffic without this delay.
|
|
time.sleep(10)
|
|
|
|
for group in mcast_groups:
|
|
LOG.debug("Starting script for group %s on "
|
|
"sender", group)
|
|
sender['ssh_client'].execute_script(
|
|
"%s /tmp/multicast_traffic_sender_%s.py" % (
|
|
PYTHON3_BIN, group))
|
|
|
|
for group_id in group_ids:
|
|
for receiver in receivers[group_id]:
|
|
utils.wait_until_true(
|
|
lambda: _message_received(
|
|
receiver['ssh_client'],
|
|
self.multicast_message % mcast_groups[group_id],
|
|
self.receiver_output_file),
|
|
exception=RuntimeError(
|
|
"Receiver {!r} didn't get multicast "
|
|
"message".format(receiver['id'])))
|
|
|
|
receiver['ssh_client'].execute_script(
|
|
"killall tcpdump && sleep 2", become_root=True)
|
|
LOG.debug('Validating number of messages on '
|
|
'receiver %s', receiver['id'])
|
|
_validate_number_of_messages(
|
|
receiver['ssh_client'], mcast_groups,
|
|
self.capture_output_file, mcast_groups[group_id])
|
|
|
|
for group_id in group_ids:
|
|
sender_output = (
|
|
self.sender_output_file % mcast_groups[group_id])
|
|
replies_result = sender['ssh_client'].execute_script(
|
|
"cat {path} || echo '{path} not exists yet'".format(
|
|
path=sender_output))
|
|
for receiver_id in receiver_ids[group_id]:
|
|
self.assertIn(receiver_id, replies_result)
|
|
|
|
for server in unregistered:
|
|
server['ssh_client'].execute_script(
|
|
"killall tcpdump && sleep 2", become_root=True)
|
|
LOG.debug('Validating number of messages on '
|
|
'unregistered %s', server['id'])
|
|
_validate_number_of_messages(
|
|
server['ssh_client'], mcast_groups,
|
|
self.capture_output_file)
|
|
|
|
def _unsubscribe_receiver_from_group(group_id):
|
|
if len(receivers[group_id]) > 0:
|
|
unregistered.append(receivers[group_id][-1])
|
|
del receivers[group_id][-1]
|
|
|
|
self._prepare_sender(sender, mcast_groups, ttl)
|
|
_test_traffic_between_servers()
|
|
# If test_unsubscribe option is enabled then we unsubscribe
|
|
# one receiver from each group and retest traffic.
|
|
# Repeat this step until all receivers are unsubscribed.
|
|
if test_unsubscribe:
|
|
while len(receivers[0]) > 0:
|
|
for group_id in range(len(mcast_groups)):
|
|
_unsubscribe_receiver_from_group(group_id)
|
|
_test_traffic_between_servers()
|
|
|
|
|
|
class MulticastTestIPv4(BaseMulticastTest, base.TrafficFlowTest):
|
|
|
|
# Import configuration options
|
|
multicast_group_range = CONF.neutron_plugin_options.multicast_group_range
|
|
|
|
# IP version specific parameters
|
|
_ip_version = constants.IP_VERSION_4
|
|
any_addresses = constants.IPv4_ANY
|
|
|
|
|
|
class MulticastTestIPv4Common(MulticastTestIPv4):
|
|
@decorators.idempotent_id('615a35d3-642e-4440-a19e-351f29d0b3ff')
|
|
def test_igmp_snooping_same_network_and_unsubscribe(self):
|
|
"""Test multicast messaging between servers on the same network
|
|
|
|
[Sender server] -> (Multicast network) -> [Receiver server]
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
Note: default(internal, same network) topology is used.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, _ = self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups,
|
|
receivers_count=self.receivers_count)
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, test_unsubscribe=True)
|
|
|
|
@decorators.idempotent_id('fcd50103-eeba-475f-803b-dfc9e8c342a5')
|
|
def test_igmp_snooping_ext_network_and_unsubscribe(self):
|
|
"""Test multicast between VMs on external network
|
|
|
|
[Sender server] -> (Multicast network) -> [Receiver server]
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
Note: External topology is used.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, _ = self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups, topology='external',
|
|
receivers_count=self.receivers_count)
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, test_unsubscribe=True)
|
|
|
|
@decorators.idempotent_id('35523bd5-4b35-4e6b-a25a-20826808d85d')
|
|
def test_flooding_when_special_groups(self):
|
|
"""Test that multicast traffic of groups 224.0.0.X is flooded
|
|
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
Note: default(internal, same network) topology is used.
|
|
mcast_groups contains only groups from the range 224.0.0.X
|
|
2. Verify that multicast packets reach all VMs, even
|
|
unregistered ones.
|
|
"""
|
|
mcast_groups = ['224.0.0.100']
|
|
sender, receivers, unregistered, _ = self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups)
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups,
|
|
sender=sender, receivers=receivers, unregistered=unregistered)
|
|
|
|
@decorators.idempotent_id('42bfc8e1-0efb-4573-bb4a-290060a40980')
|
|
def test_igmp_snooping_after_openvswitch_restart(self):
|
|
"""Test IGMP snooping after openvswitch restart
|
|
|
|
[Sender VM] -> (Multicast network) -> [Receiver VM]
|
|
|
|
Note: this test should not be run with other tests in parallel
|
|
because it can affect other tests results.
|
|
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Subscribe receiver VMs to a multicast group but do not
|
|
send multicast packets immediately.
|
|
3. Restart Openvswitch on compute nodes.
|
|
4. Send multicast traffic and verify that multicast packets
|
|
reach subscribed hosts and do not reach unsubscribed. For more
|
|
details see _check_multicast_connectivity document text and code.
|
|
"""
|
|
if not hasattr(self, 'nodes'):
|
|
raise self.skipException(
|
|
"Nodes info not available. Test won't be able to restart "
|
|
"openvswitch service on a node.")
|
|
if len(self.nodes) == 1:
|
|
raise self.skipException(
|
|
"This test is not supported on a single-node environment")
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, _ = self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups,
|
|
receivers_count=self.receivers_count)
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered,
|
|
pre_action=self.restart_openvswitch_on_compute_nodes)
|
|
|
|
|
|
class MulticastTestIPv4Sriov(MulticastTestIPv4):
|
|
|
|
@classmethod
|
|
def resource_setup(cls):
|
|
super(MulticastTestIPv4Sriov, cls).resource_setup()
|
|
if not cls.has_sriov_support:
|
|
skip_reason = "Environment does not support SR-IOV"
|
|
raise cls.skipException(skip_reason)
|
|
|
|
def _validate_pfs_availability(self):
|
|
required_sriov_ports = (
|
|
self.mcast_groups_count * self.receivers_count + 2)
|
|
available_sriov_ports = (
|
|
WB_CONF.sriov_pfs_per_host *
|
|
len([node for node in self.nodes
|
|
if node['is_compute'] is True]))
|
|
if (available_sriov_ports < required_sriov_ports):
|
|
self.skipTest(
|
|
'Not enough SR-IOV ports ({}), while required {}'.format(
|
|
available_sriov_ports, required_sriov_ports))
|
|
|
|
|
|
class MulticastTestIPv4SriovCommon(MulticastTestIPv4Sriov):
|
|
|
|
@decorators.idempotent_id('4210438f-080b-4254-a7d1-166144f04572')
|
|
def test_igmp_snooping_ext_network_with_vf_ports(self):
|
|
"""Test multicast between VMs with SR-IOV VF ports on external network
|
|
|
|
[Sender with VF port] -> (External network) -> [Receiver with VF port]
|
|
Note: the test runs only on SR-IOV environment and requires shared
|
|
external network.
|
|
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
Note: External topology is used. External network must be shared.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, _ = self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups, topology='external',
|
|
port_type='direct', receivers_count=self.receivers_count)
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, test_unsubscribe=True)
|
|
|
|
@decorators.idempotent_id('86fb3e4b-5e01-4953-99a9-4f85c561f57e')
|
|
def test_igmp_snooping_ext_network_with_pf_ports(self):
|
|
"""Test multicast between VMs with SR-IOV PF ports on external network
|
|
|
|
[Sender with PF port] -> (External network) -> [Receiver with PF port]
|
|
Note: the test runs only on SR-IOV environment and requires shared
|
|
external network.
|
|
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
Note: External topology is used. External network must be shared.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
self._validate_pfs_availability()
|
|
sender, receivers, unregistered, _ = self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups, topology='external',
|
|
port_type='direct-physical', receivers_count=self.receivers_count)
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, test_unsubscribe=True)
|
|
|
|
|
|
class MulticastTestIPv4OvnBase(MulticastTestIPv4, base.BaseTempestTestCaseOvn):
|
|
mcast_idle_timeout = 60
|
|
|
|
# We will use this function in order to decrease the idle timeout from the
|
|
# default value in order to save some time
|
|
def set_mcast_idle_timeout(self, network_id):
|
|
uuid = self.get_item_uuid(
|
|
db='nb', item='logical_switch',
|
|
search_string='name=neutron-' + network_id)
|
|
cmd = self.nbctl + " set logical_switch " + uuid + " other_config:"
|
|
self.run_on_master_controller(
|
|
cmd + "mcast_idle_timeout=" + str(self.mcast_idle_timeout))
|
|
|
|
def set_querier_on_network(self, network_id):
|
|
port = self.client.list_ports(
|
|
network_id=network_id,
|
|
device_owner=constants.DEVICE_OWNER_DHCP)['ports']
|
|
eth_src = port[0]['mac_address']
|
|
ip4_src = port[0]['fixed_ips'][0]['ip_address']
|
|
uuid = self.get_item_uuid(
|
|
db='nb', item='logical_switch',
|
|
search_string='name=neutron-' + network_id)
|
|
cmd = self.nbctl + " set logical_switch " + uuid + " other_config:"
|
|
self.run_on_master_controller(
|
|
cmd + "mcast_querier='true';" +
|
|
cmd + "mcast_eth_src=" + eth_src + ";" +
|
|
cmd + "mcast_ip4_src=" + ip4_src)
|
|
|
|
def set_mcast_relay_on_router(self, router_id, state='true'):
|
|
uuid = self.get_item_uuid(
|
|
db='nb', item='logical_router',
|
|
search_string='name=neutron-' + router_id)
|
|
self.run_on_master_controller(
|
|
self.nbctl + " set logical_router " + uuid +
|
|
" options:mcast_relay=" + state)
|
|
|
|
def restart_ovn_controller_on_compute_nodes(self):
|
|
for node in self.nodes:
|
|
if node['is_compute'] is True and node['is_controller'] is False:
|
|
self.reset_node_service('ovn controller', node['client'])
|
|
|
|
|
|
class MulticastTestIPv4Ovn(MulticastTestIPv4OvnBase):
|
|
|
|
@test.unstable_test("querier not available by default, see RHBZ 1791815")
|
|
@decorators.idempotent_id('fa082cf9-37fc-4e7f-bfdb-fbd8e6860634')
|
|
def test_multicast_after_idle_timeout(self):
|
|
"""Test multicast messaging after idle timeout
|
|
|
|
[Sender VM] -> (Multicast network) -> [Receiver VM]
|
|
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Subscribe receiver VMs to a multicast group but do not send
|
|
multicast packets immediately. Wait first until idle_timeout
|
|
expires and send packets afterwards.
|
|
3. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, dst_network_id = (
|
|
self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups,
|
|
receivers_count=self.receivers_count))
|
|
self.set_mcast_idle_timeout(dst_network_id)
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, start_delay=self.mcast_idle_timeout)
|
|
|
|
# (rsafrono) Note, this test is temporary. It includes enabling querier
|
|
# as a workaround because currently the querier is not enabled by default,
|
|
# see RHBZ 1791815
|
|
@test.unstable_test("Multicast querier not yet supported officially")
|
|
@decorators.idempotent_id('be5e153d-1ce7-4d19-9efd-2aae0ec74749')
|
|
def test_idle_timeout_with_querier_enabled(self):
|
|
"""Test multicast messaging after idle timeout when querier is enabled
|
|
|
|
[Sender VM] -> (Multicast network) -> [Receiver VM]
|
|
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Enable querier on the network. Subscribe receiver VMs to
|
|
a multicast group but do not send multicast packets immediately.
|
|
Wait first until idle_timeout expires and send afterwards.
|
|
3. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, dst_network_id = (
|
|
self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups,
|
|
receivers_count=self.receivers_count))
|
|
self.set_querier_on_network(dst_network_id)
|
|
self.set_mcast_idle_timeout(dst_network_id)
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, start_delay=self.mcast_idle_timeout)
|
|
|
|
# (rsafrono) Note, this test includes enabling mcast_relay as
|
|
# a workaround. Currently mcast_relay is not enabled on router by default.
|
|
@test.unstable_test("mcast relay not yet supported officially")
|
|
@decorators.idempotent_id('3a906cd8-e27a-40a7-a369-829a7ec91af6')
|
|
def test_multicast_east_west(self):
|
|
"""Test multicast between servers on different tenant networks
|
|
|
|
[Sender VM] [Receiver VM]
|
|
| |
|
|
[Internal network A] -> [Router] -> [Internal network B]
|
|
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
Note: East-west topology is used (2 internal networks)
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
self.set_mcast_relay_on_router(self.router['id'])
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, _ = (
|
|
self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups, topology='east-west',
|
|
receivers_count=self.receivers_count))
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, ttl=2)
|
|
|
|
# (rsafrono) Note, this test includes enabling mcast_relay as
|
|
# a workaround. Currently mcast_relay is not enabled on router by default.
|
|
@test.unstable_test("Core OVN bug, see RHBZ 1902075")
|
|
@decorators.idempotent_id('71abfc10-3f6c-4096-a1d3-8fd934b5e3ba')
|
|
def test_multicast_north_south(self):
|
|
"""Test multicast between VMs on external and internal networks
|
|
|
|
[Sender VM] -> [External network]
|
|
|
|
|
[Router]
|
|
|
|
|
[Receiver VM] <- [Internal network]
|
|
|
|
Note: the test requires shared external network.
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
Note: North-south topology is used where sender is on
|
|
the external network and receivers on the internal one.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
self.set_mcast_relay_on_router(self.router['id'])
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, _ = (
|
|
self._prepare_igmp_snooping_test(
|
|
mcast_groups=mcast_groups, topology='north-south',
|
|
receivers_count=self.receivers_count))
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, ttl=2)
|
|
|
|
|
|
class MulticastTestIPv4SriovOvn(
|
|
MulticastTestIPv4Sriov, MulticastTestIPv4OvnBase):
|
|
|
|
# (rsafrono) External IGMP querier required for this test.
|
|
# For now the only reliable option for this is to use SR-IOV environment.
|
|
@decorators.idempotent_id('fb56ac55-7863-44f9-b0e4-3383093838a2')
|
|
def test_after_ovn_controller_restart_with_external_querier(self):
|
|
"""Test IGMP snooping after ovn controller restart
|
|
|
|
[Sender VM] -> (Multicast network) -> [Receiver VM]
|
|
|
|
Notes:
|
|
1. External IGMP querier required for this test to run properly.
|
|
2. This test should not be run with other tests in parallel
|
|
because it can affect other tests results.
|
|
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Subscribe receiver VMs to a multicast group but do not
|
|
send multicast packets immediately.
|
|
3. Restart OVN controller on compute nodes. This will cause that
|
|
OVN controller unlearns the multicast groups.
|
|
4. Wait until external querier send new queries, receivers
|
|
VMs will respond to queries and OVN controller will re-learn
|
|
the multicast groups.
|
|
5. Send multicast traffic and verify that multicast packets
|
|
reach subscribed hosts and do not reach unsubscribed. For more
|
|
details see _check_multicast_connectivity document text and code.
|
|
"""
|
|
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered, dst_network_id = (
|
|
self._prepare_igmp_snooping_test(
|
|
port_type='direct',
|
|
mcast_groups=mcast_groups, topology='external',
|
|
receivers_count=self.receivers_count))
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered,
|
|
start_delay=self.external_querier_period,
|
|
pre_action=self.restart_ovn_controller_on_compute_nodes)
|
|
|
|
|
|
class MulticastTestVlanTransparency(MulticastTestIPv4):
|
|
|
|
required_extensions = ['vlan-transparent', 'allowed-address-pairs']
|
|
vlan_transparent = True
|
|
vlan_tag = 123
|
|
vlan_ipmast_template = '192.168.123.{ip_last_byte}/24'
|
|
|
|
# initialize server index: this index will be used for allowed_address_pair
|
|
# values
|
|
server_index = 0
|
|
|
|
def _prepare_igmp_snooping_test_vlan_transparency(
|
|
self, mcast_groups, receivers_count=1, topology='internal',
|
|
port_type=None):
|
|
|
|
sender = self._create_multicast_server_vlan_transparency(mcast_groups)
|
|
|
|
receivers = []
|
|
for group_id in range(len(mcast_groups)):
|
|
if receivers_count > 0:
|
|
# At least one receiver in the group we need to be launched
|
|
# on different host
|
|
receivers.append(
|
|
[self._create_multicast_server_vlan_transparency(
|
|
mcast_groups, different_host=sender)])
|
|
for _ in range(receivers_count - 1):
|
|
receivers[group_id].append(
|
|
self._create_multicast_server_vlan_transparency(
|
|
mcast_groups))
|
|
else:
|
|
# For some tests we do not create receivers
|
|
receivers.append([])
|
|
|
|
servers = [item for sublist in receivers for item in sublist]
|
|
servers.append(sender)
|
|
|
|
unregistered = self._create_multicast_server_vlan_transparency(
|
|
mcast_groups)
|
|
servers.append(unregistered)
|
|
# For some tests we need to support more than one unregistered server
|
|
unregistered = [unregistered]
|
|
|
|
for server in servers:
|
|
self._check_cmd_installed_on_server(server['ssh_client'],
|
|
server['id'], PYTHON3_BIN)
|
|
return [sender, receivers, unregistered]
|
|
|
|
def _create_multicast_server_vlan_transparency(
|
|
self, mcast_groups, different_host=None):
|
|
vlan_ipmask = self.vlan_ipmast_template.format(
|
|
ip_last_byte=self.server_index + 10)
|
|
self.server_index += 1
|
|
allowed_address_pairs = [{'ip_address': vlan_ipmask}]
|
|
port = self.create_port(
|
|
network={'id': self.network['id']},
|
|
security_groups=[self.secgroup['security_group']['id']],
|
|
allowed_address_pairs=allowed_address_pairs)
|
|
networks = [{'port': port['id']}]
|
|
|
|
params = {
|
|
'flavor_ref': self.flavor_ref,
|
|
'image_ref': self.image_ref,
|
|
'key_name': self.keypair['name'],
|
|
'networks': networks,
|
|
'security_groups': [
|
|
{'name': self.secgroup['security_group']['name']}],
|
|
'name': data_utils.rand_name('multicast-server-vlan-transparent')
|
|
}
|
|
if (different_host and CONF.compute.min_compute_nodes > 1):
|
|
params['scheduler_hints'] = {
|
|
'different_host': different_host['id']}
|
|
server = self.create_server(**params)['server']
|
|
|
|
# create fip
|
|
access_ip_address = self.create_floatingip(
|
|
port=port)['floating_ip_address']
|
|
server['ssh_client'] = ssh.Client(access_ip_address,
|
|
self.username,
|
|
pkey=self.keypair['private_key'])
|
|
|
|
# configure transparent vlan on server
|
|
server['vlan_device'] = self._configure_vlan_transparent(
|
|
port, server['ssh_client'], vlan_ipmask, mcast_groups)
|
|
|
|
return server
|
|
|
|
def _configure_vlan_transparent(
|
|
self, port, ssh_client, vlan_ip, mcast_groups):
|
|
ip_command = ip.IPCommand(ssh_client=ssh_client)
|
|
for address in ip_command.list_addresses(port=port):
|
|
port_iface = address.device.name
|
|
break
|
|
else:
|
|
self.fail("Parent port fixed IP not found on server.")
|
|
|
|
subport_iface = ip_command.configure_vlan_transparent(
|
|
port=port, vlan_tag=self.vlan_tag, ip_addresses=[vlan_ip])
|
|
for address in ip_command.list_addresses(ip_addresses=vlan_ip):
|
|
self.assertEqual(subport_iface, address.device.name)
|
|
self.assertEqual(port_iface, address.device.parent)
|
|
break
|
|
else:
|
|
self.fail("Sub-port fixed IP not found on server.")
|
|
|
|
for mcast_group in mcast_groups:
|
|
ip_command.add_route(mcast_group, subport_iface)
|
|
|
|
return subport_iface
|
|
|
|
@decorators.idempotent_id('c480cec8-3ca4-4781-baad-2e1190079467')
|
|
def test_igmp_snooping_vlan_transparency(self):
|
|
"""Test multicast messaging between servers on the same network
|
|
|
|
[Sender server] -> (Multicast network) -> [Receiver server]
|
|
Scenario:
|
|
1. Create VMs for sender, receiver(s), unregistered host.
|
|
Note: default(internal, same network) topology is used.
|
|
mcast_groups contain only groups where no flooding
|
|
to all ports expected.
|
|
2. Verify that multicast packets reach subscribed hosts
|
|
and do not reach unsubscribed. For more details see
|
|
_check_multicast_connectivity document text and code.
|
|
"""
|
|
mcast_groups = [next(self.multicast_group_iter)
|
|
for _ in range(self.mcast_groups_count)]
|
|
sender, receivers, unregistered = (
|
|
self._prepare_igmp_snooping_test_vlan_transparency(
|
|
mcast_groups=mcast_groups,
|
|
receivers_count=self.receivers_count))
|
|
self._check_multicast_conectivity(
|
|
mcast_groups=mcast_groups, sender=sender, receivers=receivers,
|
|
unregistered=unregistered, test_unsubscribe=False)
|