a8790f2303
This patch adds an action to replace a hard drive for an particular osd server. The user executing the action will give the OSD number and also the device name of the replacement drive. The rest is taken care of by the action. The action will attempt to go through all the osd removal steps for the failed drive. It will force unmount the drive and if that fails it will lazy unmount the drive. This force and then lazy pattern comes from experience with dead hard drives not behaving nicely with umount. Change-Id: I914cd484280ac3f9b9f1fad8b35ee53e92438a0a
114 lines
3.6 KiB
Python
114 lines
3.6 KiB
Python
import errno
|
|
import posix
|
|
|
|
from mock import call, Mock, patch
|
|
|
|
import test_utils
|
|
import ceph
|
|
import replace_osd
|
|
|
|
TO_PATCH = [
|
|
'ctypes',
|
|
'status_set',
|
|
]
|
|
|
|
proc_data = [
|
|
' 8 0 sda 2291336 263100 108136080 1186276 28844343 28798167 '
|
|
'2145908072 49433216 0 7550032 50630100\n',
|
|
' 8 1 sda1 1379 1636 8314 692 75 17 1656 0 0 496 692\n',
|
|
' 8 2 sda2 1 0 2 0 0 0 0 0 0 0 0\n',
|
|
]
|
|
|
|
|
|
def umount_busy(*args):
|
|
# MNT_FORCE
|
|
if args[1] == 1:
|
|
return -1
|
|
# MNT_DETACH
|
|
if args[1] == 2:
|
|
return 0
|
|
|
|
|
|
class ReplaceOsdTestCase(test_utils.CharmTestCase):
|
|
def setUp(self):
|
|
super(ReplaceOsdTestCase, self).setUp(ceph, TO_PATCH)
|
|
|
|
def test_umount_ebusy(self):
|
|
self.ctypes.util.find_library.return_value = 'libc.so.6'
|
|
umount_mock = Mock()
|
|
self.ctypes.CDLL.return_value = umount_mock
|
|
umount_mock.umount.side_effect = umount_busy
|
|
self.ctypes.get_errno.return_value = errno.EBUSY
|
|
|
|
ret = ceph.umount('/some/osd/mount')
|
|
umount_mock.assert_has_calls([
|
|
call.umount('/some/osd/mount', 1),
|
|
call.umount('/some/osd/mount', 2),
|
|
])
|
|
assert ret == 0
|
|
|
|
def test_umount(self):
|
|
self.ctypes.util.find_library.return_value = 'libc.so.6'
|
|
umount_mock = Mock()
|
|
self.ctypes.CDLL.return_value = umount_mock
|
|
umount_mock.umount.return_value = 0
|
|
|
|
ret = ceph.umount('/some/osd/mount')
|
|
umount_mock.assert_has_calls([
|
|
call.umount('/some/osd/mount', 1),
|
|
])
|
|
assert ret == 0
|
|
|
|
@patch('ceph.mounts')
|
|
@patch('ceph.subprocess')
|
|
@patch('ceph.umount')
|
|
@patch('ceph.osdize')
|
|
@patch('ceph.shutil')
|
|
@patch('ceph.systemd')
|
|
def test_replace_osd(self,
|
|
systemd,
|
|
shutil,
|
|
osdize,
|
|
umount,
|
|
subprocess,
|
|
mounts):
|
|
mounts.return_value = [['/var/lib/ceph/osd/ceph-a', '/dev/sda']]
|
|
subprocess.check_output.return_value = True
|
|
self.status_set.return_value = None
|
|
systemd.return_value = False
|
|
umount.return_value = 0
|
|
osdize.return_value = None
|
|
shutil.rmtree.return_value = None
|
|
ceph.replace_osd(dead_osd_number=0,
|
|
dead_osd_device='/dev/sda',
|
|
new_osd_device='/dev/sdb',
|
|
osd_format=True,
|
|
osd_journal=None,
|
|
reformat_osd=False,
|
|
ignore_errors=False)
|
|
subprocess.check_output.assert_has_calls(
|
|
[
|
|
call(['ceph', 'osd', 'out', 'osd.0']),
|
|
call(['stop', 'ceph-osd', 'id=0']),
|
|
call(['ceph', 'osd', 'crush', 'remove', 'osd.0']),
|
|
call(['ceph', 'auth', 'del', 'osd.0']),
|
|
call(['ceph', 'osd', 'rm', 'osd.0'])
|
|
]
|
|
)
|
|
|
|
@patch('replace_osd.get_disk_stats')
|
|
def test_lookup_device_name(self, disk_stats):
|
|
disk_stats.return_value = proc_data
|
|
dev_name = replace_osd.lookup_device_name(major_number=8,
|
|
minor_number=0)
|
|
assert dev_name == 'sda', "dev_name: {}".format(dev_name)
|
|
|
|
@patch('replace_osd.os.lstat')
|
|
def test_get_device_number(self, lstat):
|
|
lstat.return_value = posix.stat_result([
|
|
16877, 16, 51729L, 3, 0, 0, 217, 0, 1458086872, 1458086872
|
|
])
|
|
major, minor = replace_osd.get_device_number(1)
|
|
assert major == 202
|
|
assert minor == 17
|