Ver código fonte

Added iteration support for openstack objects.

nuwan_ag 10 anos atrás
pai
commit
40b4aab199

+ 40 - 12
cloudbridge/cloud/base.py

@@ -20,6 +20,7 @@ from cloudbridge.cloud.interfaces.resources import LaunchConfig
 from cloudbridge.cloud.interfaces.resources import MachineImage
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import ObjectLifeCycleMixin
+from cloudbridge.cloud.interfaces.resources import PageableObjectMixin
 from cloudbridge.cloud.interfaces.resources import Region
 from cloudbridge.cloud.interfaces.resources import ResultList
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
@@ -46,11 +47,30 @@ from cloudbridge.cloud.interfaces.services import VolumeService
 
 log = logging.getLogger(__name__)
 
+DEFAULT_RESULT_LIMIT = 50
+
+
+class BaseConfiguration(dict):
+
+    def __init__(self, user_config):
+        self.update(user_config)
+
+    @property
+    def result_limit(self):
+        """
+        Get the maximum number of results to return for a
+        list method
+
+        :rtype: ``int``
+        :return: The maximum number of results to return
+        """
+        return self.get('result_limit', DEFAULT_RESULT_LIMIT)
+
 
 class BaseCloudProvider(CloudProvider):
 
     def __init__(self, config):
-        self._config = config
+        self._config = BaseConfiguration(config)
 
     @property
     def config(self):
@@ -187,7 +207,7 @@ class BaseResultList(ResultList):
         return self._total
 
 
-class BaseIterableObjectMixin():
+class BasePageableObjectMixin(PageableObjectMixin):
     """
     A mixin to provide iteration capability for a class
     that support a list(limit, marker) method.
@@ -506,7 +526,7 @@ class BaseContainerObject(ContainerObject):
                                       self.name)
 
 
-class BaseContainer(Container):
+class BaseContainer(BasePageableObjectMixin, Container):
 
     def __repr__(self):
         return "<CB-{0}: {1}>".format(self.__class__.__name__,
@@ -529,13 +549,15 @@ class BaseComputeService(ComputeService, BaseProviderService):
         super(BaseComputeService, self).__init__(provider)
 
 
-class BaseVolumeService(VolumeService, BaseProviderService):
+class BaseVolumeService(
+        BasePageableObjectMixin, VolumeService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseVolumeService, self).__init__(provider)
 
 
-class BaseSnapshotService(SnapshotService, BaseProviderService):
+class BaseSnapshotService(
+        BasePageableObjectMixin, SnapshotService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseSnapshotService, self).__init__(provider)
@@ -547,13 +569,15 @@ class BaseBlockStoreService(BlockStoreService, BaseProviderService):
         super(BaseBlockStoreService, self).__init__(provider)
 
 
-class BaseImageService(ImageService, BaseProviderService):
+class BaseImageService(
+        BasePageableObjectMixin, ImageService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseImageService, self).__init__(provider)
 
 
-class BaseObjectStoreService(ObjectStoreService, BaseProviderService):
+class BaseObjectStoreService(
+        BasePageableObjectMixin, ObjectStoreService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseObjectStoreService, self).__init__(provider)
@@ -565,7 +589,8 @@ class BaseSecurityService(SecurityService, BaseProviderService):
         super(BaseSecurityService, self).__init__(provider)
 
 
-class BaseKeyPairService(KeyPairService, BaseProviderService):
+class BaseKeyPairService(
+        BasePageableObjectMixin, KeyPairService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseKeyPairService, self).__init__(provider)
@@ -588,13 +613,15 @@ class BaseKeyPairService(KeyPairService, BaseProviderService):
         return True
 
 
-class BaseSecurityGroupService(SecurityGroupService, BaseProviderService):
+class BaseSecurityGroupService(
+        BasePageableObjectMixin, SecurityGroupService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseSecurityGroupService, self).__init__(provider)
 
 
-class BaseInstanceTypesService(InstanceTypesService, BaseProviderService):
+class BaseInstanceTypesService(
+        BasePageableObjectMixin, InstanceTypesService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseInstanceTypesService, self).__init__(provider)
@@ -608,13 +635,14 @@ class BaseInstanceTypesService(InstanceTypesService, BaseProviderService):
 
 
 class BaseInstanceService(
-        BaseIterableObjectMixin, InstanceService, BaseProviderService):
+        BasePageableObjectMixin, InstanceService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseInstanceService, self).__init__(provider)
 
 
-class BaseRegionService(RegionService, BaseProviderService):
+class BaseRegionService(
+        BasePageableObjectMixin, RegionService, BaseProviderService):
 
     def __init__(self, provider):
         super(BaseRegionService, self).__init__(provider)

+ 83 - 2
cloudbridge/cloud/interfaces/resources.py

@@ -52,6 +52,23 @@ class InvalidConfigurationException(CloudBridgeBaseException):
     pass
 
 
+class Configuration(dict):
+    """
+    Represents a cloudbridge configuration object
+    """
+
+    @abstractproperty
+    def result_limit(self):
+        """
+        Get the maximum number of results to return for a
+        list method
+
+        :rtype: ``int``
+        :return: The maximum number of results to return
+        """
+        pass
+
+
 class ObjectLifeCycleMixin(object):
 
     """
@@ -112,6 +129,70 @@ class ObjectLifeCycleMixin(object):
         pass
 
 
+class PageableObjectMixin(object):
+    """
+    A marker interface for objects which support paged iteration through
+    a list of objects with a list(limit, marker) method.
+    """
+
+    @abstractmethod
+    def __iter__(self):
+        """
+        Enables iteration through this object. Typically, an implementation
+        will call the list(limit, marker) method to transparently page
+        additional objects in as iteration progresses.
+        """
+        pass
+
+    @abstractmethod
+    def list(self, limit=None, marker=None):
+        """
+        Returns a list of objects up to a maximum limit.
+
+        If a limit and marker are specified, the records will be fetched up to
+        the limit starting from the marker onwards. The returned list is a list
+        of class ResultList, which has extra properties like is_truncated,
+        supports_total and total_records to provide extra information
+        about record availability.
+
+        If limit is not specified, the limit will default to the underlying
+        provider's default limit. Therefore, you need to check the is_truncated
+        property to determine whether more records are available.
+
+        The total number of results can be determined through the total_results
+        property. Not all provides will support returning the total_results
+        property, so the supports_total property can be used to determine
+        whether a total is supported.
+
+        To iterate through all the records, it will be easier to iterate
+        directly through the instances using __iter__ instead of calling
+        the list method. The __iter__ method will automatically call the list
+        method to fetch a batch of records at a time.
+
+        example::
+
+            # get first page of results
+            instlist = provider.compute.instances.list(limit=50)
+            for instance in instlist:
+                print("Instance Data: {0}", instance)
+            if instlist.supports_total:
+                print("Total results: {0}".format(instlist.total_results))
+            else:
+                print("Total records unknown,"
+                      "but has more data?: {0}".format(instlist.is_truncated))
+
+            # Page to next set of results
+            if (instlist.is_truncated)
+                instlist = provider.compute.instances.list(limit=100,
+                                                           marker=instlist.marker)
+
+            # Alternative: iterate through every available record
+            for instance in provider.compute.instances:
+                print(instance)
+        """
+        pass
+
+
 class ResultList(list):
     """
     This is a wrapper class around a standard Python :py:class:`list` class
@@ -1172,7 +1253,7 @@ class ContainerObject(object):
         pass
 
 
-class Container(object):
+class Container(PageableObjectMixin):
 
     __metaclass__ = ABCMeta
 
@@ -1200,7 +1281,7 @@ class Container(object):
         pass
 
     @abstractmethod
-    def list(self):
+    def list(self, limit=None, marker=None):
         """
         List all objects within this container.
 

+ 20 - 47
cloudbridge/cloud/interfaces/services.py

@@ -2,6 +2,7 @@
 Specifications for services available through a provider
 """
 from abc import ABCMeta, abstractmethod, abstractproperty
+from cloudbridge.cloud.interfaces.resources import PageableObjectMixin
 
 
 class ProviderService(object):
@@ -71,7 +72,7 @@ class ComputeService(ProviderService):
         pass
 
 
-class InstanceService(ProviderService):
+class InstanceService(PageableObjectMixin, ProviderService):
     """
     Provides access to instances in a provider, including creating,
     listing and deleting instances.
@@ -118,47 +119,19 @@ class InstanceService(ProviderService):
     @abstractmethod
     def list(self, limit=None, marker=None):
         """
-        List all instances. If a limit and marker are specified,
-        the records will be fetched up to the limit starting from the
-        marker onwards. The returned list is a list of class
-        ResultList, which has extra properties like is_truncated,
-        supports_total and total_records to provide extra information
-        about record availability.
+        List available instances.
 
-        If limit is not specified, the limit will default to the underlying
-        provider's default limit. Therefore, you need to check the is_truncated
-        property to determine whether more records are available.
+        The returned results can be limited with limit and marker. If not
+        specified, the limit defaults to a global default.
+        See :func:`~interfaces.resources.PageableObjectMixin.list`
+        for more information on how to page through returned results.
 
-        The total number of results can be determined through the total_results
-        property. Not all provides will support returning the total_results
-        property, so the supports_total property can be used to determine
-        whether a total is supported.
+        example::
 
-        To iterate through all the records, it's recommended to iterate
-        directly through the instances using __iter__ instead of calling
-        the list method.
-
-        example:
-        ```
-        # get first page of results
-        instlist = provider.compute.instances.list(limit=50)
-        for instance in instlist:
-            print("Instance Data: {0}", instance)
-        if instlist.supports_total:
-            print("Total results: {0}".format(instlist.total_results))
-        else:
-            print("Total records unknown,"
-                  "but has more data?: {0}".format(instlist.is_truncated))
-
-        # Page to next set of results
-        if (instlist.is_truncated)
-            instlist = provider.compute.instances.list(limit=100,
-                                                       marker=instlist.marker)
-
-        # Alternative: iterate through total available records
-        for instance in provider.compute.instances:
-            print(instance)
-        ```
+            # List instances
+            instlist = provider.compute.instances.list()
+            for instance in instlist:
+                print("Instance Data: {0}", instance)
 
         :type  limit: ``int``
         :param limit: The maximum number of objects to return
@@ -233,7 +206,7 @@ class InstanceService(ProviderService):
         pass
 
 
-class VolumeService(ProviderService):
+class VolumeService(PageableObjectMixin, ProviderService):
 
     """
     Base interface for a Volume Service
@@ -296,7 +269,7 @@ class VolumeService(ProviderService):
         pass
 
 
-class SnapshotService(ProviderService):
+class SnapshotService(PageableObjectMixin, ProviderService):
 
     """
     Base interface for a Snapshot Service
@@ -384,7 +357,7 @@ class BlockStoreService(ProviderService):
         pass
 
 
-class ImageService(ProviderService):
+class ImageService(PageableObjectMixin, ProviderService):
 
     """
     Base interface for an Image Service
@@ -423,7 +396,7 @@ class ImageService(ProviderService):
         pass
 
 
-class ObjectStoreService(ProviderService):
+class ObjectStoreService(PageableObjectMixin, ProviderService):
 
     """
     Base interface for an Object Storage Service
@@ -506,7 +479,7 @@ class SecurityService(ProviderService):
         pass
 
 
-class KeyPairService(ProviderService):
+class KeyPairService(PageableObjectMixin, ProviderService):
 
     """
     Base interface for key pairs.
@@ -562,7 +535,7 @@ class KeyPairService(ProviderService):
         pass
 
 
-class SecurityGroupService(ProviderService):
+class SecurityGroupService(PageableObjectMixin, ProviderService):
 
     """
     Base interface for security groups.
@@ -633,7 +606,7 @@ class SecurityGroupService(ProviderService):
         pass
 
 
-class InstanceTypesService(object):
+class InstanceTypesService(PageableObjectMixin, ProviderService):
     __metaclass__ = ABCMeta
 
     @abstractmethod
@@ -657,7 +630,7 @@ class InstanceTypesService(object):
         pass
 
 
-class RegionService(ProviderService):
+class RegionService(PageableObjectMixin, ProviderService):
 
     """
     Base interface for a Region service

+ 27 - 0
cloudbridge/cloud/providers/openstack/helpers.py

@@ -0,0 +1,27 @@
+"""
+Helper functions
+"""
+import itertools
+from cloudbridge.cloud.base import BaseResultList
+
+
+def to_result_list(provider, limit, list_func):
+    """
+    A convenience function for wrapping a list of openstack native objects in
+    a BaseResultList. The list_func is called with a custom limit to fetch the
+    initial list of objects. Thereafter, the return list is wrapped in a
+    BaseResultList, enabling extra properties like
+    `is_truncated` and `marker` to be accessed.
+    """
+    limit = limit or provider.config.result_limit
+    # Fetch one more than the limit, so we can
+    # detect whether is_truncated=True
+    objects = list_func(limit + 1)
+    is_truncated = len(objects) > limit
+    next_token = objects[-2].id if is_truncated else None
+    results = BaseResultList(is_truncated,
+                             next_token,
+                             False)
+    for obj in itertools.islice(objects, limit):
+        results.append(obj)
+    return results

+ 13 - 4
cloudbridge/cloud/providers/openstack/resources.py

@@ -22,6 +22,7 @@ from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import PlacementZone
 from cloudbridge.cloud.interfaces.resources import SnapshotState
 from cloudbridge.cloud.interfaces.resources import VolumeState
+from cloudbridge.cloud.providers.openstack import helpers as oshelpers
 
 
 class OpenStackMachineImage(BaseMachineImage):
@@ -709,16 +710,24 @@ class OpenStackContainer(BaseContainer):
         else:
             return None
 
-    def list(self):
+    def list(self, limit=None, marker=None):
         """
         List all objects within this container.
 
         :rtype: ContainerObject
         :return: List of all available ContainerObjects within this container
         """
-        _, object_list = self._provider.swift.get_container(self.name)
-        return [
-            OpenStackContainer(self._provider, o) for o in object_list]
+        def _list_container_objects(nlimit):
+            _, object_list = self._provider.swift.get_container(
+                self.name,
+                limit=nlimit, marker=marker)
+            return [OpenStackContainerObject(
+                self._provider, self, obj) for obj in object_list]
+
+        return oshelpers.to_result_list(
+            self._provider,
+            limit,
+            _list_container_objects)
 
     def delete(self, delete_contents=False):
         """

+ 134 - 75
cloudbridge/cloud/providers/openstack/services.py

@@ -2,7 +2,6 @@
 Services implemented by the OpenStack provider.
 """
 import itertools
-
 from cinderclient.exceptions import NotFound as CinderNotFound
 from novaclient.exceptions import NotFound as NovaNotFound
 
@@ -15,7 +14,6 @@ from cloudbridge.cloud.base import BaseKeyPairService
 from cloudbridge.cloud.base import BaseLaunchConfig
 from cloudbridge.cloud.base import BaseObjectStoreService
 from cloudbridge.cloud.base import BaseRegionService
-from cloudbridge.cloud.base import BaseResultList
 from cloudbridge.cloud.base import BaseSecurityGroupService
 from cloudbridge.cloud.base import BaseSecurityService
 from cloudbridge.cloud.base import BaseSnapshotService
@@ -27,6 +25,7 @@ from cloudbridge.cloud.interfaces.resources import PlacementZone
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import Volume
+from cloudbridge.cloud.providers.openstack import helpers as oshelpers
 
 from .resources import OpenStackContainer
 from .resources import OpenStackInstance
@@ -74,23 +73,35 @@ class OpenStackKeyPairService(BaseKeyPairService):
     def __init__(self, provider):
         super(OpenStackKeyPairService, self).__init__(provider)
 
-    def list(self):
+    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
         """
-        key_pairs = self._provider.nova.keypairs.list()
-        return [OpenStackKeyPair(self._provider, kp) for kp in key_pairs]
+
+        def _list_key_pairs(nlimit):
+            keypairs = self.provider.nova.keypairs.list()
+            if marker:
+                keypairs = itertools.dropwhile(
+                    lambda kp: not kp.name == marker, keypairs)
+
+            return [OpenStackKeyPair(self.provider, kp)
+                    for kp in keypairs]
+
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            _list_key_pairs)
 
     def find(self, name):
         """
         Searches for a key pair by a given list of attributes.
         """
         try:
-            kp = self._provider.nova.keypairs.find(name=name)
-            return OpenStackKeyPair(self._provider, kp)
+            kp = self.provider.nova.keypairs.find(name=name)
+            return OpenStackKeyPair(self.provider, kp)
         except NovaNotFound:
             return None
 
@@ -107,8 +118,8 @@ class OpenStackKeyPairService(BaseKeyPairService):
         kp = self.find(name=name)
         if kp:
             return kp
-        kp = self._provider.nova.keypairs.create(name)
-        return OpenStackKeyPair(self._provider, kp)
+        kp = self.provider.nova.keypairs.create(name)
+        return OpenStackKeyPair(self.provider, kp)
 
 
 class OpenStackSecurityGroupService(BaseSecurityGroupService):
@@ -116,16 +127,27 @@ class OpenStackSecurityGroupService(BaseSecurityGroupService):
     def __init__(self, provider):
         super(OpenStackSecurityGroupService, self).__init__(provider)
 
-    def list(self):
+    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
         """
-        groups = self._provider.nova.security_groups.list()
-        return [OpenStackSecurityGroup(
-            self._provider, group) for group in groups]
+
+        def _list_security_groups(nlimit):
+            sgs = self.provider.nova.security_groups.list()
+            if marker:
+                sgs = itertools.dropwhile(
+                    lambda sg: not sg.name == marker, sgs)
+
+            return [OpenStackSecurityGroup(self.provider, sg)
+                    for sg in sgs]
+
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            _list_security_groups)
 
     def create(self, name, description):
         """
@@ -140,9 +162,9 @@ class OpenStackSecurityGroupService(BaseSecurityGroupService):
         :rtype: ``object`` of :class:`.SecurityGroup`
         :return: a SecurityGroup object
         """
-        sg = self._provider.nova.security_groups.create(name, description)
+        sg = self.provider.nova.security_groups.create(name, description)
         if sg:
-            return OpenStackSecurityGroup(self._provider, sg)
+            return OpenStackSecurityGroup(self.provider, sg)
         return None
 
     def get(self, group_names=None, group_ids=None):
@@ -167,7 +189,7 @@ class OpenStackSecurityGroupService(BaseSecurityGroupService):
             group_names = []
         if not group_ids:
             group_ids = []
-        security_groups = self._provider.nova.security_groups.list()
+        security_groups = self.provider.nova.security_groups.list()
         filtered = []
         for sg in security_groups:
             if sg.name in group_names:
@@ -175,7 +197,7 @@ class OpenStackSecurityGroupService(BaseSecurityGroupService):
             if sg.id in group_ids:
                 filtered.append(sg)
         # If a filter was specified, use the filtered list; otherwise, get all
-        return [OpenStackSecurityGroup(self._provider, sg)
+        return [OpenStackSecurityGroup(self.provider, sg)
                 for sg in (filtered
                            if (group_names or group_ids) else security_groups)]
 
@@ -209,7 +231,7 @@ class OpenStackImageService(BaseImageService):
         """
         try:
             return OpenStackMachineImage(
-                self._provider, self._provider.nova.images.get(image_id))
+                self.provider, self.provider.nova.images.get(image_id))
         except NovaNotFound:
             return None
 
@@ -220,13 +242,18 @@ class OpenStackImageService(BaseImageService):
         raise NotImplementedError(
             'find_image not implemented by this provider')
 
-    def list(self):
+    def list(self, limit=None, marker=None):
         """
         List all images.
         """
-        images = self._provider.nova.images.list()
-        return [OpenStackMachineImage(self._provider, image)
-                for image in images]
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            lambda nlimit:
+                [OpenStackMachineImage(self.provider, img)
+                 for img in self.provider.nova.images.list(
+                    limit=nlimit,
+                    marker=marker)])
 
 
 class OpenStackInstanceTypesService(BaseInstanceTypesService):
@@ -234,9 +261,15 @@ class OpenStackInstanceTypesService(BaseInstanceTypesService):
     def __init__(self, provider):
         super(OpenStackInstanceTypesService, self).__init__(provider)
 
-    def list(self):
-        return [OpenStackInstanceType(f)
-                for f in self._provider.nova.flavors.list()]
+    def list(self, limit=None, marker=None):
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            lambda nlimit:
+                [OpenStackInstanceType(obj)
+                 for obj in self.provider.nova.flavors.list(
+                    limit=nlimit,
+                    marker=marker)])
 
 
 class OpenStackBlockStoreService(BaseBlockStoreService):
@@ -245,8 +278,8 @@ class OpenStackBlockStoreService(BaseBlockStoreService):
         super(OpenStackBlockStoreService, self).__init__(provider)
 
         # Initialize provider services
-        self._volume_svc = OpenStackVolumeService(self._provider)
-        self._snapshot_svc = OpenStackSnapshotService(self._provider)
+        self._volume_svc = OpenStackVolumeService(self.provider)
+        self._snapshot_svc = OpenStackSnapshotService(self.provider)
 
     @property
     def volumes(self):
@@ -268,7 +301,7 @@ class OpenStackVolumeService(BaseVolumeService):
         """
         try:
             return OpenStackVolume(
-                self._provider, self._provider.cinder.volumes.get(volume_id))
+                self.provider, self.provider.cinder.volumes.get(volume_id))
         except CinderNotFound:
             return None
 
@@ -279,12 +312,18 @@ class OpenStackVolumeService(BaseVolumeService):
         raise NotImplementedError(
             'find_volume not implemented by this provider')
 
-    def list(self):
+    def list(self, limit=None, marker=None):
         """
         List all volumes.
         """
-        return [OpenStackVolume(self._provider, vol)
-                for vol in self._provider.cinder.volumes.list()]
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            lambda nlimit:
+                [OpenStackVolume(self.provider, vol)
+                 for vol in self.provider.cinder.volumes.list(
+                    limit=nlimit,
+                    marker=marker)])
 
     def create(self, name, size, zone, snapshot=None):
         """
@@ -294,10 +333,10 @@ class OpenStackVolumeService(BaseVolumeService):
         snapshot_id = snapshot.id if isinstance(
             zone, OpenStackSnapshot) and snapshot else snapshot
 
-        os_vol = self._provider.cinder.volumes.create(
+        os_vol = self.provider.cinder.volumes.create(
             size, name=name, availability_zone=zone_name,
             snapshot_id=snapshot_id)
-        return OpenStackVolume(self._provider, os_vol)
+        return OpenStackVolume(self.provider, os_vol)
 
 
 class OpenStackSnapshotService(BaseSnapshotService):
@@ -311,8 +350,8 @@ class OpenStackSnapshotService(BaseSnapshotService):
         """
         try:
             return OpenStackSnapshot(
-                self._provider,
-                self._provider.cinder.volume_snapshots.get(snapshot_id))
+                self.provider,
+                self.provider.cinder.volume_snapshots.get(snapshot_id))
         except CinderNotFound:
             return None
 
@@ -323,12 +362,19 @@ class OpenStackSnapshotService(BaseSnapshotService):
         raise NotImplementedError(
             'find_volume not implemented by this provider')
 
-    def list(self):
+    def list(self, limit=None, marker=None):
         """
         List all snapshot.
         """
-        return [OpenStackSnapshot(self._provider, snap)
-                for snap in self._provider.cinder.volume_snapshots.list()]
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            lambda nlimit:
+                [OpenStackSnapshot(self.provider, snap) for
+                 snap in self.provider.cinder.volume_snapshots.list(
+                    search_opts={
+                        'limit': nlimit,
+                        'marker': marker})])
 
     def create(self, name, volume, description=None):
         """
@@ -337,10 +383,10 @@ class OpenStackSnapshotService(BaseSnapshotService):
         volume_id = volume.id if \
             isinstance(volume, OpenStackVolume) else volume
 
-        os_snap = self._provider.cinder.volume_snapshots.create(
+        os_snap = self.provider.cinder.volume_snapshots.create(
             volume_id, name=name,
             description=description)
-        return OpenStackSnapshot(self._provider, os_snap)
+        return OpenStackSnapshot(self.provider, os_snap)
 
 
 class OpenStackObjectStoreService(BaseObjectStoreService):
@@ -353,10 +399,10 @@ class OpenStackObjectStoreService(BaseObjectStoreService):
         Returns a container given its id. Returns None if the container
         does not exist.
         """
