From abefbad8e1e3661923a9012fde1464ee2db71d75 Mon Sep 17 00:00:00 2001 From: Roman Safronov Date: Tue, 24 Aug 2021 15:29:18 +0300 Subject: [PATCH] Add initial trunk test The test will create a VM with a trunk port and verify that the VM's FIP is accessible after the VM is rebooted. It was found that this scenario is failing after OVN migration, see [1], so we need to add coverage for it. [1] https://bugzilla.redhat.com/show_bug.cgi?id=1857652#c12 Change-Id: I5ff39defab875cd4a9a4a1ba71192d29d7b53913 --- tobiko/openstack/neutron/config.py | 11 ++++- tobiko/openstack/stacks/__init__.py | 2 + tobiko/openstack/stacks/_centos.py | 24 ++++++++++ tobiko/openstack/stacks/_nova.py | 8 ++++ tobiko/openstack/stacks/_redhat.py | 5 +++ tobiko/openstack/stacks/nova/server.yaml | 49 +++++++++++++++++++++ tobiko/tests/scenario/neutron/test_trunk.py | 44 ++++++++++++++++++ 7 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 tobiko/tests/scenario/neutron/test_trunk.py diff --git a/tobiko/openstack/neutron/config.py b/tobiko/openstack/neutron/config.py index 0bb742379..88f5ad03d 100644 --- a/tobiko/openstack/neutron/config.py +++ b/tobiko/openstack/neutron/config.py @@ -64,7 +64,16 @@ OPTIONS = [ help="The direction for the QoS Policy Rule"), cfg.IntOpt('dscp_mark', default=40, - help="The DSCP marking value for the QoS Policy Rule") + help="The DSCP marking value for the QoS Policy Rule"), + cfg.StrOpt('trunk_subport_segmentation_type', + default='vlan', + help="Trunk subport segmentation type"), + cfg.IntOpt('trunk_subport_segmentation_id', + default=101, + help="Trunk subport segmentation ID"), + cfg.StrOpt('trunk_subport_subnet_cidr', + default='192.168.101.0/24', + help="The CIDR block for trunk subport subnet") ] diff --git a/tobiko/openstack/stacks/__init__.py b/tobiko/openstack/stacks/__init__.py index d80500d54..6bbe7444e 100644 --- a/tobiko/openstack/stacks/__init__.py +++ b/tobiko/openstack/stacks/__init__.py @@ -30,6 +30,7 @@ CentosFlavorStackFixture = _centos.CentosFlavorStackFixture CentosImageFixture = _centos.CentosImageFixture CentosServerStackFixture = _centos.CentosServerStackFixture Centos7ServerStackFixture = _centos.Centos7ServerStackFixture +CentosTrunkServerStackFixture = _centos.CentosTrunkServerStackFixture CirrosFlavorStackFixture = _cirros.CirrosFlavorStackFixture CirrosImageFixture = _cirros.CirrosImageFixture @@ -51,6 +52,7 @@ FedoraServerStackFixture = _fedora.FedoraServerStackFixture RedHatFlavorStackFixture = _redhat.RedHatFlavorStackFixture RhelImageFixture = _redhat.RhelImageFixture RedHatServerStackFixture = _redhat.RedHatServerStackFixture +RedHatTrunkServerStackFixture = _redhat.RedHatTrunkServerStackFixture L3haNetworkStackFixture = _l3ha.L3haNetworkStackFixture L3haServerStackFixture = _l3ha.L3haServerStackFixture diff --git a/tobiko/openstack/stacks/_centos.py b/tobiko/openstack/stacks/_centos.py index 80f19b48c..a79c5cdfc 100644 --- a/tobiko/openstack/stacks/_centos.py +++ b/tobiko/openstack/stacks/_centos.py @@ -71,3 +71,27 @@ class Centos7ServerStackFixture(CentosServerStackFixture): #: Glance image used to create a Nova server instance image_fixture = tobiko.required_setup_fixture(Centos7ImageFixture) + + +class CentosTrunkServerStackFixture( + CentosServerStackFixture, _nova.TrunkServerStackFixture): + + interface = 'eth0' + + @property + def user_data(self): + return ("#cloud-config\n" + "write_files:\n" + "- path: {path}/ifcfg-{interface}.{index}\n" + " owner: \"root\"\n" + " permissions: \"777\"\n" + " content: |\n" + " DEVICE=\"{interface}.{index}\"\n" + " BOOTPROTO=\"none\"\n" + " ONBOOT=\"yes\"\n" + " VLAN=\"yes\"\n" + " PERSISTENT_DHCLIENT=\"no\"\n" + "runcmd:\n" + "- [ sh, -c , \"systemctl restart NetworkManager\" ]".format( + path='/etc/sysconfig/network-scripts/', + interface=self.interface, index=self.trunk_subport_vlan)) diff --git a/tobiko/openstack/stacks/_nova.py b/tobiko/openstack/stacks/_nova.py index 8899f0d23..a43dc369f 100644 --- a/tobiko/openstack/stacks/_nova.py +++ b/tobiko/openstack/stacks/_nova.py @@ -525,3 +525,11 @@ class AntiAffinityServerGroupStackFixture(tobiko.SharedFixture): @property def scheduler_group(self): return self.server_group_stack.anti_affinity_server_group_id + + +@neutron.skip_if_missing_networking_extensions('trunk') +class TrunkServerStackFixture(CloudInitServerStackFixture): + has_trunk = True + segmentation_id = CONF.tobiko.neutron.trunk_subport_segmentation_id + segmentation_type = CONF.tobiko.neutron.trunk_subport_segmentation_type + trunk_subport_cidr = CONF.tobiko.neutron.trunk_subport_subnet_cidr diff --git a/tobiko/openstack/stacks/_redhat.py b/tobiko/openstack/stacks/_redhat.py index e46817114..e4fe77f03 100644 --- a/tobiko/openstack/stacks/_redhat.py +++ b/tobiko/openstack/stacks/_redhat.py @@ -86,3 +86,8 @@ class RedHatServerStackFixture(_centos.CentosServerStackFixture): #: Flavor used to create a Nova server instance flavor_stack = tobiko.required_setup_fixture(RedHatFlavorStackFixture) + + +class RedHatTrunkServerStackFixture( + RedHatServerStackFixture, _centos.CentosTrunkServerStackFixture): + pass diff --git a/tobiko/openstack/stacks/nova/server.yaml b/tobiko/openstack/stacks/nova/server.yaml index d12a7a1bb..4a1d68285 100644 --- a/tobiko/openstack/stacks/nova/server.yaml +++ b/tobiko/openstack/stacks/nova/server.yaml @@ -72,6 +72,24 @@ parameters: default: '' description: Optional user_data to be passed to the server + has_trunk: + type: boolean + default: false + + trunk_subport_cidr: + type: string + default: '' + + segmentation_type: + type: string + description: Segmentation type for trunk subport + default: '' + + segmentation_id: + type: number + description: Segmentation ID for trunk subport + default: 0 + conditions: has_floating_ip: @@ -80,6 +98,9 @@ conditions: use_extra_dhcp_opts: get_param: use_extra_dhcp_opts + has_trunk: + get_param: has_trunk + resources: port: @@ -133,6 +154,34 @@ resources: floating_network: {get_param: floating_network} port_id: {get_resource: port} + trunk_subport_network: + type: OS::Neutron::Net + condition: has_trunk + + trunk_subport_subnet: + type: OS::Neutron::Subnet + condition: has_trunk + properties: + network: {get_resource: trunk_subport_network} + cidr: {get_param: trunk_subport_cidr} + + trunk_subport: + type: OS::Neutron::Port + condition: has_trunk + properties: + network: {get_resource: trunk_subport_network} + mac_address: {get_attr: [port, mac_address]} + + trunk: + type: OS::Neutron::Trunk + description: Trunk port connected to the server + condition: has_trunk + properties: + port: {get_resource: port} + sub_ports: + - {port: {get_resource: trunk_subport}, + segmentation_type: {get_param: segmentation_type}, + segmentation_id: {get_param: segmentation_id}} outputs: diff --git a/tobiko/tests/scenario/neutron/test_trunk.py b/tobiko/tests/scenario/neutron/test_trunk.py new file mode 100644 index 000000000..1d49b5e37 --- /dev/null +++ b/tobiko/tests/scenario/neutron/test_trunk.py @@ -0,0 +1,44 @@ +# Copyright (c) 2021 Red Hat +# 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. +from __future__ import absolute_import + +import pytest +import testtools + +import tobiko +from tobiko import config +from tobiko.openstack import nova +from tobiko.openstack import stacks +from tobiko.shell import ping + + +CONF = config.CONF + + +class TrunkTest(testtools.TestCase): + """Tests trunk functionality""" + + stack = tobiko.required_setup_fixture( + stacks.CentosTrunkServerStackFixture) + + @pytest.mark.ovn_migration + def test_trunk_fip_after_reboot(self): + ping.assert_reachable_hosts([self.stack.floating_ip_address]) + server = nova.shutoff_server(self.stack.server_id) + self.assertEqual('SHUTOFF', server.status) + ping.assert_unreachable_hosts([self.stack.ip_address]) + server = nova.activate_server(self.stack.server_id) + self.assertEqual('ACTIVE', server.status) + ping.assert_reachable_hosts([self.stack.ip_address])