Browse Source

Simplified block device mapping api.

nuwan_ag 10 năm trước cách đây
mục cha
commit
2b3a9e4bb5

+ 25 - 33
cloudbridge/cloud/base.py

@@ -14,6 +14,7 @@ from cloudbridge.cloud.interfaces.resources import Instance
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import InstanceState
 from cloudbridge.cloud.interfaces.resources import InstanceType
 from cloudbridge.cloud.interfaces.resources import InstanceType
 from cloudbridge.cloud.interfaces.resources import KeyPair
 from cloudbridge.cloud.interfaces.resources import KeyPair
+from cloudbridge.cloud.interfaces.resources import LaunchConfig
 from cloudbridge.cloud.interfaces.resources import MachineImage
 from cloudbridge.cloud.interfaces.resources import MachineImage
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import MachineImageState
 from cloudbridge.cloud.interfaces.resources import ObjectLifeCycleMixin
 from cloudbridge.cloud.interfaces.resources import ObjectLifeCycleMixin
@@ -31,7 +32,6 @@ from cloudbridge.cloud.interfaces.services import ImageService
 from cloudbridge.cloud.interfaces.services import InstanceService
 from cloudbridge.cloud.interfaces.services import InstanceService
 from cloudbridge.cloud.interfaces.services import InstanceTypesService
 from cloudbridge.cloud.interfaces.services import InstanceTypesService
 from cloudbridge.cloud.interfaces.services import KeyPairService
 from cloudbridge.cloud.interfaces.services import KeyPairService
-from cloudbridge.cloud.interfaces.services import LaunchConfig
 from cloudbridge.cloud.interfaces.services import ObjectStoreService
 from cloudbridge.cloud.interfaces.services import ObjectStoreService
 from cloudbridge.cloud.interfaces.services import ProviderService
 from cloudbridge.cloud.interfaces.services import ProviderService
 from cloudbridge.cloud.interfaces.services import RegionService
 from cloudbridge.cloud.interfaces.services import RegionService
@@ -190,9 +190,9 @@ class BaseLaunchConfig(LaunchConfig):
         Represents a block device mapping
         Represents a block device mapping
         """
         """
 
 
-        def __init__(self, dest_type, source=None, is_root=None,
+        def __init__(self, is_volume=False, source=None, is_root=None,
                      size=None, delete_on_terminate=None):
                      size=None, delete_on_terminate=None):
-            self.dest_type = dest_type
+            self.is_volume = is_volume
             self.source = source
             self.source = source
             self.is_root = is_root
             self.is_root = is_root
             self.size = size
             self.size = size
@@ -200,49 +200,40 @@ class BaseLaunchConfig(LaunchConfig):
 
 
         def __repr__(self):
         def __repr__(self):
             return "<CB-{0}: Dest: {1}, Src: {2}, IsRoot: {3}, Size: {4}>" \
             return "<CB-{0}: Dest: {1}, Src: {2}, IsRoot: {3}, Size: {4}>" \
-                .format(self.__class__.__name__, self.dest_type, self.source,
-                        self.is_root, self.size)
+                .format(self.__class__.__name__,
+                        "volume" if self.is_volume else "ephemeral",
+                        self.source, self.is_root, self.size)
 
 
-    def add_block_device(self, dest_type, source=None, is_root=None,
-                         size=None, delete_on_terminate=None):
+    def add_ephemeral_device(self):
+        block_device = BaseLaunchConfig.BlockDeviceMapping()
+        self.block_devices.append(block_device)
 
 
-        block_device = self._parse_block_device(
-            dest_type, source, is_root, size, delete_on_terminate)
+    def add_volume_device(self, source=None, is_root=None, size=None,
+                          delete_on_terminate=None):
+        block_device = self._validate_volume_device(
+            source=source, is_root=is_root, size=size,
+            delete_on_terminate=delete_on_terminate)
         self.block_devices.append(block_device)
         self.block_devices.append(block_device)
 
 
-    def _parse_block_device(self, dest_type, source=None, is_root=None,
-                            size=None, delete_on_terminate=None):
+    def _validate_volume_device(self, source=None, is_root=None,
+                                size=None, delete_on_terminate=None):
         """
         """
