Fix live-migration when using ovs-vif-driver
When live-migrating a vm the ports are not added to the bridge at the destination. This patch ensures that ports are added correctly during live migration. Closes-Bug: #1551222 Change-Id: I70735ef1544622fbad2bf88d105192afe087a5b0
This commit is contained in:
parent
11825f0474
commit
ce3e4e9317
@ -245,6 +245,10 @@ class HyperVDriver(driver.ComputeDriver):
|
|||||||
self._livemigrationops.post_live_migration(context, instance,
|
self._livemigrationops.post_live_migration(context, instance,
|
||||||
block_device_info)
|
block_device_info)
|
||||||
|
|
||||||
|
def post_live_migration_at_source(self, context, instance, network_info):
|
||||||
|
"""Unplug VIFs from networks at source."""
|
||||||
|
self._vmops.unplug_vifs(instance, network_info)
|
||||||
|
|
||||||
def post_live_migration_at_destination(self, context, instance,
|
def post_live_migration_at_destination(self, context, instance,
|
||||||
network_info,
|
network_info,
|
||||||
block_migration=False,
|
block_migration=False,
|
||||||
|
@ -102,6 +102,7 @@ class LiveMigrationOps(object):
|
|||||||
network_info, block_migration):
|
network_info, block_migration):
|
||||||
LOG.debug("post_live_migration_at_destination called",
|
LOG.debug("post_live_migration_at_destination called",
|
||||||
instance=instance_ref)
|
instance=instance_ref)
|
||||||
|
self._vmops.post_start_vifs(instance_ref, network_info)
|
||||||
|
|
||||||
def check_can_live_migrate_destination(self, ctxt, instance_ref,
|
def check_can_live_migrate_destination(self, ctxt, instance_ref,
|
||||||
src_compute_info, dst_compute_info,
|
src_compute_info, dst_compute_info,
|
||||||
|
@ -644,10 +644,7 @@ class VMOps(object):
|
|||||||
|
|
||||||
if destroy_disks:
|
if destroy_disks:
|
||||||
self._delete_disk_files(instance_name)
|
self._delete_disk_files(instance_name)
|
||||||
if network_info:
|
self.unplug_vifs(instance, network_info)
|
||||||
for vif in network_info:
|
|
||||||
vif_driver = self._get_vif_driver(vif.get('type'))
|
|
||||||
vif_driver.unplug(instance, vif)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
LOG.exception(_LE('Failed to destroy instance: %s'),
|
LOG.exception(_LE('Failed to destroy instance: %s'),
|
||||||
@ -761,10 +758,7 @@ class VMOps(object):
|
|||||||
block_device_info)
|
block_device_info)
|
||||||
|
|
||||||
self._set_vm_state(instance, os_win_const.HYPERV_VM_STATE_ENABLED)
|
self._set_vm_state(instance, os_win_const.HYPERV_VM_STATE_ENABLED)
|
||||||
if network_info:
|
self.post_start_vifs(instance, network_info)
|
||||||
for vif in network_info:
|
|
||||||
vif_driver = self._get_vif_driver(vif.get('type'))
|
|
||||||
vif_driver.post_start(instance, vif)
|
|
||||||
|
|
||||||
def _set_vm_state(self, instance, req_state):
|
def _set_vm_state(self, instance, req_state):
|
||||||
instance_name = instance.name
|
instance_name = instance.name
|
||||||
@ -940,6 +934,18 @@ class VMOps(object):
|
|||||||
|
|
||||||
self.power_on(instance)
|
self.power_on(instance)
|
||||||
|
|
||||||
|
def unplug_vifs(self, instance, network_info):
|
||||||
|
if network_info:
|
||||||
|
for vif in network_info:
|
||||||
|
vif_driver = self._get_vif_driver(vif.get('type'))
|
||||||
|
vif_driver.unplug(instance, vif)
|
||||||
|
|
||||||
|
def post_start_vifs(self, instance, network_info):
|
||||||
|
if network_info:
|
||||||
|
for vif in network_info:
|
||||||
|
vif_driver = self._get_vif_driver(vif.get('type'))
|
||||||
|
vif_driver.post_start(instance, vif)
|
||||||
|
|
||||||
def _check_hotplug_is_available(self, instance):
|
def _check_hotplug_is_available(self, instance):
|
||||||
if (self._get_vm_state(instance.name) ==
|
if (self._get_vm_state(instance.name) ==
|
||||||
os_win_const.HYPERV_VM_STATE_DISABLED):
|
os_win_const.HYPERV_VM_STATE_DISABLED):
|
||||||
|
@ -319,6 +319,14 @@ class HyperVDriverTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock.sentinel.context, mock.sentinel.instance,
|
mock.sentinel.context, mock.sentinel.instance,
|
||||||
mock.sentinel.block_device_info)
|
mock.sentinel.block_device_info)
|
||||||
|
|
||||||
|
def test_post_live_migration_at_source(self):
|
||||||
|
self.driver.post_live_migration_at_source(
|
||||||
|
mock.sentinel.context, mock.sentinel.instance,
|
||||||
|
mock.sentinel.network_info)
|
||||||
|
|
||||||
|
self.driver._vmops.unplug_vifs.assert_called_once_with(
|
||||||
|
mock.sentinel.instance, mock.sentinel.network_info)
|
||||||
|
|
||||||
def test_post_live_migration_at_destination(self):
|
def test_post_live_migration_at_destination(self):
|
||||||
self.driver.post_live_migration_at_destination(
|
self.driver.post_live_migration_at_destination(
|
||||||
mock.sentinel.context, mock.sentinel.instance,
|
mock.sentinel.context, mock.sentinel.instance,
|
||||||
|
@ -129,3 +129,12 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock.sentinel.block_device_info)
|
mock.sentinel.block_device_info)
|
||||||
self._livemigrops._pathutils.get_instance_dir.assert_called_once_with(
|
self._livemigrops._pathutils.get_instance_dir.assert_called_once_with(
|
||||||
mock.sentinel.instance.name, create_dir=False, remove_dir=True)
|
mock.sentinel.instance.name, create_dir=False, remove_dir=True)
|
||||||
|
|
||||||
|
@mock.patch.object(livemigrationops.vmops.VMOps, 'post_start_vifs')
|
||||||
|
def test_post_live_migration_at_destination(self, mock_post_start_vifs):
|
||||||
|
self._livemigrops.post_live_migration_at_destination(
|
||||||
|
mock.sentinel.context, mock.sentinel.instance,
|
||||||
|
mock.sentinel.network_info, mock.sentinel.block_migration)
|
||||||
|
|
||||||
|
mock_post_start_vifs.assert_called_once_with(
|
||||||
|
mock.sentinel.instance, mock.sentinel.network_info)
|
||||||
|
@ -900,13 +900,15 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
@mock.patch('hyperv.nova.volumeops.VolumeOps.disconnect_volumes')
|
@mock.patch('hyperv.nova.volumeops.VolumeOps.disconnect_volumes')
|
||||||
@mock.patch('hyperv.nova.vmops.VMOps._delete_disk_files')
|
@mock.patch('hyperv.nova.vmops.VMOps._delete_disk_files')
|
||||||
@mock.patch('hyperv.nova.vmops.VMOps.power_off')
|
@mock.patch('hyperv.nova.vmops.VMOps.power_off')
|
||||||
def test_destroy(self, mock_power_off, mock_delete_disk_files,
|
@mock.patch('hyperv.nova.vmops.VMOps.unplug_vifs')
|
||||||
mock_disconnect_volumes):
|
def test_destroy(self, mock_unplug_vifs, mock_power_off,
|
||||||
|
mock_delete_disk_files, mock_disconnect_volumes):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
self._vmops._vmutils.vm_exists.return_value = True
|
self._vmops._vmutils.vm_exists.return_value = True
|
||||||
|
|
||||||
self._vmops.destroy(instance=mock_instance,
|
self._vmops.destroy(instance=mock_instance,
|
||||||
block_device_info=mock.sentinel.FAKE_BD_INFO)
|
block_device_info=mock.sentinel.FAKE_BD_INFO,
|
||||||
|
network_info=mock.sentinel.fake_network_info)
|
||||||
|
|
||||||
self._vmops._vmutils.vm_exists.assert_called_with(
|
self._vmops._vmutils.vm_exists.assert_called_with(
|
||||||
mock_instance.name)
|
mock_instance.name)
|
||||||
@ -917,6 +919,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock.sentinel.FAKE_BD_INFO)
|
mock.sentinel.FAKE_BD_INFO)
|
||||||
mock_delete_disk_files.assert_called_once_with(
|
mock_delete_disk_files.assert_called_once_with(
|
||||||
mock_instance.name)
|
mock_instance.name)
|
||||||
|
mock_unplug_vifs.assert_called_once_with(
|
||||||
|
mock_instance, mock.sentinel.fake_network_info)
|
||||||
|
|
||||||
def test_destroy_inexistent_instance(self):
|
def test_destroy_inexistent_instance(self):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
@ -1117,21 +1121,14 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
mock_set_vm_state.assert_called_once_with(
|
mock_set_vm_state.assert_called_once_with(
|
||||||
mock_instance, os_win_const.HYPERV_VM_STATE_ENABLED)
|
mock_instance, os_win_const.HYPERV_VM_STATE_ENABLED)
|
||||||
|
|
||||||
@mock.patch.object(vmops.VMOps, '_get_vif_driver')
|
@mock.patch.object(vmops.VMOps, 'post_start_vifs')
|
||||||
def test_power_on_with_network_info(self, mock_get_vif_driver):
|
def test_power_on_with_network_info(self, mock_post_start_vifs):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
fake_vif1 = {'id': mock.sentinel.ID1,
|
|
||||||
'type': mock.sentinel.vif_type1}
|
|
||||||
fake_vif2 = {'id': mock.sentinel.ID2,
|
|
||||||
'type': mock.sentinel.vif_type2}
|
|
||||||
mock_network_info = [fake_vif1, fake_vif2]
|
|
||||||
fake_vif_driver = mock.MagicMock()
|
|
||||||
mock_get_vif_driver.return_value = fake_vif_driver
|
|
||||||
calls = [mock.call(mock_instance, fake_vif1),
|
|
||||||
mock.call(mock_instance, fake_vif2)]
|
|
||||||
|
|
||||||
self._vmops.power_on(mock_instance, network_info=mock_network_info)
|
self._vmops.power_on(mock_instance,
|
||||||
fake_vif_driver.post_start.assert_has_calls(calls)
|
network_info=mock.sentinel.fake_network_info)
|
||||||
|
mock_post_start_vifs.assert_called_once_with(
|
||||||
|
mock_instance, mock.sentinel.fake_network_info)
|
||||||
|
|
||||||
def _test_set_vm_state(self, state):
|
def _test_set_vm_state(self, state):
|
||||||
mock_instance = fake_instance.fake_instance_obj(self.context)
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
@ -1481,6 +1478,40 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
|
|||||||
def test_configure_remotefx(self):
|
def test_configure_remotefx(self):
|
||||||
self._test_configure_remotefx(fail=False)
|
self._test_configure_remotefx(fail=False)
|
||||||
|
|
||||||
|
@mock.patch.object(vmops.VMOps, '_get_vif_driver')
|
||||||
|
def test_unplug_vifs(self, mock_get_vif_driver):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
fake_vif1 = {'id': mock.sentinel.ID1,
|
||||||
|
'type': mock.sentinel.vif_type1}
|
||||||
|
fake_vif2 = {'id': mock.sentinel.ID2,
|
||||||
|
'type': mock.sentinel.vif_type2}
|
||||||
|
mock_network_info = [fake_vif1, fake_vif2]
|
||||||
|
fake_vif_driver = mock.MagicMock()
|
||||||
|
mock_get_vif_driver.return_value = fake_vif_driver
|
||||||
|
calls = [mock.call(mock_instance, fake_vif1),
|
||||||
|
mock.call(mock_instance, fake_vif2)]
|
||||||
|
|
||||||
|
self._vmops.unplug_vifs(mock_instance,
|
||||||
|
network_info=mock_network_info)
|
||||||
|
fake_vif_driver.unplug.assert_has_calls(calls)
|
||||||
|
|
||||||
|
@mock.patch.object(vmops.VMOps, '_get_vif_driver')
|
||||||
|
def test_post_start_vifs(self, mock_get_vif_driver):
|
||||||
|
mock_instance = fake_instance.fake_instance_obj(self.context)
|
||||||
|
fake_vif1 = {'id': mock.sentinel.ID1,
|
||||||
|
'type': mock.sentinel.vif_type1}
|
||||||
|
fake_vif2 = {'id': mock.sentinel.ID2,
|
||||||
|
'type': mock.sentinel.vif_type2}
|
||||||
|
mock_network_info = [fake_vif1, fake_vif2]
|
||||||
|
fake_vif_driver = mock.MagicMock()
|
||||||
|
mock_get_vif_driver.return_value = fake_vif_driver
|
||||||
|
calls = [mock.call(mock_instance, fake_vif1),
|
||||||
|
mock.call(mock_instance, fake_vif2)]
|
||||||
|
|
||||||
|
self._vmops.post_start_vifs(mock_instance,
|
||||||
|
network_info=mock_network_info)
|
||||||
|
fake_vif_driver.post_start.assert_has_calls(calls)
|
||||||
|
|
||||||
@mock.patch.object(vmops.VMOps, '_get_vm_state')
|
@mock.patch.object(vmops.VMOps, '_get_vm_state')
|
||||||
def _test_check_hotplug_is_available(self, mock_get_vm_state, vm_gen,
|
def _test_check_hotplug_is_available(self, mock_get_vm_state, vm_gen,
|
||||||
windows_version, vm_state):
|
windows_version, vm_state):
|
||||||
|
Loading…
Reference in New Issue
Block a user