]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
qa: add test to verify recovery of alternate_name from journal
authorPatrick Donnelly <pdonnell@redhat.com>
Wed, 15 May 2024 02:39:05 +0000 (22:39 -0400)
committerPatrick Donnelly <pdonnell@redhat.com>
Mon, 24 Jun 2024 15:52:07 +0000 (11:52 -0400)
Test without the fix:

    2024-05-16T22:34:21.781 DEBUG:teuthology.orchestra.run.smithi044:> sudo adjust-ulimits ceph-coverage /home/ubuntu/cephtest/archive/coverage timeout 300 ceph --cluster ceph --admin-daemon /var/run/ceph/ceph-mds.a.asok --format=json dump tree /dir 0
    ...
    [
      {
        "accounted_rstat": {
          "rbytes": 4096,
          "rctime": "2024-05-16T22:34:15.251864+0000",
          "rfiles": 1,
          "rsnaps": 0,
          "rsubdirs": 1,
          "version": 0
        },
        "atime": "2024-05-16T22:34:14.498880+0000",
        "auth_pins": 0,
        "auth_state": {
          "replicas": {}
        },
        "authlock": {},
        "backtrace_version": 14,
        "btime": "2024-05-16T22:34:14.498880+0000",
        "change_attr": 3,
        "client_caps": [
          {
            "client_id": 5184,
            "issued": "pAsLsXsFsx",
            "last_sent": 4,
            "pending": "pAsLsXsFsx",
            "wanted": "pAsLsXsFsxcral"
          }
        ],
        "client_ranges": [],
        "ctime": "2024-05-16T22:34:15.249864+0000",
        "damage_flags": 0,
        "dir_layout": {
          "dir_hash": 2,
          "unused1": 0,
          "unused2": 0,
          "unused3": 0
        },
        "dirfrags": [
          {
            "auth_pins": 0,
            "auth_state": {
              "replicas": {}
            },
            "committed_version": "0",
            "committing_version": "0",
            "dentries": [
              {
                "alternate_name": "bUHUwH9E8uiVaf8xZ+zONcB1CToj53x5aUUnKdnNj5U37zbh28l1AaWwHhbOT3HyzqKjmSKKW1o4odQJc7nF9xrKIB8D3b4qqb2Cs6s7t2106hHhQk5/YV7DtpeNPZnorcTqxPM/hExtWHSS4P+S+Dpwj62hMyh/77sGhiW1Filvv1gQjV+sN/GozPNwHgfleadkUs1OkRkYtgWrCjbKP0MayRtiOLrVTRuYyOp/Qt3+XCIyiS87B9bUcOFjWratF+yR0kpJ0RYriix7NKVkBJ0kGWYSCY+PYjiLeMYJBMQcCxW/nwfVku+m6fgFJvb6pjEFxIk9zT5cunSImsjr",
                "auth_pins": 0,
                "auth_state": {
                  "replicas": {}
                },
                "inode": 1099511628283,
                "is_auth": true,
                "is_freezing": false,
                "is_frozen": false,
                "is_new": false,
                "is_null": false,
                "is_primary": true,
                "is_remote": false,
                "lock": {},
                "nref": 2,
                "path": "dir/bUHUwH9E8uiVaf8xZ+zONcB1CToj53x5aUUnKdnNj5U37zbh28l1AaWwHhbOT3HyzqKjmSKKW1o4odQJc7nF9xrKIB8D3b4qqb2Cs6s7t2106hHhQk5,YV7DtpeNPZnorcTqxPM,hExtWHSS4P+S+Dpwj62hMyh,77sGhiW1Filvv1gQjV+sN,GozPNwHgfleadkUuZ+PMLCaKQXhuid9WvmHanxJnaabYDLj4VEz+EX2WsG",
    ...
    # fail + journal recovery
    2024-05-16T22:35:31.077 DEBUG:teuthology.orchestra.run.smithi044:> sudo adjust-ulimits ceph-coverage /home/ubuntu/cephtest/archive/coverage timeout 300 ceph --cluster ceph --admin-daemon /var/run/ceph/ceph-mds.a.asok --format=json dump tree /dir 0
    ...
    [
      {
        "accounted_rstat": {
          "rbytes": 4096,
          "rctime": "2024-05-16T22:34:15.251864+0000",
          "rfiles": 1,
          "rsnaps": 0,
          "rsubdirs": 1,
          "version": 0
        },
        "atime": "2024-05-16T22:34:14.498880+0000",
        "auth_pins": 0,
        "auth_state": {
          "replicas": {}
        },
        "authlock": {},
        "backtrace_version": 14,
        "btime": "2024-05-16T22:34:14.498880+0000",
        "change_attr": 3,
        "client_caps": [],
        "client_ranges": [],
        "ctime": "2024-05-16T22:34:15.249864+0000",
        "damage_flags": 0,
        "dir_layout": {
          "dir_hash": 2,
          "unused1": 0,
          "unused2": 0,
          "unused3": 0
        },
        "dirfrags": [
          {
            "auth_pins": 0,
            "auth_state": {
              "replicas": {}
            },
            "committed_version": "5",
            "committing_version": "5",
            "dentries": [
              {
                "alternate_name": "",
                "auth_pins": 0,
                "auth_state": {
                  "replicas": {}
                },
                "inode": 1099511628283,
                "is_auth": true,
                "is_freezing": false,
                "is_frozen": false,
                "is_new": false,
                "is_null": false,
                "is_primary": true,
                "is_remote": false,
                "lock": {},
                "nref": 2,
                "path": "dir/bUHUwH9E8uiVaf8xZ+zONcB1CToj53x5aUUnKdnNj5U37zbh28l1AaWwHhbOT3HyzqKjmSKKW1o4odQJc7nF9xrKIB8D3b4qqb2Cs6s7t2106hHhQk5,YV7DtpeNPZnorcTqxPM,hExtWHSS4P+S+Dpwj62hMyh,77sGhiW1Filvv1gQjV+sN,GozPNwHgfleadkUuZ+PMLCaKQXhuid9WvmHanxJnaabYDLj4VEz+EX2WsG",

Signed-off-by: Patrick Donnelly <pdonnell@redhat.com>
(cherry picked from commit ac092f63bf38eb81ee13de7dfd518ebadfc783cc)

qa/suites/fs/fscrypt/tasks/1-tests/fscrypt-common.yaml
qa/tasks/cephfs/test_fscrypt.py

index 5cb34d9818e9a57f3ab7dbb8e919cbc8d3a48f08..959adad002121132aaf79b9a72e4a8df3ea3c972 100644 (file)
@@ -1,3 +1,10 @@
+overrides:
+  install:
+    extra_system_packages:
+      rpm:
+        - fscrypt
+      deb:
+        - fscrypt
 tasks:
   - cephfs_test_runner:
       fail_on_skip: false
index a1836717f2a91bbca13e87ebd63f2dd62072dcda..d327c43c1fc382585290a1a67ea5752204daad3b 100644 (file)
@@ -1,13 +1,95 @@
+from io import StringIO
+from os.path import basename
+import random
+import string
+
 from logging import getLogger
 
-from io import StringIO
+from tasks.cephfs.cephfs_test_case import CephFSTestCase
 from tasks.cephfs.xfstests_dev import XFSTestsDev
 
-
 log = getLogger(__name__)
 
+class FSCryptTestCase(CephFSTestCase):
+    CLIENTS_REQUIRED = 1
+
+    def setUp(self):
+        super().setUp()
+
+        self.protector = ''.join(random.choice(string.ascii_letters) for _ in range(8))
+        self.key_file = "/tmp/key"
+        self.path = "dir/"
+
+        self.mount_a.run_shell_payload("sudo fscrypt --help")
+        self.mount_a.run_shell_payload("sudo fscrypt setup --help")
+        self.mount_a.run_shell_payload("sudo fscrypt setup --force --quiet")
+        self.mount_a.run_shell_payload("sudo fscrypt status")
+        self.mount_a.run_shell_payload(f"sudo fscrypt setup --quiet {self.mount_a.hostfs_mntpt}")
+        self.mount_a.run_shell_payload("sudo fscrypt status")
+        self.mount_a.run_shell_payload(f"sudo dd if=/dev/urandom of={self.key_file} bs=32 count=1")
+        self.mount_a.run_shell_payload(f"mkdir -p {self.path}")
+        self.mount_a.run_shell_payload(f"sudo fscrypt encrypt --quiet --source=raw_key --name={self.protector} --no-recovery --skip-unlock --key={self.key_file} {self.path}")
+        self.mount_a.run_shell_payload(f"sudo fscrypt unlock --quiet --key=/tmp/key {self.path}")
+
+    def tearDown(self):
+        self.mount_a.run_shell_payload(f"sudo fscrypt purge --force --quiet {self.mount_a.hostfs_mntpt}")
+
+        super().tearDown()
+
+class TestFSCrypt(FSCryptTestCase):
+
+    def test_fscrypt_basic_mount(self):
+        """
+        That fscrypt can be setup and ingest files.
+        """
+
+        self.mount_a.run_shell_payload(f"cp -av /usr/include {self.path}/")
+
+class TestFSCryptRecovery(FSCryptTestCase):
+
+    def test_fscrypt_journal_recovery(self):
+        """
+        That alternate_name can be recovered from the journal.
+        """
+
+        file = ''.join(random.choice(string.ascii_letters) for _ in range(255))
+
+        self.mount_a.run_shell_payload(f"cd {self.path} && dd if=/dev/urandom of={file} bs=512 count=1 oflag=sync && sync . && stat {file}")
+
+        def verify_alternate_name():
+            J = self.fs.read_cache("/dir", depth=0)
+            self.assertEqual(len(J), 1)
+            inode = J[0]
+            dirfrags = inode['dirfrags']
+            self.assertEqual(len(dirfrags), 1)
+            dirfrag = dirfrags[0]
+            dentries = dirfrag['dentries']
+            self.assertEqual(len(dentries), 1)
+            # we don't know it's encrypted name, so we cannot verify that it's {file}
+            dentry = dentries[0]
+            name = basename(dentry['path'])
+            # https://github.com/ceph/ceph-client/blob/fec50db7033ea478773b159e0e2efb135270e3b7/fs/ceph/crypto.h#L65-L90
+            self.assertEqual(len(name), 240)
+            alternate_name = dentry['alternate_name']
+            self.assertGreater(len(alternate_name), 240)
+
+        verify_alternate_name()
+
+        self.fs.fail()
+
+        self.fs.journal_tool(['event', 'recover_dentries', 'list'], 0)
+        self.fs.journal_tool(['journal', 'reset', '--yes-i-really-really-mean-it'], 0)
+
+        self.fs.set_joinable()
+        self.fs.wait_for_daemons()
+
+        verify_alternate_name()
+
+        self.mount_a.run_shell_payload(f"cd {self.path} && find")
+        self.mount_a.run_shell_payload(f"cd {self.path} && stat {file}")
+
 
-class TestFscrypt(XFSTestsDev):
+class TestFSCryptXFS(XFSTestsDev):
 
     def setup_xfsprogs_devs(self):
         self.install_xfsprogs = True