You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by an...@apache.org on 2017/04/10 22:16:41 UTC

[1/3] libcloud git commit: Added Import Snapshot and Describe Import Snapshot to EC2 compute driver

Repository: libcloud
Updated Branches:
  refs/heads/trunk 9f4d0a263 -> a5731abb0


Added Import Snapshot and Describe Import Snapshot to EC2 compute driver


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/0e73f775
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/0e73f775
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/0e73f775

Branch: refs/heads/trunk
Commit: 0e73f7758caa6ca11b979c286c314e8b7c91f821
Parents: c62d7c9
Author: Nirzari Iyer <ni...@localhost.localdomain>
Authored: Tue Feb 14 14:36:41 2017 -0500
Committer: nirzari <ni...@redhat.com>
Committed: Sun Apr 9 22:46:39 2017 -0400

----------------------------------------------------------------------
 libcloud/compute/drivers/ec2.py                 | 228 +++++++++++++++++++
 .../ec2/describe_import_snapshot_tasks.xml      |  18 ++
 .../describe_import_snapshot_tasks_active.xml   |  17 ++
 .../compute/fixtures/ec2/import_snapshot.xml    |  16 ++
 libcloud/test/compute/test_ec2.py               |  41 ++++
 5 files changed, 320 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/0e73f775/libcloud/compute/drivers/ec2.py
----------------------------------------------------------------------
diff --git a/libcloud/compute/drivers/ec2.py b/libcloud/compute/drivers/ec2.py
index 466b802..461248c 100644
--- a/libcloud/compute/drivers/ec2.py
+++ b/libcloud/compute/drivers/ec2.py
@@ -22,6 +22,7 @@ import sys
 import base64
 import copy
 import warnings
+import time
 
 try:
     from lxml import etree as ET
@@ -67,6 +68,7 @@ __all__ = [
     'EC2NodeLocation',
     'EC2ReservedNode',
     'EC2SecurityGroup',
+    'EC2ImportSnapshotTask',
     'EC2PlacementGroup',
     'EC2Network',
     'EC2NetworkSubnet',
@@ -2872,6 +2874,22 @@ class EC2SecurityGroup(object):
                 % (self.id, self.name))
 
 
+class EC2ImportSnapshotTask(object):
+    """
+    Represents information about a describe_import_snapshot_task.
+
+    Note: This class is EC2 specific.
+    """
+
+    def __init__(self, status, snapshotId):
+        self.status = status
+        self.snapshotId = snapshotId
+
+    def __repr__(self):
+        return (('<EC2SecurityGroup: status=%s, snapshotId=%s')
+                % (self.status, self.snapshotId))
+
+
 class EC2PlacementGroup(object):
     """
     Represents information about a Placement Grous
@@ -3906,6 +3924,138 @@ class BaseEC2NodeDriver(NodeDriver):
         response = self.connection.request(self.path, params=params).object
         return self._get_boolean(response)
 
+    def ex_import_snapshot(self, client_data=None,
+                           client_token=None, description=None,
+                           disk_container=None, dry_run=None, role_name=None):
+        """
+        Imports a disk into an EBS snapshot. More information can be found
+        at https://goo.gl/sbXkYA.
+
+        :param  client_data: Describes the client specific data (optional)
+        :type   client_data: ``dict``
+
+        :param  client_token: The token to enable idempotency for VM
+                import requests.(optional)
+        :type   client_token: ``str``
+
+        :param  description: The description string for the
+                             import snapshot task.(optional)
+        :type   description: ``str``
+
+        :param  disk_container:The disk container object for the
+                              import snapshot request.
+        :type   disk_container:``dict``
+
+        :param  dry_run: Checks whether you have the permission for
+                        the action, without actually making the request,
+                        and provides an error response.(optional)
+        :type   dry_run: ``bool``
+
+        :param  role_name: The name of the role to use when not using the
+                          default role, 'vmimport'.(optional)
+        :type   role_name: ``str``
+
+        :rtype: :class: ``VolumeSnapshot``
+        """
+
+        params = {'Action': 'ImportSnapshot'}
+
+        if client_data is not None:
+            params.update(self._get_client_date_params(client_data))
+
+        if client_token is not None:
+            params['ClientToken'] = client_token
+
+        if description is not None:
+            params['Description'] = description
+
+        if disk_container is not None:
+            params.update(self._get_disk_container_params(disk_container))
+
+        if dry_run is not None:
+            params['DryRun'] = dry_run
+
+        if role_name is not None:
+            params['RoleName'] = role_name
+
+        importSnapshot = self.connection.request(self.path,
+                                                 params=params).object
+
+        importTaskId = findtext(element=importSnapshot,
+                                xpath='importTaskId',
+                                namespace=NAMESPACE)
+
+        volumeSnapshot = self._wait_for_import_snapshot_completion(
+            import_task_id=importTaskId, timeout=1800, interval=15)
+
+        return volumeSnapshot
+
+    def _wait_for_import_snapshot_completion(self,
+                                             import_task_id,
+                                             timeout=1800,
+                                             interval=15):
+        """
+        It waits for import snapshot to be completed
+
+        :param import_task_id: Import task Id for the
+                               current Import Snapshot Task
+        :type import_task_id: ``str``
+
+        :param timeout: Timeout value for snapshot generation
+        :type timeout: ``float``
+
+        :param interval: Time interval for repetative describe
+                         import snapshot tasks requests
+        :type interval: ``float``
+
+        :rtype: :class:``VolumeSnapshot``
+        """
+        start_time = time.time()
+        snapshotId = None
+        while snapshotId is None:
+            if (time.time() - start_time >= timeout):
+                raise Exception('Timeout while waiting '
+                                'for import task Id %s'
+                                % import_task_id)
+            res = self.ex_describe_import_snapshot_tasks(import_task_id)
+            snapshotId = res.snapshotId
+
+            if snapshotId is None:
+                time.sleep(interval)
+
+        volumeSnapshot = VolumeSnapshot(snapshotId, driver=self)
+        return volumeSnapshot
+
+    def ex_describe_import_snapshot_tasks(self, import_task_id, dry_run=None):
+        """
+        Describes your import snapshot tasks. More information can be found
+        at https://goo.gl/CI0MdS.
+
+        :param import_task_id: Import task Id for the current
+                               Import Snapshot Task
+        :type import_task_id: ``str``
+
+        :param  dry_run: Checks whether you have the permission for
+                        the action, without actually making the request,
+                        and provides an error response.(optional)
+        :type   dry_run: ``bool``
+
+        :rtype: :class:``DescribeImportSnapshotTasks Object``
+
+        """
+        params = {'Action': 'DescribeImportSnapshotTasks'}
+
+        if dry_run is not None:
+            params['DryRun'] = dry_run
+
+        # This can be extended for multiple import snapshot tasks
+        params['ImportTaskId.1'] = import_task_id
+
+        res = self._to_import_snapshot_task(
+            self.connection.request(self.path, params=params).object
+        )
+        return res
+
     def ex_list_placement_groups(self, names=None):
         """
         A list of placement groups.
@@ -6040,6 +6190,19 @@ class BaseEC2NodeDriver(NodeDriver):
                               state=state,
                               name=name)
 
+    def _to_import_snapshot_task(self, element):
+        status = findtext(element=element, xpath='importSnapshotTaskSet/item/'
+                          'snapshotTaskDetail/status', namespace=NAMESPACE)
+
+        if status != 'completed':
+            snapshotId = None
+        else:
+            xpath = 'importSnapshotTaskSet/item/snapshotTaskDetail/snapshotId'
+            snapshotId = findtext(element=element, xpath=xpath,
+                                  namespace=NAMESPACE)
+
+        return EC2ImportSnapshotTask(status, snapshotId=snapshotId)
+
     def _to_key_pairs(self, elems):
         key_pairs = [self._to_key_pair(elem=elem) for elem in elems]
         return key_pairs
@@ -6696,6 +6859,71 @@ class BaseEC2NodeDriver(NodeDriver):
                                % (idx, k, key)] = str(value)
         return params
 
+    def _get_disk_container_params(self, disk_container):
+        """
+        Return a list of dictionaries with query parameters for
+        a valid disk container.
+
+        :param      disk_container: List of dictionaries with
+                                    disk_container details
+        :type       disk_container: ``list`` or ``dict``
+
+        :return:    Dictionary representation of the disk_container
+        :rtype:     ``dict``
+        """
+
+        if not isinstance(disk_container, (list, tuple)):
+            raise AttributeError('disk_container not list or tuple')
+
+        params = {}
+
+        for idx, content in enumerate(disk_container):
+            idx += 1  # We want 1-based indexes
+            if not isinstance(content, dict):
+                raise AttributeError(
+                    'content %s in disk_container not a dict' % content)
+
+            for k, v in content.items():
+                if not isinstance(v, dict):
+                    params['DiskContainer.%s' % (k)] = str(v)
+
+                else:
+                    for key, value in v.items():
+                        params['DiskContainer.%s.%s'
+                               % (k, key)] = str(value)
+
+        return params
+
+    def _get_client_data_params(self, client_data):
+        """
+        Return a dictionary with query parameters for
+        a valid client data.
+
+        :param      client_data: List of dictionaries with the disk
+                                 upload details
+        :type       client_data: ``dict``
+
+        :return:    Dictionary representation of the client data
+        :rtype:     ``dict``
+        """
+
+        if not isinstance(client_data, (list, tuple)):
+            raise AttributeError('client_data not list or tuple')
+
+        params = {}
+
+        for idx, content in enumerate(client_data):
+            idx += 1  # We want 1-based indexes
+            if not isinstance(content, dict):
+                raise AttributeError(
+                    'content %s in client_data'
+                    'not a dict' % content)
+
+            for k, v in content.items():
+                params['ClientData.%s' % (k)] = str(v)
+
+        return params
+
     def _get_common_security_group_params(self, group_id, protocol,
                                           from_port, to_port, cidr_ips,
                                           group_pairs):

http://git-wip-us.apache.org/repos/asf/libcloud/blob/0e73f775/libcloud/test/compute/fixtures/ec2/describe_import_snapshot_tasks.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ec2/describe_import_snapshot_tasks.xml b/libcloud/test/compute/fixtures/ec2/describe_import_snapshot_tasks.xml
new file mode 100644
index 0000000..86ae679
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ec2/describe_import_snapshot_tasks.xml
@@ -0,0 +1,18 @@
+<DescribeImportSnapshotTasksResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
+    <requestId>a8c44ac4-8e63-46e9-b8b2-8c72ce3d9b18</requestId>
+    <importSnapshotTaskSet>
+        <item>
+            <importTaskId>import-snap-fh7y6i6w</importTaskId>
+            <snapshotTaskDetail>
+                <snapshotId>snap-0ea83e8a87e138f39</snapshotId>
+                <format>RAW</format>
+                <diskImageSize>1.073741824E10</diskImageSize>
+                <userBucket>
+                    <s3Bucket>dummy-bucket</s3Bucket>
+                    <s3Key>dummy-key</s3Key>
+                </userBucket>
+                <status>completed</status>
+            </snapshotTaskDetail>
+        </item>
+    </importSnapshotTaskSet>
+</DescribeImportSnapshotTasksResponse>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/0e73f775/libcloud/test/compute/fixtures/ec2/describe_import_snapshot_tasks_active.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ec2/describe_import_snapshot_tasks_active.xml b/libcloud/test/compute/fixtures/ec2/describe_import_snapshot_tasks_active.xml
new file mode 100644
index 0000000..edb00cd
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ec2/describe_import_snapshot_tasks_active.xml
@@ -0,0 +1,17 @@
+<DescribeImportSnapshotTasksResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
+    <requestId>a8c44ac4-8e63-46e9-b8b2-8c72ce3d9b18</requestId>
+    <importSnapshotTaskSet>
+        <item>
+            <importTaskId>import-snap-fh7y6i6w</importTaskId>
+            <snapshotTaskDetail>
+                <format>RAW</format>
+                <diskImageSize>1.073741824E10</diskImageSize>
+                <userBucket>
+                    <s3Bucket>dummy-bucket</s3Bucket>
+                    <s3Key>dummy-key</s3Key>
+                </userBucket>
+                <status>active</status>
+            </snapshotTaskDetail>
+        </item>
+    </importSnapshotTaskSet>
+</DescribeImportSnapshotTasksResponse>

http://git-wip-us.apache.org/repos/asf/libcloud/blob/0e73f775/libcloud/test/compute/fixtures/ec2/import_snapshot.xml
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/fixtures/ec2/import_snapshot.xml b/libcloud/test/compute/fixtures/ec2/import_snapshot.xml
new file mode 100644
index 0000000..e2d51de
--- /dev/null
+++ b/libcloud/test/compute/fixtures/ec2/import_snapshot.xml
@@ -0,0 +1,16 @@
+<ImportSnapshotResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
+    <requestId>0d490bf6-19cf-4456-9c71-31d097faf46d</requestId>
+    <importTaskId>import-snap-fgsddbhv</importTaskId>
+        <snapshotTaskDetail>
+        <format>RAW</format>
+        <progress>3</progress>
+        <diskImageSize>0.0</diskImageSize>
+        <statusMessage>pending</statusMessage>
+        <userBucket>
+            <s3Bucket>dummy-bucket</s3Bucket>
+            <s3Key>dummy-key</s3Key>
+        </userBucket>
+        <status>active</status>
+    </snapshotTaskDetail>
+</ImportSnapshotResponse>
+

http://git-wip-us.apache.org/repos/asf/libcloud/blob/0e73f775/libcloud/test/compute/test_ec2.py
----------------------------------------------------------------------
diff --git a/libcloud/test/compute/test_ec2.py b/libcloud/test/compute/test_ec2.py
index 7702d82..6b942e3 100644
--- a/libcloud/test/compute/test_ec2.py
+++ b/libcloud/test/compute/test_ec2.py
@@ -562,6 +562,35 @@ class EC2Tests(LibcloudTestCase, TestCaseMixin):
                                               ena_support=True)
         self.assertEqual(image.id, 'ami-57c2fb3e')
 
+    def test_ex_import_snapshot(self):
+        disk_container = [{'Description': 'Dummy import snapshot task',
+                           'Format': 'raw',
+                           'UserBucket': {'S3Bucket': 'dummy-bucket', 'S3Key': 'dummy-key'}}]
+
+        snap = self.driver.ex_import_snapshot(disk_container=disk_container)
+        self.assertEqual(snap.id, 'snap-0ea83e8a87e138f39')
+
+    def test_wait_for_import_snapshot_completion(self):
+        snap = self.driver._wait_for_import_snapshot_completion(
+            import_task_id='import-snap-fhdysyq6')
+        self.assertEqual(snap.id, 'snap-0ea83e8a87e138f39')
+
+    def test_timeout_wait_for_import_snapshot_completion(self):
+        import_task_id = 'import-snap-fhdysyq6'
+        EC2MockHttp.type = 'timeout'
+        with self.assertRaises(Exception) as context:
+            self.driver._wait_for_import_snapshot_completion(
+                import_task_id=import_task_id, timeout=0.01, interval=0.001)
+        self.assertEqual('Timeout while waiting for import task Id %s'
+                         % import_task_id, str(context.exception))
+
+    def test_ex_describe_import_snapshot_tasks(self):
+        snap = self.driver.ex_describe_import_snapshot_tasks(
+            import_task_id='import-snap-fh7y6i6w<')
+
+        self.assertEqual(snap.snapshotId, 'snap-0ea83e8a87e138f39')
+        self.assertEqual(snap.status, 'completed')
+
     def test_ex_list_availability_zones(self):
         availability_zones = self.driver.ex_list_availability_zones()
         availability_zone = availability_zones[0]
@@ -1264,6 +1293,18 @@ class EC2MockHttp(MockHttpTestCase):
         body = self.fixtures.load('register_image.xml')
         return (httplib.OK, body, {}, httplib.responses[httplib.OK])
 
