common.sh.in 12.1 KB
Newer Older
1
# Copyright (C) 2011-2014 GRNET S.A.
2
# 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"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
25
INSTALL_MBR="install-mbr"
26
TIMEOUT="timeout"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
27
CURL="curl"
28
TAR="tar"
29
DATE="date -u" # Time in UTC
Nikos Skalkotos's avatar
Nikos Skalkotos committed
30

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

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

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

Dimitris Aragiorgis's avatar
Dimitris Aragiorgis committed
41

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

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

52
53
54
55
log_warning() {
    echo "[WARNING] $*" >&2
}

56
57
58
report_error() {
    ERROR_MSGS+=("$@")
}
59

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

67

68
69
close_fd() {
    local fd="$1"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
70
    exec {fd}>&-
71
72
}

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

85
    eval "echo $(printf "%q" "$report") >&${MONITOR_FD}"
86
87
}

Nikos Skalkotos's avatar
Nikos Skalkotos committed
88
89
90
91
92
93
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
94
95
96
97
98
99
100

    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
101
102
103
104
105
106
107
    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
108
            log_error "Missing OS API Variable EXPORT_DEVICE"
109
            exit 1
110
111
        fi
        blockdev=$EXPORT_DEVICE
Nikos Skalkotos's avatar
Nikos Skalkotos committed
112
113
    elif [ "$SCRIPT_NAME" = "import" ]; then
        if [ -z "$IMPORT_DEVICE" ]; then
114
115
            log_error "Missing OS API Variable IMPORT_DEVICE"
            exit 1
Nikos Skalkotos's avatar
Nikos Skalkotos committed
116
117
118
119
120
121
122
        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"
123
        exit 1
Nikos Skalkotos's avatar
Nikos Skalkotos committed
124
125
126
127
128
    fi
    old_name=$OLD_INSTANCE_NAME
}

get_api20_arguments() {
129
    get_api10_arguments
Nikos Skalkotos's avatar
Nikos Skalkotos committed
130

131
    if [ "$SCRIPT_NAME" = "create" ]; then
132
        local osparams="IMG_ID IMG_FORMAT IMG_PASSWD IMG_PROPERTIES IMG_PERSONALITY CONFIG_URL"
133

134
135
136
137
        # Store OSP_VAR in VAR
        for param in $osparams; do
            eval $param=\"\$OSP_$param\"
        done
138

139
140
141
142
143
144
        if [ -n "$CONFIG_URL" ]; then
            local config config_params
            echo "Downloading configuration parameters from: \`$CONFIG_URL'" >&2
            config=$($CURL -f "$CONFIG_URL")
            config_params=$(./decode-config.py $osparams <<< "$config")
            eval "$config_params"
145
        fi
146
    fi
Nikos Skalkotos's avatar
Nikos Skalkotos committed
147
}
148

149
150
151
152
153
154
155
156
157
158
159
parameter_check() {
    local required_osparams="IMG_ID IMG_FORMAT IMG_PASSWD"

    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
160
161
map_disk0() {
    blockdev="$1"
162
    filesystem_dev_base=$($KPARTX -l -s -p- $blockdev | \
Nikos Skalkotos's avatar
Nikos Skalkotos committed
163
                            grep -m 1 -- "-1.*$blockdev" | \
164
                            $AWK '{print $1}')
Nikos Skalkotos's avatar
Nikos Skalkotos committed
165
166
167
168
    if [ -z "$filesystem_dev_base" ]; then
        log_error "Cannot interpret kpartx output and get partition mapping"
        exit 1
    fi
169
    $KPARTX -a -p- "$blockdev" > /dev/null
Nikos Skalkotos's avatar
Nikos Skalkotos committed
170
171
172
173
174
175
176
177
178
179
    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() {
180
    $KPARTX -d -p- "$1"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
181
182
183
184
185
}

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

Nikos Skalkotos's avatar
Nikos Skalkotos committed
187
188
189
190
191
192
193
194
195
196
197
    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
}

198
create_floppy() {
Nikos Skalkotos's avatar
Nikos Skalkotos committed
199
200
201
    local img target

    img=$1
202

Nikos Skalkotos's avatar
Nikos Skalkotos committed
203
    target=$(mktemp -d)
204
    add_cleanup rmdir "$target"
205

206
207
208
    dd bs=512 count=2880 if=/dev/zero of="$img"
    mkfs.ext2 -F "$img" > /dev/null
    mount "$img" "$target" -o loop
209
210
    set | egrep ^snf_export_\\w+= | sed -e 's/^snf_export_/export SNF_IMAGE_/' \
        > "$target/rules"
211
212
213
214
215
    if [ -n "$UNATTEND" ]; then
        if [ -f "$UNATTEND" ]; then
            cat "$UNATTEND" > "$target/unattend.xml"
        else
            log_error "Unattend file: \`"$UNATTEND"' does not exist"
216
            exit 1
217
218
        fi
    fi
219
    umount "$target"
220
221
}

222
223
224
225
get_backend_type() {
    local id=$1

    if [[ "$id" =~ ^pithos: ]]; then
226
        echo "pithos"
227
228
    elif [[ "$id" =~ ^pithosmap: ]]; then
        echo "pithos"
229
    elif [[ "$id" =~ ^(http|ftp)s?: ]]; then
230
        echo "network"
231
232
    elif [ "$id" = "null" ]; then
        echo "null"
233
    else
234
        echo "local"
235
236
237
    fi
}

238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
canonicalize() {
    local name="$1"

    if [ -d "$name" ]; then
        name="$name/"
    fi

    local dname="${name%/*}"
    local fname="${name##*/}"

    if [ "x$dname" = "x" -a "${name:0:1}" = "/" ]; then
        dname="/"
    fi

    if [ -d "$dname" ]; then
        (cd -- "$dname" && echo "${PWD%/}/$fname")
    else
        echo
    fi
}

259
# this one is only to be called by ganeti OS interface scripts
260
ganeti_os_main() {
261
262
263

    if [ "$OS_API_VERSION" != "20" ]; then
        log_error "Unknown OS API VERSION $OS_API_VERSION. Only OS API VERSION 20 is supported"
264
265
        exit 1
    fi
266
267

    get_api20_arguments
268
269
    source_variant
}
270

271
source_variant() {
272
273
274
275
276
277
278
279
280
281
    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
282
                log_error "ERROR: snf-image configuration error"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
283
284
285
286
                log_error "  Published variant $OS_VARIANT is missing its" \
                    "config file"
                log_error "  Please create $VARIANT_CONFIG or unpublish the" \
                    "variant"
287
288
289
                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
290
291
                log_error "Most probably this is a user error, forcing a" \
                    "wrong name"
292
                log_error "To support this variant please create file" \
Nikos Skalkotos's avatar
Nikos Skalkotos committed
293
                    "$VARIANT_CONFIG"
294
295
296
297
298
299
            fi
            exit 1
        fi
    fi
}

300
301
do_multistrap() {
   local target="$1"
302
303
   local cache="$2"
   local pkgs="$3"
304

305
306
307
308
309
    # 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
310
311
312
313
314
315
316
317
318

    # 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
319
   multistrap -d "$target" -f "$MULTISTRAP_CONFIG" 2>&1 | sed -u -e 's/^/MULTISTRAP: /g'
320

321
   rm "$target/usr/sbin/policy-rc.d"
Nikos Skalkotos's avatar
Nikos Skalkotos committed
322
   rm -rf "$target/etc/apt/preferences.d"
323
324
}

325
report_and_cleanup() {
326
    send_errors
Nikos Skalkotos's avatar
Nikos Skalkotos committed
327
328
    cleanup
}
329

330
331
332
333
suppress_errors() {
    "$@" &> /dev/null || true
}

334
335
336
337
338
339
340
341
342
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
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
369
cleanup() {
370
    # if something fails here, it shouldn't call cleanup again...
371
    trap - EXIT
372

Nikos Skalkotos's avatar
Nikos Skalkotos committed
373
374
375
376
    if [ ${#CLEANUP[*]} -gt 0 ]; then
        LAST_ELEMENT=$((${#CLEANUP[*]}-1))
        REVERSE_INDEXES=$(seq ${LAST_ELEMENT} -1 0)
        for i in $REVERSE_INDEXES; do
377
378
            # 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
379
            # dealing with NTFS partitions mounted through fuse. umount is not
380
            # synchronous and may return while the partition is still busy. A
381
            # premature attempt to delete partition mappings through kpartx on
382
            # a device that hosts previously mounted NTFS partition may fail
383
384
385
386
            # 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.
387
388
389
390
391
392
            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
393
        done
Nikos Skalkotos's avatar
Nikos Skalkotos committed
394
395
396
397
        if [ "$?" != "0" ]; then
            echo "Giving Up..."
            exit 1;
        fi
398
399
    done
  fi
Nikos Skalkotos's avatar
Nikos Skalkotos committed
400
401
402
403
404
405
406
407
408
409
410
}

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
411
: ${IMAGE_DEBUG:="no"}
412
: ${VERSION_CHECK:="@VERSION_CHECK@"}
413
: ${HELPER_DIR:="@HELPER_DIR@"}
Nikos Skalkotos's avatar
Nikos Skalkotos committed
414
415
: ${HELPER_SIZE:="600"}
: ${HELPER_SOFT_TIMEOUT:=120}
416
417
: ${HELPER_HARD_TIMEOUT:=5}
: ${HELPER_USER:="nobody"}
418
: ${HELPER_MEMORY:="512"}
419
: ${PITHOS_DB:="sqlite:////@localstatedir@/lib/pithos/backend.db"}
420
: ${PITHOS_DATA:="@localstatedir@/lib/pithos/data/"}
421
422
423
424
: ${PITHOS_BACKEND_STORAGE:="nfs"}
: ${PITHOS_RADOS_CEPH_CONF:="@sysconfdir@/ceph/ceph.conf"}
: ${PITHOS_RADOS_POOL_MAPS:="maps"}
: ${PITHOS_RADOS_POOL_BLOCKS:="blocks"}
425
: ${PROGRESS_MONITOR:="@PROGRESS_MONITOR@"}
426
: ${UNATTEND:="@UNATTEND@"}
427
: ${XEN_SCRIPTS_DIR="@sysconfdir@/xen/scripts"}
428
: ${MULTISTRAP_CONFIG:="@MULTISTRAP_CONFIG@"}
429
: ${MULTISTRAP_APTPREFDIR:="@MULTISTRAP_APTPREFDIR@"}
Nikos Skalkotos's avatar
Nikos Skalkotos committed
430
431
432
433

SCRIPT_NAME=$(basename $0)

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