Joshua Cornutt 9 лет назад
Родитель
Сommit
1c921016ce

+ 40 - 84
cloudbridge/cloud/providers/aws/provider.py

@@ -4,8 +4,7 @@ Provider implementation based on boto library for AWS-compatible clouds.
 
 import os
 
-import boto
-from boto.ec2.regioninfo import RegionInfo
+import boto3
 try:
     # These are installed only for the case of a dev instance
     from httpretty import HTTPretty
@@ -13,7 +12,7 @@ try:
     from moto.s3 import mock_s3
 except ImportError:
     # TODO: Once library logging is configured, change this
-    print("[aws provider] moto library not available!")
+    print '[aws provider] moto library not available!'
 
 from cloudbridge.cloud.base import BaseCloudProvider
 from cloudbridge.cloud.interfaces import TestMockHelperMixin
@@ -24,9 +23,11 @@ from .services import AWSNetworkService
 from .services import AWSObjectStoreService
 from .services import AWSSecurityService
 
+# pylint: disable=R0902
 
-class AWSCloudProvider(BaseCloudProvider):
 
+class AWSCloudProvider(BaseCloudProvider):
+    '''AWS cloud provider interface'''
     PROVIDER_ID = 'aws'
 
     def __init__(self, config):
@@ -34,31 +35,29 @@ class AWSCloudProvider(BaseCloudProvider):
         self.cloud_type = 'aws'
 
         # Initialize cloud connection fields
-        self.a_key = self._get_config_value(
-            'aws_access_key', os.environ.get('AWS_ACCESS_KEY', None))
-        self.s_key = self._get_config_value(
-            'aws_secret_key', os.environ.get('AWS_SECRET_KEY', None))
-        # EC2 connection fields
-        self.ec2_is_secure = self._get_config_value('ec2_is_secure', True)
-        self.region_name = self._get_config_value(
-            'ec2_region_name', 'us-east-1')
-        self.region_endpoint = self._get_config_value(
-            'ec2_region_endpoint', 'ec2.us-east-1.amazonaws.com')
-        self.ec2_port = self._get_config_value('ec2_port', None)
-        self.ec2_conn_path = self._get_config_value('ec2_conn_path', '/')
-        self.ec2_validate_certs = self._get_config_value(
-            'ec2_validate_certs', False)
-        # S3 connection fields
-        self.s3_is_secure = self._get_config_value('s3_is_secure', True)
-        self.s3_host = self._get_config_value('s3_host', 's3.amazonaws.com')
-        self.s3_port = self._get_config_value('s3_port', None)
-        self.s3_conn_path = self._get_config_value('s3_conn_path', '/')
-        self.s3_validate_certs = self._get_config_value(
-            's3_validate_certs', False)
-
-        # service connections, lazily initialized
+        # These are passed as-is to Boto
+        self.session_cfg = {
+            'aws_access_key_id': self._get_config_value(
+                'aws_access_key', os.environ.get('AWS_ACCESS_KEY', None)),
+            'aws_secret_access_key': self._get_config_value(
+                'aws_secret_key', os.environ.get('AWS_SECRET_KEY', None)),
+            'region_name': self._get_config_value(
+                'ec2_region_name', 'us-east-1')
+        }
+        self.ec2_cfg = {
+            'service_name': 'ec2',
+            'use_ssl': self._get_config_value('ec2_is_secure', True),
+            'verify': self._get_config_value('ec2_validate_certs', True)
+        }
+        self.s3_cfg = {
+            'service_name': 's3',
+            'use_ssl': self._get_config_value('s3_is_secure', True),
+            'verify': self._get_config_value('s3_validate_certs', True)
+        }
+
+        # Service connections, lazily initialized
+        self._session = None
         self._ec2_conn = None
-        self._vpc_conn = None
         self._s3_conn = None
 
         # Initialize provider services
@@ -69,22 +68,20 @@ class AWSCloudProvider(BaseCloudProvider):
         self._object_store = AWSObjectStoreService(self)
 
     @property
-    def ec2_conn(self):
-        if not self._ec2_conn:
-            self._ec2_conn = self._connect_ec2()
-        return self._ec2_conn
+    def session(self):
+        '''Get a low-level session object or create one if needed'''
+        return self._session if self._session else \
+            boto3.session.Session(**self.session_cfg)
 
     @property
-    def vpc_conn(self):
-        if not self._vpc_conn:
-            self._vpc_conn = self._connect_vpc()
-        return self._vpc_conn
+    def ec2_conn(self):
+        '''Get an EC2 connection object or create one if needed'''
+        return self._ec2_conn if self._ec2_conn else self._connect_ec2()
 
     @property
     def s3_conn(self):
-        if not self._s3_conn:
-            self._s3_conn = self._connect_s3()
-        return self._s3_conn
+        '''Get an S3 connection object or create one if needed'''
+        return self._s3_conn if self._s3_conn else self._connect_s3()
 
     @property
     def compute(self):
@@ -107,53 +104,12 @@ class AWSCloudProvider(BaseCloudProvider):
         return self._object_store
 
     def _connect_ec2(self):
-        """
-        Get a boto ec2 connection object.
-        """
-        r = RegionInfo(name=self.region_name, endpoint=self.region_endpoint)
-        return self._conect_ec2_region(r)
-
-    def _conect_ec2_region(self, region):
-        ec2_conn = boto.connect_ec2(
-            aws_access_key_id=self.a_key,
-            aws_secret_access_key=self.s_key,
-            is_secure=self.ec2_is_secure,
-            region=region,
-            port=self.ec2_port,
-            path=self.ec2_conn_path,
-            validate_certs=self.ec2_validate_certs,
-            debug=2 if self.config.debug_mode else 0)
-        return ec2_conn
-
-    def _connect_vpc(self):
-        """
-        Get a boto VPC connection object.
-        """
-        r = RegionInfo(name=self.region_name, endpoint=self.region_endpoint)
-        vpc_conn = boto.connect_vpc(
-            aws_access_key_id=self.a_key,
-            aws_secret_access_key=self.s_key,
-            is_secure=self.ec2_is_secure,
-            region=r,
-            port=self.ec2_port,
-            path=self.ec2_conn_path,
-            validate_certs=self.ec2_validate_certs,
-            debug=2 if self.config.debug_mode else 0)
-        return vpc_conn
+        '''Get an EC2 resource object'''
+        return self.session.resource(**self.ec2_cfg)
 
     def _connect_s3(self):
-        """
-        Get a boto S3 connection object.
-        """
-        s3_conn = boto.connect_s3(aws_access_key_id=self.a_key,
-                                  aws_secret_access_key=self.s_key,
-                                  is_secure=self.s3_is_secure,
-                                  port=self.s3_port,
-                                  host=self.s3_host,
-                                  path=self.s3_conn_path,
-                                  validate_certs=self.s3_validate_certs,
-                                  debug=2 if self.config.debug_mode else 0)
-        return s3_conn
+        '''Get an S3 resource object'''
+        return self.session.resource(**self.s3_cfg)
 
 
 class MockAWSCloudProvider(AWSCloudProvider, TestMockHelperMixin):

+ 78 - 58
cloudbridge/cloud/providers/aws/resources.py

@@ -401,7 +401,10 @@ class AWSVolume(BaseVolume):
 
         .. note:: an instance must have a (case sensitive) tag ``Name``
         """
-        return self._volume.tags.get('Name')
+        for tag in self._volume.tags or list():
+            if tag.get('Key') == 'Name':
+                return tag.get('Value')
+        return None
 
     @name.setter
     # pylint:disable=arguments-differ
@@ -409,15 +412,18 @@ class AWSVolume(BaseVolume):
         """
         Set the volume name.
         """
-        self._volume.add_tag('Name', value)
+        self._volume.create_tags(Tags=[{'Key': 'Name', 'Value': value}])
 
     @property
     def description(self):
-        return self._volume.tags.get('Description')
+        for tag in self._volume.tags or list():
+            if tag.get('Key') == 'Description':
+                return tag.get('Value')
+        return None
 
     @description.setter
     def description(self, value):
-        self._volume.add_tag('Description', value)
+        self._volume.create_tags(Tags=[{'Key': 'Description', 'Value': value}])
 
     @property
     def size(self):
@@ -429,7 +435,7 @@ class AWSVolume(BaseVolume):
 
     @property
     def zone_id(self):
-        return self._volume.zone
+        return self._volume.availability_zone
 
     @property
     def source(self):
@@ -440,12 +446,12 @@ class AWSVolume(BaseVolume):
 
     @property
     def attachments(self):
-        if self._volume.attach_data and self._volume.attach_data.id:
-            return BaseAttachmentInfo(self,
-                                      self._volume.attach_data.instance_id,
-                                      self._volume.attach_data.device)
-        else:
-            return None
+        return [
+            BaseAttachmentInfo(self,
+                               x.InstanceId,
+                               x.Device)
+            for x in self._volume.attachments
+        ] if self._volume.attachments else None
 
     def attach(self, instance, device):
         """