-        Validates a block device and throws an InvalidConfigurationException
-        if the configuration is incorrect.
+        Validates a volume based device and throws an
+        InvalidConfigurationException if the configuration is incorrect.
         """
         """
-        if source is None:
-            if dest_type == LaunchConfig.DestinationType.VOLUME and \
-                    not size:
-                raise InvalidConfigurationException(
-                    "A size must be specified if the destination is a blank"
-                    " new volume")
+        if source is None and not size:
+            raise InvalidConfigurationException(
+                "A size must be specified for a blank new volume")
 
 
         if source and \
         if source and \
                 not isinstance(source, (Snapshot, Volume, MachineImage)):
                 not isinstance(source, (Snapshot, Volume, MachineImage)):
             raise InvalidConfigurationException(
             raise InvalidConfigurationException(
                 "Source must be a Snapshot, Volume, MachineImage or None")
                 "Source must be a Snapshot, Volume, MachineImage or None")
-        if source and isinstance(source, (Snapshot, Volume)) and \
-                not dest_type == LaunchConfig.DestinationType.VOLUME:
-            raise InvalidConfigurationException(
-                "The destination must be Volume if the sources is of type"
-                " Snapshot or Volume")
         if size:
         if size:
-            if not isinstance(size, six.integer_types) or not size >= 0:
+            if not isinstance(size, six.integer_types) or not size > 0:
                 raise InvalidConfigurationException(
                 raise InvalidConfigurationException(
                     "The size must be None or a number greater than 0")
                     "The size must be None or a number greater than 0")
 
 
-        if source and isinstance(source, MachineImage) and \
-                dest_type == LaunchConfig.DestinationType.LOCAL:
-            # When source is an image and destination is LOCAL, is_root=True
-            # is implied
-            is_root = True
-
         if is_root:
         if is_root:
             for bd in self.block_devices:
             for bd in self.block_devices:
                 if bd.is_root:
                 if bd.is_root:
@@ -250,8 +241,9 @@ class BaseLaunchConfig(LaunchConfig):
                         "An existing block device: {0} has already been"
                         "An existing block device: {0} has already been"
                         " marked as root. There can only be one root device.")
                         " marked as root. There can only be one root device.")
 
 
-        return BaseLaunchConfig.BlockDeviceMapping(dest_type, source, is_root,
-                                                   size, delete_on_terminate)
+        return BaseLaunchConfig.BlockDeviceMapping(
+            is_volume=True, source=source, is_root=is_root, size=size,
+            delete_on_terminate=delete_on_terminate)
 
 
 
 
 class BaseMachineImage(BaseObjectLifeCycleMixin, MachineImage):
 class BaseMachineImage(BaseObjectLifeCycleMixin, MachineImage):

+ 1 - 1
cloudbridge/cloud/interfaces/__init__.py

@@ -5,8 +5,8 @@ from .impl import CloudProvider
 from .resources import CloudProviderServiceType
 from .resources import CloudProviderServiceType
 from .resources import InstanceState
 from .resources import InstanceState
 from .resources import InvalidConfigurationException
 from .resources import InvalidConfigurationException
+from .resources import LaunchConfig
 from .resources import MachineImageState
 from .resources import MachineImageState
 from .resources import Region
 from .resources import Region
 from .resources import SnapshotState
 from .resources import SnapshotState
 from .resources import VolumeState
 from .resources import VolumeState
-from .services import LaunchConfig

+ 104 - 1
cloudbridge/cloud/interfaces/resources.py

@@ -185,7 +185,7 @@ class Instance(ObjectLifeCycleMixin):
         """
         """
         Get the instance type.
         Get the instance type.
 
 
-        :rtype: str
+        :rtype: ``object`` of :class:`.InstanceType`
         :return: API type of this instance (e.g., ``m1.large``)
         :return: API type of this instance (e.g., ``m1.large``)
         """
         """
         pass
         pass
@@ -288,6 +288,109 @@ class MachineImageState(object):
     ERROR = "error"
     ERROR = "error"
 
 
 
 
