diff --git a/.zuul.yaml b/.zuul.yaml
index 402ab70057f2de84750d69c937b16c1f7d073580..8a5231475efdd4e11106d2ea85c3d12abaa65969 100644
--- a/.zuul.yaml
+++ b/.zuul.yaml
@@ -95,7 +95,6 @@
         object
         object_container
         port
-        port_info
         project
         project_info
         recordset
diff --git a/ci/roles/port/defaults/main.yml b/ci/roles/port/defaults/main.yml
index 948a315d4938d8494f089a6a4413a693e1f106a0..4e2d6602feb7589f036e101ff4c87dfde41608cc 100644
--- a/ci/roles/port/defaults/main.yml
+++ b/ci/roles/port/defaults/main.yml
@@ -1,9 +1,47 @@
-network_name: ansible_port_network
-network_external: true
-subnet_name: ansible_port_subnet
-port_name: ansible_port
-secgroup_name: ansible_port_secgroup
-no_security_groups: True
 binding_profile:
   "pci_slot": "0000:03:11.1"
   "physical_network": "provider"
+expected_fields:
+  - allowed_address_pairs
+  - binding_host_id
+  - binding_profile
+  - binding_vif_details
+  - binding_vif_type
+  - binding_vnic_type
+  - created_at
+  - data_plane_status
+  - description
+  - device_id
+  - device_owner
+  - device_profile
+  - dns_assignment
+  - dns_domain
+  - dns_name
+  - extra_dhcp_opts
+  - fixed_ips
+  - id
+  - ip_allocation
+  - is_admin_state_up
+  - is_port_security_enabled
+  - mac_address
+  - name
+  - network_id
+  - numa_affinity_policy
+  - project_id
+  - propagate_uplink_status
+  - qos_network_policy_id
+  - qos_policy_id
+  - resource_request
+  - revision_number
+  - security_group_ids
+  - status
+  - tags
+  - tenant_id
+  - trunk_details
+  - updated_at
+network_external: true
+network_name: ansible_port_network
+no_security_groups: True
+port_name: ansible_port
+secgroup_name: ansible_port_secgroup
+subnet_name: ansible_port_subnet
diff --git a/ci/roles/port/tasks/main.yml b/ci/roles/port/tasks/main.yml
index 0396b1d8ed4dc3e4364cc1e338950e482a33640d..9b5f196f7324b403b55f20a4afa01b868e9d694c 100644
--- a/ci/roles/port/tasks/main.yml
+++ b/ci/roles/port/tasks/main.yml
@@ -1,145 +1,290 @@
 ---
 - name: Create network
   openstack.cloud.network:
-     cloud: "{{ cloud }}"
-     state: present
-     name: "{{ network_name }}"
-     external: "{{ network_external }}"
+    cloud: "{{ cloud }}"
+    state: present
+    name: "{{ network_name }}"
+    external: "{{ network_external }}"
+  register: network
 
 - name: Create subnet
   openstack.cloud.subnet:
-     cloud: "{{ cloud }}"
-     state: present
-     name: "{{ subnet_name }}"
-     network_name: "{{ network_name }}"
-     cidr: 10.5.5.0/24
+    cloud: "{{ cloud }}"
+    state: present
+    name: "{{ subnet_name }}"
+    network_name: "{{ network_name }}"
+    cidr: 10.5.5.0/24
+  register: subnet
 
 - name: Create port (no security group or default security group)
   openstack.cloud.port:
-     cloud: "{{ cloud }}"
-     state: present
-     name: "{{ port_name }}"
-     network: "{{ network_name }}"
-     no_security_groups: "{{ no_security_groups }}"
-     fixed_ips:
-       - ip_address: 10.5.5.69
+    cloud: "{{ cloud }}"
+    state: present
+    name: "{{ port_name }}"
+    network: "{{ network_name }}"
+    no_security_groups: "{{ no_security_groups }}"
+    fixed_ips:
+      - ip_address: 10.5.5.69
   register: port
 
 - debug: var=port
 
+- name: assert return values of port module
+  assert:
+    that:
+      # allow new fields to be introduced but prevent fields from being removed
+      - expected_fields|difference(port.port.keys())|length == 0
+
+- name: List all ports
+  openstack.cloud.port_info:
+    cloud: "{{ cloud }}"
+  register: info
+
+- name: Get info about all ports
+  openstack.cloud.port_info:
+    cloud: "{{ cloud }}"
+  register: info
+
+- name: Check info about ports
+  assert:
+    that:
+      - info.ports|length > 0
+      # allow new fields to be introduced but prevent fields from being removed
+      - expected_fields|difference(info.ports[0].keys())|length == 0
+
+- name: Get port by id
+  openstack.cloud.port_info:
+    cloud: "{{ cloud }}"
+    name: "{{ info.ports[0].id }}"
+  register: info_id
+
+- name: Assert infos by id
+  assert:
+    that:
+      - info_id.ports|length == 1
+      - info_id.ports[0].id == info.ports[0].id
+
+- name: List port with device_id filter
+  openstack.cloud.port_info:
+    cloud: "{{ cloud }}"
+    filters:
+      device_id: "{{ info.ports[0].device_id }}"
+  register: info_filter
+
+- name: Assert port was returned
+  assert:
+    that:
+      - info_filter.ports | length >= 1
+
 - name: Delete port (no security group or default security group)
   openstack.cloud.port:
-     cloud: "{{ cloud }}"
-     state: absent
-     name: "{{ port_name }}"
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ port_name }}"
 
 - name: Create security group
   openstack.cloud.security_group:
-     cloud: "{{ cloud }}"
-     state: present
-     name: "{{ secgroup_name }}"
-     description: Test group
+    cloud: "{{ cloud }}"
+    state: present
+    name: "{{ secgroup_name }}"
+    description: Test group
+  register: security_group
 
 - name: Create port (with security group)
   openstack.cloud.port:
-     cloud: "{{ cloud }}"
-     state: present
-     name: "{{ port_name }}"
-     network: "{{ network_name }}"
-     fixed_ips:
-       - ip_address: 10.5.5.69
-     security_groups:
-       - "{{ secgroup_name }}"
+    cloud: "{{ cloud }}"
+    state: present
+    name: "{{ port_name }}"
+    network: "{{ network_name }}"
+    fixed_ips:
+      - ip_address: 10.5.5.69
+    security_groups:
+      - "{{ secgroup_name }}"
   register: port
 
 - debug: var=port
 
 - name: Delete port (with security group)
   openstack.cloud.port:
-     cloud: "{{ cloud }}"
-     state: absent
-     name: "{{ port_name }}"
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ port_name }}"
 
 - name: Create port (with dns_name, dns_domain)
   openstack.cloud.port:
-     cloud: "{{ cloud }}"
-     state: present
-     name: "{{ port_name }}"
-     network: "{{ network_name }}"
-     fixed_ips:
-       - ip_address: 10.5.5.69
-     dns_name: "dns-port-name"
-     dns_domain: "example.com."
+    cloud: "{{ cloud }}"
+    state: present
+    name: "{{ port_name }}"
+    network: "{{ network_name }}"
+    fixed_ips:
+      - ip_address: 10.5.5.69
+    dns_name: "dns-port-name"
+    dns_domain: "example.com."
   register: port
 
 - debug: var=port
 
 - name: Delete port (with dns name,domain)
   openstack.cloud.port:
