Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Back to boto3 #155

Merged
merged 1 commit into from
Dec 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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