Repository: ansible/ansible

Issue:
Failure in ec2_vpc_subnet module creating sunet with pre-existing one - InvalidSubnet.Conflict

##### ISSUE TYPE

 - Bug Report

##### COMPONENT NAME

ec2_vpc_subnet

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.0.0 (stable-2.3 0fe53eda86) last updated 2017/04/07 13:52:27 (GMT +100)
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/mikedd/dev/ansible/ansible-add-modules/library']
  python version = 2.7.12 (default, Jul  1 2016, 15:12:24) [GCC 5.4.0 20160609]
```

##### CONFIGURATION

this is standard stable taken straight out of git today

##### OS / ENVIRONMENT

Ubuntu 16.04 LTS + boto/boto3 from PIP

##### SUMMARY

In the case where I already have a smaller subnet existing inside the range of a subnet to be created ec_vpc_subnet fails to create the new subnet

##### STEPS TO REPRODUCE

1) create subnet 10.12.34.16/28 named Ansible Testing Subnet

tu 
<!--- Paste example playbooks or commands between quotes below -->
```
- hosts: localhost
  tasks:
  - name: Create subnet for use in testing
    ec2_vpc_subnet:
      region: us-east-1
      tags: create_subnet
      state: present
      vpc_id: vpc-12345678
      cidr: 10.12.34.0/24
      resource_tags:
        Name: Ansible Testing Subnet
    register: testing_subnet

```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS

Normally I'd expect ansible to create the subnet.  
If theres a decision not to I'd expect it to fail with a clear message saying what to do

##### ACTUAL RESULTS
Asible bombs out with error below

<!--- Paste verbatim command output between quotes below -->
```
 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] *******************************************************************************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************************************************
ok: [localhost]

TASK [Create subnet for use in testing] ********************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "Unable to create subnet 10.12.34.0/24, error: EC2ResponseError: 400 Bad Request\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response><Errors><Error><Code>InvalidSubnet.Conflict</Code><Message>The CIDR '10.12.34.0/24' conflicts with another subnet</Message></Error></Errors><RequestID>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</RequestID></Response>"}
	to retry, use: --limit @/home/mikedd/bug/ansible/ec2_vpc_subnet/ec2_vpc_subjet_cidr_fix_fail.retry

PLAY RECAP *************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=1  
```



Discussion:
Simple work around: delete the existing subnet and try again.

N.B. deleting an overlapping subnet is a kind of big thing.  I guess it's okay if the subnet names match?
cc @willthames
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I agree with Ansible failing here - it absolutely should not overwrite another subnet. However, I do agree that the error message should be more descriptive.

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py
--- a/lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py
@@ -113,19 +113,20 @@ class AnsibleTagCreationException(AnsibleVPCSubnetException):
 
 def get_subnet_info(subnet):
 
-    subnet_info = { 'id': subnet.id,
-                    'availability_zone': subnet.availability_zone,
-                    'available_ip_address_count': subnet.available_ip_address_count,
-                    'cidr_block': subnet.cidr_block,
-                    'default_for_az': subnet.defaultForAz,
-                    'map_public_ip_on_launch': subnet.mapPublicIpOnLaunch,
-                    'state': subnet.state,
-                    'tags': subnet.tags,
-                    'vpc_id': subnet.vpc_id
-                  }
+    subnet_info = {'id': subnet.id,
+                   'availability_zone': subnet.availability_zone,
+                   'available_ip_address_count': subnet.available_ip_address_count,
+                   'cidr_block': subnet.cidr_block,
+                   'default_for_az': subnet.defaultForAz,
+                   'map_public_ip_on_launch': subnet.mapPublicIpOnLaunch,
+                   'state': subnet.state,
+                   'tags': subnet.tags,
+                   'vpc_id': subnet.vpc_id
+                   }
 
     return subnet_info
 
+
 def subnet_exists(vpc_conn, subnet_id):
     filters = {'subnet-id': subnet_id}
     subnet = vpc_conn.get_all_subnets(filters=filters)
@@ -149,6 +150,8 @@ def create_subnet(vpc_conn, vpc_id, cidr, az, check_mode):
     except EC2ResponseError as e:
         if e.error_code == "DryRunOperation":
             subnet = None
+        elif e.error_code == "InvalidSubnet.Conflict":
+            raise AnsibleVPCSubnetCreationException("%s: the CIDR %s conflicts with another subnet with the VPC ID %s." % (e.error_code, cidr, vpc_id))
         else:
             raise AnsibleVPCSubnetCreationException(
                 'Unable to create subnet {0}, error: {1}'.format(cidr, e))
@@ -231,11 +234,11 @@ def main():
     argument_spec = ec2_argument_spec()
     argument_spec.update(
         dict(
-            az = dict(default=None, required=False),
-            cidr = dict(default=None, required=True),
-            state = dict(default='present', choices=['present', 'absent']),
-            tags = dict(default={}, required=False, type='dict', aliases=['resource_tags']),
-            vpc_id = dict(default=None, required=True)
+            az=dict(default=None, required=False),
+            cidr=dict(default=None, required=True),
+            state=dict(default='present', choices=['present', 'absent']),
+            tags=dict(default={}, required=False, type='dict', aliases=['resource_tags']),
+            vpc_id=dict(default=None, required=True)
         )
     )
 


Test patch:
diff --git a/test/sanity/pep8/legacy-files.txt b/test/sanity/pep8/legacy-files.txt
--- a/test/sanity/pep8/legacy-files.txt
+++ b/test/sanity/pep8/legacy-files.txt
@@ -181,7 +181,6 @@ lib/ansible/modules/cloud/amazon/ec2_vpc_nat_gateway.py
 lib/ansible/modules/cloud/amazon/ec2_vpc_net.py
 lib/ansible/modules/cloud/amazon/ec2_vpc_net_facts.py
 lib/ansible/modules/cloud/amazon/ec2_vpc_peer.py
-lib/ansible/modules/cloud/amazon/ec2_vpc_subnet.py
 lib/ansible/modules/cloud/amazon/ec2_vpc_vgw.py
 lib/ansible/modules/cloud/amazon/ec2_vpc_vgw_facts.py
 lib/ansible/modules/cloud/amazon/ec2_win_password.py