Merge "Migrate availability zone tests"
This commit is contained in:
commit
39ba5548ed
@ -83,5 +83,8 @@ WhiteboxNeutronPluginOptions = [
|
|||||||
help='Common user to access openstack nodes via ssh.'),
|
help='Common user to access openstack nodes via ssh.'),
|
||||||
cfg.StrOpt('overcloud_key_file',
|
cfg.StrOpt('overcloud_key_file',
|
||||||
default='/home/tempest/.ssh/id_rsa',
|
default='/home/tempest/.ssh/id_rsa',
|
||||||
help='ssh private key file path for overcloud nodes access.')
|
help='ssh private key file path for overcloud nodes access.'),
|
||||||
|
cfg.StrOpt('neutron_config',
|
||||||
|
default='/etc/neutron/neutron.conf',
|
||||||
|
help='Path to neutron configuration file.')
|
||||||
]
|
]
|
||||||
|
@ -0,0 +1,418 @@
|
|||||||
|
# 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 neutron_lib import constants
|
||||||
|
from neutron_tempest_plugin.common import utils
|
||||||
|
from neutron_tempest_plugin import config
|
||||||
|
from tempest.lib import decorators
|
||||||
|
from tempest.lib import exceptions
|
||||||
|
|
||||||
|
from whitebox_neutron_tempest_plugin.tests.scenario import base
|
||||||
|
|
||||||
|
AZ_SUPPORTED_AGENTS = [constants.AGENT_TYPE_DHCP, constants.AGENT_TYPE_L3]
|
||||||
|
CONF = config.CONF
|
||||||
|
WB_CONF = config.CONF.whitebox_neutron_plugin_options
|
||||||
|
|
||||||
|
|
||||||
|
class NeutronAvaliabilityzonesTest(base.BaseTempestWhiteboxTestCase):
|
||||||
|
|
||||||
|
credentials = ['primary', 'admin']
|
||||||
|
required_extensions = ['network_availability_zone',
|
||||||
|
'availability_zone', 'router_availability_zone']
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resource_setup(cls):
|
||||||
|
super(NeutronAvaliabilityzonesTest, cls).resource_setup()
|
||||||
|
cls.client = cls.os_adm.network_client
|
||||||
|
cls.get_neutron_agent_availability_zones()
|
||||||
|
if not cls.AZs_list:
|
||||||
|
raise cls.skipException("No availability zones configured")
|
||||||
|
if cls.has_ovn_support:
|
||||||
|
raise cls.skipException(
|
||||||
|
"These availability zone tests are supported on OVS only.")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_neutron_agent_availability_zones(cls):
|
||||||
|
"""Obtain list agents availability_zones for an agent type
|
||||||
|
Only L3 and DHCP agent support availability_zone, default
|
||||||
|
availability_zone is "nova".
|
||||||
|
"""
|
||||||
|
body = cls.client.list_agents()
|
||||||
|
agents = body['agents']
|
||||||
|
cls.AZs_list = []
|
||||||
|
cls.l3_agent_list = {}
|
||||||
|
cls.dhcp_agent_list = {}
|
||||||
|
cls.agent_conf = {}
|
||||||
|
for agent in agents:
|
||||||
|
if agent.get('agent_type') in AZ_SUPPORTED_AGENTS:
|
||||||
|
az = agent.get('availability_zone')
|
||||||
|
if (az and (az not in cls.AZs_list)):
|
||||||
|
cls.AZs_list.append(az)
|
||||||
|
if agent.get('agent_type') == constants.AGENT_TYPE_L3:
|
||||||
|
cls.l3_agent_list[agent.get('host')] = az
|
||||||
|
else:
|
||||||
|
cls.dhcp_agent_list[agent.get('host')] = az
|
||||||
|
cmd = 'sudo podman exec neutron_api crudini --get {} DEFAULT {{}}' \
|
||||||
|
'|| echo'.format(WB_CONF.neutron_config)
|
||||||
|
if WB_CONF.openstack_type == 'devstack':
|
||||||
|
cmd_executor = cls.run_on_master_controller
|
||||||
|
|
||||||
|
def integer(int_var):
|
||||||
|
try:
|
||||||
|
return int(int_var)
|
||||||
|
except ValueError:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def array(list_var):
|
||||||
|
try:
|
||||||
|
return_list = list_var.strip().split(',')
|
||||||
|
if return_list == ['']:
|
||||||
|
raise AttributeError('Empty string can not be splitted')
|
||||||
|
return return_list
|
||||||
|
except AttributeError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
agent_params = {'max_l3_agents_per_router': integer,
|
||||||
|
'min_l3_agents_per_router': integer,
|
||||||
|
'dhcp_agents_per_network': integer,
|
||||||
|
'default_availability_zones': array}
|
||||||
|
for param, func in agent_params.items():
|
||||||
|
cls.agent_conf[param] = func(
|
||||||
|
cmd_executor(cmd.format(param)))
|
||||||
|
|
||||||
|
def _check_az_router(self, az, router):
|
||||||
|
"""Check if a router was deployed over the agents
|
||||||
|
in the configured Azs
|
||||||
|
"""
|
||||||
|
self._check_az_resource(az, router=router)
|
||||||
|
|
||||||
|
def _check_az_network(self, az, network):
|
||||||
|
"""Check if a network was deployed over the agents
|
||||||
|
in the configured Azs
|
||||||
|
"""
|
||||||
|
self._check_az_resource(az, network=network)
|
||||||
|
|
||||||
|
def _check_az_resource(self, az, network=None, router=None):
|
||||||
|
"""Check that resource is correctly spawned in required AZs
|
||||||
|
|
||||||
|
Function checks that router or network (resource) is assigned to the
|
||||||
|
relevant agents plus verifies that there are network namespaces
|
||||||
|
configured on the nodes.
|
||||||
|
There are several possible scenarios:
|
||||||
|
Scenario 1 (not enough agents):
|
||||||
|
Resource is expected to be deployed over several (lets say 3)
|
||||||
|
agents, but there are less or equal amount of them available in the
|
||||||
|
provided list of availability zones. In this case the resource should
|
||||||
|
be spawned over all available agents
|
||||||
|
Scenario 2 (not enough AZs):
|
||||||
|
Resource is expected to be deployed over several (lets say 3) agents,
|
||||||
|
but there are less or equal amount of availability zones are provided
|
||||||
|
as a hint. In this case there should be at least one agent from each
|
||||||
|
availability zone to host the resource and the all other agents will
|
||||||
|
be chosen randomly
|
||||||
|
Scenario 3 (too many AZs):
|
||||||
|
Resource is expected to be deployed over several (lets say 3)
|
||||||
|
agents, but more (lets say 4) availability zones are provided as a
|
||||||
|
hint. In this case the resource should be spawned over 3 random
|
||||||
|
availability zones (one agent per zone).
|
||||||
|
"""
|
||||||
|
if not network and not router:
|
||||||
|
raise AttributeError('Network or router object required')
|
||||||
|
elif network and router:
|
||||||
|
raise AttributeError('Only one object should be provided')
|
||||||
|
elif network:
|
||||||
|
resource = network
|
||||||
|
resource_list = self.dhcp_agent_list
|
||||||
|
netspace_name = 'qdhcp-' + network['id']
|
||||||
|
conf_param = 'dhcp_agents_per_network'
|
||||||
|
list_func = self.client.list_dhcp_agent_hosting_network
|
||||||
|
else:
|
||||||
|
resource = router
|
||||||
|
resource_list = self.l3_agent_list
|
||||||
|
netspace_name = 'qrouter-' + router['id']
|
||||||
|
conf_param = 'max_l3_agents_per_router'
|
||||||
|
list_func = self.client.list_l3_agents_hosting_router
|
||||||
|
body = list_func(resource['id'])
|
||||||
|
resource_hosts = list(agent['host'] for agent in body['agents'])
|
||||||
|
az_hosts = []
|
||||||
|
for agent_host, agent_zone in resource_list.items():
|
||||||
|
if agent_zone in az:
|
||||||
|
az_hosts.append(agent_host)
|
||||||
|
|
||||||
|
def test_resource_namespace():
|
||||||
|
node_name = host.split('.')[0] + (
|
||||||
|
'.ctlplane' if WB_CONF.openstack_type == 'tripleo' else '')
|
||||||
|
netns = self.get_node_client(node_name).exec_command('ip netns')
|
||||||
|
netspaces = []
|
||||||
|
for ns in netns.split('\n'):
|
||||||
|
netspaces.append(ns.split(' ')[0])
|
||||||
|
if netspace_name in netspaces:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Scenario 1 from docstring
|
||||||
|
if self.agent_conf[conf_param] >= len(az_hosts):
|
||||||
|
for host in az_hosts:
|
||||||
|
self.assertIn(host, resource_hosts)
|
||||||
|
utils.wait_until_true(test_resource_namespace)
|
||||||
|
# Scenario 2 from docstring
|
||||||
|
elif self.agent_conf[conf_param] >= len(az):
|
||||||
|
for host in resource_hosts:
|
||||||
|
self.assertIn(host, az_hosts)
|
||||||
|
utils.wait_until_true(test_resource_namespace)
|
||||||
|
tmp_zones = []
|
||||||
|
for agent_details in body['agents']:
|
||||||
|
self.assertIn(agent_details['availability_zone'], az)
|
||||||
|
tmp_zones.append(agent_details['availability_zone'])
|
||||||
|
self.assertEqual(set(az).symmetric_difference(set(tmp_zones)),
|
||||||
|
set())
|
||||||
|
# Scenario 3 from docstring
|
||||||
|
else:
|
||||||
|
for host in resource_hosts:
|
||||||
|
self.assertIn(host, az_hosts)
|
||||||
|
utils.wait_until_true(test_resource_namespace)
|
||||||
|
tmp_zones = []
|
||||||
|
for agent_details in body['agents']:
|
||||||
|
self.assertIn(agent_details['availability_zone'], az)
|
||||||
|
tmp_zones.append(agent_details['availability_zone'])
|
||||||
|
self.assertTrue(set(az).issuperset(set(tmp_zones)))
|
||||||
|
self.assertEqual(len(tmp_zones), len(set(tmp_zones)))
|
||||||
|
|
||||||
|
@decorators.idempotent_id('7e677f27-097e-4331-a26c-47f02546d295')
|
||||||
|
def test_neutron_AZs_router_network_1az(self):
|
||||||
|
"""Check that a router and network configured with only one
|
||||||
|
AZ is deployed over nodes in this AZ
|
||||||
|
"""
|
||||||
|
for az in self.AZs_list:
|
||||||
|
network = self.create_network(availability_zone_hints=[az])
|
||||||
|
subnet = self.create_subnet(network)
|
||||||
|
router = self.create_router_by_client(availability_zone_hints=[az])
|
||||||
|
self.create_router_interface(router['id'], subnet['id'])
|
||||||
|
self._check_az_router([az], router)
|
||||||
|
self._check_az_network([az], network)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('fe77b6df-5de0-4876-9a97-8e6b3e7617e6')
|
||||||
|
def test_network_created_in_single_az_with_not_enough_agents(self):
|
||||||
|
"""Verify that network is deployed over one AZ with NOT enough agents
|
||||||
|
|
||||||
|
Network should be deployed over all the agents in the availability zone
|
||||||
|
"""
|
||||||
|
|
||||||
|
az_host_counter = {}
|
||||||
|
for tmp_az in self.dhcp_agent_list.values():
|
||||||
|
tmp_counter = az_host_counter.get(tmp_az, 0) + 1
|
||||||
|
az_host_counter[tmp_az] = tmp_counter
|
||||||
|
for tmp_az, counter in az_host_counter.items():
|
||||||
|
if counter < self.agent_conf['dhcp_agents_per_network']:
|
||||||
|
az = tmp_az
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise self.skipException('No availability zone with not enough '
|
||||||
|
'resources available')
|
||||||
|
network = self.create_network(availability_zone_hints=[az])
|
||||||
|
self.create_subnet(network)
|
||||||
|
self._check_az_network([az], network)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('51bca87e-fc8d-41b0-9a03-d8f2e16de322')
|
||||||
|
def test_network_created_in_single_az_with_enough_agents(self):
|
||||||
|
"""Verify that network is deployed over one AZ with enough agents
|
||||||
|
|
||||||
|
Network should be deployed over the required amount of the agents
|
||||||
|
in the specified availability zone
|
||||||
|
"""
|
||||||
|
|
||||||
|
az_host_counter = {}
|
||||||
|
for tmp_az in self.dhcp_agent_list.values():
|
||||||
|
tmp_counter = az_host_counter.get(tmp_az, 0) + 1
|
||||||
|
az_host_counter[tmp_az] = tmp_counter
|
||||||
|
for tmp_az, counter in az_host_counter.items():
|
||||||
|
if counter >= self.agent_conf['dhcp_agents_per_network']:
|
||||||
|
az = tmp_az
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise self.skipException('No availability zone with enough '
|
||||||
|
'resources available')
|
||||||
|
network = self.create_network(availability_zone_hints=[az])
|
||||||
|
self.create_subnet(network)
|
||||||
|
self._check_az_network([az], network)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('f9244141-d176-4d54-b21b-305c81f3aca0')
|
||||||
|
def test_network_created_in_all_azs(self):
|
||||||
|
"""Verify that network is created in all available AZs
|
||||||
|
|
||||||
|
If the amount of availability zones is more then the
|
||||||
|
`dhcp_agents_per_network` the test will be scipped as the network
|
||||||
|
will not be created in all the available AZs
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(self.AZs_list) == 1:
|
||||||
|
raise self.skipException("Only one availability zone configured "
|
||||||
|
"but multiple zones required")
|
||||||
|
network = self.create_network(
|
||||||
|
availability_zone_hints=self.AZs_list)
|
||||||
|
self.create_subnet(network)
|
||||||
|
self._check_az_network(self.AZs_list, network)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('95465489-ef2c-4c25-84d1-650b9b1395b6')
|
||||||
|
def test_network_created_in_several_azs(self):
|
||||||
|
"""Verify that network is created in several but not all the AZs
|
||||||
|
|
||||||
|
Test will be executed in the environments with more than 1 DHCP agent
|
||||||
|
per network and with more than 2 availability zones
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(self.AZs_list) <= 2:
|
||||||
|
raise self.skipException("More than 2 AZs required")
|
||||||
|
if self.agent_conf['dhcp_agents_per_network'] <= 1:
|
||||||
|
raise self.skipException("More than 1 DHCP agents required")
|
||||||
|
amount = min(self.agent_conf['dhcp_agents_per_network'],
|
||||||
|
len(self.AZs_list) - 1)
|
||||||
|
network = self.create_network(
|
||||||
|
availability_zone_hints=self.AZs_list[:amount])
|
||||||
|
self.create_subnet(network)
|
||||||
|
self._check_az_network(self.AZs_list[:amount], network)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('c559f79b-3941-4978-9337-ab0e97a7b6f5')
|
||||||
|
def test_network_created_in_default_azs(self):
|
||||||
|
"""Verify that network is correctly spawened in default AZs
|
||||||
|
|
||||||
|
If there is no parameter specified for default AZs, all the AZs
|
||||||
|
are recognized as default.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(self.agent_conf['default_availability_zones']) > \
|
||||||
|
self.agent_conf['dhcp_agents_per_network']:
|
||||||
|
raise self.skipException('something')
|
||||||
|
network = self.create_network()
|
||||||
|
self.create_subnet(network)
|
||||||
|
azs = self.agent_conf['default_availability_zones'] or self.AZs_list
|
||||||
|
self._check_az_network(azs, network)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('2673b58d-6875-4232-bf47-8cc5e9aa4479')
|
||||||
|
def test_network_creation_failed_for_not_existing_az(self):
|
||||||
|
"""Verify that network creation failed if AZ doesn't exist"""
|
||||||
|
|
||||||
|
tmp_az_list = []
|
||||||
|
for az in self.AZs_list:
|
||||||
|
tmp_az_list.append(az)
|
||||||
|
tmp_az_list.append('not_existing_az')
|
||||||
|
self.assertRaises(exceptions.NotFound,
|
||||||
|
self.create_network,
|
||||||
|
availability_zone_hints=tmp_az_list)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('22be1c6d-9bf9-4c79-a9ce-1225fef885ad')
|
||||||
|
def test_router_created_in_single_az_with_not_enough_agents(self):
|
||||||
|
"""Verify that router is deployed over one AZ with NOT enough agents
|
||||||
|
|
||||||
|
Router should be deployed over all the agents in the availability zone
|
||||||
|
"""
|
||||||
|
|
||||||
|
az_host_counter = {}
|
||||||
|
for tmp_az in self.l3_agent_list.values():
|
||||||
|
tmp_counter = az_host_counter.get(tmp_az, 0) + 1
|
||||||
|
az_host_counter[tmp_az] = tmp_counter
|
||||||
|
for tmp_az, counter in az_host_counter.items():
|
||||||
|
if counter < self.agent_conf['max_l3_agents_per_router']:
|
||||||
|
az = tmp_az
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise self.skipException('No availability zone with not enough '
|
||||||
|
'resources available')
|
||||||
|
router = self.create_router_by_client(availability_zone_hints=[az])
|
||||||
|
self._check_az_router([az], router)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('a679b0fb-9f25-4faa-8132-405a5afb722a')
|
||||||
|
def test_router_created_in_single_az_with_enough_agents(self):
|
||||||
|
"""Verify that router is deployed over one AZ with enough agents
|
||||||
|
|
||||||
|
Router should be deployed over the required amount of the agents
|
||||||
|
in the specified availability zone
|
||||||
|
"""
|
||||||
|
|
||||||
|
az_host_counter = {}
|
||||||
|
for tmp_az in self.l3_agent_list.values():
|
||||||
|
tmp_counter = az_host_counter.get(tmp_az, 0) + 1
|
||||||
|
az_host_counter[tmp_az] = tmp_counter
|
||||||
|
for tmp_az, counter in az_host_counter.items():
|
||||||
|
if counter >= self.agent_conf['max_l3_agents_per_router']:
|
||||||
|
az = tmp_az
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise self.skipException('No availability zone with enough '
|
||||||
|
'resources available')
|
||||||
|
router = self.create_router_by_client(availability_zone_hints=[az])
|
||||||
|
self._check_az_router([az], router)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('d22c3ee0-0241-4a86-8ded-0a0b9d0fefb9')
|
||||||
|
def test_router_created_in_all_azs(self):
|
||||||
|
"""Verify that router is created in all available AZs
|
||||||
|
|
||||||
|
If the amount of availability zones is more then the
|
||||||
|
`max_l3_agents_per_router` the test will be scipped as the router
|
||||||
|
will not be created in all the available AZs
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(self.AZs_list) == 1:
|
||||||
|
raise self.skipException("Only one availability zone configured "
|
||||||
|
"but multiple zones required")
|
||||||
|
router = self.create_router_by_client(
|
||||||
|
availability_zone_hints=self.AZs_list)
|
||||||
|
self._check_az_router(self.AZs_list, router)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('eeb04734-0c78-4fa5-81cb-569bfdc4daeb')
|
||||||
|
def test_router_created_in_several_azs(self):
|
||||||
|
"""Verify that router is created in several but not all the AZs
|
||||||
|
|
||||||
|
Test will be executed in the environments with more than 1 L3 agent
|
||||||
|
per router and with more than 2 availability zones
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(self.AZs_list) <= 2:
|
||||||
|
raise self.skipException("More than 2 AZs required")
|
||||||
|
if self.agent_conf['max_l3_agents_per_router'] <= 1:
|
||||||
|
raise self.skipException("More than 1 L3 agents required")
|
||||||
|
amount = min(self.agent_conf['max_l3_agents_per_router'],
|
||||||
|
len(self.AZs_list) - 1)
|
||||||
|
router = self.create_router_by_client(
|
||||||
|
availability_zone_hints=self.AZs_list[:amount])
|
||||||
|
self._check_az_router(self.AZs_list[:amount], router)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('26d05e2e-c491-43f2-8368-f396e994b14f')
|
||||||
|
def test_router_created_in_default_azs(self):
|
||||||
|
"""Verify that router is correctly spawned in default AZs
|
||||||
|
|
||||||
|
If there is no parameter specified for default AZs, all the AZs
|
||||||
|
are recognized as default.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(self.agent_conf['default_availability_zones']) > \
|
||||||
|
self.agent_conf['max_l3_agents_per_router']:
|
||||||
|
raise self.skipException('something')
|
||||||
|
router = self.create_router_by_client()
|
||||||
|
azs = self.agent_conf['default_availability_zones'] or self.AZs_list
|
||||||
|
self._check_az_router(azs, router)
|
||||||
|
|
||||||
|
@decorators.idempotent_id('8ab54f63-2b3c-48fb-87b4-e4785b72c543')
|
||||||
|
def test_router_creation_failed_for_not_existing_az(self):
|
||||||
|
"""Verify that router creation failed if AZ doesn't exist"""
|
||||||
|
|
||||||
|
tmp_az_list = []
|
||||||
|
for az in self.AZs_list:
|
||||||
|
tmp_az_list.append(az)
|
||||||
|
tmp_az_list.append('not_existing_az')
|
||||||
|
self.assertRaises(exceptions.NotFound,
|
||||||
|
self.create_router_by_client,
|
||||||
|
availability_zone_hints=tmp_az_list)
|
Loading…
Reference in New Issue
Block a user