Merge "Migrate router flavors tests"
This commit is contained in:
commit
c3df80c796
@ -779,6 +779,70 @@ class BaseTempestWhiteboxTestCase(base.BaseTempestTestCase):
|
||||
'Command failure "{}" on "{}" nodes.'.format(
|
||||
cmd, group_name))
|
||||
|
||||
@classmethod
|
||||
def cli_create_resource(cls, cmd,
|
||||
cleanup_method=None,
|
||||
cleanup_args=None,
|
||||
env_prefix=None,
|
||||
no_id_cmd=False,
|
||||
cleanup=True):
|
||||
"""Wrapper for OSP resource creation using commands.
|
||||
Includes sourcing commonly used credentials and common
|
||||
cleanup command call after test class is done/failed.
|
||||
|
||||
:param cmd: Creation command to execute.
|
||||
Example: 'openstack ... create ...'
|
||||
:type cmd: str
|
||||
|
||||
:param cleanup_method: Cleanup function to handle resource
|
||||
after test class is finished/failed.
|
||||
Default method is validate_command from base class in combination
|
||||
with a default delete command deduced from given create command.
|
||||
:type cleanup_method: function, optional
|
||||
|
||||
:param cleanup_args: Arguments passed to cleanup method.
|
||||
Default arguments work in combination with default cleanup_method,
|
||||
which is a single argument, a figured delete appropriate command.
|
||||
:type cleanup_args: tuple, optional
|
||||
|
||||
:param env_prefix: Prefix added to create command.
|
||||
Default prefix sources test user rc file, usually ~/openrc .
|
||||
:type env_prefix: str, optional
|
||||
|
||||
:param no_id_cmd: When set to True omits command suffix to return
|
||||
uuid of created resource, then returns all output.
|
||||
(Useful for non ordinary creation commands, or extra output parsing).
|
||||
Default is False.
|
||||
:type no_id_cmd: bool, optional
|
||||
|
||||
:param cleanup: When set to False, skips adding cleanup in stack
|
||||
(therefore not using cleanup_method and cleanup_args).
|
||||
Default is True.
|
||||
:type cleanup: bool, optional
|
||||
|
||||
:returns: uuid of resource, or all output according to
|
||||
no_id_cmd boolean.
|
||||
Default is uuid.
|
||||
"""
|
||||
# default to overcloudrc credentials
|
||||
_env_prefix = env_prefix or cls.get_osp_cmd_prefix()
|
||||
_get_id_suffix = '' if no_id_cmd else ' -f value -c id'
|
||||
_cmd = _env_prefix + cmd + _get_id_suffix
|
||||
_id = cls.validate_command(_cmd).strip()
|
||||
# default to delete using CLI, and common arguments figured from cmd
|
||||
if cleanup:
|
||||
_cleanup_method = cleanup_method or cls.validate_command
|
||||
_cleanup_args = cleanup_args or (
|
||||
_cmd.partition('create ')[0] + 'delete {}'.format(_id),)
|
||||
cls.addClassResourceCleanup(_cleanup_method, *_cleanup_args)
|
||||
# length of uuid with dashes, as seen in CLI output
|
||||
if not no_id_cmd and len(_id) != 36:
|
||||
raise AssertionError(
|
||||
"Command for resource creation failed: '{}'".format(_cmd))
|
||||
LOG.debug('Command for resource creation succeeded')
|
||||
return _id
|
||||
|
||||
@classmethod
|
||||
def find_host_virsh_name(cls, host):
|
||||
cmd = ("timeout 10 ssh {} sudo virsh list --name | grep -w {}").format(
|
||||
WB_CONF.hypervisor_host, host)
|
||||
|
@ -0,0 +1,341 @@
|
||||
# Copyright 2024 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.
|
||||
|
||||
from uuid import uuid4 as rand_uuid
|
||||
|
||||
from neutron_tempest_plugin import config
|
||||
from oslo_log import log
|
||||
from tempest.lib.common.utils import data_utils
|
||||
from tempest.lib import decorators
|
||||
|
||||
from whitebox_neutron_tempest_plugin.tests.scenario import base as wb_base
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
WB_CONF = CONF.whitebox_neutron_plugin_options
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class RouterFlavorsTestOvn(wb_base.BaseTempestTestCaseOvn):
|
||||
credentials = ['primary', 'admin']
|
||||
required_extensions = ['router', 'flavors']
|
||||
|
||||
USER_DRIVER_DOT_PATH = \
|
||||
'neutron.services.ovn_l3.service_providers.user_defined.UserDefined'
|
||||
|
||||
@classmethod
|
||||
def _test_router_flavors_cli_crud(cls):
|
||||
"""Test CRUD operations for router flavor using openstack CLI.
|
||||
|
||||
Many different API requests are affected by feature when configured
|
||||
|
||||
Therefore this scenario aims to realistically use many of the
|
||||
affected API requests with openstack commands,
|
||||
focus is on the commands which relate to user flavor.
|
||||
ovn flavor is the default and will be tested by other tests.
|
||||
"""
|
||||
# 1) validate new drivers (service providers): ovn and user-defined
|
||||
# are loaded and presented, ovn is shown as the only default
|
||||
cls.validate_command(
|
||||
cls.osp_cmd_prefix + 'openstack network service provider list',
|
||||
pattern='|.*user-defined.*|.*False.*|.*ovn.*|.*True.*|')
|
||||
# 2) create a user service profile for the router flavor
|
||||
cls.profile_create_cmd = (
|
||||
'openstack network flavor profile create '
|
||||
'--description "User defined router flavor profile" '
|
||||
'--enable '
|
||||
'--driver {} '
|
||||
'-f value -c id')
|
||||
cls.user_profile_id = cls.cli_create_resource(
|
||||
cmd=cls.profile_create_cmd.format(cls.USER_DRIVER_DOT_PATH))
|
||||
# 3) create user flavor
|
||||
cls.flavor_create_cmd = (
|
||||
'openstack network flavor create '
|
||||
'--service-type L3_ROUTER_NAT '
|
||||
'--description "User defined flavor for routers" '
|
||||
'{} '
|
||||
'-f value -c id')
|
||||
user_flav_name = data_utils.rand_name('user-flavor')
|
||||
cls.user_flavor_id = cls.cli_create_resource(
|
||||
cmd=cls.flavor_create_cmd.format(user_flav_name))
|
||||
# verify flavor id is in list command output
|
||||
cls.validate_command(
|
||||
'{}openstack network flavor list'.format(cls.osp_cmd_prefix),
|
||||
pattern=cls.user_flavor_id)
|
||||
# 4) add service profile to router flavor
|
||||
profile_fmt = \
|
||||
'openstack network flavor {{}} profile {} {}'.format(
|
||||
cls.user_flavor_id,
|
||||
cls.user_profile_id)
|
||||
cls.cli_create_resource(
|
||||
profile_fmt.format('add'),
|
||||
cleanup_args=(cls.osp_cmd_prefix + profile_fmt.format('remove'),),
|
||||
no_id_cmd=True)
|
||||
# 5) create routers with user-defined/ovn flavors, set external GW
|
||||
user_router_name = data_utils.rand_name('user-router')
|
||||
ovn_router_name = data_utils.rand_name('ovn-router')
|
||||
cls.user_router_id = cls.cli_create_resource(
|
||||
cmd='openstack router create --flavor-id {} {}'.format(
|
||||
cls.user_flavor_id,
|
||||
user_router_name))
|
||||
cls.validate_command(
|
||||
'{}openstack router set --external-gateway {} {}'.format(
|
||||
cls.osp_cmd_prefix,
|
||||
CONF.network.public_network_id,
|
||||
user_router_name))
|
||||
cls.ovn_router_id = cls.cli_create_resource(
|
||||
cmd='openstack router create {}'.format(ovn_router_name))
|
||||
# 6) verify user router is listed
|
||||
cls.validate_command(
|
||||
'{}openstack router list'.format(cls.osp_cmd_prefix),
|
||||
pattern=cls.user_router_id)
|
||||
# 7) verify resource creation related to user flavor router (ipv4/6):
|
||||
# network, subnet, ports (ovn router resources covered as default)
|
||||
# networks
|
||||
user_net_name = data_utils.rand_name('user-network')
|
||||
cls.user_network_id = cls.cli_create_resource(
|
||||
'openstack network create {}'.format(user_net_name))
|
||||
ovn_net_name = data_utils.rand_name('ovn-network')
|
||||
cls.ovn_network_id = cls.cli_create_resource(
|
||||
'openstack network create {}'.format(ovn_net_name))
|
||||
# subnet
|
||||
user_subnet_name = data_utils.rand_name('user-subnet')
|
||||
cls.user_subnet_id = cls.cli_create_resource(
|
||||
('openstack subnet create --subnet-range 10.1.1.0/24 '
|
||||
'--network {} {}').format(
|
||||
user_net_name,
|
||||
user_subnet_name))
|
||||
# add subnet to router
|
||||
router_subnet_fmt = \
|
||||
'openstack router {{}} subnet {} {}'.format(
|
||||
cls.user_router_id,
|
||||
cls.user_subnet_id)
|
||||
cls.cli_create_resource(
|
||||
router_subnet_fmt.format('add'),
|
||||
cleanup_args=(
|
||||
cls.osp_cmd_prefix + router_subnet_fmt.format('remove'),),
|
||||
no_id_cmd=True)
|
||||
# port
|
||||
user_port_name = data_utils.rand_name('user-port')
|
||||
cls.user_port_id = cls.cli_create_resource(
|
||||
('openstack port create --network {} --fixed-ip '
|
||||
'subnet={} {}').format(
|
||||
cls.user_network_id,
|
||||
user_subnet_name,
|
||||
user_port_name))
|
||||
if cls.ipv6:
|
||||
# ipv6 network
|
||||
user_net_ipv6_name = data_utils.rand_name('user-network-ipv6')
|
||||
cls.user_network_ipv6_id = cls.cli_create_resource(
|
||||
'openstack network create {}'.format(user_net_ipv6_name))
|
||||
# ipv6 subnet
|
||||
cidr_ipv6 = 'fd5a:3e75:a5d::/64'
|
||||
ra_address_mode = 'slaac'
|
||||
user_subnet_ipv6_name = data_utils.rand_name('user-subnet-ipv6')
|
||||
cls.user_subnet_ipv6_id = cls.cli_create_resource(
|
||||
('openstack subnet create --ip-version 6 --subnet-range {0} '
|
||||
'--ipv6-ra-mode {1} --ipv6-address-mode {1} '
|
||||
'--network {2} {3}').format(
|
||||
cidr_ipv6,
|
||||
ra_address_mode,
|
||||
user_net_ipv6_name,
|
||||
user_subnet_ipv6_name))
|
||||
# ipv6 add subnet to router
|
||||
router_subnet_ipv6_fmt = \
|
||||
'openstack router {{}} subnet {} {}'.format(
|
||||
cls.user_router_id,
|
||||
cls.user_subnet_ipv6_id)
|
||||
cls.cli_create_resource(
|
||||
router_subnet_ipv6_fmt.format('add'),
|
||||
cleanup_args=(cls.osp_cmd_prefix +
|
||||
router_subnet_ipv6_fmt.format('remove'),),
|
||||
no_id_cmd=True)
|
||||
user_port_ipv6_name = data_utils.rand_name('user-port')
|
||||
# ipv6 port
|
||||
cls.user_port_ipv6_id = cls.cli_create_resource(
|
||||
('openstack port create --network {} --fixed-ip '
|
||||
'subnet={} {}').format(
|
||||
user_net_ipv6_name,
|
||||
user_subnet_ipv6_name,
|
||||
user_port_ipv6_name))
|
||||
# 8) verify fip creation related to user flavor router
|
||||
cls.user_fip_id = cls.cli_create_resource(
|
||||
'openstack floating ip create {} --port {}'.format(
|
||||
CONF.network.public_network_id,
|
||||
user_port_name))
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(RouterFlavorsTestOvn, cls).resource_setup()
|
||||
cls.discover_nodes()
|
||||
# skip tests if OVN router flavors feature isn't enabled
|
||||
for node in cls.nodes:
|
||||
if not node['is_controller']:
|
||||
continue
|
||||
cls.check_service_setting(
|
||||
host=node,
|
||||
service='neutron',
|
||||
config_files=(WB_CONF.neutron_config,),
|
||||
param='service_plugins',
|
||||
value='ovn-router-flavors',
|
||||
msg='OVN router flavors feature not enabled, skipping tests')
|
||||
|
||||
cls.ipv6 = CONF.network_feature_enabled.ipv6_subnet_attributes
|
||||
cls.osp_cmd_prefix = cls.get_osp_cmd_prefix()
|
||||
|
||||
# Testing of router flavors CLI and CRUD starts here
|
||||
# (first basic test does essential resource setup for other tests)
|
||||
cls._test_router_flavors_cli_crud()
|
||||
|
||||
@decorators.attr(type='negative')
|
||||
@decorators.idempotent_id('5ad9c4ac-c53f-45dc-8e5e-1207d4d9b05f')
|
||||
def test_router_flavors_negatives(self):
|
||||
"""Test CRUD actions expected failures for router flavors when
|
||||
using openstack commands.
|
||||
"""
|
||||
# 1) test add/remove of non existing service profile to network flavor
|
||||
# using id/name
|
||||
bad_id = rand_uuid()
|
||||
# test add non existing profile name
|
||||
cmd = '{}openstack network flavor add profile {} kamehameha'.format(
|
||||
self.osp_cmd_prefix,
|
||||
self.user_flavor_id)
|
||||
self.assertFalse(
|
||||
self.validate_command(
|
||||
cmd,
|
||||
pattern='No ServiceProfile found for kamehameha',
|
||||
ret_bool_status=True),
|
||||
'Command should fail -> "{}"'.format(cmd))
|
||||
# test remove non existing profile name
|
||||
self.assertFalse(
|
||||
self.validate_command(
|
||||
cmd.replace(' add ', ' remove ', 1),
|
||||
pattern='No ServiceProfile found for kamehameha',
|
||||
ret_bool_status=True),
|
||||
'Command should fail -> "{}"'.format(cmd))
|
||||
# test add non existing profile id
|
||||
cmd = '{}openstack network flavor add profile {} {}'.format(
|
||||
self.osp_cmd_prefix,
|
||||
self.user_flavor_id,
|
||||
bad_id)
|
||||
self.assertFalse(
|
||||
self.validate_command(
|
||||
cmd,
|
||||
pattern='No ServiceProfile found for {}'.format(bad_id),
|
||||
ret_bool_status=True),
|
||||
'Command should fail -> "{}"'.format(cmd))
|
||||
# test remove non existing profile id
|
||||
self.assertFalse(
|
||||
self.validate_command(
|
||||
cmd.replace(' add ', ' remove ', 1),
|
||||
pattern='No ServiceProfile found for {}'.format(bad_id),
|
||||
ret_bool_status=True),
|
||||
'Command should fail -> "{}"'.format(cmd))
|
||||
# # 2) test removal of service profile from network flavor while
|
||||
# # there is existing router which uses service profile
|
||||
|
||||
# # NOTE(mblue): TEST MAY FAIL, WAITING FOR FIX.
|
||||
# # related bug: https://bugzilla.redhat.com/show_bug.cgi?id=2237290
|
||||
# # Pattern argument for failure message can be added when fixed,
|
||||
# # for now only command failure with non zero exit status verified.
|
||||
# cmd = '{}openstack network flavor remove profile {} {}'.format(
|
||||
# self.osp_cmd_prefix,
|
||||
# self.user_flavor_id,
|
||||
# self.user_profile_id)
|
||||
# try:
|
||||
# self.assertFalse(
|
||||
# self.validate_command(
|
||||
# cmd,
|
||||
# ret_bool_status=True),
|
||||
# 'Command should fail -> "{}"'.format(cmd))
|
||||
# # re-add profile in case of failure, prevent chain of failures
|
||||
# # for next test classes/methods
|
||||
# except AssertionError:
|
||||
# LOG.exception('Test failed')
|
||||
# self.validate_command(cmd.replace(' remove ', ' add ', 1))
|
||||
# raise
|
||||
# # 3) test disable of service profile from network flavor while
|
||||
# # there is existing router which uses service profile
|
||||
# cmd = '{}openstack network flavor profile set --disable {}'.format(
|
||||
# self.osp_cmd_prefix,
|
||||
# self.user_profile_id)
|
||||
# try:
|
||||
# self.assertFalse(
|
||||
# self.validate_command(
|
||||
# cmd,
|
||||
# ret_bool_status=True),
|
||||
# 'Command should fail -> "{}"'.format(cmd))
|
||||
# # enable profile in case of failure, prevent chain of failures
|
||||
# # for next test classes/methods
|
||||
# except AssertionError:
|
||||
# LOG.exception('Test failed')
|
||||
# self.validate_command(cmd.replace('--disable ', '--enable ', 1))
|
||||
# raise
|
||||
|
||||
# 4) test show flavor details of already deleted flavor, and bad id
|
||||
cmd_fmt = '{}openstack network flavor show {{}}'.format(
|
||||
self.osp_cmd_prefix)
|
||||
# create and delete temporary flavor
|
||||
temp_flavor_name = data_utils.rand_name('temp-flavor')
|
||||
temp_flavor_id = self.cli_create_resource(
|
||||
cmd=self.flavor_create_cmd.format(temp_flavor_name),
|
||||
cleanup=False)
|
||||
self.validate_command(
|
||||
'{}openstack network flavor delete {}'.format(
|
||||
self.osp_cmd_prefix,
|
||||
temp_flavor_id))
|
||||
# verify failure of flavor show request for deleted id/name
|
||||
cmd = cmd_fmt.format(temp_flavor_name)
|
||||
self.assertFalse(
|
||||
self.validate_command(
|
||||
cmd,
|
||||
pattern='No Flavor found for {}'.format(temp_flavor_name),
|
||||
ret_bool_status=True),
|
||||
'Command should fail -> "{}"'.format(cmd))
|
||||
cmd = cmd_fmt.format(temp_flavor_id)
|
||||
self.assertFalse(
|
||||
self.validate_command(
|
||||
cmd,
|
||||
pattern='No Flavor found for {}'.format(temp_flavor_id),
|
||||
ret_bool_status=True),
|
||||
'Command should fail -> "{}"'.format(cmd))
|
||||
# verify failure of flavor show request for non existing id
|
||||
cmd = cmd_fmt.format(bad_id)
|
||||
self.assertFalse(
|
||||
self.validate_command(
|
||||
cmd,
|
||||
pattern='No Flavor found for {}'.format(bad_id),
|
||||
ret_bool_status=True),
|
||||
'Command should fail -> "{}"'.format(cmd))
|
||||
# 5) test deleting network flavor while floating ip routed using
|
||||
# flavor exists
|
||||
cmd = '{}openstack network flavor delete {}'.format(
|
||||
self.osp_cmd_prefix,
|
||||
self.user_flavor_id)
|
||||
try:
|
||||
self.assertFalse(
|
||||
self.validate_command(
|
||||
cmd,
|
||||
pattern='ConflictException: 409',
|
||||
ret_bool_status=True),
|
||||
'Command should fail -> "{}"'.format(cmd))
|
||||
# re-create user flavor in case of failure, prevent chain of failures
|
||||
# for next test classes/methods
|
||||
except AssertionError:
|
||||
LOG.exception('Test failed')
|
||||
user_flav_name = data_utils.rand_name('user-flavor')
|
||||
self.user_flavor_id = self.cli_create_resource(
|
||||
cmd=self.flavor_create_cmd.format(user_flav_name))
|
||||
raise
|
@ -23,6 +23,7 @@
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario)"
|
||||
tempest_exclude_regex: "\
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario.test_metadata_rate_limiting)|\
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario.test_router_flavors)|\
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario.test_security_group_logging)|\
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario.test_l3ha_ovn)|\
|
||||
(test_multicast.*restart)|\
|
||||
@ -416,9 +417,20 @@
|
||||
name: whitebox-neutron-tempest-plugin-ovn-single-thread
|
||||
parent: whitebox-neutron-tempest-plugin-ovn
|
||||
vars:
|
||||
network_api_extensions_ovn:
|
||||
- vlan-transparent
|
||||
- ovn-router-flavors
|
||||
devstack_localrc:
|
||||
NETWORK_API_EXTENSIONS: "{{ (network_api_extensions_common + network_api_extensions_ovn) | join(',') }}"
|
||||
devstack_local_conf:
|
||||
post-config:
|
||||
$NEUTRON_CONF:
|
||||
service_providers:
|
||||
service_provider: "L3_ROUTER_NAT:user-defined:neutron.services.ovn_l3.service_providers.user_defined.UserDefined"
|
||||
tempest_concurrency: 1
|
||||
tempest_test_regex: "\
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario.test_metadata_rate_limiting)|\
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario.test_router_flavors)|\
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario.test_security_group_logging)|\
|
||||
(^whitebox_neutron_tempest_plugin.tests.scenario.test_l3ha_ovn)|\
|
||||
(test_multicast.*restart)|\
|
||||
|
Loading…
Reference in New Issue
Block a user