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 on 2015-04-27

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

12. By Francis Ginther on 2015-04-23

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

11. By Francis Ginther on 2015-04-23

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

10. By Francis Ginther on 2015-04-22

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

9. By Celso Providelo on 2015-04-22

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

8. By Evan on 2015-04-17

Suppressing noisy output. [r=Celso Providelo]

7. By Thomi Richards on 2015-04-15

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

6. By Celso Providelo on 2015-04-06

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 on 2015-04-02

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

4. By Francis Ginther on 2015-04-02

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

Subscribers

People subscribed via source and target branches