Index: third_party/buildbot_7_12/buildbot/ec2buildslave.py |
diff --git a/third_party/buildbot_7_12/buildbot/ec2buildslave.py b/third_party/buildbot_7_12/buildbot/ec2buildslave.py |
deleted file mode 100644 |
index caadbc06b7a44704bebe4f88c36d7228ccbf1f6c..0000000000000000000000000000000000000000 |
--- a/third_party/buildbot_7_12/buildbot/ec2buildslave.py |
+++ /dev/null |
@@ -1,284 +0,0 @@ |
-"""A LatentSlave that uses EC2 to instantiate the slaves on demand. |
- |
-Tested with Python boto 1.5c |
-""" |
- |
-# Portions copyright Canonical Ltd. 2009 |
- |
-import cStringIO |
-import os |
-import re |
-import time |
-import urllib |
- |
-import boto |
-import boto.exception |
-from twisted.internet import defer, threads |
-from twisted.python import log |
- |
-from buildbot.buildslave import AbstractLatentBuildSlave |
-from buildbot import interfaces |
- |
-PENDING = 'pending' |
-RUNNING = 'running' |
-SHUTTINGDOWN = 'shutting-down' |
-TERMINATED = 'terminated' |
- |
-class EC2LatentBuildSlave(AbstractLatentBuildSlave): |
- |
- instance = image = None |
- _poll_resolution = 5 # hook point for tests |
- |
- def __init__(self, name, password, instance_type, ami=None, |
- valid_ami_owners=None, valid_ami_location_regex=None, |
- elastic_ip=None, identifier=None, secret_identifier=None, |
- aws_id_file_path=None, user_data=None, |
- keypair_name='latent_buildbot_slave', |
- security_name='latent_buildbot_slave', |
- max_builds=None, notify_on_missing=[], missing_timeout=60*20, |
- build_wait_timeout=60*10, properties={}): |
- AbstractLatentBuildSlave.__init__( |
- self, name, password, max_builds, notify_on_missing, |
- missing_timeout, build_wait_timeout, properties) |
- if not ((ami is not None) ^ |
- (valid_ami_owners is not None or |
- valid_ami_location_regex is not None)): |
- raise ValueError( |
- 'You must provide either a specific ami, or one or both of ' |
- 'valid_ami_location_regex and valid_ami_owners') |
- self.ami = ami |
- if valid_ami_owners is not None: |
- if isinstance(valid_ami_owners, (int, long)): |
- valid_ami_owners = (valid_ami_owners,) |
- else: |
- for element in valid_ami_owners: |
- if not isinstance(element, (int, long)): |
- raise ValueError( |
- 'valid_ami_owners should be int or iterable ' |
- 'of ints', element) |
- if valid_ami_location_regex is not None: |
- if not isinstance(valid_ami_location_regex, basestring): |
- raise ValueError( |
- 'valid_ami_location_regex should be a string') |
- else: |
- # verify that regex will compile |
- re.compile(valid_ami_location_regex) |
- self.valid_ami_owners = valid_ami_owners |
- self.valid_ami_location_regex = valid_ami_location_regex |
- self.instance_type = instance_type |
- self.keypair_name = keypair_name |
- self.security_name = security_name |
- self.user_data = user_data |
- if identifier is None: |
- assert secret_identifier is None, ( |
- 'supply both or neither of identifier, secret_identifier') |
- if aws_id_file_path is None: |
- home = os.environ['HOME'] |
- aws_id_file_path = os.path.join(home, '.ec2', 'aws_id') |
- if not os.path.exists(aws_id_file_path): |
- raise ValueError( |
- "Please supply your AWS access key identifier and secret " |
- "access key identifier either when instantiating this %s " |
- "or in the %s file (on two lines).\n" % |
- (self.__class__.__name__, aws_id_file_path)) |
- aws_file = open(aws_id_file_path, 'r') |
- try: |
- identifier = aws_file.readline().strip() |
- secret_identifier = aws_file.readline().strip() |
- finally: |
- aws_file.close() |
- else: |
- assert aws_id_file_path is None, \ |
- 'if you supply the identifier and secret_identifier, ' \ |
- 'do not specify the aws_id_file_path' |
- assert secret_identifier is not None, \ |
- 'supply both or neither of identifier, secret_identifier' |
- # Make the EC2 connection. |
- self.conn = boto.connect_ec2(identifier, secret_identifier) |
- |
- # Make a keypair |
- # |
- # We currently discard the keypair data because we don't need it. |
- # If we do need it in the future, we will always recreate the keypairs |
- # because there is no way to |
- # programmatically retrieve the private key component, unless we |
- # generate it and store it on the filesystem, which is an unnecessary |
- # usage requirement. |
- try: |
- key_pair = self.conn.get_all_key_pairs(keypair_name)[0] |
- # key_pair.delete() # would be used to recreate |
- except boto.exception.EC2ResponseError, e: |
- if e.code != 'InvalidKeyPair.NotFound': |
- if e.code == 'AuthFailure': |
- print ('POSSIBLE CAUSES OF ERROR:\n' |
- ' Did you sign up for EC2?\n' |
- ' Did you put a credit card number in your AWS ' |
- 'account?\n' |
- 'Please doublecheck before reporting a problem.\n') |
- raise |
- # make one; we would always do this, and stash the result, if we |
- # needed the key (for instance, to SSH to the box). We'd then |
- # use paramiko to use the key to connect. |
- self.conn.create_key_pair(keypair_name) |
- |
- # create security group |
- try: |
- group = self.conn.get_all_security_groups(security_name)[0] |
- except boto.exception.EC2ResponseError, e: |
- if e.code == 'InvalidGroup.NotFound': |
- self.security_group = self.conn.create_security_group( |
- security_name, |
- 'Authorization to access the buildbot instance.') |
- # Authorize the master as necessary |
- # TODO this is where we'd open the hole to do the reverse pb |
- # connect to the buildbot |
- # ip = urllib.urlopen( |
- # 'http://checkip.amazonaws.com').read().strip() |
- # self.security_group.authorize('tcp', 22, 22, '%s/32' % ip) |
- # self.security_group.authorize('tcp', 80, 80, '%s/32' % ip) |
- else: |
- raise |
- |
- # get the image |
- if self.ami is not None: |
- self.image = self.conn.get_image(self.ami) |
- else: |
- # verify we have access to at least one acceptable image |
- discard = self.get_image() |
- |
- # get the specified elastic IP, if any |
- if elastic_ip is not None: |
- elastic_ip = self.conn.get_all_addresses([elastic_ip])[0] |
- self.elastic_ip = elastic_ip |
- |
- def get_image(self): |
- if self.image is not None: |
- return self.image |
- if self.valid_ami_location_regex: |
- level = 0 |
- options = [] |
- get_match = re.compile(self.valid_ami_location_regex).match |
- for image in self.conn.get_all_images( |
- owners=self.valid_ami_owners): |
- # gather sorting data |
- match = get_match(image.location) |
- if match: |
- alpha_sort = int_sort = None |
- if level < 2: |
- try: |
- alpha_sort = match.group(1) |
- except IndexError: |
- level = 2 |
- else: |
- if level == 0: |
- try: |
- int_sort = int(alpha_sort) |
- except ValueError: |
- level = 1 |
- options.append([int_sort, alpha_sort, |
- image.location, image.id, image]) |
- if level: |
- log.msg('sorting images at level %d' % level) |
- options = [candidate[level:] for candidate in options] |
- else: |
- options = [(image.location, image.id, image) for image |
- in self.conn.get_all_images( |
- owners=self.valid_ami_owners)] |
- options.sort() |
- log.msg('sorted images (last is chosen): %s' % |
- (', '.join( |
- ['%s (%s)' % (candidate[-1].id, candidate[-1].location) |
- for candidate in options]))) |
- if not options: |
- raise ValueError('no available images match constraints') |
- return options[-1][-1] |
- |
- def dns(self): |
- if self.instance is None: |
- return None |
- return self.instance.public_dns_name |
- dns = property(dns) |
- |
- def start_instance(self): |
- if self.instance is not None: |
- raise ValueError('instance active') |
- return threads.deferToThread(self._start_instance) |
- |
- def _start_instance(self): |
- image = self.get_image() |
- reservation = image.run( |
- key_name=self.keypair_name, security_groups=[self.security_name], |
- instance_type=self.instance_type, user_data=self.user_data) |
- self.instance = reservation.instances[0] |
- log.msg('%s %s starting instance %s' % |
- (self.__class__.__name__, self.slavename, self.instance.id)) |
- duration = 0 |
- interval = self._poll_resolution |
- while self.instance.state == PENDING: |
- time.sleep(interval) |
- duration += interval |
- if duration % 60 == 0: |
- log.msg('%s %s has waited %d minutes for instance %s' % |
- (self.__class__.__name__, self.slavename, duration//60, |
- self.instance.id)) |
- self.instance.update() |
- if self.instance.state == RUNNING: |
- self.output = self.instance.get_console_output() |
- minutes = duration//60 |
- seconds = duration%60 |
- log.msg('%s %s instance %s started on %s ' |
- 'in about %d minutes %d seconds (%s)' % |
- (self.__class__.__name__, self.slavename, |
- self.instance.id, self.dns, minutes, seconds, |
- self.output.output)) |
- if self.elastic_ip is not None: |
- self.instance.use_ip(self.elastic_ip) |
- return [self.instance.id, |
- image.id, |
- '%02d:%02d:%02d' % (minutes//60, minutes%60, seconds)] |
- else: |
- log.msg('%s %s failed to start instance %s (%s)' % |
- (self.__class__.__name__, self.slavename, |
- self.instance.id, self.instance.state)) |
- raise interfaces.LatentBuildSlaveFailedToSubstantiate( |
- self.instance.id, self.instance.state) |
- |
- def stop_instance(self, fast=False): |
- if self.instance is None: |
- # be gentle. Something may just be trying to alert us that an |
- # instance never attached, and it's because, somehow, we never |
- # started. |
- return defer.succeed(None) |
- instance = self.instance |
- self.output = self.instance = None |
- return threads.deferToThread( |
- self._stop_instance, instance, fast) |
- |
- def _stop_instance(self, instance, fast): |
- if self.elastic_ip is not None: |
- self.conn.disassociate_address(self.elastic_ip.public_ip) |
- instance.update() |
- if instance.state not in (SHUTTINGDOWN, TERMINATED): |
- instance.stop() |
- log.msg('%s %s terminating instance %s' % |
- (self.__class__.__name__, self.slavename, instance.id)) |
- duration = 0 |
- interval = self._poll_resolution |
- if fast: |
- goal = (SHUTTINGDOWN, TERMINATED) |
- instance.update() |
- else: |
- goal = (TERMINATED,) |
- while instance.state not in goal: |
- time.sleep(interval) |
- duration += interval |
- if duration % 60 == 0: |
- log.msg( |
- '%s %s has waited %d minutes for instance %s to end' % |
- (self.__class__.__name__, self.slavename, duration//60, |
- instance.id)) |
- instance.update() |
- log.msg('%s %s instance %s %s ' |
- 'after about %d minutes %d seconds' % |
- (self.__class__.__name__, self.slavename, |
- instance.id, goal, duration//60, duration%60)) |