]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #!/bin/bash |
2 | # | |
3 | # Copyright (C) 2015 Red Hat <contact@redhat.com> | |
4 | # | |
5 | # Author: Kefu Chai <kchai@redhat.com> | |
6 | # | |
7 | # This program is free software; you can redistribute it and/or modify | |
8 | # it under the terms of the GNU Library Public License as published by | |
9 | # the Free Software Foundation; either version 2, or (at your option) | |
10 | # any later version. | |
11 | # | |
12 | # This program is distributed in the hope that it will be useful, | |
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | # GNU Library Public License for more details. | |
16 | # | |
17 | ||
18 | verbose= | |
19 | ||
20 | test -d ../src && export PATH=$PATH:. | |
21 | ||
22 | if type xmlstarlet > /dev/null 2>&1; then | |
23 | XMLSTARLET=xmlstarlet | |
24 | elif type xml > /dev/null 2>&1; then | |
25 | XMLSTARLET=xml | |
26 | else | |
27 | echo "Missing xmlstarlet binary!" | |
28 | exit 1 | |
29 | fi | |
30 | ||
31 | if [ `uname` = FreeBSD ]; then | |
32 | GETOPT=/usr/local/bin/getopt | |
33 | else | |
34 | GETOPT=getopt | |
35 | fi | |
36 | ||
37 | function osdmap_get() { | |
38 | local store_path=$1 | |
39 | local query=$2 | |
40 | local epoch=${3:+-v $3} | |
41 | local osdmap=`mktemp` | |
42 | ||
43 | $CEPH_BIN/ceph-monstore-tool $store_path get osdmap -- \ | |
44 | $epoch -o $osdmap > /dev/null || return | |
45 | ||
46 | echo $($CEPH_BIN/osdmaptool --dump xml $osdmap 2> /dev/null | \ | |
47 | $XMLSTARLET sel -t -m "$query" -v .) | |
48 | ||
49 | rm -f $osdmap | |
50 | } | |
51 | ||
52 | function test_crush() { | |
53 | local store_path=$1 | |
54 | local epoch=$2 | |
55 | local max_osd=$3 | |
56 | local crush=$4 | |
57 | local osdmap=`mktemp` | |
58 | ||
59 | $CEPH_BIN/ceph-monstore-tool $store_path get osdmap -- \ | |
60 | -v $epoch -o $osdmap > /dev/null | |
61 | $CEPH_BIN/osdmaptool --export-crush $crush $osdmap &> /dev/null | |
62 | ||
63 | if $CEPH_BIN/crushtool --test --check $max_osd -i $crush > /dev/null; then | |
64 | good=true | |
65 | else | |
66 | good=false | |
67 | fi | |
68 | rm -f $osdmap | |
69 | $good || return 1 | |
70 | } | |
71 | ||
72 | function die() { | |
73 | local retval=$? | |
74 | echo "$@" >&2 | |
75 | exit $retval | |
76 | } | |
77 | ||
78 | function usage() { | |
79 | [ $# -gt 0 ] && echo -e "\n$@" | |
80 | cat <<EOF | |
81 | ||
82 | Usage: $0 [options ...] <mon-store> | |
83 | ||
84 | Search backward for a latest known-good epoch in monstore. Rewrite the osdmap | |
85 | epochs after it with the crush map in the found epoch if asked to do so. By | |
86 | default, print out the crush map in the good epoch. | |
87 | ||
88 | [-h|--help] display this message | |
89 | [--out] write the found crush map to given file (default: stdout) | |
90 | [--rewrite] rewrite the monitor storage with the found crush map | |
91 | [--verbose] be more chatty | |
92 | EOF | |
93 | [ $# -gt 0 ] && exit 1 | |
94 | exit 0 | |
95 | } | |
96 | ||
97 | function main() { | |
98 | local temp | |
99 | temp=$($GETOPT -o h --long verbose,help,mon-store:,out:,rewrite -n $0 -- "$@") || return 1 | |
100 | ||
101 | eval set -- "$temp" | |
102 | local rewrite | |
103 | while [ "$1" != "--" ]; do | |
104 | case "$1" in | |
105 | --verbose) | |
106 | verbose=true | |
107 | # set -xe | |
108 | # PS4='${FUNCNAME[0]}: $LINENO: ' | |
109 | shift;; | |
110 | -h|--help) | |
111 | usage | |
112 | return 0;; | |
113 | --out) | |
114 | output=$2 | |
115 | shift 2;; | |
116 | --osdmap-epoch) | |
117 | osdmap_epoch=$2 | |
118 | shift 2;; | |
119 | --rewrite) | |
120 | rewrite=true | |
121 | shift;; | |
122 | *) | |
123 | usage "unexpected argument $1" | |
124 | shift;; | |
125 | esac | |
126 | done | |
127 | shift | |
128 | ||
129 | local store_path="$1" | |
130 | test $store_path || usage "I need the path to mon-store." | |
131 | ||
132 | # try accessing the store; if it fails, likely means a mon is running | |
133 | local last_osdmap_epoch | |
134 | local max_osd | |
135 | last_osdmap_epoch=$(osdmap_get $store_path "/osdmap/epoch") || \ | |
136 | die "error accessing mon store at $store_path" | |
137 | # get the max_osd # in last osdmap epoch, crushtool will use it to check | |
138 | # the crush maps in previous osdmaps | |
139 | max_osd=$(osdmap_get $store_path "/osdmap/max_osd" $last_osdmap_epoch) | |
140 | ||
141 | local good_crush | |
142 | local good_epoch | |
143 | test $verbose && echo "the latest osdmap epoch is $last_osdmap_epoch" | |
144 | for epoch in `seq $last_osdmap_epoch -1 1`; do | |
145 | local crush_path=`mktemp` | |
146 | test $verbose && echo "checking crush map #$epoch" | |
147 | if test_crush $store_path $epoch $max_osd $crush_path; then | |
148 | test $verbose && echo "crush map version #$epoch works with osdmap epoch #$osdmap_epoch" | |
149 | good_epoch=$epoch | |
150 | good_crush=$crush_path | |
151 | break | |
152 | fi | |
153 | rm -f $crush_path | |
154 | done | |
155 | ||
156 | if test $good_epoch; then | |
157 | echo "good crush map found at epoch $epoch/$last_osdmap_epoch" | |
158 | else | |
159 | echo "Unable to find a crush map for osdmap version #$osdmap_epoch." 2>&1 | |
160 | return 1 | |
161 | fi | |
162 | ||
163 | if test $good_epoch -eq $last_osdmap_epoch; then | |
164 | echo "and mon store has no faulty crush maps." | |
165 | elif test $output; then | |
166 | $CEPH_BIN/crushtool --decompile $good_crush --outfn $output | |
167 | elif test $rewrite; then | |
168 | $CEPH_BIN/ceph-monstore-tool $store_path rewrite-crush -- \ | |
169 | --crush $good_crush \ | |
170 | --good-epoch $good_epoch | |
171 | else | |
172 | echo | |
173 | $CEPH_BIN/crushtool --decompile $good_crush | |
174 | fi | |
175 | rm -f $good_crush | |
176 | } | |
177 | ||
178 | main "$@" |