]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
qa/cephfs: test case for auto reconnect after blacklisted 31200/head
authorYan, Zheng <zyan@redhat.com>
Mon, 28 Oct 2019 07:39:44 +0000 (15:39 +0800)
committerYan, Zheng <zyan@redhat.com>
Wed, 15 Jan 2020 07:43:17 +0000 (15:43 +0800)
Signed-off-by: "Yan, Zheng" <zyan@redhat.com>
Fixes: https://tracker.ceph.com/issues/42085
qa/tasks/cephfs/fuse_mount.py
qa/tasks/cephfs/kernel_mount.py
qa/tasks/cephfs/mount.py
qa/tasks/cephfs/test_client_recovery.py
qa/tasks/vstart_runner.py

index 626a0682f4ad67a1b2c498b43a9be4676c373ddc..27d89437df7a23cf2b3ea2e2b77245bbe8de0755 100644 (file)
@@ -24,13 +24,13 @@ class FuseMount(CephFSMount):
         self.inst = None
         self.addr = None
 
-    def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None):
+    def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None, mount_options=[]):
         if mountpoint is not None:
             self.mountpoint = mountpoint
         self.setupfs(name=mount_fs_name)
 
         try:
-            return self._mount(mount_path, mount_fs_name)
+            return self._mount(mount_path, mount_fs_name, mount_options)
         except RuntimeError:
             # Catch exceptions by the mount() logic (i.e. not remote command
             # failures) and ensure the mount is not left half-up.
@@ -40,7 +40,7 @@ class FuseMount(CephFSMount):
             self.umount_wait(force=True)
             raise
 
-    def _mount(self, mount_path, mount_fs_name):
+    def _mount(self, mount_path, mount_fs_name, mount_options):
         log.info("Client client.%s config is %s" % (self.client_id, self.client_config))
 
         daemon_signal = 'kill'
@@ -70,6 +70,8 @@ class FuseMount(CephFSMount):
         if mount_fs_name is not None:
             fuse_cmd += ["--client_mds_namespace={0}".format(mount_fs_name)]
 
+        fuse_cmd += mount_options
+
         fuse_cmd += [
             '--name', 'client.{id}'.format(id=self.client_id),
             # TODO ceph-fuse doesn't understand dash dash '--',
index b90749f259934417d04b0c3d349f8814a64eeea7..86a06006df752e084ccee6749e2976c7c48b8fac 100644 (file)
@@ -27,7 +27,7 @@ class KernelMount(CephFSMount):
         self.ipmi_password = ipmi_password
         self.ipmi_domain = ipmi_domain
 
-    def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None):
+    def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None, mount_options=[]):
         if mountpoint is not None:
             self.mountpoint = mountpoint
         self.setupfs(name=mount_fs_name)
@@ -47,6 +47,9 @@ class KernelMount(CephFSMount):
         if mount_fs_name is not None:
             opts += ",mds_namespace={0}".format(mount_fs_name)
 
+        for mount_opt in mount_options :
+            opts += ",{0}".format(mount_opt)
+
         self.client_remote.run(
             args=[
                 'sudo',
index 4d7375f6d5c0693dcd21ad4b446d6bdd6149d95f..aeed4fa2043466c7d743332caafc13ed405a3ad8 100644 (file)
@@ -58,7 +58,7 @@ class CephFSMount(object):
         self.fs.wait_for_daemons()
         log.info('Ready to start {}...'.format(type(self).__name__))
 
-    def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None):
+    def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None, mount_options=[]):
         raise NotImplementedError()
 
     def umount(self):
index 7bc567aed83c87e0ffb5bd1a259d9141c03bbed9..e18fe997e4d1b0e48b91f4875eea42a5987caddf 100644 (file)
@@ -10,6 +10,7 @@ import distutils.version as version
 import re
 import os
 
+from teuthology.orchestra import run
 from teuthology.orchestra.run import CommandFailedError, ConnectionLostError
 from tasks.cephfs.fuse_mount import FuseMount
 from tasks.cephfs.cephfs_test_case import CephFSTestCase
@@ -629,3 +630,95 @@ class TestClientRecovery(CephFSTestCase):
         self.assert_session_count(1)
 
         self.mount_a.kill_cleanup()
+
+    def test_reconnect_after_blacklisted(self):
+        """
+        Test reconnect after blacklisted.
+        - writing to a fd that was opened before blacklist should return -EBADF
+        - reading/writing to a file with lost file locks should return -EIO
+        - readonly fd should continue to work
+        """
+
+        self.mount_a.umount_wait()
+
+        if isinstance(self.mount_a, FuseMount):
+            self.skipTest("Not implemented in FUSE client yet")
+        else:
+            try:
+                self.mount_a.mount(mount_options=['recover_session=clean'])
+            except CommandFailedError:
+                self.mount_a.kill_cleanup()
+                self.skipTest("Not implemented in current kernel")
+
+        self.mount_a.wait_until_mounted()
+
+        path = os.path.join(self.mount_a.mountpoint, 'testfile_reconnect_after_blacklisted')
+        pyscript = dedent("""
+            import os
+            import sys
+            import fcntl
+            import errno
+            import time
+
+            fd1 = os.open("{path}.1", os.O_RDWR | os.O_CREAT, 0O666)
+            fd2 = os.open("{path}.1", os.O_RDONLY)
+            fd3 = os.open("{path}.2", os.O_RDWR | os.O_CREAT, 0O666)
+            fd4 = os.open("{path}.2", os.O_RDONLY)
+
+            os.write(fd1, b'content')
+            os.read(fd2, 1);
+
+            os.write(fd3, b'content')
+            os.read(fd4, 1);
+            fcntl.flock(fd4, fcntl.LOCK_SH | fcntl.LOCK_NB)
+
+            print("blacklist")
+            sys.stdout.flush()
+
+            sys.stdin.readline()
+
+            # wait for mds to close session
+            time.sleep(10);
+
+            # trigger 'open session' message. kclient relies on 'session reject' message
+            # to detect if itself is blacklisted
+            try:
+                os.stat("{path}.1")
+            except:
+                pass
+
+            # wait for auto reconnect
+            time.sleep(10);
+
+            try:
+                os.write(fd1, b'content')
+            except OSError as e:
+                if e.errno != errno.EBADF:
+                    raise
+            else:
+                raise RuntimeError("write() failed to raise error")
+
+            os.read(fd2, 1);
+
+            try:
+                os.read(fd4, 1)
+            except OSError as e:
+                if e.errno != errno.EIO:
+                    raise
+            else:
+                raise RuntimeError("read() failed to raise error")
+            """).format(path=path)
+        rproc = self.mount_a.client_remote.run(
+                    args=['sudo', 'python3', '-c', pyscript],
+                    wait=False, stdin=run.PIPE, stdout=run.PIPE)
+
+        rproc.stdout.readline()
+
+        mount_a_client_id = self.mount_a.get_global_id()
+        self.fs.mds_asok(['session', 'evict', "%s" % mount_a_client_id])
+
+        rproc.stdin.writelines(['done\n'])
+        rproc.stdin.flush()
+
+        rproc.wait()
+        self.assertEqual(rproc.exitstatus, 0)
index de6922e61151c9749f20eb1bb113d3d055ad2552..6b5f90b687ec1fd68ad1d39e77fc16e943c52552 100644 (file)
@@ -638,7 +638,7 @@ class LocalKernelMount(KernelMount):
         rproc.wait()
         self.mounted = False
 
-    def mount(self, mount_path=None, mount_fs_name=None):
+    def mount(self, mount_path=None, mount_fs_name=None, mount_options=[]):
         self.setupfs(name=mount_fs_name)
 
         log.info('Mounting kclient client.{id} at {remote} {mnt}...'.format(
@@ -662,6 +662,9 @@ class LocalKernelMount(KernelMount):
         if mount_fs_name is not None:
             opts += ",mds_namespace={0}".format(mount_fs_name)
 
+        for mount_opt in mount_options:
+            opts += ",{0}".format(mount_opt)
+
         self.client_remote.run(
             args=[
                 'sudo',
@@ -800,7 +803,7 @@ class LocalFuseMount(FuseMount):
         if self.is_mounted():
             super(LocalFuseMount, self).umount()
 
-    def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None):
+    def mount(self, mount_path=None, mount_fs_name=None, mountpoint=None, mount_options=[]):
         if mountpoint is not None:
             self.mountpoint = mountpoint
         self.setupfs(name=mount_fs_name)
@@ -841,6 +844,8 @@ class LocalFuseMount(FuseMount):
         if mount_fs_name is not None:
             prefix += ["--client_mds_namespace={0}".format(mount_fs_name)]
 
+        prefix += mount_options;
+
         self.fuse_daemon = self.client_remote.run(args=
                                             prefix + [
                                                 "-f",