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)
|
instance_name)
|
||||||
|
|
||||||
@mock.patch('os.path.exists')
|
@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):
|
def test_lvm_disks(self, listlvs, exists):
|
||||||
instance = instance_obj.Instance(uuid='fake-uuid',
|
instance = instance_obj.Instance(uuid='fake-uuid',
|
||||||
id=1)
|
id=1)
|
||||||
|
|||||||
@@ -467,12 +467,12 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
|
|
||||||
self.disk = imagebackend.disk
|
self.disk = imagebackend.disk
|
||||||
self.utils = imagebackend.utils
|
self.utils = imagebackend.utils
|
||||||
self.libvirt_utils = imagebackend.libvirt_utils
|
self.lvm = imagebackend.lvm
|
||||||
|
|
||||||
def prepare_mocks(self):
|
def prepare_mocks(self):
|
||||||
fn = self.mox.CreateMockAnything()
|
fn = self.mox.CreateMockAnything()
|
||||||
self.mox.StubOutWithMock(self.disk, 'resize2fs')
|
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.disk, 'get_disk_size')
|
||||||
self.mox.StubOutWithMock(self.utils, 'execute')
|
self.mox.StubOutWithMock(self.utils, 'execute')
|
||||||
return fn
|
return fn
|
||||||
@@ -480,10 +480,10 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
def _create_image(self, sparse):
|
def _create_image(self, sparse):
|
||||||
fn = self.prepare_mocks()
|
fn = self.prepare_mocks()
|
||||||
fn(max_size=None, target=self.TEMPLATE_PATH)
|
fn(max_size=None, target=self.TEMPLATE_PATH)
|
||||||
self.libvirt_utils.create_lvm_image(self.VG,
|
self.lvm.create_volume(self.VG,
|
||||||
self.LV,
|
self.LV,
|
||||||
self.TEMPLATE_SIZE,
|
self.TEMPLATE_SIZE,
|
||||||
sparse=sparse)
|
sparse=sparse)
|
||||||
self.disk.get_disk_size(self.TEMPLATE_PATH
|
self.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
).AndReturn(self.TEMPLATE_SIZE)
|
).AndReturn(self.TEMPLATE_SIZE)
|
||||||
cmd = ('qemu-img', 'convert', '-O', 'raw', self.TEMPLATE_PATH,
|
cmd = ('qemu-img', 'convert', '-O', 'raw', self.TEMPLATE_PATH,
|
||||||
@@ -498,8 +498,8 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
|
|
||||||
def _create_image_generated(self, sparse):
|
def _create_image_generated(self, sparse):
|
||||||
fn = self.prepare_mocks()
|
fn = self.prepare_mocks()
|
||||||
self.libvirt_utils.create_lvm_image(self.VG, self.LV,
|
self.lvm.create_volume(self.VG, self.LV,
|
||||||
self.SIZE, sparse=sparse)
|
self.SIZE, sparse=sparse)
|
||||||
fn(target=self.PATH, ephemeral_size=None)
|
fn(target=self.PATH, ephemeral_size=None)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
@@ -512,8 +512,8 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
def _create_image_resize(self, sparse):
|
def _create_image_resize(self, sparse):
|
||||||
fn = self.prepare_mocks()
|
fn = self.prepare_mocks()
|
||||||
fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
self.libvirt_utils.create_lvm_image(self.VG, self.LV,
|
self.lvm.create_volume(self.VG, self.LV,
|
||||||
self.SIZE, sparse=sparse)
|
self.SIZE, sparse=sparse)
|
||||||
self.disk.get_disk_size(self.TEMPLATE_PATH
|
self.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
).AndReturn(self.TEMPLATE_SIZE)
|
).AndReturn(self.TEMPLATE_SIZE)
|
||||||
cmd = ('qemu-img', 'convert', '-O', 'raw', self.TEMPLATE_PATH,
|
cmd = ('qemu-img', 'convert', '-O', 'raw', self.TEMPLATE_PATH,
|
||||||
@@ -601,15 +601,15 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
def test_create_image_negative(self):
|
def test_create_image_negative(self):
|
||||||
fn = self.prepare_mocks()
|
fn = self.prepare_mocks()
|
||||||
fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
fn(max_size=self.SIZE, target=self.TEMPLATE_PATH)
|
||||||
self.libvirt_utils.create_lvm_image(self.VG,
|
self.lvm.create_volume(self.VG,
|
||||||
self.LV,
|
self.LV,
|
||||||
self.SIZE,
|
self.SIZE,
|
||||||
sparse=False
|
sparse=False
|
||||||
).AndRaise(RuntimeError())
|
).AndRaise(RuntimeError())
|
||||||
self.disk.get_disk_size(self.TEMPLATE_PATH
|
self.disk.get_disk_size(self.TEMPLATE_PATH
|
||||||
).AndReturn(self.TEMPLATE_SIZE)
|
).AndReturn(self.TEMPLATE_SIZE)
|
||||||
self.mox.StubOutWithMock(self.libvirt_utils, 'remove_logical_volumes')
|
self.mox.StubOutWithMock(self.lvm, 'remove_volumes')
|
||||||
self.libvirt_utils.remove_logical_volumes(self.PATH)
|
self.lvm.remove_volumes(self.PATH)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
image = self.image_class(self.INSTANCE, self.NAME)
|
image = self.image_class(self.INSTANCE, self.NAME)
|
||||||
@@ -622,12 +622,12 @@ class LvmTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
fn = self.prepare_mocks()
|
fn = self.prepare_mocks()
|
||||||
fn(target=self.PATH,
|
fn(target=self.PATH,
|
||||||
ephemeral_size=None).AndRaise(RuntimeError())
|
ephemeral_size=None).AndRaise(RuntimeError())
|
||||||
self.libvirt_utils.create_lvm_image(self.VG,
|
self.lvm.create_volume(self.VG,
|
||||||
self.LV,
|
self.LV,
|
||||||
self.SIZE,
|
self.SIZE,
|
||||||
sparse=False)
|
sparse=False)
|
||||||
self.mox.StubOutWithMock(self.libvirt_utils, 'remove_logical_volumes')
|
self.mox.StubOutWithMock(self.lvm, 'remove_volumes')
|
||||||
self.libvirt_utils.remove_logical_volumes(self.PATH)
|
self.lvm.remove_volumes(self.PATH)
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
image = self.image_class(self.INSTANCE, self.NAME)
|
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
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import functools
|
import functools
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova import exception
|
|
||||||
from nova.openstack.common import processutils
|
from nova.openstack.common import processutils
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova import utils
|
from nova import utils
|
||||||
@@ -49,130 +47,6 @@ blah BLAH: bb
|
|||||||
disk_type = libvirt_utils.get_disk_type(path)
|
disk_type = libvirt_utils.get_disk_type(path)
|
||||||
self.assertEqual(disk_type, 'raw')
|
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):
|
def test_list_rbd_volumes(self):
|
||||||
conf = '/etc/ceph/fake_ceph.conf'
|
conf = '/etc/ceph/fake_ceph.conf'
|
||||||
pool = 'fake_pool'
|
pool = 'fake_pool'
|
||||||
@@ -231,21 +105,6 @@ blah BLAH: bb
|
|||||||
])
|
])
|
||||||
self.assertEqual(2, mock_execute.call_count)
|
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')
|
@mock.patch('nova.utils.execute')
|
||||||
def test_copy_image_scp(self, mock_execute):
|
def test_copy_image_scp(self, mock_execute):
|
||||||
mock_execute.side_effect = [
|
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 firewall as libvirt_firewall
|
||||||
from nova.virt.libvirt import imagebackend
|
from nova.virt.libvirt import imagebackend
|
||||||
from nova.virt.libvirt import imagecache
|
from nova.virt.libvirt import imagecache
|
||||||
|
from nova.virt.libvirt import lvm
|
||||||
from nova.virt.libvirt import utils as libvirt_utils
|
from nova.virt.libvirt import utils as libvirt_utils
|
||||||
from nova.virt import netutils
|
from nova.virt import netutils
|
||||||
from nova.virt import watchdog_actions
|
from nova.virt import watchdog_actions
|
||||||
@@ -1047,7 +1048,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
"""Delete all LVM disks for given instance object."""
|
"""Delete all LVM disks for given instance object."""
|
||||||
disks = self._lvm_disks(instance)
|
disks = self._lvm_disks(instance)
|
||||||
if disks:
|
if disks:
|
||||||
libvirt_utils.remove_logical_volumes(disks)
|
lvm.remove_volumes(disks)
|
||||||
|
|
||||||
def _lvm_disks(self, instance):
|
def _lvm_disks(self, instance):
|
||||||
"""Returns all LVM disks for given instance object."""
|
"""Returns all LVM disks for given instance object."""
|
||||||
@@ -1078,7 +1079,7 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
def fullpath(name):
|
def fullpath(name):
|
||||||
return os.path.join(vg, 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)
|
disk_names = filter(belongs_to_instance, logical_volumes)
|
||||||
# TODO(sdague): remove in Juno
|
# TODO(sdague): remove in Juno
|
||||||
@@ -3794,8 +3795,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if CONF.libvirt.images_type == 'lvm':
|
if CONF.libvirt.images_type == 'lvm':
|
||||||
info = libvirt_utils.get_volume_group_info(
|
info = lvm.get_volume_group_info(
|
||||||
CONF.libvirt.images_volume_group)
|
CONF.libvirt.images_volume_group)
|
||||||
else:
|
else:
|
||||||
info = libvirt_utils.get_fs_info(CONF.instances_path)
|
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.disk import api as disk
|
||||||
from nova.virt import images
|
from nova.virt import images
|
||||||
from nova.virt.libvirt import config as vconfig
|
from nova.virt.libvirt import config as vconfig
|
||||||
|
from nova.virt.libvirt import lvm
|
||||||
from nova.virt.libvirt import utils as libvirt_utils
|
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)
|
super(Lvm, self).__init__("block", "raw", is_block_dev=True)
|
||||||
|
|
||||||
if path:
|
if path:
|
||||||
info = libvirt_utils.logical_volume_info(path)
|
info = lvm.volume_info(path)
|
||||||
self.vg = info['VG']
|
self.vg = info['VG']
|
||||||
self.lv = info['LV']
|
self.lv = info['LV']
|
||||||
self.path = path
|
self.path = path
|
||||||
@@ -449,8 +450,8 @@ class Lvm(Image):
|
|||||||
self.verify_base_size(base, size, base_size=base_size)
|
self.verify_base_size(base, size, base_size=base_size)
|
||||||
resize = size > base_size
|
resize = size > base_size
|
||||||
size = size if resize else base_size
|
size = size if resize else base_size
|
||||||
libvirt_utils.create_lvm_image(self.vg, self.lv,
|
lvm.create_volume(self.vg, self.lv,
|
||||||
size, sparse=self.sparse)
|
size, sparse=self.sparse)
|
||||||
images.convert_image(base, self.path, 'raw', run_as_root=True)
|
images.convert_image(base, self.path, 'raw', run_as_root=True)
|
||||||
if resize:
|
if resize:
|
||||||
disk.resize2fs(self.path, run_as_root=True)
|
disk.resize2fs(self.path, run_as_root=True)
|
||||||
@@ -459,8 +460,8 @@ class Lvm(Image):
|
|||||||
|
|
||||||
#Generate images with specified size right on volume
|
#Generate images with specified size right on volume
|
||||||
if generated and size:
|
if generated and size:
|
||||||
libvirt_utils.create_lvm_image(self.vg, self.lv,
|
lvm.create_volume(self.vg, self.lv,
|
||||||
size, sparse=self.sparse)
|
size, sparse=self.sparse)
|
||||||
with self.remove_volume_on_error(self.path):
|
with self.remove_volume_on_error(self.path):
|
||||||
prepare_template(target=self.path, *args, **kwargs)
|
prepare_template(target=self.path, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
@@ -475,7 +476,7 @@ class Lvm(Image):
|
|||||||
yield
|
yield
|
||||||
except Exception:
|
except Exception:
|
||||||
with excutils.save_and_reraise_exception():
|
with excutils.save_and_reraise_exception():
|
||||||
libvirt_utils.remove_logical_volumes(path)
|
lvm.remove_volumes(path)
|
||||||
|
|
||||||
def snapshot_extract(self, target, out_format):
|
def snapshot_extract(self, target, out_format):
|
||||||
images.convert_image(self.path, 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 lxml import etree
|
||||||
from oslo.config import cfg
|
from oslo.config import cfg
|
||||||
|
|
||||||
from nova import exception
|
|
||||||
from nova.openstack.common.gettextutils import _
|
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 _LI
|
||||||
from nova.openstack.common.gettextutils import _LW
|
from nova.openstack.common.gettextutils import _LW
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
from nova.openstack.common import processutils
|
from nova.openstack.common import processutils
|
||||||
from nova.openstack.common import units
|
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.virt import images
|
from nova.virt import images
|
||||||
from nova.virt import volumeutils
|
from nova.virt import volumeutils
|
||||||
@@ -206,52 +203,6 @@ def create_cow_image(backing_file, path, size=None):
|
|||||||
execute(*cmd)
|
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):
|
def import_rbd_image(*args):
|
||||||
execute('rbd', 'import', *args)
|
execute('rbd', 'import', *args)
|
||||||
|
|
||||||
@@ -292,146 +243,6 @@ def remove_rbd_volumes(pool, *names):
|
|||||||
{'name': name, 'pool': pool})
|
{'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):
|
def pick_disk_driver_name(hypervisor_version, is_block_dev=False):
|
||||||
"""Pick the libvirt primary backend driver name
|
"""Pick the libvirt primary backend driver name
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user