Disconnect from iSCSI volume sessions after live migration

The live migration source host will now disconnect from
iSCSI volume sessions after the VM is successfully migrated
to the destination host.

Fixes bug 1132146
Change-Id: I132869612bdf2baa810756586e643ea68ea8d8f6
This commit is contained in:
Jason Dillaman 2013-06-26 16:53:03 -04:00
parent a4028e6a36
commit 38e6e93a81
6 changed files with 83 additions and 25 deletions

View File

@ -3846,6 +3846,11 @@ class ComputeManager(manager.SchedulerDependentManager):
LOG.info(_('_post_live_migration() is started..'),
instance=instance_ref)
# Cleanup source host post live-migration
block_device_info = self._get_instance_volume_block_device_info(
ctxt, instance_ref)
self.driver.post_live_migration(ctxt, instance_ref, block_device_info)
# Detaching volumes.
connector = self.driver.get_volume_connector(instance_ref)
for bdm in self._get_instance_volume_bdms(ctxt, instance_ref):

View File

@ -19,6 +19,7 @@
"""Tests for compute service."""
import base64
import contextlib
import copy
import datetime
import operator
@ -28,6 +29,7 @@ import time
import traceback
import uuid
import mock
import mox
from oslo.config import cfg
@ -4345,32 +4347,35 @@ class ComputeTestCase(BaseTestCase):
'power_state': power_state.PAUSED})
# creating mocks
self.mox.StubOutWithMock(self.compute.driver, 'unfilter_instance')
self.compute.driver.unfilter_instance(inst_ref, [])
self.mox.StubOutWithMock(self.compute.conductor_api,
'network_migrate_instance_start')
migration = {'source_compute': srchost,
'dest_compute': dest, }
self.compute.conductor_api.network_migrate_instance_start(c, inst_ref,
migration)
with contextlib.nested(
mock.patch.object(self.compute.driver, 'post_live_migration'),
mock.patch.object(self.compute.driver, 'unfilter_instance'),
mock.patch.object(self.compute.conductor_api,
'network_migrate_instance_start'),
mock.patch.object(self.compute.compute_rpcapi,
'post_live_migration_at_destination'),
mock.patch.object(self.compute.driver, 'unplug_vifs'),
mock.patch.object(self.compute.network_api,
'setup_networks_on_host')
) as (
post_live_migration, unfilter_instance,
network_migrate_instance_start, post_live_migration_at_destination,
unplug_vifs, setup_networks_on_host
):
self.compute._post_live_migration(c, inst_ref, dest)
self.mox.StubOutWithMock(self.compute.compute_rpcapi,
'post_live_migration_at_destination')
self.compute.compute_rpcapi.post_live_migration_at_destination(
c, inst_ref, False, dest)
self.mox.StubOutWithMock(self.compute.driver, 'unplug_vifs')
self.compute.driver.unplug_vifs(inst_ref, [])
self.mox.StubOutWithMock(self.compute.network_api,
'setup_networks_on_host')
self.compute.network_api.setup_networks_on_host(c, inst_ref,
self.compute.host,
teardown=True)
# start test
self.mox.ReplayAll()
self.compute._post_live_migration(c, inst_ref, dest)
post_live_migration.assert_has_calls([
mock.call(c, inst_ref, {'block_device_mapping': []})])
unfilter_instance.assert_has_calls([mock.call(inst_ref, [])])
migration = {'source_compute': srchost,
'dest_compute': dest, }
network_migrate_instance_start.assert_has_calls([
mock.call(c, inst_ref, migration)])
post_live_migration_at_destination.assert_has_calls([
mock.call(c, inst_ref, False, dest)])
unplug_vifs.assert_has_calls([mock.call(inst_ref, [])])
setup_networks_on_host.assert_has_calls([
mock.call(c, inst_ref, self.compute.host, teardown=True)])
def _begin_post_live_migration_at_destination(self):
self.mox.StubOutWithMock(self.compute.network_api,

View File

@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import contextlib
import copy
import errno
import eventlet
@ -27,6 +28,7 @@ import tempfile
from eventlet import greenthread
from lxml import etree
import mock
from oslo.config import cfg
from xml.dom import minidom
@ -2757,6 +2759,31 @@ class LibvirtConnTestCase(test.TestCase):
db.instance_destroy(self.context, instance_ref['uuid'])
def test_post_live_migration(self):
vol = {'block_device_mapping': [
{'connection_info': 'dummy1', 'mount_device': '/dev/sda'},
{'connection_info': 'dummy2', 'mount_device': '/dev/sdb'}]}
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
inst_ref = {'id': 'foo'}
cntx = context.get_admin_context()
# Set up the mock expectations
with contextlib.nested(
mock.patch.object(driver, 'block_device_info_get_mapping',
return_value=vol['block_device_mapping']),
mock.patch.object(conn, 'volume_driver_method')
) as (block_device_info_get_mapping, volume_driver_method):
conn.post_live_migration(cntx, inst_ref, vol)
block_device_info_get_mapping.assert_has_calls([
mock.call(vol)])
volume_driver_method.assert_has_calls([
mock.call('disconnect_volume',
v['connection_info'],
v['mount_device'].rpartition("/")[2])
for v in vol['block_device_mapping']])
def test_get_instance_disk_info_excludes_volumes(self):
# Test data
instance_ref = db.instance_create(self.context, self.test_instance)

View File

@ -514,6 +514,15 @@ class ComputeDriver(object):
"""
raise NotImplementedError()
def post_live_migration(self, ctxt, instance_ref, block_device_info):
"""Post operation of live migration at source host.
:param ctxt: security contet
:instance_ref: instance object that was migrated
:block_device_info: instance block device information
"""
pass
def post_live_migration_at_destination(self, ctxt, instance_ref,
network_info,
block_migration=False,

View File

@ -3572,6 +3572,17 @@ class LibvirtDriver(driver.ComputeDriver):
# following normal way.
self._fetch_instance_kernel_ramdisk(context, instance)
def post_live_migration(self, context, instance, block_device_info):
# Disconnect from volume server
block_device_mapping = driver.block_device_info_get_mapping(
block_device_info)
for vol in block_device_mapping:
connection_info = vol['connection_info']
disk_dev = vol['mount_device'].rpartition("/")[2]
self.volume_driver_method('disconnect_volume',
connection_info,
disk_dev)
def post_live_migration_at_destination(self, context,
instance,
network_info,

View File

@ -3,6 +3,7 @@ coverage>=3.6
discover
feedparser
fixtures>=0.3.12
mock>=1.0
mox==0.5.3
MySQL-python
psycopg2