@@ -454,13 +460,20 @@ class AWSVolume(BaseVolume):
         instance_id = instance.id if isinstance(
             instance,
             AWSInstance) else instance
-        self._volume.attach(instance_id, device)
+        self._volume.attach_to_instance(InstanceId=instance_id,
+                                        Device=device)
 
-    def detach(self, force=False):
+    def detach(self, instance, device, force=False):
         """
         Detach this volume from an instance.
         """
-        self._volume.detach()
+        instance_id = instance.id if isinstance(
+            instance,
+            AWSInstance) else instance
+        self._volume.detach_from_instance(
+            InstanceId=instance_id,
+            Device=device,
+            Force=force)
 
     def create_snapshot(self, name, description=None):
         """
@@ -469,7 +482,7 @@ class AWSVolume(BaseVolume):
         snap = AWSSnapshot(
             self._provider,
             self._volume.create_snapshot(
-                description=description))
+                Description=description))
         snap.name = name
         return snap
 
@@ -482,7 +495,7 @@ class AWSVolume(BaseVolume):
     @property
     def state(self):
         return AWSVolume.VOLUME_STATE_MAP.get(
-            self._volume.status, VolumeState.UNKNOWN)
+            self._volume.state, VolumeState.UNKNOWN)
 
     def refresh(self):
         """
