
Stop testing for presence of - lv activation support (added in 2.02.91) - thin provisioning (added in 2.02.95) - --ignoreactivationskip (added in 2.02.99) 2.02.99 was released in 2013. Change-Id: I98bbe898bb1e75aa519dddfd44546fe9d477392f
393 lines
16 KiB
Python
393 lines
16 KiB
Python
# Copyright 2012 OpenStack Foundation
|
|
# 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.
|
|
from unittest import mock
|
|
|
|
from oslo_concurrency import processutils
|
|
|
|
from os_brick import exception
|
|
from os_brick import executor as os_brick_executor
|
|
from os_brick.local_dev import lvm as brick
|
|
from os_brick.privileged import rootwrap as priv_rootwrap
|
|
from os_brick.tests import base
|
|
|
|
|
|
class BrickLvmTestCase(base.TestCase):
|
|
|
|
def setUp(self):
|
|
super(BrickLvmTestCase, self).setUp()
|
|
if not hasattr(self, 'configuration'):
|
|
self.configuration = mock.Mock()
|
|
self.configuration.lvm_suppress_fd_warnings = False
|
|
self.volume_group_name = 'fake-vg'
|
|
|
|
# Stub processutils.execute for static methods
|
|
self.mock_object(priv_rootwrap, 'execute',
|
|
self.fake_execute)
|
|
self.vg = brick.LVM(
|
|
self.volume_group_name,
|
|
'sudo',
|
|
create_vg=False,
|
|
physical_volumes=None,
|
|
lvm_type='default',
|
|
executor=self.fake_execute,
|
|
suppress_fd_warn=self.configuration.lvm_suppress_fd_warnings)
|
|
|
|
def failed_fake_execute(obj, *cmd, **kwargs):
|
|
return ("\n", "fake-error")
|
|
|
|
def fake_pretend_lvm_version(obj, *cmd, **kwargs):
|
|
return (" LVM version: 2.03.00 (2012-03-06)\n", "")
|
|
|
|
def fake_old_lvm_version(obj, *cmd, **kwargs):
|
|
# Does not support thin prov or snap activation
|
|
return (" LVM version: 2.02.65(2) (2012-03-06)\n", "")
|
|
|
|
def fake_customised_lvm_version(obj, *cmd, **kwargs):
|
|
return (" LVM version: 2.02.100(2)-RHEL6 (2013-09-12)\n", "")
|
|
|
|
def fake_f23_lvm_version(obj, *cmd, **kwargs):
|
|
return (" LVM version: 2.02.132(2) (2015-09-22)\n", "")
|
|
|
|
def fake_execute(obj, *cmd, **kwargs):
|
|
# TODO(eharney): remove this and move to per-test mocked execute calls
|
|
if obj.configuration.lvm_suppress_fd_warnings:
|
|
_lvm_prefix = 'env, LC_ALL=C, LVM_SUPPRESS_FD_WARNINGS=1, '
|
|
else:
|
|
_lvm_prefix = 'env, LC_ALL=C, '
|
|
|
|
cmd_string = ', '.join(cmd)
|
|
data = "\n"
|
|
|
|
if (_lvm_prefix + 'vgs, --noheadings, --unit=g, -o, name' ==
|
|
cmd_string):
|
|
data = " fake-vg\n"
|
|
data += " some-other-vg\n"
|
|
elif (_lvm_prefix + 'vgs, --noheadings, -o, name, fake-vg' ==
|
|
cmd_string):
|
|
data = " fake-vg\n"
|
|
elif _lvm_prefix + 'vgs, --version' in cmd_string:
|
|
data = " LVM version: 2.02.95(2) (2012-03-06)\n"
|
|
elif (_lvm_prefix + 'vgs, --noheadings, -o, uuid, fake-vg' in
|
|
cmd_string):
|
|
data = " kVxztV-dKpG-Rz7E-xtKY-jeju-QsYU-SLG6Z1\n"
|
|
elif _lvm_prefix + 'vgs, --noheadings, --unit=g, ' \
|
|
'-o, name,size,free,lv_count,uuid, ' \
|
|
'--separator, :, --nosuffix' in cmd_string:
|
|
data = (" test-prov-cap-vg-unit:10.00:10.00:0:"
|
|
"mXzbuX-dKpG-Rz7E-xtKY-jeju-QsYU-SLG8Z4\n")
|
|
if 'test-prov-cap-vg-unit' in cmd_string:
|
|
return (data, "")
|
|
data = (" test-prov-cap-vg-no-unit:10.00:10.00:0:"
|
|
"mXzbuX-dKpG-Rz7E-xtKY-jeju-QsYU-SLG8Z4\n")
|
|
if 'test-prov-cap-vg-no-unit' in cmd_string:
|
|
return (data, "")
|
|
data = " fake-vg:10.00:10.00:0:"\
|
|
"kVxztV-dKpG-Rz7E-xtKY-jeju-QsYU-SLG6Z1\n"
|
|
if 'fake-vg' in cmd_string:
|
|
return (data, "")
|
|
data += " fake-vg-2:10.00:10.00:0:"\
|
|
"lWyauW-dKpG-Rz7E-xtKY-jeju-QsYU-SLG7Z2\n"
|
|
data += " fake-vg-3:10.00:10.00:0:"\
|
|
"mXzbuX-dKpG-Rz7E-xtKY-jeju-QsYU-SLG8Z3\n"
|
|
elif (_lvm_prefix + 'lvs, --noheadings, '
|
|
'--unit=g, -o, vg_name,name,size, --nosuffix, '
|
|
'fake-vg/lv-nothere' in cmd_string):
|
|
raise processutils.ProcessExecutionError(
|
|
stderr="One or more specified logical volume(s) not found.")
|
|
elif (_lvm_prefix + 'lvs, --noheadings, '
|
|
'--unit=g, -o, vg_name,name,size, --nosuffix, '
|
|
'fake-vg/lv-newerror' in cmd_string):
|
|
raise processutils.ProcessExecutionError(
|
|
stderr="Failed to find logical volume \"fake-vg/lv-newerror\"")
|
|
elif (_lvm_prefix + 'lvs, --noheadings, '
|
|
'--unit=g, -o, vg_name,name,size' in cmd_string):
|
|
if 'fake-unknown' in cmd_string:
|
|
raise processutils.ProcessExecutionError(
|
|
stderr="One or more volume(s) not found."
|
|
)
|
|
if 'test-prov-cap-vg-unit' in cmd_string:
|
|
data = " fake-vg test-prov-cap-pool-unit 9.50g\n"
|
|
data += " fake-vg fake-volume-1 1.00g\n"
|
|
data += " fake-vg fake-volume-2 2.00g\n"
|
|
elif 'test-prov-cap-vg-no-unit' in cmd_string:
|
|
data = " fake-vg test-prov-cap-pool-no-unit 9.50\n"
|
|
data += " fake-vg fake-volume-1 1.00\n"
|
|
data += " fake-vg fake-volume-2 2.00\n"
|
|
elif 'test-found-lv-name' in cmd_string:
|
|
data = " fake-vg test-found-lv-name 9.50\n"
|
|
else:
|
|
data = " fake-vg fake-1 1.00g\n"
|
|
data += " fake-vg fake-2 1.00g\n"
|
|
elif (_lvm_prefix + 'lvdisplay, --noheading, -C, -o, Attr' in
|
|
cmd_string):
|
|
if 'test-volumes' in cmd_string:
|
|
data = ' wi-a-'
|
|
else:
|
|
data = ' owi-a-'
|
|
elif _lvm_prefix + 'pvs, --noheadings' in cmd_string:
|
|
data = " fake-vg|/dev/sda|10.00|1.00\n"
|
|
data += " fake-vg|/dev/sdb|10.00|1.00\n"
|
|
data += " fake-vg|/dev/sdc|10.00|8.99\n"
|
|
data += " fake-vg-2|/dev/sdd|10.00|9.99\n"
|
|
elif _lvm_prefix + 'lvs, --noheadings, --unit=g' \
|
|
', -o, size,data_percent, --separator, :' in cmd_string:
|
|
if 'test-prov-cap-pool' in cmd_string:
|
|
data = " 9.5:20\n"
|
|
else:
|
|
data = " 9:12\n"
|
|
elif 'lvcreate, -T, -L, ' in cmd_string:
|
|
pass
|
|
elif 'lvcreate, -T, -l, 100%FREE' in cmd_string:
|
|
pass
|
|
elif 'lvcreate, -T, -V, ' in cmd_string:
|
|
pass
|
|
elif 'lvcreate, -n, ' in cmd_string:
|
|
pass
|
|
elif 'lvcreate, --name, ' in cmd_string:
|
|
pass
|
|
elif 'lvextend, -L, ' in cmd_string:
|
|
pass
|
|
else:
|
|
raise AssertionError('unexpected command called: %s' % cmd_string)
|
|
|
|
return (data, "")
|
|
|
|
def test_create_lv_snapshot(self):
|
|
self.assertIsNone(self.vg.create_lv_snapshot('snapshot-1', 'fake-1'))
|
|
|
|
with mock.patch.object(self.vg, 'get_volume', return_value=None):
|
|
try:
|
|
self.vg.create_lv_snapshot('snapshot-1', 'fake-non-existent')
|
|
except exception.VolumeDeviceNotFound as e:
|
|
self.assertEqual('fake-non-existent', e.kwargs['device'])
|
|
else:
|
|
self.fail("Exception not raised")
|
|
|
|
def test_vg_exists(self):
|
|
self.assertTrue(self.vg._vg_exists())
|
|
|
|
def test_get_vg_uuid(self):
|
|
self.assertEqual('kVxztV-dKpG-Rz7E-xtKY-jeju-QsYU-SLG6Z1',
|
|
self.vg._get_vg_uuid()[0])
|
|
|
|
def test_get_all_volumes(self):
|
|
out = self.vg.get_volumes()
|
|
|
|
self.assertEqual('fake-1', out[0]['name'])
|
|
self.assertEqual('1.00g', out[0]['size'])
|
|
self.assertEqual('fake-vg', out[0]['vg'])
|
|
|
|
def test_get_volume(self):
|
|
self.assertEqual('fake-1', self.vg.get_volume('fake-1')['name'])
|
|
|
|
def test_get_volume_none(self):
|
|
self.assertIsNone(self.vg.get_volume('fake-unknown'))
|
|
|
|
def test_get_lv_info_notfound(self):
|
|
# lv-nothere will raise lvm < 2.102.112 exception
|
|
self.assertEqual(
|
|
[],
|
|
self.vg.get_lv_info(
|
|
'sudo', vg_name='fake-vg', lv_name='lv-nothere')
|
|
)
|
|
# lv-newerror will raise lvm > 2.102.112 exception
|
|
self.assertEqual(
|
|
[],
|
|
self.vg.get_lv_info(
|
|
'sudo', vg_name='fake-vg', lv_name='lv-newerror')
|
|
)
|
|
|
|
def test_get_lv_info_found(self):
|
|
lv_info = [{'size': '9.50', 'name': 'test-found-lv-name',
|
|
'vg': 'fake-vg'}]
|
|
self.assertEqual(
|
|
lv_info,
|
|
self.vg.get_lv_info(
|
|
'sudo', vg_name='fake-vg',
|
|
lv_name='test-found-lv-name')
|
|
)
|
|
|
|
def test_get_lv_info_no_lv_name(self):
|
|
lv_info = [{'name': 'fake-1', 'size': '1.00g', 'vg': 'fake-vg'},
|
|
{'name': 'fake-2', 'size': '1.00g', 'vg': 'fake-vg'}]
|
|
self.assertEqual(
|
|
lv_info,
|
|
self.vg.get_lv_info(
|
|
'sudo', vg_name='fake-vg')
|
|
)
|
|
|
|
@mock.patch('tenacity.nap.sleep', mock.Mock())
|
|
@mock.patch.object(brick.putils, 'execute')
|
|
def test_get_lv_info_retry(self, exec_mock):
|
|
exec_mock.side_effect = (
|
|
processutils.ProcessExecutionError('', '', exit_code=139),
|
|
('vg name size', ''),
|
|
)
|
|
self.assertEqual(
|
|
[{'name': 'fake-1', 'size': '1.00g', 'vg': 'fake-vg'},
|
|
{'name': 'fake-2', 'size': '1.00g', 'vg': 'fake-vg'}],
|
|
self.vg.get_lv_info('sudo', vg_name='vg', lv_name='name')
|
|
)
|
|
|
|
@mock.patch('tenacity.nap.sleep', mock.Mock())
|
|
@mock.patch.object(os_brick_executor.Executor, '_execute')
|
|
def test_get_thin_pool_free_space_retry(self, exec_mock):
|
|
exec_mock.side_effect = (
|
|
processutils.ProcessExecutionError('', '', exit_code=139),
|
|
('15.84:50', ''),
|
|
)
|
|
self.assertEqual(
|
|
7.92,
|
|
self.vg._get_thin_pool_free_space('vg', 'thinpool')
|
|
)
|
|
self.assertEqual(2, exec_mock.call_count)
|
|
args = ['env', 'LC_ALL=C', 'lvs', '--noheadings', '--unit=g', '-o',
|
|
'size,data_percent', '--separator', ':', '--nosuffix',
|
|
'/dev/vg/thinpool']
|
|
if self.configuration.lvm_suppress_fd_warnings:
|
|
args.insert(2, 'LVM_SUPPRESS_FD_WARNINGS=1')
|
|
lvs_call = mock.call(*args, root_helper='sudo', run_as_root=True)
|
|
exec_mock.assert_has_calls([lvs_call, lvs_call])
|
|
|
|
def test_get_all_physical_volumes(self):
|
|
# Filtered VG version
|
|
pvs = self.vg.get_all_physical_volumes('sudo', 'fake-vg')
|
|
self.assertEqual(3, len(pvs))
|
|
|
|
# Non-Filtered, all VG's
|
|
pvs = self.vg.get_all_physical_volumes('sudo')
|
|
self.assertEqual(4, len(pvs))
|
|
|
|
def test_get_physical_volumes(self):
|
|
pvs = self.vg.get_physical_volumes()
|
|
self.assertEqual(3, len(pvs))
|
|
|
|
def test_get_volume_groups(self):
|
|
self.assertEqual(3, len(self.vg.get_all_volume_groups('sudo')))
|
|
self.assertEqual(1,
|
|
len(self.vg.get_all_volume_groups('sudo', 'fake-vg')))
|
|
|
|
def test_thin_pool_creation_manual(self):
|
|
# The size of fake-vg volume group is 10g, so the calculated thin
|
|
# pool size should be 9.5g (95% of 10g).
|
|
self.vg.create_thin_pool()
|
|
|
|
def test_thin_pool_provisioned_capacity(self):
|
|
self.vg.vg_thin_pool = "test-prov-cap-pool-unit"
|
|
self.vg.vg_name = 'test-prov-cap-vg-unit'
|
|
self.assertIsNone(self.vg.create_thin_pool(name=self.vg.vg_thin_pool))
|
|
self.assertEqual(9.50, self.vg.vg_thin_pool_size)
|
|
self.assertEqual(7.6, self.vg.vg_thin_pool_free_space)
|
|
self.assertEqual(3.0, self.vg.vg_provisioned_capacity)
|
|
|
|
self.vg.vg_thin_pool = "test-prov-cap-pool-no-unit"
|
|
self.vg.vg_name = 'test-prov-cap-vg-no-unit'
|
|
self.assertIsNone(self.vg.create_thin_pool(name=self.vg.vg_thin_pool))
|
|
self.assertEqual(9.50, self.vg.vg_thin_pool_size)
|
|
self.assertEqual(7.6, self.vg.vg_thin_pool_free_space)
|
|
self.assertEqual(3.0, self.vg.vg_provisioned_capacity)
|
|
|
|
def test_thin_pool_free_space(self):
|
|
# The size of fake-vg-pool is 9g and the allocated data sums up to
|
|
# 12% so the calculated free space should be 7.92
|
|
self.assertEqual(float("7.92"),
|
|
self.vg._get_thin_pool_free_space("fake-vg",
|
|
"fake-vg-pool"))
|
|
|
|
def test_volume_create_after_thin_creation(self):
|
|
"""Test self.vg.vg_thin_pool is set to pool_name
|
|
|
|
See bug #1220286 for more info.
|
|
"""
|
|
|
|
vg_name = "vg-name"
|
|
pool_name = vg_name + "-pool"
|
|
pool_path = "%s/%s" % (vg_name, pool_name)
|
|
|
|
def executor(obj, *cmd, **kwargs):
|
|
self.assertEqual(pool_path, cmd[-1])
|
|
|
|
self.vg._executor = executor
|
|
self.vg.create_thin_pool(pool_name)
|
|
self.vg.create_volume("test", "1G", lv_type='thin')
|
|
|
|
self.assertEqual(pool_name, self.vg.vg_thin_pool)
|
|
|
|
def test_lv_has_snapshot(self):
|
|
self.assertTrue(self.vg.lv_has_snapshot('fake-vg'))
|
|
self.assertFalse(self.vg.lv_has_snapshot('test-volumes'))
|
|
|
|
def test_activate_lv(self):
|
|
self.vg._supports_lvchange_ignoreskipactivation = True
|
|
|
|
with mock.patch.object(self.vg, '_execute') as mock_exec:
|
|
self.vg.activate_lv('my-lv')
|
|
expected = [mock.call('lvchange', '-a', 'y', '--yes', '-K',
|
|
'fake-vg/my-lv', root_helper='sudo',
|
|
run_as_root=True)]
|
|
self.assertEqual(expected, mock_exec.call_args_list)
|
|
|
|
def test_get_mirrored_available_capacity(self):
|
|
self.assertEqual(2.0, self.vg.vg_mirror_free_space(1))
|
|
|
|
def test_lv_extend(self):
|
|
self.vg.deactivate_lv = mock.MagicMock()
|
|
|
|
# Extend lv with snapshot and make sure deactivate called
|
|
self.vg.create_volume("test", "1G")
|
|
self.vg.extend_volume("test", "2G")
|
|
self.vg.deactivate_lv.assert_called_once_with('test')
|
|
self.vg.deactivate_lv.reset_mock()
|
|
|
|
# Extend lv without snapshot so deactivate should not be called
|
|
self.vg.create_volume("test", "1G")
|
|
self.vg.vg_name = "test-volumes"
|
|
self.vg.extend_volume("test", "2G")
|
|
self.assertFalse(self.vg.deactivate_lv.called)
|
|
|
|
def test_lv_deactivate(self):
|
|
with mock.patch.object(self.vg, '_execute', return_value=(0, 0)):
|
|
is_active_mock = mock.Mock()
|
|
is_active_mock.return_value = False
|
|
self.vg._lv_is_active = is_active_mock
|
|
self.vg.create_volume('test', '1G')
|
|
self.vg.deactivate_lv('test')
|
|
|
|
@mock.patch('os_brick.utils._time_sleep')
|
|
def test_lv_deactivate_timeout(self, mock_sleep):
|
|
with mock.patch.object(self.vg, '_execute', return_value=(0, 0)):
|
|
is_active_mock = mock.Mock()
|
|
is_active_mock.return_value = True
|
|
self.vg._lv_is_active = is_active_mock
|
|
self.vg.create_volume('test', '1G')
|
|
self.assertRaises(exception.VolumeNotDeactivated,
|
|
self.vg.deactivate_lv, 'test')
|
|
|
|
def test_lv_is_active(self):
|
|
self.vg.create_volume('test', '1G')
|
|
with mock.patch.object(self.vg, '_execute',
|
|
return_value=['owi-a---', '']):
|
|
self.assertTrue(self.vg._lv_is_active('test'))
|
|
with mock.patch.object(self.vg, '_execute',
|
|
return_value=['owi-----', '']):
|
|
self.assertFalse(self.vg._lv_is_active('test'))
|
|
|
|
|
|
class BrickLvmTestCaseIgnoreFDWarnings(BrickLvmTestCase):
|
|
def setUp(self):
|
|
self.configuration = mock.Mock()
|
|
self.configuration.lvm_suppress_fd_warnings = True
|
|
super(BrickLvmTestCaseIgnoreFDWarnings, self).setUp()
|