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

19
20
SNF_IMAGE_VERSION="@VERSION@"

Nikos Skalkotos's avatar
Nikos Skalkotos committed
21
22
AWK="awk"
LOSETUP="losetup"
23
KPARTX="kpartx"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
24
SFDISK="sfdisk"
25
QEMU_IMG="qemu-img"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
26
INSTALL_MBR="install-mbr"
27
TIMEOUT="timeout"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
28
CURL="curl"
29
TAR="tar"
30
DATE="date -u" # Time in UTC
Nikos Skalkotos's avatar
Nikos Skalkotos committed
31

32
33
34
35
# Temporary use stderr as monitoring file descriptor.
# `create' will overwrite this
MONITOR_FD="2"

Nikos Skalkotos's avatar
Nikos Skalkotos committed
36
MSG_TYPE_ERROR="image-error"
37
MSG_TYPE_INFO="image-info"
38

Nikos Skalkotos's avatar
Nikos Skalkotos committed
39
CLEANUP=( )
40
ERROR_MSGS=( )
Nikos Skalkotos's avatar
Nikos Skalkotos committed
41

Dimitris Aragiorgis's avatar
Dimitris Aragiorgis committed
42

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

Nikos Skalkotos's avatar
Nikos Skalkotos committed
49
log_error() {
50
    echo "[ERROR] $*" >&2
51
52
}

53
54
55
report_error() {
    ERROR_MSGS+=("$@")
}
56

57
report_info() {
Nikos Skalkotos's avatar
Nikos Skalkotos committed
58
    local report
59
    echo "[INFO] $*" >&2
Nikos Skalkotos's avatar
Nikos Skalkotos committed
60
    report="$(./host-monitor.py info <<< "$*")"
61
    eval "echo $(printf "%q" "$report") >&${MONITOR_FD}"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
62
63
}

64

65
66
close_fd() {
    local fd="$1"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
67
    exec {fd}>&-
68
69
}

70
send_errors() {
71
    local report=""
72
    if [ ${#ERROR_MSGS[@]} -gt 0 ]; then
73
        local msg=""
74
        for err in "${ERROR_MSGS[@]}"; do
75
            msg+="$(echo "$err")"
76
        done
77
        report="$(./host-monitor.py error <<< "$msg")"
78
    else
79
        report=$(./host-monitor.py error <<< "Internal Error: Image deployment failed.")
80
    fi
81

82
    eval "echo $(printf "%q" "$report") >&${MONITOR_FD}"
83
84
}

Nikos Skalkotos's avatar
Nikos Skalkotos committed
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
get_api5_arguments() {
    GETOPT_RESULT=$*
    # Note the quotes around `$TEMP': they are essential!
    eval set -- "$GETOPT_RESULT"
    while true; do
        case "$1" in
            -i|-n) instance=$2; shift 2;;

            -o) old_name=$2; shift 2;;

            -b) blockdev=$2; shift 2;;

            -s) swapdev=$2; shift 2;;

            --) shift; break;;

            *)  log_error "Internal error!" >&2; exit 1;;
        esac
    done
    if [ -z "$instance" -o -z "$blockdev" ]; then
        log_error "Missing OS API Argument (-i, -n, or -b)"
        exit 1
    fi
    if [ "$SCRIPT_NAME" != "export" -a -z "$swapdev"  ]; then
        log_error "Missing OS API Argument -s (swapdev)"
        exit 1
    fi
    if [ "$SCRIPT_NAME" = "rename" -a -z "$old_name"  ]; then
        log_error "Missing OS API Argument -o (old_name)"
        exit 1
    fi
}

get_api10_arguments() {
    if [ -z "$INSTANCE_NAME" -o -z "$HYPERVISOR" -o -z "$DISK_COUNT" ]; then
        log_error "Missing OS API Variable:"
        log_error "(INSTANCE_NAME HYPERVISOR or DISK_COUNT)"
        exit 1
    fi
124
125
126
127
128
129
130

    case $HYPERVISOR in
        xen-hvm|xen-pvm) . xen-common.sh ;;
        kvm) . kvm-common.sh ;;
        *) log_error "Unsupported hypervisor: \`$HYPERVISTOR'"; exit 1;;
    esac

