From 87a7a5e32e3ebe41ac66646290022802383dd644 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Mon, 29 Mar 2021 23:22:59 +0200 Subject: [PATCH] [Fullstack] Add segmentation_id update test This patch adds new fullstack test which spawns 2 "hosts" and 2 "VMs" on those hosts. Both VMs are plugged to the vlan network with some segmentation id. Next segmentation id of the network is updated and test ensures that new vlan id is configured in the physical bridge on both "hosts" and connectivity between VMs still works fine. Test runs only with Openvswitch agents as Linuxbridge doesn't supports live update of the segmentation_id in the network. Change-Id: I459aac7f4e9afe679d8ece1c27d0be49cec8e4ff --- neutron/tests/common/agents/l2_extensions.py | 19 +++++ neutron/tests/fullstack/base.py | 15 ++++ neutron/tests/fullstack/test_connectivity.py | 17 +---- .../tests/fullstack/test_segmentation_id.py | 76 ++++++++++++++----- 4 files changed, 93 insertions(+), 34 deletions(-) diff --git a/neutron/tests/common/agents/l2_extensions.py b/neutron/tests/common/agents/l2_extensions.py index 594befb08d2..6da72a55b02 100644 --- a/neutron/tests/common/agents/l2_extensions.py +++ b/neutron/tests/common/agents/l2_extensions.py @@ -135,3 +135,22 @@ def wait_for_dscp_marked_packet(sender_vm, receiver_vm, dscp_mark): "to %(dst)s" % {'dscp_mark': dscp_mark, 'src': sender_vm.ip, 'dst': receiver_vm.ip}) + + +def extract_vlan_id(flows): + if flows: + flow_list = flows.splitlines() + for flow in flow_list: + if 'mod_vlan_vid' in flow: + actions = flow.partition('actions=')[2] + after_mod = actions.partition('mod_vlan_vid:')[2] + return int(after_mod.partition(',')[0]) + + +def wait_for_mod_vlan_id_applied(bridge, expected_vlan_id): + def _vlan_id_rule_applied(): + flows = bridge.dump_flows_for(table='0') + vlan_id = extract_vlan_id(flows) + return vlan_id == expected_vlan_id + + common_utils.wait_until_true(_vlan_id_rule_applied) diff --git a/neutron/tests/fullstack/base.py b/neutron/tests/fullstack/base.py index 457d8ab0f57..2da8c845d64 100644 --- a/neutron/tests/fullstack/base.py +++ b/neutron/tests/fullstack/base.py @@ -30,6 +30,7 @@ from neutron.tests.common import helpers from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers from neutron.tests.fullstack.resources import client as client_resource +from neutron.tests.fullstack.resources import machine from neutron.tests.unit import testlib_api @@ -159,6 +160,20 @@ class BaseFullStackTestCase(testlib_api.MySQLTestCaseMixin, "tag", network.get("provider:segmentation_id")) return vm + def _prepare_vms_in_net(self, tenant_uuid, network, use_dhcp=False): + vms = machine.FakeFullstackMachinesList( + self.useFixture( + machine.FakeFullstackMachine( + host, + network['id'], + tenant_uuid, + self.safe_client, + use_dhcp=use_dhcp)) + for host in self.environment.hosts) + + vms.block_until_all_boot() + return vms + def assert_namespace_exists(self, ns_name): common_utils.wait_until_true( lambda: ip_lib.network_namespace_exists(ns_name, diff --git a/neutron/tests/fullstack/test_connectivity.py b/neutron/tests/fullstack/test_connectivity.py index cf00ee3cd6d..c78d153dbcf 100644 --- a/neutron/tests/fullstack/test_connectivity.py +++ b/neutron/tests/fullstack/test_connectivity.py @@ -24,7 +24,6 @@ from neutron.tests.common import net_helpers from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import config from neutron.tests.fullstack.resources import environment -from neutron.tests.fullstack.resources import machine from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests @@ -74,24 +73,10 @@ class BaseConnectivitySameNetworkTest(base.BaseFullStackTestCase): return network - def _prepare_vms_in_net(self, tenant_uuid, network): - vms = machine.FakeFullstackMachinesList( - self.useFixture( - machine.FakeFullstackMachine( - host, - network['id'], - tenant_uuid, - self.safe_client, - use_dhcp=self.use_dhcp)) - for host in self.environment.hosts) - - vms.block_until_all_boot() - return vms - def _prepare_vms_in_single_network(self): tenant_uuid = uuidutils.generate_uuid() network = self._prepare_network(tenant_uuid) - return self._prepare_vms_in_net(tenant_uuid, network) + return self._prepare_vms_in_net(tenant_uuid, network, self.use_dhcp) def _test_connectivity(self): vms = self._prepare_vms_in_single_network() diff --git a/neutron/tests/fullstack/test_segmentation_id.py b/neutron/tests/fullstack/test_segmentation_id.py index be7ca60da34..99475587a67 100644 --- a/neutron/tests/fullstack/test_segmentation_id.py +++ b/neutron/tests/fullstack/test_segmentation_id.py @@ -14,6 +14,7 @@ from neutron_lib import constants from neutronclient.common import exceptions from oslo_utils import uuidutils +from neutron.tests.common.agents import l2_extensions from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import config from neutron.tests.fullstack.resources import environment @@ -22,28 +23,28 @@ from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests -class TestSegmentationId(base.BaseFullStackTestCase): +class BaseSegmentationIdTest(base.BaseFullStackTestCase): - scenarios = [ - ('Open vSwitch Agent', {'l2_agent_type': constants.AGENT_TYPE_OVS}), - ('Linux Bridge Agent', { - 'l2_agent_type': constants.AGENT_TYPE_LINUXBRIDGE})] + network_type = "vlan" def setUp(self): - hosts_description = [ + host_descriptions = [ environment.HostDescription( - l2_agent_type=self.l2_agent_type, l3_agent=False)] + l2_agent_type=self.l2_agent_type, l3_agent=False + ) for _ in range(self.num_hosts)] env = environment.Environment( - environment.EnvironmentDescription(), - hosts_description) + environment.EnvironmentDescription( + network_type=self.network_type), + host_descriptions) - super(TestSegmentationId, self).setUp(env) - self.tenant_id = uuidutils.generate_uuid() + super(BaseSegmentationIdTest, self).setUp(env) + self.project_id = uuidutils.generate_uuid() def _create_network(self): seg_id = 100 network = self.safe_client.create_network( - self.tenant_id, network_type="vlan", segmentation_id=seg_id, + self.project_id, network_type=self.network_type, + segmentation_id=seg_id, physical_network=config.PHYSICAL_NETWORK_NAME) self.assertEqual(seg_id, network['provider:segmentation_id']) @@ -54,7 +55,6 @@ class TestSegmentationId(base.BaseFullStackTestCase): return network def _update_segmentation_id(self, network): - # Now change segmentation_id to some other value new_seg_id = network['provider:segmentation_id'] + 1 new_net_args = {'provider:segmentation_id': new_seg_id} network = self.safe_client.update_network( @@ -65,21 +65,32 @@ class TestSegmentationId(base.BaseFullStackTestCase): network = self.safe_client.client.show_network( network['id'])['network'] self.assertEqual(new_seg_id, network['provider:segmentation_id']) + return network + + +class TestSegmentationId(BaseSegmentationIdTest): + + scenarios = [ + ('Open vSwitch Agent', {'l2_agent_type': constants.AGENT_TYPE_OVS}), + ('Linux Bridge Agent', { + 'l2_agent_type': constants.AGENT_TYPE_LINUXBRIDGE})] + num_hosts = 1 def test_change_segmentation_id_no_ports_in_network(self): network = self._create_network() + # Now change segmentation_id to some other value self._update_segmentation_id(network) def test_change_segmentation_id_with_unbound_ports_in_network(self): network = self._create_network() self.safe_client.create_subnet( - self.tenant_id, network['id'], '20.0.0.0/24') + self.project_id, network['id'], '20.0.0.0/24') # Unbound port - self.safe_client.create_port(self.tenant_id, network['id']) + self.safe_client.create_port(self.project_id, network['id']) # Port failed to bind - self.safe_client.create_port(self.tenant_id, network['id'], + self.safe_client.create_port(self.project_id, network['id'], "non-exisiting-host") self._update_segmentation_id(network) @@ -88,8 +99,8 @@ class TestSegmentationId(base.BaseFullStackTestCase): network = self._create_network() self.safe_client.create_subnet( - self.tenant_id, network['id'], '20.0.0.0/24') - self.safe_client.create_port(self.tenant_id, network['id'], + self.project_id, network['id'], '20.0.0.0/24') + self.safe_client.create_port(self.project_id, network['id'], self.environment.hosts[0].hostname) if self.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: @@ -99,3 +110,32 @@ class TestSegmentationId(base.BaseFullStackTestCase): self._update_segmentation_id, network) else: self._update_segmentation_id(network) + + +class TestSegmentationIdConnectivity(BaseSegmentationIdTest): + + scenarios = [ + ('Open vSwitch Agent', {'l2_agent_type': constants.AGENT_TYPE_OVS})] + + num_hosts = 2 + + def _ensure_vlan_id_set_in_flows(self, vlan_id): + for host in self.environment.hosts: + l2_extensions.wait_for_mod_vlan_id_applied(host.br_phys, vlan_id) + + def test_connectivity_after_segmentation_id_update(self): + network = self._create_network() + self.safe_client.create_subnet( + self.project_id, network['id'], + cidr='10.0.0.0/24', + gateway_ip='10.0.0.1', + name='subnet-test', + enable_dhcp=False) + + vms = self._prepare_vms_in_net(self.project_id, network, False) + self._ensure_vlan_id_set_in_flows(network['provider:segmentation_id']) + vms.ping_all() + + network = self._update_segmentation_id(network) + self._ensure_vlan_id_set_in_flows(network['provider:segmentation_id']) + vms.ping_all()