diff options
-rw-r--r-- | nova/conductor/api.py | 15 | ||||
-rw-r--r-- | nova/conductor/manager.py | 35 | ||||
-rw-r--r-- | nova/conductor/rpcapi.py | 18 | ||||
-rw-r--r-- | nova/scheduler/driver.py | 26 | ||||
-rw-r--r-- | nova/scheduler/filter_scheduler.py | 26 | ||||
-rw-r--r-- | nova/tests/conductor/test_conductor.py | 50 | ||||
-rw-r--r-- | nova/tests/scheduler/test_filter_scheduler.py | 13 |
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) |