Merge "Create lvm.py module containing helper API for LVM"

This commit is contained in:
Jenkins
2014-06-20 11:35:48 +00:00
committed by Gerrit Code Review
8 changed files with 442 additions and 364 deletions

View File

@@ -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)

View File

@@ -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)

View 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)

View File

@@ -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 = [

View File

@@ -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)

View File

@@ -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
View 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))

View File

@@ -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