~rvb/maas/bug-1234853-1.3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#!/usr/bin/env bash
# Copyright 2012 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
#
# Download static files needed for net-booting nodes through TFTP:
# pre-boot loader, kernels, and initrd images.
#
# This script downloads the required files into the TFTP home directory
# (by default, /var/lib/maas/tftp).  Run it with the necessarily privileges
# to write them there.

# Exit immediately if a command exits with a non-zero status.
set -o errexit
# Treat unset variables as an error when substituting.
set -o nounset

# Load settings if available.
settings="/etc/maas/import_pxe_files"
[ -r $settings ] && . $settings

# Location of the GPG keyring for the Ubuntu archive.
GPG_KEYRING="${GPG_KEYRING:-/usr/share/keyrings/ubuntu-archive-keyring.gpg}"

# Whether to check GPG keys (necessary for testing of this script).
IGNORE_GPG="${IGNORE_GPG:-}"

# Download locations for Ubuntu releases.  When the cluster controller runs
# the import scripts, it provides settings from the server side.
MAIN_ARCHIVE=${MAIN_ARCHIVE:-http://archive.ubuntu.com/ubuntu/}
PORTS_ARCHIVE=${PORTS_ARCHIVE:-http://ports.ubuntu.com/ubuntu-ports/}

# Ubuntu releases that are to be downloaded.
SUPPORTED_RELEASES=$(distro-info --supported)
RELEASES=${RELEASES:-$SUPPORTED_RELEASES}

# The current Ubuntu release.
STABLE_RELEASE=$(distro-info --stable)

# Supported architectures.
# armhf/highbank is also supported by this script, but cannot be enabled here
# until maas-import-ephemerals also supports it or IMPORT_EPHEMERALS is set to
# 0.
ARCHES=${ARCHES:-amd64/generic i386/generic armhf/highbank}

# Command line to download a resource at a given URL into the current
# directory.  A wget command line will work here, but curl will do as well.
DOWNLOAD=${DOWNLOAD:-wget --no-verbose}

# Whether to download ephemeral images as well: "1" for yes, "0" for no.
# Default is yes.
IMPORT_EPHEMERALS=${IMPORT_EPHEMERALS:-1}

fail() {
    local msg=$1

    echo $msg >&2
    exit 1
}

# Show script usage/summary.
show_usage() {
    echo "Usage: ${0##*/}"
    echo
    echo "This helper script downloads the relevant boot images from an "
    echo "Ubuntu archive and uses 'maas' to provision them for PXE booting "
    echo "from TFTP."
    echo
    echo "This script takes no arguments, but you can adjust some parameters "
    echo -e "by editing the config file found at \033[1m$settings\033[0m."
    echo
    echo "MAAS homepage:<http://maas.ubuntu.com>"
    echo
}

# Return a URL that points to the images directory for the relevant
# release/arch.
compose_installer_base_url() {
    local arch=$1 release=$2

    case $arch in
        amd64/*|i386/*)
            local installer_url="$MAIN_ARCHIVE/dists/$release/main/installer-${arch%%/*}"
            echo "$installer_url/current/images/"
            ;;
        armhf/*)
            # No ARM server installers were available in precise, so always go for -updates for now
            # A better general fix is LP: #1052397
            if [ "$release" = "precise" ]; then
                updates=-updates
            else
                updates=
            fi
            local installer_url="$PORTS_ARCHIVE/dists/${release}${updates}/main/installer-${arch%%/*}"
            echo "$installer_url/current/images/"
            ;;
        *)
            echo "Unknown architecture: $arch" >&2
            exit 1
            ;;
    esac
}

# Return the URL part that is appended to the base url that gives the location
# of the images.
compose_installer_download_url_postfix() {
    local arch=$1

    case $arch in
        amd64/*|i386/*)
            echo "netboot/ubuntu-installer/${arch%%/*}/"
            ;;
        armhf/*)
            echo "${arch#*/}/netboot/"
            ;;
        *)
            echo "Unknown architecture: $arch" >&2
            exit 1
            ;;
    esac
}

# Put together a full URL for where the installer files for architecture $1
# and release $2 can be downloaded.
compose_installer_download_url() {
    local arch=$1 release=$2

    base_url=$(compose_installer_base_url $arch $release)
    postfix=$(compose_installer_download_url_postfix $arch)

    echo "$base_url/$postfix"
}

