Fix ValueError when loading old pci device record

Old pci_devices records might not have a uuid value set
and when we load those out of the database, the
PciDevice._from_db_object code was blindly trying to set
the PciDevice.uuid field to None, which fails because the
PciDevice.uuid field is not nullable.

This change fixes the problem by skipping the 'uuid' field
if it's not set in the db record so that we can auto-generate
a uuid later and update the object with it, which also performs
our online data migration.

This is similar to how we handle the uuid online migration for
other objects like compute nodes, services and migrations.

Change-Id: I5de0979e280004c1ce0acc99d69cc96089a704f8
Closes-Bug: #1735188
(cherry picked from commit e727437b0c)
This commit is contained in:
Matt Riedemann 2017-11-29 10:36:22 -05:00 committed by Lee Yarwood
parent ec20e1aca8
commit b1aa3ed017
2 changed files with 32 additions and 1 deletions

View File

@ -190,7 +190,12 @@ class PciDevice(base.NovaPersistentObject, base.NovaObject):
@staticmethod
def _from_db_object(context, pci_device, db_dev):
for key in pci_device.fields:
if key != 'extra_info':
if key == 'uuid' and db_dev['uuid'] is None:
# Older records might not have a uuid field set in the
# database so we need to skip those here and auto-generate
# a uuid later below.
continue
elif key != 'extra_info':
setattr(pci_device, key, db_dev[key])
else:
extra_info = db_dev.get("extra_info")

View File

@ -194,6 +194,32 @@ class _TestPciDeviceObject(object):
self.assertEqual(self.pci_device.obj_what_changed(), set())
mock_get.assert_called_once_with(ctxt, 1)
@mock.patch.object(db, 'pci_device_get_by_id')
@mock.patch.object(objects.PciDevice, 'save')
@mock.patch('oslo_utils.uuidutils.generate_uuid')
def test_get_by_dev_id_auto_generate_uuid(self, mock_uuid, mock_save,
mock_get):
"""Tests loading an old db record which doesn't have a uuid set so
the object code auto-generates one and saves the update.
"""
fake_db_dev_no_uuid = copy.deepcopy(fake_db_dev)
fake_db_dev_no_uuid['uuid'] = None
ctxt = context.get_admin_context()
mock_get.return_value = fake_db_dev_no_uuid
fake_uuid = '3afad0d9-d2db-46fd-b56b-79f90043de5e'
mock_uuid.return_value = fake_uuid
obj_dev = pci_device.PciDevice.get_by_dev_id(ctxt, 1)
self.assertEqual(fake_uuid, obj_dev.uuid)
# The obj_what_changed is still dirty from _from_db_object because we
# are mocking out save() which would eventually update the pci device
# in the database and call _from_db_object again on the updated record,
# and _from_db_object would reset the changed fields.
self.assertEqual(set(['uuid']), obj_dev.obj_what_changed())
mock_get.assert_called_once_with(ctxt, 1)
mock_save.assert_called_once_with()
mock_uuid.assert_called_once_with()
def test_from_db_obj_pre_1_5_format(self):
ctxt = context.get_admin_context()
fake_dev_pre_1_5 = copy.deepcopy(fake_db_dev_old)