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.'),
|
||||
cfg.StrOpt('overcloud_key_file',
|
||||
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.')
|
||||
]
|
||||
|
@ -66,7 +66,7 @@ class BaseTempestWhiteboxTestCase(base.BaseTempestTestCase):
|
||||
output, errors = local_utils.run_local_cmd(cmd)
|
||||
LOG.debug("Stderr: {}".format(errors.decode()))
|
||||
output = output.decode()
|
||||
LOG.debug("Output: {}".format(output))
|
||||
LOG.debug("Output: {}".format(output))
|
||||
return output.strip()
|
||||
|
||||
def get_host_for_server(self, server_id):
|
||||
|
@ -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…
x
Reference in New Issue
Block a user