+import errno
import pytest
import stat
from typing import Any, Callable, ClassVar, Optional
fake_filesystem.create_dir(device_path)
assert not disk.has_bluestore_label(device_path)
+ def test_bluestore_label_at_replica_offset(self):
+ class _FakeBlockDev:
+ def __init__(self, label_offset):
+ self._label_offset = label_offset
+ self._pos = 0
+
+ def seek(self, offset, whence=0):
+ if whence != 0:
+ raise NotImplementedError
+ self._pos = offset
+ return offset
+
+ def read(self, n):
+ buf_start = self._pos
+ self._pos += n
+ sig = disk.BLUESTORE_BDEV_LABEL_SIGNATURE
+ if buf_start == self._label_offset:
+ return sig[:n] + (b'\x00' * max(0, n - len(sig)))
+ return b'\x00' * n
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ return None
+
+ for off in disk.BLUESTORE_BDEV_LABEL_OFFSETS[1:3]:
+ with patch('builtins.open', lambda *a, **k: _FakeBlockDev(off)):
+ assert disk.has_bluestore_label('/dev/fake'), off
+
+ def test_bluestore_label_non_seekable_returns_false(self):
+ class _NonSeekable:
+ def seek(self, offset, whence=0):
+ raise OSError(errno.EINVAL, 'Invalid argument')
+
+ def read(self, n):
+ return b'\x00' * n
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ return None
+
+ with patch('builtins.open', lambda *a, **k: _NonSeekable()):
+ assert not disk.has_bluestore_label('/dev/fake')
+
+ def test_bluestore_label_non_seekable_fallback_read_matches(self):
+ class _NonSeekableWithLabelAtStart:
+ def seek(self, offset, whence=0):
+ raise OSError(errno.EINVAL, 'Invalid argument')
+
+ def read(self, n):
+ return disk.BLUESTORE_BDEV_LABEL_SIGNATURE[:n] + (
+ b'\x00' * max(0, n - len(disk.BLUESTORE_BDEV_LABEL_SIGNATURE))
+ )
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ return None
+
+ with patch('builtins.open', lambda *a, **k: _NonSeekableWithLabelAtStart()):
+ assert disk.has_bluestore_label('/dev/fake')
+
+ def test_bluestore_label_io_error_propagates(self):
+ class _EIOOnSeek:
+ def seek(self, offset, whence=0):
+ raise OSError(errno.EIO, 'Input/output error')
+
+ def read(self, n):
+ return b'\x00' * n
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ return None
+
+ with patch('builtins.open', lambda *a, **k: _EIOOnSeek()):
+ with pytest.raises(OSError) as excinfo:
+ disk.has_bluestore_label('/dev/fake')
+ assert excinfo.value.errno == errno.EIO
+
class TestBlockSysFs(TestCase):
def setUp(self) -> None:
+import errno
import logging
import os
import re
logger = logging.getLogger(__name__)
+_ONE_GIB = 1024 * 1024 * 1024
+BLUESTORE_BDEV_LABEL_OFFSETS = (
+ 0,
+ _ONE_GIB,
+ 10 * _ONE_GIB,
+ 100 * _ONE_GIB,
+ 1000 * _ONE_GIB,
+)
+BLUESTORE_BDEV_LABEL_SIGNATURE = b'bluestore block device'
+
# The blkid CLI tool has some oddities which prevents having one common call
# to extract the information instead of having separate utilities. The `udev`
return device_facts
def has_bluestore_label(device_path: str) -> bool:
- isBluestore = False
- bluestoreDiskSignature = 'bluestore block device' # 22 bytes long
-
- # throws OSError on failure
logger.info("opening device {} to check for BlueStore label".format(device_path))
+ sig_len = len(BLUESTORE_BDEV_LABEL_SIGNATURE)
try:
with open(device_path, "rb") as fd:
- # read first 22 bytes looking for bluestore disk signature
- signature = fd.read(22)
- if signature.decode('ascii', 'replace') == bluestoreDiskSignature:
- isBluestore = True
+ for position in BLUESTORE_BDEV_LABEL_OFFSETS:
+ try:
+ fd.seek(position)
+ except OSError as exc:
+ err = exc.errno
+ if err is None or err not in (
+ errno.EINVAL,
+ errno.ESPIPE,
+ errno.ENOTTY,
+ ):
+ raise
+ if position != 0:
+ continue
+ signature = fd.read(sig_len)
+ if signature == BLUESTORE_BDEV_LABEL_SIGNATURE:
+ return True
except IsADirectoryError:
logger.info(f'{device_path} is a directory, skipping.')
- return isBluestore
+ return False
def has_seastore_label(device_path: str) -> bool:
is_seastore = False