-     cloud: "{{ cloud }}"
-     state: absent
-     name: "{{ port_name }}"
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ port_name }}"
 
 - name: Create port (with allowed_address_pairs and extra_dhcp_opts)
   openstack.cloud.port:
-     cloud: "{{ cloud }}"
-     state: present
-     name: "{{ port_name }}"
-     network: "{{ network_name }}"
-     no_security_groups: "{{ no_security_groups }}"
-     allowed_address_pairs:
-       - ip_address: 10.6.7.0/24
-     extra_dhcp_opts:
-       - opt_name: "bootfile-name"
-         opt_value: "testfile.1"
+    cloud: "{{ cloud }}"
+    state: present
+    name: "{{ port_name }}"
+    network: "{{ network_name }}"
+    no_security_groups: "{{ no_security_groups }}"
+    allowed_address_pairs:
+      - ip_address: 10.6.7.0/24
+    extra_dhcp_opts:
+      - opt_name: "bootfile-name"
+        opt_value: "testfile.1"
   register: port
 
 - debug: var=port
 
 - name: Delete port (with allowed_address_pairs and extra_dhcp_opts)
   openstack.cloud.port:
-     cloud: "{{ cloud }}"
-     state: absent
-     name: "{{ port_name }}"
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ port_name }}"
+
+- name: Create port which will be updated
+  openstack.cloud.port:
+    allowed_address_pairs:
+      - ip_address: 10.6.7.0/24
+        mac_address: "aa:bb:cc:dd:ee:ff"
+    cloud: "{{ cloud }}"
+    description: "What a great port"
+    extra_dhcp_opts:
+      - ip_version: 4
+        opt_name: "bootfile-name"
+        opt_value: "testfile.1"
+    dns_name: "dns-port-name"
+    dns_domain: "example.com."
+    fixed_ips:
+      - ip_address: 10.5.5.69
+    name: "{{ port_name }}"
+    network: "{{ network_name }}"
+    no_security_groups: yes
+    state: present
+  register: port
+
+- name: Create port which will be updated (again)
+  openstack.cloud.port:
+    allowed_address_pairs:
+      - ip_address: 10.6.7.0/24
+        mac_address: "aa:bb:cc:dd:ee:ff"
+    cloud: "{{ cloud }}"
+    description: "What a great port"
+    extra_dhcp_opts:
+      - ip_version: 4
+        opt_name: "bootfile-name"
+        opt_value: "testfile.1"
+    # We have no valid dns name configured
+    #dns_name: "dns-port-name"
+    #dns_domain: "example.com."
+    fixed_ips:
+      - ip_address: 10.5.5.69
+        subnet_id: "{{ subnet.subnet.id }}"
+    name: "{{ port_name }}"
+    network: "{{ network_name }}"
+    no_security_groups: yes
+    state: present
+  register: port_again
+
+- name: Assert port did not change
+  assert:
+    that:
+      - port.port.id == port_again.port.id
+      - port_again is not changed
+
+- name: Update port
+  openstack.cloud.port:
+    allowed_address_pairs:
+      - ip_address: 11.9.9.0/24
+        mac_address: "aa:aa:aa:bb:bb:bb"
+    cloud: "{{ cloud }}"
+    description: "This port got updated"
+    extra_dhcp_opts:
+      - opt_name: "bootfile-name"
+        opt_value: "testfile.2"
+    # We have no valid dns name configured
+    #dns_name: "dns-port-name-2"
+    #dns_domain: "another.example.com."
+    fixed_ips:
+      - ip_address: 10.5.5.70
+        subnet_id: "{{ subnet.subnet.id }}"
+    name: "{{ port_name }}"
+    network: "{{ network_name }}"
+    security_groups:
+      - "{{ secgroup_name }}"
+    state: present
+  register: port_updated
+
+- name: Assert updated port
+  assert:
+    that:
+      - port_updated.port.id == port.port.id
+      - port_updated.port.allowed_address_pairs|length == 1
+      - port_updated.port.allowed_address_pairs[0].ip_address == "11.9.9.0/24"
+      - port_updated.port.allowed_address_pairs[0].mac_address == "aa:aa:aa:bb:bb:bb"
+      - port_updated.port.description == "This port got updated"
+      - port_updated.port.extra_dhcp_opts|length == 1
+      - port_updated.port.extra_dhcp_opts[0].opt_value == "testfile.2"
+      # We have no valid dns name configured
+      #- port_updated.port.dns_name == "dns-port-name-2"
+      #- port_updated.port.dns_domain == "another.example.com."
+      - port_updated.port.fixed_ips|length == 1
+      - port_updated.port.fixed_ips[0].ip_address == "10.5.5.70"
+      - port_updated.port.fixed_ips[0].subnet_id == subnet.subnet.id
+      - port_updated.port.security_group_ids|length == 1
+      - port_updated.port.security_group_ids[0] == security_group.secgroup.id
+
+- name: Delete updated port
+  openstack.cloud.port:
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ port_name }}"
 
 - name: Delete security group
   openstack.cloud.security_group:
-     cloud: "{{ cloud }}"
-     state: absent
-     name: "{{ secgroup_name }}"
-
-- name: Test port binding config (runs from train release sdk > 0.28)
-  block:
-    - name: Create port (with binding profile)
-      openstack.cloud.port:
-        cloud: "{{ cloud }}"
-        state: present
-        name: "{{ port_name }}"
-        network: "{{ network_name }}"
-        binding_profile: "{{ binding_profile }}"
-      register: port
-
-    - name: Assert binding:profile exists in created port
-      assert:
-        that: "port.port['binding_profile']"
-
-    - debug: var=port
-
-    - name: Delete port (with binding profile)
-      openstack.cloud.port:
-        cloud: "{{ cloud }}"
-        state: absent
-        name: "{{ port_name }}"
-  when: sdk_version is version(0.28, '>')
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ secgroup_name }}"
+
+- name: Create port (with binding profile)
+  openstack.cloud.port:
+    cloud: "{{ cloud }}"
+    state: present
+    name: "{{ port_name }}"
+    network: "{{ network_name }}"
+    binding_profile: "{{ binding_profile }}"
+  register: port
+
+- name: Assert binding_profile exists in created port
+  assert:
+    that: "port.port['binding_profile']"
+
+- debug: var=port
+
+- name: Delete port (with binding profile)
+  openstack.cloud.port:
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ port_name }}"
 
 - name: Delete subnet
   openstack.cloud.subnet:
-     cloud: "{{ cloud }}"
-     state: absent
-     name: "{{ subnet_name }}"
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ subnet_name }}"
 
 - name: Delete network
   openstack.cloud.network:
-     cloud: "{{ cloud }}"
-     state: absent
-     name: "{{ network_name }}"
+    cloud: "{{ cloud }}"
+    state: absent
+    name: "{{ network_name }}"
diff --git a/ci/roles/port_info/tasks/main.yml b/ci/roles/port_info/tasks/main.yml
deleted file mode 100644
index 496c7e08bc3f53f36dffe3a7d08e83b70dd5416c..0000000000000000000000000000000000000000
--- a/ci/roles/port_info/tasks/main.yml
+++ /dev/null
@@ -1,72 +0,0 @@
----
-- name: List all ports
-  openstack.cloud.port_info:
-    cloud: "{{ cloud }}"
-  register: result_all
-
-- name: Assert fields
-  assert:
-    that:
-      - item in result_all.ports.0
-  loop:
-    - allowed_address_pairs
-    - binding_host_id
-    - binding_profile
-    - binding_vif_details
-    - binding_vif_type
-    - binding_vnic_type
-    - created_at
-    - data_plane_status
-    - description
-    - device_id
-    - device_owner
-    - device_profile
-    - dns_assignment
-    - dns_domain
-    - dns_name
-    - extra_dhcp_opts
-    - fixed_ips
-    - id
-    - ip_allocation
-    - is_admin_state_up
-    - is_port_security_enabled
-    - mac_address
-    - name
-    - network_id
-    - numa_affinity_policy
-    - project_id
-    - propagate_uplink_status
-    - qos_network_policy_id
-    - qos_policy_id
-    - resource_request
-    - revision_number
-    - security_group_ids
-    - status
-    - tags
-    - tenant_id
-    - trunk_details
-    - updated_at
-
-- name: Get port by id
-  openstack.cloud.port_info:
-    cloud: "{{ cloud }}"
-    port: "{{ result_all.ports[0].id }}"
-  register: result_id
-
-- name: Assert results by id
-  assert:
-    that:
-      - item.id == result_all.ports[0].id
-  loop: "{{ result_id.ports }}"
-
-- name: List port with device_id filter
-  openstack.cloud.port_info:
-    cloud: "{{ cloud }}"
-    filters:
-      device_id: "{{ result_all.ports[0].device_id }}"
-  register: result_filter
-
-- name: Assert port was returned
-  assert:
-    that:
-      - result_filter.ports | length >= 1
diff --git a/ci/run-collection.yml b/ci/run-collection.yml
index b165177c7d86a32189d9e1f1f88a6712a59e82f8..3d5246f18f31a5f5c951d5e962ec11c5844ff4c3 100644
--- a/ci/run-collection.yml
+++ b/ci/run-collection.yml
@@ -52,7 +52,6 @@
       when: sdk_version is version(0.44, '>=')
     - { role: object, tags: object }
     - { role: port, tags: port }
-    - { role: port_info, tags: port_info }
     - { role: project, tags: project }
     - { role: project_info, tags: project_info }
     - { role: recordset, tags: recordset }
diff --git a/plugins/modules/port.py b/plugins/modules/port.py
index c6204f92f21650cf75558decfc2c505e40c2554d..48002a537fd859c44950cc21cbbc85ab3d86d882 100644
--- a/plugins/modules/port.py
+++ b/plugins/modules/port.py
@@ -10,128 +10,149 @@ module: port
 short_description: Add/Update/Delete ports from an OpenStack cloud.
 author: OpenStack Ansible SIG
 description:
-   - Add, Update or Remove ports from an OpenStack cloud. A I(state) of
-     'present' will ensure the port is created or updated if required.
+   - Add, Update or Remove ports from an OpenStack cloud.
 options:
-   network:
-     description:
-        - Network ID or name this port belongs to.
-        - Required when creating a new port.
-     type: str
-   name:
-     description:
-        - Name that has to be given to the port.
-     type: str
-   fixed_ips:
-     description:
-        - Desired IP and/or subnet for this port.  Subnet is referenced by
-          subnet_id and IP is referenced by ip_address.
-     type: list
-     elements: dict
-     suboptions:
-        ip_address:
-           description: The fixed IP address to attempt to allocate.
-           required: true
-           type: str
-        subnet_id:
-           description: The subnet to attach the IP address to.
-           type: str
-   admin_state_up:
-     description:
-        - Sets admin state.
-     type: bool
-   mac_address:
-     description:
-        - MAC address of this port.
-     type: str
-   security_groups:
-     description:
-        - Security group(s) ID(s) or name(s) associated with the port (comma
-          separated string or YAML list)
-     type: list
-     elements: str
-   no_security_groups:
-     description:
-        - Do not associate a security group with this port.
-     type: bool
-     default: 'no'
-   allowed_address_pairs:
-     description:
-        - "Allowed address pairs list.  Allowed address pairs are supported with
-          dictionary structure.
-          e.g.  allowed_address_pairs:
-                  - ip_address: 10.1.0.12
-                    mac_address: ab:cd:ef:12:34:56
-                  - ip_address: ..."
-     type: list
-     elements: dict
-     suboptions:
-        ip_address:
-           description: The IP address.
-           type: str
-        mac_address:
-           description: The MAC address.
-           type: str
-   extra_dhcp_opts:
-     description:
-        - "Extra dhcp options to be assigned to this port. Extra options are
-          supported with dictionary structure. Note that options cannot be removed
-          only updated.
-          e.g.  extra_dhcp_opts:
-                  - opt_name: opt name1
-                    opt_value: value1
-                    ip_version: 4
-                  - opt_name: ..."
-     type: list
-     elements: dict
-     suboptions:
-        opt_name:
-           description: The name of the DHCP option to set.
-           type: str
-           required: true
-        opt_value:
-           description: The value of the DHCP option to set.
-           type: str
-           required: true
-        ip_version:
-           description: The IP version this DHCP option is for.
-           type: int
-           required: true
-   device_owner:
-     description:
-        - The ID of the entity that uses this port.
-     type: str
-   device_id:
-     description:
-        - Device ID of device using this port.
-     type: str
-   state:
-     description:
-       - Should the resource be present or absent.
-     choices: [present, absent]
-     default: present
-     type: str
-   vnic_type:
-     description:
-       - The type of the port that should be created
-     choices: [normal, direct, direct-physical, macvtap, baremetal, virtio-forwarder]
-     type: str
-   port_security_enabled:
-     description:
-       - Whether to enable or disable the port security on the network.
-     type: bool
-   binding_profile:
-     description:
-       - Binding profile dict that the port should be created with.
-     type: dict
-   dns_name:
-     description:
-       - The dns name of the port ( only with dns-integration enabled )
-     type: str
-   dns_domain:
-     description:
-       - The dns domain of the port ( only with dns-integration enabled )
-     type: str
+    allowed_address_pairs:
+        description:
+          - "Allowed address pairs list. Allowed address pairs are supported
+            with dictionary structure.
+            e.g.  allowed_address_pairs:
+                    - ip_address: 10.1.0.12
+                      mac_address: ab:cd:ef:12:34:56
+                    - ip_address: ..."
+          - The port will change during update if not all suboptions are
+            specified, e.g. when ip_address is given but mac_address is not.
+        type: list
+        elements: dict
+        suboptions:
+            ip_address:
+                description: The IP address.
+                type: str
+            mac_address:
+                description: The MAC address.
+                type: str
+    binding_profile:
+        description:
+          - Binding profile dict that the port should be created with.
+        type: dict
+    binding_vnic_type:
+        description:
+          - The type of the port that should be created
+        choices: [normal,
+                  direct,
+                  direct-physical,
+                  macvtap,
+                  baremetal,
+                  virtio-forwarder]
+        type: str
+        aliases: ['vnic_type']
+    description:
+        description:
+          - Description of the port.
+        type: str
+    device_id:
+        description:
+          - Device ID of device using this port.
+        type: str
+    device_owner:
+        description:
+           - The ID of the entity that uses this port.
+        type: str
+    dns_domain:
+        description:
+          - The dns domain of the port ( only with dns-integration enabled )
+        type: str
+    dns_name:
+        description:
+          - The dns name of the port ( only with dns-integration enabled )
+        type: str
+    extra_dhcp_opts:
+        description:
+          - "Extra dhcp options to be assigned to this port. Extra options are
+            supported with dictionary structure. Note that options cannot be
+            removed only updated.
+            e.g.  extra_dhcp_opts:
+                    - ip_version: 4
+                      opt_name: bootfile-name
+                      opt_value: pxelinux.0
+                    - opt_name: ..."
+          - The port will change during update if not all suboptions are
+            specified, e.g. when opt_name is given but ip_version is not.
+        type: list
+        elements: dict
+        suboptions:
+            ip_version:
+                description: The IP version this DHCP option is for.
+                type: int
+                required: true
+            opt_name:
+                description: The name of the DHCP option to set.
+                type: str
+                required: true
+            opt_value:
+                description: The value of the DHCP option to set.
+                type: str
+                required: true
+    fixed_ips:
+        description:
+          - Desired IP and/or subnet for this port.  Subnet is referenced by
+            subnet_id and IP is referenced by ip_address.
+          - The port will change during update if not all suboptions are
+            specified, e.g. when ip_address is given but subnet_id is not.
+        type: list
+        elements: dict
+        suboptions:
+            ip_address:
+                description: The fixed IP address to attempt to allocate.
+                required: true
+                type: str
+            subnet_id:
+                description: The subnet to attach the IP address to.
+                type: str
+    is_admin_state_up:
+        description:
+          - Sets admin state.
+        type: bool
+        aliases: ['admin_state_up']
+    mac_address:
+        description:
+          - MAC address of this port.
+        type: str
+    name:
+        description:
+          - Name that has to be given to the port.
+          - This port attribute cannot be updated.
+        type: str
+        required: true
+    network:
+        description:
+          - ID or name of the network this port belongs to.
+          - Required when creating a new port.
+          - Must be a name when creating a port.
+          - This port attribute cannot be updated.
+        type: str
+    no_security_groups:
+        description:
+          - Do not associate a security group with this port.
+          - "Deprecated. Use I(security_groups): C([]) instead
+            of I(no_security_groups): C(yes)."
+        type: bool
+        default: 'no'
+    port_security_enabled:
+        description:
+          - Whether to enable or disable the port security on the network.
+        type: bool
+    security_groups:
+        description:
+          - Security group(s) ID(s) or name(s) associated with the port.
+        type: list
+        elements: str
+    state:
+        description:
+          - Should the resource be present or absent.
+        choices: [present, absent]
+        default: present
+        type: str
 requirements:
     - "python >= 3.6"
     - "openstacksdk"
@@ -211,7 +232,7 @@ EXAMPLES = '''
       project_name: admin
     name: port1
     network: foo
-    vnic_type: direct
+    binding_vnic_type: direct
 
 # Create a port with binding profile
 - openstack.cloud.port:
@@ -224,305 +245,457 @@ EXAMPLES = '''
     name: port1
     network: foo
     binding_profile:
-      "pci_slot": "0000:03:11.1"
-      "physical_network": "provider"
+      pci_slot: "0000:03:11.1"
+      physical_network: "provider"
 '''
 
 RETURN = '''
-id:
-    description: Unique UUID.
-    returned: success
-    type: str
-name:
-    description: Name given to the port.
-    returned: success
-    type: str
-network_id:
-    description: Network ID this port belongs in.
-    returned: success
-    type: str
-security_groups:
-    description: Security group(s) associated with this port.
-    returned: success
-    type: list
-status:
-    description: Port's status.
-    returned: success
-    type: str
-fixed_ips:
-    description: Fixed ip(s) associated with this port.
-    returned: success
-    type: list
-tenant_id:
-    description: Tenant id associated with this port.
-    returned: success
-    type: str
-allowed_address_pairs:
-    description: Allowed address pairs with this port.
-    returned: success
-    type: list
-admin_state_up:
-    description: Admin state up flag for this port.
-    returned: success
-    type: bool
-vnic_type:
-    description: Type of the created port
-    returned: success
-    type: str
-port_security_enabled:
-    description: Port security state on the network.
-    returned: success
-    type: bool
-binding:profile:
-    description: Port binded profile
-    returned: success
+port:
+    description: Dictionary describing the port.
     type: dict
+    returned: On success when I(state) is C(present).
+    contains:
+        allowed_address_pairs:
+            description: Allowed address pairs.
+            returned: success
+            type: list
+            sample: []
+        binding_host_id:
+            description: |
+                The ID of the host where the port is allocated. In some cases,
+                different implementations can run on different hosts.
+            returned: success
+            type: str
+            sample: "b4bd682d-234a-4091-aa5b-4b025a6a7759"
+        binding_profile:
+            description: |
+                A dictionary the enables the application running on the
+                specified host to pass and receive vif port-specific
+                information to the plug-in.
+            returned: success
+            type: dict
+            sample: {}
+        binding_vif_details:
+            description: |
+                A dictionary that enables the application to pass
+                information about functions that the Networking API provides.
+            returned: success
+            type: dict
+        binding_vif_type:
+            description: The VIF type for the port.
+            returned: success
+            type: dict
+        binding_vnic_type:
+            description: |
+                The virtual network interface card (vNIC) type that is
+                bound to the neutron port.
+            returned: success
+            type: str
+            sample: "normal"
+        created_at:
+            description: Timestamp when the port was created.
+            returned: success
+            type: str
+            sample: "2022-02-03T13:28:25Z"
+        data_plane_status:
+            description: Status of the underlying data plane of a port.
+            returned: success
+            type: str
+        description:
+            description: The port description.
+            returned: success
+            type: str
+        device_id:
+            description: Device ID of this port.
+            returned: success
+            type: str
+            sample: "b4bd682d-234a-4091-aa5b-4b025a6a7759"
+        device_owner:
+            description: Device owner of this port, e.g. C(network:dhcp).
+            returned: success
+            type: str
+            sample: "network:router_interface"
+        device_profile:
+            description: |
+                Device profile of this port, refers to Cyborg device-profiles:
+                https://docs.openstack.org/api-ref/accelerator/v2/index.html#
+                device-profiles.
+            returned: success
+            type: str
+        dns_assignment:
+            description: DNS assignment for the port.
+            returned: success
+            type: list
+        dns_domain:
+            description: DNS domain assigned to the port.
+            returned: success
+            type: str
+        dns_name:
+            description: DNS name for the port.
+            returned: success
+            type: str
+        extra_dhcp_opts:
+            description: |
+                A set of zero or more extra DHCP option pairs.
+                An option pair consists of an option value and name.
+            returned: success
+            type: list
+            sample: []
+        fixed_ips:
+            description: |
+                IP addresses for the port. Includes the IP address and subnet
+                ID.
+            returned: success
+            type: list
+        id:
+            description: The port ID.
+            returned: success
+            type: str
+            sample: "3ec25c97-7052-4ab8-a8ba-92faf84148de"
+        ip_allocation:
+            description: |
+                The ip_allocation indicates when ports use deferred,
+                immediate or no IP allocation.
+            returned: success
+            type: str
+        is_admin_state_up:
+            description: |
+                The administrative state of the port, which is up C(True) or
+                down C(False).
+            returned: success
+            type: bool
+            sample: true
+        is_port_security_enabled:
+            description: |
+                The port security status, which is enabled C(True) or disabled
+                C(False).
+            returned: success
+            type: bool
+            sample: false
+        mac_address:
+            description: The MAC address of an allowed address pair.
+            returned: success
+            type: str
+            sample: "00:00:5E:00:53:42"
+        name:
+            description: The port name.
+            returned: success
+            type: str
+            sample: "port_name"
+        network_id:
+            description: The ID of the attached network.
+            returned: success
+            type: str
+            sample: "dd1ede4f-3952-4131-aab6-3b8902268c7d"
+        numa_affinity_policy:
+            description: |
+                The NUMA affinity policy defined for this port.
+            returned: success
+            type: str
+            sample: "required"
+        project_id:
+            description: The ID of the project who owns the network.
+            returned: success
+            type: str
+            sample: "aa1ede4f-3952-4131-aab6-3b8902268c7d"
+        propagate_uplink_status:
+            description: Whether to propagate uplink status of the port.
+            returned: success
+            type: bool
+            sample: false
+        qos_network_policy_id:
+            description: |
+                The ID of the QoS policy attached to the network where the
+                port is bound.
+            returned: success
+            type: str
+            sample: "1e4f3958-c0c9-4dec-82fa-ed2dc1c5cb34"
+        qos_policy_id:
+            description: The ID of the QoS policy attached to the port.
+            returned: success
+            type: str
+            sample: "b20bb47f-5d6d-45a6-8fe7-2c1b44f0db73"
+        resource_request:
+            description: |
+                The port-resource-request exposes Placement resources
+                (i.e.: minimum-bandwidth) and traits (i.e.: vnic-type, physnet)
+                requested by a port to Nova and Placement.
+            returned: success
+            type: str
+        revision_number:
+            description: The revision number of the resource.
+            returned: success
+            type: int
+            sample: 0
+        security_group_ids:
+            description: The IDs of any attached security groups.
+            returned: success
+            type: list
+        status:
+            description: The port status. Value is C(ACTIVE) or C(DOWN).
+            returned: success
+            type: str
+            sample: "ACTIVE"
+        tags:
+            description: The list of tags on the resource.
+            returned: success
+            type: list
+            sample: []
+        tenant_id:
+            description: Same as I(project_id). Deprecated.
+            returned: success
+            type: str
+            sample: "51fce036d7984ba6af4f6c849f65ef00"
+        trunk_details:
+            description: |
+                The trunk referring to this parent port and its subports.
+                Present for trunk parent ports if C(trunk-details) extension
+                is loaded.
+            returned: success
+            type: dict
+        updated_at:
+            description: Timestamp when the port was last updated.
+            returned: success
+            type: str
+            sample: "2022-02-03T13:28:25Z"
 '''
 
-from ansible.module_utils.basic import missing_required_lib
 from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
 
-try:
-    from collections import OrderedDict
-    HAS_ORDEREDDICT = True
-except ImportError:
-    try:
-        from ordereddict import OrderedDict
-        HAS_ORDEREDDICT = True
-    except ImportError:
-        HAS_ORDEREDDICT = False
-
 
-class NetworkPortModule(OpenStackModule):
+class PortModule(OpenStackModule):
     argument_spec = dict(
-        network=dict(),
-        name=dict(),
+        allowed_address_pairs=dict(type='list', elements='dict'),
+        binding_profile=dict(type='dict'),
+        binding_vnic_type=dict(choices=['normal', 'direct', 'direct-physical',
+                                        'macvtap', 'baremetal',
+                                        'virtio-forwarder'],
+                               aliases=['vnic_type']),
+        description=dict(),
+        device_id=dict(),
+        device_owner=dict(),
+        dns_domain=dict(),
+        dns_name=dict(),
+        extra_dhcp_opts=dict(type='list', elements='dict'),
         fixed_ips=dict(type='list', elements='dict'),
-        admin_state_up=dict(type='bool'),
+        is_admin_state_up=dict(type='bool', aliases=['admin_state_up']),
         mac_address=dict(),
-        security_groups=dict(type='list', elements='str'),
+        name=dict(required=True),
+        network=dict(),
         no_security_groups=dict(default=False, type='bool'),
-        allowed_address_pairs=dict(type='list', elements='dict'),
-        extra_dhcp_opts=dict(type='list', elements='dict'),
-        device_owner=dict(),
-        device_id=dict(),
-        state=dict(default='present', choices=['absent', 'present']),
-        vnic_type=dict(choices=['normal', 'direct', 'direct-physical',
-                                'macvtap', 'baremetal', 'virtio-forwarder']),
         port_security_enabled=dict(type='bool'),
-        binding_profile=dict(type='dict'),
-        dns_name=dict(),
-        dns_domain=dict()
+        security_groups=dict(type='list', elements='str'),
+        state=dict(default='present', choices=['absent', 'present']),
     )
 
     module_kwargs = dict(
         mutually_exclusive=[
             ['no_security_groups', 'security_groups'],
         ],
+        required_if=[
+            ('state', 'present', ('network',)),
+        ],
         supports_check_mode=True
     )
 
-    def _is_dns_integration_enabled(self):
-        """ Check if dns-integraton is enabled """
-        for ext in self.conn.network.extensions():
-            if ext.alias == 'dns-integration':
-                return True
-        return False
-
-    def _needs_update(self, port):
-        """Check for differences in the updatable values.
-
-        NOTE: We don't currently allow name updates.
-        """
-        compare_simple = ['admin_state_up',
-                          'mac_address',
-                          'device_owner',
-                          'device_id',
-                          'binding:vnic_type',
-                          'port_security_enabled',
-                          'binding:profile']
-        compare_dns = ['dns_name', 'dns_domain']
-        compare_list_dict = ['allowed_address_pairs',
-                             'extra_dhcp_opts']
-        compare_list = ['security_groups']
-
-        if self.conn.has_service('dns') and \
-           self._is_dns_integration_enabled():
-            for key in compare_dns:
-                if self.params[key] is not None and \
-                   self.params[key] != port[key]:
-                    return True
-
-        for key in compare_simple:
-            if self.params[key] is not None and self.params[key] != port[key]:
-                return True
-        for key in compare_list:
-            if (
-                self.params[key] is not None
-                and set(self.params[key]) != set(port[key])
-            ):
-                return True
-
-        for key in compare_list_dict:
-            if not self.params[key]:
-                if port.get(key):
-                    return True
-
-            if self.params[key]:
-                if not port.get(key):
-                    return True
-
-                # sort dicts in list
-                port_ordered = [OrderedDict(sorted(d.items())) for d in port[key]]
-                param_ordered = [OrderedDict(sorted(d.items())) for d in self.params[key]]
-
-                for d in param_ordered:
-                    if d not in port_ordered:
-                        return True
-
-                for d in port_ordered:
-                    if d not in param_ordered:
-                        return True
-
-        # NOTE: if port was created or updated with 'no_security_groups=True',
-        # subsequent updates without 'no_security_groups' flag or
-        # 'no_security_groups=False' and no specified 'security_groups', will not
-        # result in an update to the port where the default security group is
-        # applied.
-        if self.params['no_security_groups'] and port['security_groups'] != []:
-            return True
-
-        if self.params['fixed_ips'] is not None:
-            for item in self.params['fixed_ips']:
-                if 'ip_address' in item:
-                    # if ip_address in request does not match any in existing port,
-                    # update is required.
-                    if not any(match['ip_address'] == item['ip_address']
-                               for match in port['fixed_ips']):
-                        return True
-                if 'subnet_id' in item:
-                    return True
-            for item in port['fixed_ips']:
-                # if ip_address in existing port does not match any in request,
-                # update is required.
-                if not any(match.get('ip_address') == item['ip_address']
-                           for match in self.params['fixed_ips']):
-                    return True
-
-        return False
-
-    def _system_state_change(self, port):
+    def run(self):
+        network_name_or_id = self.params['network']
+        port_name_or_id = self.params['name']
         state = self.params['state']
-        if state == 'present':
-            if not port:
-                return True
-            return self._needs_update(port)
-        if state == 'absent' and port:
-            return True
-        return False
-
-    def _compose_port_args(self):
-        port_kwargs = {}
-        optional_parameters = ['name',
-                               'fixed_ips',
-                               'admin_state_up',
-                               'mac_address',
-                               'security_groups',
-                               'allowed_address_pairs',
-                               'extra_dhcp_opts',
-                               'device_owner',
-                               'device_id',
-                               'binding:vnic_type',
-                               'port_security_enabled',
-                               'binding:profile']
 
-        if self.conn.has_service('dns') and \
-           self._is_dns_integration_enabled():
-            optional_parameters.extend(['dns_name', 'dns_domain'])
+        network = None
+        if network_name_or_id:
+            network = self.conn.network.find_network(
+                network_name_or_id, ignore_missing=False)
 
-        for optional_param in optional_parameters:
-            if self.params[optional_param] is not None:
-                port_kwargs[optional_param] = self.params[optional_param]
+        port = self.conn.network.find_port(
+            port_name_or_id,
+            # use network id in query if network parameter was specified
+            **(dict(network_id=network.id) if network else dict()))
 
+        if self.ansible.check_mode:
+            self.exit_json(changed=self._will_change(network, port, state))
+
+        if state == 'present' and not port:
+            # create port
+            port = self._create(network)
+            self.exit_json(changed=True,
+                           port=port.to_dict(computed=False))
+        elif state == 'present' and port:
+            # update port
+            update = self._build_update(port)
+            if update:
+                port = self._update(port, update)
+
+            self.exit_json(changed=bool(update),
+                           port=port.to_dict(computed=False))
+        elif state == 'absent' and port:
+            # delete port
+            self._delete(port)
+            self.exit_json(changed=True)
+        elif state == 'absent' and not port:
+            # do nothing
+            self.exit_json(changed=False)
+
+    def _build_update(self, port):
+        update = {}
+
+        # A port's name cannot be updated by this module because
+        # it is used to find ports by name or id.
+        # If name is an id, then we do not have a name to update.
+        # If name is a name actually, then it was used to find a
+        # matching port hence the name is the user defined one
+        # already.
+
+        # updateable port attributes in openstacksdk
+        # (OpenStack API names in braces):
+        # - allowed_address_pairs (allowed_address_pairs)
+        # - binding_host_id (binding:host_id)
+        # - binding_profile (binding:profile)
+        # - binding_vnic_type (binding:vnic_type)
+        # - data_plane_status (data_plane_status)
+        # - description (description)
+        # - device_id (device_id)
+        # - device_owner (device_owner)
+        # (- device_profile (device_profile))
+        # - dns_domain (dns_domain)
+        # - dns_name (dns_name)
+        # - extra_dhcp_opts (extra_dhcp_opts)
+        # - fixed_ips (fixed_ips)
+        # - is_admin_state_up (admin_state_up)
+        # - is_port_security_enabled (port_security_enabled)
+        # - mac_address (mac_address)
+        # - name (name)
+        # - numa_affinity_policy (numa_affinity_policy)
+        # - qos_policy_id (qos_policy_id)
+        # - security_group_ids (security_groups)
+        # Ref.: https://docs.openstack.org/api-ref/network/v2/index.html#update-port
+
+        # Update all known updateable attributes although
+        # our module might not support them yet
+
+        # Update attributes which can be compared straight away
+        port_attributes = dict(
+            (k, self.params[k])
+            for k in ['binding_host_id', 'binding_vnic_type',
+                      'data_plane_status', 'description', 'device_id',
+                      'device_owner', 'is_admin_state_up',
+                      'is_port_security_enabled', 'mac_address',
+                      'numa_affinity_policy']
+            if k in self.params and self.params[k] is not None
+            and self.params[k] != port[k])
+
+        # Compare dictionaries
+        for k in ['binding_profile']:
+            if self.params[k] is None:
+                continue
+
+            if (self.params[k] or port[k]) \
+               and self.params[k] != port[k]:
+                port_attributes[k] = self.params[k]
+
+        # Attribute qos_policy_id is not supported by this module and would
+        # need special handling using self.conn.network.find_qos_policy()
+
+        # Compare attributes which are lists of dictionaries
+        for k in ['allowed_address_pairs', 'extra_dhcp_opts', 'fixed_ips']:
+            if self.params[k] is None:
+                continue
+
+            if (self.params[k] or port[k]) \
+               and self.params[k] != port[k]:
+                port_attributes[k] = self.params[k]
+
+        # Compare security groups
         if self.params['no_security_groups']:
-            port_kwargs['security_groups'] = []
-
-        return port_kwargs
-
-    def get_security_group_id(self, security_group_name_or_id):
-        security_group = self.conn.get_security_group(security_group_name_or_id)
-        if not security_group:
-            self.fail_json(msg="Security group: %s, was not found"
-                           % security_group_name_or_id)
-        return security_group['id']
-
-    def run(self):
-        if not HAS_ORDEREDDICT:
-            self.fail_json(msg=missing_required_lib('ordereddict'))
-
-        name = self.params['name']
-        state = self.params['state']
-
-        if self.params['security_groups']:
-            # translate security_groups to UUID's if names where provided
-            self.params['security_groups'] = [
-                self.get_security_group_id(v)
-                for v in self.params['security_groups']
+            security_group_ids = []
+        elif self.params['security_groups'] is not None:
+            security_group_ids = [
+                self.conn.network.find_security_group(
+                    security_group_name_or_id, ignore_missing=False).id
+                for security_group_name_or_id in self.params['security_groups']
             ]
+        else:
+            security_group_ids = None
 
-        # Neutron API accept 'binding:vnic_type' as an argument
-        # for the port type.
-        self.params['binding:vnic_type'] = self.params.pop('vnic_type')
-        # Neutron API accept 'binding:profile' as an argument
-        # for the port binding profile type.
-        self.params['binding:profile'] = self.params.pop('binding_profile')
+        if security_group_ids is not None \
+           and set(security_group_ids) != set(port['security_group_ids']):
+            port_attributes['security_group_ids'] = security_group_ids
 
-        port = None
-        network_id = None
-        if name:
-            port = self.conn.get_port(name)
+        # Compare dns attributes
+        if self.conn.has_service('dns') and \
+           self.conn.network.find_extension('dns-integration'):
+            port_attributes.update(dict(
+                (k, self.params[k])
+                for k in ['dns_name', 'dns_domain']
+                if self.params[k] is not None and self.params[k] != port[k]
+            ))
+
+        if port_attributes:
+            update['port_attributes'] = port_attributes
+        return update
+
+    def _create(self, network):
+        args = {}
+        args['network_id'] = network.id
+
+        # Fetch IDs of security groups next to fail early
+        # if any security group does not exist
+        if self.params['no_security_groups']:
+            args['security_group_ids'] = []
+        elif self.params['security_groups'] is not None:
+            args['security_group_ids'] = [
+                self.conn.network.find_security_group(
+                    security_group_name_or_id, ignore_missing=False).id
+                for security_group_name_or_id in self.params['security_groups']
+            ]
 
-        if self.ansible.check_mode:
-            self.exit_json(changed=self._system_state_change(port))
-
-        changed = False
-        if state == 'present':
-            if not port:
-                network = self.params['network']
-                if not network:
-                    self.fail_json(
-                        msg="Parameter 'network' is required in Port Create"
-                    )
-                port_kwargs = self._compose_port_args()
-                network_object = self.conn.get_network(network)
-
-                if network_object:
-                    network_id = network_object['id']
-                else:
-                    self.fail_json(
-                        msg="Specified network was not found."
-                    )
-
-                port_kwargs['network_id'] = network_id
-                port = self.conn.network.create_port(**port_kwargs)
-                changed = True
-            else:
-                if self._needs_update(port):
-                    port_kwargs = self._compose_port_args()
-                    port = self.conn.network.update_port(port['id'],
-                                                         **port_kwargs)
-                    changed = True
-            self.exit_json(changed=changed, id=port['id'], port=port)
-
-        if state == 'absent':
-            if port:
-                self.conn.delete_port(port['id'])
-                changed = True
-            self.exit_json(changed=changed)
+        for k in ['allowed_address_pairs',
+                  'binding_profile',
+                  'binding_vnic_type',
+                  'device_id',
+                  'device_owner',
+                  'description',
+                  'extra_dhcp_opts',
+                  'is_admin_state_up',
+                  'mac_address',
+                  'port_security_enabled',
+                  'fixed_ips',
+                  'name']:
+            if self.params[k] is not None:
+                args[k] = self.params[k]
+
+        if self.conn.has_service('dns') \
+           and self.conn.network.find_extension('dns-integration'):
+            for k in ['dns_domain', 'dns_name']:
+                if self.params[k] is not None:
+                    args[k] = self.params[k]
+
+        return self.conn.network.create_port(**args)
+
+    def _delete(self, port):
+        self.conn.network.delete_port(port.id)
+
+    def _update(self, port, update):
+        port_attributes = update.get('port_attributes')
+        if port_attributes:
+            port = self.conn.network.update_port(port, **port_attributes)
+        return port
+
+    def _will_change(self, port, state):
+        if state == 'present' and not port:
+            return True
+        elif state == 'present' and port:
+            return bool(self._build_update(port))
+        elif state == 'absent' and port:
+            return False
+        else:
+            # state == 'absent' and not port:
+            return True
 
 
 def main():
-    module = NetworkPortModule()
+    module = PortModule()
     module()
 
 
diff --git a/plugins/modules/port_info.py b/plugins/modules/port_info.py
index 8622dc22a3f93a81bd749af0357f67e3d12d4df1..717480f14a10d70e5494ab3e10b6460695f25c22 100644
--- a/plugins/modules/port_info.py
+++ b/plugins/modules/port_info.py
@@ -11,10 +11,11 @@ author: OpenStack Ansible SIG
 description:
     - Retrieve information about ports from OpenStack.
 options:
-    port:
+    name:
         description:
             - Unique name or ID of a port.
         type: str
+        aliases: ['port']
     filters:
         description:
             - A dictionary of meta data to use for further filtering. Elements
@@ -41,10 +42,10 @@ EXAMPLES = '''
 # Gather information about a single port
 - openstack.cloud.port_info:
     cloud: mycloud
-    port: 6140317d-e676-31e1-8a4a-b1913814a471
+    name: 6140317d-e676-31e1-8a4a-b1913814a471
 
-# Gather information about all ports that have device_id set to a specific value
-# and with a status of ACTIVE.
+# Gather information about all ports that have device_id set to a specific
+# value and with a status of ACTIVE.
 - openstack.cloud.port_info:
     cloud: mycloud
     filters:
@@ -54,34 +55,37 @@ EXAMPLES = '''
 
 RETURN = '''
 ports:
-    description: List of port dictionaries. A subset of the dictionary keys
-                 listed below may be returned, depending on your cloud provider.
+    description: |
+        List of port dictionaries. A subset of the dictionary keys listed below
+        may be returned, depending on your cloud provider.
     returned: always
     type: list
     elements: dict
     contains:
         allowed_address_pairs:
-            description: A set of zero or more allowed address pairs. An
-                         address pair consists of an IP address and MAC address.
+            description: Allowed address pairs.
             returned: success
             type: list
             sample: []
         binding_host_id:
-            description: The UUID of the host where the port is allocated.
+            description: |
+                The ID of the host where the port is allocated. In some cases,
+                different implementations can run on different hosts.
             returned: success
             type: str
             sample: "b4bd682d-234a-4091-aa5b-4b025a6a7759"
         binding_profile:
