Merge "Create lvm.py module containing helper API for LVM"
This commit is contained in:
@@ -8972,7 +8972,7 @@ class LibvirtDriverTestCase(test.TestCase):
|
||||
instance_name)
|
||||
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('nova.virt.libvirt.utils.list_logical_volumes')
|
||||
@mock.patch('nova.virt.libvirt.lvm.list_volumes')
|
||||
def test_lvm_disks(self, listlvs, exists):
|
||||
instance = instance_obj.Instance(uuid='fake-uuid',
|
||||
id=1)
|
||||
|
||||
@@ -467,12 +467,12 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
|
||||
self.disk = imagebackend.disk
|
||||
self.utils = imagebackend.utils
|
||||
self.libvirt_utils = imagebackend.libvirt_utils
|
||||
self.lvm = imagebackend.lvm
|
||||
|
||||
def prepare_mocks(self):
|
||||
fn = self.mox.CreateMockAnything()
|
||||
self.mox.StubOutWithMock(self.disk, 'resize2fs')
|
||||
self.mox.StubOutWithMock(self.libvirt_utils, 'create_lvm_image')
|
||||
self.mox.StubOutWithMock(self.lvm, 'create_volume')
|
||||
self.mox.StubOutWithMock(self.disk, 'get_disk_size')
|
||||
self.mox.StubOutWithMock(self.utils, 'execute')
|
||||
return fn
|
||||
@@ -480,10 +480,10 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
def _create_image(self, sparse):
|
||||
fn = self.prepare_mocks()
|
||||
fn(max_size=None, target=self.TEMPLATE_PATH)
|
||||
self.libvirt_utils.create_lvm_image(self.VG,
|
||||
self.LV,
|
||||
self.TEMPLATE_SIZE,
|
||||
sparse=sparse)
|
||||
self.lvm.create_volume(self.VG,
|
||||
self.LV,
|
||||
self.TEMPLATE_SIZE,
|
||||
sparse=sparse)
|
||||
self.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
).AndReturn(self.TEMPLATE_SIZE)
|
||||
cmd = ('qemu-img', 'convert', '-O', 'raw', self.TEMPLATE_PATH,
|
||||
@@ -498,8 +498,8 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
|
||||
def _create_image_generated(self, sparse):
|
||||
fn = self.prepare_mocks()
|
||||
self.libvirt_utils.create_lvm_image(self.VG, self.LV,
|
||||
self.SIZE, sparse=sparse)
|
||||
self.lvm.create_volume(self.VG, self.LV,
|
||||
self.SIZE, sparse=sparse)
|
||||
fn(target=self.PATH, ephemeral_size=None)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
@@ -512,8 +512,8 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
def _create_image_resize(self, sparse):
|
||||
fn = self.prepare_mocks()
|
||||
fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||
self.libvirt_utils.create_lvm_image(self.VG, self.LV,
|
||||
self.SIZE, sparse=sparse)
|
||||
self.lvm.create_volume(self.VG, self.LV,
|
||||
self.SIZE, sparse=sparse)
|
||||
self.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
).AndReturn(self.TEMPLATE_SIZE)
|
||||
cmd = ('qemu-img', 'convert', '-O', 'raw', self.TEMPLATE_PATH,
|
||||
@@ -601,15 +601,15 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
def test_create_image_negative(self):
|
||||
fn = self.prepare_mocks()
|
||||
fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||
self.libvirt_utils.create_lvm_image(self.VG,
|
||||
self.LV,
|
||||
self.SIZE,
|
||||
sparse=False
|
||||
).AndRaise(RuntimeError())
|
||||
self.lvm.create_volume(self.VG,
|
||||
self.LV,
|
||||
self.SIZE,
|
||||
sparse=False
|
||||
).AndRaise(RuntimeError())
|
||||
self.disk.get_disk_size(self.TEMPLATE_PATH
|
||||
).AndReturn(self.TEMPLATE_SIZE)
|
||||
self.mox.StubOutWithMock(self.libvirt_utils, 'remove_logical_volumes')
|
||||
self.libvirt_utils.remove_logical_volumes(self.PATH)
|
||||
self.mox.StubOutWithMock(self.lvm, 'remove_volumes')
|
||||
self.lvm.remove_volumes(self.PATH)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
image = self.image_class(self.INSTANCE, self.NAME)
|
||||
@@ -622,12 +622,12 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
||||
fn = self.prepare_mocks()
|
||||
fn(target=self.PATH,
|
||||
ephemeral_size=None).AndRaise(RuntimeError())
|
||||
self.libvirt_utils.create_lvm_image(self.VG,
|
||||
self.LV,
|
||||
self.SIZE,
|
||||
sparse=False)
|
||||
self.mox.StubOutWithMock(self.libvirt_utils, 'remove_logical_volumes')
|
||||
self.libvirt_utils.remove_logical_volumes(self.PATH)
|
||||
self.lvm.create_volume(self.VG,
|
||||
self.LV,
|
||||
self.SIZE,
|
||||
sparse=False)
|
||||
self.mox.StubOutWithMock(self.lvm, 'remove_volumes')
|
||||
self.lvm.remove_volumes(self.PATH)
|
||||
self.mox.ReplayAll()
|
||||
|
||||
image = self.image_class(self.INSTANCE, self.NAME)
|
||||
|
||||
168
nova/tests/virt/libvirt/test_lvm.py
Normal file
168
nova/tests/virt/libvirt/test_lvm.py
Normal file
@@ -0,0 +1,168 @@
|
||||
# Copyright 2012 NTT Data. All Rights Reserved.
|
||||
# Copyright 2012 Yahoo! Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import contextlib
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common import processutils
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.virt.libvirt import lvm
|
||||
from nova.virt.libvirt import utils as libvirt_utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class LvmTestCase(test.NoDBTestCase):
|
||||
def test_get_volume_size(self):
|
||||
executes = []
|
||||
|
||||
def fake_execute(*cmd, **kwargs):
|
||||
executes.append(cmd)
|
||||
return 123456789, None
|
||||
|
||||
expected_commands = [('blockdev', '--getsize64', '/dev/foo')]
|
||||
self.stubs.Set(utils, 'execute', fake_execute)
|
||||
size = lvm.get_volume_size('/dev/foo')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
self.assertEqual(size, 123456789)
|
||||
|
||||
def test_lvm_clear(self):
|
||||
def fake_lvm_size(path):
|
||||
return lvm_size
|
||||
|
||||
def fake_execute(*cmd, **kwargs):
|
||||
executes.append(cmd)
|
||||
|
||||
self.stubs.Set(lvm, 'get_volume_size', fake_lvm_size)
|
||||
self.stubs.Set(utils, 'execute', fake_execute)
|
||||
|
||||
# Test the correct dd commands are run for various sizes
|
||||
lvm_size = 1
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/v1',
|
||||
'seek=0', 'count=1', 'conv=fdatasync')]
|
||||
lvm.clear_volume('/dev/v1')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1024
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1024', 'if=/dev/zero', 'of=/dev/v2',
|
||||
'seek=0', 'count=1', 'conv=fdatasync')]
|
||||
lvm.clear_volume('/dev/v2')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1025
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1024', 'if=/dev/zero', 'of=/dev/v3',
|
||||
'seek=0', 'count=1', 'conv=fdatasync')]
|
||||
expected_commands += [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/v3',
|
||||
'seek=1024', 'count=1', 'conv=fdatasync')]
|
||||
lvm.clear_volume('/dev/v3')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1048576
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v4',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
lvm.clear_volume('/dev/v4')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1048577
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v5',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
expected_commands += [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/v5',
|
||||
'seek=1048576', 'count=1', 'conv=fdatasync')]
|
||||
lvm.clear_volume('/dev/v5')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1234567
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v6',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
expected_commands += [('dd', 'bs=1024', 'if=/dev/zero', 'of=/dev/v6',
|
||||
'seek=1024', 'count=181', 'conv=fdatasync')]
|
||||
expected_commands += [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/v6',
|
||||
'seek=1233920', 'count=647', 'conv=fdatasync')]
|
||||
lvm.clear_volume('/dev/v6')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
# Test volume_clear_size limits the size
|
||||
lvm_size = 10485761
|
||||
CONF.set_override('volume_clear_size', '1', 'libvirt')
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v7',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
lvm.clear_volume('/dev/v7')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
CONF.set_override('volume_clear_size', '2', 'libvirt')
|
||||
lvm_size = 1048576
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v9',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
lvm.clear_volume('/dev/v9')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
# Test volume_clear=shred
|
||||
CONF.set_override('volume_clear', 'shred', 'libvirt')
|
||||
CONF.set_override('volume_clear_size', '0', 'libvirt')
|
||||
lvm_size = 1048576
|
||||
executes = []
|
||||
expected_commands = [('shred', '-n3', '-s1048576', '/dev/va')]
|
||||
lvm.clear_volume('/dev/va')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
CONF.set_override('volume_clear', 'shred', 'libvirt')
|
||||
CONF.set_override('volume_clear_size', '1', 'libvirt')
|
||||
lvm_size = 10485761
|
||||
executes = []
|
||||
expected_commands = [('shred', '-n3', '-s1048576', '/dev/vb')]
|
||||
lvm.clear_volume('/dev/vb')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
# Test volume_clear=none does nothing
|
||||
CONF.set_override('volume_clear', 'none', 'libvirt')
|
||||
executes = []
|
||||
expected_commands = []
|
||||
lvm.clear_volume('/dev/vc')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
# Test volume_clear=invalid falls back to the default 'zero'
|
||||
CONF.set_override('volume_clear', 'invalid', 'libvirt')
|
||||
lvm_size = 1
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/vd',
|
||||
'seek=0', 'count=1', 'conv=fdatasync')]
|
||||
lvm.clear_volume('/dev/vd')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
def test_fail_remove_all_logical_volumes(self):
|
||||
def fake_execute(*args, **kwargs):
|
||||
if 'vol2' in args:
|
||||
raise processutils.ProcessExecutionError('Error')
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(lvm, 'clear_volume'),
|
||||
mock.patch.object(libvirt_utils, 'execute',
|
||||
side_effect=fake_execute)) as (mock_clear, mock_execute):
|
||||
self.assertRaises(exception.VolumesNotRemoved,
|
||||
lvm.remove_volumes,
|
||||
['vol1', 'vol2', 'vol3'])
|
||||
self.assertEqual(3, mock_execute.call_count)
|
||||
@@ -13,14 +13,12 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import contextlib
|
||||
import functools
|
||||
import os
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common import processutils
|
||||
from nova import test
|
||||
from nova import utils
|
||||
@@ -49,130 +47,6 @@ blah BLAH: bb
|
||||
disk_type = libvirt_utils.get_disk_type(path)
|
||||
self.assertEqual(disk_type, 'raw')
|
||||
|
||||
def test_logical_volume_size(self):
|
||||
executes = []
|
||||
|
||||
def fake_execute(*cmd, **kwargs):
|
||||
executes.append(cmd)
|
||||
return 123456789, None
|
||||
|
||||
expected_commands = [('blockdev', '--getsize64', '/dev/foo')]
|
||||
self.stubs.Set(utils, 'execute', fake_execute)
|
||||
size = libvirt_utils.logical_volume_size('/dev/foo')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
self.assertEqual(size, 123456789)
|
||||
|
||||
def test_lvm_clear(self):
|
||||
def fake_lvm_size(path):
|
||||
return lvm_size
|
||||
|
||||
def fake_execute(*cmd, **kwargs):
|
||||
executes.append(cmd)
|
||||
|
||||
self.stubs.Set(libvirt_utils, 'logical_volume_size', fake_lvm_size)
|
||||
self.stubs.Set(utils, 'execute', fake_execute)
|
||||
|
||||
# Test the correct dd commands are run for various sizes
|
||||
lvm_size = 1
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/v1',
|
||||
'seek=0', 'count=1', 'conv=fdatasync')]
|
||||
libvirt_utils.clear_logical_volume('/dev/v1')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1024
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1024', 'if=/dev/zero', 'of=/dev/v2',
|
||||
'seek=0', 'count=1', 'conv=fdatasync')]
|
||||
libvirt_utils.clear_logical_volume('/dev/v2')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1025
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1024', 'if=/dev/zero', 'of=/dev/v3',
|
||||
'seek=0', 'count=1', 'conv=fdatasync')]
|
||||
expected_commands += [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/v3',
|
||||
'seek=1024', 'count=1', 'conv=fdatasync')]
|
||||
libvirt_utils.clear_logical_volume('/dev/v3')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1048576
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v4',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
libvirt_utils.clear_logical_volume('/dev/v4')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1048577
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v5',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
expected_commands += [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/v5',
|
||||
'seek=1048576', 'count=1', 'conv=fdatasync')]
|
||||
libvirt_utils.clear_logical_volume('/dev/v5')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
lvm_size = 1234567
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v6',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
expected_commands += [('dd', 'bs=1024', 'if=/dev/zero', 'of=/dev/v6',
|
||||
'seek=1024', 'count=181', 'conv=fdatasync')]
|
||||
expected_commands += [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/v6',
|
||||
'seek=1233920', 'count=647', 'conv=fdatasync')]
|
||||
libvirt_utils.clear_logical_volume('/dev/v6')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
# Test volume_clear_size limits the size
|
||||
lvm_size = 10485761
|
||||
CONF.set_override('volume_clear_size', '1', 'libvirt')
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v7',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
libvirt_utils.clear_logical_volume('/dev/v7')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
CONF.set_override('volume_clear_size', '2', 'libvirt')
|
||||
lvm_size = 1048576
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1048576', 'if=/dev/zero', 'of=/dev/v9',
|
||||
'seek=0', 'count=1', 'oflag=direct')]
|
||||
libvirt_utils.clear_logical_volume('/dev/v9')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
# Test volume_clear=shred
|
||||
CONF.set_override('volume_clear', 'shred', 'libvirt')
|
||||
CONF.set_override('volume_clear_size', '0', 'libvirt')
|
||||
lvm_size = 1048576
|
||||
executes = []
|
||||
expected_commands = [('shred', '-n3', '-s1048576', '/dev/va')]
|
||||
libvirt_utils.clear_logical_volume('/dev/va')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
CONF.set_override('volume_clear', 'shred', 'libvirt')
|
||||
CONF.set_override('volume_clear_size', '1', 'libvirt')
|
||||
lvm_size = 10485761
|
||||
executes = []
|
||||
expected_commands = [('shred', '-n3', '-s1048576', '/dev/vb')]
|
||||
libvirt_utils.clear_logical_volume('/dev/vb')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
# Test volume_clear=none does nothing
|
||||
CONF.set_override('volume_clear', 'none', 'libvirt')
|
||||
executes = []
|
||||
expected_commands = []
|
||||
libvirt_utils.clear_logical_volume('/dev/vc')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
# Test volume_clear=invalid falls back to the default 'zero'
|
||||
CONF.set_override('volume_clear', 'invalid', 'libvirt')
|
||||
lvm_size = 1
|
||||
executes = []
|
||||
expected_commands = [('dd', 'bs=1', 'if=/dev/zero', 'of=/dev/vd',
|
||||
'seek=0', 'count=1', 'conv=fdatasync')]
|
||||
libvirt_utils.clear_logical_volume('/dev/vd')
|
||||
self.assertEqual(expected_commands, executes)
|
||||
|
||||
def test_list_rbd_volumes(self):
|
||||
conf = '/etc/ceph/fake_ceph.conf'
|
||||
pool = 'fake_pool'
|
||||
@@ -231,21 +105,6 @@ blah BLAH: bb
|
||||
])
|
||||
self.assertEqual(2, mock_execute.call_count)
|
||||
|
||||
def test_fail_remove_all_logical_volumes(self):
|
||||
|
||||
def fake_execute(*args, **kwargs):
|
||||
if 'vol2' in args:
|
||||
raise processutils.ProcessExecutionError('Error')
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(libvirt_utils, 'clear_logical_volume'),
|
||||
mock.patch.object(libvirt_utils, 'execute',
|
||||
side_effect=fake_execute)) as (mock_clear, mock_execute):
|
||||
self.assertRaises(exception.VolumesNotRemoved,
|
||||
libvirt_utils.remove_logical_volumes,
|
||||
['vol1', 'vol2', 'vol3'])
|
||||
self.assertEqual(3, mock_execute.call_count)
|
||||
|
||||
@mock.patch('nova.utils.execute')
|
||||
def test_copy_image_scp(self, mock_execute):
|
||||
mock_execute.side_effect = [
|
||||
|
||||
@@ -90,6 +90,7 @@ from nova.virt.libvirt import config as vconfig
|
||||
from nova.virt.libvirt import firewall as libvirt_firewall
|
||||
from nova.virt.libvirt import imagebackend
|
||||
from nova.virt.libvirt import imagecache
|
||||
from nova.virt.libvirt import lvm
|
||||
from nova.virt.libvirt import utils as libvirt_utils
|
||||
from nova.virt import netutils
|
||||
from nova.virt import watchdog_actions
|
||||
@@ -1047,7 +1048,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
"""Delete all LVM disks for given instance object."""
|
||||
disks = self._lvm_disks(instance)
|
||||
if disks:
|
||||
libvirt_utils.remove_logical_volumes(disks)
|
||||
lvm.remove_volumes(disks)
|
||||
|
||||
def _lvm_disks(self, instance):
|
||||
"""Returns all LVM disks for given instance object."""
|
||||
@@ -1078,7 +1079,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
def fullpath(name):
|
||||
return os.path.join(vg, name)
|
||||
|
||||
logical_volumes = libvirt_utils.list_logical_volumes(vg)
|
||||
logical_volumes = lvm.list_volumes(vg)
|
||||
|
||||
disk_names = filter(belongs_to_instance, logical_volumes)
|
||||
# TODO(sdague): remove in Juno
|
||||
@@ -3794,8 +3795,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
"""
|
||||
|
||||
if CONF.libvirt.images_type == 'lvm':
|
||||
info = libvirt_utils.get_volume_group_info(
|
||||
CONF.libvirt.images_volume_group)
|
||||
info = lvm.get_volume_group_info(
|
||||
CONF.libvirt.images_volume_group)
|
||||
else:
|
||||
info = libvirt_utils.get_fs_info(CONF.instances_path)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ from nova import utils
|
||||
from nova.virt.disk import api as disk
|
||||
from nova.virt import images
|
||||
from nova.virt.libvirt import config as vconfig
|
||||
from nova.virt.libvirt import lvm
|
||||
from nova.virt.libvirt import utils as libvirt_utils
|
||||
|
||||
|
||||
@@ -418,7 +419,7 @@ class Lvm(Image):
|
||||
super(Lvm, self).__init__("block", "raw", is_block_dev=True)
|
||||
|
||||
if path:
|
||||
info = libvirt_utils.logical_volume_info(path)
|
||||
info = lvm.volume_info(path)
|
||||
self.vg = info['VG']
|
||||
self.lv = info['LV']
|
||||
self.path = path
|
||||
@@ -449,8 +450,8 @@ class Lvm(Image):
|
||||
self.verify_base_size(base, size, base_size=base_size)
|
||||
resize = size > base_size
|
||||
size = size if resize else base_size
|
||||
libvirt_utils.create_lvm_image(self.vg, self.lv,
|
||||
size, sparse=self.sparse)
|
||||
lvm.create_volume(self.vg, self.lv,
|
||||
size, sparse=self.sparse)
|
||||
images.convert_image(base, self.path, 'raw', run_as_root=True)
|
||||
if resize:
|
||||
disk.resize2fs(self.path, run_as_root=True)
|
||||
@@ -459,8 +460,8 @@ class Lvm(Image):
|
||||
|
||||
#Generate images with specified size right on volume
|
||||
if generated and size:
|
||||
libvirt_utils.create_lvm_image(self.vg, self.lv,
|
||||
size, sparse=self.sparse)
|
||||
lvm.create_volume(self.vg, self.lv,
|
||||
size, sparse=self.sparse)
|
||||
with self.remove_volume_on_error(self.path):
|
||||
prepare_template(target=self.path, *args, **kwargs)
|
||||
else:
|
||||
@@ -475,7 +476,7 @@ class Lvm(Image):
|
||||
yield
|
||||
except Exception:
|
||||
with excutils.save_and_reraise_exception():
|
||||
libvirt_utils.remove_logical_volumes(path)
|
||||
lvm.remove_volumes(path)
|
||||
|
||||
def snapshot_extract(self, target, out_format):
|
||||
images.convert_image(self.path, target, out_format,
|
||||
|
||||
238
nova/virt/libvirt/lvm.py
Normal file
238
nova/virt/libvirt/lvm.py
Normal file
@@ -0,0 +1,238 @@
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# All Rights Reserved.
|
||||
# Copyright (c) 2010 Citrix Systems, Inc.
|
||||
# Copyright (c) 2011 Piston Cloud Computing, Inc
|
||||
# Copyright (c) 2011 OpenStack Foundation
|
||||
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common.gettextutils import _LE
|
||||
from nova.openstack.common.gettextutils import _LW
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import processutils
|
||||
from nova.openstack.common import units
|
||||
from nova import utils
|
||||
from nova.virt.libvirt import utils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('instances_path', 'nova.compute.manager')
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_volume(vg, lv, size, sparse=False):
|
||||
"""Create LVM image.
|
||||
|
||||
Creates a LVM image with given size.
|
||||
|
||||
:param vg: existing volume group which should hold this image
|
||||
:param lv: name for this image (logical volume)
|
||||
:size: size of image in bytes
|
||||
:sparse: create sparse logical volume
|
||||
"""
|
||||
vg_info = get_volume_group_info(vg)
|
||||
free_space = vg_info['free']
|
||||
|
||||
def check_size(vg, lv, size):
|
||||
if size > free_space:
|
||||
raise RuntimeError(_('Insufficient Space on Volume Group %(vg)s.'
|
||||
' Only %(free_space)db available,'
|
||||
' but %(size)db required'
|
||||
' by volume %(lv)s.') %
|
||||
{'vg': vg,
|
||||
'free_space': free_space,
|
||||
'size': size,
|
||||
'lv': lv})
|
||||
|
||||
if sparse:
|
||||
preallocated_space = 64 * units.Mi
|
||||
check_size(vg, lv, preallocated_space)
|
||||
if free_space < size:
|
||||
LOG.warn(_LW('Volume group %(vg)s will not be able'
|
||||
' to hold sparse volume %(lv)s.'
|
||||
' Virtual volume size is %(size)db,'
|
||||
' but free space on volume group is'
|
||||
' only %(free_space)db.'),
|
||||
{'vg': vg,
|
||||
'free_space': free_space,
|
||||
'size': size,
|
||||
'lv': lv})
|
||||
|
||||
cmd = ('lvcreate', '-L', '%db' % preallocated_space,
|
||||
'--virtualsize', '%db' % size, '-n', lv, vg)
|
||||
else:
|
||||
check_size(vg, lv, size)
|
||||
cmd = ('lvcreate', '-L', '%db' % size, '-n', lv, vg)
|
||||
utils.execute(*cmd, run_as_root=True, attempts=3)
|
||||
|
||||
|
||||
def get_volume_group_info(vg):
|
||||
"""Return free/used/total space info for a volume group in bytes
|
||||
|
||||
:param vg: volume group name
|
||||
:returns: A dict containing:
|
||||
:total: How big the filesystem is (in bytes)
|
||||
:free: How much space is free (in bytes)
|
||||
:used: How much space is used (in bytes)
|
||||
"""
|
||||
|
||||
out, err = utils.execute('vgs', '--noheadings', '--nosuffix',
|
||||
'--separator', '|',
|
||||
'--units', 'b', '-o', 'vg_size,vg_free', vg,
|
||||
run_as_root=True)
|
||||
|
||||
info = out.split('|')
|
||||
if len(info) != 2:
|
||||
raise RuntimeError(_("vg %s must be LVM volume group") % vg)
|
||||
|
||||
return {'total': int(info[0]),
|
||||
'free': int(info[1]),
|
||||
'used': int(info[0]) - int(info[1])}
|
||||
|
||||
|
||||
def list_volumes(vg):
|
||||
"""List logical volumes paths for given volume group.
|
||||
|
||||
:param vg: volume group name
|
||||
:returns: Return a logical volume list for given volume group
|
||||
: Data format example
|
||||
: ['volume-aaa', 'volume-bbb', 'volume-ccc']
|
||||
"""
|
||||
out, err = utils.execute('lvs', '--noheadings', '-o', 'lv_name', vg,
|
||||
run_as_root=True)
|
||||
|
||||
return [line.strip() for line in out.splitlines()]
|
||||
|
||||
|
||||
def volume_info(path):
|
||||
"""Get logical volume info.
|
||||
|
||||
:param path: logical volume path
|
||||
:returns: Return a dict object including info of given logical volume
|
||||
: Data format example
|
||||
: {'#Seg': '1', 'Move': '', 'Log': '', 'Meta%': '', 'Min': '-1',
|
||||
: ...
|
||||
: 'Free': '9983', 'LV': 'volume-aaa', 'Host': 'xyz.com',
|
||||
: 'Active': 'active', 'Path': '/dev/vg/volume-aaa', '#LV': '3',
|
||||
: 'Maj': '-1', 'VSize': '50.00g', 'VFree': '39.00g', 'Pool': '',
|
||||
: 'VG Tags': '', 'KMaj': '253', 'Convert': '', 'LProfile': '',
|
||||
: '#Ext': '12799', 'Attr': '-wi-a-----', 'VG': 'vg',
|
||||
: ...
|
||||
: 'LSize': '1.00g', '#PV': '1', '#VMdaCps': 'unmanaged'}
|
||||
"""
|
||||
out, err = utils.execute('lvs', '-o', 'vg_all,lv_all',
|
||||
'--separator', '|', path, run_as_root=True)
|
||||
|
||||
info = [line.split('|') for line in out.splitlines()]
|
||||
|
||||
if len(info) != 2:
|
||||
raise RuntimeError(_("Path %s must be LVM logical volume") % path)
|
||||
|
||||
return dict(zip(*info))
|
||||
|
||||
|
||||
def get_volume_size(path):
|
||||
"""Get logical volume size in bytes.
|
||||
|
||||
:param path: logical volume path
|
||||
"""
|
||||
out, _err = utils.execute('blockdev', '--getsize64', path,
|
||||
run_as_root=True)
|
||||
|
||||
return int(out)
|
||||
|
||||
|
||||
def _zero_volume(path, volume_size):
|
||||
"""Write zeros over the specified path
|
||||
|
||||
:param path: logical volume path
|
||||
:param size: number of zeros to write
|
||||
"""
|
||||
bs = units.Mi
|
||||
direct_flags = ('oflag=direct',)
|
||||
sync_flags = ()
|
||||
remaining_bytes = volume_size
|
||||
|
||||
# The loop efficiently writes zeros using dd,
|
||||
# and caters for versions of dd that don't have
|
||||
# the easier to use iflag=count_bytes option.
|
||||
while remaining_bytes:
|
||||
zero_blocks = remaining_bytes / bs
|
||||
seek_blocks = (volume_size - remaining_bytes) / bs
|
||||
zero_cmd = ('dd', 'bs=%s' % bs,
|
||||
'if=/dev/zero', 'of=%s' % path,
|
||||
'seek=%s' % seek_blocks, 'count=%s' % zero_blocks)
|
||||
zero_cmd += direct_flags
|
||||
zero_cmd += sync_flags
|
||||
if zero_blocks:
|
||||
utils.execute(*zero_cmd, run_as_root=True)
|
||||
remaining_bytes %= bs
|
||||
bs /= units.Ki # Limit to 3 iterations
|
||||
# Use O_DIRECT with initial block size and fdatasync otherwise
|
||||
direct_flags = ()
|
||||
sync_flags = ('conv=fdatasync',)
|
||||
|
||||
|
||||
def clear_volume(path):
|
||||
"""Obfuscate the logical volume.
|
||||
|
||||
:param path: logical volume path
|
||||
"""
|
||||
volume_clear = CONF.libvirt.volume_clear
|
||||
|
||||
if volume_clear not in ('none', 'shred', 'zero'):
|
||||
LOG.error(_LE("ignoring unrecognized volume_clear='%s' value"),
|
||||
volume_clear)
|
||||
volume_clear = 'zero'
|
||||
|
||||
if volume_clear == 'none':
|
||||
return
|
||||
|
||||
volume_clear_size = int(CONF.libvirt.volume_clear_size) * units.Mi
|
||||
volume_size = get_volume_size(path)
|
||||
|
||||
if volume_clear_size != 0 and volume_clear_size < volume_size:
|
||||
volume_size = volume_clear_size
|
||||
|
||||
if volume_clear == 'zero':
|
||||
# NOTE(p-draigbrady): we could use shred to do the zeroing
|
||||
# with -n0 -z, however only versions >= 8.22 perform as well as dd
|
||||
_zero_volume(path, volume_size)
|
||||
elif volume_clear == 'shred':
|
||||
utils.execute('shred', '-n3', '-s%d' % volume_size, path,
|
||||
run_as_root=True)
|
||||
else:
|
||||
raise exception.Invalid(_("volume_clear='%s' is not handled")
|
||||
% volume_clear)
|
||||
|
||||
|
||||
def remove_volumes(paths):
|
||||
"""Remove one or more logical volume."""
|
||||
|
||||
errors = []
|
||||
for path in paths:
|
||||
clear_volume(path)
|
||||
lvremove = ('lvremove', '-f', path)
|
||||
try:
|
||||
utils.execute(*lvremove, attempts=3, run_as_root=True)
|
||||
except processutils.ProcessExecutionError as exp:
|
||||
errors.append(str(exp))
|
||||
if errors:
|
||||
raise exception.VolumesNotRemoved(reason=(', ').join(errors))
|
||||
@@ -25,14 +25,11 @@ import platform
|
||||
from lxml import etree
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common.gettextutils import _
|
||||
from nova.openstack.common.gettextutils import _LE
|
||||
from nova.openstack.common.gettextutils import _LI
|
||||
from nova.openstack.common.gettextutils import _LW
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import processutils
|
||||
from nova.openstack.common import units
|
||||
from nova import utils
|
||||
from nova.virt import images
|
||||
from nova.virt import volumeutils
|
||||
@@ -206,52 +203,6 @@ def create_cow_image(backing_file, path, size=None):
|
||||
execute(*cmd)
|
||||
|
||||
|
||||
def create_lvm_image(vg, lv, size, sparse=False):
|
||||
"""Create LVM image.
|
||||
|
||||
Creates a LVM image with given size.
|
||||
|
||||
:param vg: existing volume group which should hold this image
|
||||
:param lv: name for this image (logical volume)
|
||||
:size: size of image in bytes
|
||||
:sparse: create sparse logical volume
|
||||
"""
|
||||
vg_info = get_volume_group_info(vg)
|
||||
free_space = vg_info['free']
|
||||
|
||||
def check_size(vg, lv, size):
|
||||
if size > free_space:
|
||||
raise RuntimeError(_('Insufficient Space on Volume Group %(vg)s.'
|
||||
' Only %(free_space)db available,'
|
||||
' but %(size)db required'
|
||||
' by volume %(lv)s.') %
|
||||
{'vg': vg,
|
||||
'free_space': free_space,
|
||||
'size': size,
|
||||
'lv': lv})
|
||||
|
||||
if sparse:
|
||||
preallocated_space = 64 * units.Mi
|
||||
check_size(vg, lv, preallocated_space)
|
||||
if free_space < size:
|
||||
LOG.warn(_LW('Volume group %(vg)s will not be able'
|
||||
' to hold sparse volume %(lv)s.'
|
||||
' Virtual volume size is %(size)db,'
|
||||
' but free space on volume group is'
|
||||
' only %(free_space)db.'),
|
||||
{'vg': vg,
|
||||
'free_space': free_space,
|
||||
'size': size,
|
||||
'lv': lv})
|
||||
|
||||
cmd = ('lvcreate', '-L', '%db' % preallocated_space,
|
||||
'--virtualsize', '%db' % size, '-n', lv, vg)
|
||||
else:
|
||||
check_size(vg, lv, size)
|
||||
cmd = ('lvcreate', '-L', '%db' % size, '-n', lv, vg)
|
||||
execute(*cmd, run_as_root=True, attempts=3)
|
||||
|
||||
|
||||
def import_rbd_image(*args):
|
||||
execute('rbd', 'import', *args)
|
||||
|
||||
@@ -292,146 +243,6 @@ def remove_rbd_volumes(pool, *names):
|
||||
{'name': name, 'pool': pool})
|
||||
|
||||
|
||||
def get_volume_group_info(vg):
|
||||
"""Return free/used/total space info for a volume group in bytes
|
||||
|
||||
:param vg: volume group name
|
||||
:returns: A dict containing:
|
||||
:total: How big the filesystem is (in bytes)
|
||||
:free: How much space is free (in bytes)
|
||||
:used: How much space is used (in bytes)
|
||||
"""
|
||||
|
||||
out, err = execute('vgs', '--noheadings', '--nosuffix',
|
||||
'--separator', '|',
|
||||
'--units', 'b', '-o', 'vg_size,vg_free', vg,
|
||||
run_as_root=True)
|
||||
|
||||
info = out.split('|')
|
||||
if len(info) != 2:
|
||||
raise RuntimeError(_("vg %s must be LVM volume group") % vg)
|
||||
|
||||
return {'total': int(info[0]),
|
||||
'free': int(info[1]),
|
||||
'used': int(info[0]) - int(info[1])}
|
||||
|
||||
|
||||
def list_logical_volumes(vg):
|
||||
"""List logical volumes paths for given volume group.
|
||||
|
||||
:param vg: volume group name
|
||||
"""
|
||||
out, err = execute('lvs', '--noheadings', '-o', 'lv_name', vg,
|
||||
run_as_root=True)
|
||||
|
||||
return [line.strip() for line in out.splitlines()]
|
||||
|
||||
|
||||
def logical_volume_info(path):
|
||||
"""Get logical volume info.
|
||||
|
||||
:param path: logical volume path
|
||||
"""
|
||||
out, err = execute('lvs', '-o', 'vg_all,lv_all',
|
||||
'--separator', '|', path, run_as_root=True)
|
||||
|
||||
info = [line.split('|') for line in out.splitlines()]
|
||||
|
||||
if len(info) != 2:
|
||||
raise RuntimeError(_("Path %s must be LVM logical volume") % path)
|
||||
|
||||
return dict(zip(*info))
|
||||
|
||||
|
||||
def logical_volume_size(path):
|
||||
"""Get logical volume size in bytes.
|
||||
|
||||
:param path: logical volume path
|
||||
"""
|
||||
out, _err = execute('blockdev', '--getsize64', path, run_as_root=True)
|
||||
|
||||
return int(out)
|
||||
|
||||
|
||||
def _zero_logical_volume(path, volume_size):
|
||||
"""Write zeros over the specified path
|
||||
|
||||
:param path: logical volume path
|
||||
:param size: number of zeros to write
|
||||
"""
|
||||
bs = units.Mi
|
||||
direct_flags = ('oflag=direct',)
|
||||
sync_flags = ()
|
||||
remaining_bytes = volume_size
|
||||
|
||||
# The loop efficiently writes zeros using dd,
|
||||
# and caters for versions of dd that don't have
|
||||
# the easier to use iflag=count_bytes option.
|
||||
while remaining_bytes:
|
||||
zero_blocks = remaining_bytes / bs
|
||||
seek_blocks = (volume_size - remaining_bytes) / bs
|
||||
zero_cmd = ('dd', 'bs=%s' % bs,
|
||||
'if=/dev/zero', 'of=%s' % path,
|
||||
'seek=%s' % seek_blocks, 'count=%s' % zero_blocks)
|
||||
zero_cmd += direct_flags
|
||||
zero_cmd += sync_flags
|
||||
if zero_blocks:
|
||||
utils.execute(*zero_cmd, run_as_root=True)
|
||||
remaining_bytes %= bs
|
||||
bs /= units.Ki # Limit to 3 iterations
|
||||
# Use O_DIRECT with initial block size and fdatasync otherwise
|
||||
direct_flags = ()
|
||||
sync_flags = ('conv=fdatasync',)
|
||||
|
||||
|
||||
def clear_logical_volume(path):
|
||||
"""Obfuscate the logical volume.
|
||||
|
||||
:param path: logical volume path
|
||||
"""
|
||||
volume_clear = CONF.libvirt.volume_clear
|
||||
|
||||
if volume_clear not in ('none', 'shred', 'zero'):
|
||||
LOG.error(_LE("ignoring unrecognized volume_clear='%s' value"),
|
||||
volume_clear)
|
||||
volume_clear = 'zero'
|
||||
|
||||
if volume_clear == 'none':
|
||||
return
|
||||
|
||||
volume_clear_size = int(CONF.libvirt.volume_clear_size) * units.Mi
|
||||
volume_size = logical_volume_size(path)
|
||||
|
||||
if volume_clear_size != 0 and volume_clear_size < volume_size:
|
||||
volume_size = volume_clear_size
|
||||
|
||||
if volume_clear == 'zero':
|
||||
# NOTE(p-draigbrady): we could use shred to do the zeroing
|
||||
# with -n0 -z, however only versions >= 8.22 perform as well as dd
|
||||
_zero_logical_volume(path, volume_size)
|
||||
elif volume_clear == 'shred':
|
||||
utils.execute('shred', '-n3', '-s%d' % volume_size, path,
|
||||
run_as_root=True)
|
||||
else:
|
||||
raise exception.Invalid(_("volume_clear='%s' is not handled")
|
||||
% volume_clear)
|
||||
|
||||
|
||||
def remove_logical_volumes(paths):
|
||||
"""Remove one or more logical volume."""
|
||||
|
||||
errors = []
|
||||
for path in paths:
|
||||
clear_logical_volume(path)
|
||||
lvremove = ('lvremove', '-f', path)
|
||||
try:
|
||||
execute(*lvremove, attempts=3, run_as_root=True)
|
||||
except processutils.ProcessExecutionError as exp:
|
||||
errors.append(str(exp))
|
||||
if errors:
|
||||
raise exception.VolumesNotRemoved(reason=(', ').join(errors))
|
||||
|
||||
|
||||
def pick_disk_driver_name(hypervisor_version, is_block_dev=False):
|
||||
"""Pick the libvirt primary backend driver name
|
||||
|
||||
|
||||
Reference in New Issue
Block a user