]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
ceph-volume/batch: fail on filtered devices when non-interactive 33202/head
authorJan Fajerski <jfajerski@suse.com>
Tue, 3 Dec 2019 12:44:00 +0000 (13:44 +0100)
committerJan Fajerski <jfajerski@suse.com>
Tue, 11 Feb 2020 10:59:06 +0000 (11:59 +0100)
When batch is called non-interactively and a user explicitly specifies,
say a db-device, this will be filtered when unavailable. This can cause
the resulting OSD to be very different from the users intention
(standalone vs external db when the db-device was filtered). If devices
get filtered in non-interactive mode, ceph-volume should fail.

Fixes: https://tracker.ceph.com/issues/43105
Signed-off-by: Jan Fajerski <jfajerski@suse.com>
(cherry picked from commit 2e985053deec6c4cf60c0b85aec3df16cd77ceeb)

src/ceph-volume/ceph_volume/devices/lvm/batch.py
src/ceph-volume/ceph_volume/tests/devices/lvm/test_batch.py

index 8685f588c61e0c5ca217d09c47c4b99ed4aa847d..27ee5bb47e006f4bc9d5aa5252c1b21a9f4c91c1 100644 (file)
@@ -325,7 +325,6 @@ class Batch(object):
             self.execute()
 
     def _get_explicit_strategy(self):
-        # TODO assert that none of the device lists overlap?
         self._filter_devices()
         self._ensure_disjoint_device_lists()
         if self.args.bluestore:
@@ -355,18 +354,22 @@ class Batch(object):
         # filter devices by their available property.
         # TODO: Some devices are rejected in the argparser already. maybe it
         # makes sense to unifiy this
-        used_reason = {"reasons": ["Used by ceph as a data device already"]}
+        used_reason = {"reasons": ["Used by ceph already"]}
         self.filtered_devices = {}
         for dev_list in ['', 'db_', 'wal_', 'journal_']:
             dev_list_prop = '{}devices'.format(dev_list)
             if hasattr(self.args, dev_list_prop):
                 usable_dev_list_prop = '{}usable'.format(dev_list)
-                usable = [d for d in getattr(self.args, dev_list_prop) if
-                          d.available]
+                devs = getattr(self.args, dev_list_prop)
+                usable = [d for d in devs if d.available]
                 setattr(self, usable_dev_list_prop, usable)
                 self.filtered_devices.update({d: used_reason for d in
                                               getattr(self.args, dev_list_prop)
                                               if d.used_by_ceph})
+                if self.args.yes and dev_list and devs != usable:
+                    err = '{} devices were filtered in non-interactive mode, bailing out'
+                    raise RuntimeError(err.format(len(devs) - len(usable)))
+
 
     def _ensure_disjoint_device_lists(self):
         # check that all device lists are disjoint with each other
index c6b3f3b6d4dfea31a704b233296e4d76057ff530..1c3c25f67e310299852dbaa85bbc0abda6619eeb 100644 (file)
@@ -126,3 +126,26 @@ class TestFilterDevices(object):
         result, filtered_devices = batch.filter_devices(args)
         assert result
         assert len(filtered_devices) == 1
+
+    def test_no_auto_fails_on_unavailable_device(self, factory):
+        hdd1 = factory(
+            used_by_ceph=False,
+            abspath="/dev/sda",
+            rotational=True,
+            is_lvm_member=False,
+            available=True
+        )
+        ssd1 = factory(
+            used_by_ceph=True,
+            abspath="/dev/nvme0n1",
+            rotational=False,
+            is_lvm_member=True,
+            available=False
+        )
+        args = factory(devices=[hdd1], db_devices=[ssd1], filtered_devices={},
+                      yes=True)
+        b = batch.Batch([])
+        b.args = args
+        with pytest.raises(RuntimeError) as ex:
+            b._filter_devices()
+            assert '1 devices were filtered in non-interactive mode, bailing out' in str(ex.value)