We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
cs_staticnat: Creating a static nat fails
##### Issue Type:

Bug Report
##### Plugin Name:

module:cloud/cloudstack/cs_staticnat.py
module:cloud/cloudstack/cs_staticnat2.py
##### Ansible Version:

2.0.0.2
##### Ansible Configuration:
##### Environment:

Ubuntu 14.04
##### Summary:

When trying to assign static ip which was obtained for a vpc with `cs_staticnat` module an error occurs: 
##### Steps To Reproduce:
- Create a cloudstack instance
- Assign this instance to a network in a `vpc`
- Obtain a static ip address for this vpc

Run the following Playbook

```
    - cs_staticnat:
        ip_address: {{ obtained_address }}
        state: present
        vm: "{{ my_vm }}"
        zone: "{{ my_zone }}"
```
##### Expected Results:

SUCCESS
##### Actual Results:

> fatal: [localhost]: FAILED! => {"changed": false, "failed": true, "msg": "CloudStackException: ('HTTP 431 response from CloudStack', <Response [431]>, {u'errorcode': 431, u'uuidList': [], u'cserrorcode': 4350, u'errortext': u'Unable to enable static nat for the ipAddress id=834 as ip is not associated with any network and no networkId is passed in'})"}


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cloud/cloudstack/cs_staticnat.py`
Content:
```
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 #
4 # (c) 2015, René Moser <mail@renemoser.net>
5 #
6 # This file is part of Ansible
7 #
8 # Ansible is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # Ansible is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
20 
21 DOCUMENTATION = '''
22 ---
23 module: cs_staticnat
24 short_description: Manages static NATs on Apache CloudStack based clouds.
25 description:
26     - Create, update and remove static NATs.
27 version_added: '2.0'
28 author: "René Moser (@resmo)"
29 options:
30   ip_address:
31     description:
32       - Public IP address the static NAT is assigned to.
33     required: true
34   vm:
35     description:
36       - Name of virtual machine which we make the static NAT for.
37       - Required if C(state=present).
38     required: false
39     default: null
40   vm_guest_ip:
41     description:
42       - VM guest NIC secondary IP address for the static NAT.
43     required: false
44     default: false
45   state:
46     description:
47       - State of the static NAT.
48     required: false
49     default: 'present'
50     choices: [ 'present', 'absent' ]
51   domain:
52     description:
53       - Domain the static NAT is related to.
54     required: false
55     default: null
56   account:
57     description:
58       - Account the static NAT is related to.
59     required: false
60     default: null
61   project:
62     description:
63       - Name of the project the static NAT is related to.
64     required: false
65     default: null
66   zone:
67     description:
68       - Name of the zone in which the virtual machine is in.
69       - If not set, default zone is used.
70     required: false
71     default: null
72   poll_async:
73     description:
74       - Poll async jobs until job has finished.
75     required: false
76     default: true
77 extends_documentation_fragment: cloudstack
78 '''
79 
80 EXAMPLES = '''
81 # create a static NAT: 1.2.3.4 -> web01
82 - local_action:
83     module: cs_staticnat
84     ip_address: 1.2.3.4
85     vm: web01
86 
87 # remove a static NAT
88 - local_action:
89     module: cs_staticnat
90     ip_address: 1.2.3.4
91     state: absent
92 '''
93 
94 RETURN = '''
95 ---
96 id:
97   description: UUID of the ip_address.
98   returned: success
99   type: string
100   sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f
101 ip_address:
102   description: Public IP address.
103   returned: success
104   type: string
105   sample: 1.2.3.4
106 vm_name:
107   description: Name of the virtual machine.
108   returned: success
109   type: string
110   sample: web-01
111 vm_display_name:
112   description: Display name of the virtual machine.
113   returned: success
114   type: string
115   sample: web-01
116 vm_guest_ip:
117   description: IP of the virtual machine.
118   returned: success
119   type: string
120   sample: 10.101.65.152
121 zone:
122   description: Name of zone the static NAT is related to.
123   returned: success
124   type: string
125   sample: ch-gva-2
126 project:
127   description: Name of project the static NAT is related to.
128   returned: success
129   type: string
130   sample: Production
131 account:
132   description: Account the static NAT is related to.
133   returned: success
134   type: string
135   sample: example account
136 domain:
137   description: Domain the static NAT is related to.
138   returned: success
139   type: string
140   sample: example domain
141 '''
142 
143 
144 try:
145     from cs import CloudStack, CloudStackException, read_config
146     has_lib_cs = True
147 except ImportError:
148     has_lib_cs = False
149 
150 # import cloudstack common
151 from ansible.module_utils.cloudstack import *
152 
153 
154 class AnsibleCloudStackStaticNat(AnsibleCloudStack):
155 
156     def __init__(self, module):
157         super(AnsibleCloudStackStaticNat, self).__init__(module)
158         self.returns = {
159             'virtualmachinedisplayname':    'vm_display_name',
160             'virtualmachinename':           'vm_name',
161             'ipaddress':                    'ip_address',
162             'vmipaddress':                  'vm_guest_ip',
163         }
164         self.vm_default_nic = None
165 
166 
167 # TODO: move it to cloudstack utils, also used in cs_portforward
168     def get_vm_guest_ip(self):
169         vm_guest_ip = self.module.params.get('vm_guest_ip')
170         default_nic = self.get_vm_default_nic()
171 
172         if not vm_guest_ip:
173             return default_nic['ipaddress']
174 
175         for secondary_ip in default_nic['secondaryip']:
176             if vm_guest_ip == secondary_ip['ipaddress']:
177                 return vm_guest_ip
178         self.module.fail_json(msg="Secondary IP '%s' not assigned to VM" % vm_guest_ip)
179 
180 
181 # TODO: move it to cloudstack utils, also used in cs_portforward
182     def get_vm_default_nic(self):
183         if self.vm_default_nic:
184             return self.vm_default_nic
185 
186         nics = self.cs.listNics(virtualmachineid=self.get_vm(key='id'))
187         if nics:
188             for n in nics['nic']:
189                 if n['isdefault']:
190                     self.vm_default_nic = n
191                     return self.vm_default_nic
192         self.module.fail_json(msg="No default IP address of VM '%s' found" % self.module.params.get('vm'))
193 
194 
195     def create_static_nat(self, ip_address):
196         self.result['changed'] = True
197         args = {}
198         args['virtualmachineid'] = self.get_vm(key='id')
199         args['ipaddressid'] = ip_address['id']
200         args['vmguestip'] = self.get_vm_guest_ip()
201         if not self.module.check_mode:
202             res = self.cs.enableStaticNat(**args)
203             if 'errortext' in res:
204                 self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
205 
206             # reset ip address and query new values
207             self.ip_address = None
208             ip_address = self.get_ip_address()
209         return ip_address
210 
211 
212     def update_static_nat(self, ip_address):
213         args = {}
214         args['virtualmachineid'] = self.get_vm(key='id')
215         args['ipaddressid'] = ip_address['id']
216         args['vmguestip'] = self.get_vm_guest_ip()
217 
218         # make an alias, so we can use _has_changed()
219         ip_address['vmguestip'] = ip_address['vmipaddress']
220         if self._has_changed(args, ip_address):
221             self.result['changed'] = True
222             if not self.module.check_mode:
223                 res = self.cs.disableStaticNat(ipaddressid=ip_address['id'])
224                 if 'errortext' in res:
225                     self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
226                 res = self._poll_job(res, 'staticnat')
227                 res = self.cs.enableStaticNat(**args)
228                 if 'errortext' in res:
229                     self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
230 
231                 # reset ip address and query new values
232                 self.ip_address = None
233                 ip_address = self.get_ip_address()
234         return ip_address
235 
236 
237     def present_static_nat(self):
238         ip_address = self.get_ip_address()
239         if not ip_address['isstaticnat']:
240             ip_address = self.create_static_nat(ip_address)
241         else:
242             ip_address = self.update_static_nat(ip_address)
243         return ip_address
244 
245 
246     def absent_static_nat(self):
247         ip_address = self.get_ip_address()
248         if ip_address['isstaticnat']:
249             self.result['changed'] = True
250             if not self.module.check_mode:
251                 res = self.cs.disableStaticNat(ipaddressid=ip_address['id'])
252                 if 'errortext' in res:
253                     self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
254                 poll_async = self.module.params.get('poll_async')
255                 if poll_async:
256                     res = self._poll_job(res, 'staticnat')
257         return ip_address
258 
259 
260 
261 def main():
262     argument_spec = cs_argument_spec()
263     argument_spec.update(dict(
264         ip_address = dict(required=True),
265         vm = dict(default=None),
266         vm_guest_ip = dict(default=None),
267         state = dict(choices=['present', 'absent'], default='present'),
268         zone = dict(default=None),
269         domain = dict(default=None),
270         account = dict(default=None),
271         project = dict(default=None),
272         poll_async = dict(type='bool', default=True),
273     ))
274 
275     module = AnsibleModule(
276         argument_spec=argument_spec,
277         required_together=cs_required_together(),
278         supports_check_mode=True
279     )
280 
281     if not has_lib_cs:
282         module.fail_json(msg="python library cs required: pip install cs")
283 
284     try:
285         acs_static_nat = AnsibleCloudStackStaticNat(module)
286 
287         state = module.params.get('state')
288         if state in ['absent']:
289             ip_address = acs_static_nat.absent_static_nat()
290         else:
291             ip_address = acs_static_nat.present_static_nat()
292 
293         result = acs_static_nat.get_result(ip_address)
294 
295     except CloudStackException as e:
296         module.fail_json(msg='CloudStackException: %s' % str(e))
297 
298     module.exit_json(**result)
299 
300 # import module snippets
301 from ansible.module_utils.basic import *
302 if __name__ == '__main__':
303     main()
304
```
--- 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/cloud/cloudstack/cs_staticnat.py b/cloud/cloudstack/cs_staticnat.py
--- a/cloud/cloudstack/cs_staticnat.py
+++ b/cloud/cloudstack/cs_staticnat.py
@@ -42,6 +42,12 @@
       - VM guest NIC secondary IP address for the static NAT.
     required: false
     default: false
+  network:
+    description:
+      - Network the IP address is related to.
+    required: false
+    default: null
+    version_added: "2.2"
   state:
     description:
       - State of the static NAT.
@@ -198,6 +204,7 @@
         args['virtualmachineid'] = self.get_vm(key='id')
         args['ipaddressid'] = ip_address['id']
         args['vmguestip'] = self.get_vm_guest_ip()
+        args['networkid'] = self.get_network(key='id')
         if not self.module.check_mode:
             res = self.cs.enableStaticNat(**args)
             if 'errortext' in res:
@@ -223,7 +230,7 @@
                 res = self.cs.disableStaticNat(ipaddressid=ip_address['id'])
                 if 'errortext' in res:
                     self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
-                res = self._poll_job(res, 'staticnat')
+                self._poll_job(res, 'staticnat')
                 res = self.cs.enableStaticNat(**args)
                 if 'errortext' in res:
                     self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
@@ -253,17 +260,17 @@
                     self.module.fail_json(msg="Failed: '%s'" % res['errortext'])
                 poll_async = self.module.params.get('poll_async')
                 if poll_async:
-                    res = self._poll_job(res, 'staticnat')
+                    self._poll_job(res, 'staticnat')
         return ip_address
 
 
-
 def main():
     argument_spec = cs_argument_spec()
     argument_spec.update(dict(
         ip_address = dict(required=True),
         vm = dict(default=None),
         vm_guest_ip = dict(default=None),
+        network = dict(default=None),
         state = dict(choices=['present', 'absent'], default='present'),
         zone = dict(default=None),
         domain = dict(default=None),

{"golden_diff": "diff --git a/cloud/cloudstack/cs_staticnat.py b/cloud/cloudstack/cs_staticnat.py\n--- a/cloud/cloudstack/cs_staticnat.py\n+++ b/cloud/cloudstack/cs_staticnat.py\n@@ -42,6 +42,12 @@\n       - VM guest NIC secondary IP address for the static NAT.\n     required: false\n     default: false\n+  network:\n+    description:\n+      - Network the IP address is related to.\n+    required: false\n+    default: null\n+    version_added: \"2.2\"\n   state:\n     description:\n       - State of the static NAT.\n@@ -198,6 +204,7 @@\n         args['virtualmachineid'] = self.get_vm(key='id')\n         args['ipaddressid'] = ip_address['id']\n         args['vmguestip'] = self.get_vm_guest_ip()\n+        args['networkid'] = self.get_network(key='id')\n         if not self.module.check_mode:\n             res = self.cs.enableStaticNat(**args)\n             if 'errortext' in res:\n@@ -223,7 +230,7 @@\n                 res = self.cs.disableStaticNat(ipaddressid=ip_address['id'])\n                 if 'errortext' in res:\n                     self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n-                res = self._poll_job(res, 'staticnat')\n+                self._poll_job(res, 'staticnat')\n                 res = self.cs.enableStaticNat(**args)\n                 if 'errortext' in res:\n                     self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n@@ -253,17 +260,17 @@\n                     self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n                 poll_async = self.module.params.get('poll_async')\n                 if poll_async:\n-                    res = self._poll_job(res, 'staticnat')\n+                    self._poll_job(res, 'staticnat')\n         return ip_address\n \n \n-\n def main():\n     argument_spec = cs_argument_spec()\n     argument_spec.update(dict(\n         ip_address = dict(required=True),\n         vm = dict(default=None),\n         vm_guest_ip = dict(default=None),\n+        network = dict(default=None),\n         state = dict(choices=['present', 'absent'], default='present'),\n         zone = dict(default=None),\n         domain = dict(default=None),\n", "issue": "cs_staticnat: Creating a static nat fails\n##### Issue Type:\n\nBug Report\n##### Plugin Name:\n\nmodule:cloud/cloudstack/cs_staticnat.py\nmodule:cloud/cloudstack/cs_staticnat2.py\n##### Ansible Version:\n\n2.0.0.2\n##### Ansible Configuration:\n##### Environment:\n\nUbuntu 14.04\n##### Summary:\n\nWhen trying to assign static ip which was obtained for a vpc with `cs_staticnat` module an error occurs: \n##### Steps To Reproduce:\n- Create a cloudstack instance\n- Assign this instance to a network in a `vpc`\n- Obtain a static ip address for this vpc\n\nRun the following Playbook\n\n```\n    - cs_staticnat:\n        ip_address: {{ obtained_address }}\n        state: present\n        vm: \"{{ my_vm }}\"\n        zone: \"{{ my_zone }}\"\n```\n##### Expected Results:\n\nSUCCESS\n##### Actual Results:\n\n> fatal: [localhost]: FAILED! => {\"changed\": false, \"failed\": true, \"msg\": \"CloudStackException: ('HTTP 431 response from CloudStack', <Response [431]>, {u'errorcode': 431, u'uuidList': [], u'cserrorcode': 4350, u'errortext': u'Unable to enable static nat for the ipAddress id=834 as ip is not associated with any network and no networkId is passed in'})\"}\n\n", "before_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n#\n# (c) 2015, Ren\u00e9 Moser <mail@renemoser.net>\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\nDOCUMENTATION = '''\n---\nmodule: cs_staticnat\nshort_description: Manages static NATs on Apache CloudStack based clouds.\ndescription:\n    - Create, update and remove static NATs.\nversion_added: '2.0'\nauthor: \"Ren\u00e9 Moser (@resmo)\"\noptions:\n  ip_address:\n    description:\n      - Public IP address the static NAT is assigned to.\n    required: true\n  vm:\n    description:\n      - Name of virtual machine which we make the static NAT for.\n      - Required if C(state=present).\n    required: false\n    default: null\n  vm_guest_ip:\n    description:\n      - VM guest NIC secondary IP address for the static NAT.\n    required: false\n    default: false\n  state:\n    description:\n      - State of the static NAT.\n    required: false\n    default: 'present'\n    choices: [ 'present', 'absent' ]\n  domain:\n    description:\n      - Domain the static NAT is related to.\n    required: false\n    default: null\n  account:\n    description:\n      - Account the static NAT is related to.\n    required: false\n    default: null\n  project:\n    description:\n      - Name of the project the static NAT is related to.\n    required: false\n    default: null\n  zone:\n    description:\n      - Name of the zone in which the virtual machine is in.\n      - If not set, default zone is used.\n    required: false\n    default: null\n  poll_async:\n    description:\n      - Poll async jobs until job has finished.\n    required: false\n    default: true\nextends_documentation_fragment: cloudstack\n'''\n\nEXAMPLES = '''\n# create a static NAT: 1.2.3.4 -> web01\n- local_action:\n    module: cs_staticnat\n    ip_address: 1.2.3.4\n    vm: web01\n\n# remove a static NAT\n- local_action:\n    module: cs_staticnat\n    ip_address: 1.2.3.4\n    state: absent\n'''\n\nRETURN = '''\n---\nid:\n  description: UUID of the ip_address.\n  returned: success\n  type: string\n  sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f\nip_address:\n  description: Public IP address.\n  returned: success\n  type: string\n  sample: 1.2.3.4\nvm_name:\n  description: Name of the virtual machine.\n  returned: success\n  type: string\n  sample: web-01\nvm_display_name:\n  description: Display name of the virtual machine.\n  returned: success\n  type: string\n  sample: web-01\nvm_guest_ip:\n  description: IP of the virtual machine.\n  returned: success\n  type: string\n  sample: 10.101.65.152\nzone:\n  description: Name of zone the static NAT is related to.\n  returned: success\n  type: string\n  sample: ch-gva-2\nproject:\n  description: Name of project the static NAT is related to.\n  returned: success\n  type: string\n  sample: Production\naccount:\n  description: Account the static NAT is related to.\n  returned: success\n  type: string\n  sample: example account\ndomain:\n  description: Domain the static NAT is related to.\n  returned: success\n  type: string\n  sample: example domain\n'''\n\n\ntry:\n    from cs import CloudStack, CloudStackException, read_config\n    has_lib_cs = True\nexcept ImportError:\n    has_lib_cs = False\n\n# import cloudstack common\nfrom ansible.module_utils.cloudstack import *\n\n\nclass AnsibleCloudStackStaticNat(AnsibleCloudStack):\n\n    def __init__(self, module):\n        super(AnsibleCloudStackStaticNat, self).__init__(module)\n        self.returns = {\n            'virtualmachinedisplayname':    'vm_display_name',\n            'virtualmachinename':           'vm_name',\n            'ipaddress':                    'ip_address',\n            'vmipaddress':                  'vm_guest_ip',\n        }\n        self.vm_default_nic = None\n\n\n# TODO: move it to cloudstack utils, also used in cs_portforward\n    def get_vm_guest_ip(self):\n        vm_guest_ip = self.module.params.get('vm_guest_ip')\n        default_nic = self.get_vm_default_nic()\n\n        if not vm_guest_ip:\n            return default_nic['ipaddress']\n\n        for secondary_ip in default_nic['secondaryip']:\n            if vm_guest_ip == secondary_ip['ipaddress']:\n                return vm_guest_ip\n        self.module.fail_json(msg=\"Secondary IP '%s' not assigned to VM\" % vm_guest_ip)\n\n\n# TODO: move it to cloudstack utils, also used in cs_portforward\n    def get_vm_default_nic(self):\n        if self.vm_default_nic:\n            return self.vm_default_nic\n\n        nics = self.cs.listNics(virtualmachineid=self.get_vm(key='id'))\n        if nics:\n            for n in nics['nic']:\n                if n['isdefault']:\n                    self.vm_default_nic = n\n                    return self.vm_default_nic\n        self.module.fail_json(msg=\"No default IP address of VM '%s' found\" % self.module.params.get('vm'))\n\n\n    def create_static_nat(self, ip_address):\n        self.result['changed'] = True\n        args = {}\n        args['virtualmachineid'] = self.get_vm(key='id')\n        args['ipaddressid'] = ip_address['id']\n        args['vmguestip'] = self.get_vm_guest_ip()\n        if not self.module.check_mode:\n            res = self.cs.enableStaticNat(**args)\n            if 'errortext' in res:\n                self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n\n            # reset ip address and query new values\n            self.ip_address = None\n            ip_address = self.get_ip_address()\n        return ip_address\n\n\n    def update_static_nat(self, ip_address):\n        args = {}\n        args['virtualmachineid'] = self.get_vm(key='id')\n        args['ipaddressid'] = ip_address['id']\n        args['vmguestip'] = self.get_vm_guest_ip()\n\n        # make an alias, so we can use _has_changed()\n        ip_address['vmguestip'] = ip_address['vmipaddress']\n        if self._has_changed(args, ip_address):\n            self.result['changed'] = True\n            if not self.module.check_mode:\n                res = self.cs.disableStaticNat(ipaddressid=ip_address['id'])\n                if 'errortext' in res:\n                    self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n                res = self._poll_job(res, 'staticnat')\n                res = self.cs.enableStaticNat(**args)\n                if 'errortext' in res:\n                    self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n\n                # reset ip address and query new values\n                self.ip_address = None\n                ip_address = self.get_ip_address()\n        return ip_address\n\n\n    def present_static_nat(self):\n        ip_address = self.get_ip_address()\n        if not ip_address['isstaticnat']:\n            ip_address = self.create_static_nat(ip_address)\n        else:\n            ip_address = self.update_static_nat(ip_address)\n        return ip_address\n\n\n    def absent_static_nat(self):\n        ip_address = self.get_ip_address()\n        if ip_address['isstaticnat']:\n            self.result['changed'] = True\n            if not self.module.check_mode:\n                res = self.cs.disableStaticNat(ipaddressid=ip_address['id'])\n                if 'errortext' in res:\n                    self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n                poll_async = self.module.params.get('poll_async')\n                if poll_async:\n                    res = self._poll_job(res, 'staticnat')\n        return ip_address\n\n\n\ndef main():\n    argument_spec = cs_argument_spec()\n    argument_spec.update(dict(\n        ip_address = dict(required=True),\n        vm = dict(default=None),\n        vm_guest_ip = dict(default=None),\n        state = dict(choices=['present', 'absent'], default='present'),\n        zone = dict(default=None),\n        domain = dict(default=None),\n        account = dict(default=None),\n        project = dict(default=None),\n        poll_async = dict(type='bool', default=True),\n    ))\n\n    module = AnsibleModule(\n        argument_spec=argument_spec,\n        required_together=cs_required_together(),\n        supports_check_mode=True\n    )\n\n    if not has_lib_cs:\n        module.fail_json(msg=\"python library cs required: pip install cs\")\n\n    try:\n        acs_static_nat = AnsibleCloudStackStaticNat(module)\n\n        state = module.params.get('state')\n        if state in ['absent']:\n            ip_address = acs_static_nat.absent_static_nat()\n        else:\n            ip_address = acs_static_nat.present_static_nat()\n\n        result = acs_static_nat.get_result(ip_address)\n\n    except CloudStackException as e:\n        module.fail_json(msg='CloudStackException: %s' % str(e))\n\n    module.exit_json(**result)\n\n# import module snippets\nfrom ansible.module_utils.basic import *\nif __name__ == '__main__':\n    main()\n", "path": "cloud/cloudstack/cs_staticnat.py"}], "after_files": [{"content": "#!/usr/bin/python\n# -*- coding: utf-8 -*-\n#\n# (c) 2015, Ren\u00e9 Moser <mail@renemoser.net>\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\nDOCUMENTATION = '''\n---\nmodule: cs_staticnat\nshort_description: Manages static NATs on Apache CloudStack based clouds.\ndescription:\n    - Create, update and remove static NATs.\nversion_added: '2.0'\nauthor: \"Ren\u00e9 Moser (@resmo)\"\noptions:\n  ip_address:\n    description:\n      - Public IP address the static NAT is assigned to.\n    required: true\n  vm:\n    description:\n      - Name of virtual machine which we make the static NAT for.\n      - Required if C(state=present).\n    required: false\n    default: null\n  vm_guest_ip:\n    description:\n      - VM guest NIC secondary IP address for the static NAT.\n    required: false\n    default: false\n  network:\n    description:\n      - Network the IP address is related to.\n    required: false\n    default: null\n    version_added: \"2.2\"\n  state:\n    description:\n      - State of the static NAT.\n    required: false\n    default: 'present'\n    choices: [ 'present', 'absent' ]\n  domain:\n    description:\n      - Domain the static NAT is related to.\n    required: false\n    default: null\n  account:\n    description:\n      - Account the static NAT is related to.\n    required: false\n    default: null\n  project:\n    description:\n      - Name of the project the static NAT is related to.\n    required: false\n    default: null\n  zone:\n    description:\n      - Name of the zone in which the virtual machine is in.\n      - If not set, default zone is used.\n    required: false\n    default: null\n  poll_async:\n    description:\n      - Poll async jobs until job has finished.\n    required: false\n    default: true\nextends_documentation_fragment: cloudstack\n'''\n\nEXAMPLES = '''\n# create a static NAT: 1.2.3.4 -> web01\n- local_action:\n    module: cs_staticnat\n    ip_address: 1.2.3.4\n    vm: web01\n\n# remove a static NAT\n- local_action:\n    module: cs_staticnat\n    ip_address: 1.2.3.4\n    state: absent\n'''\n\nRETURN = '''\n---\nid:\n  description: UUID of the ip_address.\n  returned: success\n  type: string\n  sample: a6f7a5fc-43f8-11e5-a151-feff819cdc9f\nip_address:\n  description: Public IP address.\n  returned: success\n  type: string\n  sample: 1.2.3.4\nvm_name:\n  description: Name of the virtual machine.\n  returned: success\n  type: string\n  sample: web-01\nvm_display_name:\n  description: Display name of the virtual machine.\n  returned: success\n  type: string\n  sample: web-01\nvm_guest_ip:\n  description: IP of the virtual machine.\n  returned: success\n  type: string\n  sample: 10.101.65.152\nzone:\n  description: Name of zone the static NAT is related to.\n  returned: success\n  type: string\n  sample: ch-gva-2\nproject:\n  description: Name of project the static NAT is related to.\n  returned: success\n  type: string\n  sample: Production\naccount:\n  description: Account the static NAT is related to.\n  returned: success\n  type: string\n  sample: example account\ndomain:\n  description: Domain the static NAT is related to.\n  returned: success\n  type: string\n  sample: example domain\n'''\n\n\ntry:\n    from cs import CloudStack, CloudStackException, read_config\n    has_lib_cs = True\nexcept ImportError:\n    has_lib_cs = False\n\n# import cloudstack common\nfrom ansible.module_utils.cloudstack import *\n\n\nclass AnsibleCloudStackStaticNat(AnsibleCloudStack):\n\n    def __init__(self, module):\n        super(AnsibleCloudStackStaticNat, self).__init__(module)\n        self.returns = {\n            'virtualmachinedisplayname':    'vm_display_name',\n            'virtualmachinename':           'vm_name',\n            'ipaddress':                    'ip_address',\n            'vmipaddress':                  'vm_guest_ip',\n        }\n        self.vm_default_nic = None\n\n\n# TODO: move it to cloudstack utils, also used in cs_portforward\n    def get_vm_guest_ip(self):\n        vm_guest_ip = self.module.params.get('vm_guest_ip')\n        default_nic = self.get_vm_default_nic()\n\n        if not vm_guest_ip:\n            return default_nic['ipaddress']\n\n        for secondary_ip in default_nic['secondaryip']:\n            if vm_guest_ip == secondary_ip['ipaddress']:\n                return vm_guest_ip\n        self.module.fail_json(msg=\"Secondary IP '%s' not assigned to VM\" % vm_guest_ip)\n\n\n# TODO: move it to cloudstack utils, also used in cs_portforward\n    def get_vm_default_nic(self):\n        if self.vm_default_nic:\n            return self.vm_default_nic\n\n        nics = self.cs.listNics(virtualmachineid=self.get_vm(key='id'))\n        if nics:\n            for n in nics['nic']:\n                if n['isdefault']:\n                    self.vm_default_nic = n\n                    return self.vm_default_nic\n        self.module.fail_json(msg=\"No default IP address of VM '%s' found\" % self.module.params.get('vm'))\n\n\n    def create_static_nat(self, ip_address):\n        self.result['changed'] = True\n        args = {}\n        args['virtualmachineid'] = self.get_vm(key='id')\n        args['ipaddressid'] = ip_address['id']\n        args['vmguestip'] = self.get_vm_guest_ip()\n        args['networkid'] = self.get_network(key='id')\n        if not self.module.check_mode:\n            res = self.cs.enableStaticNat(**args)\n            if 'errortext' in res:\n                self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n\n            # reset ip address and query new values\n            self.ip_address = None\n            ip_address = self.get_ip_address()\n        return ip_address\n\n\n    def update_static_nat(self, ip_address):\n        args = {}\n        args['virtualmachineid'] = self.get_vm(key='id')\n        args['ipaddressid'] = ip_address['id']\n        args['vmguestip'] = self.get_vm_guest_ip()\n\n        # make an alias, so we can use _has_changed()\n        ip_address['vmguestip'] = ip_address['vmipaddress']\n        if self._has_changed(args, ip_address):\n            self.result['changed'] = True\n            if not self.module.check_mode:\n                res = self.cs.disableStaticNat(ipaddressid=ip_address['id'])\n                if 'errortext' in res:\n                    self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n                self._poll_job(res, 'staticnat')\n                res = self.cs.enableStaticNat(**args)\n                if 'errortext' in res:\n                    self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n\n                # reset ip address and query new values\n                self.ip_address = None\n                ip_address = self.get_ip_address()\n        return ip_address\n\n\n    def present_static_nat(self):\n        ip_address = self.get_ip_address()\n        if not ip_address['isstaticnat']:\n            ip_address = self.create_static_nat(ip_address)\n        else:\n            ip_address = self.update_static_nat(ip_address)\n        return ip_address\n\n\n    def absent_static_nat(self):\n        ip_address = self.get_ip_address()\n        if ip_address['isstaticnat']:\n            self.result['changed'] = True\n            if not self.module.check_mode:\n                res = self.cs.disableStaticNat(ipaddressid=ip_address['id'])\n                if 'errortext' in res:\n                    self.module.fail_json(msg=\"Failed: '%s'\" % res['errortext'])\n                poll_async = self.module.params.get('poll_async')\n                if poll_async:\n                    self._poll_job(res, 'staticnat')\n        return ip_address\n\n\ndef main():\n    argument_spec = cs_argument_spec()\n    argument_spec.update(dict(\n        ip_address = dict(required=True),\n        vm = dict(default=None),\n        vm_guest_ip = dict(default=None),\n        network = dict(default=None),\n        state = dict(choices=['present', 'absent'], default='present'),\n        zone = dict(default=None),\n        domain = dict(default=None),\n        account = dict(default=None),\n        project = dict(default=None),\n        poll_async = dict(type='bool', default=True),\n    ))\n\n    module = AnsibleModule(\n        argument_spec=argument_spec,\n        required_together=cs_required_together(),\n        supports_check_mode=True\n    )\n\n    if not has_lib_cs:\n        module.fail_json(msg=\"python library cs required: pip install cs\")\n\n    try:\n        acs_static_nat = AnsibleCloudStackStaticNat(module)\n\n        state = module.params.get('state')\n        if state in ['absent']:\n            ip_address = acs_static_nat.absent_static_nat()\n        else:\n            ip_address = acs_static_nat.present_static_nat()\n\n        result = acs_static_nat.get_result(ip_address)\n\n    except CloudStackException as e:\n        module.fail_json(msg='CloudStackException: %s' % str(e))\n\n    module.exit_json(**result)\n\n# import module snippets\nfrom ansible.module_utils.basic import *\nif __name__ == '__main__':\n    main()\n", "path": "cloud/cloudstack/cs_staticnat.py"}]}