--- /dev/null
+#!/usr/bin/env python
+#
+# Copyright (C) 2015, 2016 Red Hat <contact@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Library Public License for more details.
+#
+from mock import patch, DEFAULT
+import os
+import platform
+import io
+import shutil
+import subprocess
+import tempfile
+import unittest
+from ceph_disk import main
+
+try:
+ import builtins
+except:
+ import __builtin__ as builtins
+
+
+def fail_to_mount(dev, fstype, options):
+ raise main.MountError(dev + " mount fail")
+
+
+class TestCephDisk(object):
+
+ def setup_class(self):
+ main.setup_logging(verbose=True, log_stdout=False)
+
+ def test_main_list_json(self, capsys):
+ if platform.system() == "FreeBSD":
+ return
+
+ data = tempfile.mkdtemp()
+ main.setup_statedir(data)
+ args = main.parse_args(['list', '--format', 'json'])
+ with patch.multiple(
+ main,
+ list_devices=lambda: {}):
+ main.main_list(args)
+ out, err = capsys.readouterr()
+ assert '{}\n' == out
+ shutil.rmtree(data)
+
+ def test_main_list_plain(self, capsys):
+ if platform.system() == "FreeBSD":
+ return
+
+ data = tempfile.mkdtemp()
+ main.setup_statedir(data)
+ args = main.parse_args(['list'])
+ with patch.multiple(
+ main,
+ list_devices=lambda: {}):
+ main.main_list(args)
+ out, err = capsys.readouterr()
+ assert '' == out
+ shutil.rmtree(data)
+
+ def test_list_format_more_osd_info_plain(self):
+ dev = {
+ 'ceph_fsid': 'UUID',
+ 'cluster': 'ceph',
+ 'whoami': '1234',
+ 'journal_dev': '/dev/Xda2',
+ }
+ out = main.list_format_more_osd_info_plain(dev)
+ assert dev['cluster'] in " ".join(out)
+ assert dev['journal_dev'] in " ".join(out)
+ assert dev['whoami'] in " ".join(out)
+
+ dev = {
+ 'ceph_fsid': 'UUID',
+ 'whoami': '1234',
+ 'journal_dev': '/dev/Xda2',
+ }
+ out = main.list_format_more_osd_info_plain(dev)
+ assert 'unknown cluster' in " ".join(out)
+
+ def test_list_format_plain(self):
+ payload = [{
+ 'path': '/dev/Xda',
+ 'ptype': 'unknown',
+ 'type': 'other',
+ 'mount': '/somewhere',
+ }]
+ out = main.list_format_plain(payload)
+ assert payload[0]['path'] in out
+ assert payload[0]['type'] in out
+ assert payload[0]['mount'] in out
+
+ payload = [{
+ 'path': '/dev/Xda1',
+ 'ptype': 'unknown',
+ 'type': 'swap',
+ }]
+ out = main.list_format_plain(payload)
+ assert payload[0]['path'] in out
+ assert payload[0]['type'] in out
+
+ payload = [{
+ 'path': '/dev/Xda',
+ 'partitions': [
+ {
+ 'dmcrypt': {},
+ 'ptype': 'whatever',
+ 'is_partition': True,
+ 'fs_type': 'ext4',
+ 'path': '/dev/Xda1',
+ 'mounted': '/somewhere',
+ 'type': 'other',
+ }
+ ],
+ }]
+ out = main.list_format_plain(payload)
+ assert payload[0]['path'] in out
+ assert payload[0]['partitions'][0]['path'] in out
+
+ def test_list_format_dev_plain(dev):
+ #
+ # data
+ #
+ dev = {
+ 'path': '/dev/Xda1',
+ 'ptype': main.PTYPE['regular']['osd']['ready'],
+ 'state': 'prepared',
+ 'whoami': '1234',
+ }
+ out = main.list_format_dev_plain(dev)
+ assert 'data' in out
+ assert dev['whoami'] in out
+ assert dev['state'] in out
+ #
+ # journal
+ #
+ dev = {
+ 'path': '/dev/Xda2',
+ 'ptype': main.PTYPE['regular']['journal']['ready'],
+ 'journal_for': '/dev/Xda1',
+ }
+ out = main.list_format_dev_plain(dev)
+ assert 'journal' in out
+ assert dev['journal_for'] in out
+
+ #
+ # dmcrypt data
+ #
+ ptype2type = {
+ main.PTYPE['plain']['osd']['ready']: 'plain',
+ main.PTYPE['luks']['osd']['ready']: 'luks',
+ }
+ for (ptype, type) in ptype2type.items():
+ for holders in ((), ("dm_0",), ("dm_0", "dm_1")):
+ dev = {
+ 'dmcrypt': {
+ 'holders': holders,
+ 'type': type,
+ },
+ 'path': '/dev/Xda1',
+ 'ptype': ptype,
+ 'state': 'prepared',
+ 'whoami': '1234',
+ }
+ out = main.list_format_dev_plain(dev)
+ assert 'data' in out
+ assert 'dmcrypt' in out
+ assert type in out
+ if len(holders) == 1:
+ assert dev['whoami'] in out
+ for holder in holders:
+ assert holder in out
+
+ #
+ # dmcrypt journal
+ #
+ ptype2type = {
+ main.PTYPE['plain']['journal']['ready']: 'plain',
+ main.PTYPE['luks']['journal']['ready']: 'luks',
+ }
+ for (ptype, type) in ptype2type.items():
+ for holders in ((), ("dm_0",)):
+ dev = {
+ 'path': '/dev/Xda2',
+ 'ptype': ptype,
+ 'journal_for': '/dev/Xda1',
+ 'dmcrypt': {
+ 'holders': holders,
+ 'type': type,
+ },
+ }
+ out = main.list_format_dev_plain(dev)
+ assert 'journal' in out
+ assert 'dmcrypt' in out
+ assert type in out
+ assert dev['journal_for'] in out
+ if len(holders) == 1:
+ assert holders[0] in out
+
+ def test_list_dev_osd(self):
+ dev = "Xda"
+ mount_path = '/mount/path'
+ fs_type = 'ext4'
+ cluster = 'ceph'
+ uuid_map = {}
+
+ def more_osd_info(path, uuid_map, desc):
+ desc['cluster'] = cluster
+ #
+ # mounted therefore active
+ #
+ with patch.multiple(
+ main,
+ is_mounted=lambda dev: mount_path,
+ get_dev_fs=lambda dev: fs_type,
+ more_osd_info=more_osd_info
+ ):
+ desc = {}
+ main.list_dev_osd(dev, uuid_map, desc)
+ assert {'cluster': 'ceph',
+ 'fs_type': 'ext4',
+ 'mount': '/mount/path',
+ 'state': 'active'} == desc
+ #
+ # not mounted and cannot mount: unprepared
+ #
+ mount_path = None
+ with patch.multiple(
+ main,
+ is_mounted=lambda dev: mount_path,
+ get_dev_fs=lambda dev: fs_type,
+ mount=fail_to_mount,
+ more_osd_info=more_osd_info
+ ):
+ desc = {}
+ main.list_dev_osd(dev, uuid_map, desc)
+ assert {'fs_type': 'ext4',
+ 'mount': mount_path,
+ 'state': 'unprepared'} == desc
+ #
+ # not mounted and magic found: prepared
+ #
+
+ def get_oneliner(path, what):
+ if what == 'magic':
+ return main.CEPH_OSD_ONDISK_MAGIC
+ else:
+ raise Exception('unknown ' + what)
+ with patch.multiple(
+ main,
+ is_mounted=lambda dev: mount_path,
+ get_dev_fs=lambda dev: fs_type,
+ mount=DEFAULT,
+ unmount=DEFAULT,
+ get_oneliner=get_oneliner,
+ more_osd_info=more_osd_info
+ ):
+ desc = {}
+ main.list_dev_osd(dev, uuid_map, desc)
+ assert {'cluster': 'ceph',
+ 'fs_type': 'ext4',
+ 'mount': mount_path,
+ 'magic': main.CEPH_OSD_ONDISK_MAGIC,
+ 'state': 'prepared'} == desc
+
+ def test_list_all_partitions(self):
+ if platform.system() == "FreeBSD":
+ return
+
+ disk = "Xda"
+ partition = "Xda1"
+
+ with patch(
+ 'ceph_disk.main.os',
+ listdir=lambda path: [disk],
+ ), patch.multiple(
+ main,
+ list_partitions=lambda dev: [partition],
+ ):
+ assert {disk: [partition]} == main.list_all_partitions()
+
+ def test_list_data(self):
+ #
+ # a data partition that fails to mount is silently
+ # ignored
+ #
+ if platform.system() == "FreeBSD":
+ return
+
+ partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ disk = "Xda"
+ partition = "Xda1"
+ fs_type = "ext4"
+
+ def get_partition_type(dev):
+ return main.PTYPE['regular']['osd']['ready']
+ with patch.multiple(
+ main,
+ list_all_partitions=lambda: {disk: [partition]},
+ get_partition_uuid=lambda dev: partition_uuid,
+ get_partition_type=get_partition_type,
+ get_dev_fs=lambda dev: fs_type,
+ mount=fail_to_mount,
+ unmount=DEFAULT,
+ is_partition=lambda dev: True,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'dmcrypt': {},
+ 'fs_type': fs_type,
+ 'is_partition': True,
+ 'mount': None,
+ 'path': '/dev/' + partition,
+ 'ptype': main.PTYPE['regular']['osd']['ready'],
+ 'state': 'unprepared',
+ 'type': 'data',
+ 'uuid': partition_uuid,
+ }]}]
+ assert expect == main.list_devices()
+
+ def test_list_dmcrypt_data(self):
+ if platform.system() == "FreeBSD":
+ return
+
+ partition_type2type = {
+ main.PTYPE['plain']['osd']['ready']: 'plain',
+ main.PTYPE['luks']['osd']['ready']: 'LUKS',
+ }
+ for (partition_type, type) in partition_type2type.items():
+ #
+ # dmcrypt data partition with one holder
+ #
+ partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ disk = "Xda"
+ partition = "Xda1"
+ holders = ["dm-dummy"]
+ with patch.multiple(
+ main,
+ is_held=lambda dev: holders,
+ list_all_partitions=lambda: {disk: [partition]},
+ get_partition_uuid=lambda dev: partition_uuid,
+ get_partition_type=lambda dev: partition_type,
+ is_partition=lambda dev: True,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'dmcrypt': {
+ 'holders': holders,
+ 'type': type,
+ },
+ 'fs_type': None,
+ 'is_partition': True,
+ 'mount': None,
+ 'path': '/dev/' + partition,
+ 'ptype': partition_type,
+ 'state': 'unprepared',
+ 'type': 'data',
+ 'uuid': partition_uuid,
+ }]}]
+ assert expect == main.list_devices()
+ #
+ # dmcrypt data partition with two holders
+ #
+ partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ disk = "Xda"
+ partition = "Xda1"
+ holders = ["dm-dummy", "dm-dummy1"]
+ with patch.multiple(
+ main,
+ is_held=lambda dev: holders,
+ list_all_partitions=lambda: {disk: [partition]},
+ get_partition_uuid=lambda dev: partition_uuid,
+ get_partition_type=lambda dev: partition_type,
+ is_partition=lambda dev: True,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'dmcrypt': {
+ 'holders': holders,
+ 'type': type,
+ },
+ 'is_partition': True,
+ 'path': '/dev/' + partition,
+ 'ptype': partition_type,
+ 'type': 'data',
+ 'uuid': partition_uuid,
+ }]}]
+ assert expect == main.list_devices()
+
+ def test_list_multipath(self):
+ #
+ # multipath data partition
+ #
+ if platform.system() == "FreeBSD":
+ return
+
+ partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ disk = "Xda"
+ partition = "Xda1"
+
+ def get_partition_type(dev):
+ return main.PTYPE['mpath']['osd']['ready']
+ with patch.multiple(
+ main,
+ list_all_partitions=lambda: {disk: [partition]},
+ get_partition_uuid=lambda dev: partition_uuid,
+ get_partition_type=get_partition_type,
+ is_partition=lambda dev: True,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'dmcrypt': {},
+ 'fs_type': None,
+ 'is_partition': True,
+ 'mount': None,
+ 'multipath': True,
+ 'path': '/dev/' + partition,
+ 'ptype': main.PTYPE['mpath']['osd']['ready'],
+ 'state': 'unprepared',
+ 'type': 'data',
+ 'uuid': partition_uuid,
+ }]}]
+ assert expect == main.list_devices()
+ #
+ # multipath journal partition
+ #
+ journal_partition_uuid = "2cc40457-259e-4542-b029-785c7cc37871"
+
+ def get_partition_type(dev):
+ return main.PTYPE['mpath']['journal']['ready']
+ with patch.multiple(
+ main,
+ list_all_partitions=lambda: {disk: [partition]},
+ get_partition_uuid=lambda dev: journal_partition_uuid,
+ get_partition_type=get_partition_type,
+ is_partition=lambda dev: True,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'dmcrypt': {},
+ 'is_partition': True,
+ 'multipath': True,
+ 'path': '/dev/' + partition,
+ 'ptype': main.PTYPE['mpath']['journal']['ready'],
+ 'type': 'journal',
+ 'uuid': journal_partition_uuid,
+ }]}]
+ assert expect == main.list_devices()
+
+ def test_list_default(self):
+ self.list(main.PTYPE['plain']['osd']['ready'],
+ main.PTYPE['plain']['journal']['ready'])
+ self.list(main.PTYPE['luks']['osd']['ready'],
+ main.PTYPE['luks']['journal']['ready'])
+ self.list(main.PTYPE['regular']['osd']['ready'],
+ main.PTYPE['regular']['journal']['ready'])
+
+ def test_list_bluestore(self):
+ if platform.system() == "FreeBSD":
+ return
+
+ self.list(main.PTYPE['plain']['osd']['ready'],
+ main.PTYPE['plain']['block']['ready'])
+ self.list(main.PTYPE['luks']['osd']['ready'],
+ main.PTYPE['luks']['block']['ready'])
+ self.list(main.PTYPE['regular']['osd']['ready'],
+ main.PTYPE['regular']['block']['ready'])
+
+ def list(self, data_ptype, space_ptype):
+ #
+ # a single disk has a data partition and a journal
+ # partition and the osd is active
+ #
+ name = main.Ptype.space_ptype_to_name(space_ptype)
+ data_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ disk = "Xda"
+ data = "Xda1"
+ data_holder = "dm-dummy"
+ space = "Xda2"
+ space_holder = "dm-dummy"
+ mount_path = '/mount/path'
+ fs_type = 'ext4'
+ space_uuid = "7ad5e65a-0ca5-40e4-a896-62a74ca61c55"
+ ceph_fsid = "60a2ef70-d99b-4b9b-a83c-8a86e5e60091"
+ osd_id = '1234'
+
+ def get_oneliner(path, what):
+ if '_uuid' in what:
+ if what == name + '_uuid':
+ return space_uuid
+ else:
+ return None
+ elif what == 'ceph_fsid':
+ return ceph_fsid
+ elif what == 'whoami':
+ return osd_id
+ else:
+ raise Exception('unknown ' + what)
+
+ def get_partition_uuid(dev):
+ if dev == '/dev/' + data:
+ return data_uuid
+ elif dev == '/dev/' + space:
+ return space_uuid
+ else:
+ raise Exception('unknown ' + dev)
+
+ def get_partition_type(dev):
+ if (dev == '/dev/' + data or
+ dev == '/dev/' + data_holder):
+ return data_ptype
+ elif (dev == '/dev/' + space or
+ dev == '/dev/' + space_holder):
+ return space_ptype
+ else:
+ raise Exception('unknown ' + dev)
+ cluster = 'ceph'
+ if data_ptype == main.PTYPE['regular']['osd']['ready']:
+ data_dmcrypt = {}
+ elif data_ptype == main.PTYPE['plain']['osd']['ready']:
+ data_dmcrypt = {
+ 'type': 'plain',
+ 'holders': [data_holder],
+ }
+ elif data_ptype == main.PTYPE['luks']['osd']['ready']:
+ data_dmcrypt = {
+ 'type': 'LUKS',
+ 'holders': [data_holder],
+ }
+ else:
+ raise Exception('unknown ' + data_ptype)
+
+ if space_ptype == main.PTYPE['regular'][name]['ready']:
+ space_dmcrypt = {}
+ elif space_ptype == main.PTYPE['plain'][name]['ready']:
+ space_dmcrypt = {
+ 'type': 'plain',
+ 'holders': [space_holder],
+ }
+ elif space_ptype == main.PTYPE['luks'][name]['ready']:
+ space_dmcrypt = {
+ 'type': 'LUKS',
+ 'holders': [space_holder],
+ }
+ else:
+ raise Exception('unknown ' + space_ptype)
+
+ if data_dmcrypt:
+ def is_held(dev):
+ if dev == '/dev/' + data:
+ return [data_holder]
+ elif dev == '/dev/' + space:
+ return [space_holder]
+ else:
+ raise Exception('unknown ' + dev)
+ else:
+ def is_held(dev):
+ return []
+
+ with patch.multiple(
+ main,
+ list_all_partitions=lambda: {disk: [data, space]},
+ get_dev_fs=lambda dev: fs_type,
+ is_mounted=lambda dev: mount_path,
+ get_partition_uuid=get_partition_uuid,
+ get_partition_type=get_partition_type,
+ find_cluster_by_uuid=lambda ceph_fsid: cluster,
+ is_partition=lambda dev: True,
+ mount=DEFAULT,
+ unmount=DEFAULT,
+ get_oneliner=get_oneliner,
+ is_held=is_held,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'ceph_fsid': ceph_fsid,
+ 'cluster': cluster,
+ 'dmcrypt': data_dmcrypt,
+ 'fs_type': fs_type,
+ 'is_partition': True,
+ name + '_dev': '/dev/' + space,
+ name + '_uuid': space_uuid,
+ 'mount': mount_path,
+ 'path': '/dev/' + data,
+ 'ptype': data_ptype,
+ 'state': 'active',
+ 'type': 'data',
+ 'whoami': osd_id,
+ 'uuid': data_uuid,
+ }, {
+ 'dmcrypt': space_dmcrypt,
+ 'is_partition': True,
+ name + '_for': '/dev/' + data,
+ 'path': '/dev/' + space,
+ 'ptype': space_ptype,
+ 'type': name,
+ 'uuid': space_uuid,
+ }]}]
+ assert expect == main.list_devices()
+
+ def test_list_other(self):
+ #
+ # not swap, unknown fs type, not mounted, with uuid
+ #
+ if platform.system() == "FreeBSD":
+ return
+
+ partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ partition_type = "e51adfb9-e9fd-4718-9fc1-7a0cb03ea3f4"
+ disk = "Xda"
+ partition = "Xda1"
+ with patch.multiple(
+ main,
+ list_all_partitions=lambda: {disk: [partition]},
+ get_partition_uuid=lambda dev: partition_uuid,
+ get_partition_type=lambda dev: partition_type,
+ is_partition=lambda dev: True,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{'dmcrypt': {},
+ 'is_partition': True,
+ 'path': '/dev/' + partition,
+ 'ptype': partition_type,
+ 'type': 'other',
+ 'uuid': partition_uuid}]}]
+ assert expect == main.list_devices()
+ #
+ # not swap, mounted, ext4 fs type, with uuid
+ #
+ partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ partition_type = "e51adfb9-e9fd-4718-9fc1-7a0cb03ea3f4"
+ disk = "Xda"
+ partition = "Xda1"
+ mount_path = '/mount/path'
+ fs_type = 'ext4'
+ with patch.multiple(
+ main,
+ list_all_partitions=lambda: {disk: [partition]},
+ get_dev_fs=lambda dev: fs_type,
+ is_mounted=lambda dev: mount_path,
+ get_partition_uuid=lambda dev: partition_uuid,
+ get_partition_type=lambda dev: partition_type,
+ is_partition=lambda dev: True,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'dmcrypt': {},
+ 'is_partition': True,
+ 'mount': mount_path,
+ 'fs_type': fs_type,
+ 'path': '/dev/' + partition,
+ 'ptype': partition_type,
+ 'type': 'other',
+ 'uuid': partition_uuid,
+ }]}]
+ assert expect == main.list_devices()
+
+ #
+ # swap, with uuid
+ #
+ partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ partition_type = "e51adfb9-e9fd-4718-9fc1-7a0cb03ea3f4"
+ disk = "Xda"
+ partition = "Xda1"
+ with patch.multiple(
+ main,
+ list_all_partitions=lambda: {disk: [partition]},
+ is_swap=lambda dev: True,
+ get_partition_uuid=lambda dev: partition_uuid,
+ get_partition_type=lambda dev: partition_type,
+ is_partition=lambda dev: True,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'partitions': [{'dmcrypt': {},
+ 'is_partition': True,
+ 'path': '/dev/' + partition,
+ 'ptype': partition_type,
+ 'type': 'swap',
+ 'uuid': partition_uuid}]}]
+ assert expect == main.list_devices()
+
+ #
+ # whole disk
+ #
+ partition_uuid = "56244cf5-83ef-4984-888a-2d8b8e0e04b2"
+ disk = "Xda"
+ partition = "Xda1"
+ with patch.multiple(
+ main,
+ list_all_partitions=lambda: {disk: []},
+ is_partition=lambda dev: False,
+ ):
+ expect = [{'path': '/dev/' + disk,
+ 'dmcrypt': {},
+ 'is_partition': False,
+ 'ptype': 'unknown',
+ 'type': 'other'}]
+ assert expect == main.list_devices()
+
+
+class TestCephDiskDeactivateAndDestroy(unittest.TestCase):
+
+ def setup_class(self):
+ main.setup_logging(verbose=True, log_stdout=False)
+
+ @patch('{0}.open'.format(builtins.__name__))
+ def test_main_deactivate(self, mock_open):
+ data = tempfile.mkdtemp()
+ main.setup_statedir(data)
+ DMCRYPT_LUKS_OSD_UUID = '4fbd7e29-9d25-41b8-afd0-35865ceff05d'
+ part_uuid = '0ce28a16-6d5d-11e5-aec3-fa163e5c167b'
+ disk = 'sdX'
+ #
+ # Can not find match device by osd-id
+ #
+ args = main.parse_args(['deactivate',
+ '--cluster', 'ceph',
+ '--deactivate-by-id', '5566'])
+ fake_device = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'path': '/dev/sdX1',
+ 'whoami': '-1',
+ }]}]
+ with patch.multiple(
+ main,
+ list_devices=lambda: fake_device,
+ ):
+ self.assertRaises(Exception, main.main_deactivate, args)
+
+ #
+ # find match device by osd-id, status: OSD_STATUS_IN_DOWN
+ # with --mark-out option
+ #
+ args = main.parse_args(['deactivate',
+ '--cluster', 'ceph',
+ '--deactivate-by-id', '5566',
+ '--mark-out'])
+ fake_device = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'ptype': DMCRYPT_LUKS_OSD_UUID,
+ 'path': '/dev/sdX1',
+ 'whoami': '5566',
+ 'mount': '/var/lib/ceph/osd/ceph-5566/',
+ 'uuid': part_uuid,
+ }]}]
+ with patch.multiple(
+ main,
+ list_devices=lambda: fake_device,
+ _check_osd_status=lambda cluster, osd_id: 2,
+ _mark_osd_out=lambda cluster, osd_id: True
+ ):
+ main.main_deactivate(args)
+
+ #
+ # find match device by device partition, status: OSD_STATUS_IN_DOWN
+ #
+ args = main.parse_args(['deactivate',
+ '--cluster', 'ceph',
+ '/dev/sdX1'])
+ fake_device = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'ptype': DMCRYPT_LUKS_OSD_UUID,
+ 'path': '/dev/sdX1',
+ 'whoami': '5566',
+ 'mount': '/var/lib/ceph/osd/ceph-5566/',
+ 'uuid': part_uuid,
+ }]}]
+ with patch.multiple(
+ main,
+ list_devices=lambda: fake_device,
+ _check_osd_status=lambda cluster, osd_id: 0,
+ ):
+ main.main_deactivate(args)
+
+ #
+ # find match device by device partition, status: OSD_STATUS_IN_UP
+ # with --mark-out option
+ #
+ args = main.parse_args(['deactivate',
+ '--cluster', 'ceph',
+ '/dev/sdX1',
+ '--mark-out'])
+ fake_device = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'ptype': DMCRYPT_LUKS_OSD_UUID,
+ 'path': '/dev/sdX1',
+ 'whoami': '5566',
+ 'mount': '/var/lib/ceph/osd/ceph-5566/',
+ 'uuid': part_uuid,
+ }]}]
+
+ # mock the file open.
+ file_opened = io.StringIO()
+ file_opened.write(u'deactive')
+ mock_open.return_value = file_opened
+
+ with patch.multiple(
+ main,
+ mock_open,
+ list_devices=lambda: fake_device,
+ _check_osd_status=lambda cluster, osd_id: 3,
+ _mark_osd_out=lambda cluster, osd_id: True,
+ stop_daemon=lambda cluster, osd_id: True,
+ _remove_osd_directory_files=lambda path, cluster: True,
+ path_set_context=lambda path: True,
+ unmount=lambda path, do_rm: True,
+ dmcrypt_unmap=lambda part_uuid: True,
+ ):
+ main.main_deactivate(args)
+
+ #
+ # find match device by osd-id, status: OSD_STATUS_OUT_UP
+ #
+ args = main.parse_args(['deactivate',
+ '--cluster', 'ceph',
+ '--deactivate-by-id', '5566'])
+ fake_device = [{'path': '/dev/' + disk,
+ 'partitions': [{
+ 'ptype': DMCRYPT_LUKS_OSD_UUID,
+ 'path': '/dev/sdX1',
+ 'whoami': '5566',
+ 'mount': '/var/lib/ceph/osd/ceph-5566/',
+ 'uuid': part_uuid,
+ }]}]
+
+ # mock the file open.
+ file_opened = io.StringIO()
+ file_opened.write(u'deactive')
+ mock_open.return_value = file_opened
+
+ with patch.multiple(
+ main,
+ mock_open,
+ list_devices=lambda: fake_device,
+ _check_osd_status=lambda cluster, osd_id: 1,
+ _mark_osd_out=lambda cluster, osd_id: True,
+ stop_daemon=lambda cluster, osd_id: True,
+ _remove_osd_directory_files=lambda path, cluster: True,
+ path_set_context=lambda path: True,
+ unmount=lambda path, do_rm: True,
+ dmcrypt_unmap=lambda part_uuid: True,
+ ):
+ main.main_deactivate(args)
+ shutil.rmtree(data)
+
+ def test_mark_out_out(self):
+ def mark_osd_out_fail(osd_id):
+ raise main.Error('Could not find osd.%s, is a vaild/exist osd id?'
+ % osd_id)
+
+ with patch.multiple(
+ main,
+ command=mark_osd_out_fail,
+ ):
+ self.assertRaises(Exception, main._mark_osd_out, 'ceph', '5566')
+
+ def test_check_osd_status(self):
+ #
+ # command failure
+ #
+ with patch.multiple(
+ main,
+ command=raise_command_error,
+ ):
+ self.assertRaises(Exception, main._check_osd_status,
+ 'ceph', '5566')
+
+ #
+ # osd not found
+ #
+
+ fake_data = ('{"osds":[{"osd":0,"up":1,"in":1},'
+ '{"osd":1,"up":1,"in":1}]}')
+
+ def return_fake_value(cmd):
+ return fake_data, '', 0
+
+ with patch.multiple(
+ main,
+ command=return_fake_value,
+ ):
+ self.assertRaises(Exception, main._check_osd_status,
+ 'ceph', '5566')
+
+ #
+ # successfully
+ #
+
+ fake_data = ('{"osds":[{"osd":0,"up":1,"in":1},'
+ '{"osd":5566,"up":1,"in":1}]}')
+
+ def return_fake_value(cmd):
+ return fake_data, '', 0
+
+ with patch.multiple(
+ main,
+ command=return_fake_value,
+ ):
+ main._check_osd_status('ceph', '5566')
+
+ def test_stop_daemon(self):
+ STATEDIR = '/var/lib/ceph'
+ cluster = 'ceph'
+ osd_id = '5566'
+
+ def stop_daemon_fail(cmd):
+ raise Exception('ceph osd stop failed')
+
+ #
+ # fail on init type
+ #
+ with patch('os.path.exists', return_value=False):
+ self.assertRaises(Exception, main.stop_daemon, 'ceph', '5566')
+
+ #
+ # upstart failure
+ #
+ fake_path = (STATEDIR + '/osd/{cluster}-{osd_id}/upstart').format(
+ cluster=cluster, osd_id=osd_id)
+
+ def path_exist(check_path):
+ if check_path == fake_path:
+ return True
+ else:
+ False
+
+ patcher = patch('os.path.exists')
+ check_path = patcher.start()
+ check_path.side_effect = path_exist
+ with patch.multiple(
+ main,
+ check_path,
+ command_check_call=stop_daemon_fail,
+ ):
+ self.assertRaises(Exception, main.stop_daemon, 'ceph', '5566')
+
+ #
+ # sysvinit failure
+ #
+ fake_path = (STATEDIR + '/osd/{cluster}-{osd_id}/sysvinit').format(
+ cluster=cluster, osd_id=osd_id)
+
+ def path_exist(check_path):
+ if check_path == fake_path:
+ return True
+ else:
+ return False
+
+ patcher = patch('os.path.exists')
+ check_path = patcher.start()
+ check_path.side_effect = path_exist
+ with patch.multiple(
+ main,
+ check_path,
+ which=lambda name: True,
+ command_check_call=stop_daemon_fail,
+ ):
+ self.assertRaises(Exception, main.stop_daemon, 'ceph', '5566')
+
+ #
+ # systemd failure
+ #
+ fake_path = (STATEDIR + '/osd/{cluster}-{osd_id}/systemd').format(
+ cluster=cluster, osd_id=osd_id)
+
+ def path_exist(check_path):
+ if check_path == fake_path:
+ return True
+ else:
+ False
+
+ def stop_daemon_fail(cmd):
+ if 'stop' in cmd:
+ raise Exception('ceph osd stop failed')
+ else:
+ return True
+
+ patcher = patch('os.path.exists')
+ check_path = patcher.start()
+ check_path.side_effect = path_exist
+ with patch.multiple(
+ main,
+ check_path,
+ command_check_call=stop_daemon_fail,
+ ):
+ self.assertRaises(Exception, main.stop_daemon, 'ceph', '5566')
+
+ def test_remove_osd_directory_files(self):
+ cluster = 'ceph'
+ mounted_path = 'somewhere'
+ fake_path_2 = None
+ fake_path_remove_2 = None
+ fake_path_remove_init = None
+
+ def handle_path_exist(check_path):
+ if check_path == fake_path:
+ return True
+ elif fake_path_2 and check_path == fake_path_2:
+ return True
+ else:
+ return False
+
+ def handle_path_remove(remove_path):
+ if remove_path == fake_path_remove:
+ return True
+ elif fake_path_remove_2 and remove_path == fake_path_remove_2:
+ return True
+ elif (fake_path_remove_init and
+ remove_path == fake_path_remove_init):
+ return True
+ else:
+ raise OSError
+
+ #
+ # remove ready file failure
+ #
+ fake_path = os.path.join(mounted_path, 'ready')
+ fake_path_remove = os.path.join(mounted_path, 'no_ready')
+
+ patcher_exist = patch('os.path.exists')
+ patcher_remove = patch('os.remove')
+ path_exist = patcher_exist.start()
+ path_remove = patcher_remove.start()
+ path_exist.side_effect = handle_path_exist
+ path_remove.side_effect = handle_path_remove
+ with patch.multiple(
+ main,
+ path_exist,
+ path_remove,
+ get_conf=lambda cluster, **kwargs: True,
+ ):
+ self.assertRaises(Exception, main._remove_osd_directory_files,
+ 'somewhere', cluster)
+
+ #
+ # remove active fil failure
+ #
+ fake_path = os.path.join(mounted_path, 'ready')
+ fake_path_2 = os.path.join(mounted_path, 'active')
+ fake_path_remove = os.path.join(mounted_path, 'ready')
+ fake_path_remove_2 = os.path.join(mounted_path, 'no_active')
+
+ patcher_exist = patch('os.path.exists')
+ patcher_remove = patch('os.remove')
+ path_exist = patcher_exist.start()
+ path_remove = patcher_remove.start()
+ path_exist.side_effect = handle_path_exist
+ path_remove.side_effect = handle_path_remove
+ with patch.multiple(
+ main,
+ path_exist,
+ path_remove,
+ get_conf=lambda cluster, **kwargs: True,
+ ):
+ self.assertRaises(Exception, main._remove_osd_directory_files,
+ 'somewhere', cluster)
+
+ #
+ # conf_val is None and remove init file failure
+ #
+ fake_path = os.path.join(mounted_path, 'ready')
+ fake_path_2 = os.path.join(mounted_path, 'active')
+ fake_path_remove = os.path.join(mounted_path, 'ready')
+ fake_path_remove_2 = os.path.join(mounted_path, 'active')
+ fake_path_remove_init = os.path.join(mounted_path, 'init_failure')
+
+ patcher_exist = patch('os.path.exists')
+ patcher_remove = patch('os.remove')
+ path_exist = patcher_exist.start()
+ path_remove = patcher_remove.start()
+ path_exist.side_effect = handle_path_exist
+ path_remove.side_effect = handle_path_remove
+ with patch.multiple(
+ main,
+ path_exist,
+ path_remove,
+ get_conf=lambda cluster, **kwargs: None,
+ init_get=lambda: 'upstart',
+ ):
+ self.assertRaises(Exception, main._remove_osd_directory_files,
+ 'somewhere', cluster)
+
+ #
+ # already remove `ready`, `active` and remove init file successfully
+ #
+ fake_path = os.path.join(mounted_path, 'no_ready')
+ fake_path_2 = os.path.join(mounted_path, 'no_active')
+ fake_path_remove = os.path.join(mounted_path, 'upstart')
+
+ patcher_exist = patch('os.path.exists')
+ patcher_remove = patch('os.remove')
+ path_exist = patcher_exist.start()
+ path_remove = patcher_remove.start()
+ path_exist.side_effect = handle_path_exist
+ path_remove.side_effect = handle_path_remove
+ with patch.multiple(
+ main,
+ path_exist,
+ path_remove,
+ get_conf=lambda cluster, **kwargs: 'upstart',
+ ):
+ main._remove_osd_directory_files('somewhere', cluster)
+
+ def test_path_set_context(self):
+ path = '/somewhere'
+ with patch.multiple(
+ main,
+ get_ceph_user=lambda **kwargs: 'ceph',
+ ):
+ main.path_set_context(path)
+
+ def test_mount(self):
+ #
+ # None to mount
+ #
+ dev = None
+ fs_type = 'ext4'
+ option = ''
+ self.assertRaises(Exception, main.mount, dev, fs_type, option)
+
+ #
+ # fstype undefine
+ #
+ dev = '/dev/Xda1'
+ fs_type = None
+ option = ''
+ self.assertRaises(Exception, main.mount, dev, fs_type, option)
+
+ #
+ # mount failure
+ #
+ dev = '/dev/Xda1'
+ fstype = 'ext4'
+ options = ''
+ with patch('tempfile.mkdtemp', return_value='/mnt'):
+ self.assertRaises(Exception, main.mount, dev, fstype, options)
+
+ #
+ # mount successfully
+ #
+ def create_temp_directory(*args, **kwargs):
+ return '/mnt'
+
+ dev = '/dev/Xda1'
+ fstype = 'ext4'
+ options = ''
+ patcher = patch('tempfile.mkdtemp')
+ create_tmpdir = patcher.start()
+ create_tmpdir.side_effect = create_temp_directory
+ with patch.multiple(
+ main,
+ create_tmpdir,
+ command_check_call=lambda cmd: True,
+ ):
+ main.mount(dev, fstype, options)
+
+ def test_umount(self):
+ #
+ # umount failure
+ #
+ path = '/somewhere'
+ self.assertRaises(Exception, main.unmount, path)
+
+ #
+ # umount successfully
+ #
+ def remove_directory_successfully(path):
+ return True
+
+ path = '/somewhere'
+ patcher = patch('os.rmdir')
+ rm_directory = patcher.start()
+ rm_directory.side_effect = remove_directory_successfully
+ with patch.multiple(
+ main,
+ rm_directory,
+ command_check_call=lambda cmd: True,
+ ):
+ main.unmount(path)
+
+ def test_main_destroy(self):
+ data = tempfile.mkdtemp()
+ main.setup_statedir(data)
+ OSD_UUID = '4fbd7e29-9d25-41b8-afd0-062c0ceff05d'
+ MPATH_OSD_UUID = '4fbd7e29-8ae0-4982-bf9d-5a8d867af560'
+ part_uuid = '0ce28a16-6d5d-11e5-aec3-fa163e5c167b'
+ journal_uuid = "7ad5e65a-0ca5-40e4-a896-62a74ca61c55"
+ mount_5566 = '/var/lib/ceph/osd/ceph-5566/'
+
+ fake_devices_normal = [{'path': '/dev/sdY',
+ 'partitions': [{
+ 'dmcrypt': {},
+ 'type': 'osd',
+ 'ptype': OSD_UUID,
+ 'path': '/dev/sdY1',
+ 'whoami': '5566',
+ 'mount': mount_5566,
+ 'uuid': part_uuid,
+ 'journal_uuid': journal_uuid}]},
+ {'path': '/dev/sdX',
+ 'partitions': [{
+ 'dmcrypt': {},
+ 'type': 'osd',
+ 'ptype': MPATH_OSD_UUID,
+ 'path': '/dev/sdX1',
+ 'whoami': '7788',
+ 'mount': '/var/lib/ceph/osd/ceph-7788/',
+ 'uuid': part_uuid,
+ 'journal_uuid': journal_uuid}]}]
+
+ def list_devices_return():
+ return fake_devices_normal
+
+ #
+ # input device is not the device partition
+ #
+ args = main.parse_args(['destroy', '--cluster', 'ceph', '/dev/sdX'])
+ with patch.multiple(
+ main,
+ is_partition=lambda path: False,
+ ):
+ self.assertRaises(Exception, main.main_destroy, args)
+
+ #
+ # skip the redundent devices and not found by dev
+ #
+ args = main.parse_args(['destroy', '--cluster', 'ceph', '/dev/sdZ1'])
+ with patch.multiple(
+ main,
+ is_partition=lambda path: True,
+ list_devices=list_devices_return,
+ ):
+ self.assertRaises(Exception, main.main_destroy, args)
+
+ #
+ # skip the redundent devices and not found by osd-id
+ #
+ args = main.parse_args(['destroy', '--cluster', 'ceph',
+ '--destroy-by-id', '1234'])
+ with patch.multiple(
+ main,
+ is_partition=lambda path: True,
+ list_devices=list_devices_return,
+ ):
+ self.assertRaises(Exception, main.main_destroy, args)
+
+ #
+ # skip the redundent devices and found by dev
+ #
+ args = main.parse_args(['destroy', '--cluster',
+ 'ceph', '/dev/sdY1', '--zap'])
+ with patch.multiple(
+ main,
+ is_partition=lambda path: True,
+ list_devices=list_devices_return,
+ get_partition_base=lambda dev_path: '/dev/sdY',
+ _check_osd_status=lambda cluster, osd_id: 0,
+ zap=lambda dev: True
+ ):
+ main.main_destroy(args)
+
+ #
+ # skip the redundent devices and found by osd-id
+ # with active status and MPATH_OSD
+ #
+ args = main.parse_args(['destroy', '--cluster', 'ceph',
+ '--destroy-by-id', '7788'])
+ with patch.multiple(
+ main,
+ is_partition=lambda path: True,
+ list_devices=list_devices_return,
+ get_partition_base_mpath=lambda dev_path: '/dev/sdX',
+ _check_osd_status=lambda cluster, osd_id: 1,
+ ):
+ self.assertRaises(Exception, main.main_destroy, args)
+ shutil.rmtree(data)
+
+ def test_main_fix(self):
+ if platform.system() == "FreeBSD":
+ return
+
+ args = main.parse_args(['fix', '--all', '--selinux', '--permissions'])
+ commands = []
+
+ def _command(x):
+ commands.append(" ".join(x))
+ return ("", "", None)
+
+ class Os(object):
+ F_OK = 0
+
+ @staticmethod
+ def access(x, y):
+ return True
+
+ with patch.multiple(
+ main,
+ command=_command,
+ command_init=lambda x: commands.append(x),
+ command_wait=lambda x: None,
+ os=Os,
+ ):
+ main.main_fix(args)
+ commands = " ".join(commands)
+ assert '/var/lib/ceph' in commands
+ assert 'restorecon' in commands
+ assert 'chown' in commands
+ assert 'find' in commands
+
+
+def raise_command_error(*args):
+ e = subprocess.CalledProcessError('aaa', 'bbb', 'ccc')
+ raise e