# 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