1
0
mirror of https://github.com/gryf/openstack.git synced 2025-12-17 11:30:24 +01:00
Files
openstack/affinity_ocata/0003-get-instance-group-s-aggregate-associations.patch

251 lines
11 KiB
Diff

From 1374f28766d56361b9342d77a32c0e52034060f1 Mon Sep 17 00:00:00 2001
From: Roman Dobosz <roman.dobosz@intel.com>
Date: Wed, 10 Jan 2018 10:37:54 +0100
Subject: [PATCH 3/3] get instance group's aggregate associations
Ocata backport for patch from Jay Pipes:
https://review.openstack.org/#/c/531243/
---
nova/objects/instance_group.py | 36 +++++++-
nova/tests/functional/db/test_instance_group.py | 112 ++++++++++++++++++++++++
nova/tests/unit/objects/test_instance_group.py | 21 +++++
nova/tests/unit/objects/test_objects.py | 2 +-
4 files changed, 169 insertions(+), 2 deletions(-)
diff --git a/nova/objects/instance_group.py b/nova/objects/instance_group.py
index 670813b77e..2be47278b2 100644
--- a/nova/objects/instance_group.py
+++ b/nova/objects/instance_group.py
@@ -17,6 +17,7 @@ import copy
from oslo_db import exception as db_exc
from oslo_utils import uuidutils
from oslo_utils import versionutils
+import sqlalchemy as sa
from sqlalchemy.orm import contains_eager
from sqlalchemy.orm import joinedload
@@ -122,7 +123,8 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
# Version 1.8: Add count_members_by_user()
# Version 1.9: Add get_by_instance_uuid()
# Version 1.10: Add hosts field
- VERSION = '1.10'
+ # Version 1.11: Add get_aggregate_uuids()
+ VERSION = '1.11'
fields = {
'id': fields.IntegerField(),
@@ -455,6 +457,38 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
if instance.host]))
@base.remotable
+ def get_aggregate_uuids(self, exclude=None):
+ """Returns a set of aggregate UUIDs associated with all compute nodes
+ that are housing all non-deleted instances in the group
+ """
+ filter_uuids = self.members
+ if exclude:
+ filter_uuids = set(filter_uuids) - set(exclude)
+ filters = {'uuid': filter_uuids, 'deleted': False}
+ instances = objects.InstanceList.get_by_filters(self._context,
+ filters=filters)
+ instance_nodes = set([instance.node for instance in instances
+ if instance.node])
+ if not instance_nodes:
+ return set()
+ return self._get_aggregate_uuids(self._context, instance_nodes)
+
+ @staticmethod
+ @db_api.api_context_manager.reader
+ def _get_aggregate_uuids(ctx, instance_nodes):
+ # Now find the aggregates associated with all those nodes
+ agg_tbl = api_models.Aggregate.__table__
+ agg_host_tbl = api_models.AggregateHost.__table__
+ join = sa.join(agg_tbl, agg_host_tbl,
+ agg_tbl.c.id == agg_host_tbl.c.aggregate_id)
+ sel = sa.select([agg_tbl.c.uuid]).select_from(join)
+ sel = sel.where(agg_host_tbl.c.host.in_(instance_nodes))
+ sel = sel.group_by(agg_tbl.c.uuid)
+ res = ctx.session.execute(sel)
+ agg_uuids = [r[0] for r in res]
+ return set(agg_uuids)
+
+ @base.remotable
def count_members_by_user(self, user_id):
"""Count the number of instances in a group belonging to a user."""
filter_uuids = self.members
diff --git a/nova/tests/functional/db/test_instance_group.py b/nova/tests/functional/db/test_instance_group.py
index 4c4f627fe2..445ae655cc 100644
--- a/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
from nova import exception
from nova import objects
from nova.objects import base
+from nova.objects import fields as obj_fields
from nova.objects import instance_group
from nova import test
from nova.tests import uuidsentinel as uuids
@@ -238,3 +239,114 @@ class InstanceGroupObjectTestCase(test.TestCase):
self.context, 100)
self.assertEqual(0, total)
self.assertEqual(0, done)
+
+
+class InstanceGroupAggregatesTestCase(test.TestCase):
+ def setUp(self):
+ super(InstanceGroupAggregatesTestCase, self).setUp()
+ self.ctx = context.RequestContext('fake-user', 'fake-project')
+
+ def _create_compute_node(self, host, node):
+ cn = objects.ComputeNode(
+ self.ctx,
+ host=host,
+ vcpus=2,
+ memory_mb=2048,
+ local_gb=100,
+ vcpus_used=2,
+ memory_mb_used=2048,
+ local_gb_used=100,
+ hypervisor_type='ironic',
+ hypervisor_version=0,
+ hypervisor_hostname=node,
+ free_ram_mb=0,
+ free_disk_gb=0,
+ current_workload=0,
+ running_vms=0,
+ cpu_info='{}',
+ disk_available_least=0,
+ host_ip='1.1.1.1',
+ supported_hv_specs=[
+ objects.HVSpec.from_list([
+ obj_fields.Architecture.I686,
+ obj_fields.HVType.KVM,
+ obj_fields.VMMode.HVM])
+ ],
+ metrics=None,
+ pci_device_pools=None,
+ extra_resources=None,
+ stats={},
+ numa_topology=None,
+ cpu_allocation_ratio=1.0,
+ ram_allocation_ratio=1.0,
+ disk_allocation_ratio=1.0)
+ cn.create()
+ return cn
+
+ def test_get_aggregate_uuids(self):
+ """Tests that when associating compute nodes to aggregates, and
+ creating an instance group with instances on those compute nodes, that
+ we are able to retrieve the correct set() of aggregate UUIDs from the
+ instance group.
+ """
+ agg1 = objects.Aggregate(self.ctx, name='agg1')
+ agg1.create()
+ agg2 = objects.Aggregate(self.ctx, name='agg2')
+ agg2.create()
+
+ # NOTE(gryf): creating bare instances doesn't propagate project_id
+ # for some reason, so that test would fail.
+ i1 = objects.Instance(self.ctx, host='host1', node='node1',
+ project_id=self.ctx.project_id)
+ i1.create()
+ i2 = objects.Instance(self.ctx, host='host1', node='node2',
+ project_id=self.ctx.project_id)
+ i2.create()
+ i3 = objects.Instance(self.ctx, host='host2', node='node3',
+ project_id=self.ctx.project_id)
+ i3.create()
+
+ all_insts = objects.InstanceList.get_all(self.ctx)
+ exp_inst_uuids = set([i1.uuid, i2.uuid, i3.uuid])
+ self.assertEqual(exp_inst_uuids,
+ set([inst.uuid for inst in all_insts]))
+
+ # Create a server group with just i1 and i2 and verify no aggregate
+ # UUIDs returned from InstanceGroup.get_aggregate_uuids() since the
+ # compute nodes have not yet been associated with any aggregates
+ g1 = objects.InstanceGroup(self.ctx,
+ name='g1',
+ user_id=self.ctx.user_id,
+ project_id=self.ctx.project_id,
+ members=[i1.uuid, i2.uuid],
+ policies=['aggregate-affinity'])
+ g1.create()
+
+ # Create a server group with just i1 and i2 and verify no aggregate
+ # UUIDs returned from InstanceGroup.get_aggregate_uuids() since the
+ # compute nodes have not yet been associated with any aggregates
+ g2 = objects.InstanceGroup(self.ctx,
+ name='g2',
+ user_id=self.ctx.user_id,
+ project_id=self.ctx.project_id,
+ members=[i3.uuid],
+ policies=['aggregate-anti-affinity'])
+
+ g1_agg_uuids = g1.get_aggregate_uuids()
+ self.assertEqual(set(), g1_agg_uuids)
+
+ g2_agg_uuids = g2.get_aggregate_uuids()
+ self.assertEqual(set(), g2_agg_uuids)
+
+ # OK, now associate the compute nodes with various aggregates and
+ # verify the aggregate UUIDs returned by each instance group is
+ # correct.
+ agg1.add_host('node1')
+ agg1.add_host('node2')
+ agg2.add_host('node3')
+
+ g1_agg_uuids = g1.get_aggregate_uuids()
+ self.assertEqual(set([agg1.uuid]), g1_agg_uuids)
+
+ g2_agg_uuids = g2.get_aggregate_uuids()
+ self.assertEqual(set([agg2.uuid]), g2_agg_uuids)
diff --git a/nova/tests/unit/objects/test_instance_group.py b/nova/tests/unit/objects/test_instance_group.py
index d542c18afc..8da6712f6e 100644
--- a/nova/tests/unit/objects/test_instance_group.py
+++ b/nova/tests/unit/objects/test_instance_group.py
@@ -241,6 +241,27 @@ class _TestInstanceGroupObject(object):
mock_il_get.assert_called_once_with(self.context,
filters=expected_filters)
+ @mock.patch('nova.objects.InstanceGroup._get_aggregate_uuids')
+ @mock.patch('nova.objects.InstanceList.get_by_filters')
+ @mock.patch('nova.objects.InstanceGroup._get_from_db_by_uuid',
+ return_value=_INST_GROUP_DB)
+ def test_get_aggregate_uuids(self, mock_get_db, mock_il_get,
+ mock_internal):
+ mock_il_get.return_value = [objects.Instance(node='node1'),
+ objects.Instance(node='node2'),
+ objects.Instance(node=None)]
+ obj = objects.InstanceGroup.get_by_uuid(self.context, _DB_UUID)
+ obj.get_aggregate_uuids()
+ self.assertEqual(['instance_id1', 'instance_id2'], obj.members)
+ expected_filters = {
+ 'uuid': ['instance_id1', 'instance_id2'],
+ 'deleted': False
+ }
+ mock_il_get.assert_called_once_with(self.context,
+ filters=expected_filters)
+ exp_nodes = set(['node1', 'node2'])
+ mock_internal.assert_called_once_with(self.context, exp_nodes)
+
def test_obj_make_compatible(self):
obj = objects.InstanceGroup(self.context, **_INST_GROUP_DB)
obj_primitive = obj.obj_to_primitive()
diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py
index 71b919597f..a577820d0c 100644
--- a/nova/tests/unit/objects/test_objects.py
+++ b/nova/tests/unit/objects/test_objects.py
@@ -1106,7 +1106,7 @@ object_data = {
'InstanceExternalEvent': '1.1-6e446ceaae5f475ead255946dd443417',
'InstanceFault': '1.2-7ef01f16f1084ad1304a513d6d410a38',
'InstanceFaultList': '1.2-6bb72de2872fe49ded5eb937a93f2451',
- 'InstanceGroup': '1.10-1a0c8c7447dc7ecb9da53849430c4a5f',
+ 'InstanceGroup': '1.11-bdd9fa6ab3c80e92fd43b3ba5393e368',
'InstanceGroupList': '1.7-be18078220513316abd0ae1b2d916873',
'InstanceInfoCache': '1.5-cd8b96fefe0fc8d4d337243ba0bf0e1e',
'InstanceList': '2.2-ff71772c7bf6d72f6ef6eee0199fb1c9',
--
2.13.6