Skip to content

Commit

Permalink
Merge pull request #155 from SUSE-Enceladus/ec2-boto3
Browse files Browse the repository at this point in the history
Back to boto3
  • Loading branch information
rjschwei authored Dec 10, 2018
2 parents 33d251b + fef9312 commit 089247b
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 179 deletions.
196 changes: 115 additions & 81 deletions ipa/ipa_ec2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

"""Provider module for testing AWS EC2 images."""

# Copyright (c) 2017 SUSE LLC
# Copyright (c) 2018 SUSE LLC
#
# This file is part of ipa. Ipa provides an api and command line
# utilities for testing images in the Public Cloud.
Expand All @@ -20,6 +20,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import boto3

from ipa import ipa_utils
from ipa.ipa_constants import (
BASH_SSH_SCRIPT,
Expand All @@ -28,14 +30,10 @@
EC2_DEFAULT_USER
)
from ipa.ipa_exceptions import IpaException, EC2ProviderException
from ipa.ipa_libcloud import LibcloudProvider

from libcloud.common.exceptions import BaseHTTPError
from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver
from ipa.ipa_provider import IpaProvider


class EC2Provider(LibcloudProvider):
class EC2Provider(IpaProvider):
"""Provider class for testing AWS EC2 images."""

def __init__(self,
Expand Down Expand Up @@ -128,7 +126,9 @@ def __init__(self,
)
self.security_group_id = (
security_group_id or
self._get_value('security_group_id')
self._get_value(
security_group_id, config_key='security_group_id'
)
)
self.ssh_key_name = (
ssh_key_name or
Expand All @@ -153,26 +153,23 @@ def __init__(self,
'SSH private key file is required to connect to instance.'
)

self.compute_driver = self._get_driver()

def _get_driver(self):
"""Get authenticated EC2 driver."""
if not self.access_key_id:
raise EC2ProviderException(
'Access key id is required to authenticate EC2 driver.'
def _connect(self):
"""Connect to ec2 resource."""
resource = None
try:
resource = boto3.resource(
'ec2',
aws_access_key_id=self.access_key_id,
aws_secret_access_key=self.secret_access_key,
region_name=self.region
)

if not self.secret_access_key:
# boto3 resource is lazy so attempt method to test connection
resource.meta.client.describe_account_attributes()
except Exception:
raise EC2ProviderException(
'Secret access key is required to authenticate EC2 driver.'
'Could not connect to region: %s' % self.region
)

ComputeEngine = get_driver(Provider.EC2)
return ComputeEngine(
self.access_key_id,
self.secret_access_key,
region=self.region
)
return resource

def _get_from_ec2_config(self, entry):
"""Get config entry from ec2utils config file."""
Expand All @@ -186,65 +183,40 @@ def _get_from_ec2_config(self, entry):
else:
return None

def _get_image(self):
"""Retrieve NodeImage given the image id."""
try:
image = self.compute_driver.list_images(
ex_image_ids=[self.image_id]
)[0]
except (IndexError, BaseHTTPError):
raise EC2ProviderException(
'Image with ID: {image_id} not found.'.format(
image_id=self.image_id
)
)

return image

def _get_instance(self):
"""Retrieve instance matching instance_id."""
resource = self._connect()

try:
instance = self.compute_driver.list_nodes(
ex_node_ids=[self.running_instance_id]
)[0]
except (IndexError, BaseHTTPError):
instance = resource.Instance(self.running_instance_id)
except Exception:
raise EC2ProviderException(
'Instance with ID: {instance_id} not found.'.format(
instance_id=self.running_instance_id
)
)
return instance

def _get_instance_size(self):
"""Retrieve NodeSize given the instance type."""
instance_type = self.instance_type or EC2_DEFAULT_TYPE

try:
sizes = self.compute_driver.list_sizes()
size = [size for size in sizes if size.id == instance_type][0]
except IndexError:
raise EC2ProviderException(
'Instance type: {instance_type} not found.'.format(
instance_type=instance_type
)
)

return size
def _get_instance_state(self):
"""
Attempt to retrieve the state of the instance.
Raises:
EC2ProviderException: If the instance cannot be found.
"""
instance = self._get_instance()
state = None

def _get_subnet(self, subnet_id):
subnet = None
try:
subnet = self.compute_driver.ex_list_subnets(
subnet_ids=[subnet_id]
)[0]
state = instance.state['Name']
except Exception:
raise EC2ProviderException(
'EC2 subnet: {subnet_id} not found.'.format(
subnet_id=subnet_id
'Instance with id: {instance_id}, '
'cannot be found.'.format(
instance_id=self.running_instance_id
)
)

return subnet
return state

def _get_user_data(self):
"""
Expand All @@ -259,34 +231,96 @@ def _get_user_data(self):
script = BASH_SSH_SCRIPT.format(user=self.ssh_user, key=key)
return script

def _is_instance_running(self):
"""
Return True if instance is in running state.
"""
return self._get_instance_state() == 'running'

def _launch_instance(self):
"""Launch an instance of the given image."""
resource = self._connect()

instance_name = ipa_utils.generate_instance_name('ec2-ipa-test')
kwargs = {
'name': ipa_utils.generate_instance_name('ec2-ipa-test'),
'size': self._get_instance_size(),
'image': self._get_image()
'InstanceType': self.instance_type or EC2_DEFAULT_TYPE,
'ImageId': self.image_id,
'MaxCount': 1,
'MinCount': 1,
'TagSpecifications': [
{
'ResourceType': 'instance',
'Tags': [
{
'Key': 'Name',
'Value': instance_name
}
]
}
]
}

if self.ssh_key_name:
kwargs['ex_keyname'] = self.ssh_key_name
kwargs['KeyName'] = self.ssh_key_name
else:
kwargs['ex_userdata'] = self._get_user_data()
kwargs['UserData'] = self._get_user_data()

if self.subnet_id:
kwargs['ex_subnet'] = self._get_subnet(self.subnet_id)
kwargs['SubnetId'] = self.subnet_id

if self.security_group_id:
kwargs['ex_security_group_ids'] = [self.security_group_id]
kwargs['SecurityGroupIds'] = [self.security_group_id]

instance = self.compute_driver.create_node(**kwargs)
try:
instances = resource.create_instances(**kwargs)
except Exception as error:
raise EC2ProviderException(
'Unable to create instance: {0}.'.format(error)
)

self.compute_driver.wait_until_running(
[instance],
timeout=self.timeout
)
self.running_instance_id = instance.id
instances[0].wait_until_running()
self.running_instance_id = instances[0].instance_id

def _set_image_id(self):
"""If existing image used get image id."""
instance = self._get_instance()
self.image_id = instance.extra['image_id']
self.image_id = instance.image_id

def _set_instance_ip(self):
"""
Retrieve instance ip and cache it.
Current preference is for public ipv4, ipv6 and private.
"""
instance = self._get_instance()

# ipv6
try:
ipv6 = instance.network_interfaces[0].ipv6_addresses[0]
except IndexError:
ipv6 = None

self.instance_ip = instance.public_ip_address or \
ipv6 or instance.private_ip_address

if not self.instance_ip:
raise EC2ProviderException(
'IP address for instance cannot be found.'
)

def _start_instance(self):
"""Start the instance."""
instance = self._get_instance()
instance.start()
instance.wait_until_running()

def _stop_instance(self):
"""Stop the instance."""
instance = self._get_instance()
instance.stop()
instance.wait_until_stopped()

def _terminate_instance(self):
"""Terminate the instance."""
instance = self._get_instance()
instance.terminate()
2 changes: 2 additions & 0 deletions package/python3-ipa.spec
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ Source: https://files.pythonhosted.org/packages/source/p/python3-ipa/%{n
BuildRequires: python3-devel
BuildRequires: python3-setuptools
Requires: python3-PyYAML
Requires: python3-boto3
Requires: python3-apache-libcloud
Requires: python3-azure-common
Requires: python3-azure-mgmt-compute
Expand All @@ -43,6 +44,7 @@ Requires: python3-testinfra
BuildArch: noarch
%if %{with test}
BuildRequires: python3-PyYAML
BuildRequires: python3-boto3
BuildRequires: python3-apache-libcloud
BuildRequires: python3-azure-common
BuildRequires: python3-azure-mgmt-compute
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
boto3
apache-libcloud
azure-common
azure-mgmt-compute
Expand Down
Loading

0 comments on commit 089247b

Please sign in to comment.