diff --git a/ci/roles/router/tasks/main.yml b/ci/roles/router/tasks/main.yml
index 74aaed3fedc4e69b566af6892fd284ed3d40974f..32c8fc68ca048bdfd5047333b9bff2b398877bdc 100644
--- a/ci/roles/router/tasks/main.yml
+++ b/ci/roles/router/tasks/main.yml
@@ -1,27 +1,27 @@
 ---
-# Regular user operation
-- name: Create internal network
-  openstack.cloud.network:
-     cloud: "{{ cloud }}"
-     state: present
-     name: "{{ network_name }}"
-     external: false
-  register: internal_net
-
+# Ensure clean environment
 - name: Ensure router doesn't exist before tests
   openstack.cloud.router:
      cloud: "{{ cloud }}"
      state: absent
      name: "{{ router_name }}"
 
+- name: Find network
+  openstack.cloud.networks_info:
+     cloud: "{{ cloud }}"
+     name: "{{ network_name }}"
+  register: networks
+
 - name: Get ports in internal network
+  when: networks.networks|length > 0
   openstack.cloud.port_info:
     cloud: "{{ cloud }}"
     filters:
-      network_id: "{{ internal_net.id }}"
+      network_id: "{{ networks.networks.0.id }}"
   register: existing_ports
 
 - name: Ensure ports don't exist before tests
+  when: networks.networks|length > 0
   openstack.cloud.port:
     cloud: "{{ cloud }}"
     name: "{{ item.id }}"
@@ -35,23 +35,49 @@
     name: "{{ item.name }}"
   loop: "{{ test_subnets }}"
 
+# Regular user operation
+- name: Create internal network
+  openstack.cloud.network:
+     cloud: "{{ cloud }}"
+     state: present
+     name: "{{ network_name }}"
+     external: false
+
 - name: Create subnets 1-4
   openstack.cloud.subnet: "{{ item }}"
   loop: "{{ test_subnets }}"
 
+- name: Ensure router doesn't exist before tests
+  openstack.cloud.router:
+     cloud: "{{ cloud }}"
+     state: absent
+     name: "{{ router_name }}"
+
 - name: Create router
   openstack.cloud.router:
      cloud: "{{ cloud }}"
      state: present
      name: "{{ router_name }}"
-  register: result
+  register: router
 
 - name: Verify returned values
   assert:
-    that:
-      - item in result.router
+    that: item in router.router
   loop: "{{ expected_fields }}"
 
+- name: Gather routers info
+  openstack.cloud.routers_info:
+     cloud: "{{ cloud }}"
+     name: "{{ router_name }}"
+  register: info
+
+- name: Verify routers info
+  assert:
+    that:
+      - info.routers.0.name == router_name
+      - (info.routers.0.interfaces_info|length) == 0
+      - info.routers.0.is_admin_state_up
+
 - name: Update router (add interface)
   openstack.cloud.router:
      cloud: "{{ cloud }}"
@@ -67,45 +93,56 @@
      name: "{{ router_name }}"
      interfaces:
          - shade_subnet1
-  register: result
+  register: router
 
 - name: Assert idempotent module
   assert:
-    that: result is not changed
+    that: router is not changed
 
 - name: Gather routers info
   openstack.cloud.routers_info:
      cloud: "{{ cloud }}"
      name: "{{ router_name }}"
-     filters:
-       admin_state_up: true
-  register: result
+  register: info
 
 - name: Verify routers info
   assert:
     that:
-      - "result.routers.0.name == router_name"
-      - (result.routers.0.interfaces_info|length) == 1
+      - info.routers.0.name == router_name
+      - (info.routers.0.interfaces_info|length) == 1
+      - info.routers.0.is_admin_state_up
 
 - name: Verify existence of return values
   assert:
-    that:
-      - item in result.routers[0]
+    that: item in info.routers[0]
   loop: "{{ expected_fields + ['interfaces_info'] }}"
 
 - name: Gather routers info with filters
   openstack.cloud.routers_info:
       cloud: "{{ cloud }}"
       filters:
-        admin_state_up: true
+        is_admin_state_up: true
         name: "{{ router_name }}"
-  register: result_filter
+  register: info
 
-- name: Verify routers info with filter
+- name: Verify routers info with filters
   assert:
     that:
-      - "result_filter.routers.0.name == router_name"
-      - (result_filter.routers.0.interfaces_info|length) == 1
+      - info.routers.0.name == router_name
+      - info.routers.0.id == router.router.id
+      - (info.routers.0.interfaces_info|length) == 1
+
+- name: Gather routers info with other filters
+  openstack.cloud.routers_info:
+      cloud: "{{ cloud }}"
+      filters:
+        is_admin_state_up: false
+        name: "{{ router_name }}"
+  register: info
+
+- name: Verify routers info with other filters
+  assert:
+    that: info.routers == []
 
 - name: Update router (change interfaces)
   openstack.cloud.router:
@@ -132,29 +169,64 @@
          - net: '{{ network_name }}'
            subnet: shade_subnet3
          - shade_subnet4
-  register: result
+  register: router
 
 - name: Assert idempotent module
   assert:
-    that: result is not changed
+    that: router is not changed
 
 - name: Gather routers info
   openstack.cloud.routers_info:
      cloud: "{{ cloud }}"
      name: "{{ router_name }}"
-     filters:
-       is_admin_state_up: true
-  register: result
+  register: info
 
 - name: Verify routers info
   assert:
     that:
-      - "result.routers.0.name == router_name"
-      - (result.routers.0.interfaces_info|length) == 3
-      - result.routers.0.interfaces_info|map(attribute='ip_address')|sort|list ==
+      - info.routers.0.name == router_name
+      - info.routers.0.id == router.router.id
+      - (info.routers.0.interfaces_info|length) == 3
+      - info.routers.0.interfaces_info|map(attribute='ip_address')|sort|list ==
         ['10.10.10.1', '10.8.8.1', '10.9.9.1']
 
-- name: Update router (remove interface)
+- name: Update router (remove interfaces)
+  openstack.cloud.router:
+     cloud: "{{ cloud }}"
+     state: present
+     name: "{{ router_name }}"
+     interfaces:
+         - shade_subnet4
+
+- name: Update router (remove interfaces) again
+  openstack.cloud.router:
+     cloud: "{{ cloud }}"
+     state: present
+     name: "{{ router_name }}"
+     interfaces:
+         - shade_subnet4
+  register: router
+
+- name: Assert idempotent module
+  assert:
+    that: router is not changed
+
+- name: Gather routers info
+  openstack.cloud.routers_info:
+     cloud: "{{ cloud }}"
+     name: "{{ router_name }}"
+  register: info
+
+- name: Verify routers info
+  assert:
+    that:
+      - info.routers.0.name == router_name
+      - info.routers.0.id == router.router.id
+      - (info.routers.0.interfaces_info|length) == 1
+      - info.routers.0.interfaces_info|map(attribute='ip_address')|sort|list ==
+        ['10.10.10.1']
+
+- name: Update router (replace interfaces)
   openstack.cloud.router:
      cloud: "{{ cloud }}"
      state: present
@@ -164,7 +236,21 @@
            subnet: shade_subnet1
            portip: 10.7.7.1
 
-- name: Update router (remove interface) again
+- name: Gather routers info
+  openstack.cloud.routers_info:
+     cloud: "{{ cloud }}"
+     name: "{{ router_name }}"
+  register: info
+
+- name: Verify routers info
+  assert:
+    that:
+      - info.routers.0.name == router_name
+      - (info.routers.0.interfaces_info|length) == 1
+      - info.routers.0.interfaces_info|map(attribute='ip_address')|sort|list ==
+        ['10.7.7.1']
+
+- name: Update router (replace interfaces) again
   openstack.cloud.router:
      cloud: "{{ cloud }}"
      state: present
@@ -173,11 +259,11 @@
          - net: '{{ network_name }}'
            subnet: shade_subnet1
            portip: 10.7.7.1
-  register: result
+  register: router
 
 - name: Assert idempotent module
   assert:
-    that: result is not changed
+    that: router is not changed
 
 - name: Gather routers info
   openstack.cloud.routers_info:
@@ -185,14 +271,14 @@
      name: "{{ router_name }}"
      filters:
        is_admin_state_up: true
-  register: result
+  register: info
 
 - name: Verify routers info
   assert:
     that:
-      - "result.routers.0.name == router_name"
-      - (result.routers.0.interfaces_info|length) == 1
-      - result.routers.0.interfaces_info.0.ip_address == '10.7.7.1'
+      - info.routers.0.name == router_name
+      - (info.routers.0.interfaces_info|length) == 1
+      - info.routers.0.interfaces_info.0.ip_address == '10.7.7.1'
 
 # Admin operation
 - name: Create external network
@@ -230,14 +316,14 @@
      cloud: "{{ cloud }}"
      name: "{{ router_name }}"
      filters:
-       admin_state_up: true
-  register: result
+       is_admin_state_up: true
+  register: info
 
 - name: Verify routers info
   assert:
     that:
-      - "result.routers.0.name == router_name"
-      - (result.routers.0.interfaces_info|length) == 1
+      - info.routers.0.name == router_name
+      - (info.routers.0.interfaces_info|length) == 1
 
 - name: Update router (change external fixed ips)
   openstack.cloud.router:
@@ -258,15 +344,15 @@
      cloud: "{{ cloud }}"
      name: "{{ router_name }}"
      filters:
-       admin_state_up: true
-  register: result
+       is_admin_state_up: true
+  register: info
 
 - name: Verify routers info
   assert:
     that:
-      - "result.routers.0.name == router_name"
-      - (result.routers.0.external_gateway_info.external_fixed_ips|length) == 1
-      - result.routers.0.external_gateway_info.external_fixed_ips.0.ip_address == "10.6.6.100"
+      - info.routers.0.name == router_name
+      - (info.routers.0.external_gateway_info.external_fixed_ips|length) == 1
+      - info.routers.0.external_gateway_info.external_fixed_ips.0.ip_address == "10.6.6.100"
   when:
     - network_external
 
@@ -302,26 +388,26 @@
           ip: 10.6.6.101
   when:
     - network_external
-  register: result
+  register: router
 
 - name: Assert idempotent module
   assert:
-    that: result is not changed
+    that: router is not changed
 
 - name: Gather routers info
   openstack.cloud.routers_info:
      cloud: "{{ cloud }}"
      name: "{{ router_name }}"
      filters:
-       admin_state_up: true
-  register: result
+       is_admin_state_up: true
+  register: info
 
 - name: Verify routers info
   assert:
     that:
-      - "result.routers.0.name == router_name"
-      - (result.routers.0.external_gateway_info.external_fixed_ips|length) == 2
-      - result.routers.0.external_gateway_info.external_fixed_ips|map(attribute='ip_address')|sort|list ==
+      - info.routers.0.name == router_name
+      - (info.routers.0.external_gateway_info.external_fixed_ips|length) == 2
+      - info.routers.0.external_gateway_info.external_fixed_ips|map(attribute='ip_address')|sort|list ==
         ["10.6.6.100", "10.6.6.101"]
   when:
     - network_external
@@ -353,26 +439,26 @@
           ip: 10.6.6.101
   when:
     - network_external
-  register: result
+  register: router
 
 - name: Assert idempotent module
   assert:
-    that: result is not changed
+    that: router is not changed
 
 - name: Gather routers info
   openstack.cloud.routers_info:
      cloud: "{{ cloud }}"
      name: "{{ router_name }}"
      filters:
-       admin_state_up: true
-  register: result
+       is_admin_state_up: true
+  register: info
 
 - name: Verify routers info
   assert:
     that:
-      - "result.routers.0.name == router_name"
-      - (result.routers.0.external_gateway_info.external_fixed_ips|length) == 1
-      - result.routers.0.external_gateway_info.external_fixed_ips.0.ip_address == "10.6.6.101"
+      - info.routers.0.name == router_name
+      - (info.routers.0.external_gateway_info.external_fixed_ips|length) == 1
+      - info.routers.0.external_gateway_info.external_fixed_ips.0.ip_address == "10.6.6.101"
   when:
     - network_external
 
@@ -390,20 +476,20 @@
           ip: 10.6.6.101
   when:
     - network_external
-  register: result
 
 - name: Gather routers info
   openstack.cloud.routers_info:
      cloud: "{{ cloud }}"
      name: "{{ router_name }}"
      filters:
-       admin_state_up: true
-  register: result
+       is_admin_state_up: true
+  register: info
+
 - name: Verify routers info
   assert:
     that:
-      - "result.routers.0.name == router_name"
-      - "not result.routers.0.external_gateway_info.enable_snat"
+      - info.routers.0.name == router_name
+      - not info.routers.0.external_gateway_info.enable_snat
   when:
     - network_external
 
@@ -421,11 +507,11 @@
           ip: 10.6.6.101
   when:
     - network_external
-  register: result
+  register: router
 
 - name: Assert idempotent module
   assert:
-    that: result is not changed
+    that: router is not changed
 
 - name: Delete router
   openstack.cloud.router:
@@ -438,11 +524,11 @@
      cloud: "{{ cloud }}"
      state: absent
      name: "{{ router_name }}"
-  register: result
+  register: router
 
 - name: Assert idempotent module
   assert:
-    that: result is not changed
+    that: router is not changed
 
 - name: Create router with simple interface
   openstack.cloud.router:
@@ -451,11 +537,11 @@
      name: "{{ router_name }}"
      interfaces:
          - shade_subnet1
-  register: result
+  register: router
 
 - name: Assert changed
   assert:
-    that: result is changed
+    that: router is changed
 
 - name: Set portip in already assigned subnet
   openstack.cloud.router:
@@ -466,21 +552,21 @@
          - subnet: shade_subnet1
            net: "{{ network_name }}"
            portip: 10.7.7.42
-  register: result
+  register: router
 
 - name: Assert changed
   assert:
-    that: result is changed
+    that: router is changed
 
 - name: Gather routers info
   openstack.cloud.routers_info:
      cloud: "{{ cloud }}"
      name: "{{ router_name }}"
-  register: result
+  register: info
 
 - name: Verify routers info
   assert:
-    that: "'10.7.7.42' in result.routers[0].interfaces_info|map(attribute='ip_address')"
+    that: "'10.7.7.42' in info.routers[0].interfaces_info|map(attribute='ip_address')"
 
 - name: Unset portip in already assigned subnet
   openstack.cloud.router:
@@ -488,13 +574,15 @@
      state: present
      name: "{{ router_name }}"
      interfaces:
-         - subnet: shade_subnet1
-           net: "{{ network_name }}"
-  register: result
+       - subnet: shade_subnet1
+         net: "{{ network_name }}"
+  register: router
 
 - name: Assert not changed
   assert:
-    that: result is not changed
+    that: router is not changed
+
+# Cleanup environment
 
 - name: Delete router
   openstack.cloud.router:
@@ -502,11 +590,17 @@
      state: absent
      name: "{{ router_name }}"
 
+- name: Find network
+  openstack.cloud.networks_info:
+     cloud: "{{ cloud }}"
+     name: "{{ network_name }}"
+  register: networks
+
 - name: Get ports in internal network
   openstack.cloud.port_info:
     cloud: "{{ cloud }}"
     filters:
-      network_id: "{{ internal_net.id }}"
+      network_id: "{{ networks.networks.0.id }}"
   register: existing_ports
 
 - name: Clean up ports
diff --git a/plugins/modules/router.py b/plugins/modules/router.py
index 3041ae12ee3531ebe7d959c9b111fd02b9475e87..5d4bb3f07985f23b80496e43c322d252268651d3 100644
--- a/plugins/modules/router.py
+++ b/plugins/modules/router.py
@@ -14,89 +14,63 @@ description:
      routers to share the same name, this module enforces name uniqueness
      to be more user friendly.
 options:
-   state:
-     description:
-        - Indicate desired state of the resource
-     choices: ['present', 'absent']
-     default: present
-     type: str
-   name:
-     description:
-        - Name to be give to the router
-     required: true
-     type: str
-   enable_snat:
-     description:
+    enable_snat:
+      description:
         - Enable Source NAT (SNAT) attribute.
-     type: bool
-   is_admin_state_up:
-     description:
-        - Desired admin state of the created or existing router.
-     type: bool
-     default: 'yes'
-     aliases: [admin_state_up]
-   network:
-     description:
-        - Unique name or ID of the external gateway network.
-        - required I(interfaces) or I(enable_snat) are provided.
-     type: str
-   project:
-     description:
-        - Unique name or ID of the project.
-     type: str
-   external_gateway_info:
-     description:
+      type: bool
+    external_fixed_ips:
+      description:
+        - The IP address parameters for the external gateway network. Each
+          is a dictionary with the subnet name or ID (subnet) and the IP
+          address to assign on the subnet (ip_address). If no IP is specified,
+          one is automatically assigned from that subnet.
+      type: list
+      elements: dict
+      suboptions:
+        ip_address:
+           description: The fixed IP address to attempt to allocate.
+           type: str
+           aliases: ['ip']
+        subnet_id:
+           description: The subnet to attach the IP address to.
+           required: true
+           type: str
+           aliases: ['subnet']
+    external_gateway_info:
+      description:
        - Information about the router's external gateway
-     type: dict
-     suboptions:
-       network:
-         description:
+      type: dict
+      suboptions:
+        network:
+          description:
             - Unique name or ID of the external gateway network.
             - required I(interfaces) or I(enable_snat) are provided.
-         type: str
-       enable_snat:
-         description:
+          type: str
+        enable_snat:
+          description:
             - Unique name or ID of the external gateway network.
             - required I(interfaces) or I(enable_snat) are provided.
-         type: bool
-       external_fixed_ips:
-         description:
+          type: bool
+        external_fixed_ips:
+          description:
             - The IP address parameters for the external gateway network. Each
               is a dictionary with the subnet name or ID (subnet) and the IP
-              address to assign on the subnet (ip). If no IP is specified,
-              one is automatically assigned from that subnet.
-         type: list
-         elements: dict
-         suboptions:
+              address to assign on the subnet (ip_address). If no IP is
+              specified, one is automatically assigned from that subnet.
+          type: list
+          elements: dict
+          suboptions:
             ip_address:
                description: The fixed IP address to attempt to allocate.
                type: str
-               aliases: [ip]
+               aliases: ['ip']
             subnet_id:
                description: The subnet to attach the IP address to.
                required: true
                type: str
-               aliases: [subnet]
-   external_fixed_ips:
-     description:
-        - The IP address parameters for the external gateway network. Each
-          is a dictionary with the subnet name or ID (subnet) and the IP
-          address to assign on the subnet (ip). If no IP is specified,
-          one is automatically assigned from that subnet.
-     type: list
-     elements: dict
-     suboptions:
-        ip_address:
-           description: The fixed IP address to attempt to allocate.
-           type: str
-           aliases: [ip]
-        subnet_id:
-           description: The subnet to attach the IP address to.
-           required: true
-           type: str
-           aliases: [subnet]
-   interfaces:
-     description:
+               aliases: ['subnet']
+    interfaces:
+      description:
         - List of subnets to attach to the router internal interface. Default
           gateway associated with the subnet will be automatically attached
           with the router's internal interface.
@@ -107,8 +81,37 @@ options:
           User defined portip is often required when a multiple router need
           to be connected to a single subnet for which the default gateway has
           been already used.
-     type: list
-     elements: raw
+      type: list
+      elements: raw
+    is_admin_state_up:
+      description:
+        - Desired admin state of the created or existing router.
+      type: bool
+      default: 'yes'
+      aliases: ['admin_state_up']
+    name:
+      description:
+        - Name to be give to the router.
+        - This router attribute cannot be updated.
+      required: true
+      type: str
+    network:
+      description:
+        - Unique name or ID of the external gateway network.
+        - Required if I(external_fixed_ips) or I(enable_snat) are provided.
+        - This router attribute cannot be updated.
+      type: str
+    project:
+      description:
+        - Unique name or ID of the project.
+        - This router attribute cannot be updated.
+      type: str
+    state:
+      description:
+        - Indicate desired state of the resource
+      choices: ['present', 'absent']
+      default: present
+      type: str
 requirements:
     - "python >= 3.6"
     - "openstacksdk"
@@ -124,14 +127,14 @@ EXAMPLES = '''
     state: present
     name: simple_router
 
-# Create a simple router, not attached to a gateway or subnets for a given project.
+# Create a router, not attached to a gateway or subnets for a given project.
 - openstack.cloud.router:
     cloud: mycloud
     state: present
     name: simple_router
     project: myproj
 
-# Creates a router attached to ext_network1 on an IPv4 subnet and one
+# Creates a router attached to ext_network1 on an IPv4 subnet and with one
 # internal subnet interface.
 - openstack.cloud.router:
     cloud: mycloud
@@ -140,11 +143,11 @@ EXAMPLES = '''
     network: ext_network1
     external_fixed_ips:
       - subnet: public-subnet
-        ip: 172.24.4.2
+        ip_address: 172.24.4.2
     interfaces:
       - private-subnet
 
-# Create another router with two internal subnet interfaces.One with user defined port
+# Create a router with two internal subnet interfaces and a user defined port
 # ip and another with default gateway.
 - openstack.cloud.router:
     cloud: mycloud
@@ -157,8 +160,8 @@ EXAMPLES = '''
         portip: 10.1.1.10
       - project-subnet
 
-# Create another router with two internal subnet interface.One with user defined port
-# ip and and another with default gateway.
+# Create a router with two internal subnet interface. One with user defined
+# port ip and and another with default gateway.
 - openstack.cloud.router:
     cloud: mycloud
     state: present
@@ -170,8 +173,8 @@ EXAMPLES = '''
         portip: 10.1.1.10
       - project-subnet
 
-# Create another router with two internal subnet interface. one with  user defined port
-# ip and and another  with default gateway.
+# Create a router with two internal subnet interface. One with user defined
+# port ip and and another  with default gateway.
 - openstack.cloud.router:
     cloud: mycloud
     state: present
@@ -193,9 +196,9 @@ EXAMPLES = '''
     network: ext_network1
     external_fixed_ips:
       - subnet: public-subnet
-        ip: 172.24.4.2
+        ip_address: 172.24.4.2
       - subnet: ipv6-public-subnet
-        ip: 2001:db8::3
+        ip_address: 2001:db8::3
 
 # Delete router1
 - openstack.cloud.router:
@@ -210,69 +213,123 @@ router:
     returned: On success when I(state) is 'present'
     type: dict
     contains:
+        availability_zones:
+            description: Availability zones
+            returned: success
+            type: list
+        availability_zone_hints:
+            description: Availability zone hints
+            returned: success
+            type: list
+        created_at:
+            description: Date and time when the router was created
+            returned: success
+            type: str
+        description:
+            description: Description notes of the router
+            returned: success
+            type: str
+        external_gateway_info:
+            description: The external gateway information of the router.
+            returned: success
+            type: dict
+            sample: |
+                {
+                    "enable_snat": true,
+                    "external_fixed_ips": [
+                        {
+                           "ip_address": "10.6.6.99",
+                           "subnet_id": "4272cb52-a456-4c20-8f3c-c26024ecfa81"
+                        }
+                    ]
+                }
+        flavor_id:
+            description: ID of the flavor of the router
+            returned: success
+            type: str
         id:
-            description: Router ID.
+            description: Unique UUID.
+            returned: success
             type: str
             sample: "474acfe5-be34-494c-b339-50f06aa143e4"
+        is_admin_state_up:
+            description: Network administrative state
+            returned: success
+            type: bool
+        is_distributed:
+            description: Indicates a distributed router.
+            returned: success
+            type: bool
+        is_ha:
+            description: Indicates a highly-available router.
+            returned: success
+            type: bool
         name:
-            description: Router name.
+            description: Name given to the router.
+            returned: success
             type: str
             sample: "router1"
-        admin_state_up:
-            description: Administrative state of the router.
-            type: bool
-            sample: true
+        project_id:
+            description: Project id associated with this router.
+            returned: success
+            type: str
+        revision_number:
+            description: Revision number
+            returned: success
+            type: int
+        routes:
+            description: The extra routes configuration for L3 router.
+            returned: success
+            type: list
         status:
-            description: The router status.
+            description: Router status.
+            returned: success
             type: str
             sample: "ACTIVE"
+        tags:
+            description: List of tags
+            returned: success
+            type: list
         tenant_id:
-            description: The tenant ID.
+            description: Owner tenant ID
+            returned: success
+            type: str
+        updated_at:
+            description: Date of last update on the router
+            returned: success
             type: str
-            sample: "861174b82b43463c9edc5202aadc60ef"
-        external_gateway_info:
-            description: The external gateway parameters.
-            type: dict
-            sample: {
-                      "enable_snat": true,
-                      "external_fixed_ips": [
-                         {
-                           "ip_address": "10.6.6.99",
-                           "subnet_id": "4272cb52-a456-4c20-8f3c-c26024ecfa81"
-                         }
-                       ]
-                    }
-        routes:
-            description: The extra routes configuration for L3 router.
-            type: list
 '''
 
 from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
 from collections import defaultdict
 
 
-ext_ips_spec = dict(type='list', elements='dict', options=dict(
-    ip_address=dict(aliases=["ip"]),
-    subnet_id=dict(required=True, aliases=["subnet"]),
-))
+class RouterModule(OpenStackModule):
 
+    external_fixed_ips_spec = dict(
+        type='list',
+        elements='dict',
+        options=dict(
+            ip_address=dict(aliases=["ip"]),
+            subnet_id=dict(required=True, aliases=["subnet"]),
+        ))
 
-class RouterModule(OpenStackModule):
     argument_spec = dict(
-        state=dict(default='present', choices=['absent', 'present']),
-        name=dict(required=True),
-        is_admin_state_up=dict(type='bool', default=True,
-                               aliases=['admin_state_up']),
         enable_snat=dict(type='bool'),
-        network=dict(),
-        interfaces=dict(type='list', elements='raw'),
-        external_fixed_ips=ext_ips_spec,
+        external_fixed_ips=external_fixed_ips_spec,
         external_gateway_info=dict(type='dict', options=dict(
             network=dict(),
             enable_snat=dict(type='bool'),
-            external_fixed_ips=ext_ips_spec,
+            external_fixed_ips=external_fixed_ips_spec,
         )),
-        project=dict()
+        interfaces=dict(type='list', elements='raw'),
+        is_admin_state_up=dict(type='bool',
+                               default=True,
+                               aliases=['admin_state_up']),
+        name=dict(required=True),
+        network=dict(),
+        project=dict(),
+        state=dict(default='present', choices=['absent', 'present']),
     )
 
     module_kwargs = dict(
@@ -281,9 +338,10 @@ class RouterModule(OpenStackModule):
             ('external_gateway_info', 'external_fixed_ips'),
             ('external_gateway_info', 'enable_snat'),
         ],
-        required_by=dict(
-            external_fixed_ips='network',
-        ),
+        required_by={
+            'external_fixed_ips': 'network',
+            'enable_snat': 'network',
+        },
     )
 
     def _needs_update(self, router, kwargs, external_fixed_ips, to_add,
@@ -295,6 +353,7 @@ class RouterModule(OpenStackModule):
         cur_ext_gw_info = router['external_gateway_info']
         if 'external_gateway_info' in kwargs:
             if cur_ext_gw_info is None:
+                # added external gateway info
                 return True
             update = kwargs['external_gateway_info']
             for attr in ('enable_snat', 'network_id'):
@@ -304,6 +363,7 @@ class RouterModule(OpenStackModule):
         cur_ext_gw_info = router['external_gateway_info']
         cur_ext_fips = (cur_ext_gw_info or {}) \
             .get('external_fixed_ips', [])
+
         # map of external fixed ip subnets to addresses
         cur_fip_map = defaultdict(set)
         for p in cur_ext_fips:
@@ -320,26 +380,32 @@ class RouterModule(OpenStackModule):
             ip = fip.get('ip_address', None)
             if subnet in cur_fip_map:
                 if ip is not None and ip not in cur_fip_map[subnet]:
+                    # mismatching ip for subnet
                     return True
             else:
+                # adding ext ip with subnet 'subnet'
                 return True
+
         # Check if external ip addresses need to be removed
         for fip in cur_ext_fips:
             subnet = fip['subnet_id']
             ip = fip['ip_address']
             if subnet in req_fip_map:
                 if ip not in req_fip_map[subnet]:
+                    # removing ext ip with subnet (ip clash)
                     return True
             else:
+                # removing ext ip with subnet
                 return True
 
         if not external_fixed_ips and len(cur_ext_fips) > 1:
-            # no external fixed ips requested but router has several external
-            # fixed ips
+            # No external fixed ips requested but
+            # router has several external fixed ips
             return True
 
-        # check if internal interfaces need update
+        # Check if internal interfaces need update
         if to_add or to_remove or missing_port_ids:
+            # need to change interfaces
             return True
 
         return False
@@ -351,13 +417,9 @@ class RouterModule(OpenStackModule):
 
         if not router:
             kwargs['name'] = self.params['name']
-
-        curr_ext_gw_info = None
-        if router:
-            curr_ext_gw_info = router['external_gateway_info']
-        curr_ext_fixed_ips = []
-        if curr_ext_gw_info:
-            curr_ext_fixed_ips = curr_ext_gw_info.get('external_fixed_ips', [])
+        # We cannot update a router name because name is used to find routers
+        # by name so only any router with an already matching name will be
+        # considered for updates
 
         external_gateway_info = {}
         if network:
@@ -372,15 +434,24 @@ class RouterModule(OpenStackModule):
             kwargs['external_gateway_info'] = external_gateway_info
 
         if 'external_fixed_ips' not in external_gateway_info:
+            # no external fixed ips requested
+
+            # get current external fixed ips
+            curr_ext_gw_info = \
+                router['external_gateway_info'] if router else None
+            curr_ext_fixed_ips = \
+                curr_ext_gw_info.get('external_fixed_ips', []) \
+                if curr_ext_gw_info else []
+
             if len(curr_ext_fixed_ips) > 1:
-                fip = curr_ext_fixed_ips[0]
-                external_gateway_info['external_fixed_ips'] = [fip]
+                # but router has several external fixed ips
+                # keep first external fixed ip only
+                external_gateway_info['external_fixed_ips'] = [
+                    curr_ext_fixed_ips[0]]
 
         return kwargs
 
-    def _build_router_interface_config(self, filters=None):
-        if filters is None:
-            filters = {}
+    def _build_router_interface_config(self, filters):
         external_fixed_ips = []
         internal_ports_missing = []
         internal_ifaces = []
@@ -394,9 +465,7 @@ class RouterModule(OpenStackModule):
         if ext_fixed_ips:
             for iface in ext_fixed_ips:
                 subnet = self.conn.network.find_subnet(
-                    iface['subnet'], **filters)
-                if not subnet:
-                    self.fail(msg='subnet %s not found' % iface['subnet'])
+                    iface['subnet'], ignore_missing=False, **filters)
                 fip = dict(subnet_id=subnet.id)
                 if 'ip_address' in iface:
                     fip['ip_address'] = iface['ip_address']
@@ -407,16 +476,13 @@ class RouterModule(OpenStackModule):
             internal_ips = []
             for iface in self.params['interfaces']:
                 if isinstance(iface, str):
-                    subnet = self.conn.network.find_subnet(iface, **filters)
-                    if not subnet:
-                        self.fail(msg='subnet %s not found' % iface)
+                    subnet = self.conn.network.find_subnet(
+                        iface, ignore_missing=False, **filters)
                     internal_ifaces.append(dict(subnet_id=subnet.id))
 
                 elif isinstance(iface, dict):
-                    subnet = self.conn.network.find_subnet(iface['subnet'],
-                                                           **filters)
-                    if not subnet:
-                        self.fail(msg='subnet %s not found' % iface['subnet'])
+                    subnet = self.conn.network.find_subnet(
+                        iface['subnet'], ignore_missing=False, **filters)
 
                     # TODO: We allow passing a subnet without specifing a
                     #       network in case iface is a string, hence we
@@ -424,7 +490,8 @@ class RouterModule(OpenStackModule):
                     if 'net' not in iface:
                         self.fail(
                             "Network name missing from interface definition")
-                    net = self.conn.network.find_network(iface['net'])
+                    net = self.conn.network.find_network(iface['net'],
+                                                         ignore_missing=False)
 
                     if 'portip' not in iface:
                         # portip not set, add any ip from subnet
@@ -444,15 +511,17 @@ class RouterModule(OpenStackModule):
                         for port in existing_ports:
                             for fip in port['fixed_ips']:
                                 if (fip['subnet_id'] != subnet.id
-                                        or fip['ip_address'] != portip):
+                                   or fip['ip_address'] != portip):
                                     continue
+                                # portip exists in net already
                                 internal_ips.append(fip['ip_address'])
                                 internal_ifaces.append(
                                     dict(port_id=port.id,
                                          subnet_id=subnet.id,
                                          ip_address=portip))
                         if portip not in internal_ips:
-                            # no port with portip exists hence create a new port
+                            # No port with portip exists
+                            # hence create a new port
                             internal_ports_missing.append({
                                 'network_id': subnet.network_id,
                                 'fixed_ips': [{'ip_address': portip,
@@ -467,8 +536,8 @@ class RouterModule(OpenStackModule):
 
     def _update_ifaces(self, router, to_add, to_remove, missing_ports):
         for port in to_remove:
-            self.conn.network.remove_interface_from_router(router,
-                                                           port_id=port.id)
+            self.conn.network.remove_interface_from_router(
+                router, port_id=port.id)
         # create ports that are missing
         for port in missing_ports:
             p = self.conn.network.create_port(**port)
@@ -542,10 +611,8 @@ class RouterModule(OpenStackModule):
         project = None
         project_id = None
         if project_name_or_id is not None:
-            project = self.conn.identity.find_project(project_name_or_id)
-            if project is None:
-                self.fail(
-                    msg='Project %s could not be found' % project_name_or_id)
+            project = self.conn.identity.find_project(project_name_or_id,
+                                                      ignore_missing=False)
             project_id = project['id']
             query_filters['project_id'] = project_id
 
@@ -553,9 +620,8 @@ class RouterModule(OpenStackModule):
         network = None
         if network_name_or_id:
             network = self.conn.network.find_network(network_name_or_id,
+                                                     ignore_missing=False,
                                                      **query_filters)
-            if not network:
-                self.fail(msg='network %s not found' % network_name_or_id)
 
         # Validate and cache the subnet IDs so we can avoid duplicate checks
         # and expensive API calls.
@@ -595,12 +661,14 @@ class RouterModule(OpenStackModule):
 
             if not router:
                 changed = True
+
                 if project_id:
                     kwargs['project_id'] = project_id
                 router = self.conn.network.create_router(**kwargs)
 
                 self._update_ifaces(router, internal_ifaces, [],
                                     missing_internal_ports)
+
             else:
 
                 if self._needs_update(router, kwargs, external_fixed_ips,
@@ -625,10 +693,10 @@ class RouterModule(OpenStackModule):
                 # still fail if e.g. floating ips are attached to the
                 # router.
                 for port in router_ifs_internal:
-                    self.conn.network.remove_interface_from_router(router,
-                                                                   port_id=port['id'])
+                    self.conn.network.remove_interface_from_router(
+                        router, port_id=port['id'])
                 self.conn.network.delete_router(router)
-                self.exit_json(changed=True, router=router)
+                self.exit_json(changed=True)
 
 
 def main():