summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/conductor/api.py15
-rw-r--r--nova/conductor/manager.py35
-rw-r--r--nova/conductor/rpcapi.py18
-rw-r--r--nova/scheduler/driver.py26
-rw-r--r--nova/scheduler/filter_scheduler.py26
-rw-r--r--nova/tests/conductor/test_conductor.py50
-rw-r--r--nova/tests/scheduler/test_filter_scheduler.py13
7 files changed, 158 insertions, 25 deletions
diff --git a/nova/conductor/api.py b/nova/conductor/api.py
index 3f955d24f..c681dfb08 100644
--- a/nova/conductor/api.py
+++ b/nova/conductor/api.py
@@ -109,6 +109,21 @@ class LocalAPI(object):
def instance_fault_create(self, context, values):
return self._manager.instance_fault_create(context, values)
+ def instance_group_members_add(self, context, group_uuid, members,
+ set_delete=False):
+ return self._manager.instance_group_members_add(context,
+ group_uuid,
+ members,
+ set_delete=set_delete)
+
+ def instance_group_member_delete(self, context, group_uuid, instance_id):
+ return self._manager.instance_group_member_delete(context,
+ group_uuid,
+ instance_id)
+
+ def instance_group_get_all(self, context, group_uuid):
+ return self._manager.instance_group_get_all(context, group_uuid)
+
def migration_get(self, context, migration_id):
return self._manager.migration_get(context, migration_id)
diff --git a/nova/conductor/manager.py b/nova/conductor/manager.py
index 6693e7fee..053c44323 100644
--- a/nova/conductor/manager.py
+++ b/nova/conductor/manager.py
@@ -67,7 +67,7 @@ class ConductorManager(manager.Manager):
namespace. See the ComputeTaskManager class for details.
"""
- RPC_API_VERSION = '1.51'
+ RPC_API_VERSION = '1.52'
def __init__(self, *args, **kwargs):
super(ConductorManager, self).__init__(service_name='conductor',
@@ -319,6 +319,13 @@ class ConductorManager(manager.Manager):
self.db.instance_destroy(context, instance['uuid'])
def instance_info_cache_delete(self, context, instance):
+ # Delete the instance from the instance_group member data
+ system_meta = self.db.instance_system_metadata_get(context,
+ instance['uuid'])
+ instance_group = system_meta.get('instance_group', None)
+ if instance_group:
+ self.db.instance_group_member_delete(context, instance_group,
+ instance['uuid'])
self.db.instance_info_cache_delete(context, instance['uuid'])
def instance_info_cache_update(self, context, instance, values):
@@ -333,6 +340,32 @@ class ConductorManager(manager.Manager):
result = self.db.instance_fault_create(context, values)
return jsonutils.to_primitive(result)
+ def instance_group_members_add(self, context, group_uuid, members,
+ set_delete=False):
+ result = self.db.instance_group_members_add(context, group_uuid,
+ members, set_delete=set_delete)
+ return jsonutils.to_primitive(result)
+
+ def instance_group_member_delete(self, context, group_uuid, instance_id):
+ result = self.db.instance_group_member_delete(context,
+ group_uuid, instance_id)
+ return jsonutils.to_primitive(result)
+
+ def instance_group_get_all(self, context, group_uuid):
+ instance_group = self.db.instance_group_get(context, group_uuid)
+ instances = []
+ for member in instance_group['members']:
+ try:
+ instance = self.db.instance_get_by_uuid(context, member)
+ if instance:
+ instances.append(instance)
+ except exception.InstanceNotFound:
+ LOG.error(_("Invalid instance %(member)s in instance "
+ " group %(group)s"),
+ {'member': member, 'group': group_uuid})
+ return jsonutils.to_primitive({'instance_group': instance_group,
+ 'instances': instances})
+
# NOTE(kerrin): This method can be removed in v2.0 of the RPC API.
def vol_get_usage_by_time(self, context, start_time):
result = self.db.vol_get_usage_by_time(context, start_time)
diff --git a/nova/conductor/rpcapi.py b/nova/conductor/rpcapi.py
index bb66ca8b2..4e78e29bb 100644
--- a/nova/conductor/rpcapi.py
+++ b/nova/conductor/rpcapi.py
@@ -102,6 +102,8 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
1.50 - Added object_action() and object_class_action()
1.51 - Added the 'legacy' argument to
block_device_mapping_get_all_by_instance
+ 1.52 - Added instance_group_members_add, instance_group_member_delete and
+ instance_group_get_all
"""
BASE_RPC_API_VERSION = '1.0'
@@ -333,6 +335,22 @@ class ConductorAPI(nova.openstack.common.rpc.proxy.RpcProxy):
msg = self.make_msg('instance_fault_create', values=values)
return self.call(context, msg, version='1.36')
+ def instance_group_members_add(self, context, group_uuid, members,
+ set_delete=False):
+ msg = self.make_msg('instance_group_members_add',
+ group_uuid=group_uuid, members=members,
+ set_delete=set_delete)
+ return self.call(context, msg, version='1.52')
+
+ def instance_group_member_delete(self, context, group_uuid, instance_id):
+ msg = self.make_msg('instance_group_member_delete',
+ group_uuid=group_uuid, instance_id=instance_id)
+ return self.call(context, msg, version='1.52')
+
+ def instance_group_get_all(self, context, group_uuid):
+ msg = self.make_msg('instance_group_get_all', group_uuid=group_uuid)
+ return self.call(context, msg, version='1.52')
+
def action_event_start(self, context, values):
values_p = jsonutils.to_primitive(values)
msg = self.make_msg('action_event_start', values=values_p)
diff --git a/nova/scheduler/driver.py b/nova/scheduler/driver.py
index d5a1eedea..6a429f526 100644
--- a/nova/scheduler/driver.py
+++ b/nova/scheduler/driver.py
@@ -94,6 +94,14 @@ def instance_update_db(context, instance_uuid, extra_values=None):
values = {'host': None, 'node': None, 'scheduled_at': now}
if extra_values:
values.update(extra_values)
+ # Get the instance_group if it exists. This will be written to the
+ # system_metadata as it will be used when the VM is deleted
+ system_metadata = extra_values.get('system_metadata', None)
+ if system_metadata:
+ instance_group = system_metadata.get('instance_group', None)
+ if instance_group:
+ conductor_api.instance_group_members_add(context,
+ instance_group, [instance_uuid])
return db.instance_update(context, instance_uuid, values)
@@ -139,15 +147,17 @@ class Scheduler(object):
for service in services
if self.servicegroup_api.service_is_up(service)]
- def group_hosts(self, context, group):
- """Return the list of hosts that have VM's from the group."""
+ def instance_group_data(self, context, group_uuid):
+ """Return the the group data for the instance group."""
- # The system_metadata 'group' will be filtered
- members = db.instance_get_all_by_filters(context,
- {'deleted': False, 'group': group})
- return [member['host']
- for member in members
- if member.get('host') is not None]
+ return conductor_api.instance_group_get_all(context, group_uuid)
+
+ def instance_group_hosts(self, context, instance_group):
+ """Return the list of hosts that have VM's from the instance_group."""
+
+ instances = instance_group['instances']
+ return [instance['host'] for instance in instances
+ if instance.get('host') is not None]
def schedule_prep_resize(self, context, image, request_spec,
filter_properties, instance, instance_type,
diff --git a/nova/scheduler/filter_scheduler.py b/nova/scheduler/filter_scheduler.py
index 08cb6a20e..a75bf286f 100644
--- a/nova/scheduler/filter_scheduler.py
+++ b/nova/scheduler/filter_scheduler.py
@@ -173,11 +173,11 @@ class FilterScheduler(driver.Scheduler):
# Update the metadata if necessary
scheduler_hints = filter_properties.get('scheduler_hints') or {}
- group = scheduler_hints.get('group', None)
+ group_uuid = scheduler_hints.get('instance_group', None)
values = None
- if group:
+ if group_uuid:
values = request_spec['instance_properties']['system_metadata']
- values.update({'group': group})
+ values.update({'instance_group': group_uuid})
values = {'system_metadata': values}
updated_instance = driver.instance_update_db(context,
@@ -300,17 +300,19 @@ class FilterScheduler(driver.Scheduler):
instance_properties = request_spec['instance_properties']
instance_type = request_spec.get("instance_type", None)
- # Get the group
+ # Get the instance_group
update_group_hosts = False
scheduler_hints = filter_properties.get('scheduler_hints') or {}
- group = scheduler_hints.get('group', None)
- if group:
- group_hosts = self.group_hosts(elevated, group)
- update_group_hosts = True
- if 'group_hosts' not in filter_properties:
- filter_properties.update({'group_hosts': []})
- configured_hosts = filter_properties['group_hosts']
- filter_properties['group_hosts'] = configured_hosts + group_hosts
+ group_uuid = scheduler_hints.get('instance_group', None)
+ if group_uuid:
+ group_data = self.instance_group_data(elevated, group_uuid)
+ if 'anti-affinity' in group_data['instance_group']['policies']:
+ update_group_hosts = True
+ group_hosts = self.instance_group_hosts(elevated, group_data)
+ if 'group_hosts' not in filter_properties:
+ filter_properties.update({'group_hosts': []})
+ user_hosts = filter_properties['group_hosts']
+ filter_properties['group_hosts'] = user_hosts + group_hosts
config_options = self._get_configuration_options()
diff --git a/nova/tests/conductor/test_conductor.py b/nova/tests/conductor/test_conductor.py
index 7a33cfbb9..b86d3320a 100644
--- a/nova/tests/conductor/test_conductor.py
+++ b/nova/tests/conductor/test_conductor.py
@@ -340,7 +340,25 @@ class _BaseTestCase(object):
self.conductor.instance_destroy(self.context, {'uuid': 'fake-uuid'})
def test_instance_info_cache_delete(self):
+ self.mox.StubOutWithMock(db, 'instance_system_metadata_get')
self.mox.StubOutWithMock(db, 'instance_info_cache_delete')
+ fake_data = {}
+ db.instance_system_metadata_get(self.context,
+ 'fake-uuid').AndReturn(fake_data)
+ db.instance_info_cache_delete(self.context, 'fake-uuid')
+ self.mox.ReplayAll()
+ self.conductor.instance_info_cache_delete(self.context,
+ {'uuid': 'fake-uuid'})
+
+ def test_instance_info_cache_delete_with_instance_group(self):
+ self.mox.StubOutWithMock(db, 'instance_system_metadata_get')
+ self.mox.StubOutWithMock(db, 'instance_group_member_delete')
+ self.mox.StubOutWithMock(db, 'instance_info_cache_delete')
+ fake_data = {'instance_group': 'fake-group'}
+ db.instance_system_metadata_get(self.context,
+ 'fake-uuid').AndReturn(fake_data)
+ db.instance_group_member_delete(self.context, 'fake-group',
+ 'fake-uuid')
db.instance_info_cache_delete(self.context, 'fake-uuid')
self.mox.ReplayAll()
self.conductor.instance_info_cache_delete(self.context,
@@ -438,6 +456,38 @@ class _BaseTestCase(object):
'fake-values')
self.assertEqual(result, 'fake-result')
+ def test_instance_group_members_add(self):
+ self.mox.StubOutWithMock(db, 'instance_group_members_add')
+ db.instance_group_members_add(self.context, 'fake-uuid',
+ 'fake-members', set_delete=False).AndReturn('fake-members')
+ self.mox.ReplayAll()
+ result = self.conductor.instance_group_members_add(self.context,
+ 'fake-uuid', 'fake-members', set_delete=False)
+ self.assertEqual(result, 'fake-members')
+
+ def test_instance_group_member_delete(self):
+ self.mox.StubOutWithMock(db, 'instance_group_member_delete')
+ db.instance_group_member_delete(self.context, 'fake-uuid',
+ 'fake-instance-id').AndReturn('fake-result')
+ self.mox.ReplayAll()
+ result = self.conductor.instance_group_member_delete(self.context,
+ 'fake-uuid', 'fake-instance-id')
+ self.assertEqual(result, 'fake-result')
+
+ def test_instance_group_get_all(self):
+ self.mox.StubOutWithMock(db, 'instance_group_get')
+ self.mox.StubOutWithMock(db, 'instance_get_by_uuid')
+ group = {'uuid': 'fake-uuid',
+ 'members': ['fake-instance-id']}
+ db.instance_group_get(self.context, 'fake-uuid').AndReturn(group)
+ db.instance_get_by_uuid(self.context,
+ 'fake-instance-id').AndReturn('fake-instance')
+ self.mox.ReplayAll()
+ result = self.conductor.instance_group_get_all(self.context,
+ 'fake-uuid')
+ self.assertEqual(result, {'instance_group': group,
+ 'instances': ['fake-instance']})
+
def test_task_log_get(self):
self.mox.StubOutWithMock(db, 'task_log_get')
db.task_log_get(self.context, 'task', 'begin', 'end', 'host',
diff --git a/nova/tests/scheduler/test_filter_scheduler.py b/nova/tests/scheduler/test_filter_scheduler.py
index d6cc7808e..a02889df9 100644
--- a/nova/tests/scheduler/test_filter_scheduler.py
+++ b/nova/tests/scheduler/test_filter_scheduler.py
@@ -532,7 +532,7 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
def test_basic_schedule_run_instances_anti_affinity(self):
filter_properties = {'scheduler_hints':
- {'group': 'cats'}}
+ {'instance_group': 'fake_uuid'}}
# Request spec 1
instance_opts1 = {'project_id': 1, 'os_type': 'Linux',
'memory_mb': 512, 'root_gb': 512,
@@ -562,19 +562,24 @@ class FilterSchedulerTestCase(test_scheduler.SchedulerTestCase):
self.mox.StubOutWithMock(driver, 'instance_update_db')
self.mox.StubOutWithMock(compute_rpcapi.ComputeAPI, 'run_instance')
- self.mox.StubOutWithMock(sched, 'group_hosts')
+ self.mox.StubOutWithMock(sched, 'instance_group_data')
instance1_1 = {'uuid': 'fake-uuid1-1'}
instance1_2 = {'uuid': 'fake-uuid1-2'}
- sched.group_hosts(mox.IgnoreArg(), 'cats').AndReturn([])
+ group_data = {'instance_group': {'uuid': 'fake_uuid',
+ 'policies': ['anti-affinity']},
+ 'instances': []}
+ sched.instance_group_data(mox.IgnoreArg(),
+ 'fake_uuid').AndReturn(group_data)
def inc_launch_index1(*args, **kwargs):
request_spec1['instance_properties']['launch_index'] = (
request_spec1['instance_properties']['launch_index'] + 1)
expected_metadata = {'system_metadata':
- {'system': 'metadata', 'group': 'cats'}}
+ {'system': 'metadata',
+ 'instance_group': 'fake_uuid'}}
driver.instance_update_db(fake_context, instance1_1['uuid'],
extra_values=expected_metadata).WithSideEffects(
inc_launch_index1).AndReturn(instance1_1)