@@ -490,7 +503,7 @@ class AWSVolume(BaseVolume):
         for its latest state.
         """
         try:
-            self._volume.update(validate=True)
+            self._volume.reload()
         except (EC2ResponseError, ValueError):
             # The volume no longer exists and cannot be refreshed.
             # set the status to unknown
@@ -608,10 +621,17 @@ class AWSSecurityGroup(BaseSecurityGroup):
     def __init__(self, provider, security_group):
         super(AWSSecurityGroup, self).__init__(provider, security_group)
 
+    @property
+    def name(self):
+        """
+        Return the name of this security group.
+        """
+        return self._security_group.group_name
+
     @property
     def rules(self):
         return [AWSSecurityGroupRule(self._provider, r, self)
-                for r in self._security_group.rules]
+                for r in self._security_group.ip_permissions]
 
     def add_rule(self, ip_protocol=None, from_port=None, to_port=None,
                  cidr_ip=None, src_group=None):
@@ -646,14 +666,14 @@ class AWSSecurityGroup(BaseSecurityGroup):
                 src_group = self._provider.security.security_groups.get(
                     src_group)
 
-            if self._security_group.authorize(
-                    ip_protocol=ip_protocol,
-                    from_port=from_port,
-                    to_port=to_port,
-                    cidr_ip=cidr_ip,
+            if self._security_group.authorize_ingress(
+                    IpProtocol=ip_protocol,
+                    FromPort=from_port,
+                    ToPort=to_port,
+                    CidrIp=cidr_ip,
                     # pylint:disable=protected-access
-                    src_group=src_group._security_group if src_group
-                    else None):
+                    SourceSecurityGroupName=src_group._security_group.group_name
+                    if src_group else None):
                 return self.get_rule(ip_protocol, from_port, to_port, cidr_ip,
                                      src_group)
         except EC2ResponseError as ec2e:
@@ -666,13 +686,15 @@ class AWSSecurityGroup(BaseSecurityGroup):
 
     def get_rule(self, ip_protocol=None, from_port=None, to_port=None,
                  cidr_ip=None, src_group=None):
-        for rule in self._security_group.rules:
-            if (rule.ip_protocol == ip_protocol and
-               rule.from_port == from_port and
-               rule.to_port == to_port and
-               rule.grants[0].cidr_ip == cidr_ip) or \
-               (rule.grants[0].name == src_group.name if src_group and
-               hasattr(rule.grants[0], 'name') else False):
+        for rule in self._security_group.ip_permissions:
+            if (rule['IpProtocol'] == ip_protocol and
+                    rule['IpProtocol'] == from_port and
+                    rule['FromPort'] == to_port and
+                    rule['IpRanges'][0]['CidrIp'] == cidr_ip) or \
+                    (rule['UserIdGroupPairs'][0]['GroupName'] == src_group.name
+                     if src_group and
+                     hasattr(rule['UserIdGroupPairs'][0], 'GroupName')
+                     else False):
                 return AWSSecurityGroupRule(self._provider, rule, self)
         return None
 
@@ -697,58 +719,56 @@ class AWSSecurityGroupRule(BaseSecurityGroupRule):
         md5 = hashlib.md5()
         md5.update("{0}-{1}-{2}-{3}".format(
             self.ip_protocol, self.from_port, self.to_port, self.cidr_ip)
-            .encode('ascii'))
+                   .encode('ascii'))
         return md5.hexdigest()
 
     @property
     def ip_protocol(self):
-        return self._rule.ip_protocol
+        return self._rule.get('IpProtocol')
 
     @property
     def from_port(self):
-        if str(self._rule.from_port).isdigit():
-            return int(self._rule.from_port)
-        return 0
+        return self._rule.get('FromPort', 0)
 
     @property
     def to_port(self):
-        if str(self._rule.to_port).isdigit():
-            return int(self._rule.to_port)
-        return 0
+        return self._rule.get('ToPort', 0)
 
     @property
     def cidr_ip(self):
-        if len(self._rule.grants) > 0:
-            return self._rule.grants[0].cidr_ip
+        if len(self._rule.IpRanges) > 0:
+            return self._rule['IpRanges'][0].get('CidrIp')
         return None
 
     @property
     def group(self):
-        if len(self._rule.grants) > 0:
-            if self._rule.grants[0].name:
-                cg = self._provider.ec2_conn.get_all_security_groups(
-                    groupnames=[self._rule.grants[0].name])[0]
-                return AWSSecurityGroup(self._provider, cg)
+        if len(self._rule['UserIdGroupPairs']) > 0:
+            if self._rule['UserIdGroupPairs'][0]['GroupId']:
+                return AWSSecurityGroup(
+                    self._provider,
+                    self._provider.ec2_conn.SecurityGroup(
+                        self._rule['UserIdGroupPairs'][0]['GroupId']))
         return None
 
     def to_json(self):
-        attr = inspect.getmembers(self, lambda a: not(inspect.isroutine(a)))
-        js = {k: v for(k, v) in attr if not k.startswith('_')}
-        js['group'] = self.group.id if self.group else ''
-        js['parent'] = self.parent.id if self.parent else ''
-        return json.dumps(js, sort_keys=True)
+        attr = inspect.getmembers(self, lambda a: not inspect.isroutine(a))
+        _js = {k: v for(k, v) in attr if not k.startswith('_')}
+        _js['group'] = self.group.id if self.group else ''
+        _js['parent'] = self.parent.id if self.parent else ''
+        return json.dumps(_js, sort_keys=True)
 
     def delete(self):
         if self.group:
             # pylint:disable=protected-access
-            self.parent._security_group.revoke(
-                src_group=self.group._security_group)
+            self.parent._security_group.revoke_ingress(
+                SourceSecurityGroupName=self.group._security_group.group_name)
         else:
             # pylint:disable=protected-access
-            self.parent._security_group.revoke(self.ip_protocol,
-                                               self.from_port,
-                                               self.to_port,
-                                               self.cidr_ip)
+            self.parent._security_group.revoke_ingress(
+                IpProtocol=self.ip_protocol,
+                FromPort=self.from_port,
+                ToPort=self.to_port,
+                CidrIp=self.cidr_ip)
 
 
 class AWSBucketObject(BaseBucketObject):

+ 153 - 158
cloudbridge/cloud/providers/aws/services.py

@@ -4,7 +4,7 @@ import string
 
 from boto.ec2.blockdevicemapping import BlockDeviceMapping
 from boto.ec2.blockdevicemapping import BlockDeviceType
-from boto.exception import EC2ResponseError
+from botocore.exceptions import ClientError as EC2ResponseError
 
 from cloudbridge.cloud.base.resources import ClientPagedResultList
 from cloudbridge.cloud.base.resources import ServerPagedResultList
@@ -33,6 +33,8 @@ from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import Volume
 
+import cloudbridge as cb
+
 import requests
 
 from .resources import AWSBucket
@@ -50,10 +52,101 @@ from .resources import AWSSnapshot
 from .resources import AWSSubnet
 from .resources import AWSVolume
 
-import cloudbridge as cb
 # Uncomment to enable logging by default for this module
 # cb.set_stream_logger(__name__)
 
+
+class EC2ServiceFilter(object):
+    '''
+        Generic AWS EC2 service filter interface
+
+    :param AWSCloudProvider provider: AWS EC2 provider interface
+    :param str service: Name of the EC2 service to use
+    :param BaseCloudResource cb_iface: CloudBridge class to use
+    '''
+    def __init__(self, provider, service, cb_iface):
+        self.provider = provider
+        self.service = getattr(self.provider.ec2_conn, service)
+        self.iface = cb_iface
+
+    def get(self, val, filter_name, wrapper=True):
+        '''
+            Returns a single resource by filter
+
+        :param str val: Value to filter with
+        :param str filter_name: Name of the filter to use
+        :param bool wrapper: If True, wraps the resulting Boto
+            object in a CloudBridge object
+        :returns: Boto resource object or CloudBridge object or None
+        '''
+        try:
+            objs = self.service.filter(Filter=[{
+                'Name': filter_name,
+                'Values': [val]
+            }]).limit(1)
+            obj = objs[0] if objs else None
+            if wrapper:
+                return self.iface(self.provider, obj) if obj else None
+            return obj
+        except EC2ResponseError:
+            return None
+
+    def list(self, limit=None, marker=None):
+        '''Returns a list of resources'''
+        try:
+            objs = [self.iface(self.provider, obj)
+                    for obj in self.service.all()]
+        except EC2ResponseError:
+            objs = list()
+        return ClientPagedResultList(self.provider, objs,
+                                     limit=limit, marker=marker)
+
+    def find(self, val, filter_name, limit=None, marker=None):
+        '''
+            Returns a list of resources by filter
+
+        :param str val: Value to filter with
+        :param str filter_name: Name of the filter to use
+        '''
+        try:
+            objs = self.service.filter(Filter=[{
+                'Name': filter_name,
+                'Values': [val]
+            }])
+        except EC2ResponseError:
+            objs = list()
+        return ClientPagedResultList(self.provider, objs,
+                                     limit=limit, marker=marker)
+
+    def create(self, method, **kwargs):
+        '''
+            Creates a resource
+
+        :param str method: Service method to invoke
+        :param object kwargs: Arguments to be passed as-is to
+            the service method
+        '''
+        res = getattr(self.provider.ec2_conn, method)(**kwargs)
+        return self.iface(self.provider, res) if res else None
+
+    def delete(self, val, filter_name):
+        '''
+            Deletes a resource by filter
+
+        :param str val: Value to filter with
+        :param str filter_name: Name of the filter to use
+        :returns: False on error, True if the resource
+            does not exist or was deleted successfully
+        '''
+        res = self.iface.get(val, filter_name, wrapper=False)
+        if res:
+            try:
+                res.delete()
+            except EC2ResponseError:
+                return False
+        return True
+
+
 class AWSSecurityService(BaseSecurityService):
 
     def __init__(self, provider):
@@ -88,147 +181,57 @@ class AWSKeyPairService(BaseKeyPairService):
 
     def __init__(self, provider):
         super(AWSKeyPairService, self).__init__(provider)
+        self.iface = EC2ServiceFilter(self.provider, 'key_pairs', AWSKeyPair)
 
-    def get(self, key_pair_id):
-        """
-        Returns a KeyPair given its ID.
-        """
-        try:
-            kps = self.provider.ec2_conn.get_all_key_pairs(
-                keynames=[key_pair_id])
-            return AWSKeyPair(self.provider, kps[0])
-        except EC2ResponseError:
-            return None
+    def get(self, name):
+        """Returns a key pair given its name"""
+        return self.iface.get(name, 'key-name')
 
     def list(self, limit=None, marker=None):
-        """
-        List all key pairs associated with this account.
-
-        :rtype: ``list`` of :class:`.KeyPair`
-        :return:  list of KeyPair objects
-        """
-        cb.log.trace("Listing AWS key pairs.")
-        key_pairs = [AWSKeyPair(self.provider, kp)
-                     for kp in self.provider.ec2_conn.get_all_key_pairs()]
-        return ClientPagedResultList(self.provider, key_pairs,
-                                     limit=limit, marker=marker)
+        """List all key pairs associated with this account"""
+        return self.iface.list(limit=limit, marker=marker)
 
     def find(self, name, limit=None, marker=None):
-        """
-        Searches for a key pair by a given list of attributes.
-        """
-        try:
-            key_pairs = [
-                AWSKeyPair(self.provider, kp) for kp in
-                self.provider.ec2_conn.get_all_key_pairs(keynames=[name])]
-        except EC2ResponseError:
-            key_pairs = []
-        return ClientPagedResultList(self.provider, key_pairs,
-                                     limit=limit, marker=marker)
+        """Searches for a key pair by name"""
+        return self.iface.find(name, 'key-name', limit=limit, marker=marker)
 
     def create(self, name):
-        """
-        Create a new key pair or raise an exception if one already exists.
-
-        :type name: str
-        :param name: The name of the key pair to be created.
+        """Creates a new key pair"""
+        return self.iface.create('create_key_pair', KeyName=name)
 
