From e79ce467aa7c9fcbf49ae087b0b9f9b4f8ad1747 Mon Sep 17 00:00:00 2001 From: Chris Behrens Date: Wed, 15 May 2013 09:09:10 +0000 Subject: [PATCH] Cells: Don't allow active -> build Adds support for passing 'expected_vm_state' in an instance_update dict much like the current support for 'expected_task_state'. For cells, when the API cell receives an instance update containing a vm_state of BUILDING, raise an exception if the instance is in some other state. This addresses out-of-order messaging issues that can cause an instance to appear to have a huge delay going ACTIVE. (A periodic task run can later heal the state, but it can take a long while) Fixes bug 1180283 Change-Id: I64252b30e2596812f3b84da64b1fc8681661d7f8 --- nova/tests/test_db_api.py | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/nova/tests/test_db_api.py b/nova/tests/test_db_api.py index bf7cc003..4af0483d 100644 --- a/nova/tests/test_db_api.py +++ b/nova/tests/test_db_api.py @@ -24,6 +24,7 @@ import datetime import types import uuid as stdlib_uuid +import mox from oslo.config import cfg from sqlalchemy.dialects import sqlite from sqlalchemy import MetaData @@ -36,6 +37,7 @@ from nova.db.sqlalchemy import api as sqlalchemy_api from nova import exception from nova.openstack.common.db.sqlalchemy import session as db_session from nova.openstack.common import timeutils +from nova.openstack.common import uuidutils from nova import test from nova.tests import matchers from nova import utils @@ -336,6 +338,52 @@ class DbApiTestCase(DbTestCase): self.assertEqual(0, len(results)) db.instance_update(ctxt, instance['uuid'], {"task_state": None}) + def test_instance_update_with_expected_vm_state(self): + ctxt = context.get_admin_context() + uuid = uuidutils.generate_uuid() + updates = {'expected_vm_state': 'meow', + 'moo': 'cow'} + + class FakeInstance(dict): + def save(self, session=None): + pass + + fake_instance_values = {'vm_state': 'meow', + 'hostname': '', + 'metadata': None, + 'system_metadata': None} + fake_instance = FakeInstance(fake_instance_values) + + self.mox.StubOutWithMock(sqlalchemy_api, '_instance_get_by_uuid') + self.mox.StubOutWithMock(fake_instance, 'save') + + sqlalchemy_api._instance_get_by_uuid(ctxt, uuid, + session=mox.IgnoreArg()).AndReturn(fake_instance) + fake_instance.save(session=mox.IgnoreArg()) + + self.mox.ReplayAll() + + result = db.instance_update(ctxt, uuid, updates) + expected_instance = dict(fake_instance_values) + expected_instance['moo'] = 'cow' + self.assertEqual(expected_instance, result) + + def test_instance_update_with_unexpected_vm_state(self): + ctxt = context.get_admin_context() + uuid = uuidutils.generate_uuid() + updates = {'expected_vm_state': 'meow'} + fake_instance = {'vm_state': 'nomatch'} + + self.mox.StubOutWithMock(sqlalchemy_api, '_instance_get_by_uuid') + + sqlalchemy_api._instance_get_by_uuid(ctxt, uuid, + session=mox.IgnoreArg()).AndReturn(fake_instance) + + self.mox.ReplayAll() + + self.assertRaises(exception.UnexpectedVMStateError, + db.instance_update, ctxt, uuid, updates) + def test_network_create_safe(self): ctxt = context.get_admin_context() values = {'host': 'localhost', 'project_id': 'project1'}