]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume: various fixes in arg_validators
authorGuillaume Abrioux <gabrioux@redhat.com>
Fri, 11 Mar 2022 09:29:35 +0000 (10:29 +0100)
committerAdam King <adking@redhat.com>
Tue, 3 May 2022 00:48:33 +0000 (20:48 -0400)
if a device with an FS is passed, ceph-volume should abort
the OSD creation.

Fixes: https://tracker.ceph.com/issues/54535
Signed-off-by: Guillaume Abrioux <gabrioux@redhat.com>
(cherry picked from commit 9f4b830dcfb45eda81eabf18a8461ac4e1bf642e)

src/ceph-volume/ceph_volume/devices/lvm/batch.py
src/ceph-volume/ceph_volume/devices/lvm/common.py
src/ceph-volume/ceph_volume/devices/lvm/zap.py
src/ceph-volume/ceph_volume/devices/raw/common.py
src/ceph-volume/ceph_volume/tests/devices/lvm/test_batch.py
src/ceph-volume/ceph_volume/tests/devices/lvm/test_create.py
src/ceph-volume/ceph_volume/tests/devices/lvm/test_prepare.py
src/ceph-volume/ceph_volume/tests/devices/raw/test_prepare.py
src/ceph-volume/ceph_volume/tests/util/test_arg_validators.py
src/ceph-volume/ceph_volume/util/arg_validators.py
src/ceph-volume/ceph_volume/util/device.py

index dace25530aba0d27b11246f5505875b2448bc9f7..6fa619d5003a280ab69f3a005e849f6eafc3cd3c 100644 (file)
@@ -196,7 +196,7 @@ class Batch(object):
             'devices',
             metavar='DEVICES',
             nargs='*',
-            type=arg_validators.ValidBatchDevice(),
+            type=arg_validators.ValidBatchDataDevice(),
             default=[],
             help='Devices to provision OSDs',
         )
index 614be0af6ad6115af7c91b4e5f64b00087840e90..edc8e1cbce117c377b60bf36d97b04090b9072e5 100644 (file)
@@ -4,7 +4,6 @@ from ceph_volume import terminal
 from ceph_volume.devices.lvm.zap import Zap
 import argparse
 