-        :rtype: ``object`` of :class:`.KeyPair`
-        :return:  A key pair instance or ``None`` if one was not be created.
-        """
-        kp = self.provider.ec2_conn.create_key_pair(name)
-        if kp:
-            return AWSKeyPair(self.provider, kp)
-        return None
+    def delete(self, name):
+        """Deletes a key pair by name"""
+        return self.iface.delete(name, 'key-name')
 
 
 class AWSSecurityGroupService(BaseSecurityGroupService):
 
     def __init__(self, provider):
         super(AWSSecurityGroupService, self).__init__(provider)
+        self.iface = EC2ServiceFilter(self.provider,
+                                      'security_groups', AWSSecurityGroup)
 
-    def get(self, sg_id):
-        """
-        Returns a SecurityGroup given its id.
-        """
-        try:
-            sgs = self.provider.ec2_conn.get_all_security_groups(
-                group_ids=[sg_id])
-            return AWSSecurityGroup(self.provider, sgs[0]) if sgs else None
-        except EC2ResponseError:
-            return None
+    def get(self, gid):
+        """Returns a security group given its ID"""
+        return self.iface.get(gid, 'group-id')
 
     def list(self, limit=None, marker=None):
-        """
-        List all security groups associated with this account.
-
-        :rtype: ``list`` of :class:`.SecurityGroup`
-        :return:  list of SecurityGroup objects
-        """
-        sgs = [AWSSecurityGroup(self.provider, sg)
-               for sg in self.provider.ec2_conn.get_all_security_groups()]
-
-        return ClientPagedResultList(self.provider, sgs,
-                                     limit=limit, marker=marker)
-
-    def create(self, name, description, network_id=None):
-        """
-        Create a new SecurityGroup.
-
-        :type name: str
-        :param name: The name of the new security group.
-
-        :type description: str
-        :param description: The description of the new security group.
-
-        :type  network_id: ``str``
-        :param network_id: The ID of the VPC to create the security group in,
-                           if any.
-
-        :rtype: ``object`` of :class:`.SecurityGroup`
-        :return:  A SecurityGroup instance or ``None`` if one was not created.
-        """
-        sg = self.provider.ec2_conn.create_security_group(name, description,
-                                                          network_id)
-        if sg:
-            return AWSSecurityGroup(self.provider, sg)
-        return None
+        """List all security groups associated with this account"""
+        return self.iface.list(limit=limit, marker=marker)
 
     def find(self, name, limit=None, marker=None):
-        """
-        Get all security groups associated with your account.
-        """
-        try:
-            flters = {'group-name': name}
-            security_groups = self.provider.ec2_conn.get_all_security_groups(
-                filters=flters)
-        except EC2ResponseError:
-            security_groups = []
-        return [AWSSecurityGroup(self.provider, sg) for sg in security_groups]
+        """Searches for a security group by name"""
+        return self.iface.find(name, 'group-name', limit=limit, marker=marker)
 
-    def delete(self, group_id):
-        """
-        Delete an existing SecurityGroup.
-
-        :type group_id: str
-        :param group_id: The security group ID to be deleted.
+    def create(self, name, description, network_id=None):
+        """Creates a security group pair"""
+        return self.iface.create('create_security_group',
+                                 GroupName=name,
+                                 Description=description)
 
-        :rtype: ``bool``
-        :return:  ``True`` if the security group does not exist, ``False``
-                  otherwise. Note that this implies that the group may not have
-                  been deleted by this method but instead has not existed in
-                  the first place.
-        """
-        try:
-            for sg in self.provider.ec2_conn.get_all_security_groups(
-                    group_ids=[group_id]):
-                try:
-                    sg.delete()
-                except EC2ResponseError:
-                    return False
-        except EC2ResponseError:
-            pass
-        return True
+    def delete(self, name):
+        """Deletes a security group by name"""
+        return self.iface.delete(name, 'group-name')
 
 
 class AWSBlockStoreService(BaseBlockStoreService):
@@ -253,50 +256,42 @@ class AWSVolumeService(BaseVolumeService):
 
     def __init__(self, provider):
         super(AWSVolumeService, self).__init__(provider)
+        self.iface = EC2ServiceFilter(self.provider,
+                                      'volumes', AWSVolume)
 
-    def get(self, volume_id):
-        """
-        Returns a volume given its id.
-        """
-        vols = self.provider.ec2_conn.get_all_volumes(volume_ids=[volume_id])
-        return AWSVolume(self.provider, vols[0]) if vols else None
-
-    def find(self, name, limit=None, marker=None):
-        """
-        Searches for a volume by a given list of attributes.
-        """
-        filtr = {'tag:Name': name}
-        aws_vols = self.provider.ec2_conn.get_all_volumes(filters=filtr)
-        cb_vols = [AWSVolume(self.provider, vol) for vol in aws_vols]
-        return ClientPagedResultList(self.provider, cb_vols,
-                                     limit=limit, marker=marker)
+    def get(self, vid):
+        """Returns a volume given its ID"""
+        return self.iface.get(vid, 'volume-id')
 
     def list(self, limit=None, marker=None):
-        """
-        List all volumes.
-        """
-        aws_vols = self.provider.ec2_conn.get_all_volumes()
-        cb_vols = [AWSVolume(self.provider, vol) for vol in aws_vols]
-        return ClientPagedResultList(self.provider, cb_vols,
-                                     limit=limit, marker=marker)
+        """List all volumes associated with this account"""
+        return self.iface.list(limit=limit, marker=marker)
+
+    def find(self, name, limit=None, marker=None):
+        """Searches for a volume by name"""
+        return self.iface.find(name, 'tag:Name', limit=limit, marker=marker)
 
     def create(self, name, size, zone, snapshot=None, description=None):
-        """
-        Creates a new volume.
-        """
+        """Creates a volume"""
         zone_id = zone.id if isinstance(zone, PlacementZone) else zone
         snapshot_id = snapshot.id if isinstance(
             snapshot, AWSSnapshot) and snapshot else snapshot
-
-        ec2_vol = self.provider.ec2_conn.create_volume(
-            size,
-            zone_id,
-            snapshot=snapshot_id)
-        cb_vol = AWSVolume(self.provider, ec2_vol)
-        cb_vol.name = name
-        if description:
-            cb_vol.description = description
-        return cb_vol
+        res = self.iface.create('create_volume',
+                                Size=size,
+                                AvailabilityZone=zone_id,
+                                SnapshotId=snapshot_id)
+        res.create_tags(Tags=[{
+            'Key': 'Name',
+            'Value': name
+        }, {
+            'Key': 'Description',
+            'Value': description
+        }])
+        return res
+
+    def delete(self, name):
+        """Deletes a volume by name"""
+        return self.iface.delete(name, 'tag:Name')
 
 
 class AWSSnapshotService(BaseSnapshotService):