d1681e
From 91cfe700849856245f58a8c0ee98c0fd1e9d47f6 Mon Sep 17 00:00:00 2001
d1681e
From: Kotresh HR <khiremat@redhat.com>
d1681e
Date: Mon, 28 May 2018 03:05:26 -0400
d1681e
Subject: [PATCH 304/305] cluster/dht: Fix rename journal in changelog
d1681e
d1681e
With patch [1], renames are journalled only
d1681e
on cached subvolume. The dht sends the special
d1681e
key on the cached subvolume so that the changelog
d1681e
journals the rename. With single distribute
d1681e
sub-volume, the key is not being set. This patch
d1681e
fixes the same.
d1681e
d1681e
[1] https://review.gluster.org/10410
d1681e
d1681e
Backport of:
d1681e
 > Patch: https://review.gluster.org/20093/
d1681e
 > fixes: bz#1583018
d1681e
 > Change-Id: Ic2e35b40535916fa506a714f257ba325e22d0961
d1681e
 > Signed-off-by: Kotresh HR <khiremat@redhat.com>
d1681e
d1681e
BUG: 1583047
d1681e
Change-Id: Ic2e35b40535916fa506a714f257ba325e22d0961
d1681e
Signed-off-by: Kotresh HR <khiremat@redhat.com>
d1681e
Reviewed-on: https://code.engineering.redhat.com/gerrit/142601
d1681e
Tested-by: RHGS Build Bot <nigelb@redhat.com>
d1681e
Reviewed-by: Atin Mukherjee <amukherj@redhat.com>
d1681e
---
d1681e
 tests/basic/changelog/changelog-rename.t |  44 ++++++
d1681e
 tests/utils/changelogparser.py           | 234 +++++++++++++++++++++++++++++++
d1681e
 tests/volume.rc                          |   7 +
d1681e
 xlators/cluster/dht/src/dht-rename.c     |  11 ++
d1681e
 4 files changed, 296 insertions(+)
d1681e
 create mode 100644 tests/basic/changelog/changelog-rename.t
d1681e
 create mode 100644 tests/utils/changelogparser.py
