aboutsummaryrefslogtreecommitdiff
path: root/linaro-ami/aws_controller.py
blob: 1ffc7039b4ae99dfc2e33407142402ce9dd88a74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import boto
import logging
import sys
import time


class InstanceRunFailedError(Exception):
    """Instance still not running after waiting for it to start."""


class AwsController:
    """AWS API wrapper.

    Provides various methods from boto library, mostly tied to EC2,
    with some additional tweaks.
    """

    def connect(self, aws_access_key_id=None, aws_secret_access_key=None):
        """Connect to EC2.

        If aws_access_key_id and aws_secret_access_key are empty, this
        method will take the values from the respective environment variables.
        """
        self._connection = boto.connect_ec2(aws_access_key_id,
                                           aws_secret_access_key)

    def run_instance(self, image_id, key_name, instance_type='m1.medium'):
        """Run the instance from particular AMI and return a reference.

        This method will throw an exception if no instances are marked
        for running.
        """
        reservation = self._connection.run_instances(
            image_id, key_name=key_name, instance_type=instance_type)
        return reservation.instances[0]

    def wait_for_instance_state(self, instance, state):
        """Block the code execution until instance is in specified state.

        Try for a maximum of 60 times roughly equaling 1 minute.
        """
        max_retries = 60
        for counter in range(max_retries):
            instance.update()
            if instance.state == state:
                break
            time.sleep(1)

        if counter >= max_retries:
            raise TimeoutError("State '%s' was never reached for " + \
                               "instance '%s' in '%d' attempts" % state,
                               instance.id, max_retries)

    def run_instance_and_wait(self, image_id, key_name,
                              instance_type='m1.medium'):
        """Run the instance and wait until it's in running state."""
        reservation = self._connection.run_instances(
            image_id, key_name=key_name, instance_type=instance_type)
        instance = reservation.instances[0]
        self.wait_for_instance_state(reservation.instances[0], "running")

        # Wait another 30 seconds for SSH server to come up
        time.sleep(30)

        logging.info("Instance up: id= " + instance.id +
                     ", public_dns= " + instance.public_dns_name)
        return instance

    def stop_instance_and_wait(self, instance_id):
        """Stop the instance and then wait until it's in stopped state."""
        instance_list = self._connection.stop_instances([instance_id])
        self.wait_for_instance_state(instance_list[0], "stopped")
        return instance_list[0]

    def terminate_instance(self, instance_id):
        """Send termination signal to the instance."""
        self._connection.terminate_instances([instance_id])

    def start_instance(self, instance_id):
        """Start an already running instance."""
        self._connection.start_instances([instance_id])

    def create_image(self, instance_id, name, description):
        """Create image from an instance.

        Returns image id as string.
        """
        return self._connection.create_image(instance_id, name, description)

    def list_images(self):
        """List all images owned by current user."""
        return self._connection.get_all_images(owners=["self"])

    def get_image_creation_time(self, image):
        """Get image creation timestamp."""
        # There doesn't seem to be timestamp associated with AMI, so iterate
        # over its associated snapshots, and find oldest.
        snapshot_ids = [bdm.snapshot_id
                        for bdm in image.block_device_mapping.values()]
        snapshots = self._connection.get_all_snapshots(snapshot_ids)
        oldest = "Z"
        for s in snapshots:
            if s.start_time < oldest:
                oldest = s.start_time
        return oldest

    def create_tags(self, resource_ids, tags):
        """Create a tag for a specific EC2 resource.

        EC2 tag represents a metadata field used to describe this particular
        resource.
        """
        return self._connection.create_tags(resource_ids, tags)

    def import_key_pair(self, key_name, public_key_material):
        """Import a key pair to this account.

        Key pairs are combination of user id and public part of RSA
        used for SSH authentication on the instances created by AWS tools.
        """
        return self._connection.import_key_pair(key_name, public_key_material)

    def delete_key_pair(self, key_name):
        """Deletes a key pair from this account."""
        return self._connection.delete_key_pair(key_name)