Statistics
| Branch: | Tag: | Revision:

root / snf-image-helper / common.sh @ 87d1bf2e

History | View | Annotate | Download (10.5 kB)

1
# Copyright (C) 2011 GRNET S.A. 
2
# Copyright (C) 2007, 2008, 2009 Google Inc.
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful, but
10
# WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
# General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
# 02110-1301, USA.
18

    
19
RESULT=/dev/ttyS1
20
MONITOR=/dev/ttyS2
21

    
22
FLOPPY_DEV=/dev/fd0
23
PROGNAME=$(basename $0)
24

    
25
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
26

    
27
# Programs
28
XMLSTARLET=xmlstarlet
29
TUNE2FS=tune2fs
30
RESIZE2FS=resize2fs
31
PARTED=parted
32
SFDISK=sfdisk
33
MKSWAP=mkswap
34
BLKID=blkid
35
BLOCKDEV=blockdev
36
REGLOOKUP=reglookup
37
CHNTPW=chntpw
38

    
39
CLEANUP=( )
40
ERRORS=( )
41
WARNINGS=( )
42

    
43
add_cleanup() {
44
    local cmd=""
45
    for arg; do cmd+=$(printf "%q " "$arg"); done
46
    CLEANUP+=("$cmd")
47
}
48

    
49
log_error() {
50
    ERRORS+=("$@")
51
    echo "ERROR: $@" | tee $RESULT >&2
52
    exit 1
53
}
54

    
55
warn() {
56
    WARNINGS+=("$@")
57
    echo "Warning: $@" >&2
58
}
59

    
60
report_start_task() {
61

    
62
    local type="start-task"
63
    local timestamp=$(date +%s.%N)
64
    local name="${PROGNAME}"
65

    
66
    report+="\"type\":\"$type\","
67
    report+="\"timestamp\":$(date +%s.%N),"
68
    report+="\"name\":\"$name\"}"
69

    
70
    echo "$report" > "$MONITOR"
71
}
72

    
73
json_list() {
74
    declare -a items=("${!1}")
75
    report="["
76
    for item in "${items[@]}"; do
77
        report+="\"$(sed 's/"/\\"/g' <<< "$item")\","
78
    done
79
    if [ ${#report} -gt 1 ]; then
80
        # remove last comma(,)
81
        report="${report%?}"
82
    fi
83
    report+="]"
84

    
85
    echo "$report"
86
}
87

    
88
report_end_task() {
89

    
90
    local type="end-task"
91
    local timestam=$(date +%s.%N)
92
    local name=${PROGNAME}
93
    local warnings=$(json_list WARNINGS[@])
94

    
95
    report="\"type\":\"$type\","
96
    report+="\"timestamp\":$(date +%s),"
97
    report+="\"name\":\"$name\","
98
    report+="\"warnings\":\"$warnings\"}"
99

    
100
    echo "$report" > "$MONITOR"
101
}
102

    
103
report_error() {
104
    local type="ganeti-error"
105
    local timestamp=$(date +%s.%N)
106
    local location="${PROGNAME}"
107
    local errors=$(json_list ERRORS[@])
108
    local warnings=$(json_list WARNINGS[@])
109
    local stderr="$(cat "$STDERR_FILE" | sed 's/"/\\"/g')"
110

    
111
    report="\"type\":\"$type\","
112
    report+="\"timestamp\":$(date +%s),"
113
    report+="\"location\":\"$location\","
114
    report+="\"errors\":$errors,"
115
    report+="\"warnings\":$warnings,"
116
    report+="\"stderr\":\"$stderr\"}"
117

    
118
    echo "$report" > "$MONITOR"
119
}
120

    
121
get_base_distro() {
122
    local root_dir=$1
123

    
124
    if [ -e "$root_dir/etc/debian_version" ]; then
125
        echo "debian"
126
    elif [ -e "$root_dir/etc/redhat-release" ]; then
127
        echo "redhat"
128
    elif [ -e "$root_dir/etc/slackware-version" ]; then
129
        echo "slackware"
130
    elif [ -e "$root_dir/etc/SuSE-release" ]; then
131
        echo "suse"
132
    elif [ -e "$root_dir/etc/gentoo-release" ]; then
133
        echo "gentoo"
134
    else
135
        warn "Unknown base distro."
136
    fi
137
}
138

    
139
get_distro() {
140
    local root_dir=$1
141

    
142
    if [ -e "$root_dir/etc/debian_version" ]; then
143
        distro="debian"
144
        if [ -e ${root_dir}/etc/lsb-release ]; then
145
            ID=$(grep ^DISTRIB_ID= ${root_dir}/etc/lsb-release | cut -d= -f2)
146
            if [ "x$ID" = "xUbuntu" ]; then
147
                distro="ubuntu"
148
            fi
149
        fi
150
        echo "$distro"
151
    elif [ -e "$root_dir/etc/fedora-release" ]; then
152
        echo "fedora"
153
    elif [ -e "$root_dir/etc/centos-release" ]; then
154
        echo "centos"
155
    elif [ -e "$root_dir/etc/redhat-release" ]; then
156
        echo "redhat"
157
    elif [ -e "$root_dir/etc/slackware-version" ]; then
158
        echo "slackware"
159
    elif [ -e "$root_dir/etc/SuSE-release" ]; then
160
        echo "suse"
161
    elif [ -e "$root_dir/etc/gentoo-release" ]; then
162
        echo "gentoo"
163
    else
164
        warn "Unknown distro."
165
    fi
166
}
167

    
168

    
169
get_partition_table() {
170
    local dev="$1"
171
    # If the partition table is gpt then parted will raise an error if the
172
    # secondary gpt is not it the end of the disk, and a warning that has to
173
    # do with the "Last Usable LBA" entry in gpt.
174
    if ! output="$("$PARTED" -s -m "$dev" unit s print | grep -E -v "^(Warning|Error): ")"; then
175
        log_error "Unable to read partition table for device \`${dev}'"
176
    fi
177

    
178
    echo "$output"
179
}
180

    
181
get_partition_table_type() {
182
    local ptable="$1"
183

    
184
    local dev="$(sed -n 2p <<< "$ptable")"
185
    declare -a field
186
    IFS=':' read -ra field <<< "$dev"
187

    
188
    echo "${field[5]}"
189
}
190

    
191
get_partition_count() {
192
    local ptable="$1"
193

    
194
    expr $(echo "$ptable" | wc -l) - 2
195
}
196

    
197
get_partition_by_num() {
198
    local ptable="$1"
199
    local id="$2"
200

    
201
    grep "^$id:" <<< "$ptable"
202
}
203

    
204
get_last_partition() {
205
    local ptable="$1"
206

    
207
    echo "$ptable" | tail -1
208
}
209

    
210
is_extended_partition() {
211
    local dev="$1"
212
    local part_num="$2"
213

    
214
    id=$($SFDISK --print-id "$dev" "$part_num")
215
    if [ "$id" = "5" ]; then
216
        echo "yes"
217
    else
218
        echo "no"
219
    fi
220
}
221

    
222
get_extended_partition() {
223
    local ptable="$1"
224
    local dev="$(echo "$ptable" | sed -n 2p | cut -d':' -f1)"
225

    
226
    tail -n +3 <<< "$ptable" | while read line; do
227
        part_num=$(cut -d':' -f1 <<< "$line")
228
        if [ $(is_extended_partition "$dev" "$part_num") == "yes" ]; then
229
            echo "$line"
230
            return 0
231
        fi
232
    done
233
    echo ""
234
}
235

    
236
get_logical_partitions() {
237
    local ptable="$1"
238

    
239
    tail -n +3 <<< "$ptable" | while read line; do
240
        part_num=$(cut -d':' -f1 <<< "$line")
241
        if [ $part_num -ge 5 ]; then
242
            echo "$line"
243
        fi
244
    done
245

    
246
    return 0
247
}
248

    
249
get_last_primary_partition() {
250
    local ptable="$1"
251
    local dev=$(echo "ptable" | sed -n 2p | cut -d':' -f1)
252

    
253
    for i in 4 3 2 1; do
254
        if output=$(grep "^$i:" <<< "$ptable"); then
255
            echo "$output"
256
            return 0
257
        fi
258
    done
259
    echo ""
260
}
261

    
262
get_partition_to_resize() {
263
    local dev="$1"
264

    
265
    table=$(get_partition_table "$dev")
266

    
267
    if [ $(get_partition_count "$table") -eq 0 ]; then
268
        return 0
269
    fi
270

    
271
    table_type=$(get_partition_table_type "$table")
272
    last_part=$(get_last_partition "$table")
273
    last_part_num=$(cut -d: -f1 <<< "$last_part")
274

    
275
    if [ "$table_type" == "msdos" -a $last_part_num -gt 4 ]; then
276
        extended=$(get_extended_partition "$table")
277
        last_primary=$(get_last_primary_partition "$table")
278
        ext_num=$(cut -d: -f1 <<< "$extended")
279
        prim_num=$(cut -d: -f1 <<< "$last_primary")
280

    
281
        if [ "$ext_num" != "$last_prim_num" ]; then
282
            echo "$last_prim_num"
283
        else
284
            echo "$last_part_num"
285
        fi
286
    else
287
        echo "$last_part_num"
288
    fi
289
}
290

    
291
create_partition() {
292
    local device="$1"
293
    local part="$2"
294
    local ptype="$3"
295

    
296
    declare -a fields
297
    IFS=":;" read -ra fields <<< "$part"
298
    local id="${fields[0]}"
299
    local start="${fields[1]}"
300
    local end="${fields[2]}"
301
    local size="${fields[3]}"
302
    local fs="${fields[4]}"
303
    local name="${fields[5]}"
304
    local flags="${fields[6]//,/ }"
305

    
306
    $PARTED -s -m -- $device mkpart "$ptype" $fs "$start" "$end"
307
    for flag in $flags; do
308
        $PARTED -s -m $device set "$id" "$flag" on
309
    done
310
}
311

    
312
enlarge_partition() {
313
    local device="$1"
314
    local part="$2"
315
    local ptype="$3"
316
    local new_end="$4"
317

    
318
    if [ -z "$new_end" ]; then
319
        new_end=$(cut -d: -f 3 <<< "$(get_last_free_sector "$device")")
320
    fi
321

    
322
    declare -a fields
323
    IFS=":;" read -ra fields <<< "$part"
324
    fields[2]="$new_end"
325

    
326
    local new_part=""
327
    for ((i = 0; i < ${#fields[*]}; i = i + 1)); do
328
        new_part="$new_part":"${fields[$i]}"
329
    done
330
    new_part=${new_part:1}
331

    
332
    # If this is an extended partition, removing it will also remove the
333
    # logical partitions it contains. We need to save them for later.
334
    if [ "$ptype" = "extended" ]; then
335
        local table="$(get_partition_table "$device")"
336
        local logical="$(get_logical_partitions "$table")"
337
    fi
338

    
339
    id=${fields[0]}
340
    $PARTED -s -m "$device" rm "$id"
341
    create_partition "$device" "$new_part" "$ptype"
342

    
343
    if [ "$ptype" = "extended" ]; then
344
        # Recreate logical partitions
345
        echo "$logical" | while read logical_part; do
346
            create_partition "$device" "$logical_part" "logical"
347
        done
348
    fi
349
}
350

    
351
get_last_free_sector() {
352
    local dev="$1"
353
    local unit="$2"
354

    
355
    if [ -n "$unit" ]; then
356
        unit="unit $unit"
357
    fi
358

    
359
    local last_line="$("$PARTED" -s -m "$dev" "$unit" print free | tail -1)"
360
    local ptype="$(cut -d: -f 5 <<< "$last_line")"
361

    
362
    if [ "$ptype" = "free;" ]; then
363
        echo "$last_line"
364
    fi
365
}
366

    
367
cleanup() {
368
    # if something fails here, it shouldn't call cleanup again...
369
    trap - EXIT
370

    
371
    if [ ${#CLEANUP[*]} -gt 0 ]; then
372
        LAST_ELEMENT=$((${#CLEANUP[*]}-1))
373
        REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0)
374
        for i in $REVERSE_INDEXES; do
375
            # If something fails here, it's better to retry it for a few times
376
            # before we give up with an error. This is needed for kpartx when
377
            # dealing with ntfs partitions mounted through fuse. umount is not
378
            # synchronous and may return while the partition is still busy. A
379
            # premature attempt to delete partition mappings through kpartx on
380
            # a device that hosts previously mounted ntfs partition may fail
381
            # with a `device-mapper: remove ioctl failed: Device or resource
382
            # busy' error. A sensible workaround for this is to wait for a
383
            # while and then try again.
384
            local cmd=${CLEANUP[$i]}
385
            $cmd || for interval in 0.25 0.5 1 2 4; do
386
            echo "Command $cmd failed!"
387
            echo "I'll wait for $interval secs and will retry..."
388
            sleep $interval
389
            $cmd && break
390
        done
391
	if [ "$?" != "0" ]; then
392
            echo "Giving Up..."
393
            exit 1;
394
        fi
395
    done
396
  fi
397
}
398

    
399
task_cleanup() {
400
    rc=$?
401

    
402
    if [ $rc -eq 0 ]; then
403
       report_end_task
404
    else
405
       report_error
406
    fi
407

    
408
    cleanup
409
}
410

    
411
check_if_excluded() {
412

    
413
    local exclude=SNF_IMAGE_PROPERTY_EXCLUDE_TASK_${PROGNAME:2}
414
    if [ -n "${!exclude}" ]; then
415
        warn "Task $PROGNAME was excluded and will not run."
416
        exit 0
417
    fi
418

    
419
    return 0
420
}
421

    
422
trap cleanup EXIT
423
set -o pipefail
424

    
425
STDERR_FILE=$(mktemp)
426
add_cleanup rm -f "$STDERR_FILE"
427
exec 2> >(tee -a "$STDERR_FILE" >&2)
428

    
429
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :