Repository: ansible/ansible

Issue:
cs_staticnat fails on second execution
##### ISSUE TYPE
 - Bug Report


##### COMPONENT NAME
cs_staticnat

##### ANSIBLE VERSION

```
2.2.1.0
```

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
N/A

##### SUMMARY
Trying to assign an ip with `cs_staticnat` to a network in VPC (I don't know if it plays a role), the
first time works out fine. On the second execution one gets:

> Unable to enable static nat for the ipAddress ... as ip is not associated with any network

##### STEPS TO REPRODUCE

Execute the following task multiple times

```yaml

- name: assign primary nat IPs
  cs_staticnat:
    zone: "MyZone"
    network: "MyNetwork"
    api_region: "MyRegion"
    ip_address: "MyAlreadyAcquiredIP"
    vm: "MyVM"
```

##### EXPECTED RESULTS
First time: `changed`
Second time : `ok`

##### ACTUAL RESULTS
- On the first execution I get: `changed` and the IP address is assigned
- On the second execution I get: 

```
failed: ...
"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=2816 as ip is not associated with any network and no networkId is passed in'})

```
and the StaticNAT  is not there anymore

- On the third execution same result as on the first execution

I suppose there is some error in Logic if state has not changed



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

Resolving patch:
diff --git a/lib/ansible/modules/cloud/cloudstack/cs_staticnat.py b/lib/ansible/modules/cloud/cloudstack/cs_staticnat.py
--- a/lib/ansible/modules/cloud/cloudstack/cs_staticnat.py
+++ b/lib/ansible/modules/cloud/cloudstack/cs_staticnat.py
@@ -55,7 +55,7 @@
     version_added: "2.2"
   vpc:
     description:
-      - Name of the VPC.
+      - VPC the network related to.
     required: false
     default: null
     version_added: "2.3"
@@ -157,8 +157,13 @@
   sample: example domain
 '''
 
-# import cloudstack common
-from ansible.module_utils.cloudstack import *
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.cloudstack import (
+    AnsibleCloudStack,
+    CloudStackException,
+    cs_argument_spec,
+    cs_required_together,
+)
 
 
 class AnsibleCloudStackStaticNat(AnsibleCloudStack):
@@ -166,20 +171,20 @@ class AnsibleCloudStackStaticNat(AnsibleCloudStack):
     def __init__(self, module):
         super(AnsibleCloudStackStaticNat, self).__init__(module)
         self.returns = {
-            'virtualmachinedisplayname':    'vm_display_name',
-            'virtualmachinename':           'vm_name',
-            'ipaddress':                    'ip_address',
-            'vmipaddress':                  'vm_guest_ip',
+            'virtualmachinedisplayname': 'vm_display_name',
+            'virtualmachinename': 'vm_name',
+            'ipaddress': 'ip_address',
+            'vmipaddress': 'vm_guest_ip',
         }
 
-
     def create_static_nat(self, ip_address):
         self.result['changed'] = True
-        args = {}
-        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')
+        args = {
+            'virtualmachineid': self.get_vm(key='id'),
+            'ipaddressid': ip_address['id'],
+            'vmguestip': self.get_vm_guest_ip(),
+            'networkid': self.get_network(key='id')
+        }
         if not self.module.check_mode:
             res = self.cs.enableStaticNat(**args)
             if 'errortext' in res:
@@ -190,14 +195,14 @@ def create_static_nat(self, ip_address):
             ip_address = self.get_ip_address()
         return ip_address
 
-
     def update_static_nat(self, ip_address):
-        args = {}
-        args['virtualmachineid'] = self.get_vm(key='id')
-        args['ipaddressid'] = ip_address['id']
-        args['vmguestip'] = self.get_vm_guest_ip()
-
-        # make an alias, so we can use _has_changed()
+        args = {
+            'virtualmachineid': self.get_vm(key='id'),
+            'ipaddressid': ip_address['id'],
+            'vmguestip': self.get_vm_guest_ip(),
+            'networkid': self.get_network(key='id')
+        }
+        # make an alias, so we can use has_changed()
         ip_address['vmguestip'] = ip_address['vmipaddress']
         if self.has_changed(args, ip_address, ['vmguestip', 'virtualmachineid']):
             self.result['changed'] = True
@@ -215,7 +220,6 @@ def update_static_nat(self, ip_address):
                 ip_address = self.get_ip_address()
         return ip_address
 
-
     def present_static_nat(self):
         ip_address = self.get_ip_address()
         if not ip_address['isstaticnat']:
@@ -224,7 +228,6 @@ def present_static_nat(self):
             ip_address = self.update_static_nat(ip_address)
         return ip_address
 
-
     def absent_static_nat(self):
         ip_address = self.get_ip_address()
         if ip_address['isstaticnat']:
@@ -242,17 +245,17 @@ def absent_static_nat(self):
 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),
-        vpc = dict(default=None),
-        state = dict(choices=['present', 'absent'], default='present'),
-        zone = dict(default=None),
-        domain = dict(default=None),
-        account = dict(default=None),
-        project = dict(default=None),
-        poll_async = dict(type='bool', default=True),
+        ip_address=dict(required=True),
+        vm=dict(),
+        vm_guest_ip=dict(),
+        network=dict(),
+        vpc=dict(),
+        state=dict(choices=['present', 'absent'], default='present'),
+        zone=dict(),
+        domain=dict(),
+        account=dict(),
+        project=dict(),
+        poll_async=dict(type='bool', default=True),
     ))
 
     module = AnsibleModule(
@@ -277,7 +280,6 @@ def main():
 
     module.exit_json(**result)
 
-# import module snippets
-from ansible.module_utils.basic import *
+
 if __name__ == '__main__':
     main()


Test patch: