Repository: boto/boto

Issue:
boto.ec2.connection.create_image() does not include BlockDeviceMapping parameters.
The API specification allows various BlockDeviceMapping parameters when creating an image: http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-CreateImage.html

However, these parameters are not currently available via boto.ec2.connection.create_image().

My specific use case is that I would like to create an image (AMI) of an instance but only include the root device and not specified attached EBS volumes.



Discussion:
The fix/workaround is trivial:

``` python
from boto import ec2
from boto.ec2.image import Image


class EC2Connection( ec2.connection.EC2Connection ):
    def create_image(self, instance_id, name,
                     description=None,
                     no_reboot=False,
                     dry_run=False,
                     block_device_map=None):
        params = {
            'InstanceId': instance_id,
            'Name': name }
        if description:
            params[ 'Description' ] = description
        if no_reboot:
            params[ 'NoReboot' ] = 'true'
        if dry_run:
            params[ 'DryRun' ] = 'true'
        # This is missing in boto 2.13
        if block_device_map:
            block_device_map.ec2_build_list_params( params )
        img = self.get_object( 'CreateImage', params, Image, verb='POST' )
        return img.id
```


Resolving patch:
diff --git a/boto/ec2/connection.py b/boto/ec2/connection.py
--- a/boto/ec2/connection.py
+++ b/boto/ec2/connection.py
@@ -364,7 +364,8 @@ def deregister_image(self, image_id, delete_snapshot=False, dry_run=False):
         return result
 
     def create_image(self, instance_id, name,
-                     description=None, no_reboot=False, dry_run=False):
+                     description=None, no_reboot=False,
+                     block_device_mapping=None, dry_run=False):
         """
         Will create an AMI from the instance in the running or stopped
         state.
@@ -386,6 +387,10 @@ def create_image(self, instance_id, name,
             responsibility of maintaining file system integrity is
             left to the owner of the instance.
 
+        :type block_device_mapping: :class:`boto.ec2.blockdevicemapping.BlockDeviceMapping`
+        :param block_device_mapping: A BlockDeviceMapping data structure
+            describing the EBS volumes associated with the Image.
+
         :type dry_run: bool
         :param dry_run: Set to True if the operation should not actually run.
 
@@ -398,6 +403,8 @@ def create_image(self, instance_id, name,
             params['Description'] = description
         if no_reboot:
             params['NoReboot'] = 'true'
+        if block_device_mapping:
+            block_device_mapping.ec2_build_list_params(params)
         if dry_run:
             params['DryRun'] = 'true'
         img = self.get_object('CreateImage', params, Image, verb='POST')


Test patch:
diff --git a/tests/unit/ec2/test_connection.py b/tests/unit/ec2/test_connection.py
--- a/tests/unit/ec2/test_connection.py
+++ b/tests/unit/ec2/test_connection.py
@@ -9,6 +9,7 @@
 import boto.ec2
 
 from boto.regioninfo import RegionInfo
+from boto.ec2.blockdevicemapping import BlockDeviceType, BlockDeviceMapping
 from boto.ec2.connection import EC2Connection
 from boto.ec2.snapshot import Snapshot
 from boto.ec2.reservedinstance import ReservedInstancesConfiguration
@@ -155,6 +156,42 @@ def test_serialized_api_args(self):
                                    'Version'])
 
 
+class TestCreateImage(TestEC2ConnectionBase):
+    def default_body(self):
+        return """<CreateImageResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-01/">
+   <requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
+   <imageId>ami-4fa54026</imageId>
+</CreateImageResponse>"""
+
+    def test_minimal(self):
+        self.set_http_response(status_code=200)
+        response = self.ec2.create_image(
+                'instance_id', 'name')
+        self.assert_request_parameters({
+            'Action': 'CreateImage',
+            'InstanceId': 'instance_id',
+            'Name': 'name'},
+             ignore_params_values=['AWSAccessKeyId', 'SignatureMethod',
+                                   'SignatureVersion', 'Timestamp',
+                                   'Version'])
+
+    def test_block_device_mapping(self):
+        self.set_http_response(status_code=200)
+        bdm = BlockDeviceMapping()
+        bdm['test'] = BlockDeviceType()
+        response = self.ec2.create_image(
+                'instance_id', 'name', block_device_mapping=bdm)
+        self.assert_request_parameters({
+            'Action': 'CreateImage',
+            'InstanceId': 'instance_id',
+            'Name': 'name',
+            'BlockDeviceMapping.1.DeviceName': 'test',
+            'BlockDeviceMapping.1.Ebs.DeleteOnTermination': 'false'},
+             ignore_params_values=['AWSAccessKeyId', 'SignatureMethod',
+                                   'SignatureVersion', 'Timestamp',
+                                   'Version'])
+
+
 class TestCancelReservedInstancesListing(TestEC2ConnectionBase):
     def default_body(self):
         return """