summaryrefslogtreecommitdiff
path: root/tests/cloud_tests/platforms/ec2/instance.py
blob: d2e84047b0d8c24f4144912f1425d5914d2cbc74 (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
# This file is part of cloud-init. See LICENSE file for license information.

"""Base EC2 instance."""
import os

import botocore

from ..instances import Instance
from tests.cloud_tests import LOG, util


class EC2Instance(Instance):
    """EC2 backed instance."""

    platform_name = "ec2"
    _ssh_client = None

    def __init__(self, platform, properties, config, features,
                 image_ami, user_data=None):
        """Set up instance.

        @param platform: platform object
        @param properties: dictionary of properties
        @param config: dictionary of configuration values
        @param features: dictionary of supported feature flags
        @param image_ami: AWS AMI ID for image to use
        @param user_data: test user-data to pass to instance
        """
        super(EC2Instance, self).__init__(
            platform, image_ami, properties, config, features)

        self.image_ami = image_ami
        self.instance = None
        self.user_data = user_data
        self.ssh_ip = None
        self.ssh_port = 22
        self.ssh_key_file = os.path.join(
            platform.config['data_dir'], platform.config['private_key'])
        self.ssh_pubkey_file = os.path.join(
            platform.config['data_dir'], platform.config['public_key'])

    def console_log(self):
        """Collect console log from instance.

        The console log is buffered and not always present, therefore
        may return empty string.
        """
        try:
            # OutputBytes comes from platform._decode_console_output_as_bytes
            response = self.instance.console_output()
            return response['OutputBytes']
        except KeyError as e:
            if 'Output' in response:
                msg = ("'OutputBytes' did not exist in console_output() but "
                       "'Output' did: %s..." % response['Output'][0:128])
                raise util.PlatformError('console_log', msg) from e
            return ('No Console Output [%s]' % self.instance).encode()

    def destroy(self):
        """Clean up instance."""
        if self.instance:
            LOG.debug('destroying instance %s', self.instance.id)
            self.instance.terminate()
            self.instance.wait_until_terminated()

        self._ssh_close()

        super(EC2Instance, self).destroy()

    def _execute(self, command, stdin=None, env=None):
        """Execute command on instance."""
        env_args = []
        if env:
            env_args = ['env'] + ["%s=%s" for k, v in env.items()]

        return self._ssh(['sudo'] + env_args + list(command), stdin=stdin)

    def start(self, wait=True, wait_for_cloud_init=False):
        """Start instance on EC2 with the platfrom's VPC."""
        if self.instance:
            if self.instance.state['Name'] == 'running':
                return

            LOG.debug('starting instance %s', self.instance.id)
            self.instance.start()
        else:
            LOG.debug('launching instance')

            args = {
                'ImageId': self.image_ami,
                'InstanceType': self.platform.instance_type,
                'KeyName': self.platform.key_name,
                'MaxCount': 1,
                'MinCount': 1,
                'SecurityGroupIds': [self.platform.security_group.id],
                'SubnetId': self.platform.subnet.id,
                'TagSpecifications': [{
                    'ResourceType': 'instance',
                    'Tags': [{
                        'Key': 'Name', 'Value': self.platform.tag
                    }]
                }],
            }

            if self.user_data:
                args['UserData'] = self.user_data

            try:
                instances = self.platform.ec2_resource.create_instances(**args)
            except botocore.exceptions.ClientError as error:
                error_msg = error.response['Error']['Message']
                raise util.PlatformError('start', error_msg)

            self.instance = instances[0]

        LOG.debug('instance id: %s', self.instance.id)
        if wait:
            self.instance.wait_until_running()
            self.instance.reload()
            self.ssh_ip = self.instance.public_ip_address
            self._wait_for_system(wait_for_cloud_init)

    def shutdown(self, wait=True):
        """Shutdown instance."""
        LOG.debug('stopping instance %s', self.instance.id)
        self.instance.stop()

        if wait:
            self.instance.wait_until_stopped()
            self.instance.reload()

# vi: ts=4 expandtab