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
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
|
#!/bin/sh
# Description:
# This script prepares a test environment in a VM from a cloud image
# that is used as testbed to run autopkgtest
#
# Copyright (C) 2012-2013, Canonical Ltd (http://www.canonical.com/)
#
# Author: Jean-Baptiste Lallement <jean-baptiste.lallement@canonical.com>
#
# This software 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 3 of
# the License, or (at your option) any later version.
#
# This software 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.
#
# You should have received a copy of the GNU General Public License
# along with this software. If not, see <http://www.gnu.org/licenses/>.
#
# TODO:
# * Lock file to prevent job building the same target env
# * Normalize exit statuses
# * Create images in /dev/shm then move them to final destination
# * Add support for apt_proxy
# Find out our current directory
BINDIR=$(dirname $(readlink -f $0))
EATMYDATA="$(which eatmydata)" || true
KVMCMD=kvm
APTURI=http://archive.ubuntu.com/ubuntu
USERDATAFILE=""
METADATAFILE=""
# New qemu in raring replaces kvm and requires explicit activation of kvm
# virtualization support
if expr $(lsb_release -rs) '>=' 13.04; then
KVMCMD="qemu-system-x86_64 -enable-kvm"
fi
. $BINDIR/functions
. $BINDIR/../etc/config
# Overrides global configuration
[ -f ~/.adtrc ] && . ~/.adtrc
if [ ! -f "$VMCFG" ]; then
log_info_msg "Using default VM configuration file"
VMCFG=$BINDIR/../etc/vm.cfg
fi
# ~/.adtrc might override BASEDIR, so only set DISKDIR here
[ -n "$DISKDIR" ] || DISKDIR=$BASEDIR/disks
on_exit() {
log_info_msg "Cleaning up"
# Cleanup
if [ -f "$DISKPATH.pid" ]; then
kill -9 $(cat $DISKPATH.pid) || true
rm -f $DISKPATH.pid
fi
rm -f $LOCKDIR/ssh.$SSHPORT.lock
rm -f $LOCKDIR/vnc.$VNCPORT.lock
[ -d "$TDIR" ] && rm -Rf $TDIR
exit $RET
}
trap on_exit EXIT INT
# Main exit code
# on_exit callback exits with this value
RET=0
BUILDID="current"
usage() {
cat <<EOF
Usage: $(basename $0) [OPTIONS] <arch>
Build a base testbed from the latest dev release image
arch Target architecture. i386 or amd64
Options:
-h, --help This help.
-b, --build BUILDID
Use this specific build id instead of default ($BUILDID)
-k,--sshkey PATH
path to private key used to ssh to the VM. Public key is
copied to the VM and must be located in the same directory
than the private key and a .pub extension.
-r,--release CODENAME
release (default:$RELEASE)
-S,--disksize SIZE
disksize of the VM in GB
-d,--debug enable debug mode
-u,--user-data FILE
Use custom user-data.
EOF
exit 1
}
TEMP=$(getopt -o hb:k:r:S:du: --long help,build:,sshkey:,release:,disksize:,debug,user-data: -- "$@")
eval set -- "$TEMP"
while true ; do
case "$1" in
-h|--help)
usage;;
-b|--build)
BUILDID=$2
shift 2;;
-k|--sshkey)
KEYPATH=$2
shift 2;;
-r|--release)
RELEASE=$2
shift 2;;
-S|--size)
DISKSIZE=$2
shift 2;;
-d|--debug)
set -x
shift;;
-u|--user-data)
USERDATAFILE=$2
shift 2;;
-m|--meta-data)
METADATAFILE="$2"
shift 2;;
--) shift ; break ;;
*) usage;;
esac
done
exec 2>&1
if [ ! -z "$1" ]; then
ARCH=$1
else
usage
fi
TDIR=$(mktemp -d ${TMPDIR:-/tmp}/adt-${ARCH}.XXXXXX)
CLOUDIMGURL=http://cloud-images.ubuntu.com/$RELEASE/$BUILDID
IMGDIR=${BASEDIR}/images/
IMGNAME=$RELEASE-server-cloudimg-${ARCH}-disk1.img
IMGPATH=$IMGDIR/$IMGNAME
DISKNAME=pristine-${RELEASE}-${ARCH}-$(date +%Y%m%d_%H%M%S).img
DISKPATH=$DISKDIR/$DISKNAME
USERDATA=${DISKDIR}/user-data
METADATA=${DISKDIR}/meta-data
EXTRAENV=""
# Download Server ISO that we'll use to build the testbed
DODOWNLOAD=""
mkdir -p $IMGDIR $DISKDIR
DEFAULT_KEY=$DISKDIR/adtkey
if [ ! -f $DEFAULT_KEY ]; then
log_info_msg "Generating default ssh key"
ssh-keygen -N '' -f $DEFAULT_KEY
fi
if [ ! -f "$IMGPATH" ]; then
DODOWNLOAD="Y"
else
# Verify Checksum
log_info_msg "Verifying checksum"
LOCALCHKSUM=$(md5sum $IMGPATH | cut -d' ' -f1)
wget -O $TDIR/MD5SUMS $CLOUDIMGURL/MD5SUMS
REMOTECHKSUM=$( grep $IMGNAME $TDIR/MD5SUMS | cut -d' ' -f1)
if [ "$LOCALCHKSUM" = "$REMOTECHKSUM" ]; then
log_info_msg "Checksums match. Not downloading again"
else
log_info_msg "Checksums differs. Marking for download"
DODOWNLOAD="Y"
fi
fi
if [ ! -z "$DODOWNLOAD" ]; then
if wget -O $TDIR/$IMGNAME $CLOUDIMGURL/$IMGNAME; then
# TODO Make sure there is no running snapshot
mv $TDIR/$IMGNAME $IMGPATH
else
log_failure_msg "An error occurred during download. Aborting!"
RET=1
exit $RET
fi
fi
cp $IMGPATH $DISKPATH
qemu-img resize $DISKPATH +${DISKSIZE}
cd $DISKDIR
if [ ! -z "$KEYPATH" ]; then
PUBKEY="$( cat $KEYPATH.pub )"
else
[ ! -f $DEFAULT_KEY ] && ssh-keygen -N '' -f $DEFAULT_KEY
KEYPATH=$DEFAULT_KEY
PUBKEY="$( cat ${KEYPATH}.pub )"
fi
if [ -f "$METADATAFILE" ]; then
cp "$METADATAFILE" "$METADATA"
else
LOCALHOSTNAME=autopkgtest
{ echo instance-id: nocloud; echo local-hostname: $LOCALHOSTNAME; } > $METADATA
fi
TZ="Etc/UTC"
[ -e /etc/timezone ] && TZ=$(cat /etc/timezone)
# make VM use the same proxy as the host
if [ -n "$http_proxy" ]; then
EXTRAENV="http_proxy=\"$http_proxy\""
fi
if [ -n "$https_proxy" ]; then
EXTRAENV="$EXTRAENV\nhttps_proxy=\"$https_proxy\""
fi
if [ -n "$no_proxy" ]; then
EXTRAENV="$EXTRAENV\nno_proxy=\"$no_proxy\""
fi
if [ -f "$USERDATAFILE" ]; then
cat $USERDATAFILE > $USERDATA
else
cat > $USERDATA << EOF
#cloud-config
locale: en_US.UTF-8
timezone: $TZ
password: ubuntu
chpasswd: { expire: False }
ssh_pwauth: True
manage_etc_hosts: True
ssh_authorized_keys:
- $PUBKEY
apt_proxy: ${APTPROXY:-}
apt_mirror: $APTURI
apt_sources:
- source: deb $APTURI $RELEASE restricted multiverse
- source: deb-src $APTURI $RELEASE restricted multiverse
- source: deb $APTURI $RELEASE-updates restricted multiverse
- source: deb-src $APTURI $RELEASE-updates restricted multiverse
apt_update: true
apt_upgrade: true
byobu_by_default: system
packages:
- eatmydata
- linux-image-generic
- autopkgtest
- dpkg-dev
- pbuilder
- bzr
- distro-info
- debian-keyring
runcmd:
- [ sh, -c, 'echo "${EXTRAENV}" >> /etc/environment' ]
EOF
fi
SEED=${DISKPATH}.seed
genisoimage -output $SEED -volid cidata -joliet -rock user-data meta-data
# When we are called in Jenkins, add some jitter to avoid lots of VMs starting
# up at the same time
if [ -n "$BUILD_ID" ]; then
SLEEP=$( shuf -i 1-30 -n1)
log_info_msg "Waiting $SLEEP seconds before starting VM"
sleep $SLEEP
fi
# Find free SSH and VNC ports
get_free_port "ssh"
SSHPORT=$?
get_free_port "vnc"
VNCPORT=$?
VNCKVM=$((VNCPORT - 5900))
# Launch in background, -daemonize option causes very slow network IO
log_info_msg "Starting $DISKNAME on port $SSHPORT"
$EATMYDATA $KVMCMD -m $RAMSIZE -smp $VCPU -localtime \
-no-reboot -net user -redir tcp:$SSHPORT::22 \
-drive file=$DISKNAME,if=virtio \
-drive file=$SEED,if=virtio -vnc localhost:$VNCKVM \
-pidfile $DISKPATH.pid -readconfig $VMCFG &
if [ $? -gt 0 ]; then
log_failure_msg "KVM failed to start. Exiting!"
exit 1
fi
wait_vm_startup $SSHPORT
LOOP=0
RET=1
log_info_msg "Waiting for installation to finish"
until [ $RET -eq 0 ]; do
scp $SSHOPTS -P $SSHPORT -i $KEYPATH $BINDIR/testbed/wait_bootfinished ubuntu@localhost:~/
RET=$?
if [ $LOOP -gt 12 ]; then
log_failure_msg "Failed to reach the VM after 120s. Exiting."
RET=1
exit 1
fi
sleep 10
LOOP=$(( LOOP + 1 ))
done
RET=0
ssh $SSHOPTS -i $KEYPATH -p $SSHPORT -l ubuntu localhost /home/ubuntu/wait_bootfinished
RET=$?
if [ $RET -gt 0 ]; then
log_failure_msg "Remote command exited with status $RET. Exiting!"
exit 1
fi
# stop qemu
if [ -f "$DISKPATH.pid" ]; then
log_info_msg "Stopping QEMU"
pid=$(cat $DISKPATH.pid)
kill -9 $pid || true
LOOP=0
while [ -d /proc/$pid ]; do
sleep 0.5
LOOP=$(( LOOP + 1 ))
if [ $LOOP -gt 20 ]; then
log_failure_msg "Failed to kill VM after 10 s. Exiting."
RET=1
exit 1
fi
done
rm -f $DISKPATH.pid
fi
# try to run the new VM to ensure that it actually boots and works
log_info_msg "Verifying that the VM works"
$KVMCMD -m $RAMSIZE -smp $VCPU -localtime \
-no-reboot -net user -redir tcp:$SSHPORT::22 \
-drive file=$DISKNAME,if=virtio,index=0 \
-drive file=$SEED,if=virtio,index=1,readonly \
-vnc localhost:$VNCKVM \
-pidfile $DISKPATH.pid -readconfig $VMCFG &
wait_vm_startup $SSHPORT
timeout=10
while true; do
OUT=$(ssh $SSHOPTS -i $KEYPATH -p $SSHPORT -l ubuntu localhost which adt-run)
if [ "$OUT" = "/usr/bin/adt-run" ]; then
log_info_msg "Succeeded to ssh into VM, and adt-run is installed"
break
fi
sleep 1
timeout=$(( timeout - 1 ))
if [ "$timeout" -lt 0 ]; then
log_failure_msg "Failed to ssh into VM, or adt-run not installed. Exiting!"
RET=1
exit 1
fi
done
# Create link to new test file
LINKNAME=pristine-${RELEASE}-${ARCH}.img
rm -f ${DISKDIR}/${LINKNAME}
ln -s ${DISKPATH} ${DISKDIR}/${LINKNAME}
# Cleanup old images
for DISKFILE in ${DISKDIR}/pristine-${RELEASE}-${ARCH}*.img; do
if [ -f $DISKFILE -a ! -L $DISKFILE -a "$(basename $DISKFILE)" != "$DISKNAME" ]; then
if ! fuser -s $DISKFILE; then
log_info_msg "Removing $DISKFILE"
[ -f "${DISKFILE}.pid" ] && rm ${DISKFILE}.pid
[ -f "${DISKFILE}.seed" ] && rm ${DISKFILE}.seed
rm $DISKFILE
fi
fi
done
log_info_msg "Installation finished"
|