]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
qa: add fs tests to test_rbd_wnbd.py 48929/head
authorLucian Petrut <lpetrut@cloudbasesolutions.com>
Mon, 12 Dec 2022 10:13:41 +0000 (12:13 +0200)
committerLucian Petrut <lpetrut@cloudbasesolutions.com>
Fri, 16 Dec 2022 10:20:02 +0000 (12:20 +0200)
The Windows rbd-wnbd python test performs various IO operations
against raw disks.

However, it can be useful to test overlaying filesystems as well.
For this reason, we're adding the following tests:

* RbdFsTest
* RbdFsFioTest
* RbdFsStampFioTest

To simplify the implementation, those tests reuse the existing
ones along with a mixin class (RbdFsTestMixin).

Signed-off-by: Lucian Petrut <lpetrut@cloudbasesolutions.com>
qa/workunits/windows/test_rbd_wnbd.py

index f16cb22cdfd3bbd324519bc106f453d0680b5c45..092a8ae14891f4cc67e470369bfd83fc00fc9f19 100644 (file)
@@ -3,6 +3,7 @@ import collections
 import json
 import logging
 import math
+import os
 import prettytable
 import random
 import subprocess
@@ -213,6 +214,7 @@ class RbdImage(object):
         self.disk_number = disk_number
         self.mapped = mapped
         self.removed = False
+        self.drive_letter = ""
 
     @classmethod
     @Tracer.trace
@@ -343,11 +345,44 @@ class RbdImage(object):
         finally:
             self.remove()
 
+    @Tracer.trace
+    def init_fs(self):
+        if not self.mapped:
+            raise CephTestException("Unable to create fs, image not mapped.")
+
+        cmd = ("powershell.exe", "-command",
+               f"Get-Disk -Number {self.disk_number} | "
+               "Initialize-Disk -PassThru | "
+               "New-Partition -AssignDriveLetter -UseMaximumSize | "
+               "Format-Volume -Force -Confirm:$false")
+        execute(*cmd)
+
+        # Retrieve the drive letter.
+        cmd = (
+            "powershell.exe", "-command",
+            f"(Get-Partition -DiskNumber {self.disk_number})[0].DriveLetter")
+        result = execute(*cmd)
+
+        self.drive_letter = result.stdout.decode().strip()
+        if len(self.drive_letter) != 1:
+            raise CephTestException(
+                "Invalid drive letter received: %s" % self.drive_letter)
+
+    @Tracer.trace
+    def get_fs_capacity(self):
+        if not self.drive_letter:
+            raise CephTestException("No drive letter available")
+
+        cmd = ("powershell.exe", "-command",
+               f"(Get-Volume -DriveLetter {self.drive_letter}).Size")
+        result = execute(*cmd)
+
+        return int(result.stdout.decode().strip())
+
 
 class RbdTest(object):
     image: RbdImage
 
-    # Windows disks must be turned online before accessing partitions.
     requires_disk_online = False
     requires_disk_write = False
 
@@ -389,6 +424,26 @@ class RbdTest(object):
         pass
 
 
+class RbdFsTestMixin(object):
+    # Windows disks must be turned online before accessing partitions.
+    requires_disk_online = True
+    requires_disk_write = True
+
+    @Tracer.trace
+    def initialize(self):
+        super(RbdFsTestMixin, self).initialize()
+
+        self.image.init_fs()
+
+    def get_subpath(self, *args):
+        drive_path = f"{self.image.drive_letter}:\\"
+        return os.path.join(drive_path, *args)
+
+
+class RbdFsTest(RbdFsTestMixin, RbdTest):
+    pass
+
+
 class RbdFioTest(RbdTest):
     data: typing.DefaultDict[str, typing.List[typing.Dict[str, str]]] = (
         collections.defaultdict(list))
@@ -434,6 +489,9 @@ class RbdFioTest(RbdTest):
                             'dropped_ios': job[op]['short_ios'],
                         })
 
+    def _get_fio_path(self):
+        return self.image.path
+
     @Tracer.trace
     def run(self):
         LOG.info("Starting FIO test.")
