Repository: ansible/ansible

Issue:
ec2_vpc_route_table: if an existing route is propagated, attempts to replace it will fail 
##### SUMMARY
If a route is preexisting and its Origin is "EnableVgwRoutePropagation", it is not possible to replace it.  Instead, the module should attempt to create the new route.  

In our specific use case, we are trying to add a static vpc peering connection to the route table, to take priority over a propagated VGW route.

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
ec2_vpc_route_table

##### ANSIBLE VERSION
```
ansible 2.6.1
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/dse09/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Jul 13 2018, 13:06:57) [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)]
```

##### CONFIGURATION

##### OS / ENVIRONMENT
CentOS 7

##### STEPS TO REPRODUCE
Need an environment that has propagated BGP routes.  Then attempt to add a static route to the route table with one of the propagated CIDRs:
```yaml
    - name: Set up private subnet route table
      ec2_vpc_route_table:
        vpc_id: "{{ vpc_id }}"
        propagating_vgw_ids: "{{ vgw_id }}"
        tags:
          Name: "stage-private-rt"
        routes:
          - dest: "10.50.27.0/24"
            vpc_peering_connection_id: "pcx-905168f9"
      register: vpc_route_table
```

##### EXPECTED RESULTS
I'd expect the overlapping route to be created.

##### ACTUAL RESULTS
```
TASK [Set up private subnet route table] ********************************************************************************************************************************************************************************************
task path: /home/dse09/src/vtp-aws-infra/test2.yml:18
Using module file /usr/lib/python2.7/site-packages/ansible/modules/cloud/amazon/ec2_vpc_route_table.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: dse09
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python2 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_f7ocmU/ansible_module_ec2_vpc_route_table.py", line 481, in ensure_routes
    **route_spec)
  File "/usr/lib/python2.7/site-packages/botocore/client.py", line 314, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/lib/python2.7/site-packages/botocore/client.py", line 612, in _make_api_call
    raise error_class(parsed_response, operation_name)
ClientError: An error occurred (InvalidParameterValue) when calling the ReplaceRoute operation: There is no route defined for '10.50.27.0/24' in the route table. Use CreateRoute instead.

fatal: [localhost]: FAILED! => {
    "boto3_version": "1.4.6",
    "botocore_version": "1.10.30",
    "changed": false,
    "error": {
        "code": "InvalidParameterValue",
        "message": "There is no route defined for '10.50.27.0/24' in the route table. Use CreateRoute instead."
    },
    "invocation": {
        "module_args": {
            "aws_access_key": "xxx",
            "aws_secret_key": "VALUE_SPECIFIED_IN_NO_LOG_PARAMETER",
            "ec2_url": null,
            "lookup": "tag",
            "profile": null,
            "propagating_vgw_ids": [
                "vgw-xxx"
            ],
            "purge_routes": true,
            "purge_subnets": true,
            "purge_tags": false,
            "region": "eu-west-1",
            "route_table_id": null,
            "routes": [
                {
                    "destination_cidr_block": "10.50.27.0/24",
                    "vpc_peering_connection_id": "pcx-xxx"
                }
            ],
            "security_token": null,
            "state": "present",
            "subnets": null,
            "tags": {
                "Name": "stage-private-rt"
            },
            "validate_certs": true,
            "vpc_id": "vpc-xxx"
        }
    },
    "msg": "Couldn't recreate route: An error occurred (InvalidParameterValue) when calling the ReplaceRoute operation: There is no route defined for '10.50.27.0/24' in the route table. Use CreateRoute instead.",
    "response_metadata": {
        "http_headers": {
            "connection": "close",
            "date": "Mon, 30 Jul 2018 09:11:05 GMT",
            "server": "AmazonEC2",
            "transfer-encoding": "chunked"
        },
        "http_status_code": 400,
        "request_id": "c63eac49-4bd8-4a56-b4d6-76273e16ae40",
        "retry_attempts": 0
    }
}
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @willthames @wimnat
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Hi @dseeley-sky,

Thank you for the issue, just so you are aware we have a dedicated Working Group for aws.
You can find other people interested in this in #ansible-aws on Freenode IRC
For more information about communities, meetings and agendas see https://github.com/ansible/community

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: community_workgroups --->

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py
--- a/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py
@@ -432,8 +432,9 @@ def index_of_matching_route(route_spec, routes_to_match):
     for i, route in enumerate(routes_to_match):
         if route_spec_matches_route(route_spec, route):
             return "exact", i
-        elif route_spec_matches_route_cidr(route_spec, route):
-            return "replace", i
+        elif 'Origin' in route_spec and route_spec['Origin'] != 'EnableVgwRoutePropagation':
+            if route_spec_matches_route_cidr(route_spec, route):
+                return "replace", i
 
 
 def ensure_routes(connection=None, module=None, route_table=None, route_specs=None,


Test patch: