Merge lp:~thomir-deactivatedaccount/adt-cloud-worker/uci-nova-testbed-fixes into lp:adt-cloud-worker

Proposed by Thomi Richards
Status: Superseded
Proposed branch: lp:~thomir-deactivatedaccount/adt-cloud-worker/uci-nova-testbed-fixes
Merge into: lp:adt-cloud-worker
Diff against target: 433 lines (+423/-0) (has conflicts)
2 files modified
README.rst (+30/-0)
uci-nova (+393/-0)
Conflict adding file README.rst.  Moved existing file to README.rst.moved.
To merge this branch: bzr merge lp:~thomir-deactivatedaccount/adt-cloud-worker/uci-nova-testbed-fixes
Reviewer Review Type Date Requested Status
Canonical CI Engineering Pending
Review via email: mp+256357@code.launchpad.net

This proposal has been superseded by a proposal from 2015-04-15.

Commit message

cloud-data improvements.

Description of the change

Include fixes from pitti's vm-setup script. Should improve pass rate.

To post a comment you must log in.
8. By Thomi Richards

Fix typo.

9. By Thomi Richards

Always reboot.

Unmerged revisions

9. By Thomi Richards

Always reboot.

8. By Thomi Richards

Fix typo.

7. By Thomi Richards

Integrate fixes for cloud-init userdata from pitti's autopkgtest vm-setup script.

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]

3. By Paul Larson

Support snappy images by enabling ssh in the cloud-init data for snappy

2. By Celso Providelo

Coping with missing neutron security-groups capabilities (cannonistack and stackystack) and falling back to nova security-groups without egress traffic control.

1. By Celso Providelo