fetch_server_md5sums() {
    local base_url=$1
    local ignore

    # Make $IGNORE_GPG non-empty for test programs, as they can't possibly
    # GPG sign with the right key.
    if [ "x$IGNORE_GPG" == "x" ]; then
        $DOWNLOAD "$base_url/MD5SUMS" &&
            $DOWNLOAD "$base_url/MD5SUMS.gpg" ||
                fail "unable to download $base_url/MD5SUMS[.gpg]"

        ignore=$(gpg --keyring=$GPG_KEYRING --verify MD5SUMS.gpg MD5SUMS 2>&1) ||
            fail "failed to verify MD5SUMS via $GPG_KEYRING ($base_url/MD5SUMS)"
    else
        $DOWNLOAD "$base_url/MD5SUMS" ||
            fail "unable to download $base_url/MD5SUMS"
    fi
}

get_md5sum_for_file() {
    local filename=$1

    # The filename supplied in $1 must be the full path as seen in the
    # MD5SUMS file.  The files are rooted from a single place so the grepped
    # string will only match once.
    server_md5sum=$(grep $filename MD5SUMS|awk '{print $1}') ||
        fail "failed to find checksum for $filename"
    echo $server_md5sum
}

check_checksum() {
    local server_md5sum=$1 file_on_disk=$2
    local md5sum

    md5sum=$(md5sum $file_on_disk|awk '{print $1}')

    if [ "$md5sum" != "$server_md5sum" ]; then
        fail "md5 checksum mismatch for $file_on_disk: expected $server_md5sum, got $md5sum"
    fi
}

# Return a list of files for architecture $1 and release $2 that need to be
# downloaded
compose_installer_download_files() {
    local arch=$1 release=$2

    case $arch in
        amd64/*|i386/*)
            echo "linux initrd.gz"
            ;;
        armhf/highbank)
            echo "vmlinuz initrd.gz"
            ;;
        *)
            echo "Unknown architecture: $arch" >&2
            exit 1
            ;;
    esac
}


# Rename downloaded files for architecture $1 and release $2 into the form that
# MAAS expects them
rename_installer_download_files() {
    local arch=$1 release=$2

    case $arch in
        amd64/*|i386/*)
            # do nothing
            ;;
        armhf/highbank)
            mv vmlinuz linux
            ;;
        *)
            echo "Unknown architecture: $arch" >&2
            exit 1
            ;;
    esac
}


# Copy the pre-boot loader pxelinux.0, and modules we need, from the
# installed syslinux version.  Install it into the TFTP tree for
# netbooting.
update_pre_boot_loader() {
    for loader_file in pxelinux.0 chain.c32 ifcpu64.c32
    do
        maas-provision install-pxe-bootloader \
            --loader="/usr/lib/syslinux/$loader_file"
    done
}


# Download kernel/initrd for installing Ubuntu release $2 for
# architecture $1 and install it into the TFTP directory hierarchy.
update_install_files() {
    local arch=$1 release=$2
    local files file url file_prefix filename_in_md5sums_file md5sum

    files=$(compose_installer_download_files $arch $release)
    url=$(compose_installer_download_url $arch $release)

    mkdir "install"
    pushd "install" >/dev/null
    fetch_server_md5sums $(compose_installer_base_url $arch $release)
    echo "MD5SUMS GPG signature OK for $arch $release"
    for file in $files
    do
        $DOWNLOAD $url/$file
        file_prefix=$(compose_installer_download_url_postfix $arch)
        filename_in_md5sums_file=$file_prefix$file
        md5sum=$(get_md5sum_for_file $filename_in_md5sums_file)
        check_checksum $md5sum $file
        echo "'$file' md5sum OK"
    done
    rename_installer_download_files $arch $release
    popd >/dev/null

    maas-provision install-pxe-image \
        --arch="${arch%%/*}" --subarch="${arch#*/}" \
        --release=$release --purpose="install" \
        --image="install"
}


# Download and install the "install" images.
import_install_images() {
    local arch release DOWNLOAD_DIR

    DOWNLOAD_DIR=$(mktemp -d)
    echo "Downloading to temporary location $DOWNLOAD_DIR."
    pushd -- $DOWNLOAD_DIR

    for arch in $ARCHES
    do
        for release in $RELEASES
        do
            update_install_files $arch $release
        done
    done

    popd
    rm -rf -- $DOWNLOAD_DIR
}


# Download and install the ephemeral images.
import_ephemeral_images() {
    if test "$IMPORT_EPHEMERALS" != "0"
    then
        maas-import-ephemerals
    fi
}


main() {
    # All files we create here are public.  The TFTP user will need to be
    # able to read them.
    umask a+r

    update_pre_boot_loader
    import_install_images
    import_ephemeral_images
}

# check for commandline arguments
if [ $# -gt 0 ]
  then
   case $1 in
    "-h"|"--help") show_usage ; exit ;;
    esac
fi

if [ ! -f "$GPG_KEYRING" ]; then
    fail "gpg keyring $GPG_KEYRING is not a file"
fi

main