Fix east_west capture for vlan tenant

With vlan tenant we have mac-mapping configured in ovs db
for compute nodes. These MAC values should be used when
filtering relevant traffic for east_west communication.

Also, introduced separate tunnel_interface config parameter
to define an appropriate value when capturing traffic on
a normal node and on ovs_pod.

Change-Id: Ibef4498604bd9fff7ab537ed276ce59e8a052cd6
This commit is contained in:
Roman Safronov 2024-07-30 16:18:20 +03:00
parent a022d38865
commit 048c752ca3
4 changed files with 74 additions and 13 deletions

View File

@ -15,7 +15,6 @@
GLOBAL_IP = '1.1.1.1' GLOBAL_IP = '1.1.1.1'
METADATA_SERVICE_IP = '169.254.169.254' METADATA_SERVICE_IP = '169.254.169.254'
NODE_TUNNEL_INTERFACE = 'genev_sys_6081'
OVN_MAX_GW_PORTS_PER_ROUTER = 5 OVN_MAX_GW_PORTS_PER_ROUTER = 5
NEUTRON_CONF = { NEUTRON_CONF = {
'devstack': '/etc/neutron/neutron.conf', 'devstack': '/etc/neutron/neutron.conf',

View File

@ -128,6 +128,10 @@ WhiteboxNeutronPluginOptions = [
default='eth1', default='eth1',
help='Physical interface of a node that is connected to the ' help='Physical interface of a node that is connected to the '
'external network.'), 'external network.'),
cfg.StrOpt('ovs_pod_ext_interface',
default='datacentre',
help='Interface of a ovs pod (on podified openstack) that is '
'connected to the external network.'),
cfg.IntOpt('minbw_placement_nic_kbps_egress', cfg.IntOpt('minbw_placement_nic_kbps_egress',
default=None, default=None,
help='BW configured per NIC for the minimum BW placement ' help='BW configured per NIC for the minimum BW placement '
@ -150,6 +154,18 @@ WhiteboxNeutronPluginOptions = [
help='Interface of a node that intended to pass tenant' help='Interface of a node that intended to pass tenant'
'network traffic. Note: currently only environments with ' 'network traffic. Note: currently only environments with '
'the same name of the tenant interface are supported'), 'the same name of the tenant interface are supported'),
cfg.StrOpt('node_tunnel_interface',
default='genev_sys_6081',
help='Interface of a node that intended to pass tunnelled '
'traffic. genev_sys_6081 is default for geneve tenant '
'networks but for vlan tenant network this value should '
'be updated according to the environment.'),
cfg.StrOpt('ovs_pod_tunnel_interface',
default='genev_sys_6081',
help='Interface of a ovs pod that intended to pass tunnelled '
'traffic. genev_sys_6081 is default for geneve tenant '
'networks but for vlan tenant network this value should '
'be updated according to the environment.'),
cfg.IntOpt('capture_timeout', cfg.IntOpt('capture_timeout',
default=120, default=120,
help='Maximal time for running remote capture, in seconds. ' help='Maximal time for running remote capture, in seconds. '

View File

@ -182,6 +182,11 @@ class BaseTempestWhiteboxTestCase(base.BaseTempestTestCase):
return host_name return host_name
return hostname return hostname
def get_network_type(self, network_id):
network_details = self.os_admin.network_client.show_network(
network_id)
return network_details['network']['provider:network_type']
@staticmethod @staticmethod
def _get_local_ip_from_network(network): def _get_local_ip_from_network(network):
host_ip_addresses = [ifaddresses(iface)[AF_INET][0]['addr'] host_ip_addresses = [ifaddresses(iface)[AF_INET][0]['addr']
@ -1055,7 +1060,7 @@ class TrafficFlowTest(BaseTempestWhiteboxTestCase):
if node['name'] == line.split()[0]: if node['name'] == line.split()[0]:
node['ovs_pod'] = line.split()[1] node['ovs_pod'] = line.split()[1]
def _start_captures(self, filters, interface=None): def _start_captures(self, filters, scenario='north_south', interface=None):
for node in self.nodes: for node in self.nodes:
if not (node['is_controller'] or if not (node['is_controller'] or
node['is_compute'] or node['is_compute'] or
@ -1067,18 +1072,27 @@ class TrafficFlowTest(BaseTempestWhiteboxTestCase):
node['is_controller']): node['is_controller']):
capture_client = self.proxy_host_client capture_client = self.proxy_host_client
command_prefix = "{} rsh {} ".format(self.OC, node['ovs_pod']) command_prefix = "{} rsh {} ".format(self.OC, node['ovs_pod'])
capture_interface = interface if interface else 'datacentre' if interface:
capture_interface = interface
else:
if scenario == 'north_south':
capture_interface = WB_CONF.ovs_pod_ext_interface
else:
capture_interface = WB_CONF.ovs_pod_tunnel_interface
else: else:
capture_client = node['client'] capture_client = node['client']
command_prefix = '' command_prefix = ''
if interface: if interface:
capture_interface = interface capture_interface = interface
else: else:
capture_interface = WB_CONF.node_ext_interface if scenario == 'north_south':
capture_interface = WB_CONF.node_ext_interface
else:
capture_interface = WB_CONF.node_tunnel_interface
node['capture'] = capture.TcpdumpCapture( node['capture'] = capture.TcpdumpCapture(
capture_client, capture_interface, filters, command_prefix) capture_client, capture_interface, filters, command_prefix)
self.useFixture(node['capture']) self.useFixture(node['capture'])
time.sleep(10) time.sleep(2)
def _stop_captures(self): def _stop_captures(self):
for node in self.nodes: for node in self.nodes:
@ -1116,9 +1130,9 @@ class TrafficFlowTest(BaseTempestWhiteboxTestCase):
else: else:
raise TypeError(expected_macs) raise TypeError(expected_macs)
self._start_captures(filters, self._start_captures(filters, scenario='east_west')
interface=local_constants.NODE_TUNNEL_INTERFACE)
self.check_remote_connectivity(ssh_client, dst_ip, ping_count=2) self.check_remote_connectivity(ssh_client, dst_ip, ping_count=2)
time.sleep(5)
self._stop_captures() self._stop_captures()
LOG.debug('Expected routing nodes: {}'.format( LOG.debug('Expected routing nodes: {}'.format(
','.join(expected_routing_nodes))) ','.join(expected_routing_nodes)))
@ -1345,6 +1359,14 @@ class BaseTempestTestCaseOvn(BaseTempestWhiteboxTestCase):
output = self.run_on_master_controller(cmd) output = self.run_on_master_controller(cmd)
self.assertEqual(output, '') self.assertEqual(output, '')
def get_mac_mappings(self, client, physical_network):
output = client.exec_command(
"sudo ovs-vsctl get open . external_ids:ovn-chassis-mac-"
"mappings").strip().replace('"', '').split(',')
for value in output:
if physical_network in value:
return value.replace(physical_network + ':', '')
class BaseDisruptiveTempestTestCase(BaseTempestWhiteboxTestCase): class BaseDisruptiveTempestTestCase(BaseTempestWhiteboxTestCase):
@classmethod @classmethod

View File

@ -123,9 +123,7 @@ class OvnDvrBase(base.TrafficFlowTest, base.BaseTempestTestCaseOvn):
# on environments that schedule ovn routers on compute nodes # on environments that schedule ovn routers on compute nodes
self.exclude_hosts = [self.chassis_name] self.exclude_hosts = [self.chassis_name]
network_details = self.os_admin.network_client.show_network( if self.get_network_type(self.network['id']) == 'vlan':
self.network['id'])
if network_details['network']['provider:network_type'] == 'vlan':
# This helps to avoid false positives with vlan+dvr,see BZ2192633 # This helps to avoid false positives with vlan+dvr,see BZ2192633
self.ignore_outbound = True self.ignore_outbound = True
else: else:
@ -534,10 +532,29 @@ class OvnDvrTest(OvnDvrBase):
device_owner=constants.DEVICE_OWNER_ROUTER_INTF, device_owner=constants.DEVICE_OWNER_ROUTER_INTF,
network_id=networks[i]['id'])['ports'][0]['mac_address']) network_id=networks[i]['id'])['ports'][0]['mac_address'])
def get_mac_mapping_for_vm(vm):
return self.get_mac_mappings(
self.find_node_client(
self.get_host_for_server(vm['server']['id'])),
'datacentre')
def get_expected_macs_for_vlan_tenant():
return [
(servers[0]['port']['mac_address'],
get_mac_mapping_for_vm(servers[1])),
(get_mac_mapping_for_vm(servers[0]),
servers[1]['port']['mac_address'])]
# verify E/W connection between servers # verify E/W connection between servers
ew_expected_macs = [ if self.get_network_type(networks[0]['id']) == 'vlan' and \
(servers[0]['port']['mac_address'], router_port_subnet_macs[0]), self.get_network_type(networks[1]['id']) == 'vlan':
(router_port_subnet_macs[1], servers[1]['port']['mac_address'])] ew_expected_macs = get_expected_macs_for_vlan_tenant()
else:
ew_expected_macs = [
(servers[0]['port']['mac_address'],
router_port_subnet_macs[0]),
(router_port_subnet_macs[1],
servers[1]['port']['mac_address'])]
# remove duplicates # remove duplicates
expected_routing_nodes = list(set([host for host in servers_host])) expected_routing_nodes = list(set([host for host in servers_host]))
if len(expected_routing_nodes) == 1: if len(expected_routing_nodes) == 1:
@ -581,6 +598,13 @@ class OvnDvrTest(OvnDvrBase):
ssh_client=servers_ssh_client[i]) ssh_client=servers_ssh_client[i])
# verify E/W connection between servers # verify E/W connection between servers
# Since with vlan tenant we have MAC mapping on compute nodes
# and VMs migrated, we need to retrieve MAC address combination
# for filtering again
if self.get_network_type(networks[0]['id']) == 'vlan' and \
self.get_network_type(networks[1]['id']) == 'vlan':
ew_expected_macs = get_expected_macs_for_vlan_tenant()
# remove duplicates # remove duplicates
expected_routing_nodes = list(set([host for host in new_servers_host])) expected_routing_nodes = list(set([host for host in new_servers_host]))
if len(expected_routing_nodes) == 1: if len(expected_routing_nodes) == 1: