Merge "Add tests for creating servers with (anti-)affinity"
This commit is contained in:
@@ -110,6 +110,8 @@ class ServersOnMultiNodesTest(base.BaseV2ComputeAdminTest):
|
|||||||
|
|
||||||
Creates two servers in an anti-affinity server group and
|
Creates two servers in an anti-affinity server group and
|
||||||
asserts the servers are in the group and on different hosts.
|
asserts the servers are in the group and on different hosts.
|
||||||
|
|
||||||
|
Uses the /servers multi-create API.
|
||||||
"""
|
"""
|
||||||
hosts = self._create_servers_with_group('anti-affinity')
|
hosts = self._create_servers_with_group('anti-affinity')
|
||||||
hostnames = list(hosts.values())
|
hostnames = list(hosts.values())
|
||||||
@@ -126,6 +128,8 @@ class ServersOnMultiNodesTest(base.BaseV2ComputeAdminTest):
|
|||||||
|
|
||||||
Creates two servers in an affinity server group and
|
Creates two servers in an affinity server group and
|
||||||
asserts the servers are in the group and on same host.
|
asserts the servers are in the group and on same host.
|
||||||
|
|
||||||
|
Uses the /servers multi-create API.
|
||||||
"""
|
"""
|
||||||
hosts = self._create_servers_with_group('affinity')
|
hosts = self._create_servers_with_group('affinity')
|
||||||
hostnames = list(hosts.values())
|
hostnames = list(hosts.values())
|
||||||
|
0
tempest/serial_tests/api/compute/admin/__init__.py
Normal file
0
tempest/serial_tests/api/compute/admin/__init__.py
Normal file
252
tempest/serial_tests/api/compute/admin/test_server_affinity.py
Normal file
252
tempest/serial_tests/api/compute/admin/test_server_affinity.py
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
import testtools
|
||||||
|
|
||||||
|
from tempest.api.compute import base
|
||||||
|
from tempest.common import compute
|
||||||
|
from tempest import config
|
||||||
|
from tempest import exceptions
|
||||||
|
from tempest.lib.common.utils import data_utils
|
||||||
|
from tempest.lib import decorators
|
||||||
|
|
||||||
|
CONF = config.CONF
|
||||||
|
|
||||||
|
|
||||||
|
@decorators.serial
|
||||||
|
class ServersAffinityTest(base.BaseV2ComputeAdminTest):
|
||||||
|
"""Test creating servers without multi-create with scheduler_hints.
|
||||||
|
|
||||||
|
The server affinity tests in ServersOnMultiNodesTest use the /servers
|
||||||
|
multi-create API and therefore do not test affinity behavior when server
|
||||||
|
group members already exist on hosts.
|
||||||
|
|
||||||
|
These tests must be run in serial because they will be disabling compute
|
||||||
|
hosts in order to verify affinity behavior.
|
||||||
|
"""
|
||||||
|
# 2.64 added 'policy' and 'rules' fields to POST /os-server-groups
|
||||||
|
min_microversion = '2.64'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def resource_setup(cls):
|
||||||
|
super().resource_setup()
|
||||||
|
|
||||||
|
# Disable all compute hosts except two.
|
||||||
|
services = cls.os_admin.services_client.list_services(
|
||||||
|
binary='nova-compute')['services']
|
||||||
|
num_extra = len(services) - 2
|
||||||
|
for i in range(num_extra):
|
||||||
|
service = services.pop()
|
||||||
|
cls.os_admin.services_client.update_service(
|
||||||
|
service['id'], status='disabled')
|
||||||
|
cls.services = {
|
||||||
|
service['host']: service['id'] for service in services}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def skip_checks(cls):
|
||||||
|
super().skip_checks()
|
||||||
|
|
||||||
|
if CONF.compute.min_compute_nodes < 2:
|
||||||
|
raise cls.skipException(
|
||||||
|
"Less than 2 compute nodes, skipping affinity tests.")
|
||||||
|
|
||||||
|
def _disable_compute_host(self, hostname):
|
||||||
|
service_id = self.services[hostname]
|
||||||
|
self.os_admin.services_client.update_service(
|
||||||
|
service_id, status='disabled')
|
||||||
|
self.addCleanup(
|
||||||
|
self.os_admin.services_client.update_service, service_id,
|
||||||
|
status='enabled')
|
||||||
|
|
||||||
|
def _create_server(self, **kwargs):
|
||||||
|
body, servers = compute.create_test_server(
|
||||||
|
self.os_primary, networks='none', **kwargs)
|
||||||
|
for server in servers:
|
||||||
|
self.addCleanup(self.servers_client.delete_server, server['id'])
|
||||||
|
return body
|
||||||
|
|
||||||
|
def _create_server_group(self, **kwargs):
|
||||||
|
name = data_utils.rand_name(
|
||||||
|
prefix=CONF.resource_name_prefix,
|
||||||
|
name=self.__class__.__name__ + "-Server-Group")
|
||||||
|
group_id = self.server_groups_client.create_server_group(
|
||||||
|
name=name, **kwargs)['server_group']['id']
|
||||||
|
self.addCleanup(
|
||||||
|
self.server_groups_client.delete_server_group, group_id)
|
||||||
|
return group_id
|
||||||
|
|
||||||
|
def _create_server_in_group(self, group_id):
|
||||||
|
hints = {'group': group_id}
|
||||||
|
server = self._create_server(
|
||||||
|
scheduler_hints=hints, wait_until='ACTIVE')
|
||||||
|
# Assert the server is in the group.
|
||||||
|
server_group = self.server_groups_client.show_server_group(
|
||||||
|
group_id)['server_group']
|
||||||
|
self.assertIn(server['id'], server_group['members'])
|
||||||
|
return server
|
||||||
|
|
||||||
|
@decorators.attr(type='multinode')
|
||||||
|
@decorators.idempotent_id('28ef4c29-09db-40a8-aacd-dc5fa321f35e')
|
||||||
|
@testtools.skipUnless(
|
||||||
|
compute.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
|
||||||
|
'ServerGroupAffinityFilter is not available.')
|
||||||
|
def test_create_server_with_affinity(self):
|
||||||
|
group_id = self._create_server_group(policy='affinity')
|
||||||
|
|
||||||
|
# Create server1 in the group.
|
||||||
|
server1 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Create server2 in the group.
|
||||||
|
server2 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Servers should be on the same host.
|
||||||
|
self.assertEqual(
|
||||||
|
self.get_host_for_server(server1['id']),
|
||||||
|
self.get_host_for_server(server2['id']))
|
||||||
|
|
||||||
|
@decorators.attr(type='multinode')
|
||||||
|
@decorators.idempotent_id('3ac1ff4e-0fa2-4069-ae59-695cf829275b')
|
||||||
|
@testtools.skipUnless(
|
||||||
|
compute.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
|
||||||
|
'ServerGroupAffinityFilter is not available.')
|
||||||
|
def test_create_server_with_affinity_negative(self):
|
||||||
|
group_id = self._create_server_group(policy='affinity')
|
||||||
|
|
||||||
|
# Create server1 in the group.
|
||||||
|
server1 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Disable the compute host server1 is on.
|
||||||
|
self._disable_compute_host(self.get_host_for_server(server1['id']))
|
||||||
|
|
||||||
|
# Create server2 in the group. This should fail because affinity policy
|
||||||
|
# cannot be honored.
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.BuildErrorException,
|
||||||
|
self._create_server_in_group, group_id)
|
||||||
|
|
||||||
|
@decorators.attr(type='multinode')
|
||||||
|
@decorators.idempotent_id('99cf4819-479c-4176-a9a6-ad501f6fc4b7')
|
||||||
|
@testtools.skipUnless(
|
||||||
|
compute.is_scheduler_filter_enabled("ServerGroupAffinityFilter"),
|
||||||
|
'ServerGroupAffinityFilter is not available.')
|
||||||
|
def test_create_server_with_soft_affinity(self):
|
||||||
|
group_id = self._create_server_group(policy='soft-affinity')
|
||||||
|
|
||||||
|
# Create server1 in the group.
|
||||||
|
server1 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Disable the compute host server1 is on.
|
||||||
|
self._disable_compute_host(self.get_host_for_server(server1['id']))
|
||||||
|
|
||||||
|
# Create server2 in the group. This should succeed because soft
|
||||||
|
# affinity is best effort and scheduling should go ahead even if the
|
||||||
|
# policy cannot be honored.
|
||||||
|
server2 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Servers should be on different hosts.
|
||||||
|
self.assertNotEqual(
|
||||||
|
self.get_host_for_server(server1['id']),
|
||||||
|
self.get_host_for_server(server2['id']))
|
||||||
|
|
||||||
|
@decorators.attr(type='multinode')
|
||||||
|
@decorators.idempotent_id('475e9db0-5512-41cb-a6b2-4bd6fb3c7603')
|
||||||
|
@testtools.skipUnless(
|
||||||
|
compute.is_scheduler_filter_enabled("ServerGroupAntiAffinityFilter"),
|
||||||
|
'ServerGroupAntiAffinityFilter is not available.')
|
||||||
|
def test_create_server_with_anti_affinity(self):
|
||||||
|
group_id = self._create_server_group(policy='anti-affinity')
|
||||||
|
|
||||||
|
# Create server1 in the group.
|
||||||
|
server1 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Create server2 in the group.
|
||||||
|
server2 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Servers should be on different hosts.
|
||||||
|
self.assertNotEqual(
|
||||||
|
self.get_host_for_server(server1['id']),
|
||||||
|
self.get_host_for_server(server2['id']))
|
||||||
|
|
||||||
|
@decorators.attr(type='multinode')
|
||||||
|
@decorators.idempotent_id('c5e43585-0fdd-42a9-a525-2b99465c28df')
|
||||||
|
@testtools.skipUnless(
|
||||||
|
compute.is_scheduler_filter_enabled("ServerGroupAntiAffinityFilter"),
|
||||||
|
'ServerGroupAntiAffinityFilter is not available.')
|
||||||
|
def test_create_server_with_anti_affinity_negative(self):
|
||||||
|
group_id = self._create_server_group(policy='anti-affinity')
|
||||||
|
|
||||||
|
# Create server1 in the group.
|
||||||
|
server1 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Disable the compute host server1 is not on.
|
||||||
|
self._disable_compute_host(self.get_host_other_than(server1['id']))
|
||||||
|
|
||||||
|
# Create server2 in the group. This should fail because anti-affinity
|
||||||
|
# policy cannot be honored.
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.BuildErrorException,
|
||||||
|
self._create_server_in_group, group_id)
|
||||||
|
|
||||||
|
@decorators.attr(type='multinode')
|
||||||
|
@decorators.idempotent_id('88a8c3d4-c0e8-4873-ba6f-006004779f29')
|
||||||
|
@testtools.skipUnless(
|
||||||
|
compute.is_scheduler_filter_enabled("ServerGroupAntiAffinityFilter"),
|
||||||
|
'ServerGroupAntiAffinityFilter is not available.')
|
||||||
|
def test_create_server_with_anti_affinity_max_server_per_host(self):
|
||||||
|
group_id = self._create_server_group(
|
||||||
|
policy='anti-affinity', rules={'max_server_per_host': 2})
|
||||||
|
|
||||||
|
# Create server1 in the group.
|
||||||
|
server1 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Disable the compute host server1 is not on.
|
||||||
|
self._disable_compute_host(self.get_host_other_than(server1['id']))
|
||||||
|
|
||||||
|
# Create server2 in the group. This should succeed because we are
|
||||||
|
# allowing a maximum of two servers per compute host for anti-affinity.
|
||||||
|
server2 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Servers should be on the same host.
|
||||||
|
self.assertEqual(
|
||||||
|
self.get_host_for_server(server1['id']),
|
||||||
|
self.get_host_for_server(server2['id']))
|
||||||
|
|
||||||
|
# A attempt to create a third server in the group should fail because
|
||||||
|
# we have already reached our maximum allowed servers per compute host
|
||||||
|
# for anti-affinity.
|
||||||
|
self.assertRaises(
|
||||||
|
exceptions.BuildErrorException,
|
||||||
|
self._create_server_in_group, group_id)
|
||||||
|
|
||||||
|
@decorators.attr(type='multinode')
|
||||||
|
@decorators.idempotent_id('ef2bc189-5ecc-4a23-8c1b-0d70a9138a77')
|
||||||
|
@testtools.skipUnless(
|
||||||
|
compute.is_scheduler_filter_enabled("ServerGroupAntiAffinityFilter"),
|
||||||
|
'ServerGroupAntiAffinityFilter is not available.')
|
||||||
|
def test_create_server_with_soft_anti_affinity(self):
|
||||||
|
group_id = self._create_server_group(policy='soft-anti-affinity')
|
||||||
|
|
||||||
|
# Create server1 in the group.
|
||||||
|
server1 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Disable the compute host server1 is not on.
|
||||||
|
self._disable_compute_host(self.get_host_other_than(server1['id']))
|
||||||
|
|
||||||
|
# Create server2 in the group. This should succeed because soft
|
||||||
|
# anti-affinity is best effort and scheduling should go ahead even if
|
||||||
|
# the policy cannot be honored.
|
||||||
|
server2 = self._create_server_in_group(group_id)
|
||||||
|
|
||||||
|
# Servers should be on the same host.
|
||||||
|
self.assertEqual(
|
||||||
|
self.get_host_for_server(server1['id']),
|
||||||
|
self.get_host_for_server(server2['id']))
|
Reference in New Issue
Block a user