9389.6.6
by Michael Hudson
move main to its own file, add standard header to all new files |
1 |
# Copyright 2009 Canonical Ltd. This software is licensed under the
|
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
|
3 |
||
9389.6.9
by Michael Hudson
docstrings, __all__s and a little whitespace |
4 |
"""Code to represent a single machine instance in EC2."""
|
9389.6.6
by Michael Hudson
move main to its own file, add standard header to all new files |
5 |
|
6 |
__metaclass__ = type |
|
9389.6.9
by Michael Hudson
docstrings, __all__s and a little whitespace |
7 |
__all__ = [ |
8 |
'EC2Instance', |
|
9 |
]
|
|
9389.6.6
by Michael Hudson
move main to its own file, add standard header to all new files |
10 |
|
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
11 |
import code |
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
12 |
import glob |
9397.1.8
by Michael Hudson
no plan survives contact etc |
13 |
import os |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
14 |
import select |
15 |
import socket |
|
16 |
import subprocess |
|
17 |
import sys |
|
18 |
import time |
|
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
19 |
import traceback |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
20 |
|
9453.2.22
by Michael Hudson
kill error_and_quit |
21 |
from bzrlib.errors import BzrCommandError |
11962.1.2
by Gavin Panella
Format imports. |
22 |
from devscripts.ec2test.session import EC2SessionName |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
23 |
import paramiko |
24 |
||
9453.2.30
by Michael Hudson
start responding to review |
25 |
|
9397.2.25
by Michael Hudson
act on comments from jml |
26 |
DEFAULT_INSTANCE_TYPE = 'c1.xlarge' |
27 |
AVAILABLE_INSTANCE_TYPES = ('m1.large', 'm1.xlarge', 'c1.xlarge') |
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
28 |
|
9636.1.1
by Jonathan Lange
Clean up PEP 8 violations. |
29 |
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
30 |
class AcceptAllPolicy: |
31 |
"""We accept all unknown host key."""
|
|
32 |
||
33 |
def missing_host_key(self, client, hostname, key): |
|
9636.1.1
by Jonathan Lange
Clean up PEP 8 violations. |
34 |
# Normally the console output is supposed to contain the Host key but
|
35 |
# it doesn't seem to be the case here, so we trust that the host we
|
|
36 |
# are connecting to is the correct one.
|
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
37 |
pass
|
38 |
||
39 |
||
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
40 |
def get_user_key(): |
10835.1.3
by Maris Fogels
Small tweak to the module variables and docstring. |
41 |
"""Get a SSH key from the agent. Raise an error if no keys were found.
|
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
42 |
|
43 |
This key will be used to let the user log in (as $USER) to the instance.
|
|
44 |
"""
|
|
45 |
agent = paramiko.Agent() |
|
46 |
keys = agent.get_keys() |
|
47 |
if len(keys) == 0: |
|
9453.2.22
by Michael Hudson
kill error_and_quit |
48 |
raise BzrCommandError( |
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
49 |
'You must have an ssh agent running with keys installed that '
|
10835.1.4
by Maris Fogels
Updated an old error message. |
50 |
'will allow the script to access Launchpad and get your '
|
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
51 |
'branch.\n') |
10835.1.1
by Maris Fogels
Added an XXX for bug 577118 in ec2test. |
52 |
|
10835.1.3
by Maris Fogels
Small tweak to the module variables and docstring. |
53 |
# XXX mars 2010-05-07 bug=577118
|
10835.1.1
by Maris Fogels
Added an XXX for bug 577118 in ec2test. |
54 |
# Popping the first key off of the stack can create problems if the person
|
55 |
# has more than one key in their ssh-agent, but alas, we have no good way
|
|
10835.1.2
by Maris Fogels
Added a bit more to the comment. |
56 |
# to detect the right key to use. See bug 577118 for a workaround.
|
10835.1.3
by Maris Fogels
Small tweak to the module variables and docstring. |
57 |
return keys[0] |
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
58 |
|
59 |
||
9550.13.40
by Michael Hudson
self-review-ish comments |
60 |
# Commands to run to turn a blank image into one usable for the rest of the
|
61 |
# ec2 functionality. They come in two parts, one set that need to be run as
|
|
62 |
# root and another that should be run as the 'ec2test' user.
|
|
11699.1.1
by Gary Poster
make update-image work without hiccups on lucid. |
63 |
# Note that the sources from http://us.ec2.archive.ubuntu.com/ubuntu/ are per
|
64 |
# instructions described in http://is.gd/g1MIT . When we switch to
|
|
65 |
# Eucalyptus, we can dump this.
|
|
9550.13.40
by Michael Hudson
self-review-ish comments |
66 |
|
67 |
from_scratch_root = """ |
|
9550.13.44
by Michael Hudson
review inspired comments |
68 |
# From 'help set':
|
69 |
# -x Print commands and their arguments as they are executed.
|
|
70 |
# -e Exit immediately if a command exits with a non-zero status.
|
|
9550.13.30
by Michael Hudson
this might possibly work now |
71 |
set -xe
|
72 |
||
73 |
sed -ie 's/main universe/main universe multiverse/' /etc/apt/sources.list
|
|
74 |
||
75 |
. /etc/lsb-release
|
|
76 |
||
77 |
cat >> /etc/apt/sources.list << EOF
|
|
78 |
deb http://ppa.launchpad.net/launchpad/ubuntu $DISTRIB_CODENAME main
|
|
79 |
deb http://ppa.launchpad.net/bzr/ubuntu $DISTRIB_CODENAME main
|
|
80 |
deb http://ppa.launchpad.net/bzr-beta-ppa/ubuntu $DISTRIB_CODENAME main
|
|
11699.1.1
by Gary Poster
make update-image work without hiccups on lucid. |
81 |
deb http://us.ec2.archive.ubuntu.com/ubuntu/ $DISTRIB_CODENAME multiverse
|
82 |
deb-src http://us.ec2.archive.ubuntu.com/ubuntu/ $DISTRIB_CODENAME main
|
|
9550.13.30
by Michael Hudson
this might possibly work now |
83 |
EOF
|
84 |
||
9550.13.40
by Michael Hudson
self-review-ish comments |
85 |
# This next part is cribbed from rocketfuel-setup
|
9550.13.30
by Michael Hudson
this might possibly work now |
86 |
dev_host() {
|
9550.13.43
by Michael Hudson
oops! |
87 |
sed -i \"s/^127.0.0.88.*$/&\ ${hostname}/\" /etc/hosts |
9550.13.30
by Michael Hudson
this might possibly work now |
88 |
}
|
89 |
||
90 |
echo 'Adding development hosts on local machine'
|
|
91 |
echo '
|
|
92 |
# Launchpad virtual domains. This should be on one line.
|
|
93 |
127.0.0.88 launchpad.dev
|
|
94 |
' >> /etc/hosts
|
|
95 |
||
96 |
declare -a hostnames
|
|
97 |
hostnames=$(cat <<EOF
|
|
98 |
answers.launchpad.dev
|
|
99 |
api.launchpad.dev
|
|
100 |
bazaar-internal.launchpad.dev
|
|
101 |
beta.launchpad.dev
|
|
102 |
blueprints.launchpad.dev
|
|
103 |
bugs.launchpad.dev
|
|
104 |
code.launchpad.dev
|
|
105 |
feeds.launchpad.dev
|
|
106 |
id.launchpad.dev
|
|
107 |
keyserver.launchpad.dev
|
|
108 |
lists.launchpad.dev
|
|
109 |
openid.launchpad.dev
|
|
110 |
ppa.launchpad.dev
|
|
111 |
private-ppa.launchpad.dev
|
|
112 |
shipit.edubuntu.dev
|
|
113 |
shipit.kubuntu.dev
|
|
114 |
shipit.ubuntu.dev
|
|
10212.7.16
by Guilherme Salgado
Rename testopenid.launchpad.dev to testopenid.dev on lib/devscripts/ec2test/instance.py |
115 |
testopenid.dev
|
9550.13.30
by Michael Hudson
this might possibly work now |
116 |
translations.launchpad.dev
|
117 |
xmlrpc-private.launchpad.dev
|
|
118 |
xmlrpc.launchpad.dev
|
|
119 |
EOF
|
|
120 |
)
|
|
121 |
||
122 |
for hostname in $hostnames; do
|
|
123 |
dev_host;
|
|
124 |
done
|
|
125 |
||
126 |
echo '
|
|
127 |
127.0.0.99 bazaar.launchpad.dev
|
|
128 |
' >> /etc/hosts
|
|
129 |
||
9550.13.40
by Michael Hudson
self-review-ish comments |
130 |
# Add the keys for the three PPAs added to sources.list above.
|
9550.13.30
by Michael Hudson
this might possibly work now |
131 |
apt-key adv --recv-keys --keyserver pool.sks-keyservers.net 2af499cb24ac5f65461405572d1ffb6c0a5174af
|
132 |
apt-key adv --recv-keys --keyserver pool.sks-keyservers.net ece2800bacf028b31ee3657cd702bf6b8c6c1efd
|
|
133 |
apt-key adv --recv-keys --keyserver pool.sks-keyservers.net cbede690576d1e4e813f6bb3ebaf723d37b19b80
|
|
134 |
||
135 |
aptitude update
|
|
136 |
aptitude -y full-upgrade
|
|
137 |
||
10271.3.2
by Michael Hudson
fixes |
138 |
DEBIAN_FRONTEND=noninteractive apt-get -y install launchpad-developer-dependencies apache2 apache2-mpm-worker
|
9550.13.30
by Michael Hudson
this might possibly work now |
139 |
|
10287.1.1
by Guilherme Salgado
Make sure ec2 instances are shut down 8h after they start running the test suite |
140 |
# Create the ec2test user, give them passwordless sudo.
|
9550.13.30
by Michael Hudson
this might possibly work now |
141 |
adduser --gecos "" --disabled-password ec2test
|
142 |
echo 'ec2test\tALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers |
|
143 |
||
9550.13.32
by Michael Hudson
create ~ec2test/.ssh before putting stuff in it |
144 |
mkdir /home/ec2test/.ssh
|
9550.13.30
by Michael Hudson
this might possibly work now |
145 |
cat > /home/ec2test/.ssh/config << EOF
|
146 |
CheckHostIP no
|
|
147 |
StrictHostKeyChecking no
|
|
148 |
EOF
|
|
149 |
||
9550.13.32
by Michael Hudson
create ~ec2test/.ssh before putting stuff in it |
150 |
mkdir /var/launchpad
|
151 |
chown -R ec2test:ec2test /var/www /var/launchpad /home/ec2test/
|
|
9550.13.35
by Michael Hudson
really run as ec2test user |
152 |
"""
|
153 |
||
154 |
||
9550.13.40
by Michael Hudson
self-review-ish comments |
155 |
from_scratch_ec2test = """ |
9550.13.44
by Michael Hudson
review inspired comments |
156 |
# From 'help set':
|
157 |
# -x Print commands and their arguments as they are executed.
|
|
158 |
# -e Exit immediately if a command exits with a non-zero status.
|
|
9550.13.36
by Michael Hudson
set -xe for these commands too |
159 |
set -xe
|
160 |
||
9550.13.35
by Michael Hudson
really run as ec2test user |
161 |
bzr launchpad-login %(launchpad-login)s
|
162 |
bzr init-repo --2a /var/launchpad
|
|
9550.13.50
by Michael Hudson
pre-landing update to refer to proper trunk branches |
163 |
bzr branch lp:~launchpad-pqm/launchpad/devel /var/launchpad/test
|
9550.13.35
by Michael Hudson
really run as ec2test user |
164 |
bzr branch --standalone lp:lp-source-dependencies /var/launchpad/download-cache
|
165 |
mkdir /var/launchpad/sourcecode
|
|
166 |
/var/launchpad/test/utilities/update-sourcecode /var/launchpad/sourcecode
|
|
9550.13.30
by Michael Hudson
this might possibly work now |
167 |
"""
|
168 |
||
169 |
||
9639.1.1
by Gavin Panella
Revert revision 9636, which itself was a reversion of an earlier revision. |
170 |
postmortem_banner = """\ |
171 |
Postmortem Console. EC2 instance is not yet dead.
|
|
172 |
It will shut down when you exit this prompt (CTRL-D)
|
|
173 |
||
174 |
Tab-completion is enabled.
|
|
175 |
EC2Instance is available as `instance`.
|
|
176 |
Also try these:
|
|
177 |
http://%(dns)s/current_test.log |
|
9760.5.1
by Guilherme Salgado
Fix the bug |
178 |
ssh -A ec2test@%(dns)s |
9639.1.1
by Gavin Panella
Revert revision 9636, which itself was a reversion of an earlier revision. |
179 |
"""
|
180 |
||
181 |
||
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
182 |
class EC2Instance: |
183 |
"""A single EC2 instance."""
|
|
184 |
||
9397.2.25
by Michael Hudson
act on comments from jml |
185 |
@classmethod
|
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
186 |
def make(cls, name, instance_type, machine_id, demo_networks=None, |
187 |
credentials=None): |
|
9397.2.26
by Michael Hudson
docstrings! |
188 |
"""Construct an `EC2Instance`.
|
189 |
||
190 |
:param name: The name to use for the key pair and security group for
|
|
191 |
the instance.
|
|
9639.1.1
by Gavin Panella
Revert revision 9636, which itself was a reversion of an earlier revision. |
192 |
:type name: `EC2SessionName`
|
9397.2.26
by Michael Hudson
docstrings! |
193 |
:param instance_type: One of the AVAILABLE_INSTANCE_TYPES.
|
9453.2.20
by Michael Hudson
kill make_instance helper |
194 |
:param machine_id: The AMI to use, or None to do the usual regexp
|
9550.13.40
by Michael Hudson
self-review-ish comments |
195 |
matching. If you put 'based-on:' before the AMI id, it is assumed
|
196 |
that the id specifies a blank image that should be made into one
|
|
197 |
suitable for the other ec2 functions (see `from_scratch_root` and
|
|
198 |
`from_scratch_ec2test` above).
|
|
9453.2.30
by Michael Hudson
start responding to review |
199 |
:param demo_networks: A list of networks to add to the security group
|
200 |
to allow access to the instance.
|
|
9453.2.20
by Michael Hudson
kill make_instance helper |
201 |
:param credentials: An `EC2Credentials` object.
|
9397.2.26
by Michael Hudson
docstrings! |
202 |
"""
|
9952.1.4
by Jeroen Vermeulen
Review changes. |
203 |
# This import breaks in the test environment. Do it here so
|
204 |
# that unit tests (which don't use this factory) can still
|
|
205 |
# import EC2Instance.
|
|
9942.2.1
by Jeroen Vermeulen
Added test, reproduced problem. |
206 |
from bzrlib.plugins.launchpad.account import get_lp_login |
9952.1.4
by Jeroen Vermeulen
Review changes. |
207 |
|
208 |
# XXX JeroenVermeulen 2009-11-27 bug=489073: EC2Credentials
|
|
209 |
# imports boto, which isn't necessarily installed in our test
|
|
210 |
# environment. Doing the import here so that unit tests (which
|
|
211 |
# don't use this factory) can still import EC2Instance.
|
|
9952.1.1
by Jeroen Vermeulen
Work around import issues. |
212 |
from devscripts.ec2test.credentials import EC2Credentials |
9942.2.1
by Jeroen Vermeulen
Added test, reproduced problem. |
213 |
|
9639.1.1
by Gavin Panella
Revert revision 9636, which itself was a reversion of an earlier revision. |
214 |
assert isinstance(name, EC2SessionName) |
9397.2.25
by Michael Hudson
act on comments from jml |
215 |
if instance_type not in AVAILABLE_INSTANCE_TYPES: |
216 |
raise ValueError('unknown instance_type %s' % (instance_type,)) |
|
217 |
||
9550.13.40
by Michael Hudson
self-review-ish comments |
218 |
# We call this here so that it has a chance to complain before the
|
219 |
# instance is started (which can take some time).
|
|
9550.13.44
by Michael Hudson
review inspired comments |
220 |
user_key = get_user_key() |
9550.13.40
by Michael Hudson
self-review-ish comments |
221 |
|
9453.2.20
by Michael Hudson
kill make_instance helper |
222 |
if credentials is None: |
223 |
credentials = EC2Credentials.load_from_file() |
|
224 |
||
9397.2.25
by Michael Hudson
act on comments from jml |
225 |
# Make the EC2 connection.
|
226 |
account = credentials.connect(name) |
|
227 |
||
228 |
# We do this here because it (1) cleans things up and (2) verifies
|
|
229 |
# that the account is correctly set up. Both of these are appropriate
|
|
230 |
# for initialization.
|
|
231 |
#
|
|
232 |
# We always recreate the keypairs because there is no way to
|
|
9594.2.16
by Gavin Panella
Undo unnecessary changes. |
233 |
# programmatically retrieve the private key component, unless we
|
234 |
# generate it.
|
|
9639.1.1
by Gavin Panella
Revert revision 9636, which itself was a reversion of an earlier revision. |
235 |
account.collect_garbage() |
9397.2.25
by Michael Hudson
act on comments from jml |
236 |
|
9550.13.39
by Michael Hudson
trivial fix |
237 |
if machine_id and machine_id.startswith('based-on:'): |
9550.13.30
by Michael Hudson
this might possibly work now |
238 |
from_scratch = True |
239 |
machine_id = machine_id[len('based-on:'):] |
|
240 |
else: |
|
241 |
from_scratch = False |
|
242 |
||
9397.2.25
by Michael Hudson
act on comments from jml |
243 |
# get the image
|
244 |
image = account.acquire_image(machine_id) |
|
245 |
||
246 |
login = get_lp_login() |
|
247 |
if not login: |
|
9453.2.22
by Michael Hudson
kill error_and_quit |
248 |
raise BzrCommandError( |
9397.2.25
by Michael Hudson
act on comments from jml |
249 |
'you must have set your launchpad login in bzr.') |
250 |
||
251 |
return EC2Instance( |
|
9636.1.15
by Jonathan Lange
Remove the vals dict. |
252 |
name, image, instance_type, demo_networks, account, |
9636.1.14
by Jonathan Lange
Drop launchpad-login from the vals dictionary. |
253 |
from_scratch, user_key, login) |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
254 |
|
9397.2.1
by Michael Hudson
rename controller variables to account |
255 |
def __init__(self, name, image, instance_type, demo_networks, account, |
9636.1.15
by Jonathan Lange
Remove the vals dict. |
256 |
from_scratch, user_key, launchpad_login): |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
257 |
self._name = name |
258 |
self._image = image |
|
9397.2.1
by Michael Hudson
rename controller variables to account |
259 |
self._account = account |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
260 |
self._instance_type = instance_type |
261 |
self._demo_networks = demo_networks |
|
262 |
self._boto_instance = None |
|
9550.13.30
by Michael Hudson
this might possibly work now |
263 |
self._from_scratch = from_scratch |
9550.13.44
by Michael Hudson
review inspired comments |
264 |
self._user_key = user_key |
9636.1.14
by Jonathan Lange
Drop launchpad-login from the vals dictionary. |
265 |
self._launchpad_login = launchpad_login |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
266 |
|
267 |
def log(self, msg): |
|
268 |
"""Log a message on stdout, flushing afterwards."""
|
|
269 |
# XXX: JonathanLange 2009-05-31 bug=383076: Should delete this and use
|
|
270 |
# Python logging module instead.
|
|
271 |
sys.stdout.write(msg) |
|
272 |
sys.stdout.flush() |
|
273 |
||
274 |
def start(self): |
|
275 |
"""Start the instance."""
|
|
276 |
if self._boto_instance is not None: |
|
277 |
self.log('Instance %s already started' % self._boto_instance.id) |
|
278 |
return
|
|
279 |
start = time.time() |
|
9397.2.1
by Michael Hudson
rename controller variables to account |
280 |
self.private_key = self._account.acquire_private_key() |
9453.2.7
by Michael Hudson
flesh out demo command |
281 |
self.security_group = self._account.acquire_security_group( |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
282 |
demo_networks=self._demo_networks) |
283 |
reservation = self._image.run( |
|
9594.2.16
by Gavin Panella
Undo unnecessary changes. |
284 |
key_name=self._name, security_groups=[self._name], |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
285 |
instance_type=self._instance_type) |
286 |
self._boto_instance = reservation.instances[0] |
|
287 |
self.log('Instance %s starting..' % self._boto_instance.id) |
|
288 |
while self._boto_instance.state == 'pending': |
|
289 |
self.log('.') |
|
290 |
time.sleep(5) |
|
291 |
self._boto_instance.update() |
|
292 |
if self._boto_instance.state == 'running': |
|
293 |
self.log(' started on %s\n' % self.hostname) |
|
294 |
elapsed = time.time() - start |
|
295 |
self.log('Started in %d minutes %d seconds\n' % |
|
296 |
(elapsed // 60, elapsed % 60)) |
|
297 |
self._output = self._boto_instance.get_console_output() |
|
298 |
self.log(self._output.output) |
|
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
299 |
self._ec2test_user_has_keys = False |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
300 |
else: |
9453.2.22
by Michael Hudson
kill error_and_quit |
301 |
raise BzrCommandError( |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
302 |
'failed to start: %s\n' % self._boto_instance.state) |
303 |
||
304 |
def shutdown(self): |
|
305 |
"""Shut down the instance."""
|
|
306 |
if self._boto_instance is None: |
|
307 |
self.log('no instance created\n') |
|
308 |
return
|
|
309 |
self._boto_instance.update() |
|
310 |
if self._boto_instance.state not in ('shutting-down', 'terminated'): |
|
311 |
# terminate instance
|
|
312 |
self._boto_instance.stop() |
|
313 |
self._boto_instance.update() |
|
314 |
self.log('instance %s\n' % (self._boto_instance.state,)) |
|
315 |
||
316 |
@property
|
|
317 |
def hostname(self): |
|
318 |
if self._boto_instance is None: |
|
319 |
return None |
|
320 |
return self._boto_instance.public_dns_name |
|
321 |
||
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
322 |
def _connect(self, username): |
9397.1.3
by Michael Hudson
resolve one of Jono's XXXs: make EC2Instance less stateful |
323 |
"""Connect to the instance as `user`. """
|
324 |
ssh = paramiko.SSHClient() |
|
325 |
ssh.set_missing_host_key_policy(AcceptAllPolicy()) |
|
9550.13.2
by Michael Hudson
move towards using a pre-setup account |
326 |
connect_args = { |
9550.13.6
by Michael Hudson
some complexity to cope with how the base instance does root login |
327 |
'username': username, |
9550.13.2
by Michael Hudson
move towards using a pre-setup account |
328 |
'pkey': self.private_key, |
329 |
'allow_agent': False, |
|
330 |
'look_for_keys': False, |
|
331 |
}
|
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
332 |
for count in range(10): |
333 |
try: |
|
9397.1.3
by Michael Hudson
resolve one of Jono's XXXs: make EC2Instance less stateful |
334 |
ssh.connect(self.hostname, **connect_args) |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
335 |
except (socket.error, paramiko.AuthenticationException), e: |
9550.13.2
by Michael Hudson
move towards using a pre-setup account |
336 |
self.log('_connect: %r\n' % (e,)) |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
337 |
if count < 9: |
338 |
time.sleep(5) |
|
339 |
self.log('retrying...') |
|
340 |
else: |
|
341 |
raise
|
|
342 |
else: |
|
343 |
break
|
|
9550.13.6
by Michael Hudson
some complexity to cope with how the base instance does root login |
344 |
return EC2InstanceConnection(self, username, ssh) |
9397.1.4
by Michael Hudson
move the unix user creation method to its own method on EC2Instance |
345 |
|
9550.13.40
by Michael Hudson
self-review-ish comments |
346 |
def _upload_local_key(self, conn, remote_filename): |
9550.13.44
by Michael Hudson
review inspired comments |
347 |
"""Upload a key from the local user's agent to `remote_filename`.
|
348 |
||
349 |
The key will be uploaded in a format suitable for
|
|
350 |
~/.ssh/authorized_keys.
|
|
351 |
"""
|
|
9550.13.40
by Michael Hudson
self-review-ish comments |
352 |
authorized_keys_file = conn.sftp.open(remote_filename, 'w') |
9550.13.31
by Michael Hudson
enable logging in as root with the local key before trying to use run_with_ssh_agent as root |
353 |
authorized_keys_file.write( |
9636.1.1
by Jonathan Lange
Clean up PEP 8 violations. |
354 |
"%s %s\n" % ( |
355 |
self._user_key.get_name(), self._user_key.get_base64())) |
|
9550.13.31
by Michael Hudson
enable logging in as root with the local key before trying to use run_with_ssh_agent as root |
356 |
authorized_keys_file.close() |
357 |
||
9550.13.44
by Michael Hudson
review inspired comments |
358 |
def _ensure_ec2test_user_has_keys(self, connection=None): |
359 |
"""Make sure that we can connect over ssh as the 'ec2test' user.
|
|
360 |
||
361 |
We add both the key that was used to start the instance (so
|
|
362 |
_connect('ec2test') works and a key from the locally running ssh agent
|
|
363 |
(so EC2InstanceConnection.run_with_ssh_agent works).
|
|
364 |
"""
|
|
365 |
if not self._ec2test_user_has_keys: |
|
366 |
if connection is None: |
|
10271.3.1
by Michael Hudson
maybe this is all? |
367 |
connection = self._connect('ubuntu') |
9550.13.44
by Michael Hudson
review inspired comments |
368 |
our_connection = True |
369 |
else: |
|
370 |
our_connection = False |
|
371 |
self._upload_local_key(connection, 'local_key') |
|
372 |
connection.perform( |
|
10271.3.1
by Michael Hudson
maybe this is all? |
373 |
'cat /home/ubuntu/.ssh/authorized_keys local_key '
|
10271.3.2
by Michael Hudson
fixes |
374 |
'| sudo tee /home/ec2test/.ssh/authorized_keys > /dev/null'
|
10271.3.1
by Michael Hudson
maybe this is all? |
375 |
'&& rm local_key') |
10271.3.2
by Michael Hudson
fixes |
376 |
connection.perform('sudo chown -R ec2test:ec2test /home/ec2test/') |
377 |
connection.perform('sudo chmod 644 /home/ec2test/.ssh/*') |
|
9550.13.44
by Michael Hudson
review inspired comments |
378 |
if our_connection: |
379 |
connection.close() |
|
380 |
self.log( |
|
9639.1.1
by Gavin Panella
Revert revision 9636, which itself was a reversion of an earlier revision. |
381 |
'You can now use ssh -A ec2test@%s to ' |
382 |
'log in the instance.\n' % self.hostname) |
|
9550.13.44
by Michael Hudson
review inspired comments |
383 |
self._ec2test_user_has_keys = True |
384 |
||
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
385 |
def connect(self): |
386 |
"""Connect to the instance as a user with passwordless sudo.
|
|
387 |
||
388 |
This may involve first connecting as root and adding SSH keys to the
|
|
9550.13.30
by Michael Hudson
this might possibly work now |
389 |
user's account, and in the case of a from scratch image, it will do a
|
390 |
lot of set up.
|
|
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
391 |
"""
|
9550.13.30
by Michael Hudson
this might possibly work now |
392 |
if self._from_scratch: |
10271.3.1
by Michael Hudson
maybe this is all? |
393 |
ubuntu_connection = self._connect('ubuntu') |
394 |
self._upload_local_key(ubuntu_connection, 'local_key') |
|
395 |
ubuntu_connection.perform( |
|
9550.13.37
by Michael Hudson
more streamlining, possibly with more correctness too |
396 |
'cat local_key >> ~/.ssh/authorized_keys && rm local_key') |
10271.3.1
by Michael Hudson
maybe this is all? |
397 |
ubuntu_connection.run_script(from_scratch_root, sudo=True) |
398 |
self._ensure_ec2test_user_has_keys(ubuntu_connection) |
|
399 |
ubuntu_connection.close() |
|
9550.13.44
by Michael Hudson
review inspired comments |
400 |
conn = self._connect('ec2test') |
9636.1.14
by Jonathan Lange
Drop launchpad-login from the vals dictionary. |
401 |
conn.run_script( |
402 |
from_scratch_ec2test
|
|
403 |
% {'launchpad-login': self._launchpad_login}) |
|
9550.13.35
by Michael Hudson
really run as ec2test user |
404 |
self._from_scratch = False |
9550.13.44
by Michael Hudson
review inspired comments |
405 |
return conn |
406 |
self._ensure_ec2test_user_has_keys() |
|
407 |
return self._connect('ec2test') |
|
9397.1.3
by Michael Hudson
resolve one of Jono's XXXs: make EC2Instance less stateful |
408 |
|
9942.2.4
by Jeroen Vermeulen
Cleaned up the test: generalized the stubbing. |
409 |
def _report_traceback(self): |
410 |
"""Print traceback."""
|
|
411 |
traceback.print_exc() |
|
412 |
||
9550.13.41
by Michael Hudson
revert a change that turned out not to be much use |
413 |
def set_up_and_run(self, postmortem, shutdown, func, *args, **kw): |
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
414 |
"""Start, run `func` and then maybe shut down.
|
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
415 |
|
9550.13.6
by Michael Hudson
some complexity to cope with how the base instance does root login |
416 |
:param config: A dictionary specifying details of how the instance
|
417 |
should be run:
|
|
9550.13.41
by Michael Hudson
revert a change that turned out not to be much use |
418 |
:param postmortem: If true, any exceptions will be caught and an
|
419 |
interactive session run to allow debugging the problem.
|
|
420 |
:param shutdown: If true, shut down the instance after `func` and
|
|
421 |
postmortem (if any) are completed.
|
|
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
422 |
:param func: A callable that will be called when the instance is
|
423 |
running and a user account has been set up on it.
|
|
424 |
:param args: Passed to `func`.
|
|
425 |
:param kw: Passed to `func`.
|
|
426 |
"""
|
|
9926.2.1
by Michael Hudson
in set_up_and_run, only pay attention to the shutdown if the supplied function exits normally |
427 |
# We ignore the value of the 'shutdown' argument and always shut down
|
9926.2.2
by Michael Hudson
typo |
428 |
# unless `func` returns normally.
|
9926.2.1
by Michael Hudson
in set_up_and_run, only pay attention to the shutdown if the supplied function exits normally |
429 |
really_shutdown = True |
9949.1.1
by Michael Hudson
don't always shutdown for headless builds :( |
430 |
retval = None |
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
431 |
try: |
9550.13.41
by Michael Hudson
revert a change that turned out not to be much use |
432 |
self.start() |
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
433 |
try: |
9949.1.1
by Michael Hudson
don't always shutdown for headless builds :( |
434 |
retval = func(*args, **kw) |
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
435 |
except Exception: |
9636.1.1
by Jonathan Lange
Clean up PEP 8 violations. |
436 |
# When running in postmortem mode, it is really helpful to see
|
437 |
# if there are any exceptions before it waits in the console
|
|
438 |
# (in the finally block), and you can't figure out why it's
|
|
439 |
# broken.
|
|
9942.2.4
by Jeroen Vermeulen
Cleaned up the test: generalized the stubbing. |
440 |
self._report_traceback() |
9926.2.1
by Michael Hudson
in set_up_and_run, only pay attention to the shutdown if the supplied function exits normally |
441 |
else: |
442 |
really_shutdown = shutdown |
|
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
443 |
finally: |
444 |
try: |
|
9550.13.41
by Michael Hudson
revert a change that turned out not to be much use |
445 |
if postmortem: |
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
446 |
console = code.InteractiveConsole(locals()) |
9639.1.1
by Gavin Panella
Revert revision 9636, which itself was a reversion of an earlier revision. |
447 |
console.interact( |
448 |
postmortem_banner % {'dns': self.hostname}) |
|
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
449 |
print 'Postmortem console closed.' |
450 |
finally: |
|
9926.2.1
by Michael Hudson
in set_up_and_run, only pay attention to the shutdown if the supplied function exits normally |
451 |
if really_shutdown: |
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
452 |
self.shutdown() |
9949.1.1
by Michael Hudson
don't always shutdown for headless builds :( |
453 |
return retval |
9453.2.17
by Michael Hudson
move run_with_instance to be EC2Instance.set_up_and_run |
454 |
|
9397.2.15
by Michael Hudson
a bit more LBYL |
455 |
def _copy_single_file(self, sftp, local_path, remote_dir): |
9397.2.26
by Michael Hudson
docstrings! |
456 |
"""Copy `local_path` to `remote_dir` on this instance.
|
457 |
||
458 |
The name in the remote directory will be that of the local file.
|
|
459 |
||
460 |
:param sftp: A paramiko SFTP object.
|
|
461 |
:param local_path: The local path.
|
|
462 |
:param remote_dir: The directory on the instance to copy into.
|
|
463 |
"""
|
|
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
464 |
name = os.path.basename(local_path) |
465 |
remote_path = os.path.join(remote_dir, name) |
|
466 |
remote_file = sftp.open(remote_path, 'w') |
|
467 |
remote_file.write(open(local_path).read()) |
|
468 |
remote_file.close() |
|
9397.2.15
by Michael Hudson
a bit more LBYL |
469 |
return remote_path |
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
470 |
|
471 |
def copy_key_and_certificate_to_image(self, sftp): |
|
9397.2.26
by Michael Hudson
docstrings! |
472 |
"""Copy the AWS private key and certificate to the image.
|
473 |
||
474 |
:param sftp: A paramiko SFTP object.
|
|
475 |
"""
|
|
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
476 |
remote_ec2_dir = '/mnt/ec2' |
9397.2.15
by Michael Hudson
a bit more LBYL |
477 |
remote_pk = self._copy_single_file( |
478 |
sftp, self.local_pk, remote_ec2_dir) |
|
9397.2.21
by Michael Hudson
even even even more small fixes |
479 |
remote_cert = self._copy_single_file( |
9397.2.15
by Michael Hudson
a bit more LBYL |
480 |
sftp, self.local_cert, remote_ec2_dir) |
481 |
return (remote_pk, remote_cert) |
|
482 |
||
483 |
def _check_single_glob_match(self, local_dir, pattern, file_kind): |
|
9397.2.26
by Michael Hudson
docstrings! |
484 |
"""Check that `pattern` matches one file in `local_dir` and return it.
|
485 |
||
486 |
:param local_dir: The local directory to look in.
|
|
487 |
:param pattern: The glob patten to match.
|
|
488 |
:param file_kind: The sort of file we're looking for, to be used in
|
|
489 |
error messages.
|
|
490 |
"""
|
|
9397.2.15
by Michael Hudson
a bit more LBYL |
491 |
pattern = os.path.join(local_dir, pattern) |
492 |
matches = glob.glob(pattern) |
|
493 |
if len(matches) != 1: |
|
9453.2.22
by Michael Hudson
kill error_and_quit |
494 |
raise BzrCommandError( |
9397.2.15
by Michael Hudson
a bit more LBYL |
495 |
'%r must match a single %s file' % (pattern, file_kind)) |
496 |
return matches[0] |
|
497 |
||
11962.1.1
by Gavin Panella
Create an S3 bucket for the given AMI name in check_bundling_prerequisites(). |
498 |
def check_bundling_prerequisites(self, name, credentials): |
9397.2.15
by Michael Hudson
a bit more LBYL |
499 |
"""Check, as best we can, that all the files we need to bundle exist.
|
500 |
"""
|
|
11699.1.1
by Gary Poster
make update-image work without hiccups on lucid. |
501 |
if subprocess.call(['which', 'ec2-register']): |
502 |
raise BzrCommandError( |
|
503 |
'`ec2-register` command not found. '
|
|
504 |
'Try `sudo apt-get install ec2-api-tools`.') |
|
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
505 |
local_ec2_dir = os.path.expanduser('~/.ec2') |
9397.2.15
by Michael Hudson
a bit more LBYL |
506 |
if not os.path.exists(local_ec2_dir): |
9453.2.22
by Michael Hudson
kill error_and_quit |
507 |
raise BzrCommandError( |
9397.2.15
by Michael Hudson
a bit more LBYL |
508 |
"~/.ec2 must exist and contain aws_user, aws_id, a private "
|
509 |
"key file and a certificate.") |
|
510 |
aws_user_file = os.path.expanduser('~/.ec2/aws_user') |
|
511 |
if not os.path.exists(aws_user_file): |
|
9453.2.22
by Michael Hudson
kill error_and_quit |
512 |
raise BzrCommandError( |
9397.2.15
by Michael Hudson
a bit more LBYL |
513 |
"~/.ec2/aws_user must exist and contain your numeric AWS id.") |
514 |
self.aws_user = open(aws_user_file).read().strip() |
|
515 |
self.local_cert = self._check_single_glob_match( |
|
516 |
local_ec2_dir, 'cert-*.pem', 'certificate') |
|
517 |
self.local_pk = self._check_single_glob_match( |
|
518 |
local_ec2_dir, 'pk-*.pem', 'private key') |
|
11962.1.1
by Gavin Panella
Create an S3 bucket for the given AMI name in check_bundling_prerequisites(). |
519 |
# The bucket `name` needs to exist and be accessible. We create it
|
520 |
# here to reserve the name. If the bucket already exists and conforms
|
|
521 |
# to the above requirements, this is a no-op.
|
|
522 |
credentials.connect_s3().create_bucket(name) |
|
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
523 |
|
9397.2.25
by Michael Hudson
act on comments from jml |
524 |
def bundle(self, name, credentials): |
9397.2.26
by Michael Hudson
docstrings! |
525 |
"""Bundle, upload and register the instance as a new AMI.
|
526 |
||
527 |
:param name: The name-to-be of the new AMI.
|
|
528 |
:param credentials: An `EC2Credentials` object.
|
|
529 |
"""
|
|
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
530 |
connection = self.connect() |
11699.1.1
by Gary Poster
make update-image work without hiccups on lucid. |
531 |
# See http://is.gd/g1MIT . When we switch to Eucalyptus, we can dump
|
532 |
# this installation of the ec2-ami-tools.
|
|
533 |
connection.perform( |
|
534 |
'sudo env DEBIAN_FRONTEND=noninteractive '
|
|
535 |
'apt-get -y install ec2-ami-tools') |
|
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
536 |
connection.perform('rm -f .ssh/authorized_keys') |
537 |
connection.perform('sudo mkdir /mnt/ec2') |
|
538 |
connection.perform('sudo chown $USER:$USER /mnt/ec2') |
|
9550.13.34
by Michael Hudson
make using sftp more convenient |
539 |
|
9636.1.1
by Jonathan Lange
Clean up PEP 8 violations. |
540 |
remote_pk, remote_cert = self.copy_key_and_certificate_to_image( |
9550.13.34
by Michael Hudson
make using sftp more convenient |
541 |
connection.sftp) |
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
542 |
|
543 |
bundle_dir = os.path.join('/mnt', name) |
|
544 |
||
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
545 |
connection.perform('sudo mkdir ' + bundle_dir) |
546 |
connection.perform(' '.join([ |
|
9550.13.2
by Michael Hudson
move towards using a pre-setup account |
547 |
'sudo ec2-bundle-vol', |
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
548 |
'-d %s' % bundle_dir, |
9550.13.4
by Michael Hudson
fix |
549 |
'--batch', # Set batch-mode, which doesn't use prompts. |
9397.2.4
by Michael Hudson
this might work, probably not though... |
550 |
'-k %s' % remote_pk, |
551 |
'-c %s' % remote_cert, |
|
9397.2.15
by Michael Hudson
a bit more LBYL |
552 |
'-u %s' % self.aws_user, |
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
553 |
]))
|
554 |
||
555 |
# Assume that the manifest is 'image.manifest.xml', since "image" is
|
|
556 |
# the default prefix.
|
|
557 |
manifest = os.path.join(bundle_dir, 'image.manifest.xml') |
|
558 |
||
559 |
# Best check that the manifest actually exists though.
|
|
560 |
test = 'test -f %s' % manifest |
|
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
561 |
connection.perform(test) |
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
562 |
|
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
563 |
connection.perform(' '.join([ |
9550.13.2
by Michael Hudson
move towards using a pre-setup account |
564 |
'sudo ec2-upload-bundle', |
9397.2.10
by Michael Hudson
grr v2 |
565 |
'-b %s' % name, |
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
566 |
'-m %s' % manifest, |
9397.2.25
by Michael Hudson
act on comments from jml |
567 |
'-a %s' % credentials.identifier, |
568 |
'-s %s' % credentials.secret, |
|
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
569 |
]))
|
570 |
||
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
571 |
connection.close() |
9397.2.2
by Michael Hudson
copy paste stuff from ec2-generate-windmill-image.py |
572 |
|
9397.2.4
by Michael Hudson
this might work, probably not though... |
573 |
# This is invoked locally.
|
574 |
mfilename = os.path.basename(manifest) |
|
9397.2.10
by Michael Hudson
grr v2 |
575 |
manifest_path = os.path.join(name, mfilename) |
9397.2.4
by Michael Hudson
this might work, probably not though... |
576 |
|
577 |
env = os.environ.copy() |
|
578 |
if 'JAVA_HOME' not in os.environ: |
|
579 |
env['JAVA_HOME'] = '/usr/lib/jvm/default-java' |
|
580 |
cmd = [ |
|
581 |
'ec2-register', |
|
9397.2.15
by Michael Hudson
a bit more LBYL |
582 |
'--private-key=%s' % self.local_pk, |
583 |
'--cert=%s' % self.local_cert, |
|
10554.1.2
by Jonathan Lange
Add the --name option, which seems to be required |
584 |
'--name=%s' % (name,), |
9636.1.1
by Jonathan Lange
Clean up PEP 8 violations. |
585 |
manifest_path, |
9397.2.4
by Michael Hudson
this might work, probably not though... |
586 |
]
|
587 |
self.log("Executing command: %s" % ' '.join(cmd)) |
|
588 |
subprocess.check_call(cmd, env=env) |
|
589 |
||
590 |
||
9397.1.3
by Michael Hudson
resolve one of Jono's XXXs: make EC2Instance less stateful |
591 |
class EC2InstanceConnection: |
592 |
"""An ssh connection to an `EC2Instance`."""
|
|
593 |
||
594 |
def __init__(self, instance, username, ssh): |
|
9550.13.34
by Michael Hudson
make using sftp more convenient |
595 |
self._instance = instance |
596 |
self._username = username |
|
597 |
self._ssh = ssh |
|
598 |
self._sftp = None |
|
599 |
||
600 |
@property
|
|
601 |
def sftp(self): |
|
602 |
if self._sftp is None: |
|
603 |
self._sftp = self._ssh.open_sftp() |
|
604 |
return self._sftp |
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
605 |
|
9636.1.4
by Jonathan Lange
Slightly clearer stdout / stderr behaviour. |
606 |
def perform(self, cmd, ignore_failure=False, out=None, err=None): |
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
607 |
"""Perform 'cmd' on server.
|
608 |
||
609 |
:param ignore_failure: If False, raise an error on non-zero exit
|
|
610 |
statuses.
|
|
611 |
:param out: A stream to write the output of the remote command to.
|
|
9636.1.4
by Jonathan Lange
Slightly clearer stdout / stderr behaviour. |
612 |
:param err: A stream to write the error of the remote command to.
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
613 |
"""
|
9636.1.4
by Jonathan Lange
Slightly clearer stdout / stderr behaviour. |
614 |
if out is None: |
615 |
out = sys.stdout |
|
616 |
if err is None: |
|
617 |
err = sys.stderr |
|
9550.13.34
by Michael Hudson
make using sftp more convenient |
618 |
self._instance.log( |
619 |
'%s@%s$ %s\n' |
|
620 |
% (self._username, self._instance._boto_instance.id, cmd)) |
|
621 |
session = self._ssh.get_transport().open_session() |
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
622 |
session.exec_command(cmd) |
623 |
session.shutdown_write() |
|
624 |
while 1: |
|
625 |
select.select([session], [], [], 0.5) |
|
626 |
if session.recv_ready(): |
|
627 |
data = session.recv(4096) |
|
628 |
if data: |
|
9636.1.4
by Jonathan Lange
Slightly clearer stdout / stderr behaviour. |
629 |
out.write(data) |
630 |
out.flush() |
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
631 |
if session.recv_stderr_ready(): |
632 |
data = session.recv_stderr(4096) |
|
633 |
if data: |
|
9636.1.4
by Jonathan Lange
Slightly clearer stdout / stderr behaviour. |
634 |
err.write(data) |
635 |
err.flush() |
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
636 |
if session.exit_status_ready(): |
637 |
break
|
|
638 |
session.close() |
|
639 |
# XXX: JonathanLange 2009-05-31: If the command is killed by a signal
|
|
640 |
# on the remote server, the SSH protocol does not send an exit_status,
|
|
641 |
# it instead sends a different message with the number of the signal
|
|
642 |
# that killed the process. AIUI, this code will fail confusingly if
|
|
643 |
# that happens.
|
|
644 |
res = session.recv_exit_status() |
|
645 |
if res and not ignore_failure: |
|
646 |
raise RuntimeError('Command failed: %s' % (cmd,)) |
|
647 |
return res |
|
648 |
||
649 |
def run_with_ssh_agent(self, cmd, ignore_failure=False): |
|
650 |
"""Run 'cmd' in a subprocess.
|
|
651 |
||
652 |
Use this to run commands that require local SSH credentials. For
|
|
653 |
example, getting private branches from Launchpad.
|
|
654 |
"""
|
|
9550.13.34
by Michael Hudson
make using sftp more convenient |
655 |
self._instance.log( |
9550.13.29
by Michael Hudson
some more, some less genericity, make the remote user called ec2test |
656 |
'%s@%s$ %s\n' |
9550.13.34
by Michael Hudson
make using sftp more convenient |
657 |
% (self._username, self._instance._boto_instance.id, cmd)) |
658 |
call = ['ssh', '-A', self._username + '@' + self._instance.hostname, |
|
9389.6.3
by Michael Hudson
give EC2Instance to its own file |
659 |
'-o', 'CheckHostIP no', |
660 |
'-o', 'StrictHostKeyChecking no', |
|
661 |
'-o', 'UserKnownHostsFile ~/.ec2/known_hosts', |
|
662 |
cmd] |
|
663 |
res = subprocess.call(call) |
|
664 |
if res and not ignore_failure: |
|
665 |
raise RuntimeError('Command failed: %s' % (cmd,)) |
|
666 |
return res |
|
9397.1.3
by Michael Hudson
resolve one of Jono's XXXs: make EC2Instance less stateful |
667 |
|
10271.3.1
by Michael Hudson
maybe this is all? |
668 |
def run_script(self, script_text, sudo=False): |
9550.13.45
by Michael Hudson
bit more |
669 |
"""Upload `script_text` to the instance and run it with bash."""
|
9550.13.37
by Michael Hudson
more streamlining, possibly with more correctness too |
670 |
script = self.sftp.open('script.sh', 'w') |
671 |
script.write(script_text) |
|
672 |
script.close() |
|
10271.3.1
by Michael Hudson
maybe this is all? |
673 |
cmd = '/bin/bash script.sh' |
674 |
if sudo: |
|
675 |
cmd = 'sudo ' + cmd |
|
676 |
self.run_with_ssh_agent(cmd) |
|
9550.13.37
by Michael Hudson
more streamlining, possibly with more correctness too |
677 |
# At least for mwhudson, the paramiko connection often drops while the
|
678 |
# script is running. Reconnect just in case.
|
|
9550.13.45
by Michael Hudson
bit more |
679 |
self.reconnect() |
680 |
self.perform('rm script.sh') |
|
681 |
||
682 |
def reconnect(self): |
|
683 |
"""Close the connection and reopen it."""
|
|
9550.13.37
by Michael Hudson
more streamlining, possibly with more correctness too |
684 |
self.close() |
685 |
self._ssh = self._instance._connect(self._username)._ssh |
|
686 |
||
9397.1.3
by Michael Hudson
resolve one of Jono's XXXs: make EC2Instance less stateful |
687 |
def close(self): |
9550.13.34
by Michael Hudson
make using sftp more convenient |
688 |
if self._sftp is not None: |
689 |
self._sftp.close() |
|
690 |
self._sftp = None |
|
691 |
self._ssh.close() |
|
692 |
self._ssh = None |