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