Merge "Make libvirt driver return migrate data objects for source and dest checks"
This commit is contained in:
@@ -12,10 +12,17 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from nova import objects
|
||||
from nova.objects import base as obj_base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
@obj_base.NovaObjectRegistry.register_if(False)
|
||||
class LiveMigrateData(obj_base.NovaObject):
|
||||
fields = {
|
||||
@@ -39,3 +46,133 @@ class LiveMigrateData(obj_base.NovaObject):
|
||||
self.is_volume_backed = legacy['is_volume_backed']
|
||||
if 'migration' in legacy:
|
||||
self.migration = legacy['migration']
|
||||
|
||||
|
||||
@obj_base.NovaObjectRegistry.register
|
||||
class LibvirtLiveMigrateBDMInfo(obj_base.NovaObject):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
# FIXME(danms): some of these can be enums?
|
||||
'serial': fields.StringField(),
|
||||
'bus': fields.StringField(),
|
||||
'dev': fields.StringField(),
|
||||
'type': fields.StringField(),
|
||||
'format': fields.StringField(nullable=True),
|
||||
'boot_index': fields.IntegerField(nullable=True),
|
||||
'connection_info_json': fields.StringField(),
|
||||
}
|
||||
|
||||
# NOTE(danms): We don't have a connection_info object right
|
||||
# now, and instead mostly store/pass it as JSON that we're
|
||||
# careful with. When we get a connection_info object in the
|
||||
# future, we should use it here, so make this easy to convert
|
||||
# for later.
|
||||
@property
|
||||
def connection_info(self):
|
||||
return jsonutils.loads(self.connection_info_json)
|
||||
|
||||
@connection_info.setter
|
||||
def connection_info(self, info):
|
||||
self.connection_info_json = jsonutils.dumps(info)
|
||||
|
||||
def as_disk_info(self):
|
||||
info_dict = {
|
||||
'dev': self.dev,
|
||||
'bus': self.bus,
|
||||
'type': self.type,
|
||||
}
|
||||
if self.obj_attr_is_set('format') and self.format:
|
||||
info_dict['format'] = self.format
|
||||
if self.obj_attr_is_set('boot_index') and self.boot_index is not None:
|
||||
info_dict['boot_index'] = str(self.boot_index)
|
||||
return info_dict
|
||||
|
||||
|
||||
@obj_base.NovaObjectRegistry.register
|
||||
class LibvirtLiveMigrateData(LiveMigrateData):
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'filename': fields.StringField(),
|
||||
# FIXME: image_type should be enum?
|
||||
'image_type': fields.StringField(),
|
||||
'block_migration': fields.BooleanField(),
|
||||
'disk_over_commit': fields.BooleanField(),
|
||||
'disk_available_mb': fields.IntegerField(nullable=True),
|
||||
'is_shared_instance_path': fields.BooleanField(),
|
||||
'is_shared_block_storage': fields.BooleanField(),
|
||||
'instance_relative_path': fields.StringField(),
|
||||
'graphics_listen_addr_vnc': fields.IPAddressField(nullable=True),
|
||||
'graphics_listen_addr_spice': fields.IPAddressField(nullable=True),
|
||||
'serial_listen_addr': fields.StringField(),
|
||||
'bdms': fields.ListOfObjectsField('LibvirtLiveMigrateBDMInfo'),
|
||||
}
|
||||
|
||||
def _bdms_to_legacy(self, legacy):
|
||||
if not self.obj_attr_is_set('bdms'):
|
||||
return
|
||||
legacy['volume'] = {}
|
||||
for bdmi in self.bdms:
|
||||
legacy['volume'][bdmi.serial] = {
|
||||
'disk_info': bdmi.as_disk_info(),
|
||||
'connection_info': bdmi.connection_info}
|
||||
|
||||
def _bdms_from_legacy(self, legacy_pre_result):
|
||||
self.bdms = []
|
||||
volume = legacy_pre_result.get('volume', {})
|
||||
for serial in volume:
|
||||
vol = volume[serial]
|
||||
bdmi = objects.LibvirtLiveMigrateBDMInfo(serial=serial)
|
||||
bdmi.connection_info = vol['connection_info']
|
||||
bdmi.bus = vol['disk_info']['bus']
|
||||
bdmi.dev = vol['disk_info']['dev']
|
||||
bdmi.type = vol['disk_info']['type']
|
||||
if 'format' in vol:
|
||||
bdmi.format = vol['disk_info']['format']
|
||||
if 'boot_index' in vol:
|
||||
bdmi.boot_index = int(vol['disk_info']['boot_index'])
|
||||
self.bdms.append(bdmi)
|
||||
|
||||
def to_legacy_dict(self, pre_migration_result=False):
|
||||
LOG.debug('Converting to legacy: %s' % self)
|
||||
legacy = super(LibvirtLiveMigrateData, self).to_legacy_dict()
|
||||
keys = (set(self.fields.keys()) -
|
||||
set(LiveMigrateData.fields.keys()) - {'bdms'})
|
||||
legacy.update({k: getattr(self, k) for k in keys
|
||||
if self.obj_attr_is_set(k)})
|
||||
|
||||
graphics_vnc = legacy.pop('graphics_listen_addr_vnc', None)
|
||||
graphics_spice = legacy.pop('graphics_listen_addr_spice', None)
|
||||
live_result = {
|
||||
'graphics_listen_addrs': {
|
||||
'vnc': graphics_vnc and str(graphics_vnc),
|
||||
'spice': graphics_spice and str(graphics_spice),
|
||||
},
|
||||
'serial_listen_addr': legacy.pop('serial_listen_addr', None),
|
||||
}
|
||||
|
||||
if pre_migration_result:
|
||||
legacy['pre_live_migration_result'] = live_result
|
||||
self._bdms_to_legacy(live_result)
|
||||
|
||||
LOG.debug('Legacy result: %s' % legacy)
|
||||
return legacy
|
||||
|
||||
def from_legacy_dict(self, legacy):
|
||||
LOG.debug('Converting legacy dict to obj: %s' % legacy)
|
||||
super(LibvirtLiveMigrateData, self).from_legacy_dict(legacy)
|
||||
keys = set(self.fields.keys()) - set(LiveMigrateData.fields.keys())
|
||||
for k in keys - {'bdms'}:
|
||||
if k in legacy:
|
||||
setattr(self, k, legacy[k])
|
||||
if 'pre_live_migration_result' in legacy:
|
||||
pre_result = legacy['pre_live_migration_result']
|
||||
self.graphics_listen_addr_vnc = \
|
||||
pre_result['graphics_listen_addrs'].get('vnc')
|
||||
self.graphics_listen_addr_spice = \
|
||||
pre_result['graphics_listen_addrs'].get('spice')
|
||||
if 'serial_listen_addr' in pre_result:
|
||||
self.serial_listen_addr = pre_result['serial_listen_addr']
|
||||
self._bdms_from_legacy(pre_result)
|
||||
LOG.debug('Converted object: %s' % self)
|
||||
|
||||
@@ -51,3 +51,141 @@ class TestLiveMigrateData(test_objects._LocalTest,
|
||||
class TestRemoteLiveMigrateData(test_objects._RemoteTest,
|
||||
_TestLiveMigrateData):
|
||||
pass
|
||||
|
||||
|
||||
class _TestLibvirtLiveMigrateData(object):
|
||||
def test_bdm_to_disk_info(self):
|
||||
obj = migrate_data.LibvirtLiveMigrateBDMInfo(
|
||||
serial='foo', bus='scsi', dev='sda', type='disk')
|
||||
expected_info = {
|
||||
'dev': 'sda',
|
||||
'bus': 'scsi',
|
||||
'type': 'disk',
|
||||
}
|
||||
self.assertEqual(expected_info, obj.as_disk_info())
|
||||
obj.format = 'raw'
|
||||
obj.boot_index = 1
|
||||
expected_info['format'] = 'raw'
|
||||
expected_info['boot_index'] = '1'
|
||||
self.assertEqual(expected_info, obj.as_disk_info())
|
||||
|
||||
def test_to_legacy_dict(self):
|
||||
obj = migrate_data.LibvirtLiveMigrateData(
|
||||
is_volume_backed=False,
|
||||
filename='foo',
|
||||
image_type='rbd',
|
||||
block_migration=False,
|
||||
disk_over_commit=False,
|
||||
disk_available_mb=123,
|
||||
is_shared_instance_path=False,
|
||||
is_shared_block_storage=False,
|
||||
instance_relative_path='foo/bar')
|
||||
expected = {
|
||||
'is_volume_backed': False,
|
||||
'filename': 'foo',
|
||||
'image_type': 'rbd',
|
||||
'block_migration': False,
|
||||
'disk_over_commit': False,
|
||||
'disk_available_mb': 123,
|
||||
'is_shared_instance_path': False,
|
||||
'is_shared_block_storage': False,
|
||||
'instance_relative_path': 'foo/bar',
|
||||
}
|
||||
self.assertEqual(expected, obj.to_legacy_dict())
|
||||
|
||||
def test_from_legacy_dict(self):
|
||||
obj = migrate_data.LibvirtLiveMigrateData(
|
||||
is_volume_backed=False,
|
||||
filename='foo',
|
||||
image_type='rbd',
|
||||
block_migration=False,
|
||||
disk_over_commit=False,
|
||||
disk_available_mb=123,
|
||||
is_shared_instance_path=False,
|
||||
is_shared_block_storage=False,
|
||||
instance_relative_path='foo/bar')
|
||||
legacy = obj.to_legacy_dict()
|
||||
legacy['ignore_this_thing'] = True
|
||||
obj2 = migrate_data.LibvirtLiveMigrateData()
|
||||
obj2.from_legacy_dict(legacy)
|
||||
self.assertEqual(obj.filename, obj2.filename)
|
||||
|
||||
def test_to_legacy_dict_with_pre_result(self):
|
||||
test_bdmi = migrate_data.LibvirtLiveMigrateBDMInfo(
|
||||
serial='123',
|
||||
bus='scsi',
|
||||
dev='/dev/sda',
|
||||
type='disk',
|
||||
format='qcow2',
|
||||
boot_index=1,
|
||||
connection_info='myinfo')
|
||||
obj = migrate_data.LibvirtLiveMigrateData(
|
||||
is_volume_backed=False,
|
||||
filename='foo',
|
||||
image_type='rbd',
|
||||
block_migration=False,
|
||||
disk_over_commit=False,
|
||||
disk_available_mb=123,
|
||||
is_shared_instance_path=False,
|
||||
is_shared_block_storage=False,
|
||||
instance_relative_path='foo/bar',
|
||||
graphics_listen_addr_vnc='127.0.0.1',
|
||||
serial_listen_addr='127.0.0.1',
|
||||
bdms=[test_bdmi])
|
||||
legacy = obj.to_legacy_dict(pre_migration_result=True)
|
||||
self.assertIn('pre_live_migration_result', legacy)
|
||||
expected = {
|
||||
'graphics_listen_addrs': {'vnc': '127.0.0.1',
|
||||
'spice': None},
|
||||
'serial_listen_addr': '127.0.0.1',
|
||||
'volume': {
|
||||
'123': {
|
||||
'connection_info': 'myinfo',
|
||||
'disk_info': {
|
||||
'bus': 'scsi',
|
||||
'dev': '/dev/sda',
|
||||
'type': 'disk',
|
||||
'format': 'qcow2',
|
||||
'boot_index': '1',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.assertEqual(expected, legacy['pre_live_migration_result'])
|
||||
|
||||
def test_from_legacy_with_pre_result(self):
|
||||
test_bdmi = migrate_data.LibvirtLiveMigrateBDMInfo(
|
||||
serial='123',
|
||||
bus='scsi',
|
||||
dev='/dev/sda',
|
||||
type='disk',
|
||||
format='qcow2',
|
||||
boot_index=1,
|
||||
connection_info='myinfo')
|
||||
obj = migrate_data.LibvirtLiveMigrateData(
|
||||
is_volume_backed=False,
|
||||
filename='foo',
|
||||
image_type='rbd',
|
||||
block_migration=False,
|
||||
disk_over_commit=False,
|
||||
disk_available_mb=123,
|
||||
is_shared_instance_path=False,
|
||||
is_shared_block_storage=False,
|
||||
instance_relative_path='foo/bar',
|
||||
graphics_listen_addrs={'vnc': '127.0.0.1'},
|
||||
serial_listen_addr='127.0.0.1',
|
||||
bdms=[test_bdmi])
|
||||
obj2 = migrate_data.LibvirtLiveMigrateData()
|
||||
obj2.from_legacy_dict(obj.to_legacy_dict())
|
||||
self.assertEqual(obj.to_legacy_dict(),
|
||||
obj2.to_legacy_dict())
|
||||
|
||||
|
||||
class TestLibvirtLiveMigrateData(test_objects._LocalTest,
|
||||
_TestLibvirtLiveMigrateData):
|
||||
pass
|
||||
|
||||
|
||||
class TestRemoteLibvirtLiveMigrateData(test_objects._RemoteTest,
|
||||
_TestLibvirtLiveMigrateData):
|
||||
pass
|
||||
|
||||
@@ -1152,6 +1152,8 @@ object_data = {
|
||||
'InstanceNUMATopology': '1.2-d944a7d6c21e1c773ffdf09c6d025954',
|
||||
'InstancePCIRequest': '1.1-b1d75ebc716cb12906d9d513890092bf',
|
||||
'InstancePCIRequests': '1.1-65e38083177726d806684cb1cc0136d2',
|
||||
'LibvirtLiveMigrateBDMInfo': '1.0-252aabb723ca79d5469fa56f64b57811',
|
||||
'LibvirtLiveMigrateData': '1.0-eb8b5f6c49ae3858213a7012558a2f3d',
|
||||
'KeyPair': '1.3-bfaa2a8b148cdf11e0c72435d9dd097a',
|
||||
'KeyPairList': '1.2-58b94f96e776bedaf1e192ddb2a24c4e',
|
||||
'Migration': '1.2-8784125bedcea0a9227318511904e853',
|
||||
|
||||
@@ -5691,12 +5691,14 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
# No need for the src_compute_info
|
||||
return_value = drvr.check_can_live_migrate_destination(self.context,
|
||||
instance_ref, None, compute_info, True)
|
||||
return_value.is_volume_backed = False
|
||||
self.assertThat({"filename": "file",
|
||||
'image_type': 'default',
|
||||
'disk_available_mb': 409600,
|
||||
"disk_over_commit": False,
|
||||
"block_migration": True},
|
||||
matchers.DictMatches(return_value))
|
||||
"block_migration": True,
|
||||
"is_volume_backed": False},
|
||||
matchers.DictMatches(return_value.to_legacy_dict()))
|
||||
|
||||
@mock.patch.object(libvirt_driver.LibvirtDriver,
|
||||
'_create_shared_storage_test_file')
|
||||
@@ -5718,12 +5720,14 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
# No need for the src_compute_info
|
||||
return_value = drvr.check_can_live_migrate_destination(self.context,
|
||||
instance_ref, None, compute_info, False)
|
||||
return_value.is_volume_backed = False
|
||||
self.assertThat({"filename": "file",
|
||||
"image_type": 'default',
|
||||
"block_migration": False,
|
||||
"disk_over_commit": False,
|
||||
"disk_available_mb": None},
|
||||
matchers.DictMatches(return_value))
|
||||
"disk_available_mb": None,
|
||||
"is_volume_backed": False},
|
||||
matchers.DictMatches(return_value.to_legacy_dict()))
|
||||
|
||||
@mock.patch.object(libvirt_driver.LibvirtDriver,
|
||||
'_create_shared_storage_test_file',
|
||||
@@ -5740,13 +5744,15 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
compute_info = {'cpu_info': 'asdf'}
|
||||
result = drvr.check_can_live_migrate_destination(
|
||||
self.context, instance_ref, compute_info, compute_info)
|
||||
result.is_volume_backed = False
|
||||
mock_cpu.assert_called_once_with(None, 'asdf')
|
||||
expected_result = {"filename": 'fake',
|
||||
"image_type": CONF.libvirt.images_type,
|
||||
"block_migration": False,
|
||||
"disk_over_commit": False,
|
||||
"disk_available_mb": None}
|
||||
self.assertDictEqual(expected_result, result)
|
||||
"disk_available_mb": None,
|
||||
"is_volume_backed": False}
|
||||
self.assertDictEqual(expected_result, result.to_legacy_dict())
|
||||
|
||||
@mock.patch.object(libvirt_driver.LibvirtDriver,
|
||||
'_create_shared_storage_test_file')
|
||||
@@ -5771,12 +5777,15 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
|
||||
return_value = drvr.check_can_live_migrate_destination(self.context,
|
||||
instance_ref, compute_info, compute_info, False)
|
||||
# NOTE(danms): Compute manager would have set this, so set it here
|
||||
return_value.is_volume_backed = False
|
||||
self.assertThat({"filename": "file",
|
||||
"image_type": 'default',
|
||||
"block_migration": False,
|
||||
"disk_over_commit": False,
|
||||
"disk_available_mb": None},
|
||||
matchers.DictMatches(return_value))
|
||||
"disk_available_mb": None,
|
||||
"is_volume_backed": False},
|
||||
matchers.DictMatches(return_value.to_legacy_dict()))
|
||||
|
||||
@mock.patch.object(fakelibvirt.Connection, 'compareCPU')
|
||||
def test_check_can_live_migrate_dest_incompatible_cpu_raises(
|
||||
@@ -5874,11 +5883,12 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
block_device_info=None,
|
||||
block_device_text=None):
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
dest_check_data = {'filename': 'file',
|
||||
'image_type': 'default',
|
||||
'block_migration': block_migration,
|
||||
'disk_over_commit': False,
|
||||
'disk_available_mb': disk_available_mb}
|
||||
dest_check_data = objects.LibvirtLiveMigrateData(
|
||||
filename='file',
|
||||
image_type='default',
|
||||
block_migration=block_migration,
|
||||
disk_over_commit=False,
|
||||
disk_available_mb=disk_available_mb)
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
|
||||
self.mox.StubOutWithMock(drvr, '_is_shared_block_storage')
|
||||
@@ -5903,17 +5913,15 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
|
||||
self.mox.StubOutWithMock(drvr, "_assert_dest_node_has_enough_disk")
|
||||
drvr._assert_dest_node_has_enough_disk(
|
||||
self.context, instance, dest_check_data['disk_available_mb'],
|
||||
self.context, instance, dest_check_data.disk_available_mb,
|
||||
False, None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
ret = drvr.check_can_live_migrate_source(self.context, instance,
|
||||
dest_check_data)
|
||||
self.assertIsInstance(ret, dict)
|
||||
self.assertIsInstance(ret, objects.LibvirtLiveMigrateData)
|
||||
self.assertIn('is_shared_block_storage', ret)
|
||||
self.assertIn('is_shared_instance_path', ret)
|
||||
self.assertEqual(ret['is_shared_instance_path'],
|
||||
ret['is_shared_storage'])
|
||||
|
||||
def test_check_can_live_migrate_source_shared_block_storage(self):
|
||||
instance, dest_check_data, drvr = self._mock_can_live_migrate_source(
|
||||
@@ -6015,11 +6023,12 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
bdi = {'block_device_mapping': ['bdm']}
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
dest_check_data = {'filename': 'file',
|
||||
'image_type': 'default',
|
||||
'block_migration': True,
|
||||
'disk_over_commit': False,
|
||||
'disk_available_mb': 100}
|
||||
dest_check_data = objects.LibvirtLiveMigrateData(
|
||||
filename='file',
|
||||
image_type='default',
|
||||
block_migration=True,
|
||||
disk_over_commit=False,
|
||||
disk_available_mb=100)
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertRaises(exception.MigrationPreCheckError,
|
||||
drvr.check_can_live_migrate_source,
|
||||
@@ -6073,11 +6082,11 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
mock_get_instance_disk_info = mock.Mock()
|
||||
data = objects.LibvirtLiveMigrateData(image_type='rbd')
|
||||
with mock.patch.object(drvr, 'get_instance_disk_info',
|
||||
mock_get_instance_disk_info):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertTrue(drvr._is_shared_block_storage(instance,
|
||||
{'image_type': 'rbd'},
|
||||
self.assertTrue(drvr._is_shared_block_storage(instance, data,
|
||||
block_device_info=bdi))
|
||||
self.assertEqual(0, mock_get_instance_disk_info.call_count)
|
||||
|
||||
@@ -6087,11 +6096,14 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
mock_get_instance_disk_info = mock.Mock()
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
data = objects.LibvirtLiveMigrateData(image_type='lvm',
|
||||
is_volume_backed=False,
|
||||
is_shared_instance_path=False)
|
||||
with mock.patch.object(drvr, 'get_instance_disk_info',
|
||||
mock_get_instance_disk_info):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertFalse(drvr._is_shared_block_storage(
|
||||
instance, {'image_type': 'lvm'},
|
||||
instance, data,
|
||||
block_device_info=bdi))
|
||||
self.assertEqual(0, mock_get_instance_disk_info.call_count)
|
||||
|
||||
@@ -6101,11 +6113,14 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
mock_get_instance_disk_info = mock.Mock()
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
data = objects.LibvirtLiveMigrateData(image_type='qcow2',
|
||||
is_volume_backed=False,
|
||||
is_shared_instance_path=False)
|
||||
with mock.patch.object(drvr, 'get_instance_disk_info',
|
||||
mock_get_instance_disk_info):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertFalse(drvr._is_shared_block_storage(
|
||||
instance, {'image_type': 'qcow2'},
|
||||
instance, data,
|
||||
block_device_info=bdi))
|
||||
self.assertEqual(0, mock_get_instance_disk_info.call_count)
|
||||
|
||||
@@ -6115,11 +6130,13 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
mock_get_instance_disk_info = mock.Mock()
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
data = objects.LibvirtLiveMigrateData(is_shared_instance_path=False,
|
||||
is_volume_backed=False)
|
||||
with mock.patch.object(drvr, 'get_instance_disk_info',
|
||||
mock_get_instance_disk_info):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertFalse(drvr._is_shared_block_storage(
|
||||
instance, {'is_shared_instance_path': False},
|
||||
instance, data,
|
||||
block_device_info=bdi))
|
||||
self.assertEqual(0, mock_get_instance_disk_info.call_count)
|
||||
|
||||
@@ -6128,12 +6145,14 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
mock_get_instance_disk_info = mock.Mock()
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
data = objects.LibvirtLiveMigrateData(image_type='rbd',
|
||||
is_volume_backed=False,
|
||||
is_shared_instance_path=False)
|
||||
with mock.patch.object(drvr, 'get_instance_disk_info',
|
||||
mock_get_instance_disk_info):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
self.assertFalse(drvr._is_shared_block_storage(
|
||||
instance, {'image_type': 'rbd',
|
||||
'is_shared_instance_path': False},
|
||||
instance, data,
|
||||
block_device_info=bdi))
|
||||
self.assertEqual(0, mock_get_instance_disk_info.call_count)
|
||||
|
||||
@@ -6149,10 +6168,10 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
(mock_getsize, mock_lookup) =\
|
||||
self._is_shared_block_storage_test_create_mocks(disks)
|
||||
data = objects.LibvirtLiveMigrateData(is_volume_backed=True,
|
||||
is_shared_instance_path=False)
|
||||
with mock.patch.object(host.Host, 'get_domain', mock_lookup):
|
||||
self.assertTrue(drvr._is_shared_block_storage(instance,
|
||||
{'is_volume_backed': True,
|
||||
'is_shared_instance_path': False},
|
||||
self.assertTrue(drvr._is_shared_block_storage(instance, data,
|
||||
block_device_info = bdi))
|
||||
mock_lookup.assert_called_once_with(instance)
|
||||
|
||||
@@ -6173,13 +6192,13 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
(mock_getsize, mock_lookup) =\
|
||||
self._is_shared_block_storage_test_create_mocks(disks)
|
||||
data = objects.LibvirtLiveMigrateData(is_volume_backed=True,
|
||||
is_shared_instance_path=False)
|
||||
with test.nested(
|
||||
mock.patch.object(os.path, 'getsize', mock_getsize),
|
||||
mock.patch.object(host.Host, 'get_domain', mock_lookup)):
|
||||
self.assertFalse(drvr._is_shared_block_storage(
|
||||
instance,
|
||||
{'is_volume_backed': True,
|
||||
'is_shared_instance_path': False},
|
||||
instance, data,
|
||||
block_device_info = bdi))
|
||||
mock_getsize.assert_called_once_with('/instance/disk.local')
|
||||
mock_lookup.assert_called_once_with(instance)
|
||||
@@ -6193,11 +6212,13 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
mock_image_backend.backend.return_value = mock_backend
|
||||
mock_backend.is_file_in_instance_path.return_value = True
|
||||
mock_get_instance_disk_info = mock.Mock()
|
||||
data = objects.LibvirtLiveMigrateData(
|
||||
is_shared_instance_path=True,
|
||||
image_type='foo')
|
||||
with mock.patch.object(drvr, 'get_instance_disk_info',
|
||||
mock_get_instance_disk_info):
|
||||
self.assertTrue(drvr._is_shared_block_storage('instance',
|
||||
{'is_shared_instance_path': True},
|
||||
block_device_info=bdi))
|
||||
self.assertTrue(drvr._is_shared_block_storage(
|
||||
'instance', data, block_device_info=bdi))
|
||||
self.assertEqual(0, mock_get_instance_disk_info.call_count)
|
||||
|
||||
def test_live_migration_update_graphics_xml(self):
|
||||
@@ -7381,9 +7402,15 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
drvr.plug_vifs(mox.IsA(instance), nw_info)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
migrate_data = {
|
||||
"block_migration": False,
|
||||
"instance_relative_path": "foo",
|
||||
"is_shared_block_storage": False,
|
||||
"is_shared_instance_path": False,
|
||||
}
|
||||
result = drvr.pre_live_migration(
|
||||
c, instance, vol, nw_info, None,
|
||||
migrate_data={"block_migration": False})
|
||||
migrate_data=migrate_data)
|
||||
|
||||
target_ret = {
|
||||
'graphics_listen_addrs': {'spice': '127.0.0.1', 'vnc': '127.0.0.1'},
|
||||
@@ -7401,7 +7428,10 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
'disk_info': {'bus': 'scsi',
|
||||
'dev': 'sdb',
|
||||
'type': 'disk'}}}}
|
||||
self.assertEqual(result, target_ret)
|
||||
self.assertEqual(
|
||||
result.to_legacy_dict(
|
||||
pre_migration_result=True)['pre_live_migration_result'],
|
||||
target_ret)
|
||||
|
||||
def test_pre_live_migration_block_with_config_drive_mocked(self):
|
||||
# Creating testdata
|
||||
@@ -7421,7 +7451,9 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
self.assertRaises(exception.NoLiveMigrationForConfigDriveInLibVirt,
|
||||
drvr.pre_live_migration, c, instance, vol, None,
|
||||
None, {'is_shared_instance_path': False,
|
||||
'is_shared_block_storage': False})
|
||||
'is_shared_block_storage': False,
|
||||
'block_migration': False,
|
||||
'instance_relative_path': 'foo'})
|
||||
|
||||
@mock.patch('nova.virt.driver.block_device_info_get_mapping',
|
||||
return_value=())
|
||||
@@ -7441,7 +7473,10 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
res_data = drvr.pre_live_migration(
|
||||
self.context, instance, vol, [], None,
|
||||
{'is_shared_instance_path': False,
|
||||
'is_shared_block_storage': False})
|
||||
'is_shared_block_storage': False,
|
||||
'block_migration': False,
|
||||
'instance_relative_path': 'foo'})
|
||||
res_data = res_data.to_legacy_dict(pre_migration_result=True)
|
||||
block_device_info_get_mapping.assert_called_once_with(
|
||||
{'block_device_mapping': [
|
||||
{'connection_info': 'dummy', 'mount_device': '/dev/sda'},
|
||||
@@ -7451,7 +7486,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
self.assertEqual({'graphics_listen_addrs': {'spice': '127.0.0.1',
|
||||
'vnc': '127.0.0.1'},
|
||||
'serial_listen_addr': '127.0.0.1',
|
||||
'volume': {}}, res_data)
|
||||
'volume': {}}, res_data['pre_live_migration_result'])
|
||||
|
||||
def test_pre_live_migration_vol_backed_works_correctly_mocked(self):
|
||||
# Creating testdata, using temp dir.
|
||||
@@ -7494,10 +7529,15 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
drvr.plug_vifs(mox.IsA(inst_ref), nw_info)
|
||||
self.mox.ReplayAll()
|
||||
migrate_data = {'is_shared_instance_path': False,
|
||||
'is_shared_block_storage': False,
|
||||
'is_volume_backed': True,
|
||||
'block_migration': False,
|
||||
'instance_relative_path': inst_ref['name']
|
||||
}
|
||||
'instance_relative_path': inst_ref['name'],
|
||||
'disk_over_commit': False,
|
||||
'disk_available_mb': 123,
|
||||
'image_type': 'qcow2',
|
||||
'filename': 'foo',
|
||||
}
|
||||
ret = drvr.pre_live_migration(c, inst_ref, vol, nw_info, None,
|
||||
migrate_data)
|
||||
target_ret = {
|
||||
@@ -7517,7 +7557,9 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
'disk_info': {'bus': 'scsi',
|
||||
'dev': 'sdb',
|
||||
'type': 'disk'}}}}
|
||||
self.assertEqual(ret, target_ret)
|
||||
self.assertEqual(
|
||||
ret.to_legacy_dict(True)['pre_live_migration_result'],
|
||||
target_ret)
|
||||
self.assertTrue(os.path.exists('%s/%s/' % (tmpdir,
|
||||
inst_ref['name'])))
|
||||
|
||||
@@ -7560,10 +7602,31 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
|
||||
def test_pre_live_migration_image_not_created_with_shared_storage(self):
|
||||
migrate_data_set = [{'is_shared_block_storage': False,
|
||||
'is_shared_instance_path': True,
|
||||
'is_volume_backed': False,
|
||||
'filename': 'foo',
|
||||
'instance_relative_path': 'bar',
|
||||
'disk_over_commit': False,
|
||||
'disk_available_mb': 123,
|
||||
'image_type': 'qcow2',
|
||||
'block_migration': False},
|
||||
{'is_shared_block_storage': True,
|
||||
'is_shared_instance_path': True,
|
||||
'is_volume_backed': False,
|
||||
'filename': 'foo',
|
||||
'instance_relative_path': 'bar',
|
||||
'disk_over_commit': False,
|
||||
'disk_available_mb': 123,
|
||||
'image_type': 'qcow2',
|
||||
'block_migration': False},
|
||||
{'is_shared_block_storage': False,
|
||||
'is_shared_instance_path': True,
|
||||
'is_volume_backed': False,
|
||||
'filename': 'foo',
|
||||
'instance_relative_path': 'bar',
|
||||
'disk_over_commit': False,
|
||||
'disk_available_mb': 123,
|
||||
'image_type': 'qcow2',
|
||||
'block_migration': True}]
|
||||
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
@@ -7588,11 +7651,14 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
disk_info=disk_info_json,
|
||||
migrate_data=migrate_data)
|
||||
self.assertFalse(create_image_mock.called)
|
||||
self.assertIsInstance(res, dict)
|
||||
self.assertIsInstance(res,
|
||||
objects.LibvirtLiveMigrateData)
|
||||
|
||||
def test_pre_live_migration_with_not_shared_instance_path(self):
|
||||
migrate_data = {'is_shared_block_storage': False,
|
||||
'is_shared_instance_path': False}
|
||||
'is_shared_instance_path': False,
|
||||
'block_migration': False,
|
||||
'instance_relative_path': 'foo'}
|
||||
|
||||
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
instance = objects.Instance(**self.test_instance)
|
||||
@@ -7623,7 +7689,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
|
||||
create_image_mock.assert_has_calls(
|
||||
[mock.call(self.context, instance, mock.ANY, {},
|
||||
fallback_from_host=instance.host)])
|
||||
self.assertIsInstance(res, dict)
|
||||
self.assertIsInstance(res, objects.LibvirtLiveMigrateData)
|
||||
|
||||
def test_get_instance_disk_info_works_correctly(self):
|
||||
# Test data
|
||||
|
||||
@@ -734,7 +734,8 @@ disk size: 4.4M
|
||||
expected_path = os.path.join(CONF.instances_path, instance['uuid'])
|
||||
self.assertEqual(expected_path, inst_path_at_dest)
|
||||
|
||||
migrate_data = dict(instance_relative_path='fake_relative_path')
|
||||
migrate_data = objects.LibvirtLiveMigrateData(
|
||||
instance_relative_path='fake_relative_path')
|
||||
inst_path_at_dest = libvirt_utils.get_instance_path_at_destination(
|
||||
instance, migrate_data)
|
||||
expected_path = os.path.join(CONF.instances_path, 'fake_relative_path')
|
||||
|
||||
+65
-47
@@ -78,6 +78,7 @@ from nova import image
|
||||
from nova.network import model as network_model
|
||||
from nova import objects
|
||||
from nova.objects import fields
|
||||
from nova.objects import migrate_data as migrate_data_obj
|
||||
from nova.pci import manager as pci_manager
|
||||
from nova.pci import utils as pci_utils
|
||||
from nova import utils
|
||||
@@ -5142,11 +5143,13 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
# Create file on storage, to be checked on source host
|
||||
filename = self._create_shared_storage_test_file()
|
||||
|
||||
return {"filename": filename,
|
||||
"image_type": CONF.libvirt.images_type,
|
||||
"block_migration": block_migration,
|
||||
"disk_over_commit": disk_over_commit,
|
||||
"disk_available_mb": disk_available_mb}
|
||||
data = objects.LibvirtLiveMigrateData()
|
||||
data.filename = filename
|
||||
data.image_type = CONF.libvirt.images_type
|
||||
data.block_migration = block_migration
|
||||
data.disk_over_commit = disk_over_commit
|
||||
data.disk_available_mb = disk_available_mb
|
||||
return data
|
||||
|
||||
def check_can_live_migrate_destination_cleanup(self, context,
|
||||
dest_check_data):
|
||||
@@ -5175,13 +5178,18 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
# if block migration, instances_paths should not be on shared storage.
|
||||
source = CONF.host
|
||||
|
||||
dest_check_data.update({'is_shared_instance_path':
|
||||
self._check_shared_storage_test_file(
|
||||
dest_check_data['filename'])})
|
||||
if not isinstance(dest_check_data, migrate_data_obj.LiveMigrateData):
|
||||
md_obj = migrate_data_obj.LibvirtLiveMigrateData()
|
||||
md_obj.from_legacy_dict(dest_check_data)
|
||||
dest_check_data = md_obj
|
||||
|
||||
dest_check_data.update({'is_shared_block_storage':
|
||||
self._is_shared_block_storage(instance, dest_check_data,
|
||||
block_device_info)})
|
||||
dest_check_data.is_shared_instance_path = (
|
||||
self._check_shared_storage_test_file(
|
||||
dest_check_data.filename))
|
||||
|
||||
dest_check_data.is_shared_block_storage = (
|
||||
self._is_shared_block_storage(instance, dest_check_data,
|
||||
block_device_info))
|
||||
|
||||
disk_info_text = self.get_instance_disk_info(
|
||||
instance, block_device_info=block_device_info)
|
||||
@@ -5189,15 +5197,15 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
disk_info_text)
|
||||
has_local_disk = self._has_local_disk(instance, disk_info_text)
|
||||
|
||||
if dest_check_data['block_migration']:
|
||||
if (dest_check_data['is_shared_block_storage'] or
|
||||
dest_check_data['is_shared_instance_path']):
|
||||
if dest_check_data.block_migration:
|
||||
if (dest_check_data.is_shared_block_storage or
|
||||
dest_check_data.is_shared_instance_path):
|
||||
reason = _("Block migration can not be used "
|
||||
"with shared storage.")
|
||||
raise exception.InvalidLocalStorage(reason=reason, path=source)
|
||||
self._assert_dest_node_has_enough_disk(context, instance,
|
||||
dest_check_data['disk_available_mb'],
|
||||
dest_check_data['disk_over_commit'],
|
||||
dest_check_data.disk_available_mb,
|
||||
dest_check_data.disk_over_commit,
|
||||
block_device_info)
|
||||
if block_device_info:
|
||||
bdm = block_device_info.get('block_device_mapping')
|
||||
@@ -5213,8 +5221,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
'volumes') % instance.uuid)
|
||||
raise exception.MigrationPreCheckError(reason=msg)
|
||||
|
||||
elif not (dest_check_data['is_shared_block_storage'] or
|
||||
dest_check_data['is_shared_instance_path'] or
|
||||
elif not (dest_check_data.is_shared_block_storage or
|
||||
dest_check_data.is_shared_instance_path or
|
||||
(booted_from_volume and not has_local_disk)):
|
||||
reason = _("Live migration can not be used "
|
||||
"without shared storage except "
|
||||
@@ -5227,13 +5235,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
# same name to be used
|
||||
instance_path = libvirt_utils.get_instance_path(instance,
|
||||
relative=True)
|
||||
dest_check_data['instance_relative_path'] = instance_path
|
||||
|
||||
# NOTE(danms): Emulate this old flag in case we're talking to
|
||||
# an older client (<= Juno). We can remove this when we bump the
|
||||
# compute RPC API to 4.0.
|
||||
dest_check_data['is_shared_storage'] = (
|
||||
dest_check_data['is_shared_instance_path'])
|
||||
dest_check_data.instance_relative_path = instance_path
|
||||
|
||||
return dest_check_data
|
||||
|
||||
@@ -5250,18 +5252,19 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
:param dest_check_data: dict with boolean fields image_type,
|
||||
is_shared_instance_path, and is_volume_backed
|
||||
"""
|
||||
if (CONF.libvirt.images_type == dest_check_data.get('image_type') and
|
||||
if (dest_check_data.obj_attr_is_set('image_type') and
|
||||
CONF.libvirt.images_type == dest_check_data.image_type and
|
||||
self.image_backend.backend().is_shared_block_storage()):
|
||||
# NOTE(dgenin): currently true only for RBD image backend
|
||||
return True
|
||||
|
||||
if (dest_check_data.get('is_shared_instance_path') and
|
||||
if (dest_check_data.is_shared_instance_path and
|
||||
self.image_backend.backend().is_file_in_instance_path()):
|
||||
# NOTE(angdraug): file based image backends (Raw, Qcow2)
|
||||
# place block device files under the instance path
|
||||
return True
|
||||
|
||||
if (dest_check_data.get('is_volume_backed') and
|
||||
if (dest_check_data.is_volume_backed and
|
||||
not bool(jsonutils.loads(
|
||||
self.get_instance_disk_info(instance,
|
||||
block_device_info)))):
|
||||
@@ -6161,12 +6164,18 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
# NOTE(gcb): Failed block live migration may leave instance
|
||||
# directory at destination node, ensure it is always deleted.
|
||||
is_shared_instance_path = True
|
||||
instance_relative_path = None
|
||||
if migrate_data:
|
||||
is_shared_instance_path = migrate_data.get(
|
||||
'is_shared_instance_path', True)
|
||||
instance_relative_path = migrate_data.get(
|
||||
'instance_relative_path')
|
||||
mdo = objects.LibvirtLiveMigrateData()
|
||||
if instance_relative_path:
|
||||
mdo.instance_relative_path = instance_relative_path
|
||||
if not is_shared_instance_path:
|
||||
instance_dir = libvirt_utils.get_instance_path_at_destination(
|
||||
instance, migrate_data)
|
||||
instance, mdo)
|
||||
if os.path.exists(instance_dir):
|
||||
shutil.rmtree(instance_dir)
|
||||
|
||||
@@ -6181,13 +6190,15 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
is_shared_instance_path = True
|
||||
is_block_migration = True
|
||||
if migrate_data:
|
||||
if not isinstance(migrate_data, migrate_data_obj.LiveMigrateData):
|
||||
obj = objects.LibvirtLiveMigrateData()
|
||||
obj.from_legacy_dict(migrate_data)
|
||||
migrate_data = obj
|
||||
LOG.debug('migrate_data in pre_live_migration: %s', migrate_data,
|
||||
instance=instance)
|
||||
is_shared_block_storage = migrate_data.get(
|
||||
'is_shared_block_storage', True)
|
||||
is_shared_instance_path = migrate_data.get(
|
||||
'is_shared_instance_path', True)
|
||||
is_block_migration = migrate_data.get('block_migration', True)
|
||||
is_shared_block_storage = migrate_data.is_shared_block_storage
|
||||
is_shared_instance_path = migrate_data.is_shared_instance_path
|
||||
is_block_migration = migrate_data.block_migration
|
||||
|
||||
image_meta = objects.ImageMeta.from_instance(instance)
|
||||
|
||||
@@ -6277,25 +6288,32 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
greenthread.sleep(1)
|
||||
|
||||
# Store vncserver_listen and latest disk device info
|
||||
res_data = {'graphics_listen_addrs': {}, 'volume': {},
|
||||
'serial_listen_addr': {}}
|
||||
res_data['graphics_listen_addrs']['vnc'] = CONF.vnc.vncserver_listen
|
||||
res_data['graphics_listen_addrs']['spice'] = CONF.spice.server_listen
|
||||
res_data['serial_listen_addr'] = \
|
||||
CONF.serial_console.proxyclient_address
|
||||
if not migrate_data:
|
||||
migrate_data = objects.LibvirtLiveMigrateData(bdms=[])
|
||||
else:
|
||||
migrate_data.bdms = []
|
||||
migrate_data.graphics_listen_addr_vnc = CONF.vnc.vncserver_listen
|
||||
migrate_data.graphics_listen_addr_spice = CONF.spice.server_listen
|
||||
migrate_data.serial_listen_addr = \
|
||||
CONF.serial_console.proxyclient_address
|
||||
|
||||
for vol in block_device_mapping:
|
||||
connection_info = vol['connection_info']
|
||||
if connection_info.get('serial'):
|
||||
serial = connection_info['serial']
|
||||
res_data['volume'][serial] = {'connection_info': {},
|
||||
'disk_info': {}}
|
||||
res_data['volume'][serial]['connection_info'] = \
|
||||
connection_info
|
||||
disk_info = blockinfo.get_info_from_bdm(
|
||||
instance, CONF.libvirt.virt_type, image_meta, vol)
|
||||
res_data['volume'][serial]['disk_info'] = disk_info
|
||||
|
||||
return res_data
|
||||
bdmi = objects.LibvirtLiveMigrateBDMInfo()
|
||||
bdmi.serial = connection_info['serial']
|
||||
bdmi.connection_info = connection_info
|
||||
bdmi.bus = disk_info['bus']
|
||||
bdmi.dev = disk_info['dev']
|
||||
bdmi.type = disk_info['type']
|
||||
bdmi.format = disk_info.get('format')
|
||||
bdmi.boot_index = disk_info.get('boot_index')
|
||||
migrate_data.bdms.append(bdmi)
|
||||
|
||||
return migrate_data
|
||||
|
||||
def _try_fetch_image_cache(self, image, fetch_func, context, filename,
|
||||
image_id, instance, size,
|
||||
|
||||
@@ -449,7 +449,7 @@ def get_instance_path_at_destination(instance, migrate_data=None):
|
||||
"""
|
||||
instance_relative_path = None
|
||||
if migrate_data:
|
||||
instance_relative_path = migrate_data.get('instance_relative_path')
|
||||
instance_relative_path = migrate_data.instance_relative_path
|
||||
# NOTE(mikal): this doesn't use libvirt_utils.get_instance_path
|
||||
# because we are ensuring that the same instance directory name
|
||||
# is used as was at the source
|
||||
|
||||
Reference in New Issue
Block a user