Use uuid instead of name for lvm backend

Because libvirt lvm volumes are based on instance['name'], it means
that the actual names used in lvm storage are based on an operator
configuration variable: instance_name_template (default is
'instance-%08x').

However this is site changeable, and changeable at any time. This
creates 2 failure modes.

1. Operator changes this, the result is all volumes created before the
change are no longer able to be cleaned up by nova

2. Operator has changed this to something that includes end user
input, like %(display_name), which would allow one user to impact
another (user A has display name "bob", user B has display name
"bob_joe")

This changes the lvm backend to use instance['uuid'] as it's
identifier which we know is unique, non overlapping, and not
changeable based on whims of site policy.

It also provides limited backwards compatibility for cleaning up old
disks if the installation was using the default template (which is
safe), otherwise it logs a warning about possibly leaking a disk which
will need to be manually cleaned up. That should be removed once we
open the Juno tree.

UpgradeImpact - while there is no operator action required to upgrade
over this change, if the nova install included a non default value for
instance_name_template then old lvm volumes will need to be cleaned up
manually after old guests are destroyed.

Change-Id: Ib36b962971fd1f66ea9a0818e91fec59e118e686
Closes-Bug: #1285735
This commit is contained in:
Sean Dague 2014-02-27 15:57:17 -05:00 committed by Dan Smith
parent 126e1c5ae1
commit d9a5a80bc0
4 changed files with 37 additions and 3 deletions

View File

@ -362,7 +362,7 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
self.image_class = imagebackend.Lvm
super(LvmTestCase, self).setUp()
self.flags(images_volume_group=self.VG, group='libvirt')
self.LV = '%s_%s' % (self.INSTANCE['name'], self.NAME)
self.LV = '%s_%s' % (self.INSTANCE['uuid'], self.NAME)
self.OLD_STYLE_INSTANCE_PATH = None
self.PATH = os.path.join('/dev', self.VG, self.LV)

View File

@ -8093,6 +8093,21 @@ class LibvirtDriverTestCase(test.TestCase):
self.libvirtconnection.get_instance_disk_info,
instance_name)
@mock.patch('os.path.exists')
@mock.patch('nova.virt.libvirt.utils.list_logical_volumes')
def test_lvm_disks(self, listlvs, exists):
instance = instance_obj.Instance(uuid='fake-uuid',
id=1)
self.flags(images_volume_group='vols', group='libvirt')
exists.return_value = True
listlvs.return_value = ['fake-uuid_foo',
'instance-00000001_bar',
'other-uuid_foo',
'instance-00000002_bar']
disks = self.libvirtconnection._lvm_disks(instance)
self.assertEqual(['/dev/vols/fake-uuid_foo',
'/dev/vols/instance-00000001_bar'], disks)
class LibvirtVolumeUsageTestCase(test.TestCase):
"""Test for LibvirtDriver.get_all_volume_usage."""

View File

@ -1077,7 +1077,22 @@ class LibvirtDriver(driver.ComputeDriver):
vg = os.path.join('/dev', CONF.libvirt.images_volume_group)
if not os.path.exists(vg):
return []
pattern = '%s_' % instance['name']
pattern = '%s_' % instance['uuid']
# TODO(sdague): remove in Juno
def belongs_to_instance_legacy(disk):
# We don't want to leak old disks, but at the same time, we
# don't want to do an unsafe thing. So we will only handle
# the old filter if it's the system default still.
pattern = '%s_' % instance['name']
if disk.startswith(pattern):
if CONF.instance_name_template == 'instance-%08x':
return True
else:
LOG.warning(_('Volume %(disk)s possibly unsafe to '
'remove, please clean up manually'),
{'disk': disk})
return False
def belongs_to_instance(disk):
return disk.startswith(pattern)
@ -1088,6 +1103,10 @@ class LibvirtDriver(driver.ComputeDriver):
logical_volumes = libvirt_utils.list_logical_volumes(vg)
disk_names = filter(belongs_to_instance, logical_volumes)
# TODO(sdague): remove in Juno
disk_names.extend(
filter(belongs_to_instance_legacy, logical_volumes)
)
disks = map(fullpath, disk_names)
return disks
return []

View File

@ -361,7 +361,7 @@ class Lvm(Image):
' images_volume_group'
' flag to use LVM images.'))
self.vg = CONF.libvirt.images_volume_group
self.lv = '%s_%s' % (self.escape(instance['name']),
self.lv = '%s_%s' % (instance['uuid'],
self.escape(disk_name))
self.path = os.path.join('/dev', self.vg, self.lv)