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
This commit is contained in:
Chris Behrens
2013-05-15 09:09:10 +00:00
parent 9905253132
commit e79ce467aa

View File

@@ -24,6 +24,7 @@ import datetime
import types import types
import uuid as stdlib_uuid import uuid as stdlib_uuid
import mox
from oslo.config import cfg from oslo.config import cfg
from sqlalchemy.dialects import sqlite from sqlalchemy.dialects import sqlite
from sqlalchemy import MetaData from sqlalchemy import MetaData
@@ -36,6 +37,7 @@ from nova.db.sqlalchemy import api as sqlalchemy_api
from nova import exception from nova import exception
from nova.openstack.common.db.sqlalchemy import session as db_session from nova.openstack.common.db.sqlalchemy import session as db_session
from nova.openstack.common import timeutils from nova.openstack.common import timeutils
from nova.openstack.common import uuidutils
from nova import test from nova import test
from nova.tests import matchers from nova.tests import matchers
from nova import utils from nova import utils
@@ -336,6 +338,52 @@ class DbApiTestCase(DbTestCase):
self.assertEqual(0, len(results)) self.assertEqual(0, len(results))
db.instance_update(ctxt, instance['uuid'], {"task_state": None}) 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): def test_network_create_safe(self):
ctxt = context.get_admin_context() ctxt = context.get_admin_context()
values = {'host': 'localhost', 'project_id': 'project1'} values = {'host': 'localhost', 'project_id': 'project1'}