+class LaunchConfig(object):
+    """
+    Represents an advanced launch configuration object, containing
+    information such as BlockDeviceMappings, NetworkInterface configurations,
+    and other advanced options which may be useful when launching an instance.
+
+    Typical Usage:
+    ```
+        lc = provider.compute.instances.create_launch_config()
+        lc.add_block_device(...)
+        lc.add_network_interface(...)
+
+        inst = provider.compute.instances.create(name, image, instance_type,
+                                               launch_configuration=lc)
+    ```
+    """
+
+    @abstractmethod
+    def add_ephemeral_device(self):
+        """
+        Adds a new ephemeral block device mapping to the boot configuration.
+        This can be used to add existing ephemeral devices to the instance.
+        (The total number of ephemeral devices available for a particular
+        InstanceType can be determined by querying the InstanceTypes service).
+        Note that on some services, such as AWS, ephemeral devices must be
+        added in as a device mapping at instance creation time, and cannot be
+        added afterwards.
+
+        Note that the device name, such as /dev/sda1, cannot be selected at
+        present, since this tends to be provider and instance type specific.
+        However, the order of device addition coupled with device type will
+        generally determine naming order, with devices added first getting
+        lower letters than instances added later.
+
+        Examples:
+        ```
+        lc = provider.compute.instances.create_launch_config()
+
+        # 1. Add all available ephemeral devices
+        inst_type = next(provider.compute.instance_types.find(name='m1.small'))
+        for i in range(inst_type.num_ephemeral_disks):
+            lc.add_ephemeral_device()
+        """
+        pass
+
+    @abstractmethod
+    def add_volume_device(self, source=None, is_root=None, size=None,
+                          delete_on_terminate=None):
+        """
+        Adds a new volume based block device mapping to the boot configuration.
+        The volume can be based on a snapshot, image, existing volume or
+        be a blank new volume, and is specified by the source parameter.
+
+        The property is_root can be set to True to override any existing root
+        device mappings. Otherwise, the default behaviour is to add new block
+        devices to the instance.
+
+        Note that the device name, such as /dev/sda1, cannot be selected at
+        present, since this tends to be provider and instance type specific.
+        However, the order of device addition coupled with device type will
+        generally determine naming order, with devices added first getting
+        lower letters than instances added later (except when is_root is set).
+
+        Examples:
+        ```
+        lc = provider.compute.instances.create_launch_config()
+
+        # 1. Create and attach an empty volume to the instance of size 100GB
+        lc.add_volume_device(size=100, delete_on_terminate=True)
+
+        # 2. Create and attach a volume based on a snapshot
+        snap = provider.block_store.snapshots.get('<my_snapshot_id>')
+        lc.add_volume_device(source=snap)
+
+        # 3. Create and attach a volume based on an image and set it as root
+        img = provider.images.get('<my_image_id>')
+        lc.add_volume_device(source=img, size=100, is_root=True)
+        ```
+
+        :type  source: ``Volume``, ``Snapshot``, ``Image`` or None.
+        :param source: The source block_device to add. If ``Volume``, the
+        volume will be attached directly to the instance. If ``Snapshot``, a
+        volume will be created based on the Snapshot and attached to the
+        instance. If ``Image``, a volume based on the Image will be attached to
+        the instance. If None, the source is assumed to be an empty blank
+        volume.
+
+        :type  is_root: ``bool``
+        :param is_root: Determines which device will serve as the root device.
+        If more than one device is defined as root, an
+        InvalidConfigurationException will be thrown.
+
+        :type  size: ``int``
+        :param size: The size of the volume to create. An implementation may
+        ignore this parameter for certain sources like 'Volume'.
+
+        :type  delete_on_terminate: ``bool``
+        :param delete_on_terminate: Determines whether to delete or keep the
+        volume on instance termination.
+        """
+        pass
+
+
 class MachineImage(ObjectLifeCycleMixin):
 class MachineImage(ObjectLifeCycleMixin):
 
 
     __metaclass__ = ABCMeta
     __metaclass__ = ABCMeta

+ 0 - 105
cloudbridge/cloud/interfaces/services.py

@@ -2,7 +2,6 @@
 Specifications for services available through a provider
 Specifications for services available through a provider
 """
 """
 from abc import ABCMeta, abstractmethod, abstractproperty
 from abc import ABCMeta, abstractmethod, abstractproperty
-from enum import Enum
 
 
 
 
 class ProviderService(object):
 class ProviderService(object):
@@ -170,110 +169,6 @@ class InstanceService(ProviderService):
         pass
         pass
 
 
 
 
-class LaunchConfig(object):
-    """
-    Represents an advanced launch configuration object, containing
-    information such as BlockDeviceMappings, NetworkInterface configurations,
-    and other advanced options which may be useful when launching an instance.
-
-    Typical Usage:
-    ```
-        lc = provider.compute.instances.create_launch_config()
-        lc.add_block_device(...)
-        lc.add_network_interface(...)
-
-        inst = provider.compute.instances.create(name, image, instance_type,
-                                               launch_configuration=lc)
-    ```
-    """
-    class DestinationType(Enum):
-        LOCAL = 'local'
-        VOLUME = 'volume'
-
-    def add_block_device(self, dest_type, source=None, is_root=None,
-                         size=None, delete_on_terminate=None):
-        """
-        Adds a new block device mapping to the boot configuration. The block
-        device can be based on a snapshot, image, existing volume or be a blank
-        new volume, and is specified by the source parameter.
-        The destination can be either a Volume or a Local ephemeral device.
-
-        The property is_root can be set to True to override any existing root
-        device mappings. Otherwise, the default behaviour is to add new block
-        devices to the instance. When source is an Image and destination is
-        LOCAL, is_root=True is implied and does not need to be manually
-        specified. Specifying more than one device as root is an error and the
-        behaviour is indeterminate.
-
-        If no source is specified, then the destination must be LOCAL, and can
-        be used to add available ephemeral devices. (The total number of
-        ephemeral devices available for a particular InstanceType can be
-        determined by querying the InstanceTypes service).
-
-        Note that the device name, such as /dev/sda1, cannot be selected at
-        present, since this tends to be provider and instance type specific.
-        However, the order of device addition coupled with device type will
-        generally determine naming order, with devices added first getting
-        lower letters than instances added later (except when is_root is set).
-
-        Examples:
-        ```
-        lc = provider.compute.instances.create_launch_config()
-
-        # 1. Create and attach an empty volume to the instance of size 100GB
-        lc.add_block_device(LaunchConfig.DestinationType.VOLUME,
-                            size=100)
-
-        # 2. Override the size of the root device with a larger size
-        img = provider.images.get('<my_image_id>')
-        lc.add_block_device(LaunchConfig.DestinationType.LOCAL,
-                            source=img, size=100)
-
-        # 3. Create and attach a volume based on a snapshot
-        snap = provider.block_store.snapshots.get('<my_snapshot_id>')
-        lc.add_block_device(LaunchConfig.DestinationType.VOLUME,
-                            source=snap)
-
-        # 4. Add all available ephemeral devices
-        inst_type = next(provider.compute.instance_types.find(name='m1.small'))
-        for i in xrange(inst_type.num_ephemeral_disks):
-            lc.add_block_device(LaunchConfig.DestinationType.LOCAL)
-        ```
-
-        :type  source: ``Volume``, ``Snapshot``, ``Image`` or None.
-        :param source: The source block_device to add. If ``Volume``, the
-        volume will be attached directly to the instance. If ``Snapshot``, a
-        volume will be created based on the Snapshot and attached to the
-        instance. If ``Image``, a volume based on the Image will be attached to
-        the instance. If blank, the source is assumed to be an empty blank
-        volume.
-
-        :type  dest_type: an  enum of ``LaunchConfig.DestinationType``
-        :param dest_type: The dest_type can be DestinationType.LOCAL, in which
-        case a local, ephemeral disk is assumed. Otherwise, it can be
-        DestinationType.VOLUME, in which case a volume is used. Note however,
-        that not all source and destination types are compatible. Only
-        a source of type ``Image`` and ``None`` can be used with a destination
-        type of Local. The destination type ``Volume`` supports all valid
-        sources.
-
-        :type  is_root: ``bool``
-        :param is_root: Determines which device will serve as the root device.
-        If more than one device is defined as root, the behaviour is
-        indeterminate and provider specific.
-
-        :type  size: ``int``
-        :param size: The size of the destination volume. Only valid for
-        dest_type of 'volume'. An implementation may ignore this parameter
-        for certain sources like 'Volume'.
-
-        :type  delete_on_terminate: ``bool``
-        :param delete_on_terminate: Applies only if the dest_type is Volume,
-        and determines whether to delete the volume on instance termination.
-        """
-        pass
-
-
 class VolumeService(ProviderService):
 class VolumeService(ProviderService):
 
 
     """
     """

+ 22 - 23
cloudbridge/cloud/providers/aws/services.py

@@ -28,7 +28,6 @@ from cloudbridge.cloud.interfaces.resources import PlacementZone
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import Volume
 from cloudbridge.cloud.interfaces.resources import Volume
-from cloudbridge.cloud.interfaces.services import LaunchConfig
 
 
 from .resources import AWSContainer
 from .resources import AWSContainer
 from .resources import AWSInstance
 from .resources import AWSInstance
@@ -392,11 +391,11 @@ class AWSComputeService(BaseComputeService):
         self._instance_type_svc = AWSInstanceTypesService(self.provider)
         self._instance_type_svc = AWSInstanceTypesService(self.provider)
         self._instance_svc = AWSInstanceService(self.provider)
         self._instance_svc = AWSInstanceService(self.provider)
         self._region_svc = AWSRegionService(self.provider)
         self._region_svc = AWSRegionService(self.provider)
-        self._images = AWSImageService(self.provider)
+        self._images_svc = AWSImageService(self.provider)
 
 
     @property
     @property
     def images(self):
     def images(self):
-        return self._images
+        return self._images_svc
 
 
     @property
     @property
     def instance_types(self):
     def instance_types(self):
@@ -468,21 +467,21 @@ class AWSInstanceService(BaseInstanceService):
         ephemeral_counter = 0
         ephemeral_counter = 0
         for device in launch_config.block_devices:
         for device in launch_config.block_devices:
             bd_type = BlockDeviceType()
             bd_type = BlockDeviceType()
-            if device.is_root:
-                bdm['/dev/sda1'] = bd_type
-            else:
-                bdm['sd' + next(next_letter)] = bd_type
-
-            if isinstance(device.source, Snapshot):
-                bd_type.snapshot_id = device.source.id
-            elif isinstance(device.source, Volume):
-                bd_type.volume_id = device.source.id
-            elif isinstance(device.source, MachineImage):
-                # Not supported
-                pass
-            else:
-                if device.dest_type == \
-                        LaunchConfig.DestinationType.VOLUME:
+
+            if device.is_volume:
+                if device.is_root:
+                    bdm['/dev/sda1'] = bd_type
+                else:
+                    bdm['sd' + next(next_letter)] = bd_type
+
+                if isinstance(device.source, Snapshot):
+                    bd_type.snapshot_id = device.source.id
+                elif isinstance(device.source, Volume):
+                    bd_type.volume_id = device.source.id
+                elif isinstance(device.source, MachineImage):
+                    # Not supported
+                    pass
+                else:
                     # source is None, but destination is volume, therefore
                     # source is None, but destination is volume, therefore
                     # create a blank volume. If the Zone is None, this
                     # create a blank volume. If the Zone is None, this
                     # could fail since the volume and instance may be created
                     # could fail since the volume and instance may be created
@@ -492,12 +491,12 @@ class AWSInstanceService(BaseInstanceService):
                         device.size,
                         device.size,
                         zone)
                         zone)
                     bd_type.volume_id = new_vol.id
                     bd_type.volume_id = new_vol.id
-                else:
-                    bd_type.ephemeral_name = 'ephemeral%s' % ephemeral_counter
+                bd_type.delete_on_terminate = device.delete_on_terminate
+                if device.size:
+                    bd_type.size = device.size
+            else:  # device is ephemeral
+                bd_type.ephemeral_name = 'ephemeral%s' % ephemeral_counter
 
 
-            bd_type.delete_on_terminate = device.delete_on_terminate
-            if device.size:
-                bd_type.size = device.size
         return bdm
         return bdm
 
 
     def create_launch_config(self):
     def create_launch_config(self):

+ 2 - 1
cloudbridge/cloud/providers/openstack/resources.py

@@ -158,7 +158,8 @@ class OpenStackInstanceType(BaseInstanceType):
 
 
     @property
     @property
     def num_ephemeral_disks(self):
     def num_ephemeral_disks(self):
-        return 0 if self._os_flavor.ephemeral == 'N/A' else 1
+        return 0 if self._os_flavor.ephemeral == 'N/A' else \
+            self._os_flavor.ephemeral
 
 
     @property
     @property
     def extra_data(self):
     def extra_data(self):

+ 36 - 27
cloudbridge/cloud/providers/openstack/services.py

@@ -24,7 +24,6 @@ from cloudbridge.cloud.interfaces.resources import PlacementZone
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import SecurityGroup
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import Snapshot
 from cloudbridge.cloud.interfaces.resources import Volume
 from cloudbridge.cloud.interfaces.resources import Volume
-from cloudbridge.cloud.interfaces.services import LaunchConfig
 
 
 from .resources import OpenStackContainer
 from .resources import OpenStackContainer
 from .resources import OpenStackInstance
 from .resources import OpenStackInstance
@@ -428,7 +427,11 @@ class OpenStackComputeService(BaseComputeService):
         self._instance_type_svc = OpenStackInstanceTypesService(self._provider)
         self._instance_type_svc = OpenStackInstanceTypesService(self._provider)
         self._instance_svc = OpenStackInstanceService(self._provider)
         self._instance_svc = OpenStackInstanceService(self._provider)
         self._region_svc = OpenStackRegionService(self.provider)
         self._region_svc = OpenStackRegionService(self.provider)
-        self._images = OpenStackImageService(self.provider)
+        self._images_svc = OpenStackImageService(self.provider)
+
+    @property
+    def images(self):
+        return self._images_svc
 
 
     @property
     @property
     def instance_types(self):
     def instance_types(self):
@@ -473,7 +476,7 @@ class OpenStackInstanceService(BaseInstanceService):
         else:
         else:
             security_groups_list = None
             security_groups_list = None
         if launch_config:
         if launch_config:
-            bdm = None  # self._to_block_device_mapping(launch_config)
+            bdm = self._to_block_device_mapping(launch_config)
         else:
         else:
             bdm = None
             bdm = None
 
 
@@ -499,32 +502,38 @@ class OpenStackInstanceService(BaseInstanceService):
         bdm = []
         bdm = []
         for device in launch_config.block_devices:
         for device in launch_config.block_devices:
             bdm_dict = {}
             bdm_dict = {}
-            if device.is_root:
-                bdm_dict['device_name'] = '/dev/vda'
-            else:
-                # Let openstack auto assign device name
-                bdm_dict['device_name'] = None
-
-            if isinstance(device.source, Snapshot):
-                bdm_dict['source_type'] = 'snapshot'
-                bdm_dict['uuid'] = device.source.id
-            elif isinstance(device.source, Volume):
-                bdm_dict['source_type'] = 'volume'
-                bdm_dict['uuid'] = device.source.id
-            elif isinstance(device.source, MachineImage):
-                bdm_dict['source_type'] = 'image'
-                bdm_dict['uuid'] = device.source.id
+
+            # Let openstack auto assign device name
+            bdm_dict['device_name'] = None
+
+            if device.is_volume:
+                bdm_dict['destination_type'] = 'volume'
+
+                if device.is_root:
+                    bdm_dict['device_name'] = '/dev/sda'
+
+                if isinstance(device.source, Snapshot):
+                    bdm_dict['source_type'] = 'snapshot'
+                    bdm_dict['uuid'] = device.source.id
+                elif isinstance(device.source, Volume):
+                    bdm_dict['source_type'] = 'volume'
+                    bdm_dict['uuid'] = device.source.id
+                elif isinstance(device.source, MachineImage):
+                    bdm_dict['source_type'] = 'image'
+                    bdm_dict['uuid'] = device.source.id
+                else:
+                    bdm_dict['source_type'] = 'blank'
+
+                if device.delete_on_terminate is not None:
+                    bdm_dict[
+                        'delete_on_termination'] = device.delete_on_terminate
+
+                if device.size:
+                    bdm_dict['volume_size'] = device.size
             else:
             else:
+                bdm_dict['destination_type'] = 'local'
                 bdm_dict['source_type'] = 'blank'
                 bdm_dict['source_type'] = 'blank'
-
-            bdm_dict['destination_type'] = \
-                'volume' if device.dest_type == \
-                LaunchConfig.DestinationType.LOCAL \
-                else 'local'
-            bdm_dict['delete_on_termination'] = device.delete_on_terminate
-            if device.size:
-                bdm_dict['size'] = device.size
-
+                bdm_dict['delete_on_termination'] = True
             bdm.append(bdm_dict)
             bdm.append(bdm_dict)
         return bdm
         return bdm
 
 

+ 1 - 1
docs/index.rst

@@ -17,7 +17,7 @@ look like the following.
 
 
 .. code-block:: python
 .. code-block:: python
 
 
-	from cloudbridge.providers.factory import CloudProviderFactory, ProviderList
+	from cloudbridge.cloud.factory import CloudProviderFactory, ProviderList
 
 
 	provider = CloudProviderFactory().create_provider(ProviderList.AWS, {})
 	provider = CloudProviderFactory().create_provider(ProviderList.AWS, {})
 	print(provider.security.key_pairs.list())
 	print(provider.security.key_pairs.list())

+ 2 - 2
test/helpers.py

@@ -56,8 +56,8 @@ TEST_DATA_CONFIG = {
     },
     },
     "OpenStackCloudProvider": {
     "OpenStackCloudProvider": {
         "image": os.environ.get('CB_IMAGE_OS',
         "image": os.environ.get('CB_IMAGE_OS',
-                                'fdd9a6ee-2c9b-490c-91ac-ca9b7c134264'),
-        "instance_type": os.environ.get('CB_INSTANCE_TYPE_OS', 'm1.nano'),
+                                'ca4beba8-4948-4410-b6ed-4d68e54b5913'),
+        "instance_type": os.environ.get('CB_INSTANCE_TYPE_OS', 'm1.tiny'),
         "placement": os.environ.get('CB_PLACEMENT_OS', 'nova'),
         "placement": os.environ.get('CB_PLACEMENT_OS', 'nova'),
     }
     }
 }
 }

+ 55 - 72
test/test_provider_compute_service.py

@@ -5,7 +5,6 @@ import ipaddress
 from cloudbridge.cloud.interfaces \
 from cloudbridge.cloud.interfaces \
     import InvalidConfigurationException
     import InvalidConfigurationException
 from cloudbridge.cloud.interfaces import InstanceState
 from cloudbridge.cloud.interfaces import InstanceState
-from cloudbridge.cloud.interfaces import LaunchConfig
 from test.helpers import ProviderTestBase
 from test.helpers import ProviderTestBase
 import test.helpers as helpers
 import test.helpers as helpers
 
 
@@ -104,74 +103,58 @@ class ProviderComputeServiceTestCase(ProviderTestBase):
             self.provider.name,
             self.provider.name,
             uuid.uuid4())
             uuid.uuid4())
 
 
