1
0
mirror of https://github.com/gryf/openstack.git synced 2025-12-17 11:30:24 +01:00

Adds support for two new server group policies

This is an Ocata backport of Jay patch for adding support for two new
policies aggregate-affinity and aggregate-anti-affinity.

https://review.openstack.org/#/c/529201
This commit is contained in:
Roman Dobosz
2018-01-22 13:13:25 +01:00
parent 0d155c4c18
commit b0cd31de21
4 changed files with 483 additions and 10 deletions

View File

@@ -1,7 +1,7 @@
From 0f820a60994586debef47a59ebf8d9eef225b69c Mon Sep 17 00:00:00 2001 From 0f820a60994586debef47a59ebf8d9eef225b69c Mon Sep 17 00:00:00 2001
From: Roman Dobosz <roman.dobosz@intel.com> From: Roman Dobosz <roman.dobosz@intel.com>
Date: Wed, 27 Dec 2017 13:51:25 +0100 Date: Wed, 27 Dec 2017 13:51:25 +0100
Subject: [PATCH 1/3] allow compute nodes to be associated with host agg Subject: [PATCH 1/4] allow compute nodes to be associated with host agg
This is basically an Ocata backport patch from Jay Pipes: This is basically an Ocata backport patch from Jay Pipes:
https://review.openstack.org/#/c/526753 https://review.openstack.org/#/c/526753

View File

@@ -1,7 +1,7 @@
From f5e23e436d341a44dafe5a18876cfcadc809b46b Mon Sep 17 00:00:00 2001 From f5e23e436d341a44dafe5a18876cfcadc809b46b Mon Sep 17 00:00:00 2001
From: Roman Dobosz <roman.dobosz@intel.com> From: Roman Dobosz <roman.dobosz@intel.com>
Date: Mon, 8 Jan 2018 14:33:45 +0100 Date: Mon, 8 Jan 2018 14:33:45 +0100
Subject: [PATCH 2/3] Remove server group sched filter support caching Subject: [PATCH 2/4] Remove server group sched filter support caching
Backport of https://review.openstack.org/#/c/529200 by Jay Pipes to Backport of https://review.openstack.org/#/c/529200 by Jay Pipes to
Ocata. Ocata.

View File

@@ -1,16 +1,16 @@
From 1374f28766d56361b9342d77a32c0e52034060f1 Mon Sep 17 00:00:00 2001 From 69d0e023edfc2edc123fd5ed29b79ebbd3abe97f Mon Sep 17 00:00:00 2001
From: Roman Dobosz <roman.dobosz@intel.com> From: Roman Dobosz <roman.dobosz@intel.com>
Date: Wed, 10 Jan 2018 10:37:54 +0100 Date: Wed, 10 Jan 2018 10:37:54 +0100
Subject: [PATCH 3/3] get instance group's aggregate associations Subject: [PATCH 3/4] get instance group's aggregate associations
Ocata backport for patch from Jay Pipes: Ocata backport for patch from Jay Pipes:
https://review.openstack.org/#/c/531243/ https://review.openstack.org/#/c/531243/
--- ---
nova/objects/instance_group.py | 36 +++++++- nova/objects/instance_group.py | 36 +++++++-
nova/tests/functional/db/test_instance_group.py | 112 ++++++++++++++++++++++++ nova/tests/functional/db/test_instance_group.py | 116 ++++++++++++++++++++++++
nova/tests/unit/objects/test_instance_group.py | 21 +++++ nova/tests/unit/objects/test_instance_group.py | 21 +++++
nova/tests/unit/objects/test_objects.py | 2 +- nova/tests/unit/objects/test_objects.py | 2 +-
4 files changed, 169 insertions(+), 2 deletions(-) 4 files changed, 173 insertions(+), 2 deletions(-)
diff --git a/nova/objects/instance_group.py b/nova/objects/instance_group.py diff --git a/nova/objects/instance_group.py b/nova/objects/instance_group.py
index 670813b77e..2be47278b2 100644 index 670813b77e..2be47278b2 100644
@@ -74,7 +74,7 @@ index 670813b77e..2be47278b2 100644
"""Count the number of instances in a group belonging to a user.""" """Count the number of instances in a group belonging to a user."""
filter_uuids = self.members filter_uuids = self.members
diff --git a/nova/tests/functional/db/test_instance_group.py b/nova/tests/functional/db/test_instance_group.py diff --git a/nova/tests/functional/db/test_instance_group.py b/nova/tests/functional/db/test_instance_group.py
index 4c4f627fe2..445ae655cc 100644 index 4c4f627fe2..b4c7ef3fd8 100644
--- a/nova/tests/functional/db/test_instance_group.py --- a/nova/tests/functional/db/test_instance_group.py
+++ b/nova/tests/functional/db/test_instance_group.py +++ b/nova/tests/functional/db/test_instance_group.py
@@ -18,6 +18,7 @@ from nova.db.sqlalchemy import api as db_api @@ -18,6 +18,7 @@ from nova.db.sqlalchemy import api as db_api
@@ -85,7 +85,7 @@ index 4c4f627fe2..445ae655cc 100644
from nova.objects import instance_group from nova.objects import instance_group
from nova import test from nova import test
from nova.tests import uuidsentinel as uuids from nova.tests import uuidsentinel as uuids
@@ -238,3 +239,114 @@ class InstanceGroupObjectTestCase(test.TestCase): @@ -238,3 +239,118 @@ class InstanceGroupObjectTestCase(test.TestCase):
self.context, 100) self.context, 100)
self.assertEqual(0, total) self.assertEqual(0, total)
self.assertEqual(0, done) self.assertEqual(0, done)
@@ -144,8 +144,12 @@ index 4c4f627fe2..445ae655cc 100644
+ agg2 = objects.Aggregate(self.ctx, name='agg2') + agg2 = objects.Aggregate(self.ctx, name='agg2')
+ agg2.create() + agg2.create()
+ +
+ # NOTE(gryf): creating bare instances doesn't propagate project_id + # NOTE(gryf): We are passing project_id explicitly, due to not going
+ # for some reason, so that test would fail. + # through all the process, like calling
+ # nova.compute.api.API._validate_and_build_base_options(), which
+ # return among other things base_options which contain project_id.
+ # We could do the i1.update({'project_id': ctx.project_id}) instead,
+ # but passing project_id as a parameter during object init is cleaner.
+ i1 = objects.Instance(self.ctx, host='host1', node='node1', + i1 = objects.Instance(self.ctx, host='host1', node='node1',
+ project_id=self.ctx.project_id) + project_id=self.ctx.project_id)
+ i1.create() + i1.create()

View File

