Statistics
| Branch: | Tag: | Revision:

root / snf-image-helper / common.sh @ df1c62e2

History | View | Annotate | Download (10.6 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="{\"id\":\"$id\","
67
    report+="\"type\":\"$type\"," \
68
    report+="\"timestamp\":$(date +%s.%N)," \
69
    report+="\"name\":\"$name\"}"
70

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

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

    
86
    echo "$report"
87
}
88

    
89
report_end_task() {
90

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

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

    
102
    echo "$report" > "$MONITOR"
103
}
104

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

    
113
    report="{\"id\":\"$id\","
114
    report+="\"type\":\"$type\"," \
115
    report+="\"timestamp\":$(date +%s)," \
116
    report+="\"location\":\"$location\"," \
117
    report+="\"errors\":$errors," \
118
    report+="\"warnings\":$warnings," \
119
    report+="\"stderr\":\"$stderr\"}"
120

    
121
    echo "$report" > "$MONITOR"
122
}
123

    
124
get_base_distro() {
125
    local root_dir=$1
126

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

    
142
get_distro() {
143
    local root_dir=$1
144

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

    
171

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

    
181
    echo "$output"
182
}
183

    
184
get_partition_table_type() {
185
    local ptable="$1"
186

    
187
    local dev="$(sed -n 2p <<< "$ptable")"
188
    declare -a field
189
    IFS=':' read -ra field <<< "$dev"
190

    
191
    echo "${field[5]}"
192
}
193

    
194
get_partition_count() {
195
    local ptable="$1"
196

    
197
    expr $(echo "$ptable" | wc -l) - 2
198
}
199

    
200
get_partition_by_num() {
201
    local ptable="$1"
202
    local id="$2"
203

    
204
    grep "^$id:" <<< "$ptable"
205
}
206

    
207
get_last_partition() {
208
    local ptable="$1"
209

    
210
    echo "$ptable" | tail -1
211
}
212

    
213
is_extended_partition() {
214
    local dev="$1"
215
    local part_num="$2"
216

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

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

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

    
239
get_logical_partitions() {
240
    local ptable="$1"
241

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

    
249
    return 0
250
}
251

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

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

    
265
get_partition_to_resize() {
266
    local dev="$1"
267

    
268
    table=$(get_partition_table "$dev")
269

    
270
    if [ $(get_partition_count "$table") -eq 0 ]; then
271
        return 0
272
    fi
273

    
274
    table_type=$(get_partition_table_type "$table")
275
    last_part=$(get_last_partition "$table")
276
    last_part_num=$(cut -d: -f1 <<< "$last_part")
277

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

    
284
        if [ "$ext_num" != "$last_prim_num" ]; then
285
            echo "$last_prim_num"
286
        else
287
            echo "$last_part_num"
288
        fi
289
    else
290
        echo "$last_part_num"
291
    fi
292
}
293

    
294
create_partition() {
295
    local device="$1"
296
    local part="$2"
297
    local ptype="$3"
298

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

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

    
315
enlarge_partition() {
316
    local device="$1"
317
    local part="$2"
318
    local ptype="$3"
319
    local new_end="$4"
320

    
321
    if [ -z "$new_end" ]; then
322
        new_end=$(cut -d: -f 3 <<< "$(get_last_free_sector "$device")")
323
    fi
324

    
325
    declare -a fields
326
    IFS=":;" read -ra fields <<< "$part"
327
    fields[2]="$new_end"
328

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

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

    
342
    id=${fields[0]}
343
    $PARTED -s -m "$device" rm "$id"
344
    create_partition "$device" "$new_part" "$ptype"
345

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

    
354
get_last_free_sector() {
355
    local dev="$1"
356
    local unit="$2"
357

    
358
    if [ -n "$unit" ]; then
359
        unit="unit $unit"
360
    fi
361

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

    
365
    if [ "$ptype" = "free;" ]; then
366
        echo "$last_line"
367
    fi
368
}
369

    
370
cleanup() {
371
    # if something fails here, it shouldn't call cleanup again...
372
    trap - EXIT
373

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

    
402
task_cleanup() {
403
    rc=$?
404

    
405
    if [ $rc -eq 0 ]; then
406
       report_end_task
407
    else
408
       report_error
409
    fi
410

    
411
    cleanup
412
}
413

    
414
check_if_excluded() {
415

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

    
422
    return 0
423
}
424

    
425
trap cleanup EXIT
426
set -o pipefail
427

    
428
STDERR_FILE=$(mktemp)
429
add_cleanup rm -f "$STDERR_FILE"
430
exec 2> >(tee -a "$STDERR_FILE" >&2)
431

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