+    def _ImportSnapshot(self, method, url, body, headers):
+        body = self.fixtures.load('import_snapshot.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _DescribeImportSnapshotTasks(self, method, url, body, headers):
+        body = self.fixtures.load('describe_import_snapshot_tasks.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _timeout_DescribeImportSnapshotTasks(self, method, url, body, headers):
+        body = self.fixtures.load('describe_import_snapshot_tasks_active.xml')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
     def _ex_imageids_DescribeImages(self, method, url, body, headers):
         body = self.fixtures.load('describe_images_ex_imageids.xml')
         return (httplib.OK, body, {}, httplib.responses[httplib.OK])


[3/3] libcloud git commit: changes for #1023

Posted by an...@apache.org.
changes for #1023


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/a5731abb
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/a5731abb
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/a5731abb

Branch: refs/heads/trunk
Commit: a5731abb0665d0a823f93f6c5e2c8fe7aa601385
Parents: c3dde04
Author: Anthony Shaw <an...@apache.org>
Authored: Tue Apr 11 08:16:33 2017 +1000
Committer: Anthony Shaw <an...@apache.org>
Committed: Tue Apr 11 08:16:33 2017 +1000

----------------------------------------------------------------------
 CHANGES.rst | 4 ++++
 1 file changed, 4 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/a5731abb/CHANGES.rst
----------------------------------------------------------------------
diff --git a/CHANGES.rst b/CHANGES.rst
index 221f5f1..bb4acef 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -14,6 +14,10 @@ Common
 Compute
 ~~~~~~~
 
+- Added Import Snapshot and Describe Import Snapshot to EC2 compute driver
+  [GITHUB-1023]
+  (Nirzari Iyer)
+
 - Add price_monthly extra param to digitalocean sizes
   [GITHUB-1021]
   (Francisco Ros)


[2/3] libcloud git commit: Merge branch 'github-1023' into trunk Closes #1023

Posted by an...@apache.org.
Merge branch 'github-1023' into trunk
Closes #1023


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/c3dde041
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/c3dde041
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/c3dde041

Branch: refs/heads/trunk
Commit: c3dde041a5648cf5f4138561e75443758f292d25
Parents: 9f4d0a2 0e73f77
Author: Anthony Shaw <an...@apache.org>
Authored: Tue Apr 11 08:07:49 2017 +1000
Committer: Anthony Shaw <an...@apache.org>
Committed: Tue Apr 11 08:07:49 2017 +1000

----------------------------------------------------------------------
 libcloud/compute/drivers/ec2.py                 | 226 +++++++++++++++++++
 .../ec2/describe_import_snapshot_tasks.xml      |  18 ++
 .../describe_import_snapshot_tasks_active.xml   |  17 ++
 .../compute/fixtures/ec2/import_snapshot.xml    |  16 ++
 libcloud/test/compute/test_ec2.py               |  41 ++++
 5 files changed, 318 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/c3dde041/libcloud/compute/drivers/ec2.py
----------------------------------------------------------------------
diff --cc libcloud/compute/drivers/ec2.py
index 31b8b8e,461248c..5177327
--- a/libcloud/compute/drivers/ec2.py
+++ b/libcloud/compute/drivers/ec2.py
@@@ -6799,27 -6859,69 +6962,90 @@@ class BaseEC2NodeDriver(NodeDriver)
                                 % (idx, k, key)] = str(value)
          return params
  
 +    def _get_billing_product_params(self, billing_products):
 +        """
 +        Return a list of dictionaries with valid param for billing product.
 +
 +        :param      billing_product: List of billing code values(str)
 +        :type       billing product: ``list``
 +
 +        :return:    Dictionary representation of the billing product codes
 +        :rtype:     ``dict``
 +        """
 +
 +        if not isinstance(billing_products, (list, tuple)):
 +            raise AttributeError(
 +                'billing_products not list or tuple')
 +
 +        params = {}
 +
 +        for idx, v in enumerate(billing_products):
 +            idx += 1  # We want 1-based indexes
 +            params['BillingProduct.%d' % (idx)] = str(v)
 +
+     def _get_disk_container_params(self, disk_container):
+         """
+         Return a list of dictionaries with query parameters for
+         a valid disk container.
+ 
+         :param      disk_container: List of dictionaries with
+                                     disk_container details
+         :type       disk_container: ``list`` or ``dict``
+ 
+         :return:    Dictionary representation of the disk_container
+         :rtype:     ``dict``
+         """
+ 
+         if not isinstance(disk_container, (list, tuple)):
+             raise AttributeError('disk_container not list or tuple')
+ 
+         params = {}
+ 
+         for idx, content in enumerate(disk_container):
+             idx += 1  # We want 1-based indexes
+             if not isinstance(content, dict):
+                 raise AttributeError(
+                     'content %s in disk_container not a dict' % content)
+ 
+             for k, v in content.items():
+                 if not isinstance(v, dict):
+                     params['DiskContainer.%s' % (k)] = str(v)
+ 
+                 else:
+                     for key, value in v.items():
+                         params['DiskContainer.%s.%s'
+                                % (k, key)] = str(value)
+ 
+         return params
+ 
+     def _get_client_data_params(self, client_data):
+         """
+         Return a dictionary with query parameters for
+         a valid client data.
+ 
+         :param      client_data: List of dictionaries with the disk
+                                  upload details
+         :type       client_data: ``dict``
+ 
+         :return:    Dictionary representation of the client data
+         :rtype:     ``dict``
+         """
+ 
+         if not isinstance(client_data, (list, tuple)):
+             raise AttributeError('client_data not list or tuple')
+ 
+         params = {}
+ 
+         for idx, content in enumerate(client_data):
+             idx += 1  # We want 1-based indexes
+             if not isinstance(content, dict):
+                 raise AttributeError(
+                     'content %s in client_data'
+                     'not a dict' % content)
+ 
+             for k, v in content.items():
+                 params['ClientData.%s' % (k)] = str(v)
+ 
          return params
  
      def _get_common_security_group_params(self, group_id, protocol,

http://git-wip-us.apache.org/repos/asf/libcloud/blob/c3dde041/libcloud/test/compute/test_ec2.py
----------------------------------------------------------------------
diff --cc libcloud/test/compute/test_ec2.py
index 3e761a5,6b942e3..4d34e47
--- a/libcloud/test/compute/test_ec2.py
+++ b/libcloud/test/compute/test_ec2.py
@@@ -564,11 -559,38 +564,40 @@@ class EC2Tests(LibcloudTestCase, TestCa
                                                description='My Image',
                                                architecture='x86_64',
                                                block_device_mapping=mapping,
 -                                              ena_support=True)
 +                                              ena_support=True,
 +                                              billing_products=['ab-5dh78019'],
 +                                              sriov_net_support='simple')
          self.assertEqual(image.id, 'ami-57c2fb3e')
  
+     def test_ex_import_snapshot(self):
+         disk_container = [{'Description': 'Dummy import snapshot task',
+                            'Format': 'raw',
+                            'UserBucket': {'S3Bucket': 'dummy-bucket', 'S3Key': 'dummy-key'}}]
+ 
+         snap = self.driver.ex_import_snapshot(disk_container=disk_container)
+         self.assertEqual(snap.id, 'snap-0ea83e8a87e138f39')
+ 
+     def test_wait_for_import_snapshot_completion(self):
+         snap = self.driver._wait_for_import_snapshot_completion(
+             import_task_id='import-snap-fhdysyq6')
+         self.assertEqual(snap.id, 'snap-0ea83e8a87e138f39')
+ 
+     def test_timeout_wait_for_import_snapshot_completion(self):
+         import_task_id = 'import-snap-fhdysyq6'
+         EC2MockHttp.type = 'timeout'
+         with self.assertRaises(Exception) as context:
+             self.driver._wait_for_import_snapshot_completion(
+                 import_task_id=import_task_id, timeout=0.01, interval=0.001)
+         self.assertEqual('Timeout while waiting for import task Id %s'
+                          % import_task_id, str(context.exception))
+ 
+     def test_ex_describe_import_snapshot_tasks(self):
+         snap = self.driver.ex_describe_import_snapshot_tasks(
+             import_task_id='import-snap-fh7y6i6w<')
+ 
+         self.assertEqual(snap.snapshotId, 'snap-0ea83e8a87e138f39')
+         self.assertEqual(snap.status, 'completed')
+ 
      def test_ex_list_availability_zones(self):
          availability_zones = self.driver.ex_list_availability_zones()
          availability_zone = availability_zones[0]