Merge lp:~fginther/adt-cloud-worker/uci-nova-swapfile into lp:adt-cloud-worker

Proposed by Francis Ginther
Status: Superseded
Proposed branch: lp:~fginther/adt-cloud-worker/uci-nova-swapfile
Merge into: lp:adt-cloud-worker
Diff against target: 476 lines (+466/-0) (has conflicts)
2 files modified
README.rst (+30/-0)
uci-nova (+436/-0)
Conflict adding file README.rst.  Moved existing file to README.rst.moved.
To merge this branch: bzr merge lp:~fginther/adt-cloud-worker/uci-nova-swapfile
Reviewer Review Type Date Requested Status
Canonical CI Engineering Pending
Review via email: mp+257560@code.launchpad.net

Commit message

Add a 4GB swapfile as a way to extend available RAM.

Description of the change

Add a 4GB swapfile as a way to extend available RAM.

To post a comment you must log in.

Unmerged revisions

13. By Francis Ginther

Add a 4GB swapfile as a way to extend available RAM.

12. By Francis Ginther

Add 'apt-get update' to the cloud-config script after modifying the sources list. [r=Celso Providelo]

11. By Francis Ginther

Add support for an optional archive mirror and add multiverse if it's specified. [r=Celso Providelo]

10. By Francis Ginther

Wait for nova instance to become ACTIVE before searching for ipaddr. [r=Celso Providelo]

9. By Celso Providelo

Fixing broken stderr/stdout redirect notation. [r=Paul Larson, Francis Ginther]

8. By Evan

Suppressing noisy output. [r=Celso Providelo]

7. By Thomi Richards

Include fixes from pitti's vm-setup script. Should improve pass rate. [r=Thomi Richards, Joe Talbott]

6. By Celso Providelo

Return console-logging cmd-line option back to adt on open(), so it can be properly used when calling cleanup() later on. [r=Francis Ginther]

5. By Francis Ginther

Add missing ':' for console in long opts list. [r=Thomi Richards]

4. By Francis Ginther

