Repository: ansible/ansible

Issue:
Modules for AWS ElastiCache are not working in cn-north-1
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
`elasticache_subnet_group` and `elasticache`

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.0.0
  config file = 
  configured module search path = Default w/o overrides
  python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->
Xubuntu 16.04

##### SUMMARY
<!--- Explain the problem briefly -->
Aws elasticache modules are not working in `cn-north-1`, the same commands works fine in `eu-west-1`. Aws cli works fine in both regions.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
Run module `elasticache_subnet_group` in `cn-north-1`. I have the same behaviour for playbook and single command.

<!--- Paste example playbooks or commands between quotes below -->
- Example of not working command ( [output](https://gist.github.com/sszwed/dd857ede3079055b77f8885493d1159b) ) :

```
ansible -c local -i localhost, localhost -m elasticache_subnet_group -a "description='Subnet group for Redis' name=redis state=present subnets=subnet-aaaaaaaa,subnet-bbbbbbbb region=cn-north-1 profile=china" -vvvv
```

- Example of working command ( [output](https://gist.github.com/sszwed/f2b2e9d64f73c608e9120e1321b75fd8) ):
```
ansible -c local -i localhost, localhost -m elasticache_subnet_group -a "description='Subnet group for Redis' name=redis state=present subnets=subnet-cccccccc,subnet-dddddddd region=eu-west-1 profile=ireland" -vvvv
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Module works for all regions.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
In region where command works it takes 2-3 seconds to create subnet group. In `cn-north-1` it takes about a minute to fail.
<!--- Paste verbatim command output between quotes below -->
```
No config file found; using defaults
Set default localhost to localhost
Loading callback plugin minimal of type stdout, v2.0 from /usr/local/lib/python2.7/dist-packages/ansible/plugins/callback/__init__.pyc
META: ran handlers
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/cloud/amazon/elasticache_subnet_group.py
<localhost> ESTABLISH LOCAL CONNECTION FOR USER: sszwed
<localhost> EXEC /bin/sh -c 'echo ~ && sleep 0'
<localhost> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/sszwed/.ansible/tmp/ansible-tmp-1493119113.83-14610261905763 `" && echo ansible-tmp-1493119113.83-14610261905763="` echo /home/sszwed/.ansible/tmp/ansible-tmp-1493119113.83-14610261905763 `" ) && sleep 0'
<localhost> PUT /tmp/tmp3G1P22 TO /home/sszwed/.ansible/tmp/ansible-tmp-1493119113.83-14610261905763/elasticache_subnet_group.py
<localhost> EXEC /bin/sh -c 'chmod u+x /home/sszwed/.ansible/tmp/ansible-tmp-1493119113.83-14610261905763/ /home/sszwed/.ansible/tmp/ansible-tmp-1493119113.83-14610261905763/elasticache_subnet_group.py && sleep 0'
<localhost> EXEC /bin/sh -c '/usr/bin/python /home/sszwed/.ansible/tmp/ansible-tmp-1493119113.83-14610261905763/elasticache_subnet_group.py; rm -rf "/home/sszwed/.ansible/tmp/ansible-tmp-1493119113.83-14610261905763/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_vLU7aO/ansible_module_elasticache_subnet_group.py", line 160, in <module>
    main()
  File "/tmp/ansible_vLU7aO/ansible_module_elasticache_subnet_group.py", line 129, in main
    matching_groups = conn.describe_cache_subnet_groups(group_name, max_records=100)
  File "/usr/local/lib/python2.7/dist-packages/boto/elasticache/layer1.py", line 851, in describe_cache_subnet_groups
    path='/', params=params)
  File "/usr/local/lib/python2.7/dist-packages/boto/elasticache/layer1.py", line 1658, in _make_request
    path='/', params=params)
  File "/usr/local/lib/python2.7/dist-packages/boto/connection.py", line 1116, in make_request
    return self._mexe(http_request)
  File "/usr/local/lib/python2.7/dist-packages/boto/connection.py", line 1030, in _mexe
    raise ex
socket.gaierror: [Errno -2] Name or service not known

localhost | FAILED! => {
    "changed": false, 
    "failed": true, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_vLU7aO/ansible_module_elasticache_subnet_group.py\", line 160, in <module>\n    main()\n  File \"/tmp/ansible_vLU7aO/ansible_module_elasticache_subnet_group.py\", line 129, in main\n    matching_groups = conn.describe_cache_subnet_groups(group_name, max_records=100)\n  File \"/usr/local/lib/python2.7/dist-packages/boto/elasticache/layer1.py\", line 851, in describe_cache_subnet_groups\n    path='/', params=params)\n  File \"/usr/local/lib/python2.7/dist-packages/boto/elasticache/layer1.py\", line 1658, in _make_request\n    path='/', params=params)\n  File \"/usr/local/lib/python2.7/dist-packages/boto/connection.py\", line 1116, in make_request\n    return self._mexe(http_request)\n  File \"/usr/local/lib/python2.7/dist-packages/boto/connection.py\", line 1030, in _mexe\n    raise ex\nsocket.gaierror: [Errno -2] Name or service not known\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE", 
    "rc": 0
}
```



Discussion:
I just ran into this too -  those modules are constructing the AWS service endpoint for elasticache themselves using the region you have set: `endpoint = "elasticache.%s.amazonaws.com" % self.region `

Since AWS China's service endpoints live in a different top level domain, amazonaws.com.cn, this doesn't work. I put a PR in for the Elasticache one to hand off responsibility for figuring out endpoints to the Boto library instead - happy to fix the rest of them too.

I'm guessing fixes for more than one module should each be in seperate PRs for the individual module maintainers to review?

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/elasticache_subnet_group.py b/lib/ansible/modules/cloud/amazon/elasticache_subnet_group.py
--- a/lib/ansible/modules/cloud/amazon/elasticache_subnet_group.py
+++ b/lib/ansible/modules/cloud/amazon/elasticache_subnet_group.py
@@ -71,8 +71,7 @@
 
 try:
     import boto
-    from boto.elasticache.layer1 import ElastiCacheConnection
-    from boto.regioninfo import RegionInfo
+    from boto.elasticache import connect_to_region
     from boto.exception import BotoServerError
     HAS_BOTO = True
 except ImportError:
@@ -115,9 +114,7 @@ def main():
 
     """Get an elasticache connection"""
     try:
-        endpoint = "elasticache.%s.amazonaws.com" % region
-        connect_region = RegionInfo(name=region, endpoint=endpoint)
-        conn = ElastiCacheConnection(region=connect_region, **aws_connect_kwargs)
+        conn = connect_to_region(region_name=region, **aws_connect_kwargs)
     except boto.exception.NoAuthHandlerFound as e:
         module.fail_json(msg=e.message)
 


Test patch: