~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/devscripts/ec2test/instance.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2011-08-03 11:23:34 UTC
  • mfrom: (13457.6.16 upgrade-stderr)
  • Revision ID: launchpad@pqm.canonical.com-20110803112334-acnupsa7jmzmdeet
[r=stevenk][bug=819751] Fix the implementation of several methods in
 LoggingUIFactory.

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
    ]
10
10
 
11
11
import code
12
 
from datetime import datetime
13
12
import errno
14
13
import glob
15
14
import os
21
20
import traceback
22
21
 
23
22
from bzrlib.errors import BzrCommandError
 
23
from devscripts.ec2test.session import EC2SessionName
24
24
import paramiko
25
25
 
26
 
from devscripts.ec2test.session import EC2SessionName
27
 
 
28
 
 
29
 
DEFAULT_INSTANCE_TYPE = 'm2.xlarge'
30
 
DEFAULT_REGION = 'us-east-1'
31
 
AVAILABLE_INSTANCE_TYPES = (
32
 
    'm1.large', 'm1.xlarge', 'm2.xlarge', 'm2.2xlarge', 'm2.4xlarge',
33
 
    'c1.xlarge', 'cc1.4xlarge', 'cc1.8xlarge')
 
26
 
 
27
DEFAULT_INSTANCE_TYPE = 'c1.xlarge'
 
28
AVAILABLE_INSTANCE_TYPES = ('m1.large', 'm1.xlarge', 'c1.xlarge')
34
29
 
35
30
 
36
31
class AcceptAllPolicy:
76
71
# -e  Exit immediately if a command exits with a non-zero status.
77
72
set -xe
78
73
 
79
 
# They end up as just one stream; this avoids ordering problems.
80
 
exec 2>&1
81
 
 
82
74
sed -ie 's/main universe/main universe multiverse/' /etc/apt/sources.list
83
75
 
84
76
. /etc/lsb-release
85
77
 
86
 
mount -o remount,data=writeback,commit=3600,async,relatime /
87
 
 
88
78
cat >> /etc/apt/sources.list << EOF
89
79
deb http://ppa.launchpad.net/launchpad/ubuntu $DISTRIB_CODENAME main
90
80
deb http://ppa.launchpad.net/bzr/ubuntu $DISTRIB_CODENAME main
 
81
deb http://ppa.launchpad.net/bzr-beta-ppa/ubuntu $DISTRIB_CODENAME main
 
82
deb http://us.ec2.archive.ubuntu.com/ubuntu/ $DISTRIB_CODENAME multiverse
 
83
deb-src http://us.ec2.archive.ubuntu.com/ubuntu/ $DISTRIB_CODENAME main
91
84
EOF
92
85
 
93
 
export DEBIAN_FRONTEND=noninteractive
94
 
 
95
 
# PPA keys
96
 
apt-key adv --recv-keys --keyserver pool.sks-keyservers.net 2af499cb24ac5f65461405572d1ffb6c0a5174af # launchpad
97
 
apt-key adv --recv-keys --keyserver pool.sks-keyservers.net ece2800bacf028b31ee3657cd702bf6b8c6c1efd # bzr
98
 
 
99
 
aptitude update
100
 
 
101
 
# Do this first so later things don't complain about locales:
102
 
LANG=C aptitude -y install language-pack-en
103
 
 
104
 
aptitude -y full-upgrade
105
 
 
106
86
# This next part is cribbed from rocketfuel-setup
107
87
dev_host() {
108
88
  sed -i \"s/^127.0.0.88.*$/&\ ${hostname}/\" /etc/hosts
145
125
127.0.0.99      bazaar.launchpad.dev
146
126
' >> /etc/hosts
147
127
 
148
 
apt-get -y install launchpad-developer-dependencies apache2 apache2-mpm-worker
 
128
# Add the keys for the three PPAs added to sources.list above.
 
129
apt-key adv --recv-keys --keyserver pool.sks-keyservers.net 2af499cb24ac5f65461405572d1ffb6c0a5174af
 
130
apt-key adv --recv-keys --keyserver pool.sks-keyservers.net ece2800bacf028b31ee3657cd702bf6b8c6c1efd
 
131
apt-key adv --recv-keys --keyserver pool.sks-keyservers.net cbede690576d1e4e813f6bb3ebaf723d37b19b80
 
132
 
 
133
aptitude update
 
134
aptitude -y full-upgrade
 
135
 
 
136
DEBIAN_FRONTEND=noninteractive apt-get -y install launchpad-developer-dependencies apache2 apache2-mpm-worker
149
137
 
150
138
# Create the ec2test user, give them passwordless sudo.
151
139
adduser --gecos "" --disabled-password ec2test
168
156
# -e  Exit immediately if a command exits with a non-zero status.
169
157
set -xe
170
158
 
171
 
# They end up as just one stream; this avoids ordering problems.
172
 
exec 2>&1
173
 
 
174
159
bzr launchpad-login %(launchpad-login)s
175
160
bzr init-repo --2a /var/launchpad
176
161
bzr branch lp:~launchpad-pqm/launchpad/devel /var/launchpad/test
197
182
 
198
183
    @classmethod
199
184
    def make(cls, name, instance_type, machine_id, demo_networks=None,
200
 
             credentials=None, region=None):
 
185
             credentials=None):
201
186
        """Construct an `EC2Instance`.
202
187
 
203
188
        :param name: The name to use for the key pair and security group for
212
197
        :param demo_networks: A list of networks to add to the security group
213
198
            to allow access to the instance.
214
199
        :param credentials: An `EC2Credentials` object.
215
 
        :param region: A string region name eg 'us-east-1'.
216
200
        """
217
201
        # This import breaks in the test environment.  Do it here so
218
202
        # that unit tests (which don't use this factory) can still
226
210
        from devscripts.ec2test.credentials import EC2Credentials
227
211
 
228
212
        assert isinstance(name, EC2SessionName)
 
213
        if instance_type not in AVAILABLE_INSTANCE_TYPES:
 
214
            raise ValueError('unknown instance_type %s' % (instance_type,))
229
215
 
230
216
        # We call this here so that it has a chance to complain before the
231
217
        # instance is started (which can take some time).
232
218
        user_key = get_user_key()
233
219
 
234
220
        if credentials is None:
235
 
            credentials = EC2Credentials.load_from_file(region_name=region)
 
221
            credentials = EC2Credentials.load_from_file()
236
222
 
237
223
        # Make the EC2 connection.
238
224
        account = credentials.connect(name)
260
246
            raise BzrCommandError(
261
247
                'you must have set your launchpad login in bzr.')
262
248
 
263
 
        instance = EC2Instance(
 
249
        return EC2Instance(
264
250
            name, image, instance_type, demo_networks, account,
265
 
            from_scratch, user_key, login, region)
266
 
        instance._credentials = credentials
267
 
        return instance
 
251
            from_scratch, user_key, login)
268
252
 
269
253
    def __init__(self, name, image, instance_type, demo_networks, account,
270
 
                 from_scratch, user_key, launchpad_login, region):
 
254
                 from_scratch, user_key, launchpad_login):
271
255
        self._name = name
272
256
        self._image = image
273
257
        self._account = account
277
261
        self._from_scratch = from_scratch
278
262
        self._user_key = user_key
279
263
        self._launchpad_login = launchpad_login
280
 
        self._region = region
281
264
 
282
265
    def log(self, msg):
283
266
        """Log a message on stdout, flushing afterwards."""
314
297
            self._ec2test_user_has_keys = False
315
298
        else:
316
299
            raise BzrCommandError(
317
 
                "failed to start: %s: %r\n" % (
318
 
                    self._boto_instance.state,
319
 
                    self._boto_instance.state_reason,
320
 
                    ))
 
300
                'failed to start: %s\n' % self._boto_instance.state)
321
301
 
322
302
    def shutdown(self):
323
303
        """Shut down the instance."""
326
306
            return
327
307
        self._boto_instance.update()
328
308
        if self._boto_instance.state not in ('shutting-down', 'terminated'):
329
 
            self.log("terminating %s..." % self._boto_instance)
330
 
            self._boto_instance.terminate()
 
309
            # terminate instance
 
310
            self._boto_instance.stop()
331
311
            self._boto_instance.update()
332
 
            self.log(" done\n")
333
312
        self.log('instance %s\n' % (self._boto_instance.state,))
334
313
 
335
314
    @property
342
321
        """Connect to the instance as `user`. """
343
322
        ssh = paramiko.SSHClient()
344
323
        ssh.set_missing_host_key_policy(AcceptAllPolicy())
345
 
        self.log('ssh connect to %s: ' % self.hostname)
346
324
        connect_args = {
347
325
            'username': username,
348
326
            'pkey': self.private_key,
349
327
            'allow_agent': False,
350
328
            'look_for_keys': False,
351
329
            }
352
 
        for count in range(20):
353
 
            caught_errors = (
354
 
                socket.error,
355
 
                paramiko.AuthenticationException,
356
 
                EOFError,
357
 
                )
 
330
        for count in range(10):
358
331
            try:
359
332
                ssh.connect(self.hostname, **connect_args)
360
 
            except caught_errors as e:
361
 
                self.log('.')
362
 
                not_connected = [
363
 
                    errno.ECONNREFUSED,
364
 
                    errno.ETIMEDOUT,
365
 
                    errno.EHOSTUNREACH,
366
 
                    ]
367
 
                if getattr(e, 'errno', None) not in not_connected:
368
 
                    self.log('ssh _connect: %r\n' % (e,))
 
333
            except (socket.error, paramiko.AuthenticationException), e:
 
334
                self.log('_connect: %r\n' % (e,))
369
335
                if count < 9:
370
336
                    time.sleep(5)
 
337
                    self.log('retrying...')
371
338
                else:
372
339
                    raise
373
340
            else:
374
341
                break
375
 
        self.log(' ok!\n')
376
342
        return EC2InstanceConnection(self, username, ssh)
377
343
 
378
344
    def _upload_local_key(self, conn, remote_filename):
434
400
                from_scratch_ec2test
435
401
                % {'launchpad-login': self._launchpad_login})
436
402
            self._from_scratch = False
437
 
            self.log('done running from_scratch setup\n')
438
403
            return conn
439
404
        self._ensure_ec2test_user_has_keys()
440
405
        return self._connect('ec2test')
528
493
                '%r must match a single %s file' % (pattern, file_kind))
529
494
        return matches[0]
530
495
 
531
 
    def check_bundling_prerequisites(self, name):
 
496
    def check_bundling_prerequisites(self, name, credentials):
532
497
        """Check, as best we can, that all the files we need to bundle exist.
533
498
        """
 
499
        if subprocess.call(['which', 'ec2-register']):
 
500
            raise BzrCommandError(
 
501
                '`ec2-register` command not found.  '
 
502
                'Try `sudo apt-get install ec2-api-tools`.')
534
503
        local_ec2_dir = os.path.expanduser('~/.ec2')
535
504
        if not os.path.exists(local_ec2_dir):
536
505
            raise BzrCommandError(
548
517
        # The bucket `name` needs to exist and be accessible. We create it
549
518
        # here to reserve the name. If the bucket already exists and conforms
550
519
        # to the above requirements, this is a no-op.
551
 
        #
552
 
        # The API for region creation is a little quirky: you apparently can't
553
 
        # explicitly ask for 'us-east-1' you must just say '', etc.
554
 
        location = self._credentials.region_name
555
 
        if location.startswith('us-east'):
556
 
            location = ''
557
 
        elif location.startswith('eu'):
558
 
            location = 'EU'
559
 
        self._credentials.connect_s3().create_bucket(
560
 
            name, location=location)
 
520
        credentials.connect_s3().create_bucket(name)
561
521
 
562
522
    def bundle(self, name, credentials):
563
523
        """Bundle, upload and register the instance as a new AMI.
564
524
 
565
 
        :param name: The name-to-be of the new AMI, eg 'launchpad-ec2test500'.
 
525
        :param name: The name-to-be of the new AMI.
566
526
        :param credentials: An `EC2Credentials` object.
567
527
        """
568
528
        connection = self.connect()
612
572
        mfilename = os.path.basename(manifest)
613
573
        manifest_path = os.path.join(name, mfilename)
614
574
 
615
 
        now = datetime.strftime(datetime.utcnow(), "%Y-%m-%d %H:%M:%S UTC")
616
 
        description = "launchpad ec2test created %s by %r on %s" % (
617
 
            now,
618
 
            os.environ.get('EMAIL', '<unknown>'),
619
 
            socket.gethostname())
620
 
 
621
 
        self.log('registering image: ')
622
 
        image_id = credentials.connect('bundle').conn.register_image(
623
 
            name=name,
624
 
            description=description,
625
 
            image_location=manifest_path,
626
 
            )
627
 
        self.log('ok\n')
628
 
        self.log('** new instance: %r\n' % (image_id,))
 
575
        env = os.environ.copy()
 
576
        if 'JAVA_HOME' not in os.environ:
 
577
            env['JAVA_HOME'] = '/usr/lib/jvm/default-java'
 
578
        cmd = [
 
579
            'ec2-register',
 
580
            '--private-key=%s' % self.local_pk,
 
581
            '--cert=%s' % self.local_cert,
 
582
            '--name=%s' % (name,),
 
583
            manifest_path,
 
584
            ]
 
585
        self.log("Executing command: %s" % ' '.join(cmd))
 
586
        subprocess.check_call(cmd, env=env)
629
587
 
630
588
 
631
589
class EC2InstanceConnection:
641
599
    def sftp(self):
642
600
        if self._sftp is None:
643
601
            self._sftp = self._ssh.open_sftp()
644
 
        if self._sftp is None:
645
 
            raise AssertionError("failed to open sftp connection")
646
602
        return self._sftp
647
603
 
648
604
    def perform(self, cmd, ignore_failure=False, out=None, err=None):