-        outer_inst = helpers.get_test_instance(self.provider, name)
-        with helpers.cleanup_action(lambda: outer_inst.terminate()):
-            img = outer_inst.create_image(name)
-            with helpers.cleanup_action(lambda: img.delete()):
-                lc = self.provider.compute.instances.create_launch_config()
-
-                # specifying no size with a destination of volume should raise
-                # an exception
-                with self.assertRaises(InvalidConfigurationException):
-                    lc.add_block_device(LaunchConfig.DestinationType.VOLUME)
-
-                # specifying an invalid source type should raise an error
-                with self.assertRaises(InvalidConfigurationException):
-                    lc.add_block_device(LaunchConfig.DestinationType.LOCAL,
-                                        source='1234')
-
-                # specifying an invalid size should raise an error
-                with self.assertRaises(InvalidConfigurationException):
-                    lc.add_block_device(LaunchConfig.DestinationType.LOCAL,
-                                        source=img, size=-1)
-
-                # block_devices should be empty so far
-                self.assertListEqual(
-                    lc.block_devices, [], "No block devices should have been"
-                    " added to mappings list since the configuration was"
-                    " invalid")
-
-                # Add a new volume
-                lc.add_block_device(LaunchConfig.DestinationType.VOLUME,
-                                    size=1)
-                # Override root volume size
-                if img:
-                    lc.add_block_device(LaunchConfig.DestinationType.LOCAL,
-                                        source=img, size=11)
-
-                # Since the previous addition with destination=LOCAL with
-                # source=img implies a root volume, attempting to add another
-                # root volume should raise an exception.
-                with self.assertRaises(InvalidConfigurationException):
-                    lc.add_block_device(LaunchConfig.DestinationType.LOCAL,
-                                        size=1, is_root=True)
-
-                # Add all available ephemeral devices
-                instance_type_name = helpers.get_provider_test_data(
-                    self.provider,
-                    "instance_type")
-                inst_type = next(self.provider.compute.instance_types.find(
-                    name=instance_type_name))
-                for _ in range(inst_type.num_ephemeral_disks):
-                    lc.add_block_device(LaunchConfig.DestinationType.LOCAL)
-
-                # block_devices should be populated
-                self.assertTrue(
-                    len(lc.block_devices) >= 1,
-                    "Expected number of block devices %s not found" %
-                    len(lc.block_devices))
-
-                inst = helpers.create_test_instance(
-                    self.provider,
-                    name,
-                    launch_config=lc)
-                with helpers.cleanup_action(lambda: inst.terminate()):
-                    inst.wait_till_ready(
-                        interval=self.get_test_wait_interval())
-                    inst.terminate()
-                    inst.wait_for(
-                        [InstanceState.TERMINATED, InstanceState.UNKNOWN],
-                        terminal_states=[InstanceState.ERROR],
-                        interval=self.get_test_wait_interval())
-                    # TODO: Check instance attachments and make sure they
-                    # correspond to requested mappings
+        lc = self.provider.compute.instances.create_launch_config()
+
+        # specifying an invalid size should raise
+        # an exception
+        with self.assertRaises(InvalidConfigurationException):
+            lc.add_volume_device(-1)
+
+        # block_devices should be empty so far
+        self.assertListEqual(
+            lc.block_devices, [], "No block devices should have been"
+            " added to mappings list since the configuration was"
+            " invalid")
+
+        # Add a new volume
+        lc.add_volume_device(size=1)
+
+        # Override root volume size
+        image_id = helpers.get_provider_test_data(self.provider, "image")
+        img = self.provider.compute.images.get(image_id)
+        lc.add_volume_device(is_root=True, source=img, size=3)
+
+        # Attempting to add more than one root volume should raise an
+        # exception.
+        with self.assertRaises(InvalidConfigurationException):
+            lc.add_volume_device(size=1, is_root=True)
+
+        # Add all available ephemeral devices
+#         instance_type_name = helpers.get_provider_test_data(
+#             self.provider,
+#             "instance_type")
+#         inst_type = next(self.provider.compute.instance_types.find(
+#             name=instance_type_name))
+#         for _ in range(inst_type.num_ephemeral_disks):
+#             lc.add_ephemeral_device()
+
+        # block_devices should be populated
+#         self.assertTrue(
+#             len(lc.block_devices) == 2 + inst_type.num_ephemeral_disks,
+#             "Expected %d total block devices bit found %d" %
+#             (2 + inst_type.num_ephemeral_disks, len(lc.block_devices)))
+
+        inst = helpers.create_test_instance(
+            self.provider,
+            name,
+            launch_config=lc)
+        with helpers.cleanup_action(lambda: inst.terminate()):
+            inst.wait_till_ready(
+                interval=self.get_test_wait_interval())
+            inst.terminate()
+            inst.wait_for(
+                [InstanceState.TERMINATED, InstanceState.UNKNOWN],
+                terminal_states=[InstanceState.ERROR],
+                interval=self.get_test_wait_interval())
+            # TODO: Check instance attachments and make sure they
+            # correspond to requested mappings