Skip to content

Commit

Permalink
Compute doesn't set the 'host' field in instance
Browse files Browse the repository at this point in the history
When an instance starts, compute ask the resource manager to check the
compute capabilities. If the compute resources are sufficient, the
resource manager updates the 'host' instance field in the database. But
the local variable 'instance' use by compute manger isn't updated.
So, when compute manager asks the network manager to allocate a network
for the instance, the 'host' instance field is null.

Some compute tests doesn't update the local variable instance when
they change some parameter on instance and failed with this patch.

Fixes LP bug #1073600

Change-Id: I842d4814b9eabc6222c68118d8a244b20bb68164
  • Loading branch information
Édouard Thuleau committed Nov 21, 2012
1 parent 5582d20 commit d32d5b2
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 41 deletions.
5 changes: 5 additions & 0 deletions nova/compute/manager.py
Expand Up @@ -1762,6 +1762,11 @@ def _prep_resize(self, context, image, instance, instance_type,
if not filter_properties:
filter_properties = {}

if not instance['host']:
self._set_instance_error_state(context, instance['uuid'])
msg = _('Instance has no source host')
raise exception.MigrationError(msg)

same_host = instance['host'] == self.host
if same_host and not CONF.allow_resize_to_same_host:
self._set_instance_error_state(context, instance['uuid'])
Expand Down
21 changes: 10 additions & 11 deletions nova/compute/resource_tracker.py
Expand Up @@ -83,22 +83,20 @@ def instance_claim(self, context, instance_ref, limits=None):
if self.disabled:
# compute_driver doesn't support resource tracking, just
# set the 'host' field and continue the build:
instance_ref = self._set_instance_host(context,
instance_ref['uuid'])
self._set_instance_host(context, instance_ref)
return claims.NopClaim()

# sanity check:
if instance_ref['host']:
LOG.warning(_("Host field should be not be set on the instance "
"until resources have been claimed."),
LOG.warning(_("Host field should not be set on the instance until "
"resources have been claimed."),
instance=instance_ref)

claim = claims.Claim(instance_ref, self)

if claim.test(self.compute_node, limits):

instance_ref = self._set_instance_host(context,
instance_ref['uuid'])
self._set_instance_host(context, instance_ref)

# Mark resources in-use and update stats
self._update_usage_from_instance(self.compute_node, instance_ref)
Expand Down Expand Up @@ -170,16 +168,17 @@ def _create_migration(self, context, instance, instance_type):
'new_instance_type_id': instance_type['id'],
'status': 'pre-migrating'})

def _set_instance_host(self, context, instance_uuid):
def _set_instance_host(self, context, instance_ref):
"""Tag the instance as belonging to this host. This should be done
while the COMPUTE_RESOURCES_SEMPAHORE is held so the resource claim
will not be lost if the audit process starts.
"""
values = {'host': self.host, 'launched_on': self.host}
(old_ref, instance_ref) = db.instance_update_and_get_original(context,
instance_uuid, values)
notifications.send_update(context, old_ref, instance_ref)
return instance_ref
(old_ref, new_ref) = db.instance_update_and_get_original(context,
instance_ref['uuid'], values)
notifications.send_update(context, old_ref, new_ref)
instance_ref['host'] = self.host
instance_ref['launched_on'] = self.host

def abort_instance_claim(self, instance):
"""Remove usage from the given instance"""
Expand Down
91 changes: 61 additions & 30 deletions nova/tests/compute/test_compute.py
Expand Up @@ -1771,21 +1771,23 @@ def test_finish_resize_instance_notification(self):
new_type_id = new_type['id']
self.compute.run_instance(self.context, instance=instance)

db.instance_update(self.context, instance['uuid'], {'host': 'foo'})
db.instance_update(self.context, instance["uuid"],
new_instance = db.instance_update(self.context, instance['uuid'],
{'host': 'foo'})
new_instance = jsonutils.to_primitive(new_instance)
db.instance_update(self.context, new_instance["uuid"],
{"task_state": task_states.RESIZE_PREP})
self.compute.prep_resize(self.context, instance=instance,
self.compute.prep_resize(self.context, instance=new_instance,
instance_type=new_type, image={})
migration_ref = db.migration_get_by_instance_and_status(
self.context.elevated(), instance['uuid'], 'pre-migrating')
self.compute.resize_instance(self.context, instance=instance,
self.context.elevated(), new_instance['uuid'], 'pre-migrating')
self.compute.resize_instance(self.context, instance=new_instance,
migration=migration_ref, image={}, instance_type=new_type)
timeutils.set_time_override(cur_time)
test_notifier.NOTIFICATIONS = []

self.compute.finish_resize(self.context,
migration=jsonutils.to_primitive(migration_ref),
disk_info={}, image={}, instance=instance)
disk_info={}, image={}, instance=new_instance)

self.assertEquals(len(test_notifier.NOTIFICATIONS), 2)
msg = test_notifier.NOTIFICATIONS[0]
Expand All @@ -1798,7 +1800,7 @@ def test_finish_resize_instance_notification(self):
payload = msg['payload']
self.assertEquals(payload['tenant_id'], self.project_id)
self.assertEquals(payload['user_id'], self.user_id)
self.assertEquals(payload['instance_id'], instance['uuid'])
self.assertEquals(payload['instance_id'], new_instance['uuid'])
self.assertEquals(payload['instance_type'], 'm1.small')
self.assertEquals(str(payload['instance_type_id']), str(new_type_id))
self.assertTrue('display_name' in payload)
Expand All @@ -1808,7 +1810,7 @@ def test_finish_resize_instance_notification(self):
image_ref_url = utils.generate_image_url(FAKE_IMAGE_REF)
self.assertEquals(payload['image_ref_url'], image_ref_url)
self.compute.terminate_instance(self.context,
instance=jsonutils.to_primitive(instance))
instance=jsonutils.to_primitive(new_instance))

def test_resize_instance_notification(self):
"""Ensure notifications on instance migrate/resize"""
Expand All @@ -1821,12 +1823,14 @@ def test_resize_instance_notification(self):
timeutils.set_time_override(cur_time)
test_notifier.NOTIFICATIONS = []

db.instance_update(self.context, instance['uuid'], {'host': 'foo'})
new_instance = db.instance_update(self.context, instance['uuid'],
{'host': 'foo'})
new_instance = jsonutils.to_primitive(new_instance)
instance_type = instance_types.get_default_instance_type()
self.compute.prep_resize(self.context, instance=instance,
self.compute.prep_resize(self.context, instance=new_instance,
instance_type=instance_type, image={})
db.migration_get_by_instance_and_status(self.context.elevated(),
instance['uuid'],
new_instance['uuid'],
'pre-migrating')

self.assertEquals(len(test_notifier.NOTIFICATIONS), 3)
Expand All @@ -1843,7 +1847,7 @@ def test_resize_instance_notification(self):
payload = msg['payload']
self.assertEquals(payload['tenant_id'], self.project_id)
self.assertEquals(payload['user_id'], self.user_id)
self.assertEquals(payload['instance_id'], instance['uuid'])
self.assertEquals(payload['instance_id'], new_instance['uuid'])
self.assertEquals(payload['instance_type'], 'm1.tiny')
type_id = instance_types.get_instance_type_by_name('m1.tiny')['id']
self.assertEquals(str(payload['instance_type_id']), str(type_id))
Expand All @@ -1852,10 +1856,12 @@ def test_resize_instance_notification(self):
self.assertTrue('launched_at' in payload)
image_ref_url = utils.generate_image_url(FAKE_IMAGE_REF)
self.assertEquals(payload['image_ref_url'], image_ref_url)
self.compute.terminate_instance(self.context, instance=instance)
self.compute.terminate_instance(self.context, instance=new_instance)

def test_prep_resize_instance_migration_error(self):
"""Ensure prep_resize raise a migration error"""
def test_prep_resize_instance_migration_error_on_same_host(self):
"""Ensure prep_resize raise a migration error if destination is set on
the same source host and allow_resize_to_same_host is false
"""
self.flags(host="foo", allow_resize_to_same_host=False)

instance = jsonutils.to_primitive(self._create_fake_instance())
Expand All @@ -1874,6 +1880,26 @@ def test_prep_resize_instance_migration_error(self):
reservations=reservations)
self.compute.terminate_instance(self.context, instance=new_instance)

def test_prep_resize_instance_migration_error_on_none_host(self):
"""Ensure prep_resize raise a migration error if destination host is
not defined
"""
instance = jsonutils.to_primitive(self._create_fake_instance())

reservations = self._ensure_quota_reservations_rolledback()

self.compute.run_instance(self.context, instance=instance)
new_instance = db.instance_update(self.context, instance['uuid'],
{'host': None})
new_instance = jsonutils.to_primitive(new_instance)
instance_type = instance_types.get_default_instance_type()

self.assertRaises(exception.MigrationError, self.compute.prep_resize,
self.context, instance=new_instance,
instance_type=instance_type, image={},
reservations=reservations)
self.compute.terminate_instance(self.context, instance=new_instance)

def test_resize_instance_driver_error(self):
"""Ensure instance status set to Error on resize error"""

Expand All @@ -1889,22 +1915,24 @@ def throw_up(*args, **kwargs):
reservations = self._ensure_quota_reservations_rolledback()

self.compute.run_instance(self.context, instance=instance)
db.instance_update(self.context, instance['uuid'], {'host': 'foo'})
self.compute.prep_resize(self.context, instance=instance,
new_instance = db.instance_update(self.context, instance['uuid'],
{'host': 'foo'})
new_instance = jsonutils.to_primitive(new_instance)
self.compute.prep_resize(self.context, instance=new_instance,
instance_type=instance_type, image={},
reservations=reservations)
migration_ref = db.migration_get_by_instance_and_status(
self.context.elevated(), instance['uuid'], 'pre-migrating')
self.context.elevated(), new_instance['uuid'], 'pre-migrating')

db.instance_update(self.context, instance['uuid'],
db.instance_update(self.context, new_instance['uuid'],
{"task_state": task_states.RESIZE_PREP})
#verify
self.assertRaises(test.TestingException, self.compute.resize_instance,
self.context, instance=instance,
self.context, instance=new_instance,
migration=migration_ref, image={},
reservations=reservations,
instance_type=jsonutils.to_primitive(instance_type))
instance = db.instance_get_by_uuid(self.context, instance['uuid'])
instance = db.instance_get_by_uuid(self.context, new_instance['uuid'])
self.assertEqual(instance['vm_state'], vm_states.ERROR)

self.compute.terminate_instance(self.context,
Expand All @@ -1916,22 +1944,23 @@ def test_resize_instance(self):
instance_type = instance_types.get_default_instance_type()

self.compute.run_instance(self.context, instance=instance)
db.instance_update(self.context, instance['uuid'],
{'host': 'foo'})
self.compute.prep_resize(self.context, instance=instance,
new_instance = db.instance_update(self.context, instance['uuid'],
{'host': 'foo'})
new_instance = jsonutils.to_primitive(new_instance)
self.compute.prep_resize(self.context, instance=new_instance,
instance_type=instance_type, image={})
migration_ref = db.migration_get_by_instance_and_status(
self.context.elevated(), instance['uuid'], 'pre-migrating')
db.instance_update(self.context, instance['uuid'],
self.context.elevated(), new_instance['uuid'], 'pre-migrating')
db.instance_update(self.context, new_instance['uuid'],
{"task_state": task_states.RESIZE_PREP})
self.compute.resize_instance(self.context, instance=instance,
self.compute.resize_instance(self.context, instance=new_instance,
migration=migration_ref, image={},
instance_type=jsonutils.to_primitive(instance_type))
inst = db.instance_get_by_uuid(self.context, instance['uuid'])
inst = db.instance_get_by_uuid(self.context, new_instance['uuid'])
self.assertEqual(migration_ref['dest_compute'], inst['host'])

self.compute.terminate_instance(self.context,
instance=jsonutils.to_primitive(instance))
instance=jsonutils.to_primitive(inst))

def test_finish_revert_resize(self):
"""Ensure that the flavor is reverted to the original on revert"""
Expand Down Expand Up @@ -2052,7 +2081,9 @@ def raise_migration_failure(*args):
instance_type = instance_types.get_default_instance_type()

self.compute.run_instance(self.context, instance=inst_ref)
db.instance_update(self.context, inst_ref['uuid'], {'host': 'foo'})
inst_ref = db.instance_update(self.context, inst_ref['uuid'],
{'host': 'foo'})
inst_ref = jsonutils.to_primitive(inst_ref)
self.compute.prep_resize(self.context, instance=inst_ref,
instance_type=instance_type,
image={}, reservations=reservations)
Expand Down
12 changes: 12 additions & 0 deletions nova/tests/compute/test_resource_tracker.py
Expand Up @@ -155,6 +155,7 @@ def _fake_instance(self, *args, **kwargs):
'vcpus': 1,
'host': None,
'instance_type_id': 1,
'launched_on': None,
}
instance.update(kwargs)

Expand Down Expand Up @@ -799,3 +800,14 @@ def test_dupe_filter(self):

self.tracker.update_available_resource(self.context)
self.assertEqual(1, len(self.tracker.tracked_migrations))

def test_set_instance_host(self):
instance = self._fake_instance()
self.assertEqual(None, instance['host'])
self.assertEqual(None, instance['launched_on'])

claim = self.tracker.instance_claim(self.context, instance)
self.assertNotEqual(0, claim.memory_mb)

self.assertEqual('fakehost', instance['host'])
self.assertEqual('fakehost', instance['launched_on'])

0 comments on commit d32d5b2

Please sign in to comment.