@@ -445,7 +503,7 @@ class RbdFioTest(RbdTest):
             "--size=%sM" % self.fio_size_mb,
             "--readwrite=%s" % self.op,
             "--numjobs=%s" % self.workers,
-            "--filename=%s" % self.image.path,
+            "--filename=%s" % self._get_fio_path(),
         ]
         if self.verify:
             cmd += ["--verify=%s" % self.verify]
@@ -503,9 +561,33 @@ class RbdFioTest(RbdTest):
             print(table)
 
 
+class RbdFsFioTest(RbdFsTestMixin, RbdFioTest):
+    def initialize(self):
+        super(RbdFsFioTest, self).initialize()
+
+        if not self.fio_size_mb or self.fio_size_mb == self.image_size_mb:
+            # Out of caution, we'll use up to 80% of the FS by default
+            self.fio_size_mb = int(
+                self.image.get_fs_capacity() * 0.8 / (1024 * 1024))
+
+    @staticmethod
+    def _fio_escape_path(path):
+        # FIO allows specifying multiple files separated by colon.
+        # This means that ":" has to be escaped, so
+        # F:\filename becomes F\:\filename.
+        return path.replace(":", "\\:")
+
+    def _get_fio_path(self):
+        return self._fio_escape_path(self.get_subpath("test-fio"))
+
+
 class RbdStampTest(RbdTest):
     requires_disk_write = True
 
+    _write_open_mode = "rb+"
+    _read_open_mode = "rb"
+    _expect_path_exists = True
+
     @staticmethod
     def _rand_float(min_val: float, max_val: float):
         return min_val + (random.random() * max_val - min_val)
@@ -516,24 +598,31 @@ class RbdStampTest(RbdTest):
         buff += b'\0' * padding
         return buff
 
+    def _get_stamp_path(self):
+        return self.image.path
+
     @Tracer.trace
     def _write_stamp(self):
-        with open(self.image.path, 'rb+') as disk:
+        with open(self._get_stamp_path(), self._write_open_mode) as disk:
             stamp = self._get_stamp()
             disk.write(stamp)
 
     @Tracer.trace
     def _read_stamp(self):
-        with open(self.image.path, 'rb') as disk:
+        with open(self._get_stamp_path(), self._read_open_mode) as disk:
             return disk.read(len(self._get_stamp()))
 
     @Tracer.trace
     def run(self):
-        # Wait up to 5 seconds and then check the disk,
-        # ensuring that nobody else wrote to it.
-        time.sleep(self._rand_float(0, 5))
-        stamp = self._read_stamp()
-        assert(stamp == b'\0' * len(self._get_stamp()))
+        if self._expect_path_exists:
+            # Wait up to 5 seconds and then check the disk, ensuring that
+            # nobody else wrote to it. This is particularly useful when
+            # running a high number of tests in parallel, ensuring that
+            # we aren't writing to the wrong disk.
+            time.sleep(self._rand_float(0, 5))
+
+            stamp = self._read_stamp()
+            assert(stamp == b'\0' * len(self._get_stamp()))
 
         self._write_stamp()
 
@@ -541,6 +630,14 @@ class RbdStampTest(RbdTest):
         assert(stamp == self._get_stamp())
 
 
+class RbdFsStampTest(RbdFsTestMixin, RbdStampTest):
+    _write_open_mode = "wb"
+    _expect_path_exists = False
+
+    def _get_stamp_path(self):
+        return self.get_subpath("test-stamp")
+
+
 class TestRunner(object):
     def __init__(self,
                  test_cls: typing.Type[RbdTest],
@@ -614,7 +711,11 @@ class TestRunner(object):
 TESTS: typing.Dict[str, typing.Type[RbdTest]] = {
     'RbdTest': RbdTest,
     'RbdFioTest': RbdFioTest,
-    'RbdStampTest': RbdStampTest
+    'RbdStampTest': RbdStampTest,
+    # FS tests
+    'RbdFsTest': RbdFsTest,
+    'RbdFsFioTest': RbdFsFioTest,
+    'RbdFsStampTest': RbdFsStampTest,
 }
 
 if __name__ == '__main__':