Merge lp:~thomir-deactivatedaccount/core-image-tester/trunk-local-ssh-setup into lp:core-image-tester

Proposed by Thomi Richards
Status: Merged
Approved by: Thomi Richards
Approved revision: 32
Merged at revision: 32
Proposed branch: lp:~thomir-deactivatedaccount/core-image-tester/trunk-local-ssh-setup
Merge into: lp:core-image-tester
Diff against target: 464 lines (+405/-8)
3 files modified
README.rst (+16/-7)
core_image_tester/worker.py (+13/-1)
uci-nova (+376/-0)
To merge this branch: bzr merge lp:~thomir-deactivatedaccount/core-image-tester/trunk-local-ssh-setup
Reviewer Review Type Date Requested Status
Francis Ginther Approve
Review via email: mp+257582@code.launchpad.net

Commit message

Make uci-nova inline.

Description of the change

This branch copies uci-nova from revno 6 of the uci-nova branch. I'll follow this with another branch that removes some of the options that aren't applicable to the core-image-tester.

To post a comment you must log in.
Francis Ginther (fginther) wrote :

Approve

review: Approve

Preview Diff

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

Subscribers

People subscribed via source and target branches

to all changes: