Statistics
| Branch: | Tag: | Revision:

root / snf-image-helper / common.sh @ 2a0ab295

History | View | Annotate | Download (10.7 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 id="$SNF_IMAGE_HOSTNAME"
63
    local type="ganeti-start-task"
64
    local timestamp=$(date +%s)
65
    local name="${PROGNAME}"
66

    
67
    report="{\"id\":\"$id\","
68
    report+="\"type\":\"$type\"," \
69
    report+="\"timestamp\":$(date +%s)," \
70
    report+="\"name\":\"$name\"}"
71

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

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

    
87
    echo "$report"
88
}
89

    
90
report_end_task() {
91

    
92
    local id="$SNF_IMAGE_HOSTNAME"
93
    local type="ganeti-end-task"
94
    local timestam=$(date +%s)
95
    local name=${PROGNAME}
96
    local warnings=$(json_list WARNINGS[@])
97

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

    
104
    echo "$report" > "$MONITOR"
105
}
106

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

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

    
124
    echo "$report" > "$MONITOR"
125
}
126

    
127
get_base_distro() {
128
    local root_dir=$1
129

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

    
145
get_distro() {
146
    local root_dir=$1
147

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

    
174

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

    
184
    echo "$output"
185
}
186

    
187
get_partition_table_type() {
188
    local ptable="$1"
189

    
190
    local dev="$(sed -n 2p <<< "$ptable")"
191
    declare -a field
192
    IFS=':' read -ra field <<< "$dev"
193

    
194
    echo "${field[5]}"
195
}
196

    
197
get_partition_count() {
198
    local ptable="$1"
199

    
200
    expr $(echo "$ptable" | wc -l) - 2
201
}
202

    
203
get_partition_by_num() {
204
    local ptable="$1"
205
    local id="$2"
206

    
207
    grep "^$id:" <<< "$ptable"
208
}
209

    
210
get_last_partition() {
211
    local ptable="$1"
212

    
213
    echo "$ptable" | tail -1
214
}
215

    
216
is_extended_partition() {
217
    local dev="$1"
218
    local part_num="$2"
219

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

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

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

    
242
get_logical_partitions() {
243
    local ptable="$1"
244

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

    
252
    return 0
253
}
254

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

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

    
268
get_partition_to_resize() {
269
    local dev="$1"
270

    
271
    table=$(get_partition_table "$dev")
272

    
273
    if [ $(get_partition_count "$table") -eq 0 ]; then
274
        return 0
275
    fi
276

    
277
    table_type=$(get_partition_table_type "$table")
278
    last_part=$(get_last_partition "$table")
279
    last_part_num=$(cut -d: -f1 <<< "$last_part")
280

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

    
287
        if [ "$ext_num" != "$last_prim_num" ]; then
288
            echo "$last_prim_num"
289
        else
290
            echo "$last_part_num"
291
        fi
292
    else
293
        echo "$last_part_num"
294
    fi
295
}
296

    
297
create_partition() {
298
    local device="$1"
299
    local part="$2"
300
    local ptype="$3"
301

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

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

    
318
enlarge_partition() {
319
    local device="$1"
320
    local part="$2"
321
    local ptype="$3"
322
    local new_end="$4"
323

    
324
    if [ -z "$new_end" ]; then
325
        new_end=$(cut -d: -f 3 <<< "$(get_last_free_sector "$device")")
326
    fi
327

    
328
    declare -a fields
329
    IFS=":;" read -ra fields <<< "$part"
330
    fields[2]="$new_end"
331

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

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

    
345
    id=${fields[0]}
346
    $PARTED -s -m "$device" rm "$id"
347
    create_partition "$device" "$new_part" "$ptype"
348

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

    
357
get_last_free_sector() {
358
    local dev="$1"
359
    local unit="$2"
360

    
361
    if [ -n "$unit" ]; then
362
        unit="unit $unit"
363
    fi
364

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

    
368
    if [ "$ptype" = "free;" ]; then
369
        echo "$last_line"
370
    fi
371
}
372

    
373
cleanup() {
374
    # if something fails here, it shouldn't call cleanup again...
375
    trap - EXIT
376

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

    
405
task_cleanup() {
406
    rc=$?
407

    
408
    if [ $rc -eq 0 ]; then
409
       report_end_task 
410
    else
411
       report_error
412
    fi
413

    
414
    cleanup
415
}
416

    
417
check_if_excluded() {
418

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

    
425
    return 0
426
}
427

    
428
trap cleanup EXIT
429
set -o pipefail
430

    
431
STDERR_FILE=$(mktemp)
432
add_cleanup rm -f "$STDERR_FILE"
433
exec 2> >(tee -a "$STDERR_FILE" >&2)
434

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