Whamcloud - gitweb
LU-16232 scripts: changelog/updatelog emergency cleanup
[fs/lustre-release.git] / lustre / scripts / remove_updatelog
1 #!/bin/bash
2
3 # remove_updatelogs: emergency remove MDT updatelog files from server.
4 #
5 # This is emergency tool to cleanup updatelogs in server if llog records
6 # cannot be removed by regular means, e.g. due to llog corruptions
7 #
8 # Tool goes the following:
9 # - goes through update_log catlist to find per-MDT update llog catalog
10 # - process llog catalog to delete all plain llogs in it
11 # - truncate or remove related llog catalog after all
12 # - truncates update_llogs itself if all catalogs were removed
13 #
14 # Script required parameter is mount point of server FS mounted locally
15 # it accepts also optional options as described below in usage()
16 #
17 # Steps to cleanup problematic llogs:
18 #
19 # 1. mount MDT filesystem locally on server as ldiskfs mount
20 # 2. run script first in dry-run mode to make sure it parses llogs as needed:
21 #    # bash remove_updatelog -n <ldiskfs_mount>
22 # 3. save all llogs for analysis:
23 #    # bash remove_updatelog -n -z /tmp/llogs_saved <ldiskfs_mount>
24 # 4. check that /tmp/llogs_saved.tar.gz exists and has all llogs inside:
25 #    # ls -ali /tmp/llogs_saved.tar.gz
26 #    # tar -tf /tmp/llog_saved.tar.gz
27 # 5. finally run script to delete all llogs:
28 #    # bash remove_updatelog <ldiskfs_mount>
29 #
30 # For better llogs compression xz can be used as well, pass it to the script
31 # via GZIP env variable:
32 #    # GZIP=xz bash remove_updatelog -n -z /tmp/llogs_saved <ldiskfs_mount>
33 # Archive name will ends with .xz in that case instead of .gz
34
35 ECHO=echo
36 PROG=$(basename $0)
37 LLOG_READER=${LLOG_READER:-llog_reader}
38 GZIP=${GZIP:-gzip}
39
40 usage() {
41     cat -- <<USAGE 1>&2
42 usage: remove_updatelog [--dry-run|-n] [--help|-h] [--quiet|-q] <localmount>
43         --help|-h          show this usage message
44         --dry-run|-n       only print the names of files to be removed
45         --quiet|-q         run quietly (don't print filenames or status)
46         --zip|-z <name_prefix>
47                         save all llogs into compressed tar archive with given
48                         name prefix using gzip by default. Other compression
49                         tools can be used via GZIP env variable.
50
51 The 'localmount' argument should be an ldiskfs mounted MDT device mountpoint.
52
53 Examples:
54       remove_updatelog /mnt/mdt0
55       remove_updatelog --dry-run /mnt/mdt0
56       remove_changelog -z /tmp/llogs /mnt/mdt0
57 USAGE
58     exit 1
59 }
60
61 OPT_DRYRUN=false
62 OPT_ARCH=""
63 OPT_MOUNT=""
64 OPT_MDTS=()
65
66 # Examine any long options and arguments
67 while [ -n "$*" ]; do
68         arg="$1"
69         case "$arg" in
70         -h|--help) usage;;
71         -n|--dry-run) OPT_DRYRUN=true;;
72         -q|--quiet) ECHO=:;;
73         -z|--zip) OPT_ARCH="$2.tar"; shift;;
74         *)
75            [ -e "$arg" ] && OPT_MOUNT="$arg" && break
76         esac
77         shift
78 done
79
80 remove_updatelog() {
81         local mntpoint=$OPT_MOUNT
82         local catlist=${mntpoint}/update_log
83         local dir=${mntpoint}/update_log_dir
84         local arch=$OPT_ARCH
85         local length=0
86
87         if [[ -z $(df -t ldiskfs $mntpoint 2>/dev/null) ]] ; then
88                 echo "$PROG: '$mntpoint' is not ldiskfs mount."
89                 exit 1
90         fi
91
92         if $OPT_DRYRUN; then
93                 $ECHO "Dry run was requested, no changes will be applied"
94         fi
95
96         $ECHO "Scan update_log at '$mntpoint':"
97         if [[ ! -f $catlist ]] ; then
98                 echo "$PROG: $catlist doesn't exist already."
99         else
100                 read -r -d '' -a OPT_MDTS < <(hexdump -v -e '2/8 " %16x" 2/8 "\n"' $catlist |
101                                               awk '{print "[0x"$2":0x"$1":0x0]"}')
102
103                 if [[ ! $(which $LLOG_READER 2>/dev/null) ]] ; then
104                         echo "$PROG: $LLOG_READER is missing."
105                         exit 1
106                 fi
107                 [[ -z $arch ]] || tar -cf $arch $catlist 2>/dev/null
108                 length=${#OPT_MDTS[@]}
109                 for (( i = 0; i < ${length}; i++ )); do
110                         local catalog=$dir/${OPT_MDTS[$i]}
111
112                         $ECHO "Processing MDT$i llogs ..."
113                         if [[ ! -f $catalog ]] ; then
114                                 echo "$PROG: $catalog doesn't exist already."
115                                 continue
116                         fi
117                         [[ -z $arch ]] || tar -rf $arch $catalog 2>/dev/null
118                         if (( $(stat -c %s $catalog) >= 8192 )) ; then
119                                 while read -r plain ; do
120                                         local path
121
122                                         # compatibility checks:
123                                         # old llog reader reports path in /O
124                                         # but correct path in update_log_dir
125                                         if [ ${plain:0:1} == 'O' ] ; then
126                                                 local fid=${plain#"O/"*}
127
128                                                 # old format: O/8589935617/d3/3
129                                                 # get sequence and oid in hex:
130                                                 fid=$(printf "[0x%x:0x%x:0x0]" ${fid%%/*} ${fid##*/})
131                                                 path="$dir/$fid"
132                                         else
133                                                 path=$mntpoint/$plain
134                                         fi
135                                         [[ -z $arch ]] ||
136                                                 tar -rf $arch $path 2>/dev/null
137                                         $ECHO "rm -f $path"
138                                         $OPT_DRYRUN || rm -f $path
139                                 done < <(llog_reader $catalog |
140                                          awk -F "path=" '/path=/ { print $2 }')
141                         else
142                                 echo "$PROG: $catalog is too small."
143                         fi
144                         $ECHO "> $catalog"
145                         $OPT_DRYRUN || > $catalog
146                 done
147         fi
148         if [[ "$arch" ]] ; then
149                 $GZIP -3 $arch
150                 $ECHO "llog archive was created by $GZIP"
151         fi
152 }
153
154 if [ -z $OPT_MOUNT ] ; then
155         echo "Mount is not specified, exiting"
156         exit 1
157 fi
158 remove_updatelog
159
160