We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ec2_facts throws str error in Python 3.5+
<!---
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
ec2_facts

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
```
# inventory file that I use via ansible-playbook -i
[all]
localhost

[all:vars]
ansible_python_interpreter=/usr/bin/python3
```

##### OS / ENVIRONMENT
Ubuntu 16.04.2 LTS
Python 3.5.2

##### SUMMARY
When trying to run `ec2_facts`, I get this error: `TypeError: a bytes-like object is required, not 'str'`
<!--- Explain the problem briefly -->

##### 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.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
# in any playbook
  - name: Gather facts
    action: ec2_facts
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
That ec2_facts would work.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
It errors out with `a bytes-like object is required, not 'str'`

Just as a note, I thought this was connected to #17038, but perhaps not.
ec2_facts throws str error in Python 3.5+
<!---
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
ec2_facts

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.0.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
```
# inventory file that I use via ansible-playbook -i
[all]
localhost

[all:vars]
ansible_python_interpreter=/usr/bin/python3
```

##### OS / ENVIRONMENT
Ubuntu 16.04.2 LTS
Python 3.5.2

##### SUMMARY
When trying to run `ec2_facts`, I get this error: `TypeError: a bytes-like object is required, not 'str'`
<!--- Explain the problem briefly -->

##### 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.
-->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
# in any playbook
  - name: Gather facts
    action: ec2_facts
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
That ec2_facts would work.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
It errors out with `a bytes-like object is required, not 'str'`

Just as a note, I thought this was connected to #17038, but perhaps not.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/ansible/modules/cloud/amazon/ec2_facts.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 
4 # This file is part of Ansible
5 #
6 # Ansible is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # Ansible is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
18 
19 ANSIBLE_METADATA = {'metadata_version': '1.0',
20                     'status': ['stableinterface'],
21                     'supported_by': 'curated'}
22 
23 
24 DOCUMENTATION = '''
25 ---
26 module: ec2_facts
27 short_description: Gathers facts about remote hosts within ec2 (aws)
28 version_added: "1.0"
29 options:
30     validate_certs:
31         description:
32             - If C(no), SSL certificates will not be validated. This should only be used
33               on personally controlled sites using self-signed certificates.
34         required: false
35         default: 'yes'
36         choices: ['yes', 'no']
37         version_added: '1.5.1'
38 description:
39      - This module fetches data from the metadata servers in ec2 (aws) as per
40        http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html.
41        The module must be called from within the EC2 instance itself.
42 notes:
43     - Parameters to filter on ec2_facts may be added later.
44 author: "Silviu Dicu (@silviud) <silviudicu@gmail.com>"
45 '''
46 
47 EXAMPLES = '''
48 # Conditional example
49 - name: Gather facts
50   ec2_facts:
51 
52 - name: Conditional
53   debug:
54     msg: "This instance is a t1.micro"
55   when: ansible_ec2_instance_type == "t1.micro"
56 '''
57 
58 import socket
59 import re
60 
61 socket.setdefaulttimeout(5)
62 
63 
64 class Ec2Metadata(object):
65     ec2_metadata_uri = 'http://169.254.169.254/latest/meta-data/'
66     ec2_sshdata_uri = 'http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key'
67     ec2_userdata_uri = 'http://169.254.169.254/latest/user-data/'
68 
69     AWS_REGIONS = ('ap-northeast-1',
70                    'ap-northeast-2',
71                    'ap-south-1',
72                    'ap-southeast-1',
73                    'ap-southeast-2',
74                    'ca-central-1',
75                    'eu-central-1',
76                    'eu-west-1',
77                    'eu-west-2',
78                    'sa-east-1',
79                    'us-east-1',
80                    'us-east-2',
81                    'us-west-1',
82                    'us-west-2',
83                    'us-gov-west-1',
84                    )
85 
86     def __init__(self, module, ec2_metadata_uri=None, ec2_sshdata_uri=None, ec2_userdata_uri=None):
87         self.module = module
88         self.uri_meta = ec2_metadata_uri or self.ec2_metadata_uri
89         self.uri_user = ec2_userdata_uri or self.ec2_userdata_uri
90         self.uri_ssh = ec2_sshdata_uri or self.ec2_sshdata_uri
91         self._data = {}
92         self._prefix = 'ansible_ec2_%s'
93 
94     def _fetch(self, url):
95         (response, info) = fetch_url(self.module, url, force=True)
96         if response:
97             data = response.read()
98         else:
99             data = None
100         return data
101 
102     def _mangle_fields(self, fields, uri, filter_patterns=['public-keys-0']):
103         new_fields = {}
104         for key, value in fields.items():
105             split_fields = key[len(uri):].split('/')
106             if len(split_fields) > 1 and split_fields[1]:
107                 new_key = "-".join(split_fields)
108                 new_fields[self._prefix % new_key] = value
109             else:
110                 new_key = "".join(split_fields)
111                 new_fields[self._prefix % new_key] = value
112         for pattern in filter_patterns:
113             for key in new_fields.keys():
114                 match = re.search(pattern, key)
115                 if match:
116                     new_fields.pop(key)
117         return new_fields
118 
119     def fetch(self, uri, recurse=True):
120         raw_subfields = self._fetch(uri)
121         if not raw_subfields:
122             return
123         subfields = raw_subfields.split('\n')
124         for field in subfields:
125             if field.endswith('/') and recurse:
126                 self.fetch(uri + field)
127             if uri.endswith('/'):
128                 new_uri = uri + field
129             else:
130                 new_uri = uri + '/' + field
131             if new_uri not in self._data and not new_uri.endswith('/'):
132                 content = self._fetch(new_uri)
133                 if field == 'security-groups':
134                     sg_fields = ",".join(content.split('\n'))
135                     self._data['%s' % (new_uri)] = sg_fields
136                 else:
137                     self._data['%s' % (new_uri)] = content
138 
139     def fix_invalid_varnames(self, data):
140         """Change ':'' and '-' to '_' to ensure valid template variable names"""
141         for (key, value) in data.items():
142             if ':' in key or '-' in key:
143                 newkey = key.replace(':', '_').replace('-', '_')
144                 del data[key]
145                 data[newkey] = value
146 
147     def add_ec2_region(self, data):
148         """Use the 'ansible_ec2_placement_availability_zone' key/value
149         pair to add 'ansible_ec2_placement_region' key/value pair with
150         the EC2 region name.
151         """
152 
153         # Only add a 'ansible_ec2_placement_region' key if the
154         # 'ansible_ec2_placement_availability_zone' exists.
155         zone = data.get('ansible_ec2_placement_availability_zone')
156         if zone is not None:
157             # Use the zone name as the region name unless the zone
158             # name starts with a known AWS region name.
159             region = zone
160             for r in self.AWS_REGIONS:
161                 if zone.startswith(r):
162                     region = r
163                     break
164             data['ansible_ec2_placement_region'] = region
165 
166     def run(self):
167         self.fetch(self.uri_meta)  # populate _data
168         data = self._mangle_fields(self._data, self.uri_meta)
169         data[self._prefix % 'user-data'] = self._fetch(self.uri_user)
170         data[self._prefix % 'public-key'] = self._fetch(self.uri_ssh)
171         self.fix_invalid_varnames(data)
172         self.add_ec2_region(data)
173         return data
174 
175 
176 def main():
177     argument_spec = url_argument_spec()
178 
179     module = AnsibleModule(
180         argument_spec=argument_spec,
181         supports_check_mode=True,
182     )
183 
184     ec2_facts = Ec2Metadata(module).run()
185     ec2_facts_result = dict(changed=False, ansible_facts=ec2_facts)
186 
187     module.exit_json(**ec2_facts_result)
188 
189 
190 # import module snippets
191 from ansible.module_utils.basic import *
192 from ansible.module_utils.urls import *
193 
194 if __name__ == '__main__':
195     main()
196
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/ansible/modules/cloud/amazon/ec2_facts.py b/lib/ansible/modules/cloud/amazon/ec2_facts.py
--- a/lib/ansible/modules/cloud/amazon/ec2_facts.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_facts.py
@@ -57,7 +57,7 @@
 
 import socket
 import re
-
+from ansible.module_utils._text import to_text
 socket.setdefaulttimeout(5)
 
 
@@ -97,7 +97,7 @@
             data = response.read()
         else:
             data = None
-        return data
+        return to_text(data)
 
     def _mangle_fields(self, fields, uri, filter_patterns=['public-keys-0']):
         new_fields = {}
@@ -110,7 +110,7 @@
                 new_key = "".join(split_fields)
                 new_fields[self._prefix % new_key] = value
         for pattern in filter_patterns:
-            for key in new_fields.keys():
+            for key in dict(new_fields):
                 match = re.search(pattern, key)
                 if match:
                     new_fields.pop(key)
@@ -138,11 +138,10 @@
 
     def fix_invalid_varnames(self, data):
         """Change ':'' and '-' to '_' to ensure valid template variable names"""
-        for (key, value) in data.items():
+        for key in data:
             if ':' in key or '-' in key:
                 newkey = key.replace(':', '_').replace('-', '_')
-                del data[key]
-                data[newkey] = value
+                data[newkey] = data.pop(key)
 
     def add_ec2_region(self, data):
         """Use the 'ansible_ec2_placement_availability_zone' key/value

{"golden_diff": "diff --git a/lib/ansible/modules/cloud/amazon/ec2_facts.py b/lib/ansible/modules/cloud/amazon/ec2_facts.py\n--- a/lib/ansible/modules/cloud/amazon/ec2_facts.py\n+++ b/lib/ansible/modules/cloud/amazon/ec2_facts.py\n@@ -57,7 +57,7 @@\n \n import socket\n import re\n-\n+from ansible.module_utils._text import to_text\n socket.setdefaulttimeout(5)\n \n \n@@ -97,7 +97,7 @@\n             data = response.read()\n         else:\n             data = None\n-        return data\n+        return to_text(data)\n \n     def _mangle_fields(self, fields, uri, filter_patterns=['public-keys-0']):\n         new_fields = {}\n@@ -110,7 +110,7 @@\n                 new_key = \"\".join(split_fields)\n                 new_fields[self._prefix % new_key] = value\n         for pattern in filter_patterns:\n-            for key in new_fields.keys():\n+            for key in dict(new_fields):\n                 match = re.search(pattern, key)\n                 if match:\n                     new_fields.pop(key)\n@@ -138,11 +138,10 @@\n \n     def fix_invalid_varnames(self, data):\n         \"\"\"Change ':'' and '-' to '_' to ensure valid template variable names\"\"\"\n-        for (key, value) in data.items():\n+        for key in data:\n             if ':' in key or '-' in key:\n                 newkey = key.replace(':', '_').replace('-', '_')\n-                del data[key]\n-                data[newkey] = value\n+                data[newkey] = data.pop(key)\n \n     def add_ec2_region(self, data):\n         \"\"\"Use the 'ansible_ec2_placement_availability_zone' key/value\n", "issue": "ec2_facts throws str error in Python 3.5+\n<!---\r\nVerify first that your issue/request is not already reported on GitHub.\r\nAlso test if the latest release, and master branch are affected too.\r\n-->\r\n\r\n##### ISSUE TYPE\r\n<!--- Pick one below and delete the rest: -->\r\n - Bug Report\r\n\r\n##### COMPONENT NAME\r\nec2_facts\r\n\r\n##### ANSIBLE VERSION\r\n<!--- Paste verbatim output from \u201cansible --version\u201d between quotes below -->\r\n```\r\nansible 2.3.0.0\r\n  config file = /etc/ansible/ansible.cfg\r\n  configured module search path = Default w/o overrides\r\n  python version = 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609]\r\n```\r\n\r\n##### CONFIGURATION\r\n```\r\n# inventory file that I use via ansible-playbook -i\r\n[all]\r\nlocalhost\r\n\r\n[all:vars]\r\nansible_python_interpreter=/usr/bin/python3\r\n```\r\n\r\n##### OS / ENVIRONMENT\r\nUbuntu 16.04.2 LTS\r\nPython 3.5.2\r\n\r\n##### SUMMARY\r\nWhen trying to run `ec2_facts`, I get this error: `TypeError: a bytes-like object is required, not 'str'`\r\n<!--- Explain the problem briefly -->\r\n\r\n##### STEPS TO REPRODUCE\r\n<!---\r\nFor bugs, show exactly how to reproduce the problem, using a minimal test-case.\r\nFor new features, show how the feature would be used.\r\n-->\r\n\r\n<!--- Paste example playbooks or commands between quotes below -->\r\n```yaml\r\n# in any playbook\r\n  - name: Gather facts\r\n    action: ec2_facts\r\n```\r\n\r\n<!--- You can also paste gist.github.com links for larger files -->\r\n\r\n##### EXPECTED RESULTS\r\n<!--- What did you expect to happen when running the steps above? -->\r\nThat ec2_facts would work.\r\n\r\n##### ACTUAL RESULTS\r\n<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->\r\nIt errors out with `a bytes-like object is required, not 'str'`\r\n\r\nJust as a note, I thought this was connected to #17038, but perhaps not.\nec2_facts throws str error in Python 3.5+\n<!---\r\nVerify first that your issue/request is not already reported on GitHub.\r\nAlso test if the latest release, and master branch are affected too.\r\n-->\r\n\r\n##### ISSUE TYPE\r\n<!--- Pick one below and delete the rest: -->\r\n - Bug Report\r\n\r\n##### COMPONENT NAME\r\nec2_facts\r\n\r\n##### ANSIBLE VERSION\r\n<!--- Paste verbatim output from \u201cansible --version\u201d between quotes below -->\r\n```\r\nansible 2.3.0.0\r\n  config file = /etc/ansible/ansible.cfg\r\n  configured module search path = Default w/o overrides\r\n  python version = 3.5.2 (default, Nov 17 2016, 17:05:23) [GCC 5.4.0 20160609]\r\n```\r\n\r\n##### CONFIGURATION\r\n```\r\n# inventory file that I use via ansible-playbook -i\r\n[all]\r\nlocalhost\r\n\r\n[all:vars]\r\nansible_python_interpreter=/usr/bin/python3\r\n```\r\n\r\n##### OS / ENVIRONMENT\r\nUbuntu 16.04.2 LTS\r\nPython 3.5.2\r\n\r\n##### SUMMARY\r\nWhen trying to run `ec2_facts`, I get this error: `TypeError: a bytes-like object is required, not 'str'`\r\n<!--- Explain the problem briefly -->\r\n\r\n##### STEPS TO REPRODUCE\r\n<!---\r\nFor bugs, show exactly how to reproduce the problem, using a minimal test-case.\r\nFor new features, show how the feature would be used.\r\n-->\r\n\r\n<!--- Paste example playbooks or commands between quotes below -->\r\n```yaml\r\n# in any playbook\r\n  - name: Gather facts\r\n    action: ec2_facts\r\n```\r\n\r\n<!--- You can also paste gist.github.com links for larger files -->\r\n\r\n##### EXPECTED RESULTS\r\n<!--- What did you expect to happen when running the steps above? -->\r\nThat ec2_facts would work.\r\n\r\n##### ACTUAL RESULTS\r\n<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->\r\nIt errors out with `a bytes-like object is required, not 'str'`\r\n\r\nJust as a note, I thought this was connected to #17038, but perhaps not.\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\nANSIBLE_METADATA = {'metadata_version': '1.0',\n                    'status': ['stableinterface'],\n                    'supported_by': 'curated'}\n\n\nDOCUMENTATION = '''\n---\nmodule: ec2_facts\nshort_description: Gathers facts about remote hosts within ec2 (aws)\nversion_added: \"1.0\"\noptions:\n    validate_certs:\n        description:\n            - If C(no), SSL certificates will not be validated. This should only be used\n              on personally controlled sites using self-signed certificates.\n        required: false\n        default: 'yes'\n        choices: ['yes', 'no']\n        version_added: '1.5.1'\ndescription:\n     - This module fetches data from the metadata servers in ec2 (aws) as per\n       http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html.\n       The module must be called from within the EC2 instance itself.\nnotes:\n    - Parameters to filter on ec2_facts may be added later.\nauthor: \"Silviu Dicu (@silviud) <silviudicu@gmail.com>\"\n'''\n\nEXAMPLES = '''\n# Conditional example\n- name: Gather facts\n  ec2_facts:\n\n- name: Conditional\n  debug:\n    msg: \"This instance is a t1.micro\"\n  when: ansible_ec2_instance_type == \"t1.micro\"\n'''\n\nimport socket\nimport re\n\nsocket.setdefaulttimeout(5)\n\n\nclass Ec2Metadata(object):\n    ec2_metadata_uri = 'http://169.254.169.254/latest/meta-data/'\n    ec2_sshdata_uri = 'http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key'\n    ec2_userdata_uri = 'http://169.254.169.254/latest/user-data/'\n\n    AWS_REGIONS = ('ap-northeast-1',\n                   'ap-northeast-2',\n                   'ap-south-1',\n                   'ap-southeast-1',\n                   'ap-southeast-2',\n                   'ca-central-1',\n                   'eu-central-1',\n                   'eu-west-1',\n                   'eu-west-2',\n                   'sa-east-1',\n                   'us-east-1',\n                   'us-east-2',\n                   'us-west-1',\n                   'us-west-2',\n                   'us-gov-west-1',\n                   )\n\n    def __init__(self, module, ec2_metadata_uri=None, ec2_sshdata_uri=None, ec2_userdata_uri=None):\n        self.module = module\n        self.uri_meta = ec2_metadata_uri or self.ec2_metadata_uri\n        self.uri_user = ec2_userdata_uri or self.ec2_userdata_uri\n        self.uri_ssh = ec2_sshdata_uri or self.ec2_sshdata_uri\n        self._data = {}\n        self._prefix = 'ansible_ec2_%s'\n\n    def _fetch(self, url):\n        (response, info) = fetch_url(self.module, url, force=True)\n        if response:\n            data = response.read()\n        else:\n            data = None\n        return data\n\n    def _mangle_fields(self, fields, uri, filter_patterns=['public-keys-0']):\n        new_fields = {}\n        for key, value in fields.items():\n            split_fields = key[len(uri):].split('/')\n            if len(split_fields) > 1 and split_fields[1]:\n                new_key = \"-\".join(split_fields)\n                new_fields[self._prefix % new_key] = value\n            else:\n                new_key = \"\".join(split_fields)\n                new_fields[self._prefix % new_key] = value\n        for pattern in filter_patterns:\n            for key in new_fields.keys():\n                match = re.search(pattern, key)\n                if match:\n                    new_fields.pop(key)\n        return new_fields\n\n    def fetch(self, uri, recurse=True):\n        raw_subfields = self._fetch(uri)\n        if not raw_subfields:\n            return\n        subfields = raw_subfields.split('\\n')\n        for field in subfields:\n            if field.endswith('/') and recurse:\n                self.fetch(uri + field)\n            if uri.endswith('/'):\n                new_uri = uri + field\n            else:\n                new_uri = uri + '/' + field\n            if new_uri not in self._data and not new_uri.endswith('/'):\n                content = self._fetch(new_uri)\n                if field == 'security-groups':\n                    sg_fields = \",\".join(content.split('\\n'))\n                    self._data['%s' % (new_uri)] = sg_fields\n                else:\n                    self._data['%s' % (new_uri)] = content\n\n    def fix_invalid_varnames(self, data):\n        \"\"\"Change ':'' and '-' to '_' to ensure valid template variable names\"\"\"\n        for (key, value) in data.items():\n            if ':' in key or '-' in key:\n                newkey = key.replace(':', '_').replace('-', '_')\n                del data[key]\n                data[newkey] = value\n\n    def add_ec2_region(self, data):\n        \"\"\"Use the 'ansible_ec2_placement_availability_zone' key/value\n        pair to add 'ansible_ec2_placement_region' key/value pair with\n        the EC2 region name.\n        \"\"\"\n\n        # Only add a 'ansible_ec2_placement_region' key if the\n        # 'ansible_ec2_placement_availability_zone' exists.\n        zone = data.get('ansible_ec2_placement_availability_zone')\n        if zone is not None:\n            # Use the zone name as the region name unless the zone\n            # name starts with a known AWS region name.\n            region = zone\n            for r in self.AWS_REGIONS:\n                if zone.startswith(r):\n                    region = r\n                    break\n            data['ansible_ec2_placement_region'] = region\n\n    def run(self):\n        self.fetch(self.uri_meta)  # populate _data\n        data = self._mangle_fields(self._data, self.uri_meta)\n        data[self._prefix % 'user-data'] = self._fetch(self.uri_user)\n        data[self._prefix % 'public-key'] = self._fetch(self.uri_ssh)\n        self.fix_invalid_varnames(data)\n        self.add_ec2_region(data)\n        return data\n\n\ndef main():\n    argument_spec = url_argument_spec()\n\n    module = AnsibleModule(\n        argument_spec=argument_spec,\n        supports_check_mode=True,\n    )\n\n    ec2_facts = Ec2Metadata(module).run()\n    ec2_facts_result = dict(changed=False, ansible_facts=ec2_facts)\n\n    module.exit_json(**ec2_facts_result)\n\n\n# import module snippets\nfrom ansible.module_utils.basic import *\nfrom ansible.module_utils.urls import *\n\nif __name__ == '__main__':\n    main()\n", "path": "lib/ansible/modules/cloud/amazon/ec2_facts.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n\n# This file is part of Ansible\n#\n# Ansible is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# Ansible is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.\n\nANSIBLE_METADATA = {'metadata_version': '1.0',\n                    'status': ['stableinterface'],\n                    'supported_by': 'curated'}\n\n\nDOCUMENTATION = '''\n---\nmodule: ec2_facts\nshort_description: Gathers facts about remote hosts within ec2 (aws)\nversion_added: \"1.0\"\noptions:\n    validate_certs:\n        description:\n            - If C(no), SSL certificates will not be validated. This should only be used\n              on personally controlled sites using self-signed certificates.\n        required: false\n        default: 'yes'\n        choices: ['yes', 'no']\n        version_added: '1.5.1'\ndescription:\n     - This module fetches data from the metadata servers in ec2 (aws) as per\n       http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html.\n       The module must be called from within the EC2 instance itself.\nnotes:\n    - Parameters to filter on ec2_facts may be added later.\nauthor: \"Silviu Dicu (@silviud) <silviudicu@gmail.com>\"\n'''\n\nEXAMPLES = '''\n# Conditional example\n- name: Gather facts\n  ec2_facts:\n\n- name: Conditional\n  debug:\n    msg: \"This instance is a t1.micro\"\n  when: ansible_ec2_instance_type == \"t1.micro\"\n'''\n\nimport socket\nimport re\nfrom ansible.module_utils._text import to_text\nsocket.setdefaulttimeout(5)\n\n\nclass Ec2Metadata(object):\n    ec2_metadata_uri = 'http://169.254.169.254/latest/meta-data/'\n    ec2_sshdata_uri = 'http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key'\n    ec2_userdata_uri = 'http://169.254.169.254/latest/user-data/'\n\n    AWS_REGIONS = ('ap-northeast-1',\n                   'ap-northeast-2',\n                   'ap-south-1',\n                   'ap-southeast-1',\n                   'ap-southeast-2',\n                   'ca-central-1',\n                   'eu-central-1',\n                   'eu-west-1',\n                   'eu-west-2',\n                   'sa-east-1',\n                   'us-east-1',\n                   'us-east-2',\n                   'us-west-1',\n                   'us-west-2',\n                   'us-gov-west-1',\n                   )\n\n    def __init__(self, module, ec2_metadata_uri=None, ec2_sshdata_uri=None, ec2_userdata_uri=None):\n        self.module = module\n        self.uri_meta = ec2_metadata_uri or self.ec2_metadata_uri\n        self.uri_user = ec2_userdata_uri or self.ec2_userdata_uri\n        self.uri_ssh = ec2_sshdata_uri or self.ec2_sshdata_uri\n        self._data = {}\n        self._prefix = 'ansible_ec2_%s'\n\n    def _fetch(self, url):\n        (response, info) = fetch_url(self.module, url, force=True)\n        if response:\n            data = response.read()\n        else:\n            data = None\n        return to_text(data)\n\n    def _mangle_fields(self, fields, uri, filter_patterns=['public-keys-0']):\n        new_fields = {}\n        for key, value in fields.items():\n            split_fields = key[len(uri):].split('/')\n            if len(split_fields) > 1 and split_fields[1]:\n                new_key = \"-\".join(split_fields)\n                new_fields[self._prefix % new_key] = value\n            else:\n                new_key = \"\".join(split_fields)\n                new_fields[self._prefix % new_key] = value\n        for pattern in filter_patterns:\n            for key in dict(new_fields):\n                match = re.search(pattern, key)\n                if match:\n                    new_fields.pop(key)\n        return new_fields\n\n    def fetch(self, uri, recurse=True):\n        raw_subfields = self._fetch(uri)\n        if not raw_subfields:\n            return\n        subfields = raw_subfields.split('\\n')\n        for field in subfields:\n            if field.endswith('/') and recurse:\n                self.fetch(uri + field)\n            if uri.endswith('/'):\n                new_uri = uri + field\n            else:\n                new_uri = uri + '/' + field\n            if new_uri not in self._data and not new_uri.endswith('/'):\n                content = self._fetch(new_uri)\n                if field == 'security-groups':\n                    sg_fields = \",\".join(content.split('\\n'))\n                    self._data['%s' % (new_uri)] = sg_fields\n                else:\n                    self._data['%s' % (new_uri)] = content\n\n    def fix_invalid_varnames(self, data):\n        \"\"\"Change ':'' and '-' to '_' to ensure valid template variable names\"\"\"\n        for key in data:\n            if ':' in key or '-' in key:\n                newkey = key.replace(':', '_').replace('-', '_')\n                data[newkey] = data.pop(key)\n\n    def add_ec2_region(self, data):\n        \"\"\"Use the 'ansible_ec2_placement_availability_zone' key/value\n        pair to add 'ansible_ec2_placement_region' key/value pair with\n        the EC2 region name.\n        \"\"\"\n\n        # Only add a 'ansible_ec2_placement_region' key if the\n        # 'ansible_ec2_placement_availability_zone' exists.\n        zone = data.get('ansible_ec2_placement_availability_zone')\n        if zone is not None:\n            # Use the zone name as the region name unless the zone\n            # name starts with a known AWS region name.\n            region = zone\n            for r in self.AWS_REGIONS:\n                if zone.startswith(r):\n                    region = r\n                    break\n            data['ansible_ec2_placement_region'] = region\n\n    def run(self):\n        self.fetch(self.uri_meta)  # populate _data\n        data = self._mangle_fields(self._data, self.uri_meta)\n        data[self._prefix % 'user-data'] = self._fetch(self.uri_user)\n        data[self._prefix % 'public-key'] = self._fetch(self.uri_ssh)\n        self.fix_invalid_varnames(data)\n        self.add_ec2_region(data)\n        return data\n\n\ndef main():\n    argument_spec = url_argument_spec()\n\n    module = AnsibleModule(\n        argument_spec=argument_spec,\n        supports_check_mode=True,\n    )\n\n    ec2_facts = Ec2Metadata(module).run()\n    ec2_facts_result = dict(changed=False, ansible_facts=ec2_facts)\n\n    module.exit_json(**ec2_facts_result)\n\n\n# import module snippets\nfrom ansible.module_utils.basic import *\nfrom ansible.module_utils.urls import *\n\nif __name__ == '__main__':\n    main()\n", "path": "lib/ansible/modules/cloud/amazon/ec2_facts.py"}]}