]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
qa: add test_rbd_wnbd resize test
authorLucian Petrut <lpetrut@cloudbasesolutions.com>
Fri, 6 Jan 2023 10:58:00 +0000 (12:58 +0200)
committerLucian Petrut <lpetrut@cloudbasesolutions.com>
Mon, 16 Jan 2023 08:12:12 +0000 (10:12 +0200)
We're adding a test for the newly introduced live resize feature.
It will simply extend/shrink the image, wait for the new size to
be picked up and then run FIO tests to validate the resized image.

While at it, we're fixing two unrelated linter warnings:

  E275 missing whitespace after keyword

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

index 720fd3ff54179b9d50fd03764e7745086850ffd6..b64b9fc639baa5680d43df2338b3db6b612ba424 100644 (file)
@@ -419,6 +419,8 @@ class RbdImage(object):
         if not self.mapped:
             raise CephTestException("Unable to create fs, image not mapped.")
 
+        LOG.info("Initializing fs, image: %s.", self.name)
+
         self._init_disk()
         self._create_partition()
         self._format_volume()
@@ -434,6 +436,49 @@ class RbdImage(object):
 
         return int(result.stdout.decode().strip())
 
+    @Tracer.trace
+    def resize(self, new_size_mb, allow_shrink=False):
+        LOG.info(
+            "Resizing image: %s. New size: %s MB, old size: %s MB",
+            self.name, new_size_mb, self.size_mb)
+
+        cmd = ["rbd", "resize", self.name,
+               "--size", f"{new_size_mb}M", "--no-progress"]
+        if allow_shrink:
+            cmd.append("--allow-shrink")
+
+        execute(*cmd)
+
+        self.size_mb = new_size_mb
+
+    @Tracer.trace
+    def get_disk_size(self):
+        """Retrieve the virtual disk size (bytes) reported by Windows."""
+        cmd = f"(Get-Disk -Number {self.disk_number}).Size"
+        result = ps_execute(cmd)
+
+        disk_size = result.stdout.decode().strip()
+        if not disk_size.isdigit():
+            raise CephTestException(
+                "Invalid disk size received: %s" % disk_size)
+
+        return int(disk_size)
+
+    @Tracer.trace
+    @retry_decorator(timeout=30)
+    def wait_for_disk_resize(self):
+        # After resizing the rbd image, the daemon is expected to receive
+        # the notification, inform the WNBD driver and then trigger a disk
+        # rescan (IOCTL_DISK_UPDATE_PROPERTIES). This might take a few seconds,
+        # so we'll need to do some polling.
+        disk_size = self.get_disk_size()
+        disk_size_mb = disk_size // (1 << 20)
+
+        if disk_size_mb != self.size_mb:
+            raise CephTestException(
+                "The disk size hasn't been updated yet. Retrieved size: "
+                f"{disk_size_mb}MB. Expected size: {self.size_mb}MB.")
+
 
 class RbdTest(object):
     image: RbdImage
@@ -548,14 +593,14 @@ class RbdFioTest(RbdTest):
         return self.image.path
 
     @Tracer.trace
-    def run(self):
+    def _run_fio(self, fio_size_mb=None):
         LOG.info("Starting FIO test.")
         cmd = [
             "fio", "--thread", "--output-format=json",
             "--randrepeat=%d" % self.iterations,
             "--direct=1", "--gtod_reduce=1", "--name=test",
             "--bs=%s" % self.bs, "--iodepth=%s" % self.iodepth,
-            "--size=%sM" % self.fio_size_mb,
+            "--size=%sM" % (fio_size_mb or self.fio_size_mb),
             "--readwrite=%s" % self.op,
             "--numjobs=%s" % self.workers,
             "--filename=%s" % self._get_fio_path(),
@@ -566,6 +611,10 @@ class RbdFioTest(RbdTest):
         LOG.info("Completed FIO test.")
         self.process_result(result.stdout)
 
+    @Tracer.trace
+    def run(self):
+        self._run_fio()
+
     @classmethod
     def print_results(cls,
                       title: str = "Benchmark results",
@@ -616,6 +665,30 @@ class RbdFioTest(RbdTest):
             print(table)
 
 
+class RbdResizeFioTest(RbdFioTest):
+    """Image resize test.
+
+    This test extends and then shrinks the image, performing FIO tests to
+    validate the resized image.
+    """
+
+    @Tracer.trace
+    def run(self):
+        self.image.resize(self.image_size_mb * 2)
+        self.image.wait_for_disk_resize()
+
+        self._run_fio(fio_size_mb=self.image_size_mb * 2)
+
+        self.image.resize(self.image_size_mb // 2, allow_shrink=True)
+        self.image.wait_for_disk_resize()
+
+        self._run_fio(fio_size_mb=self.image_size_mb // 2)
+
+        # Just like rbd-nbd, rbd-wnbd is masking out-of-bounds errors.
+        # For this reason, we don't have a negative test that writes
+        # passed the disk boundary.
+
+
 class RbdFsFioTest(RbdFsTestMixin, RbdFioTest):
     def initialize(self):
         super(RbdFsFioTest, self).initialize()
@@ -677,12 +750,12 @@ class RbdStampTest(RbdTest):
             time.sleep(self._rand_float(0, 5))
 
             stamp = self._read_stamp()
-            assert(stamp == b'\0' * len(self._get_stamp()))
+            assert stamp == b'\0' * len(self._get_stamp())
 
         self._write_stamp()
 
         stamp = self._read_stamp()
-        assert(stamp == self._get_stamp())
+        assert stamp == self._get_stamp()
 
 
 class RbdFsStampTest(RbdFsTestMixin, RbdStampTest):
@@ -766,6 +839,7 @@ class TestRunner(object):
 TESTS: typing.Dict[str, typing.Type[RbdTest]] = {
     'RbdTest': RbdTest,
     'RbdFioTest': RbdFioTest,
+    'RbdResizeFioTest': RbdResizeFioTest,
     'RbdStampTest': RbdStampTest,
     # FS tests
     'RbdFsTest': RbdFsTest,