Add support for saving the nova console-log to the specified location. Also increase the number of ipaddr retries as spawning can be a bit slow. [r=Thomi Richards, Paul Larson]

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'README.rst'
--- README.rst 1970-01-01 00:00:00 +0000
+++ README.rst 2015-04-27 18:21:22 +0000
@@ -0,0 +1,30 @@
1Ubuntu CI nova ssh setup for autopkgtest
2########################################
3
4
5This branch contains a custom autopkgtest ssh-setup script for creating
6nova testbeds with the same network restrictions applied on the Ubuntu
7adt-cloud production system.
8
9It basically restricts ingress network traffic to SSH (tcp:22) using an
10ephemeral keypair created exclusively for the current test. Egress network
11traffic is restricted to DNS (on 8.8.8.8) and APT and NTP (from
12nova.clouds.archive.ubuntu.com and ntp.ubuntu.com respectively, as required
13for the stock ubuntu cloud-images).
14
15Getting the code::
16
17 $ bzr branch lp:~canonical-ci-engineering/adt-cloud-worker/uci-nova-ssh-setup
18
19Enabling it involves copying 'uci-nova' to the default autopkgtest ssh-setup
20location::
21
22 $ sudo cp uci-nova-ssh-setup/uci-nova /usr/share/autopkgtest/ssh-setup/
23
24A example usage command-line is::
25
26 $ adt-run libpng -d \
27 --- ssh -s uci-nova -d \
28 -- -d \
29 --flavor m1.small \
30 --image ubuntu-trusty-14.04-amd64-server-20150305-disk1.img
031
=== renamed file 'README.rst' => 'README.rst.moved'
=== added file 'uci-nova'
--- uci-nova 1970-01-01 00:00:00 +0000
+++ uci-nova 2015-04-27 18:21:22 +0000
@@ -0,0 +1,436 @@
1#!/bin/sh -eu
2#
3# This script is part of autopkgtest
4# autopkgtest is a tool for testing Debian binary packages
5#
6# This script sets up a nova instance to use as an autopkgtest testbed. It
7# assumes that the host system is already prepared to run nova commands.
8# WARNING: This is mostly a proof of concept and not very robust.
9
10# Options:
11#
12# -f flavor | --flavor=flavor
13# Name or ID of flavor (see 'nova flavor-list'), mandatory
14# -i image | --image=image
15# Name or ID of image (see 'nova image-list'), mandatory
16# -N net-id | --net-id=net-id
17# UUID of the network that should be used for the instance
18# -n name | --name=name
19# Name for the new server. A name will be generated if not specified.
20# -m mirror | --mirror=mirror
21# Optional ubuntu archive mirror to override the default.
22# -c console-log | --console=file-name
23# Save the nova console-log of the server to the specified file.
24#
25#
26# Authors:
27# Celso Providelo <celso.providelo@canonical.com>
28#
29#
30# autopkgtest is Copyright (C) 2006-2015 Canonical Ltd.
31#
32# This program is free software; you can redistribute it and/or modify
33# it under the terms of the GNU General Public License as published by
34# the Free Software Foundation; either version 2 of the License, or
35# (at your option) any later version.
36#
37# This program is distributed in the hope that it will be useful,
38# but WITHOUT ANY WARRANTY; without even the implied warranty of
39# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40# GNU General Public License for more details.
41#
42# You should have received a copy of the GNU General Public License
43# along with this program; if not, write to the Free Software
44# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
45#
46# See the file CREDITS for a full list of credits information (often
47# installed as /usr/share/doc/autopkgtest/CREDITS).
48set -eu
49
50CAPABILITIES='isolation-machine,reboot,revert,revert-full-system'
51
52SUDO_PASSWORD=''
53SSH_USER=ubuntu
54
55FLAVOR=""
56IMAGE=""
57
58SRVNAME=""
59NET_ID=""
60CONSOLE=""
61MIRROR=""
62DEBUG=""
63
64
65debug() {
66 [ -z "$DEBUG" ] && return
67 echo "$@">&2
68}
69
70warning() {
71 echo "$@">&2
72}
73
74error() {
75 echo "$@">&2
76}
77
78parse_args() {
79 # Parse command line argument and populate environment
80
81 SHORTOPTS="f:,i:,N:,n:,m:,c:,d"
82 LONGOPTS="flavor:,image:,net-id:,name:,mirror:,console:,debug"
83
84 TEMP=$(getopt -o $SHORTOPTS --long $LONGOPTS -- "$@")
85 eval set -- "$TEMP"
86
87 while true; do
88 case "$1" in
89 -f|--flavor)
90 FLAVOR=$2
91 shift 2;;
92 -i|--image)
93 IMAGE=$2
94 shift 2;;
95 -N|--net-id)
96 NET_ID="$2"
97 shift 2;;
98 -n|--name)
99 SRVNAME=$2
100 shift 2;;
101 -m|--mirror)
102 MIRROR=$2
103 shift 2;;
104 -c|--console)
105 CONSOLE=$2
106 shift 2;;
107 -d|--debug)
108 DEBUG=1; shift;;
109 --)
110 shift;
111 break;;
112 *)
113 error "E: $(basename $0): Unsupported option $1"
114 exit 1;;
115 esac
116 done
117
118 if [ -z "$FLAVOR" ]; then
119 error "Argument 'flavor' is mandatory. Run 'nova flavor-list' to "\
120 "print a list of available flavors."
121 exit 1
122 fi
123 if [ -z "$IMAGE" ]; then
124 error "Argument 'image' is mandatory. Run 'nova image-list' to "\
125 "print a list of available images to boot from."
126 exit 1
127 fi
128}
129
130security_setup_nova() {
131 if ! nova secgroup-create $SRVNAME "$SRVNAME testbed" >/dev/null 2>&1; then
132 debug "Could not create specific nova security-group: $SRVNAME"
133 exit 1
134 fi
135
136 if ! nova secgroup-add-rule $SRVNAME tcp 22 22 0.0.0.0/0 >/dev/null 2>&1; then
137 debug "Could not allow ingress SSH."
138 exit 1
139 fi
140}
141
142security_setup_neutron() {
143 debug "Creating specific neutron security-group: $SRVNAME"
144 neutron security-group-create $SRVNAME \
145 --description "$SRVNAME testbed"
146
147 debug "Cleaning up default egress rules ..."
148 DEFAULT_RULES=$(neutron security-group-rule-list \
149 --format csv -c id -c security_group --quote none \
150 | grep $SRVNAME | cut -d',' -f1)
151 for rule_id in $DEFAULT_RULES; do
152 neutron security-group-rule-delete $rule_id
153 done
154
155 debug "Allowing ingress SSH ..."
156 neutron security-group-rule-create \
157 --direction ingress \
158 --ethertype ipv4 \
159 --protocol tcp \
160 --port-range-min 22 \
161 --port-range-max 22 \
162 --remote-ip-prefix 0.0.0.0/0 \
163 $SRVNAME
164
165 debug "Allowing DNS egress traffic ..."
166 neutron security-group-rule-create \
167 --direction egress \
168 --remote-ip-prefix 8.8.8.8 \
169 $SRVNAME
170
171 debug "Allowing APT and NTP egress traffic ..."
172 neutron security-group-rule-create \
173 --direction egress \
174 --remote-ip-prefix 91.189.88.0/21 \
175 $SRVNAME
176}
177
178# create a testbed (if necessary), configure ssh, copy ssh key into it,
179# configure sudo, etc.; print a list of "key=value" parameters to stdout on
180# success
181open() {
182 # Boot a nova instance and returns its connection parameters
183 [ -n "$SRVNAME" ] || SRVNAME=`mktemp -u adt-nova-XXXXXX`
184
185 mkdir /tmp/$SRVNAME
186
187 debug "Creating new SSH key on /tmp/$SRVNAME"
188 SSH_IDENTITY=/tmp/$SRVNAME/id_rsa
189 ssh-keygen -f $SSH_IDENTITY -q -N ""
190
191 debug "Creating specific nova keypair: $SRVNAME"
192 nova keypair-add --pub-key $SSH_IDENTITY.pub $SRVNAME
193
194 # Setup testbed security with nova or neutron according to their
195 # availability in the target cloud.
196 if type neutron >/dev/null && neutron security-group-list >/dev/null; then
197 debug "Setting up neutron secgroup"
198 security_setup_neutron
199 else
200 security_setup_nova
201 fi
202
203 # Generate a new apt sources.list using either the specified mirror
204 # or the test client's currently configured archive mirror. This includes
205 # adding multiverse which is not enabled in our cloud images by default.
206 #
207 # The following archive_setup uses an awk command to determine the release
208 # and archive mirror configured on the test client. $2 will be set to the
209 # archive mirror and $3 to the release. These are used before overwriting
210 # the test client's /etc/apt/sources.list file.
211 if [ -n "${MIRROR}" ]; then
212 archive_setup="mirror=${MIRROR}; release=\`awk '/^deb .*(debian|ubuntu)/ { print \$3; exit }' \"\$root/etc/apt/sources.list\"\`; echo \"deb \${mirror} \${release} main restricted universe multiverse\ndeb \${mirror} \${release}-updates main restricted universe multiverse\ndeb-src \${mirror} \${release} main restricted universe multiverse\ndeb-src \${mirror} \${release}-updates main restricted universe multiverse\" > /etc/apt/sources.list"
213 else
214 archive_setup="mirror_release=\`awk '/^deb .*(debian|ubuntu)/ { print \$2,\$3; exit }' \"\$root/etc/apt/sources.list\"\`; echo \"deb \${mirror_release} main restricted universe multiverse\ndeb \${mirror_release}-updates main restricted universe multiverse\ndeb-src \${mirror_release} main restricted universe multiverse\ndeb-src \${mirror_release}-updates main restricted universe multiverse\" > /etc/apt/sources.list"
215 fi
216
217 # Add a swapfile to extend the amount of RAM available to process. As this
218 # is a local file on the boot disk, it does not need any support within the
219 # nova flavor. This does consume space on the boot disk and is not ideal
220 # from the performance perspective.
221 swap_setup="fallocate -l 4G /swapfile; chmod 600 /swapfile; mkswap /swapfile; swapon /swapfile; echo \"/swapfile none swap sw 0 0\" >> /etc/fstab"
222
223 # generate cloud-init user data; mostly for manage_etc_hosts, but also get
224 # rid of some unnecessary stuff in the VM
225 #
226 # NOTE: The "#cloud-config" at the top of this file tells cloud-init that this is
227 # a config file, as opposed to a shell script (which must start with "#!"). Don't
228 # Do what I did and think "commented out code! DELETE!" and then wonder why nothing
229 # works. - Thomi
230 local userdata=`mktemp`
231 cat <<EOF > $userdata
232#cloud-config
233manage_etc_hosts: true
234apt_update: true
235apt_upgrade: false
236snappy:
237 ssh_enabled: True
238
239runcmd:
240 # Setup archive and do an update after changing the apt sources
241 - ${archive_setup}
242 - apt-get update
243 # Add a swapfile
244 - ${swap_setup}
245 # Make apt faster:
246 - echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/90nolanguages
247 - echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/autopkgtest
248 # provides kmods like scsi_debug or mac80211_hwsim
249 - apt-get install -y linux-generic
250 # some tests use a lot of /dev/random, avoid hangs
251 - apt-get install -y haveged
252 # We need python to run the auxverb helper - install it iff it's missing:
253 - if ! sh -c 'type python3 >/dev/null 2>&1 || type python >/dev/null 2>&1'; then apt-get install -y --no-install-recommends python3-minimal; fi
254 # Remove some packages we don't need
255 - for p in accountsservice apt-xapian-index cryptsetup landscape-client landscape-common open-vm-tools w3m vim-runtime aptitude-common command-not-found-data manpages ntfs-3g sosreport ubuntu-release-upgrader-core; do apt-get --auto-remove -y purge \$p || true; done
256 - apt-get clean
257 # Do a dist-upgrade:
258 - DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade
259EOF
260
261 EXTRA_OPTS=''
262 if [ -n "$NET_ID" ]; then
263 EXTRA_OPTS="$EXTRA_OPTS --nic net-id=$NET_ID"
264 fi
265
266 # Boot the instance
267 debug "Creating nova instance $SRVNAME ..."
268 OUT=$(nova boot --config-drive=1 \
269 --flavor $FLAVOR --image $IMAGE --user-data $userdata \
270 --key_name $SRVNAME --security-groups $SRVNAME \
271 $EXTRA_OPTS $SRVNAME 2>&1) || {
272 error "nova boot failed:"
273 error "$OUT"
274 exit 1
275 }
276 debug "Nova boot succeeded"
277 rm $userdata
278
279 # Find IP address
280 ipaddr=""
281 retry=60
282 while [ -z "$ipaddr" ]; do
283 OUT=$(nova show --minimal $SRVNAME)
284 # Check that the instance is in the 'ACTIVE' state before probing for
285 # the IP address to avoid awk'ing the wrong field
286 if $(echo "$OUT" | grep -q "ACTIVE"); then
287 ipaddr=$(echo "$OUT" | awk 'BEGIN {FS="|"} /network/ {n=split($3,i,/,\s*/); gsub(" ", "", i[n]); print i[n]}')
288 fi
289 retry=$(( retry - 1 ))
290 if [ $retry -le 0 ]; then
291 error "Failed to acquire an IP address. Aborting!"
292 error "$OUT"
293 cleanup
294 exit 1
295 fi
296 sleep 3
297 done
298 debug "Finding IP address succeeded: $ipaddr"
299
300 # purge the device host key so that SSH doesn't print a scary warning
301 ssh-keygen -f ~/.ssh/known_hosts -R $ipaddr >/dev/null 2>&1 || true
302
303 ADT_EXTRA_OPTS="-n $SRVNAME"
304 if [ -n "$CONSOLE" ]; then
305 ADT_EXTRA_OPTS="$ADT_EXTRA_OPTS -c $CONSOLE"
306 fi
307
308 # Return access information to adt-virt-ssh.
309 cat<<EOF
310identity=$SSH_IDENTITY
311login=$SSH_USER
312hostname=$ipaddr
313capabilities=$CAPABILITIES
314extraopts=$ADT_EXTRA_OPTS
315EOF
316 if [ -n "$SUDO_PASSWORD" ]; then
317 echo "password=$SUDO_PASSWORD"
318 fi
319
320 # wait until ssh is available and cloud-config is done
321 debug "Waiting until ssh becomes available"
322 SSH="ssh -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i /tmp/$SRVNAME/id_rsa -l $SSH_USER $ipaddr"
323 retry=60
324 while ! $SSH true; do
325 retry=$(( retry - 1 ))
326 if [ $retry -le 0 ]; then
327 error "Timed out waiting for ssh. Aborting!"
328 cleanup
329 exit 1
330 fi
331 sleep 5
332 done
333
334 debug "Waiting for cloud-init to finish"
335 if ! timeout 30m $SSH 'while [ ! -e /var/lib/cloud/instance/boot-finished ]; do sleep 1; done'; then
336 error "Timed out waiting for cloud-init to finish. Aborting!"
337 cleanup
338 exit 1
339 fi
340
341 reboot
342
343}
344
345cleanup() {
346 if [ -z "$SRVNAME" ]; then
347 error "Cannot determine server name. Instance won't be deleted!"
348 exit 0
349 fi
350
351 if [ -n "$CONSOLE" ]; then
352 debug "Saving console-log for $SRVNAME"
353 nova console-log $SRVNAME > $CONSOLE
354 fi
355
356 if ! nova delete $SRVNAME >/dev/null 2>&1; then
357 error "Could not delete $SRVNAME instance."
358 fi
359
360 if ! nova keypair-delete $SRVNAME >/dev/null 2>&1; then
361 error "Could not delete $SRVNAME keypair."
362 fi
363
364 if ! rm -rf /tmp/$SRVNAME >/dev/null 2>&1; then
365 debug "Could not delete /tmp/$SRVNAME SSH keys"
366 fi
367
368 DELETE_CMD="neutron security-group-delete $SRVNAME"
369 if ! type neutron >/dev/null || ! neutron security-group-list >/dev/null; then
370 DELETE_CMD="nova secgroup-delete $SRVNAME"
371 fi
372
373 debug "Deleting $SRVNAME security-group"
374 retry=3
375 while ! eval "$DELETE_CMD"; do
376 retry=$(( retry - 1 ))
377 if [ $retry -le 0 ]; then
378 error "Timed out deleting secgroup. Aborting!"
379 cleanup
380 exit 1
381 fi
382 sleep 5
383 done
384
385 SRVNAME=""
386}
387
388revert() {
389 if [ -z "$SRVNAME" ]; then
390 echo "Needs to be called with -n <server name>" >&2
391 exit 1
392 fi
393 cleanup
394 open
395}
396
397reboot() {
398 if [ -z "$SRVNAME" ]; then
399 error "Cannot determine server name. Instance won't be rebooted!"
400 exit 1
401 fi
402
403 nova reboot --poll $SRVNAME >/dev/null 2>&1||true
404}
405
406# ########################################
407# Main procedure
408#
409if [ $# -eq 0 ]; then
410 error "Invalid number of arguments, command is missing"
411 exit 1
412fi
413cmd=$(echo $1|tr [[:upper:]] [[:lower:]])
414shift
415parse_args "$@"
416
417# Don't leave stuff behind ...
418trap "cleanup" 1 2 6 15
419
420case $cmd in
421 open)
422 open;;
423 cleanup)
424 cleanup;;
425 revert)
426 revert;;
427 reboot)
428 reboot;;
429 '')
430 echo "Needs to be called with command as first argument" >&2
431 exit 1
432 ;;
433 *)
434 echo "invalid command $cmd" >&2
435 exit 1
436esac

Subscribers

People subscribed via source and target branches