d1681e
d1681e
diff --git a/tests/basic/changelog/changelog-rename.t b/tests/basic/changelog/changelog-rename.t
d1681e
new file mode 100644
d1681e
index 0000000..9a0ef52
d1681e
--- /dev/null
d1681e
+++ b/tests/basic/changelog/changelog-rename.t
d1681e
@@ -0,0 +1,44 @@
d1681e
+#!/bin/bash
d1681e
+. $(dirname $0)/../../include.rc
d1681e
+. $(dirname $0)/../../volume.rc
d1681e
+cleanup;
d1681e
+
d1681e
+CHANGELOG_PATH_0="$B0/${V0}0/.glusterfs/changelogs"
d1681e
+ROLLOVER_TIME=30
d1681e
+
d1681e
+TEST glusterd
d1681e
+TEST pidof glusterd
d1681e
+TEST $CLI volume create $V0 $H0:$B0/${V0}0
d1681e
+TEST $CLI volume set $V0 changelog.changelog on
d1681e
+TEST $CLI volume set $V0 changelog.rollover-time $ROLLOVER_TIME
d1681e
+TEST $CLI volume start $V0
d1681e
+
d1681e
+TEST $GFS --volfile-id=$V0 --volfile-server=$H0 $M0;
d1681e
+touch $M0/file1
d1681e
+mv $M0/file1 $M0/rn_file1
d1681e
+mkdir $M0/dir1
d1681e
+mv $M0/dir1 $M0/rn_dir1
d1681e
+
d1681e
+EXPECT "2" check_changelog_op ${CHANGELOG_PATH_0} "RENAME"
d1681e
+
d1681e
+cleanup;
d1681e
+
d1681e
+#####Test on multiple subvolume#####
d1681e
+#==========================================#
d1681e
+
d1681e
+TEST glusterd
d1681e
+TEST pidof glusterd
d1681e
+TEST $CLI volume create $V0 $H0:$B0/${V0}{0,1}
d1681e
+TEST $CLI volume set $V0 changelog.changelog on
d1681e
+TEST $CLI volume set $V0 changelog.rollover-time $ROLLOVER_TIME
d1681e
+TEST $CLI volume start $V0
d1681e
+
d1681e
+TEST $GFS --volfile-id=$V0 --volfile-server=$H0 $M0;
d1681e
+touch $M0/gluster_file
d1681e
+mv $M0/gluster_file $M0/rn_gluster_file
d1681e
+mkdir $M0/dir1
d1681e
+mv $M0/dir1 $M0/rn_dir1
d1681e
+
d1681e
+EXPECT "2" check_changelog_op ${CHANGELOG_PATH_0} "RENAME"
d1681e
+
d1681e
+cleanup;
d1681e
diff --git a/tests/utils/changelogparser.py b/tests/utils/changelogparser.py
d1681e
new file mode 100644
d1681e
index 0000000..e173e52
d1681e
--- /dev/null
d1681e
+++ b/tests/utils/changelogparser.py
d1681e
@@ -0,0 +1,234 @@
d1681e
+#!/usr/bin/env python
d1681e
+# -*- coding: utf-8 -*-
d1681e
+"""
d1681e
+Why?
d1681e
+
d1681e
+Converts this
d1681e
+
d1681e
+GlusterFS Changelog | version: v1.1 | encoding : 2
d1681e
+E0b99ef11-4b79-4cd0-9730-b5a0e8c4a8c0^@4^@16877^@0^@0^@00000000-0000-0000-0000-
d1681e
+000000000001/dir1^@Ec5250af6-720e-4bfe-b938-827614304f39^@23^@33188^@0^@0^@0b99
d1681e
+ef11-4b79-4cd0-9730-b5a0e8c4a8c0/hello.txt^@Dc5250af6-720e-4bfe-b938-827614304f
d1681e
+39^@Dc5250af6-720e-4bfe-b938-827614304f39^@
d1681e
+
d1681e
+
d1681e
+to human readable :)
d1681e
+
d1681e
+E 0b99ef11-4b79-4cd0-9730-b5a0e8c4a8c0 MKDIR 16877 0 000000000-0000-0000-0000
d1681e
+  -000000000001/dir1
d1681e
+E c5250af6-720e-4bfe-b938-827614304f39 CREATE 33188 0 0 0b99ef11-4b79-4cd0-9730
d1681e
+  -b5a0e8c4a8c0/hello.txt
d1681e
+D c5250af6-720e-4bfe-b938-827614304f39
d1681e
+D c5250af6-720e-4bfe-b938-827614304f39
d1681e
+
d1681e
+
d1681e
+"""
d1681e
+import sys
d1681e
+import codecs
d1681e
+
d1681e
+ENTRY = 'E'
d1681e
+META = 'M'
d1681e
+DATA = 'D'
d1681e
+SEP = "\x00"
d1681e
+
d1681e
+GF_FOP = [
d1681e
+    "NULL", "STAT", "READLINK", "MKNOD", "MKDIR", "UNLINK",
d1681e
+    "RMDIR", "SYMLINK", "RENAME", "LINK", "TRUNCATE", "OPEN",
d1681e
+    "READ", "WRITE", "STATFS", "FLUSH", "FSYNC", "SETXATTR",
d1681e
+    "GETXATTR", "REMOVEXATTR", "OPENDIR", "FSYNCDIR", "ACCESS",
d1681e
+    "CREATE", "FTRUNCATE", "FSTAT", "LK", "LOOKUP", "READDIR",
d1681e
+    "INODELK", "FINODELK", "ENTRYLK", "FENTRYLK", "XATTROP",
d1681e
+    "FXATTROP", "FSETXATTR", "FGETXATTR", "RCHECKSUM", "SETATTR",
d1681e
+    "FSETATTR", "READDIRP", "GETSPEC", "FORGET", "RELEASE",
d1681e
+    "RELEASEDIR", "FREMOVEXATTR", "FALLOCATE", "DISCARD", "ZEROFILL"]
d1681e
+
d1681e
+
d1681e
+class NumTokens_V11(object):
d1681e
+    E = 7
d1681e
+    M = 3
d1681e
+    D = 2
d1681e
+    NULL = 3
d1681e
+    MKNOD = 7
d1681e
+    MKDIR = 7
d1681e
+    UNLINK = 4
d1681e
+    RMDIR = 4
d1681e
+    SYMLINK = 4
d1681e
+    RENAME = 5
d1681e
+    LINK = 4
d1681e
+    SETXATTR = 3
d1681e
+    REMOVEXATTR = 3
d1681e
+    CREATE = 7
d1681e
+    SETATTR = 3
d1681e
+    FTRUNCATE = 3
d1681e
+    FXATTROP = 3
d1681e
+
d1681e
+
d1681e
+class NumTokens_V12(NumTokens_V11):
d1681e
+    UNLINK = 5
d1681e
+    RMDIR = 5
d1681e
+
d1681e
+
d1681e
+class Version:
d1681e
+    V11 = "v1.1"
d1681e
+    V12 = "v1.2"
d1681e
+
d1681e
+
d1681e
+class Record(object):
d1681e
+    def __init__(self, **kwargs):
d1681e
+        self.ts = kwargs.get("ts", None)
d1681e
+        self.fop_type = kwargs.get("fop_type", None)
d1681e
+        self.gfid = kwargs.get("gfid", None)
d1681e
+        self.path = kwargs.get("path", None)
d1681e
+        self.fop = kwargs.get("fop", None)
d1681e
+        self.path1 = kwargs.get("path1", None)
d1681e
+        self.path2 = kwargs.get("path2", None)
d1681e
+        self.mode = kwargs.get("mode", None)
d1681e
+        self.uid = kwargs.get("uid", None)
d1681e
+        self.gid = kwargs.get("gid", None)
d1681e
+
d1681e
+    def create_mknod_mkdir(self, **kwargs):
d1681e
+        self.path = kwargs.get("path", None)
d1681e
+        self.fop = kwargs.get("fop", None)
d1681e
+        self.mode = kwargs.get("mode", None)
d1681e
+        self.uid = kwargs.get("uid", None)
d1681e
+        self.gid = kwargs.get("gid", None)
d1681e
+
d1681e
+    def metadata(self, **kwargs):
d1681e
+        self.fop = kwargs.get("fop", None)
d1681e
+
d1681e
+    def rename(self, **kwargs):
d1681e
+        self.fop = kwargs.get("fop", None)
d1681e
+        self.path1 = kwargs.get("path1", None)
d1681e
+        self.path2 = kwargs.get("path2", None)
d1681e
+
d1681e
+    def link_symlink_unlink_rmdir(self, **kwargs):
d1681e
+        self.path = kwargs.get("path", None)
d1681e
+        self.fop = kwargs.get("fop", None)
d1681e
+
d1681e
+    def __unicode__(self):
d1681e
+        if self.fop_type == "D":
d1681e
+            return u"{ts} {fop_type} {gfid}".format(**self.__dict__)
d1681e
+        elif self.fop_type == "M":
d1681e
+            return u"{ts} {fop_type} {gfid} {fop}".format(**self.__dict__)
d1681e
+        elif self.fop_type == "E":
d1681e
+            if self.fop in ["CREATE", "MKNOD", "MKDIR"]:
d1681e
+                return (u"{ts} {fop_type} {gfid} {fop} "
d1681e
+                        u"{path} {mode} {uid} {gid}".format(**self.__dict__))
d1681e
+            elif self.fop == "RENAME":
d1681e
+                return (u"{ts} {fop_type} {gfid} {fop} "
d1681e
+                        u"{path1} {path2}".format(**self.__dict__))
d1681e
+            elif self.fop in ["LINK", "SYMLINK", "UNLINK", "RMDIR"]:
d1681e
+                return (u"{ts} {fop_type} {gfid} {fop} "
d1681e
+                        u"{path}".format(**self.__dict__))
d1681e
+            else:
d1681e
+                return repr(self.__dict__)
d1681e
+        else:
d1681e
+            return repr(self.__dict__)
d1681e
+
d1681e
+    def __str__(self):
d1681e
+        return unicode(self).encode('utf-8')
d1681e
+
d1681e
+
d1681e
+def get_num_tokens(data, tokens, version=Version.V11):
d1681e
+    if version == Version.V11:
d1681e
+        cls_numtokens = NumTokens_V11
d1681e
+    elif version == Version.V12:
d1681e
+        cls_numtokens = NumTokens_V12
d1681e
+    else:
d1681e
+        sys.stderr.write("Unknown Changelog Version\n")
d1681e
+        sys.exit(1)
d1681e
+
d1681e
+    if data[tokens[0]] in [ENTRY, META]:
d1681e
+        if len(tokens) >= 3:
d1681e
+            return getattr(cls_numtokens, GF_FOP[int(data[tokens[2]])])
d1681e
+        else:
d1681e
+            return None
d1681e
+    else:
d1681e
+        return getattr(cls_numtokens, data[tokens[0]])
d1681e
+
d1681e
+
d1681e
+def process_record(data, tokens, changelog_ts, callback):
d1681e
+    if data[tokens[0]] in [ENTRY, META]:
d1681e
+        try:
d1681e
+            tokens[2] = GF_FOP[int(data[tokens[2]])]
d1681e
+        except ValueError:
d1681e
+            tokens[2] = "NULL"
d1681e
+
d1681e
+    if not changelog_ts:
d1681e
+        ts1 = int(changelog_ts)
d1681e
+    else:
d1681e
+        ts1=""
d1681e
+    record = Record(ts=ts1, fop_type=data[tokens[0]],
d1681e
+                    gfid=data[tokens[1]])
d1681e
+    if data[tokens[0]] == META:
d1681e
+        record.metadata(fop=tokens[2])
d1681e
+    elif data[tokens[0]] == ENTRY:
d1681e
+        if tokens[2] in ["CREATE", "MKNOD", "MKDIR"]:
d1681e
+            record.create_mknod_mkdir(fop=tokens[2],
d1681e
+                                      path=data[tokens[6]],
d1681e
+                                      mode=int(data[tokens[3]]),
d1681e
+                                      uid=int(data[tokens[4]]),
d1681e
+                                      gid=int(data[tokens[5]]))
d1681e
+        elif tokens[2] == "RENAME":
d1681e
+            record.rename(fop=tokens[2],
d1681e
+                          path1=data[tokens[3]],
d1681e
+                          path2=data[tokens[4]])
d1681e
+        if tokens[2] in ["LINK", "SYMLINK", "UNLINK", "RMDIR"]:
d1681e
+            record.link_symlink_unlink_rmdir(fop=tokens[2],
d1681e
+                                             path=data[tokens[3]])
d1681e
+    callback(record)
d1681e
+
d1681e
+
d1681e
+def default_callback(record):
d1681e
+    sys.stdout.write(u"{0}\n".format(record))
d1681e
+
d1681e
+
d1681e
+def parse(filename, callback=default_callback):
d1681e
+    data = None
d1681e
+    tokens = []
d1681e
+    changelog_ts = filename.rsplit(".")[-1]
d1681e
+    with codecs.open(filename, mode="rb", encoding="utf-8") as f:
d1681e
+        # GlusterFS Changelog | version: v1.1 | encoding : 2
d1681e
+        header = f.readline()
d1681e
+        version = header.split()[4]
d1681e
+
d1681e
+        data = f.readline()
d1681e
+
d1681e
+        slice_start = 0
d1681e
+        in_record = False
d1681e
+
d1681e
+        prev_char = ""
d1681e
+        next_char = ""
d1681e
+        for i, c in enumerate(data):
d1681e
+            next_char = ""
d1681e
+            if len(data) >= (i + 2):
d1681e
+                next_char = data[i+1]
d1681e
+
d1681e
+            if not in_record and c in [ENTRY, META, DATA]:
d1681e
+                tokens.append(slice(slice_start, i+1))
d1681e
+                slice_start = i+1
d1681e
+                in_record = True
d1681e
+                continue
d1681e
+
d1681e
+            if c == SEP and ((prev_char != SEP and next_char == SEP) or
d1681e
+                             (prev_char == SEP and next_char != SEP) or
d1681e
+                             (prev_char != SEP and next_char != SEP)):
d1681e
+                tokens.append(slice(slice_start, i))
d1681e
+                slice_start = i+1
d1681e
+
d1681e
+                num_tokens = get_num_tokens(data, tokens, version)
d1681e
+
d1681e
+                if num_tokens == len(tokens):
d1681e
+                    process_record(data, tokens, changelog_ts, callback)
d1681e
+                    in_record = False
d1681e
+                    tokens = []
d1681e
+
d1681e
+            prev_char = c
d1681e
+
d1681e
+        # process last record
d1681e
+        if slice_start < (len(data) - 1):
d1681e
+            tokens.append(slice(slice_start, len(data)))
d1681e
+            process_record(data, tokens, changelog_ts, callback)
d1681e
+            tokens = []
d1681e
+
d1681e
+parse(sys.argv[1])
d1681e
diff --git a/tests/volume.rc b/tests/volume.rc
d1681e
index f9e16c5..bba7e4e 100644
d1681e
--- a/tests/volume.rc
d1681e
+++ b/tests/volume.rc
d1681e
@@ -865,3 +865,10 @@ function get_mount_lru_size_value {
d1681e
         rm -f $statedump
d1681e
         echo $val
d1681e
 }
d1681e
+
d1681e
+function check_changelog_op {
d1681e
+        local clog_path=$1
d1681e
+        local op=$2
d1681e
+
d1681e
+        $PYTHON $(dirname $0)/../../utils/changelogparser.py ${clog_path}/CHANGELOG | grep $op | wc -l
d1681e
+}
d1681e
diff --git a/xlators/cluster/dht/src/dht-rename.c b/xlators/cluster/dht/src/dht-rename.c
d1681e
index d311ac6..1d0c2bb 100644
d1681e
--- a/xlators/cluster/dht/src/dht-rename.c
d1681e
+++ b/xlators/cluster/dht/src/dht-rename.c
d1681e
@@ -1948,6 +1948,7 @@ dht_rename (call_frame_t *frame, xlator_t *this,
d1681e
         dht_conf_t  *conf                   = NULL;
d1681e
         char         gfid[GF_UUID_BUF_SIZE] = {0};
d1681e
         char         newgfid[GF_UUID_BUF_SIZE] = {0};
d1681e
+        gf_boolean_t free_xdata             = _gf_false;
d1681e
 
d1681e
         VALIDATE_OR_GOTO (frame, err);
d1681e
         VALIDATE_OR_GOTO (this, err);
d1681e
@@ -1957,7 +1958,17 @@ dht_rename (call_frame_t *frame, xlator_t *this,
d1681e
         conf = this->private;
d1681e
 
d1681e
         if (conf->subvolume_cnt == 1) {
d1681e
+                if (!IA_ISDIR (oldloc->inode->ia_type)) {
d1681e
+                        if (!xdata) {
d1681e
+                                free_xdata = _gf_true;
d1681e
+                        }
d1681e
+                        DHT_CHANGELOG_TRACK_AS_RENAME(xdata, oldloc, newloc);
d1681e
+                }
d1681e
                 default_rename (frame, this, oldloc, newloc, xdata);
d1681e
+                if (free_xdata && xdata) {
d1681e
+                        dict_unref(xdata);
d1681e
+                        xdata = NULL;
d1681e
+                }
d1681e
                 return 0;
d1681e
         }
d1681e
 
d1681e
-- 
d1681e
1.8.3.1
d1681e