@@ -0,0 +1,469 @@
From f69827ff3502552a45a19a50ef2cfad30c41af2d Mon Sep 17 00:00:00 2001
From: Roman Dobosz <roman.dobosz@intel.com>
Date: Thu, 18 Jan 2018 09:17:04 +0100
Subject: [PATCH 4/4] Support aggregate affinity filters
Jay patch for two new policies: aggregate-affinity and
aggregate-antiaffinity backported to Ocata.
https://review.openstack.org/#/c/529201/
---
doc/api_samples/versions/v21-version-get-resp.json | 2 +-
doc/api_samples/versions/versions-get-resp.json | 2 +-
nova/api/openstack/api_version_request.py | 4 +-
.../openstack/compute/rest_api_version_history.rst | 12 ++
.../api/openstack/compute/schemas/server_groups.py | 5 +
nova/api/openstack/compute/server_groups.py | 3 +-
nova/compute/manager.py | 33 ++++-
nova/scheduler/filters/affinity_filter.py | 71 +++++++++++
nova/scheduler/host_manager.py | 7 ++
nova/scheduler/utils.py | 21 ++--
nova/tests/functional/test_server_group.py | 140 ++++++++++++++++++++-
11 files changed, 285 insertions(+), 15 deletions(-)
diff --git a/doc/api_samples/versions/v21-version-get-resp.json b/doc/api_samples/versions/v21-version-get-resp.json
index 64120de26c..241212017e 100644
--- a/doc/api_samples/versions/v21-version-get-resp.json
+++ b/doc/api_samples/versions/v21-version-get-resp.json
@@ -19,7 +19,7 @@
}
],
"status": "CURRENT",
- "version": "2.42",
+ "version": "2.43",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}
diff --git a/doc/api_samples/versions/versions-get-resp.json b/doc/api_samples/versions/versions-get-resp.json
index 27ad9b3703..924b060df1 100644
--- a/doc/api_samples/versions/versions-get-resp.json
+++ b/doc/api_samples/versions/versions-get-resp.json
@@ -22,7 +22,7 @@
}
],
"status": "CURRENT",
- "version": "2.42",
+ "version": "2.43",
"min_version": "2.1",
"updated": "2013-07-23T11:33:21Z"
}
diff --git a/nova/api/openstack/api_version_request.py b/nova/api/openstack/api_version_request.py
index dde18537e4..9957afd0af 100644
--- a/nova/api/openstack/api_version_request.py
+++ b/nova/api/openstack/api_version_request.py
@@ -102,6 +102,8 @@ REST_API_VERSION_HISTORY = """REST API Version History:
re-introduce the tag attribute that, due to bugs, was lost
starting with version 2.33 for block devices and starting with
version 2.37 for network interfaces.
+ * 2.43 - Add 'aggregate-affinity' and 'aggregate-anti-affinity' server
+ group policies
"""
# The minimum and maximum versions of the API supported
@@ -110,7 +112,7 @@ REST_API_VERSION_HISTORY = """REST API Version History:
# Note(cyeoh): This only applies for the v2.1 API once microversions
# support is fully merged. It does not affect the V2 API.
_MIN_API_VERSION = "2.1"
-_MAX_API_VERSION = "2.42"
+_MAX_API_VERSION = "2.43"
DEFAULT_API_VERSION = _MIN_API_VERSION
# Almost all proxy APIs which related to network, images and baremetal
diff --git a/nova/api/openstack/compute/rest_api_version_history.rst b/nova/api/openstack/compute/rest_api_version_history.rst
index fee83ead14..deccddfebf 100644
--- a/nova/api/openstack/compute/rest_api_version_history.rst
+++ b/nova/api/openstack/compute/rest_api_version_history.rst
@@ -483,3 +483,15 @@ user documentation.
2.37 and for block_device_mapping_v2 starting with version 2.33. Microversion
2.42 restores the tag parameter to both networks and block_device_mapping_v2,
allowing networks and block devices to be tagged again.
+
+2.43
+----
+
+From this version of the API users can choose `aggregate-affinity` and
+`aggregate-anti-affinity` rules for server-groups. The `aggregate-affinity`
+policy for a server group indicates that new members of the server group should
+be placed on hosts that are in the same aggregate(s) associated with hosts
+where other members of the server group are placed. The
+`aggregate-anti-affinity` policy enforces the opposite: ensure that instances
+launched in a server group land on hosts that are *not* associated with
+aggregates that other members of the server group are associated to.
diff --git a/nova/api/openstack/compute/schemas/server_groups.py b/nova/api/openstack/compute/schemas/server_groups.py
index 52a08413aa..4b274e3251 100644
--- a/nova/api/openstack/compute/schemas/server_groups.py
+++ b/nova/api/openstack/compute/schemas/server_groups.py
@@ -43,3 +43,8 @@ create = {
create_v215 = copy.deepcopy(create)
policies = create_v215['properties']['server_group']['properties']['policies']
policies['items'][0]['enum'].extend(['soft-anti-affinity', 'soft-affinity'])
+
+create_v243 = copy.deepcopy(create_v215)
+policies = create_v243['properties']['server_group']['properties']['policies']
+policies['items'][0]['enum'].extend(['aggregate-anti-affinity',
+ 'aggregate-affinity'])
diff --git a/nova/api/openstack/compute/server_groups.py b/nova/api/openstack/compute/server_groups.py
index dfd2380ec2..82429af5a6 100644
--- a/nova/api/openstack/compute/server_groups.py
+++ b/nova/api/openstack/compute/server_groups.py
@@ -132,7 +132,8 @@ class ServerGroupController(wsgi.Controller):
@wsgi.Controller.api_version("2.1")
@extensions.expected_errors((400, 403))
@validation.schema(schema.create, "2.0", "2.14")
- @validation.schema(schema.create_v215, "2.15")
+ @validation.schema(schema.create_v215, "2.15", "2.42")
+ @validation.schema(schema.create_v243, "2.43")
def create(self, req, body):
"""Creates a new server group."""
context = _authorize_context(req, 'create')
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 86c39c190a..10ed9d3df0 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -1294,7 +1294,9 @@ class ComputeManager(manager.Manager):
@utils.synchronized(group_hint)
def _do_validation(context, instance, group_hint):
group = objects.InstanceGroup.get_by_hint(context, group_hint)
- if 'anti-affinity' in group.policies:
+ # NOTE(jaypipes): A server group only has 1 policy...
+ group_policy = group.policies[0]
+ if 'anti-affinity' == group_policy:
group_hosts = group.get_hosts(exclude=[instance.uuid])
if self.host in group_hosts:
msg = _("Anti-affinity instance group policy "
@@ -1302,14 +1304,39 @@ class ComputeManager(manager.Manager):
raise exception.RescheduledException(
instance_uuid=instance.uuid,
reason=msg)
- elif 'affinity' in group.policies:
+ elif 'affinity' == group_policy:
group_hosts = group.get_hosts(exclude=[instance.uuid])
if group_hosts and self.host not in group_hosts:
msg = _("Affinity instance group policy was violated.")
raise exception.RescheduledException(
instance_uuid=instance.uuid,
reason=msg)
-
+ elif 'aggregate' in group_policy:
+ # NOTE(jaypipes): We look up aggregates by the **node** here,
+ # not the instance.host. This is because the compute node (not
+ # the service record) is expected to be associated with
+ # aggregates when the aggregate affinity filters are being
+ # used.
+ node_aggs = objects.AggregateList.get_by_host(
+ context, instance.node)
+ node_aggs = set(agg.uuid for agg in node_aggs)
+ if 'aggregate-affinity' == group_policy:
+ group_aggs = group.get_aggregate_uuids()
+ if not node_aggs.issubset(group_aggs):
+ msg = _("Aggregate affinity server group policy was "
+ "violated.")
+ raise exception.RescheduledException(
+ instance_uuid=instance.uuid,
+ reason=msg)
+ else:
+ group_aggs = group.get_aggregate_uuids(
+ exclude=[instance.uuid])
+ if not node_aggs.isdisjoint(group_aggs):
+ msg = _("Aggregate anti-affinity server group policy "
+ "was violated.")
+ raise exception.RescheduledException(
+ instance_uuid=instance.uuid,
+ reason=msg)
_do_validation(context, instance, group_hint)
def _log_original_error(self, exc_info, instance_uuid):
diff --git a/nova/scheduler/filters/affinity_filter.py b/nova/scheduler/filters/affinity_filter.py
index f8aa47ee03..f025df45df 100644
--- a/nova/scheduler/filters/affinity_filter.py
+++ b/nova/scheduler/filters/affinity_filter.py
@@ -145,3 +145,74 @@ class ServerGroupAffinityFilter(_GroupAffinityFilter):
def __init__(self):
self.policy_name = 'affinity'
super(ServerGroupAffinityFilter, self).__init__()
+
+
+class ServerGroupAggregateAffinityFilter(filters.BaseHostFilter):
+ """Filter out any host that isn't in the same host aggregates as the
+ aggregates associated with the host from another member of the server
+ group.
+ """
+
+ RUN_ON_REBUILD = False
+ POLICY_NAME = 'aggregate-affinity'
+ REVERSE_CHECK = False
+
+ def host_passes(self, host_state, spec_obj):
+ if not spec_obj.instance_group.policies:
+ return True
+ policy = spec_obj.instance_group.policies[0]
+ if self.POLICY_NAME != policy:
+ return True
+
+ host_aggs = set(agg.id for agg in host_state.aggregates)
+
+ if not host_aggs:
+ if not self.REVERSE_CHECK:
+ # Clearly, if the host doesn't belong to any aggregates, then
+ # it cannot satisfy an aggregate affinity constraint
+ return False
+ else:
+ # And clearly, if the host doesn't belong to any aggregates,
+ # then it must always satisfy an aggregate anti-affinity
+ # constraint
+ return True
+
+ group_hosts = (spec_obj.instance_group.hosts
+ if spec_obj.instance_group else [])
+ if not group_hosts:
+ # There are no members of the server group yet, so this host meets
+ # the aggregate affinity (or anti-affinity) constraint
+ return True
+
+ # TODO(jaypipes): The sets used here contain the autoincremented
+ # integer keys for aggregates. :( This means this isn't multi-cell
+ # safe. We would need to modify the host_aggregate_map and associated
+ # HostState.aggregates sets to contain UUIDs instead of IDs to make
+ # this multi-cell safe
+
+ # Grab all aggregates for all hosts in the server group and ensure we
+ # have an intersection with this host's aggregates
+ group_aggs = set()
+ for group_host in group_hosts:
+ group_aggs |= self.host_manager.host_aggregates_map[group_host]
+
+ LOG.debug("%(policy_name)s: check if %(host_aggs)s "
+ "is %(policy_cond)s subset of %(group_aggs)s",
+ {
+ 'policy_name': self.POLICY_NAME,
+ 'policy_cond': 'not a' if self.REVERSE_CHECK else 'a',
+ 'group_aggs': group_aggs,
+ 'host_aggs': host_aggs})
+ if self.REVERSE_CHECK:
+ return host_aggs.isdisjoint(group_aggs)
+ return host_aggs.issubset(group_aggs)
+
+
+class ServerGroupAggregateAntiAffinityFilter(
+ ServerGroupAggregateAffinityFilter):
+ """Filter out any host that is in the same host aggregates as the
+ aggregates associated with any host from another member of the server
+ group.
+ """
+ POLICY_NAME = 'aggregate-anti-affinity'
+ REVERSE_CHECK = True
diff --git a/nova/scheduler/host_manager.py b/nova/scheduler/host_manager.py
index 4f66d40913..7347722a94 100644
--- a/nova/scheduler/host_manager.py
+++ b/nova/scheduler/host_manager.py
@@ -341,6 +341,13 @@ class HostManager(object):
self.filter_cls_map = {cls.__name__: cls for cls in filter_classes}
self.filter_obj_map = {}
self.enabled_filters = self._choose_host_filters(self._load_filters())
+ # NOTE(jaypipes): This is a total hack because the design of the
+ # scheduler and scheduler filters is teh suck. Basically, we are just
+ # jamming a pointer to the host manager into each filter object so that
+ # the filter objects can have access to things like the host manager's
+ # cached aggregate map. Ugly, but it works...
+ for f in self.enabled_filters:
+ f.host_manager = self
self.weight_handler = weights.HostWeightHandler()
weigher_classes = self.weight_handler.get_matching_classes(
CONF.filter_scheduler.weight_classes)
diff --git a/nova/scheduler/utils.py b/nova/scheduler/utils.py
index 682b4cc199..57a306e07a 100644
--- a/nova/scheduler/utils.py
+++ b/nova/scheduler/utils.py
@@ -299,13 +299,20 @@ def _get_group_details(context, instance_uuid, user_group_hosts=None):
# NOTE(jaypipes): There's only a single policy for the server group
group_policy = group.policies[0]
- checks = {'affinity': (_validate_filter, 'ServerGroupAffinityFilter'),
- 'anti-affinity': (_validate_filter,
- 'ServerGroupAntiAffinityFilter'),
- 'soft-affinity': (_validate_weigher,
- 'ServerGroupSoftAffinityWeigher'),
- 'soft-anti-affinity': (_validate_weigher,
- 'ServerGroupSoftAntiAffinityWeigher')}
+ checks = {
+ 'affinity': (
+ _validate_filter, 'ServerGroupAffinityFilter'),
+ 'anti-affinity': (
+ _validate_filter, 'ServerGroupAntiAffinityFilter'),
+ 'soft-affinity': (
+ _validate_weigher, 'ServerGroupSoftAffinityWeigher'),
+ 'soft-anti-affinity': (
+ _validate_weigher, 'ServerGroupSoftAntiAffinityWeigher'),
+ 'aggregate-affinity': (
+ _validate_filter, 'ServerGroupAggregateAffinityFilter'),
+ 'aggregate-anti-affinity': (
+ _validate_filter, 'ServerGroupAggregateAntiAffinityFilter')
+ }
check_fn, class_name = checks[group_policy]
if not check_fn(class_name):
diff --git a/nova/tests/functional/test_server_group.py b/nova/tests/functional/test_server_group.py
index 5e52088c14..52af7688bb 100644
--- a/nova/tests/functional/test_server_group.py
+++ b/nova/tests/functional/test_server_group.py
@@ -17,6 +17,7 @@ import time
from oslo_config import cfg
+from nova.compute import api as compute_api
from nova import context
from nova import db
from nova.db.sqlalchemy import api as db_api
@@ -46,7 +47,9 @@ class ServerGroupTestBase(test.TestCase,
_enabled_filters = (CONF.filter_scheduler.enabled_filters
+ ['ServerGroupAntiAffinityFilter',
- 'ServerGroupAffinityFilter'])
+ 'ServerGroupAffinityFilter',
+ 'ServerGroupAggregateAntiAffinityFilter',
+ 'ServerGroupAggregateAffinityFilter'])
# Override servicegroup parameters to make the tests run faster
_service_down_time = 10
@@ -812,3 +815,138 @@ class ServerGroupTestV215(ServerGroupTestV21):
def test_soft_affinity_not_supported(self):
pass
+
+
+class ServerGroupAggregateAffinityConfTest(ServerGroupTestBase):
+ api_major_version = 'v2.1'
+ group = {
+ 'name': 'fake-rack-affinity',
+ 'policies': ['aggregate-affinity'],
+ }
+
+ # Load only anti-affinity filter so affinity will be missing
+ _enabled_filters = ['ServerGroupAggregateAntiAffinityFilter']
+
+ def test_aggregate_affinity_no_filter(self):
+ # We need to do this because _boot_a_server_to_group() ends up calling
+ # the images API, and if we set the test case class's microversion
+ # attribute to 2.43, then we will blow up the call to images API (which
+ # was removed in 2.35). yay.
+ self.api.microversion = '2.43'
+ created_group = self.api.post_server_groups(self.group)
+ self.api.microversion = None
+
+ failed_server = self._boot_a_server_to_group(created_group,
+ expected_status='ERROR')
+ self.assertEqual(
+ 'ServerGroup policy is not supported: '
+ 'ServerGroupAggregateAffinityFilter not configured',
+ failed_server['fault']['message'])
+ self.assertEqual(400, failed_server['fault']['code'])
+
+
+class ServerGroupAggregateAntiAffinityConfTest(ServerGroupTestBase):
+ api_major_version = 'v2.1'
+ group = {
+ 'name': 'fake-rack-anti-affinity',
+ 'policies': ['aggregate-anti-affinity'],
+ }
+
+ # Load only affinity filter so anti-affinity will be missing
+ _enabled_filters = ['ServerGroupAggregateAffinityFilter']
+
+ def test_aggregate_anti_affinity_no_filter(self):
+ # We need to do this because _boot_a_server_to_group() ends up calling
+ # the images API, and if we set the test case class's microversion
+ # attribute to 2.43, then we will blow up the call to images API (which
+ # was removed in 2.35). yay.
+ self.api.microversion = '2.43'
+ created_group = self.api.post_server_groups(self.group)
+ self.api.microversion = None
+
+ failed_server = self._boot_a_server_to_group(created_group,
+ expected_status='ERROR')
+ self.assertEqual(
+ 'ServerGroup policy is not supported: '
+ 'ServerGroupAggregateAntiAffinityFilter not configured',
+ failed_server['fault']['message'])
+ self.assertEqual(400, failed_server['fault']['code'])
+
+
+def _host_from_instance(instance):
+ return instance['OS-EXT-SRV-ATTR:host']
+
+
+class AggregateAffinityTest(ServerGroupTestBase):
+
+ def setUp(self):
+ super(AggregateAffinityTest, self).setUp()
+
+ self.stub_out('nova.virt.driver.load_compute_driver',
+ _fake_load_compute_driver)
+
+ # Start up two compute nodes, associating each with a different host
+ # aggregate
+ self.agg_api = compute_api.AggregateAPI()
+ fake.set_nodes(['node1'])
+ self.compute1 = self.start_service('compute', host='node1')
+ fake.set_nodes(['node2'])
+ self.compute2 = self.start_service('compute', host='node2')
+
+ self.addCleanup(fake.restore_nodes)
+ fake_network.set_stub_network_methods(self)
+
+ payload = {'aggregate': {'name': 'rack1'}}
+ self.agg1 = self.admin_api.post_aggregate(payload)
+ payload = {'aggregate': {'name': 'rack2'}}
+ self.agg2 = self.admin_api.post_aggregate(payload)
+
+ ctxt = context.get_admin_context()
+ self.agg_api.add_host_to_aggregate(ctxt, self.agg1['id'], 'node1')
+ self.agg_api.add_host_to_aggregate(ctxt, self.agg2['id'], 'node2')
+
+ def _create_server_group(self, payload):
+ # We need to do this because _boot_a_server_to_group() ends up calling
+ # the images API, and if we set the test case class's microversion
+ # attribute to 2.43, then we will blow up the call to images API (which
+ # was removed in 2.35). yay.
+ self.api.microversion = '2.43'
+ group = self.api.post_server_groups(payload)
+ self.api.microversion = None
+ return group
+
+ def test_aggregate_affinity(self):
+ """Create a server group with a policy of aggregate-affinity, launch
+ one instance into this group. Launch another instance into the same
+ group and ensure that the instance ends up on a host with the same
+ aggregate as the first instance's destination compute node.
+ """
+ group_name = 'keep-in-rack'
+ group_payload = {
+ 'name': group_name,
+ 'policies': ['aggregate-affinity'],
+ }
+ group = self._create_server_group(group_payload)
+ inst1 = self._boot_a_server_to_group(group)
+ inst2 = self._boot_a_server_to_group(group)
+
+ self.assertEqual(_host_from_instance(inst1),
+ _host_from_instance(inst2))
+
+ def test_aggregate_anti_affinity(self):
+ """Create a server group with a policy of aggregate-anti-affinity,
+ launch one instance into this group. Launch another instance into the
+ same group and ensure that the instance ends up on a host in a
+ different aggregate as the first instance's destination compute node.
+ """
+ group_name = 'not-in-rack'
+ group_payload = {
+ 'name': group_name,
+ 'policies': ['aggregate-anti-affinity'],
+ }
+ group = self._create_server_group(group_payload)
+ inst1 = self._boot_a_server_to_group(group)
+ inst2 = self._boot_a_server_to_group(group)
+
+ self.assertNotEqual(_host_from_instance(inst1),
+ _host_from_instance(inst2))
--
2.13.6