Initial import of the neutron-security-group-based setup.

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-15 16:34:08 +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-15 16:34:08 +0000
40@@ -0,0 +1,393 @@
41+#!/bin/sh
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+# -c console-log | --console=file-name
61+# Save the nova console-log of the server to the specified file.
62+#
63+#
64+# Authors:
65+# Celso Providelo <celso.providelo@canonical.com>
66+#
67+#
68+# autopkgtest is Copyright (C) 2006-2015 Canonical Ltd.
69+#
70+# This program is free software; you can redistribute it and/or modify
71+# it under the terms of the GNU General Public License as published by
72+# the Free Software Foundation; either version 2 of the License, or
73+# (at your option) any later version.
74+#
75+# This program is distributed in the hope that it will be useful,
76+# but WITHOUT ANY WARRANTY; without even the implied warranty of
77+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
78+# GNU General Public License for more details.
79+#
80+# You should have received a copy of the GNU General Public License
81+# along with this program; if not, write to the Free Software
82+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
83+#
84+# See the file CREDITS for a full list of credits information (often
85+# installed as /usr/share/doc/autopkgtest/CREDITS).
86+set -eu
87+
88+CAPABILITIES='isolation-machine,reboot,revert,revert-full-system'
89+
90+SUDO_PASSWORD=''
91+SSH_USER=ubuntu
92+
93+FLAVOR=""
94+IMAGE=""
95+
96+SRVNAME=""
97+NET_ID=""
98+CONSOLE=""
99+DEBUG=""
100+
101+
102+debug() {
103+ [ -z "$DEBUG" ] && return
104+ echo "$@">&2
105+}
106+
107+warning() {
108+ echo "$@">&2
109+}
110+
111+error() {
112+ echo "$@">&2
113+}
114+
115+parse_args() {
116+ # Parse command line argument and populate environment
117+
118+ SHORTOPTS="f:,i:,N:,n:,c:,d"
119+ LONGOPTS="flavor:,image:,net-id:,name:,console:,debug"
120+
121+ TEMP=$(getopt -o $SHORTOPTS --long $LONGOPTS -- "$@")
122+ eval set -- "$TEMP"
123+
124+ while true; do
125+ case "$1" in
126+ -f|--flavor)
127+ FLAVOR=$2
128+ shift 2;;
129+ -i|--image)
130+ IMAGE=$2
131+ shift 2;;
132+ -N|--net-id)
133+ NET_ID="$2"
134+ shift 2;;
135+ -n|--name)
136+ SRVNAME=$2
137+ shift 2;;
138+ -c|--console)
139+ CONSOLE=$2
140+ shift 2;;
141+ -d|--debug)
142+ DEBUG=1; shift;;
143+ --)
144+ shift;
145+ break;;
146+ *)
147+ error "E: $(basename $0): Unsupported option $1"
148+ exit 1;;
149+ esac
150+ done
151+
152+ if [ -z "$FLAVOR" ]; then
153+ error "Argument 'flavor' is mandatory. Run 'nova flavor-list' to "\
154+ "print a list of available flavors."
155+ exit 1
156+ fi
157+ if [ -z "$IMAGE" ]; then
158+ error "Argument 'image' is mandatory. Run 'nova image-list' to "\
159+ "print a list of available images to boot from."
160+ exit 1
161+ fi
162+}
163+
164+security_setup_nova() {
165+ debug "Creating specific nova security-group: $SRVNAME"
166+ nova secgroup-create $SRVNAME "$SRVNAME testbed"
167+
168+ debug "Allowing ingress SSH ..."
169+ nova secgroup-add-rule $SRVNAME tcp 22 22 0.0.0.0/0
170+
171+ debug "Egress traffic will respect external Firewall rules ..."
172+}
173+
174+security_setup_neutron() {
175+ debug "Creating specific neutron security-group: $SRVNAME"
176+ neutron security-group-create $SRVNAME \
177+ --description "$SRVNAME testbed"
178+
179+ debug "Cleaning up default egress rules ..."
180+ DEFAULT_RULES=$(neutron security-group-rule-list \
181+ --format csv -c id -c security_group --quote none \
182+ | grep $SRVNAME | cut -d',' -f1)
183+ for rule_id in $DEFAULT_RULES; do
184+ neutron security-group-rule-delete $rule_id
185+ done
186+
187+ debug "Allowing ingress SSH ..."
188+ neutron security-group-rule-create \
189+ --direction ingress \
190+ --ethertype ipv4 \
191+ --protocol tcp \
192+ --port-range-min 22 \
193+ --port-range-max 22 \
194+ --remote-ip-prefix 0.0.0.0/0 \
195+ $SRVNAME
196+
197+ debug "Allowing DNS egress traffic ..."
198+ neutron security-group-rule-create \
199+ --direction egress \
200+ --remote-ip-prefix 8.8.8.8 \
201+ $SRVNAME
202+
203+ debug "Allowing APT and NTP egress traffic ..."
204+ neutron security-group-rule-create \
205+ --direction egress \
206+ --remote-ip-prefix 91.189.88.0/21 \
207+ $SRVNAME
208+}
209+
210+# create a testbed (if necessary), configure ssh, copy ssh key into it,
211+# configure sudo, etc.; print a list of "key=value" parameters to stdout on
212+# success
213+open() {
214+ # Boot a nova instance and returns its connection parameters
215+ [ -n "$SRVNAME" ] || SRVNAME=`mktemp -u adt-nova-XXXXXX`
216+
217+ mkdir /tmp/$SRVNAME
218+
219+ debug "Creating new SSH key on /tmp/$SRVNAME"
220+ SSH_IDENTITY=/tmp/$SRVNAME/id_rsa
221+ ssh-keygen -f $SSH_IDENTITY -q -N ""
222+
223+ debug "Creating specific nova keypair: $SRVNAME"
224+ nova keypair-add --pub-key $SSH_IDENTITY.pub $SRVNAME
225+
226+ # Setup testbed security with nova or neutron according to their
227+ # availability in the target cloud.
228+ if ! neutron security-group-list; then
229+ security_setup_nova
230+ else
231+ security_setup_neutron
232+ fi
233+
234+ # generate cloud-init user data; mostly for manage_etc_hosts, but also get
235+ # rid of some unnecessary stuff in the VM
236+ #
237+ # NOTE: The "#cloud-config" at the top of this file tells cloud-init that this is
238+ # a config file, as opposed to a shell script (which must start with "#!""). Don't
239+ # Do what I did and think "commented out code! DELETE!" and then wonder why nothing
240+ # works. - Thomi
241+ local userdata=`mktemp`
242+ cat <<EOF > $userdata
243+#cloud-config
244+manage_etc_hosts: true
245+apt_update: true
246+apt_upgrade: false
247+snappy:
248+ ssh_enabled: True
249+
250+runcmd:
251+ # Make apt faster:
252+ - echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/90nolanguages
253+ - echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/autopkgtest
254+ # provides kmods like scsi_debug or mac80211_hwsim
255+ - apt-get install -y linux-generic
256+ # some tests use a lot of /dev/random, avoid hangs
257+ - apt-get install -y haveged
258+ # We need python to run the auxverb helper - install it iff it's missing:
259+ - 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
260+ # Remove some packages we don't need
261+ - 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
262+ - apt-get clean
263+ # Do a dist-upgrade:
264+ - DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade
265+EOF
266+
267+ EXTRA_OPTS=''
268+ if [ -n "$NET_ID" ]; then
269+ EXTRA_OPTS="$EXTRA_OPTS --nic net-id=$NET_ID"
270+ fi
271+
272+ # Boot the instance
273+ debug "Creating nova instance $SRVNAME ..."
274+ OUT=$(nova boot --config-drive=1 \
275+ --flavor $FLAVOR --image $IMAGE --user-data $userdata \
276+ --key_name $SRVNAME --security-groups $SRVNAME \
277+ $EXTRA_OPTS $SRVNAME 2>&1) || {
278+ error "nova boot failed:"
279+ error "$OUT"
280+ exit 1
281+ }
282+ debug "Nova boot succeeded"
283+ rm $userdata
284+
285+ # Find IP address
286+ ipaddr=""
287+ retry=60
288+ while [ -z "$ipaddr" ]; do
289+ OUT=$(nova show --minimal $SRVNAME)
290+ ipaddr=$(echo "$OUT" | awk 'BEGIN {FS="|"} /network/ {n=split($3,i,/,\s*/); gsub(" ", "", i[n]); print i[n]}')
291+ retry=$(( retry - 1 ))
292+ if [ $retry -le 0 ]; then
293+ error "Failed to acquire an IP address. Aborting!"
294+ error "$OUT"
295+ cleanup
296+ exit 1
297+ fi
298+ sleep 3
299+ done
300+ debug "Finding IP address succeeded: $ipaddr"
301+
302+ # purge the device host key so that SSH doesn't print a scary warning
303+ ssh-keygen -f ~/.ssh/known_hosts -R $ipaddr >/dev/null 2>&1 || true
304+
305+ ADT_EXTRA_OPTS="-n $SRVNAME"
306+ if [ -n "$CONSOLE" ]; then
307+ ADT_EXTRA_OPTS="$ADT_EXTRA_OPTS -c $CONSOLE"
308+ fi
309+
310+ # Return access information to adt-virt-ssh.
311+ cat<<EOF
312+identity=$SSH_IDENTITY
313+login=$SSH_USER
314+hostname=$ipaddr
315+capabilities=$CAPABILITIES
316+extraopts=$ADT_EXTRA_OPTS
317+EOF
318+ if [ -n "$SUDO_PASSWORD" ]; then
319+ echo "password=$SUDO_PASSWORD"
320+ fi
321+
322+ # wait until ssh is available and cloud-config is done
323+ debug "Waiting until ssh becomes available"
324+ SSH="ssh -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i /tmp/$SRVNAME/id_rsa -l $SSH_USER $ipaddr"
325+ retry=60
326+ while ! $SSH true; do
327+ retry=$(( retry - 1 ))
328+ if [ $retry -le 0 ]; then
329+ error "Timed out waiting for ssh. Aborting!"
330+ cleanup
331+ exit 1
332+ fi
333+ sleep 5
334+ done
335+
336+ debug "Waiting for cloud-init to finish"
337+ if ! timeout 30m $SSH 'while [ ! -e /var/lib/cloud/instance/boot-finished ]; do sleep 1; done'; then
338+ error "Timed out waiting for cloud-init to finish. Aborting!"
339+ cleanup
340+ exit 1
341+ fi
342+
343+}
344+
345+cleanup() {
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+ debug "Deleting $SRVNAME instance"
357+ nova delete $SRVNAME || true
358+
359+ debug "Deleting $SRVNAME keypair"
360+ nova keypair-delete $SRVNAME || true
361+
362+ debug "Deleting /tmp/$SRVNAME SSH keys"
363+ rm -rf /tmp/$SRVNAME || true
364+
365+ DELETE_CMD="neutron security-group-delete $SRVNAME"
366+ if ! neutron security-group-list; then
367+ DELETE_CMD="nova secgroup-delete $SRVNAME"
368+ fi
369+
370+ debug "Deleting $SRVNAME security-group"
371+ retry=3
372+ while ! eval "$DELETE_CMD"; do
373+ retry=$(( retry - 1 ))
374+ if [ $retry -le 0 ]; then
375+ error "Timed out deleting secgroup. Aborting!"
376+ cleanup
377+ exit 1
378+ fi
379+ sleep 5
380+ done
381+
382+ SRVNAME=""
383+}
384+
385+revert() {
386+ if [ -z "$SRVNAME" ]; then
387+ echo "Needs to be called with -n <server name>" >&2
388+ exit 1
389+ fi
390+ cleanup
391+ open
392+}
393+
394+reboot() {
395+ if [ -z "$SRVNAME" ]; then
396+ error "Cannot determine server name. Instance won't be rebooted!"
397+ exit 1
398+ fi
399+
400+ nova reboot --poll $SRVNAME >/dev/null 2>&1||true
401+}
402+
403+# ########################################
404+# Main procedure
405+#
406+if [ $# -eq 0 ]; then
407+ error "Invalid number of arguments, command is missing"
408+ exit 1
409+fi
410+cmd=$(echo $1|tr [[:upper:]] [[:lower:]])
411+shift
412+parse_args "$@"
413+
414+# Don't leave stuff behind ...
415+trap "cleanup" 1 2 6 15
416+
417+case $cmd in
418+ open)
419+ open;;
420+ cleanup)
421+ cleanup;;
422+ revert)
423+ revert;;
424+ reboot)
425+ reboot;;
426+ '')
427+ echo "Needs to be called with command as first argument" >&2
428+ exit 1
429+ ;;
430+ *)
431+ echo "invalid command $cmd" >&2
432+ exit 1
433+esac

Subscribers

People subscribed via source and target branches

to all changes: