437 lines
19 KiB
Python
437 lines
19 KiB
Python
# Copyright 2014 Mirantis, Inc.
|
|
#
|
|
# 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 mock
|
|
import unittest2
|
|
|
|
from fuel_agent import errors
|
|
from fuel_agent.utils import lvm as lu
|
|
from fuel_agent.utils import utils
|
|
|
|
|
|
class TestLvmUtils(unittest2.TestCase):
|
|
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_pvdisplay(self, mock_exec):
|
|
# should run os command pvdisplay
|
|
# in order to get actual pv configuration
|
|
mock_exec.return_value = (
|
|
'/dev/fake1;vg;892.00m;1024.00m;'
|
|
'123456-1234-1234-1234-1234-1234-000000\n'
|
|
'/dev/fake2;;1024.00m;1024.00m;'
|
|
'123456-1234-1234-1234-1234-1234-111111\n',
|
|
''
|
|
)
|
|
expected = [
|
|
{
|
|
'uuid': '123456-1234-1234-1234-1234-1234-000000',
|
|
'vg': 'vg',
|
|
'devsize': 1024,
|
|
'psize': 892,
|
|
'name': '/dev/fake1',
|
|
},
|
|
{
|
|
'uuid': '123456-1234-1234-1234-1234-1234-111111',
|
|
'vg': None,
|
|
'devsize': 1024,
|
|
'psize': 1024,
|
|
'name': '/dev/fake2',
|
|
}
|
|
]
|
|
|
|
pvs = lu.pvdisplay()
|
|
mock_exec.assert_called_once_with(
|
|
'pvdisplay',
|
|
'-C',
|
|
'--noheading',
|
|
'--units', 'm',
|
|
'--options', 'pv_name,vg_name,pv_size,dev_size,pv_uuid',
|
|
'--separator', ';',
|
|
check_exit_code=[0]
|
|
)
|
|
key = lambda x: x['name']
|
|
self.assertEqual(sorted(expected, key=key), sorted(pvs, key=key))
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_pvcreate_ok(self, mock_exec, mock_pvdisplay):
|
|
# should set metadatasize=64 and metadatacopies=2 if they are not set
|
|
# should run pvcreate command
|
|
mock_pvdisplay.return_value = [{'name': '/dev/another'}]
|
|
|
|
lu.pvcreate('/dev/fake1', metadatasize=32, metadatacopies=1)
|
|
lu.pvcreate('/dev/fake2', metadatacopies=1)
|
|
lu.pvcreate('/dev/fake3', metadatasize=32)
|
|
lu.pvcreate('/dev/fake4')
|
|
|
|
expected_calls = [
|
|
mock.call('pvcreate',
|
|
'--metadatacopies', '1',
|
|
'--metadatasize', '32m',
|
|
'/dev/fake1',
|
|
check_exit_code=[0]),
|
|
mock.call('pvcreate',
|
|
'--metadatacopies', '1',
|
|
'--metadatasize', '64m',
|
|
'/dev/fake2',
|
|
check_exit_code=[0]),
|
|
mock.call('pvcreate',
|
|
'--metadatacopies', '2',
|
|
'--metadatasize', '32m',
|
|
'/dev/fake3',
|
|
check_exit_code=[0]),
|
|
mock.call('pvcreate',
|
|
'--metadatacopies', '2',
|
|
'--metadatasize', '64m',
|
|
'/dev/fake4',
|
|
check_exit_code=[0])
|
|
]
|
|
self.assertEqual(mock_exec.call_args_list, expected_calls)
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
def test_pvcreate_duplicate(self, mock_pvdisplay):
|
|
# should check if pv exists
|
|
# then raise exception if it exists
|
|
mock_pvdisplay.return_value = [{'name': '/dev/fake'}]
|
|
self.assertRaises(
|
|
errors.PVAlreadyExistsError, lu.pvcreate, '/dev/fake')
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_pvremove_ok(self, mock_exec, mock_pvdisplay):
|
|
# should check if pv exists and is not attached to some vg
|
|
# then should run pvremove command
|
|
mock_pvdisplay.return_value = [{'vg': None, 'name': '/dev/fake'}]
|
|
lu.pvremove('/dev/fake')
|
|
mock_exec.assert_called_once_with('pvremove', '-ff', '-y', '/dev/fake',
|
|
check_exit_code=[0])
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
def test_pvremove_attached_to_vg(self, mock_pvdisplay):
|
|
# should check if pv exists and is not attached to some vg
|
|
# then raise exception if it is attached to some vg
|
|
mock_pvdisplay.return_value = [{'vg': 'some', 'name': '/dev/fake'}]
|
|
self.assertRaises(errors.PVBelongsToVGError, lu.pvremove, '/dev/fake')
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
def test_pvremove_notfound(self, mock_pvdisplay):
|
|
# should check if pv exists
|
|
# then should raise exception if it does not exist
|
|
mock_pvdisplay.return_value = [{'name': '/dev/another'}]
|
|
self.assertRaises(errors.PVNotFoundError, lu.pvremove, '/dev/fake')
|
|
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_vgdisplay(self, mock_exec):
|
|
# should run os command vgdisplay
|
|
# in order to get actual vg configuration
|
|
mock_exec.return_value = (
|
|
'vg1;123456-1234-1234-1234-1234-1234-000000;2040.00m;2040.00m\n'
|
|
'vg2;123456-1234-1234-1234-1234-1234-111111;2040.00m;1020.00m\n',
|
|
''
|
|
)
|
|
expected = [
|
|
{
|
|
'uuid': '123456-1234-1234-1234-1234-1234-000000',
|
|
'size': 2040,
|
|
'free': 2040,
|
|
'name': 'vg1',
|
|
},
|
|
{
|
|
'uuid': '123456-1234-1234-1234-1234-1234-111111',
|
|
'size': 2040,
|
|
'free': 1020,
|
|
'name': 'vg2',
|
|
}
|
|
]
|
|
|
|
vg = lu.vgdisplay()
|
|
mock_exec.assert_called_once_with(
|
|
'vgdisplay',
|
|
'-C',
|
|
'--noheading',
|
|
'--units', 'm',
|
|
'--options', 'vg_name,vg_uuid,vg_size,vg_free',
|
|
'--separator', ';',
|
|
check_exit_code=[0]
|
|
)
|
|
key = lambda x: x['name']
|
|
self.assertEqual(sorted(expected, key=key), sorted(vg, key=key))
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_vgcreate_ok(self, mock_exec, mock_vgdisplay, mock_pvdisplay):
|
|
# should check if vg already exists
|
|
# should check if all necessary pv exist
|
|
# should run vgcreate command
|
|
mock_vgdisplay.return_value = [{'name': 'some'}, {'name': 'another'}]
|
|
mock_pvdisplay.return_value = [{'vg': None, 'name': '/dev/fake1'},
|
|
{'vg': None, 'name': '/dev/fake2'}]
|
|
|
|
# one pvname
|
|
lu.vgcreate('vgname', '/dev/fake1')
|
|
# several pvnames
|
|
lu.vgcreate('vgname', '/dev/fake1', '/dev/fake2')
|
|
|
|
expected_calls = [
|
|
mock.call('vgcreate', 'vgname', '/dev/fake1',
|
|
check_exit_code=[0]),
|
|
mock.call('vgcreate', 'vgname', '/dev/fake1', '/dev/fake2',
|
|
check_exit_code=[0])
|
|
]
|
|
self.assertEqual(mock_exec.call_args_list, expected_calls)
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
def test_vgcreate_duplicate(self, mock_vgdisplay):
|
|
# should check if vg exists
|
|
# should raise exception if it exists
|
|
mock_vgdisplay.return_value = [{'name': 'vgname'}, {'name': 'some'}]
|
|
self.assertRaises(errors.VGAlreadyExistsError,
|
|
lu.vgcreate, 'vgname', '/dev/fake')
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
def test_vgcreate_pv_not_found(self, mock_vgdisplay, mock_pvdisplay):
|
|
# should check if all necessary pv exist
|
|
# should raise error if at least one pv does not
|
|
mock_vgdisplay.return_value = []
|
|
mock_pvdisplay.return_value = [{'vg': None, 'name': '/dev/fake1'},
|
|
{'vg': None, 'name': '/dev/fake2'}]
|
|
self.assertRaises(errors.PVNotFoundError,
|
|
lu.vgcreate, 'vgname', '/dev/fake', '/dev/fake2')
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
def test_vgcreate_pv_attached(self, mock_vgdisplay, mock_pvdisplay):
|
|
# should check if all necessary pv are not attached to some vg
|
|
# should raise error if at least one pv is attached
|
|
mock_vgdisplay.return_value = []
|
|
mock_pvdisplay.return_value = [{'vg': None, 'name': '/dev/fake1'},
|
|
{'vg': 'some', 'name': '/dev/fake2'}]
|
|
self.assertRaises(errors.PVBelongsToVGError,
|
|
lu.vgcreate, 'vgname', '/dev/fake1', '/dev/fake2')
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_vgextend_ok(self, mock_exec, mock_vgdisplay, mock_pvdisplay):
|
|
# should check if vg exists
|
|
# should check if all necessary pv exist and not attached to any vg
|
|
# should run vgextend command
|
|
mock_vgdisplay.return_value = [{'name': 'some'}, {'name': 'another'}]
|
|
mock_pvdisplay.return_value = [{'vg': None, 'name': '/dev/fake1'},
|
|
{'vg': None, 'name': '/dev/fake2'}]
|
|
lu.vgextend('some', '/dev/fake1', '/dev/fake2')
|
|
mock_exec.assert_called_once_with(
|
|
'vgextend', 'some', '/dev/fake1', '/dev/fake2',
|
|
check_exit_code=[0])
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_vgextend_not_found(self, mock_exec, mock_vgdisplay):
|
|
# should check if vg exists
|
|
# should raise error if it does not
|
|
mock_vgdisplay.return_value = [{'name': 'some'}]
|
|
self.assertRaises(errors.VGNotFoundError,
|
|
lu.vgextend, 'vgname', '/dev/fake1')
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
def test_vgextend_pv_not_found(self, mock_vgdisplay, mock_pvdisplay):
|
|
# should check if all necessary pv exist
|
|
# should raise error if at least one pv does not
|
|
mock_vgdisplay.return_value = [{'name': 'vgname'}]
|
|
mock_pvdisplay.return_value = [{'vg': None, 'name': '/dev/fake1'},
|
|
{'vg': None, 'name': '/dev/fake2'}]
|
|
self.assertRaises(errors.PVNotFoundError,
|
|
lu.vgextend, 'vgname', '/dev/fake', '/dev/fake2')
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
def test_vgextend_pv_attached(self, mock_vgdisplay, mock_pvdisplay):
|
|
# should check if all necessary pv are not attached to some vg
|
|
# should raise error if at least one pv is attached
|
|
mock_vgdisplay.return_value = [{'name': 'vgname'}]
|
|
mock_pvdisplay.return_value = [{'vg': None, 'name': '/dev/fake1'},
|
|
{'vg': 'some', 'name': '/dev/fake2'}]
|
|
self.assertRaises(errors.PVBelongsToVGError,
|
|
lu.vgextend, 'vgname', '/dev/fake1', '/dev/fake2')
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_vgremove_ok(self, mock_exec, mock_vgdisplay):
|
|
# should check if vg exists
|
|
# then run vgremove command if it exists
|
|
mock_vgdisplay.return_value = [{'name': 'vgname'}, {'name': 'some'}]
|
|
lu.vgremove('vgname')
|
|
mock_exec.assert_called_once_with('vgremove', '-f', 'vgname',
|
|
check_exit_code=[0])
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_vgremove_not_found(self, mock_exec, mock_vgdisplay):
|
|
# should check if vg exists
|
|
# then raise error if it doesn't
|
|
mock_vgdisplay.return_value = [{'name': 'some'}]
|
|
self.assertRaises(errors.VGNotFoundError, lu.vgremove, 'vgname')
|
|
|
|
@mock.patch.object(lu, 'lvdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvremove_ok(self, mock_exec, mock_lvdisplay):
|
|
mock_lvdisplay.return_value = [{'path': '/dev/vg/lv'},
|
|
{'path': '/dev/vg2/lv2'}]
|
|
lu.lvremove('/dev/vg/lv')
|
|
mock_exec.assert_called_once_with('lvremove', '-f', '/dev/vg/lv',
|
|
check_exit_code=[0])
|
|
|
|
@mock.patch.object(lu, 'lvdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvremove_not_found(self, mock_exec, mock_lvdisplay):
|
|
mock_lvdisplay.return_value = [{'path': '/dev/vg/lv'}]
|
|
self.assertRaises(errors.LVNotFoundError, lu.lvremove, '/dev/vg/lv2')
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(lu, 'lvdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvcreate_ok(self, mock_exec, mock_lvdisplay, mock_vgdisplay):
|
|
mock_vgdisplay.return_value = [{'name': 'vgname', 'free': 2000},
|
|
{'name': 'some'}]
|
|
mock_lvdisplay.return_value = [{'name': 'some'}]
|
|
mock_exec.side_effect = [
|
|
# pretend lvcreate supports --yes option
|
|
('--yes', ''),
|
|
('', '')
|
|
]
|
|
expected_calls = [
|
|
mock.call('lvcreate', '--help'),
|
|
mock.call('lvcreate', '--yes', '-L', '1000m',
|
|
'-n', 'lvname', 'vgname',
|
|
check_exit_code=[0])
|
|
]
|
|
lu.lvcreate('vgname', 'lvname', 1000)
|
|
self.assertEqual(mock_exec.call_args_list, expected_calls)
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(lu, 'lvdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvcreate_old_lvm(self, mock_exec, mock_lvdisplay, mock_vgdisplay):
|
|
mock_vgdisplay.return_value = [{'name': 'vgname', 'free': 2000},
|
|
{'name': 'some'}]
|
|
mock_lvdisplay.return_value = [{'name': 'some'}]
|
|
mock_exec.side_effect = [
|
|
# pretend lvcreate does not support --yes option
|
|
# (which is the case for Ubuntu 14.04)
|
|
('--foo', ''),
|
|
('', '')
|
|
]
|
|
expected_calls = [
|
|
mock.call('lvcreate', '--help'),
|
|
mock.call('lvcreate', '-L', '1000m',
|
|
'-n', 'lvname', 'vgname',
|
|
check_exit_code=[0])
|
|
]
|
|
lu.lvcreate('vgname', 'lvname', 1000)
|
|
self.assertEqual(mock_exec.call_args_list, expected_calls)
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvcreate_not_found(self, mock_exec, mock_vgdisplay):
|
|
mock_vgdisplay.return_value = [{'name': 'some'}]
|
|
self.assertRaises(errors.VGNotFoundError, lu.lvcreate, 'vgname',
|
|
'lvname', 1)
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvcreate_not_enough_space(self, mock_exec, mock_vgdisplay):
|
|
mock_vgdisplay.return_value = [{'name': 'vgname', 'free': 1},
|
|
{'name': 'some'}]
|
|
self.assertRaises(errors.NotEnoughSpaceError, lu.lvcreate, 'vgname',
|
|
'lvname', 2)
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(lu, 'lvdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvcreate_lv_already_exists(self, mock_exec, mock_lvdisplay,
|
|
mock_vgdisplay):
|
|
mock_vgdisplay.return_value = [{'name': 'vgname', 'free': 2000},
|
|
{'name': 'some'}]
|
|
mock_lvdisplay.return_value = [{'name': 'lvname', 'vg': 'vgname'}]
|
|
self.assertRaises(errors.LVAlreadyExistsError, lu.lvcreate, 'vgname',
|
|
'lvname', 1000)
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(lu, 'lvdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvcreate_lv_name_collision(self, mock_exec, mock_lvdisplay,
|
|
mock_vgdisplay):
|
|
# lv lvname already exists in another pv
|
|
mock_vgdisplay.return_value = [{'name': 'vgname', 'free': 2000},
|
|
{'name': 'some', 'free': 2000}]
|
|
mock_lvdisplay.return_value = [{'name': 'lvname', 'vg': 'some'}]
|
|
mock_exec.side_effect = [('--yes', ''), ('', '')]
|
|
expected_calls = [
|
|
mock.call('lvcreate', '--help'),
|
|
mock.call('lvcreate', '--yes', '-L', '1000m',
|
|
'-n', 'lvname', 'vgname',
|
|
check_exit_code=[0])
|
|
]
|
|
lu.lvcreate('vgname', 'lvname', 1000)
|
|
self.assertEqual(mock_exec.call_args_list, expected_calls)
|
|
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_lvdisplay(self, mock_exec):
|
|
mock_exec.return_value = [
|
|
' lv_name1;1234.12m;vg_name;lv_uuid1\n'
|
|
' lv_name2;5678.79m;vg_name;lv_uuid2\n ']
|
|
expected_lvs = [{'name': 'lv_name1', 'size': 1235, 'vg': 'vg_name',
|
|
'uuid': 'lv_uuid1', 'path': '/dev/vg_name/lv_name1'},
|
|
{'name': 'lv_name2', 'size': 5679, 'vg': 'vg_name',
|
|
'uuid': 'lv_uuid2', 'path': '/dev/vg_name/lv_name2'}]
|
|
actual_lvs = lu.lvdisplay()
|
|
self.assertEqual(expected_lvs, actual_lvs)
|
|
mock_exec.assert_called_once_with('lvdisplay', '-C', '--noheading',
|
|
'--units', 'm', '--options',
|
|
'lv_name,lv_size,vg_name,lv_uuid',
|
|
'--separator', ';',
|
|
check_exit_code=[0])
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_vgreduce_ok(self, mock_exec, mock_vgdisplay, mock_pvdisplay):
|
|
mock_vgdisplay.return_value = [{'name': 'vgname'}, {'name': 'some'}]
|
|
mock_pvdisplay.return_value = [{'vg': 'vgname', 'name': '/dev/fake1'},
|
|
{'vg': 'vgname', 'name': '/dev/fake2'}]
|
|
lu.vgreduce('vgname', '/dev/fake1', '/dev/fake2')
|
|
mock_exec.assert_called_once_with('vgreduce', '-f', 'vgname',
|
|
'/dev/fake1', '/dev/fake2',
|
|
check_exit_code=[0])
|
|
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
def test_vgreduce_vg_not_found(self, mock_vgdisplay):
|
|
mock_vgdisplay.return_value = [{'name': 'some'}]
|
|
self.assertRaises(errors.VGNotFoundError, lu.vgreduce, 'vgname1',
|
|
'/dev/fake1', '/dev/fake2')
|
|
|
|
@mock.patch.object(lu, 'pvdisplay')
|
|
@mock.patch.object(lu, 'vgdisplay')
|
|
@mock.patch.object(utils, 'execute')
|
|
def test_vgreduce_pv_not_attached(self, mock_exec, mock_vgdisplay,
|
|
mock_pvdisplay):
|
|
mock_vgdisplay.return_value = [{'name': 'vgname'}, {'name': 'some'}]
|
|
mock_pvdisplay.return_value = [{'vg': None, 'name': '/dev/fake1'},
|
|
{'vg': None, 'name': '/dev/fake2'}]
|
|
self.assertRaises(errors.PVNotFoundError, lu.vgreduce, 'vgname',
|
|
'/dev/fake1', '/dev/fake2')
|