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:
		@@ -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'}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user