libvirt: Collocate encryptor and volume driver calls

This change introduces new utility methods for attaching and detaching
frontend volume encryptors. These methods centralise the optional
fetching of encryption metadata associated with a volume, fetching of the
required encryptor and calls to detach or attach the encryptor.

These new utility methods are called either after initially connecting
to or before disconnecting from a volume. This ensures encryptors are
correctly connected when swapping volumes for example, where previously
no attempt was made to attach an encryptor to the target volume.

The request context is provided to swap_volume and various other config
generation related methods to allow for the lookup of the relevant
encryption metadata if it is not provided.

Closes-bug: #1739593
Change-Id: Ica323b87fa85a454fca9d46ada3677f18fe50022
This commit is contained in:
Lee Yarwood 2017-04-24 19:23:44 +01:00
parent 0ff7af38ff
commit cd3eb60c2c
7 changed files with 327 additions and 121 deletions

View File

@ -5438,8 +5438,8 @@ class ComputeManager(manager.Manager):
"old: %(old_cinfo)s", "old: %(old_cinfo)s",
{'new_cinfo': new_cinfo, 'old_cinfo': old_cinfo}, {'new_cinfo': new_cinfo, 'old_cinfo': old_cinfo},
instance=instance) instance=instance)
self.driver.swap_volume(old_cinfo, new_cinfo, instance, mountpoint, self.driver.swap_volume(context, old_cinfo, new_cinfo, instance,
resize_to) mountpoint, resize_to)
if new_attachment_id: if new_attachment_id:
self.volume_api.attachment_complete(context, new_attachment_id) self.volume_api.attachment_complete(context, new_attachment_id)
LOG.debug("swap_volume: Driver volume swap returned, new " LOG.debug("swap_volume: Driver volume swap returned, new "

View File

@ -1966,8 +1966,9 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
self.assertTrue(uuidutils.is_uuid_like(volume)) self.assertTrue(uuidutils.is_uuid_like(volume))
return {} return {}
def _assert_swap_volume(self, old_connection_info, new_connection_info, def _assert_swap_volume(self, context, old_connection_info,
instance, mountpoint, resize_to): new_connection_info, instance, mountpoint,
resize_to):
self.assertEqual(2, resize_to) self.assertEqual(2, resize_to)
@mock.patch.object(cinder.API, 'initialize_connection') @mock.patch.object(cinder.API, 'initialize_connection')
@ -2306,7 +2307,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
instance, uuids.new_attachment_id) instance, uuids.new_attachment_id)
# Assert the expected calls. # Assert the expected calls.
# The new connection_info has the new_volume_id as the serial. # The new connection_info has the new_volume_id as the serial.
new_cinfo = mock_driver_swap.call_args[0][1] new_cinfo = mock_driver_swap.call_args[0][2]
self.assertIn('serial', new_cinfo) self.assertIn('serial', new_cinfo)
self.assertEqual(uuids.new_volume_id, new_cinfo['serial']) self.assertEqual(uuids.new_volume_id, new_cinfo['serial'])
get_bdm.assert_called_once_with( get_bdm.assert_called_once_with(

View File

@ -6706,7 +6706,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
test.MatchType(objects.ImageMeta), test.MatchType(objects.ImageMeta),
bdm) bdm)
mock_connect_volume.assert_called_with( mock_connect_volume.assert_called_with(
connection_info, instance) self.context, connection_info, instance, encryption=None)
mock_get_volume_config.assert_called_with( mock_get_volume_config.assert_called_with(
connection_info, disk_info) connection_info, disk_info)
mock_dom.attachDeviceFlags.assert_called_with( mock_dom.attachDeviceFlags.assert_called_with(
@ -6761,7 +6761,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
</disk> </disk>
""", flags=flags) """, flags=flags)
mock_disconnect_volume.assert_called_with( mock_disconnect_volume.assert_called_with(
connection_info, instance) None, connection_info, instance, encryption=None)
@mock.patch('nova.virt.libvirt.host.Host._get_domain') @mock.patch('nova.virt.libvirt.host.Host._get_domain')
def test_detach_volume_disk_not_found(self, mock_get_domain): def test_detach_volume_disk_not_found(self, mock_get_domain):
@ -6785,12 +6785,15 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_get_domain.assert_called_once_with(instance) mock_get_domain.assert_called_once_with(instance)
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume') @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_driver')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor') @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
@mock.patch('nova.virt.libvirt.host.Host.get_guest') @mock.patch('nova.virt.libvirt.host.Host.get_guest')
def test_detach_volume_order_with_encryptors(self, mock_get_guest, def test_detach_volume_order_with_encryptors(self, mock_get_guest,
mock_get_encryptor, mock_disconnect_volume): mock_get_encryptor, mock_get_volume_driver):
mock_volume_driver = mock.MagicMock(
spec=volume_drivers.LibvirtBaseVolumeDriver)
mock_get_volume_driver.return_value = mock_volume_driver
mock_guest = mock.MagicMock(spec=libvirt_guest.Guest) mock_guest = mock.MagicMock(spec=libvirt_guest.Guest)
mock_guest.get_power_state.return_value = power_state.RUNNING mock_guest.get_power_state.return_value = power_state.RUNNING
mock_get_guest.return_value = mock_guest mock_get_guest.return_value = mock_guest
@ -6799,7 +6802,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_get_encryptor.return_value = mock_encryptor mock_get_encryptor.return_value = mock_encryptor
mock_order = mock.Mock() mock_order = mock.Mock()
mock_order.attach_mock(mock_disconnect_volume, 'disconnect_volume') mock_order.attach_mock(mock_volume_driver.disconnect_volume,
'disconnect_volume')
mock_order.attach_mock(mock_guest.detach_device_with_retry(), mock_order.attach_mock(mock_guest.detach_device_with_retry(),
'detach_volume') 'detach_volume')
mock_order.attach_mock(mock_encryptor.detach_volume, mock_order.attach_mock(mock_encryptor.detach_volume,
@ -6917,6 +6921,196 @@ class LibvirtConnTestCase(test.NoDBTestCase,
drvr.extend_volume, drvr.extend_volume,
connection_info, instance) connection_info, instance)
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_use_encryptor_connection_info_incomplete(self,
mock_get_encryptor, mock_get_metadata):
"""Assert no attach attempt is made given incomplete connection_info.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
connection_info = {'data': {}}
drvr._attach_encryptor(self.context, connection_info, None)
mock_get_metadata.assert_not_called()
mock_get_encryptor.assert_not_called()
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_attach_encryptor_unencrypted_volume_meta_missing(self,
mock_get_encryptor, mock_get_metadata):
"""Assert that if not provided encryption metadata is fetched even
if the volume is ultimately unencrypted and no attempt to attach
is made.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
encryption = {}
connection_info = {'data': {'volume_id': uuids.volume_id}}
mock_get_metadata.return_value = encryption
drvr._attach_encryptor(self.context, connection_info, None)
mock_get_metadata.assert_called_once_with(self.context,
drvr._volume_api, uuids.volume_id, connection_info)
mock_get_encryptor.assert_not_called()
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_attach_encryptor_unencrypted_volume_meta_provided(self,
mock_get_encryptor, mock_get_metadata):
"""Assert that if an empty encryption metadata dict is provided that
there is no additional attempt to lookup the metadata or attach the
encryptor.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
encryption = {}
connection_info = {'data': {'volume_id': uuids.volume_id}}
drvr._attach_encryptor(self.context, connection_info,
encryption=encryption)
mock_get_metadata.assert_not_called()
mock_get_encryptor.assert_not_called()
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_attach_encryptor_encrypted_volume_meta_missing(self,
mock_get_encryptor, mock_get_metadata):
"""Assert that if missing the encryption metadata of an encrypted
volume is fetched and then used to attach the encryptor for the volume.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
mock_encryptor = mock.MagicMock()
mock_get_encryptor.return_value = mock_encryptor
encryption = {'provider': 'luks', 'control_location': 'front-end'}
mock_get_metadata.return_value = encryption
connection_info = {'data': {'volume_id': uuids.volume_id}}
drvr._attach_encryptor(self.context, connection_info, None)
mock_get_metadata.assert_called_once_with(self.context,
drvr._volume_api, uuids.volume_id, connection_info)
mock_get_encryptor.assert_called_once_with(connection_info,
encryption)
mock_encryptor.attach_volume.assert_called_once_with(self.context,
**encryption)
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_attach_encryptor_encrypted_volume_meta_provided(self,
mock_get_encryptor, mock_get_metadata):
"""Assert that when provided there are no further attempts to fetch the
encryption metadata for the volume and that the provided metadata is
then used to attach the volume.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
mock_encryptor = mock.MagicMock()
mock_get_encryptor.return_value = mock_encryptor
encryption = {'provider': 'luks', 'control_location': 'front-end'}
connection_info = {'data': {'volume_id': uuids.volume_id}}
drvr._attach_encryptor(self.context, connection_info,
encryption=encryption)
mock_get_metadata.assert_not_called()
mock_get_encryptor.assert_called_once_with(connection_info,
encryption)
mock_encryptor.attach_volume.assert_called_once_with(self.context,
**encryption)
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_detach_encryptor_connection_info_incomplete(self,
mock_get_encryptor, mock_get_metadata):
"""Assert no detach attempt is made given incomplete connection_info.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
connection_info = {'data': {}}
drvr._detach_encryptor(self.context, connection_info, None)
mock_get_metadata.assert_not_called()
mock_get_encryptor.assert_not_called()
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_detach_encryptor_unencrypted_volume_meta_missing(self,
mock_get_encryptor, mock_get_metadata):
"""Assert that if not provided encryption metadata is fetched even
if the volume is ultimately unencrypted and no attempt to detach
is made.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
encryption = {}
connection_info = {'data': {'volume_id': uuids.volume_id}}
mock_get_metadata.return_value = encryption
drvr._detach_encryptor(self.context, connection_info, None)
mock_get_metadata.assert_called_once_with(self.context,
drvr._volume_api, uuids.volume_id, connection_info)
mock_get_encryptor.assert_not_called()
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_detach_encryptor_unencrypted_volume_meta_provided(self,
mock_get_encryptor, mock_get_metadata):
"""Assert that if an empty encryption metadata dict is provided that
there is no additional attempt to lookup the metadata or detach the
encryptor.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
encryption = {}
connection_info = {'data': {'volume_id': uuids.volume_id}}
drvr._detach_encryptor(self.context, connection_info, encryption)
mock_get_metadata.assert_not_called()
mock_get_encryptor.assert_not_called()
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_detach_encryptor_encrypted_volume_meta_missing(self,
mock_get_encryptor, mock_get_metadata):
"""Assert that if missing the encryption metadata of an encrypted
volume is fetched and then used to detach the encryptor for the volume.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
mock_encryptor = mock.MagicMock()
mock_get_encryptor.return_value = mock_encryptor
encryption = {'provider': 'luks', 'control_location': 'front-end'}
mock_get_metadata.return_value = encryption
connection_info = {'data': {'volume_id': uuids.volume_id}}
drvr._detach_encryptor(self.context, connection_info, None)
mock_get_metadata.assert_called_once_with(self.context,
drvr._volume_api, uuids.volume_id, connection_info)
mock_get_encryptor.assert_called_once_with(connection_info,
encryption)
mock_encryptor.detach_volume.assert_called_once_with(**encryption)
@mock.patch('os_brick.encryptors.get_encryption_metadata')
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
def test_detach_encryptor_encrypted_volume_meta_provided(self,
mock_get_encryptor, mock_get_metadata):
"""Assert that when provided there are no further attempts to fetch the
encryption metadata for the volume and that the provided metadata is
then used to detach the volume.
"""
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
mock_encryptor = mock.MagicMock()
mock_get_encryptor.return_value = mock_encryptor
encryption = {'provider': 'luks', 'control_location': 'front-end'}
connection_info = {'data': {'volume_id': uuids.volume_id}}
drvr._detach_encryptor(self.context, connection_info, encryption)
mock_get_metadata.assert_not_called()
mock_get_encryptor.assert_called_once_with(connection_info,
encryption)
mock_encryptor.detach_volume.assert_called_once_with(**encryption)
def test_multi_nic(self): def test_multi_nic(self):
network_info = _fake_network_info(self, 2) network_info = _fake_network_info(self, 2)
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True) drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
@ -10146,7 +10340,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
).AndReturn(vol['block_device_mapping']) ).AndReturn(vol['block_device_mapping'])
self.mox.StubOutWithMock(drvr, "_connect_volume") self.mox.StubOutWithMock(drvr, "_connect_volume")
for v in vol['block_device_mapping']: for v in vol['block_device_mapping']:
drvr._connect_volume(v['connection_info'], instance) drvr._connect_volume(c, v['connection_info'], instance)
self.mox.StubOutWithMock(drvr, 'plug_vifs') self.mox.StubOutWithMock(drvr, 'plug_vifs')
drvr.plug_vifs(mox.IsA(instance), nw_info) drvr.plug_vifs(mox.IsA(instance), nw_info)
@ -10278,7 +10472,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
# Creating mocks # Creating mocks
self.mox.StubOutWithMock(drvr, "_connect_volume") self.mox.StubOutWithMock(drvr, "_connect_volume")
for v in vol['block_device_mapping']: for v in vol['block_device_mapping']:
drvr._connect_volume(v['connection_info'], inst_ref) drvr._connect_volume(c, v['connection_info'], inst_ref)
self.mox.StubOutWithMock(drvr, 'plug_vifs') self.mox.StubOutWithMock(drvr, 'plug_vifs')
drvr.plug_vifs(mox.IsA(inst_ref), nw_info) drvr.plug_vifs(mox.IsA(inst_ref), nw_info)
self.mox.ReplayAll() self.mox.ReplayAll()
@ -10627,8 +10821,9 @@ class LibvirtConnTestCase(test.NoDBTestCase,
get_volume_connector.assert_has_calls([ get_volume_connector.assert_has_calls([
mock.call(inst_ref)]) mock.call(inst_ref)])
_disconnect_volume.assert_has_calls([ _disconnect_volume.assert_has_calls([
mock.call({'data': {'multipath_id': 'dummy1'}}, inst_ref), mock.call(cntx, {'data': {'multipath_id': 'dummy1'}},
mock.call({'data': {}}, inst_ref)]) inst_ref),
mock.call(cntx, {'data': {}}, inst_ref)])
def test_post_live_migration_cinder_v3(self): def test_post_live_migration_cinder_v3(self):
cntx = context.get_admin_context() cntx = context.get_admin_context()
@ -10666,7 +10861,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock_attachment_get.assert_called_once_with(cntx, mock_attachment_get.assert_called_once_with(cntx,
old_attachment_id) old_attachment_id)
mock_disconnect.assert_called_once_with(connection_info, instance) mock_disconnect.assert_called_once_with(cntx, connection_info,
instance)
_test() _test()
def test_get_instance_disk_info_excludes_volumes(self): def test_get_instance_disk_info_excludes_volumes(self):
@ -11024,7 +11220,7 @@ class LibvirtConnTestCase(test.NoDBTestCase,
'delete_on_termination': False 'delete_on_termination': False
} }
def _connect_volume_side_effect(connection_info, instance): def _connect_volume_side_effect(ctxt, connection_info, instance):
bdm['connection_info']['data']['device_path'] = '/dev/path/to/dev' bdm['connection_info']['data']['device_path'] = '/dev/path/to/dev'
def _get(key, opt=None): def _get(key, opt=None):
@ -14563,8 +14759,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
connection_info = {'driver_volume_type': 'fake'} connection_info = {'driver_volume_type': 'fake'}
drvr.detach_volume(connection_info, instance, '/dev/sda') drvr.detach_volume(connection_info, instance, '/dev/sda')
_get_domain.assert_called_once_with(instance) _get_domain.assert_called_once_with(instance)
_disconnect_volume.assert_called_once_with(connection_info, _disconnect_volume.assert_called_once_with(None, connection_info,
instance) instance, encryption=None)
def _test_attach_detach_interface_get_config(self, method_name): def _test_attach_detach_interface_get_config(self, method_name):
"""Tests that the get_config() method is properly called in """Tests that the get_config() method is properly called in
@ -15047,25 +15243,20 @@ class LibvirtConnTestCase(test.NoDBTestCase,
network_model.VIF(id='2', active=True)] network_model.VIF(id='2', active=True)]
with test.nested( with test.nested(
mock.patch.object(drvr, '_get_volume_encryptor'),
mock.patch.object(drvr, 'plug_vifs'), mock.patch.object(drvr, 'plug_vifs'),
mock.patch.object(drvr.firewall_driver, 'setup_basic_filtering'), mock.patch.object(drvr.firewall_driver, 'setup_basic_filtering'),
mock.patch.object(drvr.firewall_driver, mock.patch.object(drvr.firewall_driver,
'prepare_instance_filter'), 'prepare_instance_filter'),
mock.patch.object(drvr, '_create_domain'), mock.patch.object(drvr, '_create_domain'),
mock.patch.object(drvr.firewall_driver, 'apply_instance_filter'), mock.patch.object(drvr.firewall_driver, 'apply_instance_filter'),
) as (get_volume_encryptor, plug_vifs, setup_basic_filtering, ) as (plug_vifs, setup_basic_filtering, prepare_instance_filter,
prepare_instance_filter, create_domain, apply_instance_filter): create_domain, apply_instance_filter):
create_domain.return_value = libvirt_guest.Guest(mock_dom) create_domain.return_value = libvirt_guest.Guest(mock_dom)
guest = drvr._create_domain_and_network( guest = drvr._create_domain_and_network(
self.context, fake_xml, instance, network_info, self.context, fake_xml, instance, network_info,
block_device_info=block_device_info) block_device_info=block_device_info)
get_encryption_metadata.assert_called_once_with(self.context,
drvr._volume_api, fake_volume_id, connection_info)
get_volume_encryptor.assert_called_once_with(connection_info,
mock_encryption_meta)
plug_vifs.assert_called_once_with(instance, network_info) plug_vifs.assert_called_once_with(instance, network_info)
setup_basic_filtering.assert_called_once_with(instance, setup_basic_filtering.assert_called_once_with(instance,
network_info) network_info)
@ -15109,13 +15300,14 @@ class LibvirtConnTestCase(test.NoDBTestCase,
mock.patch.object(drvr, '_get_volume_config', mock.patch.object(drvr, '_get_volume_config',
return_value=mock_conf) return_value=mock_conf)
) as (volume_save, connect_volume, get_volume_config): ) as (volume_save, connect_volume, get_volume_config):
devices = drvr._get_guest_storage_config(instance, image_meta, devices = drvr._get_guest_storage_config(self.context, instance,
disk_info, False, bdi, flavor, "hvm") image_meta, disk_info, False, bdi, flavor, "hvm")
self.assertEqual(3, len(devices)) self.assertEqual(3, len(devices))
self.assertEqual('/dev/vdb', instance.default_ephemeral_device) self.assertEqual('/dev/vdb', instance.default_ephemeral_device)
self.assertIsNone(instance.default_swap_device) self.assertIsNone(instance.default_swap_device)
connect_volume.assert_called_with(bdm['connection_info'], instance) connect_volume.assert_called_with(self.context,
bdm['connection_info'], instance)
get_volume_config.assert_called_with(bdm['connection_info'], get_volume_config.assert_called_with(bdm['connection_info'],
{'bus': 'virtio', 'type': 'disk', 'dev': 'vdc'}) {'bus': 'virtio', 'type': 'disk', 'dev': 'vdc'})
volume_save.assert_called_once_with() volume_save.assert_called_once_with()
@ -15325,14 +15517,16 @@ class LibvirtConnTestCase(test.NoDBTestCase,
conf = mock.MagicMock(source_path='/fake-new-volume') conf = mock.MagicMock(source_path='/fake-new-volume')
get_volume_config.return_value = conf get_volume_config.return_value = conf
conn.swap_volume(old_connection_info, new_connection_info, instance, conn.swap_volume(self.context, old_connection_info,
'/dev/vdb', 1) new_connection_info, instance, '/dev/vdb', 1)
get_guest.assert_called_once_with(instance) get_guest.assert_called_once_with(instance)
connect_volume.assert_called_once_with(new_connection_info, instance) connect_volume.assert_called_once_with(self.context,
new_connection_info, instance)
swap_volume.assert_called_once_with(guest, 'vdb', conf, 1) swap_volume.assert_called_once_with(guest, 'vdb', conf, 1)
disconnect_volume.assert_called_once_with(old_connection_info, disconnect_volume.assert_called_once_with(self.context,
old_connection_info,
instance) instance)
def test_swap_volume_driver_source_is_volume(self): def test_swap_volume_driver_source_is_volume(self):
@ -15366,12 +15560,12 @@ class LibvirtConnTestCase(test.NoDBTestCase,
rebase.side_effect = exc rebase.side_effect = exc
self.assertRaises(exception.VolumeRebaseFailed, conn.swap_volume, self.assertRaises(exception.VolumeRebaseFailed, conn.swap_volume,
mock.sentinel.old_connection_info, self.context, mock.sentinel.old_connection_info,
mock.sentinel.new_connection_info, mock.sentinel.new_connection_info,
instance, '/dev/vdb', 0) instance, '/dev/vdb', 0)
connect_volume.assert_called_once_with( connect_volume.assert_called_once_with(self.context,
mock.sentinel.new_connection_info, instance) mock.sentinel.new_connection_info, instance)
disconnect_volume.assert_called_once_with( disconnect_volume.assert_called_once_with(self.context,
mock.sentinel.new_connection_info, instance) mock.sentinel.new_connection_info, instance)
@mock.patch('nova.virt.libvirt.guest.BlockDevice.is_job_complete') @mock.patch('nova.virt.libvirt.guest.BlockDevice.is_job_complete')
@ -15398,12 +15592,12 @@ class LibvirtConnTestCase(test.NoDBTestCase,
abort_job.side_effect = [None, exc] abort_job.side_effect = [None, exc]
self.assertRaises(exception.VolumeRebaseFailed, conn.swap_volume, self.assertRaises(exception.VolumeRebaseFailed, conn.swap_volume,
mock.sentinel.old_connection_info, self.context, mock.sentinel.old_connection_info,
mock.sentinel.new_connection_info, mock.sentinel.new_connection_info,
instance, '/dev/vdb', 0) instance, '/dev/vdb', 0)
connect_volume.assert_called_once_with( connect_volume.assert_called_once_with(self.context,
mock.sentinel.new_connection_info, instance) mock.sentinel.new_connection_info, instance)
disconnect_volume.assert_called_once_with( disconnect_volume.assert_called_once_with(self.context,
mock.sentinel.new_connection_info, instance) mock.sentinel.new_connection_info, instance)
@mock.patch('nova.virt.libvirt.guest.BlockDevice.is_job_complete') @mock.patch('nova.virt.libvirt.guest.BlockDevice.is_job_complete')
@ -16171,7 +16365,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
context.get_admin_context(), ins_ref, '10.0.0.2', context.get_admin_context(), ins_ref, '10.0.0.2',
flavor_obj, None) flavor_obj, None)
def _test_migrate_disk_and_power_off(self, flavor_obj, def _test_migrate_disk_and_power_off(self, ctxt, flavor_obj,
block_device_info=None, block_device_info=None,
params_for_instance=None): params_for_instance=None):
"""Test for nova.virt.libvirt.libvirt_driver.LivirtConnection """Test for nova.virt.libvirt.libvirt_driver.LivirtConnection
@ -16210,21 +16404,20 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
# dest is different host case # dest is different host case
out = self.drvr.migrate_disk_and_power_off( out = self.drvr.migrate_disk_and_power_off(
context.get_admin_context(), instance, '10.0.0.2', ctxt, instance, '10.0.0.2', flavor_obj, None,
flavor_obj, None, block_device_info=block_device_info) block_device_info=block_device_info)
self.assertEqual(out, disk_info_text) self.assertEqual(out, disk_info_text)
# dest is same host case # dest is same host case
out = self.drvr.migrate_disk_and_power_off( out = self.drvr.migrate_disk_and_power_off(
context.get_admin_context(), instance, '10.0.0.1', ctxt, instance, '10.0.0.1', flavor_obj, None,
flavor_obj, None, block_device_info=block_device_info) block_device_info=block_device_info)
self.assertEqual(out, disk_info_text) self.assertEqual(out, disk_info_text)
def test_migrate_disk_and_power_off(self): def test_migrate_disk_and_power_off(self):
flavor = {'root_gb': 10, 'ephemeral_gb': 20} flavor = {'root_gb': 10, 'ephemeral_gb': 20}
flavor_obj = objects.Flavor(**flavor) flavor_obj = objects.Flavor(**flavor)
self._test_migrate_disk_and_power_off(self.context, flavor_obj)
self._test_migrate_disk_and_power_off(flavor_obj)
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume') @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
def test_migrate_disk_and_power_off_boot_from_volume(self, def test_migrate_disk_and_power_off_boot_from_volume(self,
@ -16240,14 +16433,14 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
flavor = {'root_gb': 1, 'ephemeral_gb': 0} flavor = {'root_gb': 1, 'ephemeral_gb': 0}
flavor_obj = objects.Flavor(**flavor) flavor_obj = objects.Flavor(**flavor)
# Note(Mike_D): The size of instance's ephemeral_gb is 0 gb. # Note(Mike_D): The size of instance's ephemeral_gb is 0 gb.
self._test_migrate_disk_and_power_off( self._test_migrate_disk_and_power_off(self.context,
flavor_obj, block_device_info=info, flavor_obj, block_device_info=info,
params_for_instance={'image_ref': None, params_for_instance={'image_ref': None,
'root_gb': 10, 'root_gb': 10,
'ephemeral_gb': 0, 'ephemeral_gb': 0,
'flavor': {'root_gb': 10, 'flavor': {'root_gb': 10,
'ephemeral_gb': 0}}) 'ephemeral_gb': 0}})
disconnect_volume.assert_called_with( disconnect_volume.assert_called_with(self.context,
mock.sentinel.conn_info_vda, mock.ANY) mock.sentinel.conn_info_vda, mock.ANY)
@mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume') @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._disconnect_volume')
@ -16265,7 +16458,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
'connection_info': mock.sentinel.conn_info_vda}]} 'connection_info': mock.sentinel.conn_info_vda}]}
flavor = {'root_gb': 1, 'ephemeral_gb': 0} flavor = {'root_gb': 1, 'ephemeral_gb': 0}
flavor_obj = objects.Flavor(**flavor) flavor_obj = objects.Flavor(**flavor)
self._test_migrate_disk_and_power_off( self._test_migrate_disk_and_power_off(self.context,
flavor_obj, block_device_info=info, flavor_obj, block_device_info=info,
params_for_instance={ params_for_instance={
'image_ref': uuids.fake_volume_backed_image_ref, 'image_ref': uuids.fake_volume_backed_image_ref,
@ -16273,7 +16466,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
'ephemeral_gb': 0, 'ephemeral_gb': 0,
'flavor': {'root_gb': 10, 'flavor': {'root_gb': 10,
'ephemeral_gb': 0}}) 'ephemeral_gb': 0}})
disconnect_volume.assert_called_with( disconnect_volume.assert_called_with(self.context,
mock.sentinel.conn_info_vda, mock.ANY) mock.sentinel.conn_info_vda, mock.ANY)
@mock.patch('nova.utils.execute') @mock.patch('nova.utils.execute')
@ -16540,7 +16733,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
# Old flavor, eph is 20, real disk is 3, target is 4 # Old flavor, eph is 20, real disk is 3, target is 4
flavor = {'root_gb': 10, 'ephemeral_gb': 4} flavor = {'root_gb': 10, 'ephemeral_gb': 4}
flavor_obj = objects.Flavor(**flavor) flavor_obj = objects.Flavor(**flavor)
self._test_migrate_disk_and_power_off(flavor_obj) self._test_migrate_disk_and_power_off(self.context, flavor_obj)
@mock.patch('nova.utils.execute') @mock.patch('nova.utils.execute')
@mock.patch('nova.virt.libvirt.utils.copy_image') @mock.patch('nova.virt.libvirt.utils.copy_image')

View File

@ -494,7 +494,7 @@ class _VirtDriverTestCase(_FakeDriverBackendTestCase):
instance_ref, instance_ref,
'/dev/sda')) '/dev/sda'))
self.assertIsNone( self.assertIsNone(
self.connection.swap_volume({'driver_volume_type': 'fake', self.connection.swap_volume(None, {'driver_volume_type': 'fake',
'data': {}}, 'data': {}},
{'driver_volume_type': 'fake', {'driver_volume_type': 'fake',
'data': {}}, 'data': {}},

View File

@ -471,10 +471,11 @@ class ComputeDriver(object):
"""Detach the disk attached to the instance.""" """Detach the disk attached to the instance."""
raise NotImplementedError() raise NotImplementedError()
def swap_volume(self, old_connection_info, new_connection_info, def swap_volume(self, context, old_connection_info, new_connection_info,
instance, mountpoint, resize_to): instance, mountpoint, resize_to):
"""Replace the volume attached to the given `instance`. """Replace the volume attached to the given `instance`.
:param context: The request context.
:param dict old_connection_info: :param dict old_connection_info:
The volume for this connection gets detached from the given The volume for this connection gets detached from the given
`instance`. `instance`.

View File

@ -317,7 +317,7 @@ class FakeDriver(driver.ComputeDriver):
except KeyError: except KeyError:
pass pass
def swap_volume(self, old_connection_info, new_connection_info, def swap_volume(self, context, old_connection_info, new_connection_info,
instance, mountpoint, resize_to): instance, mountpoint, resize_to):
"""Replace the disk attached to the instance.""" """Replace the disk attached to the instance."""
instance_name = instance.name instance_name = instance.name

View File

@ -1008,23 +1008,8 @@ class LibvirtDriver(driver.ComputeDriver):
disk_dev = vol['mount_device'] disk_dev = vol['mount_device']
if disk_dev is not None: if disk_dev is not None:
disk_dev = disk_dev.rpartition("/")[2] disk_dev = disk_dev.rpartition("/")[2]
if ('data' in connection_info and
'volume_id' in connection_info['data']):
volume_id = connection_info['data']['volume_id']
encryption = encryptors.get_encryption_metadata(
context, self._volume_api, volume_id, connection_info)
if encryption:
# The volume must be detached from the VM before
# disconnecting it from its encryptor. Otherwise, the
# encryptor may report that the volume is still in use.
encryptor = self._get_volume_encryptor(connection_info,
encryption)
encryptor.detach_volume(**encryption)
try: try:
self._disconnect_volume(connection_info, instance) self._disconnect_volume(context, connection_info, instance)
except Exception as exc: except Exception as exc:
with excutils.save_and_reraise_exception() as ctxt: with excutils.save_and_reraise_exception() as ctxt:
if destroy_disks: if destroy_disks:
@ -1207,11 +1192,15 @@ class LibvirtDriver(driver.ComputeDriver):
raise exception.VolumeDriverNotFound(driver_type=driver_type) raise exception.VolumeDriverNotFound(driver_type=driver_type)
return self.volume_drivers[driver_type] return self.volume_drivers[driver_type]
def _connect_volume(self, connection_info, instance): def _connect_volume(self, context, connection_info, instance,
encryption=None):
vol_driver = self._get_volume_driver(connection_info) vol_driver = self._get_volume_driver(connection_info)
vol_driver.connect_volume(connection_info, instance) vol_driver.connect_volume(connection_info, instance)
self._attach_encryptor(context, connection_info, encryption=encryption)
def _disconnect_volume(self, connection_info, instance): def _disconnect_volume(self, context, connection_info, instance,
encryption=None):
self._detach_encryptor(context, connection_info, encryption=encryption)
vol_driver = self._get_volume_driver(connection_info) vol_driver = self._get_volume_driver(connection_info)
vol_driver.disconnect_volume(connection_info, instance) vol_driver.disconnect_volume(connection_info, instance)
@ -1232,6 +1221,44 @@ class LibvirtDriver(driver.ComputeDriver):
connection_info=connection_info, connection_info=connection_info,
**encryption) **encryption)
def _get_volume_encryption(self, context, connection_info):
"""Get the encryption metadata dict if it is not provided
"""
encryption = {}
if connection_info.get('data', {}).get('volume_id'):
volume_id = connection_info['data']['volume_id']
encryption = encryptors.get_encryption_metadata(context,
self._volume_api, volume_id, connection_info)
return encryption
def _attach_encryptor(self, context, connection_info, encryption):
"""Attach the frontend encryptor if one is required by the volume.
The request context is only used when an encryption metadata dict is
not provided. The encryption metadata dict being populated is then used
to determine if an attempt to attach the encryptor should be made.
"""
if encryption is None:
encryption = self._get_volume_encryption(context, connection_info)
if encryption:
encryptor = self._get_volume_encryptor(connection_info,
encryption)
encryptor.attach_volume(context, **encryption)
def _detach_encryptor(self, context, connection_info, encryption):
"""Detach the frontend encryptor if one is required by the volume.
The request context is only used when an encryption metadata dict is
not provided. The encryption metadata dict being populated is then used
to determine if an attempt to detach the encryptor should be made.
"""
if encryption is None:
encryption = self._get_volume_encryption(context, connection_info)
if encryption:
encryptor = self._get_volume_encryptor(connection_info,
encryption)
encryptor.detach_volume(**encryption)
def _check_discard_for_attach_volume(self, conf, instance): def _check_discard_for_attach_volume(self, conf, instance):
"""Perform some checks for volumes configured for discard support. """Perform some checks for volumes configured for discard support.
@ -1274,7 +1301,8 @@ class LibvirtDriver(driver.ComputeDriver):
"block size") % CONF.libvirt.virt_type "block size") % CONF.libvirt.virt_type
raise exception.InvalidHypervisorType(msg) raise exception.InvalidHypervisorType(msg)
self._connect_volume(connection_info, instance) self._connect_volume(context, connection_info, instance,
encryption=encryption)
disk_info = blockinfo.get_info_from_bdm( disk_info = blockinfo.get_info_from_bdm(
instance, CONF.libvirt.virt_type, instance.image_meta, bdm) instance, CONF.libvirt.virt_type, instance.image_meta, bdm)
if disk_info['bus'] == 'scsi': if disk_info['bus'] == 'scsi':
@ -1288,11 +1316,6 @@ class LibvirtDriver(driver.ComputeDriver):
state = guest.get_power_state(self._host) state = guest.get_power_state(self._host)
live = state in (power_state.RUNNING, power_state.PAUSED) live = state in (power_state.RUNNING, power_state.PAUSED)
if encryption:
encryptor = self._get_volume_encryptor(connection_info,
encryption)
encryptor.attach_volume(context, **encryption)
guest.attach_device(conf, persistent=True, live=live) guest.attach_device(conf, persistent=True, live=live)
# NOTE(artom) If we're attaching with a device role tag, we need to # NOTE(artom) If we're attaching with a device role tag, we need to
# rebuild device_metadata. If we're attaching without a role # rebuild device_metadata. If we're attaching without a role
@ -1309,7 +1332,8 @@ class LibvirtDriver(driver.ComputeDriver):
LOG.exception(_('Failed to attach volume at mountpoint: %s'), LOG.exception(_('Failed to attach volume at mountpoint: %s'),
mountpoint, instance=instance) mountpoint, instance=instance)
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self._disconnect_volume(connection_info, instance) self._disconnect_volume(context, connection_info, instance,
encryption=encryption)
def _swap_volume(self, guest, disk_path, conf, resize_to): def _swap_volume(self, guest, disk_path, conf, resize_to):
"""Swap existing disk with a new block device.""" """Swap existing disk with a new block device."""
@ -1367,7 +1391,7 @@ class LibvirtDriver(driver.ComputeDriver):
finally: finally:
self._host.write_instance_config(xml) self._host.write_instance_config(xml)
def swap_volume(self, old_connection_info, def swap_volume(self, context, old_connection_info,
new_connection_info, instance, mountpoint, resize_to): new_connection_info, instance, mountpoint, resize_to):
guest = self._host.get_guest(instance) guest = self._host.get_guest(instance)
@ -1388,19 +1412,19 @@ class LibvirtDriver(driver.ComputeDriver):
# LibvirtConfigGuestDisk object it returns. We do not explicitly save # LibvirtConfigGuestDisk object it returns. We do not explicitly save
# this to the BDM here as the upper compute swap_volume method will # this to the BDM here as the upper compute swap_volume method will
# eventually do this for us. # eventually do this for us.
self._connect_volume(new_connection_info, instance) self._connect_volume(context, new_connection_info, instance)
conf = self._get_volume_config(new_connection_info, disk_info) conf = self._get_volume_config(new_connection_info, disk_info)
if not conf.source_path: if not conf.source_path:
self._disconnect_volume(new_connection_info, instance) self._disconnect_volume(context, new_connection_info, instance)
raise NotImplementedError(_("Swap only supports host devices")) raise NotImplementedError(_("Swap only supports host devices"))
try: try:
self._swap_volume(guest, disk_dev, conf, resize_to) self._swap_volume(guest, disk_dev, conf, resize_to)
except exception.VolumeRebaseFailed: except exception.VolumeRebaseFailed:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
self._disconnect_volume(new_connection_info, instance) self._disconnect_volume(context, new_connection_info, instance)
self._disconnect_volume(old_connection_info, instance) self._disconnect_volume(context, old_connection_info, instance)
def _get_existing_domain_xml(self, instance, network_info, def _get_existing_domain_xml(self, instance, network_info,
block_device_info=None): block_device_info=None):
@ -1426,20 +1450,15 @@ class LibvirtDriver(driver.ComputeDriver):
state = guest.get_power_state(self._host) state = guest.get_power_state(self._host)
live = state in (power_state.RUNNING, power_state.PAUSED) live = state in (power_state.RUNNING, power_state.PAUSED)
# NOTE(lyarwood): The volume must be detached from the VM before
# The volume must be detached from the VM before disconnecting it # detaching any attached encryptors or disconnecting the underlying
# from its encryptor. Otherwise, the encryptor may report that the # volume in _disconnect_volume. Otherwise, the encryptor or volume
# volume is still in use. # driver may report that the volume is still in use.
wait_for_detach = guest.detach_device_with_retry(guest.get_disk, wait_for_detach = guest.detach_device_with_retry(guest.get_disk,
disk_dev, disk_dev,
live=live) live=live)
wait_for_detach() wait_for_detach()
if encryption:
encryptor = self._get_volume_encryptor(connection_info,
encryption)
encryptor.detach_volume(**encryption)
except exception.InstanceNotFound: except exception.InstanceNotFound:
# NOTE(zhaoqin): If the instance does not exist, _lookup_by_name() # NOTE(zhaoqin): If the instance does not exist, _lookup_by_name()
# will throw InstanceNotFound exception. Need to # will throw InstanceNotFound exception. Need to
@ -1460,7 +1479,12 @@ class LibvirtDriver(driver.ComputeDriver):
else: else:
raise raise
self._disconnect_volume(connection_info, instance) # NOTE(lyarwood): We can provide None as the request context here as we
# already have the encryption metadata dict from the compute layer.
# This avoids the need to add the request context to the signature of
# detach_volume requiring changes across all drivers.
self._disconnect_volume(None, connection_info, instance,
encryption=encryption)
def extend_volume(self, connection_info, instance): def extend_volume(self, connection_info, instance):
try: try:
@ -3682,7 +3706,7 @@ class LibvirtDriver(driver.ComputeDriver):
disk = self.image_backend.by_name(instance, name, image_type) disk = self.image_backend.by_name(instance, name, image_type)
return disk.libvirt_fs_info("/", "ploop") return disk.libvirt_fs_info("/", "ploop")
def _get_guest_storage_config(self, instance, image_meta, def _get_guest_storage_config(self, context, instance, image_meta,
disk_info, disk_info,
rescue, block_device_info, rescue, block_device_info,
inst_type, os_type): inst_type, os_type):
@ -3793,7 +3817,7 @@ class LibvirtDriver(driver.ComputeDriver):
connection_info = vol['connection_info'] connection_info = vol['connection_info']
vol_dev = block_device.prepend_dev(vol['mount_device']) vol_dev = block_device.prepend_dev(vol['mount_device'])
info = disk_mapping[vol_dev] info = disk_mapping[vol_dev]
self._connect_volume(connection_info, instance) self._connect_volume(context, connection_info, instance)
if scsi_controller and scsi_controller.model == 'virtio-scsi': if scsi_controller and scsi_controller.model == 'virtio-scsi':
info['unit'] = disk_mapping['unit'] info['unit'] = disk_mapping['unit']
disk_mapping['unit'] += 1 disk_mapping['unit'] += 1
@ -4881,7 +4905,7 @@ class LibvirtDriver(driver.ComputeDriver):
image_meta) image_meta)
self._set_clock(guest, instance.os_type, image_meta, virt_type) self._set_clock(guest, instance.os_type, image_meta, virt_type)
storage_configs = self._get_guest_storage_config( storage_configs = self._get_guest_storage_config(context,
instance, image_meta, disk_info, rescue, block_device_info, instance, image_meta, disk_info, rescue, block_device_info,
flavor, guest.os_type) flavor, guest.os_type)
for config in storage_configs: for config in storage_configs:
@ -5097,14 +5121,15 @@ class LibvirtDriver(driver.ComputeDriver):
# workaround, see libvirt/compat.py # workaround, see libvirt/compat.py
return guest.get_info(self._host) return guest.get_info(self._host)
def _create_domain_setup_lxc(self, instance, image_meta, def _create_domain_setup_lxc(self, context, instance, image_meta,
block_device_info): block_device_info):
inst_path = libvirt_utils.get_instance_path(instance) inst_path = libvirt_utils.get_instance_path(instance)
block_device_mapping = driver.block_device_info_get_mapping( block_device_mapping = driver.block_device_info_get_mapping(
block_device_info) block_device_info)
root_disk = block_device.get_root_bdm(block_device_mapping) root_disk = block_device.get_root_bdm(block_device_mapping)
if root_disk: if root_disk:
self._connect_volume(root_disk['connection_info'], instance) self._connect_volume(context, root_disk['connection_info'],
instance)
disk_path = root_disk['connection_info']['data']['device_path'] disk_path = root_disk['connection_info']['data']['device_path']
# NOTE(apmelton) - Even though the instance is being booted from a # NOTE(apmelton) - Even though the instance is being booted from a
@ -5153,7 +5178,8 @@ class LibvirtDriver(driver.ComputeDriver):
disk_api.teardown_container(container_dir=container_dir) disk_api.teardown_container(container_dir=container_dir)
@contextlib.contextmanager @contextlib.contextmanager
def _lxc_disk_handler(self, instance, image_meta, block_device_info): def _lxc_disk_handler(self, context, instance, image_meta,
block_device_info):
"""Context manager to handle the pre and post instance boot, """Context manager to handle the pre and post instance boot,
LXC specific disk operations. LXC specific disk operations.
@ -5167,7 +5193,8 @@ class LibvirtDriver(driver.ComputeDriver):
yield yield
return return
self._create_domain_setup_lxc(instance, image_meta, block_device_info) self._create_domain_setup_lxc(context, instance, image_meta,
block_device_info)
try: try:
yield yield
@ -5233,23 +5260,6 @@ class LibvirtDriver(driver.ComputeDriver):
destroy_disks_on_failure=False): destroy_disks_on_failure=False):
"""Do required network setup and create domain.""" """Do required network setup and create domain."""
block_device_mapping = driver.block_device_info_get_mapping(
block_device_info)
for vol in block_device_mapping:
connection_info = vol['connection_info']
if ('data' in connection_info and
'volume_id' in connection_info['data']):
volume_id = connection_info['data']['volume_id']
encryption = encryptors.get_encryption_metadata(
context, self._volume_api, volume_id, connection_info)
if encryption:
encryptor = self._get_volume_encryptor(connection_info,
encryption)
encryptor.attach_volume(context, **encryption)
timeout = CONF.vif_plugging_timeout timeout = CONF.vif_plugging_timeout
if (self._conn_supports_start_paused and if (self._conn_supports_start_paused and
utils.is_neutron() and not utils.is_neutron() and not
@ -5269,7 +5279,8 @@ class LibvirtDriver(driver.ComputeDriver):
network_info) network_info)
self.firewall_driver.prepare_instance_filter(instance, self.firewall_driver.prepare_instance_filter(instance,
network_info) network_info)
with self._lxc_disk_handler(instance, instance.image_meta, with self._lxc_disk_handler(context, instance,
instance.image_meta,
block_device_info): block_device_info):
guest = self._create_domain( guest = self._create_domain(
xml, pause=pause, power_on=power_on, xml, pause=pause, power_on=power_on,
@ -7061,7 +7072,7 @@ class LibvirtDriver(driver.ComputeDriver):
for bdm in block_device_mapping: for bdm in block_device_mapping:
connection_info = bdm['connection_info'] connection_info = bdm['connection_info']
self._connect_volume(connection_info, instance) self._connect_volume(context, connection_info, instance)
# We call plug_vifs before the compute manager calls # We call plug_vifs before the compute manager calls
# ensure_filtering_rules_for_instance, to ensure bridge is set up # ensure_filtering_rules_for_instance, to ensure bridge is set up
@ -7260,7 +7271,7 @@ class LibvirtDriver(driver.ComputeDriver):
multipath_id = vol['connection_info']['data']['multipath_id'] multipath_id = vol['connection_info']['data']['multipath_id']
connection_info['data']['multipath_id'] = multipath_id connection_info['data']['multipath_id'] = multipath_id
self._disconnect_volume(connection_info, instance) self._disconnect_volume(context, connection_info, instance)
def post_live_migration_at_source(self, context, instance, network_info): def post_live_migration_at_source(self, context, instance, network_info):
"""Unplug VIFs from networks at source. """Unplug VIFs from networks at source.
@ -7616,7 +7627,7 @@ class LibvirtDriver(driver.ComputeDriver):
block_device_info) block_device_info)
for vol in block_device_mapping: for vol in block_device_mapping:
connection_info = vol['connection_info'] connection_info = vol['connection_info']
self._disconnect_volume(connection_info, instance) self._disconnect_volume(context, connection_info, instance)
disk_info = self._get_instance_disk_info(instance, block_device_info) disk_info = self._get_instance_disk_info(instance, block_device_info)