Nikos Skalkotos's avatar
Nikos Skalkotos committed
131
132
133
134
135
136
137
    instance=$INSTANCE_NAME
    if [ $DISK_COUNT -lt 1 -o -z "$DISK_0_PATH" ]; then
        log_error "At least one disk is needed"
        exit 1
    fi
    if [ "$SCRIPT_NAME" = "export" ]; then
        if [ -z "$EXPORT_DEVICE" ]; then
138
            log_error "Missing OS API Variable EXPORT_DEVICE"
139
            exit 1
140
141
        fi
        blockdev=$EXPORT_DEVICE
Nikos Skalkotos's avatar
Nikos Skalkotos committed
142
143
    elif [ "$SCRIPT_NAME" = "import" ]; then
        if [ -z "$IMPORT_DEVICE" ]; then
144
145
            log_error "Missing OS API Variable IMPORT_DEVICE"
            exit 1
Nikos Skalkotos's avatar
Nikos Skalkotos committed
146
147
148
149
150
151
152
        fi
        blockdev=$IMPORT_DEVICE
    else
        blockdev=$DISK_0_PATH
    fi
    if [ "$SCRIPT_NAME" = "rename" -a -z "$OLD_INSTANCE_NAME" ]; then
        log_error "Missing OS API Variable OLD_INSTANCE_NAME"
153
        exit 1
Nikos Skalkotos's avatar
Nikos Skalkotos committed
154
155
156
157
158
    fi
    old_name=$OLD_INSTANCE_NAME
}

get_api20_arguments() {
159
    get_api10_arguments
Nikos Skalkotos's avatar
Nikos Skalkotos committed
160

161
162
163
164
165
166
167
168
169
    local required_osparams="IMG_ID IMG_FORMAT IMG_PASSWD"
    local osparams="$required_osparams IMG_PROPERTIES IMG_PERSONALITY CONFIG_URL"

    # Store OSP_VAR in VAR
    for param in $osparams; do
        eval $param=\"\$OSP_$param\"
    done

    if [ -n "$CONFIG_URL" ]; then
170
171
172
173
        local config config_params
        config=$($CURL -f "$CONFIG_URL")
        config_params=$(./decode-config.py $osparams <<< "$config")
        eval "$config_params"
174
    fi
175
176
177
178
179
180
181

    for var in $required_osparams; do
        if [ -z "${!var}" ]; then
             log_error "Missing OS API Parameter: ${var}"
             exit 1
        fi
    done
Nikos Skalkotos's avatar
Nikos Skalkotos committed
182
}
183

Nikos Skalkotos's avatar
Nikos Skalkotos committed
184
185
map_disk0() {
    blockdev="$1"
186
    filesystem_dev_base=$($KPARTX -l -p- $blockdev | \
Nikos Skalkotos's avatar
Nikos Skalkotos committed
187
                            grep -m 1 -- "-1.*$blockdev" | \
188
                            $AWK '{print $1}')
Nikos Skalkotos's avatar
Nikos Skalkotos committed
189
190
191
192
    if [ -z "$filesystem_dev_base" ]; then
        log_error "Cannot interpret kpartx output and get partition mapping"
        exit 1
    fi
193
    $KPARTX -a -p- "$blockdev" > /dev/null
Nikos Skalkotos's avatar
Nikos Skalkotos committed
194
195
196
197
198
199
200
201
202
203
    filesystem_dev="/dev/mapper/${filesystem_dev_base/%-1/}"
    if [ ! -b "/dev/mapper/$filesystem_dev_base" ]; then
        log_error "Can't find kpartx mapped partition:" \
                                            "/dev/mapper/$filesystem_dev_base"
        exit 1
    fi
    echo "$filesystem_dev"
}

unmap_disk0() {
204
    $KPARTX -d -p- "$1"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
205
206
207
208
209
}

format_disk0() {
    local device="$1"
    local image_type="$2"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
210

Nikos Skalkotos's avatar
Nikos Skalkotos committed
211
212
213
214
215
216
217
218
219
220
221
    declare -A part_id=( ['extdump']="83" ["ntfsdump"]="7" )

    # The -f is needed, because we use an optimal alignment and sfdisk complains
    # about partitions not ending on clylinder boundary.
    local sfdisk_cmd="$SFDISK -uS -H 255 -S 63 -f --quiet --Linux --DOS $device"

    $sfdisk_cmd > /dev/null <<EOF
2048,,${part_id["$image_type"]},*
EOF
}

222
create_floppy() {
Nikos Skalkotos's avatar
Nikos Skalkotos committed
223
224
225
    local img target

    img=$1
226

Nikos Skalkotos's avatar
Nikos Skalkotos committed
227
    target=$(mktemp -d)
228
    add_cleanup rmdir "$target"
229

230
231
232
    dd bs=512 count=2880 if=/dev/zero of="$img"
    mkfs.ext2 -F "$img" > /dev/null
    mount "$img" "$target" -o loop
233
234
    set | egrep ^snf_export_\\w+= | sed -e 's/^snf_export_/export SNF_IMAGE_/' \
        > "$target/rules"
235
236
237
238
239
    if [ -n "$UNATTEND" ]; then
        if [ -f "$UNATTEND" ]; then
            cat "$UNATTEND" > "$target/unattend.xml"
        else
            log_error "Unattend file: \`"$UNATTEND"' does not exist"
240
            exit 1
241
242
        fi
    fi
243
    umount "$target"
244
245
}

246
247
248
249
get_backend_type() {
    local id=$1

    if [[ "$id" =~ ^pithos: ]]; then
250
        echo "pithos"
251
252
    elif [[ "$id" =~ ^pithosmap: ]]; then
        echo "pithos"
253
    elif [[ "$id" =~ ^(http|ftp)s?: ]]; then
254
        echo "network"
255
256
    elif [ "$id" = "null" ]; then
        echo "null"
257
    else
258
        echo "local"
259
260
261
    fi
}

262
263
264
265
266
267
268
269
270
271
272
# this one is only to be called by create
ganeti_os_main() {
    if [ -z "$OS_API_VERSION" -o "$OS_API_VERSION" = "5" ]; then
        OS_API_VERSION=5
        GETOPT_RESULT=`getopt -o o:n:i:b:s: -n '$0' -- "$@"`
        if [ $? != 0 ] ; then log_error "Terminating..."; exit 1 ; fi
        get_api5_arguments $GETOPT_RESULT
    elif [ "$OS_API_VERSION" = "10" -o "$OS_API_VERSION" = "15" ]; then
        get_api10_arguments
    elif [ "$OS_API_VERSION" = "20" ]; then
        get_api20_arguments
273
274
275
        IMAGE_NAME="$IMG_ID"
        IMAGE_TYPE="$IMG_FORMAT"
        BACKEND_TYPE=$(get_backend_type $IMG_ID)
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
    else
        log_error "Unknown OS API VERSION $OS_API_VERSION"
        exit 1
    fi
    
    if [ -n "$OS_VARIANT" ]; then
        if [ ! -d "$VARIANTS_DIR" ]; then
            log_error "OS Variants directory $VARIANTS_DIR doesn't exist"
            exit 1
        fi
        VARIANT_CONFIG="$VARIANTS_DIR/$OS_VARIANT.conf"
        if [ -f "$VARIANT_CONFIG" ]; then
            . "$VARIANT_CONFIG"
        else
            if grep -qxF "$OS_VARIANT" variants.list; then
                log_error "ERROR: instance-image configuration error"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
292
293
294
295
                log_error "  Published variant $OS_VARIANT is missing its" \
                    "config file"
                log_error "  Please create $VARIANT_CONFIG or unpublish the" \
                    "variant"
296
297
298
                log_error "  (by removing $OS_VARIANT from variants.list)"
            else
                log_error "Unofficial variant $OS_VARIANT is unsupported"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
299
300
                log_error "Most probably this is a user error, forcing a" \
                    "wrong name"
301
                log_error "To support this variant please create file" \
Nikos Skalkotos's avatar
Nikos Skalkotos committed
302
                    "$VARIANT_CONFIG"
303
304
305
306
307
308
            fi
            exit 1
        fi
    fi
}

