aboutsummaryrefslogtreecommitdiff
path: root/linaro-ami/linaro_ami.py
blob: c0840b7c467a457c55ec15c71a3066156e27d6ea (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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import sys
import logging
import os
import time

from aws_controller import AwsController, InstanceRunFailedError
from remote_executor import RemoteExecutor, RemoteCommandFailedError
from remote_executor import AuthenticationException


def iso_timestamp():
    return time.strftime("%Y%m%dT%H%M%SZ", time.gmtime())

DEFAULT_AMI_TYPE = "build-slave"


class LinaroAMI:
    """Main class which contains all the command methods.

    Represents the placeholder for all the custom AMI tool features.
    Constructor keyword arguments:
    config_file -- configuration file path.
    username -- username for EC2 instances SSH connection.
    options -- options forwarded from the command line tool.
    aws_access_key_id -- AWS key ID (default None).
    aws_secret_access_key -- AWS secret key (default None).
    """

    def __init__(self, config, aws_controller, remote_executor, username,
                 key_filename=None,
                 options={}, aws_access_key_id=None,
                 aws_secret_access_key=None):
        self.log = logging.getLogger("linaro-ami")
        self.username = username
        self.key_filename = key_filename
        self.options = options
        self.aws_access_key_id = aws_access_key_id
        self.aws_secret_access_key = aws_secret_access_key
        self.config = config
        self.aws = aws_controller
        self.executor = remote_executor

    def has_ami(self, ami_name):
        """ Check if config object has a specific section. """
        return self.config.has_section(ami_name)

    def command_create(self, ami_name):
        """ Create command.

        It finds the configuration for the ami_name and creates the custom AMI
        based on the parameters provided.
        """

        self.log.info("Starting instance to create AMI...")
        self.aws.connect()

        instance = None
        base_ami = self.config.get(ami_name, "base_ami")
        instance_type = self.config.get(ami_name, "instance_type")
        script_fetch = self.config.get(ami_name, "init_script_fetch")
        if self.options.key:
            key_autocreate = False
            key_name = self.options.key
        else:
            self.log.warn("Trying to use auto-created key. This is not recommended, use --key= if any issues.")
            key_autocreate = True
            key_name = self.config.get(ami_name, "key_name")

        try:
            if key_autocreate:
                self.aws.import_key_pair(key_name, self.executor.get_public_key())
            instance = self.aws.run_instance_and_wait(base_ami,
                key_name, instance_type)
            self.log.info("Started instance: %s (%s)", instance.public_dns_name, instance.id)
            for i in xrange(20):
                try:
                    self.executor.connect(instance.public_dns_name, self.username, self.key_filename)
                    break
                except AuthenticationException, e:
                    if i == 19:
                        raise e
                    self.log.debug("Could not login to instance so far, retrying")
                    time.sleep(2)
            self.log.debug("Connected to instance")

            self.log.info("Installing version control systems.")
            install_command_output = self.executor.execute(self.config.get(
                "DEFAULT", "vcs_install"))
            self.log.debug(install_command_output)

            self.log.info("Cloning slave init repo.")
            fetch_command_output = self.executor.execute(script_fetch + " slave-init")
            self.log.debug(fetch_command_output)

            script_command = "sudo bash slave-init/" + self.config.get(
                ami_name, "init_script")
            self.log.info("Executing slave init script: " + \
                           script_command)
            script_command_output = self.executor.execute(script_command)
            self.log.debug(script_command_output)
            self.log.info("Slave init script finished successfully.")

            # Stop instance so create_image() doesn't restart it.
            self.aws.stop_instance_and_wait(instance.id)

            self.log.info("Creating AMI.")
            descr = self.config.get(ami_name, "description")
            if self.options.descr:
                descr += " | " + self.options.descr
            ami_id = self.aws.create_image(
                instance.id, ami_name + "-" + iso_timestamp(),
                descr)
            self.aws.create_tags([ami_id],
                {"Name": ami_name, "Type": DEFAULT_AMI_TYPE})

            return ami_id

        except RemoteCommandFailedError, e:
            self.log.exception("linaro-ami create:")
        except Exception, e:
            self.log.exception("linaro-ami create:")
        finally:
            if key_autocreate:
                self.aws.delete_key_pair(key_name)
            self.executor.close()
            if instance:
                self.aws.terminate_instance(instance.id)

    def command_list(self):
        """ List command. """
        self.aws.connect()
        print "Name ID Created Status"
        print "Description"
        print "-" * 30
        results = []
        for image in self.aws.list_images():
            if self.options.type and image.tags.get(
                    "Type") != self.options.type:
                continue
            results.append((image.tags.get("Name", "*" +
                                                  image.name),
                                   image.id,
                                   self.aws.get_image_creation_time(image),
                                   image.state,
                                   image.description))
        def ami_cmp(a, b):
            c = cmp(a[0], b[0])
            if c == 0:
                c = cmp(a[2], b[2])
            return c
        results.sort(ami_cmp)
        for r in results:
            print "%s %s %s %s" % r[0:4]
            print r[-1]
            print "-" * 30

    def command_delete(self):
        raise NotImplementedError