-            description: A dictionary the enables the application running on
-                         the host to pass and receive VIF port-specific
-                         information to the plug-in.
+            description: |
+                A dictionary the enables the application running on the
+                specified host to pass and receive vif port-specific
+                information to the plug-in.
             returned: success
             type: dict
             sample: {}
         binding_vif_details:
-            description: A dictionary that enables the application to pass
-                         information about functions that the Networking API
-                         provides.
+            description: |
+                A dictionary that enables the application to pass
+                information about functions that the Networking API provides.
             returned: success
             type: dict
         binding_vif_type:
@@ -89,13 +93,14 @@ ports:
             returned: success
             type: dict
         binding_vnic_type:
-            description: The virtual network interface card (vNIC) type that is
-                         bound to the neutron port.
+            description: |
+                The virtual network interface card (vNIC) type that is
+                bound to the neutron port.
             returned: success
             type: str
             sample: "normal"
         created_at:
-            description: Date the port was created
+            description: Timestamp when the port was created.
             returned: success
             type: str
             sample: "2022-02-03T13:28:25Z"
@@ -104,69 +109,78 @@ ports:
             returned: success
             type: str
         description:
-            description: Description of a port
+            description: The port description.
             returned: success
             type: str
         device_id:
-            description: The UUID of the device that uses this port.
+            description: Device ID of this port.
             returned: success
             type: str
             sample: "b4bd682d-234a-4091-aa5b-4b025a6a7759"
         device_owner:
-            description: The UUID of the entity that uses this port.
+            description: Device owner of this port, e.g. C(network:dhcp).
             returned: success
             type: str
             sample: "network:router_interface"
         device_profile:
-            description: Device profile
+            description: |
+                Device profile of this port, refers to Cyborg device-profiles:
+                https://docs.openstack.org/api-ref/accelerator/v2/index.html#
+                device-profiles.
             returned: success
             type: str
         dns_assignment:
-            description: DNS assignment information.
+            description: DNS assignment for the port.
             returned: success
             type: list
         dns_domain:
-            description: A valid DNS domain
+            description: DNS domain assigned to the port.
             returned: success
             type: str
         dns_name:
-            description: DNS name
+            description: DNS name for the port.
             returned: success
             type: str
         extra_dhcp_opts:
-            description: A set of zero or more extra DHCP option pairs.
-                         An option pair consists of an option value and name.
+            description: |
+                A set of zero or more extra DHCP option pairs.
+                An option pair consists of an option value and name.
             returned: success
             type: list
             sample: []
         fixed_ips:
-            description: The IP addresses for the port. Includes the IP address
-                         and UUID of the subnet.
+            description: |
+                IP addresses for the port. Includes the IP address and subnet
+                ID.
             returned: success
             type: list
         id:
-            description: The UUID of the port.
+            description: The port ID.
             returned: success
             type: str
             sample: "3ec25c97-7052-4ab8-a8ba-92faf84148de"
         ip_allocation:
-            description: Indicates when ports use either deferred, immediate
-                         or no IP allocation (none).
+            description: |
+                The ip_allocation indicates when ports use deferred,
+                immediate or no IP allocation.
             returned: success
             type: str
         is_admin_state_up:
-            description: The administrative state of the router, which is
-                         up (true) or down (false).
+            description: |
+                The administrative state of the port, which is up C(True) or
+                down C(False).
             returned: success
             type: bool
             sample: true
         is_port_security_enabled:
-            description: The port security status. The status is enabled (true) or disabled (false).
+            description: |
+                The port security status, which is enabled C(True) or disabled
+                C(False).
             returned: success
             type: bool
             sample: false
         mac_address:
-            description: The MAC address.
+            description: The MAC address of an allowed address pair.
             returned: success
             type: str
             sample: "00:00:5E:00:53:42"
@@ -176,42 +190,43 @@ ports:
             type: str
             sample: "port_name"
         network_id:
-            description: The UUID of the attached network.
+            description: The ID of the attached network.
             returned: success
             type: str
             sample: "dd1ede4f-3952-4131-aab6-3b8902268c7d"
         numa_affinity_policy:
-            description: The port NUMA affinity policy requested during the
-                         virtual machine scheduling. Values are None, required,
-                         preferred or legacy.
+            description: |
+                The NUMA affinity policy defined for this port.
             returned: success
             type: str
             sample: "required"
         project_id:
-            description: The ID of the project.
+            description: The ID of the project who owns the network.
             returned: success
             type: str
             sample: "aa1ede4f-3952-4131-aab6-3b8902268c7d"
         propagate_uplink_status:
-            description: The uplink status propagation of the port.
+            description: Whether to propagate uplink status of the port.
             returned: success
             type: bool
             sample: false
         qos_network_policy_id:
-            description: The ID of the QoS policy of the network where this
-                         port is plugged.
+            description: |
+                The ID of the QoS policy attached to the network where the
+                port is bound.
             returned: success
             type: str
             sample: "1e4f3958-c0c9-4dec-82fa-ed2dc1c5cb34"
         qos_policy_id:
-            description: The ID of the QoS policy associated with the port.
+            description: The ID of the QoS policy attached to the port.
             returned: success
             type: str
             sample: "b20bb47f-5d6d-45a6-8fe7-2c1b44f0db73"
         resource_request:
-            description: Expose Placement resources i.e. minimum-bandwidth
-                         and traits i.e. vnic-type, physnet requested by a
-                         port to Nova and Placement
+            description: |
+                The port-resource-request exposes Placement resources
+                (i.e.: minimum-bandwidth) and traits (i.e.: vnic-type, physnet)
+                requested by a port to Nova and Placement.
             returned: success
             type: str
         revision_number:
@@ -220,11 +235,11 @@ ports:
             type: int
             sample: 0
         security_group_ids:
-            description: The UUIDs of any attached security groups.
+            description: The IDs of any attached security groups.
             returned: success
             type: list
         status:
-            description: The port status.
+            description: The port status. Value is C(ACTIVE) or C(DOWN).
             returned: success
             type: str
             sample: "ACTIVE"
@@ -234,47 +249,47 @@ ports:
             type: list
             sample: []
         tenant_id:
-            description: The UUID of the tenant who owns the network. Deprecated.
+            description: Same as I(project_id). Deprecated.
             returned: success
             type: str
             sample: "51fce036d7984ba6af4f6c849f65ef00"
         trunk_details:
-            description: The details about the trunk.
+            description: |
+                The trunk referring to this parent port and its subports.
+                Present for trunk parent ports if C(trunk-details) extension
+                is loaded.
             returned: success
             type: dict
         updated_at:
-            description: Last port update
+            description: Timestamp when the port was last updated.
             returned: success
             type: str
             sample: "2022-02-03T13:28:25Z"
-
 '''
 
 from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
 
 
-class NetworkPortInfoModule(OpenStackModule):
+class PortInfoModule(OpenStackModule):
     argument_spec = dict(
-        port=dict(),
-        filters=dict(type='dict', default={}),
+        name=dict(aliases=['port']),
+        filters=dict(type='dict'),
     )
     module_kwargs = dict(
         supports_check_mode=True
     )
 
     def run(self):
-        port = self.params['port']
-        filters = self.params['filters']
-
-        ports = self.conn.search_ports(port, filters)
-
-        ports = [p.to_dict(computed=False) for p in ports]
+        ports = [p.to_dict(computed=False) for p in
+                 self.conn.search_ports(
+                     name_or_id=self.params['name'],
+                     filters=self.params['filters'])]
 
         self.exit_json(changed=False, ports=ports)
 
 
 def main():
-    module = NetworkPortInfoModule()
+    module = PortInfoModule()
     module()