309
310
do_multistrap() {
   local target="$1"
311
312
   local cache="$2"
   local pkgs="$3"
313

314
315
316
317
318
    # Create preferences.d for apt
    mkdir -p "$target/etc/apt/preferences.d"
    if [ -d "$MULTISTRAP_APTPREFDIR" ]; then
        find "$MULTISTRAP_APTPREFDIR" -maxdepth 1 -type f -exec cp {} "$target/etc/apt/preferences.d" \;
    fi
319
320
321
322
323
324
325
326
327

    # Create a policy-rc.d file to deny init script execution
    mkdir -p "$target/usr/sbin"
    cat > "$target/usr/sbin/policy-rc.d" <<EOF
#!/bin/sh
exit 101
EOF
    chmod +x "$target/usr/sbin/policy-rc.d"

Nikos Skalkotos's avatar
Nikos Skalkotos committed
328
   multistrap -d "$target" -f "$MULTISTRAP_CONFIG" 2>&1 | sed -u -e 's/^/MULTISTRAP: /g'
329

330
   rm "$target/usr/sbin/policy-rc.d"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
331
   rm -rf "$target/etc/apt/preferences.d"
332
333
}

334
report_and_cleanup() {
335
    send_errors
Nikos Skalkotos's avatar
Nikos Skalkotos committed
336
337
    cleanup
}
338

339
340
341
342
suppress_errors() {
    "$@" &> /dev/null || true
}

343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
check_helper_rc() {
    local rc=$1

    if [ $rc -ne 0 ]; then
        if [ $rc -eq 124 ];  then
            log_error "Customization VM was terminated. Did not finish on time."
            report_error "Image customization failed. Did not finish on time."
        elif [ $rc -eq 137 ]; then # (128 + SIGKILL)
            log_error "Customization VM was killed. Did not finish on time."
            report_error "Image customization failed. Did not finish on time."
        elif [ $rc -eq 141 ]; then # (128 + SIGPIPE)
            log_error "Customization VM was terminated by a SIGPIPE."
            log_error "Maybe progress monitor has died unexpectedly."
        elif [ $rc -eq 125 ]; then
            log_error "Internal Error. Image customization could not start."
            log_error "timeout did not manage to run."
        else
            log_error "Customization VM died unexpectedly (return code $rc)."
        fi
        exit 1
    else
        report_info "Customization VM exited normally."
    fi
}

check_helper_result() {
   local result=$1

    if [ "x$result" != "xSUCCESS" ]; then
        log_error "Image customization failed."
        report_error "Image customization failed."
        exit 1
    fi
}

Nikos Skalkotos's avatar
Nikos Skalkotos committed
378
cleanup() {
379
    # if something fails here, it souldn't call cleanup again...
380
    trap - EXIT
381

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

trap cleanup EXIT

DEFAULT_FILE="@sysconfdir@/default/snf-image"
if [ -f "$DEFAULT_FILE" ]; then
    . "$DEFAULT_FILE"
fi

: ${VARIANTS_DIR:="@sysconfdir@/ganeti/snf-image/variants"}
: ${IMAGE_DIR:="@localstatedir@/lib/snf-image"}
Nikos Skalkotos's avatar
Nikos Skalkotos committed
420
: ${IMAGE_DEBUG:="no"}
421
: ${VERSION_CHECK:="@VERSION_CHECK@"}
422
: ${HELPER_DIR:="@HELPER_DIR@"}
Nikos Skalkotos's avatar
Nikos Skalkotos committed
423
424
: ${HELPER_SIZE:="600"}
: ${HELPER_SOFT_TIMEOUT:=120}
425
426
: ${HELPER_HARD_TIMEOUT:=5}
: ${HELPER_USER:="nobody"}
427
: ${PITHOS_DB:="sqlite:////@localstatedir@/lib/pithos/backend.db"}
428
: ${PITHOS_DATA:="@localstatedir@/lib/pithos/data/"}
429
: ${PROGRESS_MONITOR:="@PROGRESS_MONITOR@"}
430
: ${UNATTEND:="@UNATTEND@"}
431
: ${XEN_SCRIPTS_DIR="@sysconfdir@/xen/scripts"}
432
: ${MULTISTRAP_CONFIG:="@MULTISTRAP_CONFIG@"}
433
: ${MULTISTRAP_APTPREFDIR:="@MULTISTRAP_APTPREFDIR@"}
Nikos Skalkotos's avatar
Nikos Skalkotos committed
434
435
436
437

SCRIPT_NAME=$(basename $0)

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