-        _, container_list = self._provider.swift.get_account(
+        _, container_list = self.provider.swift.get_account(
             prefix=container_id)
         if container_list:
-            return OpenStackContainer(self._provider, container_list[0])
+            return OpenStackContainer(self.provider, container_list[0])
         else:
             return None
 
@@ -367,19 +413,26 @@ class OpenStackObjectStoreService(BaseObjectStoreService):
         raise NotImplementedError(
             'find_container not implemented by this provider')
 
-    def list(self):
+    def list(self, limit=None, marker=None):
         """
         List all containers.
         """
-        _, container_list = self._provider.swift.get_account()
-        return [
-            OpenStackContainer(self._provider, c) for c in container_list]
+        def _list_containers(nlimit):
+            _, container_list = self.provider.swift.get_account(
+                limit=nlimit, marker=marker)
+            return [OpenStackContainer(self.provider, c)
+                    for c in container_list]
+
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            _list_containers)
 
     def create(self, name, location=None):
         """
         Create a new container.
         """
-        self._provider.swift.put_container(name)
+        self.provider.swift.put_container(name)
         return self.get(name)
 
 
@@ -392,21 +445,34 @@ class OpenStackRegionService(BaseRegionService):
         region = (r for r in self.list() if r.id == region_id)
         return next(region, None)
 
-    def list(self):
-        regions = [endpoint.get('region') or endpoint.get('region_id')
-                   for svc in self.provider.keystone.service_catalog.get_data()
-                   for endpoint in svc.get('endpoints', [])]
-        regions = [region for region in regions if region]
-        return [OpenStackRegion(self.provider, region)
-                for region in regions]
+    def list(self, limit=None, marker=None):
+
+        def _list_regions(nlimit):
+            regions = (
+                endpoint.get('region') or endpoint.get('region_id')
+                for svc in self.provider.keystone.service_catalog.get_data()
+                for endpoint in svc.get('endpoints', [])
+            )
+            regions = (region for region in regions if region)
+            if marker:
+                regions = itertools.dropwhile(
+                    lambda region: not region == marker, regions)
+
+            return [OpenStackRegion(self.provider, region)
+                    for region in regions]
+
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            _list_regions)
 
 
 class OpenStackComputeService(BaseComputeService):
 
     def __init__(self, provider):
         super(OpenStackComputeService, self).__init__(provider)
-        self._instance_type_svc = OpenStackInstanceTypesService(self._provider)
-        self._instance_svc = OpenStackInstanceService(self._provider)
+        self._instance_type_svc = OpenStackInstanceTypesService(self.provider)
+        self._instance_svc = OpenStackInstanceService(self.provider)
         self._region_svc = OpenStackRegionService(self.provider)
         self._images_svc = OpenStackImageService(self.provider)
 
@@ -462,7 +528,7 @@ class OpenStackInstanceService(BaseInstanceService):
         else:
             bdm = nics = None
 
-        os_instance = self._provider.nova.servers.create(
+        os_instance = self.provider.nova.servers.create(
             name,
             image_id,
             instance_size,
@@ -474,7 +540,7 @@ class OpenStackInstanceService(BaseInstanceService):
             userdata=user_data,
             block_device_mapping_v2=bdm,
             nics=nics)
-        return OpenStackInstance(self._provider, os_instance)
+        return OpenStackInstance(self.provider, os_instance)
 
     def _to_block_device_mapping(self, launch_config):
         """
@@ -543,28 +609,21 @@ class OpenStackInstanceService(BaseInstanceService):
         """
         List all instances.
         """
-        # TODO: Move hardcoded number to config setting
-        limit = limit or 50
-        instances = self._provider.nova.servers.list(
-            limit=limit + 1,
-            marker=marker)
-        # since we fetched one more than the limit, we can
-        # detect whether extra records are present
-        is_truncated = len(instances) > limit
-        next_token = instances[-2].id if is_truncated else instances[-1].id
-        results = BaseResultList(is_truncated,
-                                 next_token,
-                                 False)
-        for instance in itertools.islice(instances, limit):
-            results.append(OpenStackInstance(self._provider, instance))
-        return results
+        return oshelpers.to_result_list(
+            self.provider,
+            limit,
+            lambda nlimit:
+                [OpenStackInstance(self.provider, inst)
+                 for inst in self.provider.nova.servers.list(
+                    limit=nlimit,
+                    marker=marker)])
 
     def get(self, instance_id):
         """
         Returns an instance given its id.
         """
         try:
-            os_instance = self._provider.nova.servers.get(instance_id)
-            return OpenStackInstance(self._provider, os_instance)
+            os_instance = self.provider.nova.servers.get(instance_id)
+            return OpenStackInstance(self.provider, os_instance)
         except NovaNotFound:
             return None