-
 def rollback_osd(args, osd_id=None):
     """
     When the process of creating or preparing fails, the OSD needs to be
@@ -40,7 +39,7 @@ common_args = {
     '--data': {
         'help': 'OSD data path. A physical device or logical volume',
         'required': True,
-        'type': arg_validators.ValidDevice(as_string=True),
+        'type': arg_validators.ValidDataDevice(as_string=True),
         #'default':,
         #'type':,
     },
index e0cbfb172194063662c06b14637ba049c3cfde66..d74e88fb633e45d2a1067c55b3ed1ffdb9a43163 100644 (file)
@@ -362,7 +362,7 @@ class Zap(object):
             'devices',
             metavar='DEVICES',
             nargs='*',
-            type=arg_validators.ValidDevice(gpt_ok=True),
+            type=arg_validators.ValidZapDevice(gpt_ok=True),
             default=[],
             help='Path to one or many lv (as vg/lv), partition (as /dev/sda1) or device (as /dev/sda)'
         )
index 54e77aca63ca11fcebcb1dd18040639747cb22c4..19de81fe5ef8a4c7d4dd37a3ea58f2b14942c5c3 100644 (file)
@@ -14,7 +14,7 @@ def create_parser(prog, description):
     parser.add_argument(
         '--data',
         required=True,
-    type=arg_validators.ValidDevice(as_string=True),
+        type=arg_validators.ValidRawDevice(as_string=True),
         help='a raw device to use for the OSD',
     )
     parser.add_argument(
@@ -35,12 +35,14 @@ def create_parser(prog, description):
     parser.add_argument(
         '--block.db',
         dest='block_db',
-        help='Path to bluestore block.db block device'
+        help='Path to bluestore block.db block device',
+        type=arg_validators.ValidRawDevice(as_string=True)
     )
     parser.add_argument(
         '--block.wal',
         dest='block_wal',
-        help='Path to bluestore block.wal block device'
+        help='Path to bluestore block.wal block device',
+        type=arg_validators.ValidRawDevice(as_string=True)
     )
     parser.add_argument(
         '--dmcrypt',
index 265a9b84ef4b4d556d27b42afe6c02c4084b3505..96a5b5d74db5cc6ffdf9377155cc0ade67110bb7 100644 (file)
@@ -32,6 +32,8 @@ class TestBatch(object):
     def test_reject_partition(self, mocked_device):
         mocked_device.return_value = MagicMock(
             is_partition=True,
+            has_fs=False,
+            is_lvm_member=False,
             has_gpt_headers=False,
             has_partitions=False,
         )
index 994038f3b88631920e93fb772cd3c504130f5778..8e888d02b3b51e85675813992c4b5025736eff42 100644 (file)
@@ -1,5 +1,6 @@
 import pytest
 from ceph_volume.devices import lvm
+from mock import patch
 
 
 class TestCreate(object):
@@ -17,7 +18,8 @@ class TestCreate(object):
         assert 'Use the bluestore objectstore' in stdout
         assert 'A physical device or logical' in stdout
 
-    def test_excludes_filestore_bluestore_flags(self, capsys, device_info):
+    @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False)
+    def test_excludes_filestore_bluestore_flags(self, m_has_bs_label, capsys, device_info):
         device_info()
         with pytest.raises(SystemExit):
             lvm.create.Create(argv=['--data', '/dev/sdfoo', '--filestore', '--bluestore']).main()
@@ -25,7 +27,8 @@ class TestCreate(object):
         expected = 'Cannot use --filestore (filestore) with --bluestore (bluestore)'
         assert expected in stderr
 
-    def test_excludes_other_filestore_bluestore_flags(self, capsys, device_info):
+    @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False)
+    def test_excludes_other_filestore_bluestore_flags(self, m_has_bs_label, capsys, device_info):
         device_info()
         with pytest.raises(SystemExit):
             lvm.create.Create(argv=[
@@ -36,7 +39,8 @@ class TestCreate(object):
         expected = 'Cannot use --bluestore (bluestore) with --journal (filestore)'
         assert expected in stderr
 
-    def test_excludes_block_and_journal_flags(self, capsys, device_info):
+    @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False)
+    def test_excludes_block_and_journal_flags(self, m_has_bs_label, capsys, device_info):
         device_info()
         with pytest.raises(SystemExit):
             lvm.create.Create(argv=[
index fcbc276f0de1b6f4b3d25cc0f11a865881cb2da4..e6e42463465b26c74b94ef869c2a6ec2e3bf1ebf 100644 (file)
@@ -66,7 +66,9 @@ class TestPrepare(object):
         assert 'Use the bluestore objectstore' in stdout
         assert 'A physical device or logical' in stdout
 
-    def test_excludes_filestore_bluestore_flags(self, capsys, device_info):
+
+    @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False)
+    def test_excludes_filestore_bluestore_flags(self, m_has_bs_label, capsys, device_info):
         device_info()
         with pytest.raises(SystemExit):
             lvm.prepare.Prepare(argv=['--data', '/dev/sdfoo', '--filestore', '--bluestore']).main()
@@ -74,7 +76,9 @@ class TestPrepare(object):
         expected = 'Cannot use --filestore (filestore) with --bluestore (bluestore)'
         assert expected in stderr
 
-    def test_excludes_other_filestore_bluestore_flags(self, capsys, device_info):
+
+    @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False)
+    def test_excludes_other_filestore_bluestore_flags(self, m_has_bs_label, capsys, device_info):
         device_info()
         with pytest.raises(SystemExit):
             lvm.prepare.Prepare(argv=[
@@ -85,7 +89,8 @@ class TestPrepare(object):
         expected = 'Cannot use --bluestore (bluestore) with --journal (filestore)'
         assert expected in stderr
 
-    def test_excludes_block_and_journal_flags(self, capsys, device_info):
+    @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False)
+    def test_excludes_block_and_journal_flags(self, m_has_bs_label, capsys, device_info):
         device_info()
         with pytest.raises(SystemExit):
             lvm.prepare.Prepare(argv=[
@@ -96,7 +101,8 @@ class TestPrepare(object):
         expected = 'Cannot use --block.db (bluestore) with --journal (filestore)'
         assert expected in stderr
 
-    def test_journal_is_required_with_filestore(self, is_root, monkeypatch, device_info):
+    @patch('ceph_volume.util.disk.has_bluestore_label', return_value=False)
+    def test_journal_is_required_with_filestore(self, m_has_bs_label, is_root, monkeypatch, device_info):
         monkeypatch.setattr("os.path.exists", lambda path: True)
         device_info()
         with pytest.raises(SystemExit) as error:
index e4cf8ce110913f0f5f54a5e0d069a0956adafd7d..f814bbf136b7bc920771874987aac1dca6bbacb4 100644 (file)
@@ -41,7 +41,7 @@ class TestPrepare(object):
         assert 'Path to bluestore block.wal block device' in stdout
         assert 'Enable device encryption via dm-crypt' in stdout
 
-    @patch('ceph_volume.util.arg_validators.ValidDevice.__call__')
+    @patch('ceph_volume.util.arg_validators.ValidRawDevice.__call__')
     def test_prepare_dmcrypt_no_secret_passed(self, m_valid_device, capsys):
         m_valid_device.return_value = '/dev/foo'
         with pytest.raises(SystemExit):
@@ -87,7 +87,7 @@ class TestPrepare(object):
 
     @patch('ceph_volume.devices.raw.prepare.rollback_osd')
     @patch('ceph_volume.devices.raw.prepare.Prepare.prepare')
-    @patch('ceph_volume.util.arg_validators.ValidDevice.__call__')
+    @patch('ceph_volume.util.arg_validators.ValidRawDevice.__call__')
     def test_safe_prepare_exception_raised(self, m_valid_device, m_prepare, m_rollback_osd):
         m_valid_device.return_value = '/dev/foo'
         m_prepare.side_effect=Exception('foo')
index 13dff80bfe957d1a7b0f5ee1837150a96bbd0741..19aaaa3bd7e839f3cda5d02c88597e67db18399b 100644 (file)
@@ -1,9 +1,9 @@
 import argparse
 import pytest
 import os
-from ceph_volume import exceptions
+from ceph_volume import exceptions, process
 from ceph_volume.util import arg_validators
-from mock.mock import patch, PropertyMock
+from mock.mock import patch, MagicMock
 
 
 class TestOSDPath(object):
@@ -81,21 +81,252 @@ class TestValidDevice(object):
     def setup(self):
         self.validator = arg_validators.ValidDevice()
 
-    def test_path_is_valid(self, fake_call, patch_bluestore_label):
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    def test_path_is_valid(self, m_has_bs_label, fake_call, patch_bluestore_label):
         result = self.validator('/')
         assert result.abspath == '/'
 
-    def test_path_is_invalid(self, fake_call, patch_bluestore_label):
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    def test_path_is_invalid(self, m_has_bs_label, fake_call, patch_bluestore_label):
         with pytest.raises(argparse.ArgumentError):
             self.validator('/device/does/not/exist')
 
-    @patch('ceph_volume.util.arg_validators.Device.has_partitions', new_callable=PropertyMock, return_value=True)
-    @patch('ceph_volume.util.arg_validators.Device.exists', new_callable=PropertyMock, return_value=True)
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
     @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
-    def test_dev_has_partitions(self, m_get_single_lv, m_exists, m_has_partitions, fake_call):
+    def test_dev_has_partitions(self, m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            exists=True,
+            has_partitions=True,
+        )
+        with pytest.raises(RuntimeError):
+            self.validator('/dev/foo')
+
+class TestValidZapDevice(object):
+    def setup(self):
+        self.validator = arg_validators.ValidZapDevice()
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_has_partition(self,  m_get_single_lv, m_has_bs_label, mocked_device):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=True,
+            has_gpt_headers=False,
+            has_fs=False
+        )
+        self.validator.zap = False
+        with pytest.raises(RuntimeError):
+            assert self.validator('/dev/foo')
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_has_no_partition(self,  m_get_single_lv, m_has_bs_label, mocked_device):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False
+        )
+        self.validator.zap = False
+        assert self.validator('/dev/foo')
+
+class TestValidDataDevice(object):
+    def setup(self):
+        self.validator = arg_validators.ValidDataDevice()
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_used_by_ceph(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=True,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False
+        )
+        with pytest.raises(SystemExit):
+            self.validator.zap = False
+            self.validator('/dev/foo')
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_has_fs(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=True
+        )
+        with pytest.raises(RuntimeError):
+            self.validator.zap = False
+            self.validator('/dev/foo')
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=True)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_has_bs_signature(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False
+        )
+        with pytest.raises(RuntimeError):
+            self.validator.zap = False
+            self.validator('/dev/foo')
+
+class TestValidRawDevice(object):
+    def setup(self):
+        self.validator = arg_validators.ValidRawDevice()
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.util.arg_validators.disk.blkid')
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_dmcrypt_device_already_prepared(self,  m_get_single_lv, m_blkid, m_has_bs_label, mocked_device, fake_call, monkeypatch):
+        def mock_call(cmd, **kw):
+            return ('', '', 1)
+        monkeypatch.setattr(process, 'call', mock_call)
+        m_blkid.return_value = {'UUID': '8fd92779-ad78-437c-a06f-275f7170fa74', 'TYPE': 'crypto_LUKS'}
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False
+        )
+        with pytest.raises(SystemExit):
+            self.validator.zap = False
+            self.validator('/dev/foo')
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_already_prepared(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False
+        )
+        with pytest.raises(SystemExit):
+            self.validator.zap = False
+            self.validator('/dev/foo')
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_not_prepared(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call, monkeypatch):
+        def mock_call(cmd, **kw):
+            return ('', '', 1)
+        monkeypatch.setattr(process, 'call', mock_call)
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False
+        )
+        self.validator.zap = False
+        assert self.validator('/dev/foo')
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_has_partition(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call, monkeypatch):
+        def mock_call(cmd, **kw):
+            return ('', '', 1)
+        monkeypatch.setattr(process, 'call', mock_call)
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=True,
+            has_gpt_headers=False,
+            has_fs=False
+        )
+        self.validator.zap = False
         with pytest.raises(RuntimeError):
+            assert self.validator('/dev/foo')
+
+class TestValidBatchDevice(object):
+    def setup(self):
+        self.validator = arg_validators.ValidBatchDevice()
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_is_partition(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False,
+            is_partition=True
+        )
+        with pytest.raises(argparse.ArgumentError):
+            self.validator.zap = False
             self.validator('/dev/foo')
 
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_is_not_partition(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False,
+            is_partition=False
+        )
+        self.validator.zap = False
+        assert self.validator('/dev/foo')
+
+class TestValidBatchDataDevice(object):
+    def setup(self):
+        self.validator = arg_validators.ValidBatchDataDevice()
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_is_partition(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False,
+            is_partition=True
+        )
+        with pytest.raises(argparse.ArgumentError):
+            self.validator.zap = False
+            assert self.validator('/dev/foo')
+
+    @patch('ceph_volume.util.arg_validators.Device')
+    @patch('ceph_volume.util.arg_validators.disk.has_bluestore_label', return_value=False)
+    @patch('ceph_volume.api.lvm.get_single_lv', return_value=None)
+    def test_device_is_not_partition(self,  m_get_single_lv, m_has_bs_label, mocked_device, fake_call):
+        mocked_device.return_value = MagicMock(
+            used_by_ceph=False,
+            exists=True,
+            has_partitions=False,
+            has_gpt_headers=False,
+            has_fs=False,
+            is_partition=False
+        )
+        self.validator.zap = False
+        assert self.validator('/dev/foo')
+
 
 class TestValidFraction(object):
 
index 9793457d4e55dbcb3007b5a36daa5fc0301f357d..655f7cd55ed0f9d882e72a255a267da0524930ee 100644 (file)
@@ -1,10 +1,9 @@
 import argparse
 import os
 import math
-from ceph_volume import terminal
-from ceph_volume import decorators
-from ceph_volume.util import disk
+from ceph_volume import terminal, decorators, process
 from ceph_volume.util.device import Device
+from ceph_volume.util import disk
 
 
 def valid_osd_id(val):
@@ -17,8 +16,13 @@ class ValidDevice(object):
         self.gpt_ok = gpt_ok
 
     def __call__(self, dev_path):
-        device = self._is_valid_device(dev_path)
-        return self._format_device(device)
+        self.get_device(dev_path)
+        self._validated_device = self._is_valid_device()
+        return self._format_device(self._validated_device)
+
+    def get_device(self, dev_path):
+        self._device = Device(dev_path)
+        self.dev_path = dev_path
 
     def _format_device(self, device):
         if self.as_string:
@@ -28,36 +32,101 @@ class ValidDevice(object):
             return device.path
         return device
 
-    def _is_valid_device(self, dev_path):
-        device = Device(dev_path)
+    def _is_valid_device(self):
         error = None
-        if not device.exists:
-            error = "Unable to proceed with non-existing device: %s" % dev_path
+        if not self._device.exists:
+            error = "Unable to proceed with non-existing device: %s" % self.dev_path
         # FIXME this is not a nice API, this validator was meant to catch any
         # non-existing devices upfront, not check for gpt headers. Now this
         # needs to optionally skip checking gpt headers which is beyond
         # verifying if the device exists. The better solution would be to
         # configure this with a list of checks that can be excluded/included on
         # __init__
-        elif device.has_gpt_headers and not self.gpt_ok:
-            error = "GPT headers found, they must be removed on: %s" % dev_path
-        if device.has_partitions:
-            raise RuntimeError("Device {} has partitions.".format(dev_path))
+        elif self._device.has_gpt_headers and not self.gpt_ok:
+            error = "GPT headers found, they must be removed on: %s" % self.dev_path
+        if self._device.has_partitions:
+            raise RuntimeError("Device {} has partitions.".format(self.dev_path))
         if error:
             raise argparse.ArgumentError(None, error)
-        return device
+        return self._device
 
 
-class ValidBatchDevice(ValidDevice):
+class ValidZapDevice(ValidDevice):
+    def __call__(self, dev_path):
+        super().get_device(dev_path)
+        return self._format_device(self._is_valid_device())
+
+    def _is_valid_device(self, raise_sys_exit=True):
+        super()._is_valid_device()
+        return self._device
+
 
+class ValidDataDevice(ValidDevice):
     def __call__(self, dev_path):
-        dev = self._is_valid_device(dev_path)
-        if dev.is_partition:
+        super().get_device(dev_path)
+        return self._format_device(self._is_valid_device())
+
+    def _is_valid_device(self, raise_sys_exit=True):
+        super()._is_valid_device()
+        if self._device.used_by_ceph:
+            terminal.info('Device {} is already prepared'.format(self.dev_path))
+            if raise_sys_exit:
+                raise SystemExit(0)
+        if self._device.has_fs and not self._device.used_by_ceph:
+            raise RuntimeError("Device {} has a filesystem.".format(self.dev_path))
+        if self.dev_path[0] == '/' and disk.has_bluestore_label(self.dev_path):
+            raise RuntimeError("Device {} has bluestore signature.".format(self.dev_path))
+        return self._device
+
+class ValidRawDevice(ValidDevice):
+    def __call__(self, dev_path):
+        super().get_device(dev_path)
+        return self._format_device(self._is_valid_device())
+
+    def _is_valid_device(self, raise_sys_exit=True):
+        out, err, rc = process.call([
+           'ceph-bluestore-tool', 'show-label',
+           '--dev', self.dev_path], verbose_on_failure=False)
+        if not rc:
+            terminal.info("Raw device {} is already prepared.".format(self.dev_path))
+            raise SystemExit(0)
+        if disk.blkid(self.dev_path).get('TYPE') == 'crypto_LUKS':
+            terminal.info("Raw device {} might already be in use for a dmcrypt OSD, skipping.".format(self.dev_path))
+            raise SystemExit(0)
+        super()._is_valid_device()
+        return self._device
+
+class ValidBatchDevice(ValidDevice):
+    def __call__(self, dev_path):
+        super().get_device(dev_path)
+        return self._format_device(self._is_valid_device())
+
+    def _is_valid_device(self, raise_sys_exit=False):
+        super()._is_valid_device()
+        if self._device.is_partition:
             raise argparse.ArgumentError(
                 None,
                 '{} is a partition, please pass '
-                'LVs or raw block devices'.format(dev_path))
-        return self._format_device(dev)
+                'LVs or raw block devices'.format(self.dev_path))
+        return self._device
+
+
+class ValidBatchDataDevice(ValidBatchDevice, ValidDataDevice):
+    def __call__(self, dev_path):
+        super().get_device(dev_path)
+        return self._format_device(self._is_valid_device())
+
+    def _is_valid_device(self):
+        # if device is already used by ceph,
+        # leave the validation to Batch.get_deployment_layout()
+        # This way the idempotency isn't broken (especially when using --osds-per-device)
+        for lv in self._device.lvs:
+            if lv.tags.get('ceph.type') in ['db', 'wal', 'journal']:
+                return self._device
+        if self._device.used_by_ceph:
+            return self._device
+        super()._is_valid_device(raise_sys_exit=False)
+        return self._device
 
 
 class OSDPath(object):
index c30a094b6adeadbc75789b7c2f872d959b9f2a15..feddb59d99c50e70e95ae9f04f55fce36bec6c47 100644 (file)
@@ -301,6 +301,10 @@ class Device(object):
     def exists(self):
         return os.path.exists(self.abspath)
 
+    @property
+    def has_fs(self):
+        return 'TYPE' in self.blkid_api
+
     @property
     def has_gpt_headers(self):
         return self.blkid_api.get("PTTYPE") == "gpt"