mirror of
https://github.com/gryf/openstack.git
synced 2025-12-17 11:30:24 +01:00
255 lines
11 KiB
Diff
255 lines
11 KiB
Diff
From 69d0e023edfc2edc123fd5ed29b79ebbd3abe97f 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/9] 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 | 116 ++++++++++++++++++++++++
|
|
nova/tests/unit/objects/test_instance_group.py | 21 +++++
|
|
nova/tests/unit/objects/test_objects.py | 2 +-
|
|
4 files changed, 173 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(),
|
|
@@ -454,6 +456,38 @@ class InstanceGroup(base.NovaPersistentObject, base.NovaObject,
|
|
return list(set([instance.host for instance in instances
|
|
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."""
|
|
diff --git a/nova/tests/functional/db/test_instance_group.py b/nova/tests/functional/db/test_instance_group.py
|
|
index 4c4f627fe2..b4c7ef3fd8 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,118 @@ 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): We are passing project_id explicitly, due to not going
|
|
+ # 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',
|
|
+ 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.16.1
|
|
|