Sfoglia il codice sorgente

Simplified block device mapping api.

nuwan_ag 10 anni fa
parent
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 InstanceType
 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 MachineImageState
 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 InstanceTypesService
 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 ProviderService
 from cloudbridge.cloud.interfaces.services import RegionService
@@ -190,9 +190,9 @@ class BaseLaunchConfig(LaunchConfig):
         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):
-            self.dest_type = dest_type
+            self.is_volume = is_volume
             self.source = source
             self.is_root = is_root
             self.size = size
@@ -200,49 +200,40 @@ class BaseLaunchConfig(LaunchConfig):
 
         def __repr__(self):
             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)
 
-    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 \
                 not isinstance(source, (Snapshot, Volume, MachineImage)):
             raise InvalidConfigurationException(
                 "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 not isinstance(size, six.integer_types) or not size >= 0:
+            if not isinstance(size, six.integer_types) or not size > 0:
                 raise InvalidConfigurationException(
                     "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:
             for bd in self.block_devices:
                 if bd.is_root:
@@ -250,8 +241,9 @@ class BaseLaunchConfig(LaunchConfig):
                         "An existing block device: {0} has already been"
                         " 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):

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

@@ -5,8 +5,8 @@ from .impl import CloudProvider
 from .resources import CloudProviderServiceType
 from .resources import InstanceState
 from .resources import InvalidConfigurationException
+from .resources import LaunchConfig
 from .resources import MachineImageState
 from .resources import Region
 from .resources import SnapshotState
 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.
 
-        :rtype: str
+        :rtype: ``object`` of :class:`.InstanceType`
         :return: API type of this instance (e.g., ``m1.large``)
         """
         pass
@@ -288,6 +288,109 @@ class MachineImageState(object):
     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):
 
     __metaclass__ = ABCMeta

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

@@ -2,7 +2,6 @@
 Specifications for services available through a provider
 """
 from abc import ABCMeta, abstractmethod, abstractproperty
-from enum import Enum
 
 
 class ProviderService(object):
@@ -170,110 +169,6 @@ class InstanceService(ProviderService):
         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):
 
     """

+ 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 Snapshot
 from cloudbridge.cloud.interfaces.resources import Volume
-from cloudbridge.cloud.interfaces.services import LaunchConfig
 
 from .resources import AWSContainer
 from .resources import AWSInstance
@@ -392,11 +391,11 @@ class AWSComputeService(BaseComputeService):
         self._instance_type_svc = AWSInstanceTypesService(self.provider)
         self._instance_svc = AWSInstanceService(self.provider)
         self._region_svc = AWSRegionService(self.provider)
-        self._images = AWSImageService(self.provider)
+        self._images_svc = AWSImageService(self.provider)
 
     @property
     def images(self):
-        return self._images
+        return self._images_svc
 
     @property
     def instance_types(self):
@@ -468,21 +467,21 @@ class AWSInstanceService(BaseInstanceService):
         ephemeral_counter = 0
         for device in launch_config.block_devices:
             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
                     # create a blank volume. If the Zone is None, this
                     # could fail since the volume and instance may be created
@@ -492,12 +491,12 @@ class AWSInstanceService(BaseInstanceService):
                         device.size,
                         zone)
                     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
 
     def create_launch_config(self):

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

@@ -158,7 +158,8 @@ class OpenStackInstanceType(BaseInstanceType):
 
     @property
     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
     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 Snapshot
 from cloudbridge.cloud.interfaces.resources import Volume
-from cloudbridge.cloud.interfaces.services import LaunchConfig
 
 from .resources import OpenStackContainer
 from .resources import OpenStackInstance
@@ -428,7 +427,11 @@ class OpenStackComputeService(BaseComputeService):
         self._instance_type_svc = OpenStackInstanceTypesService(self._provider)
         self._instance_svc = OpenStackInstanceService(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
     def instance_types(self):
@@ -473,7 +476,7 @@ class OpenStackInstanceService(BaseInstanceService):
         else:
             security_groups_list = None
         if launch_config:
-            bdm = None  # self._to_block_device_mapping(launch_config)
+            bdm = self._to_block_device_mapping(launch_config)
         else:
             bdm = None
 
@@ -499,32 +502,38 @@ class OpenStackInstanceService(BaseInstanceService):
         bdm = []
         for device in launch_config.block_devices:
             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:
+                bdm_dict['destination_type'] = 'local'
                 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)
         return bdm
 

+ 1 - 1
docs/index.rst

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

+ 2 - 2
test/helpers.py

@@ -56,8 +56,8 @@ TEST_DATA_CONFIG = {
     },
     "OpenStackCloudProvider": {
         "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'),
     }
 }

+ 55 - 72
test/test_provider_compute_service.py

@@ -5,7 +5,6 @@ import ipaddress
 from cloudbridge.cloud.interfaces \
     import InvalidConfigurationException
 from cloudbridge.cloud.interfaces import InstanceState
-from cloudbridge.cloud.interfaces import LaunchConfig
 from test.helpers import ProviderTestBase
 import test.helpers as helpers
 
@@ -104,74 +103,58 @@ class ProviderComputeServiceTestCase(ProviderTestBase):
             self.provider.name,
             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