From fb0fb529b7b8753cef624002b376b56bac18bf1d Mon Sep 17 00:00:00 2001
From: anbanerj <anbanerj@redhat.com>
Date: Thu, 16 Jun 2022 13:35:45 +0200
Subject: [PATCH] Makes security_group_rule_info compatible with new sdk
 version

- Changed get_security_group_rule to find_security_group_rule as
  get_security_group_rule throws an exception if the rule is not
  found
- Updated docs
- Updated tests
- Renamed ethertype to ether_type to match openstacksdk's attribute
  names and added the former as an alias to keep backward compat
- Renamed rule to id to match openstacksdk's attribute names and
  added the former as an alias to keep backward compat

Change-Id: Ieb99f875c990e11623c81e482013d0ecb8e61055
---
 ci/roles/security_group_rule/tasks/main.yml |  22 ++-
 plugins/modules/security_group_rule_info.py | 176 ++++++++++----------
 2 files changed, 110 insertions(+), 88 deletions(-)

diff --git a/ci/roles/security_group_rule/tasks/main.yml b/ci/roles/security_group_rule/tasks/main.yml
index 739821b..1397858 100644
--- a/ci/roles/security_group_rule/tasks/main.yml
+++ b/ci/roles/security_group_rule/tasks/main.yml
@@ -22,11 +22,31 @@
      remote_ip_prefix: 0.0.0.0/0
   register: rule
 
-- name: Assert return fields
+- name: Assert return fields for security_group_rule
   assert:
      that: item in rule.rule
   loop: "{{ expected_fields }}"
 
+- name: Fetch all security group rule
+  openstack.cloud.security_group_rule_info:
+     cloud: "{{ cloud }}"
+  register: all_rules
+
+- name: Assert return fields security_group_rule_info
+  assert:
+     that: item in all_rules.security_group_rules[0]
+  loop: "{{ expected_fields }}"
+
+- name: Fetch security group rule based on rule
+  openstack.cloud.security_group_rule_info:
+     cloud: "{{ cloud }}"
+     id: "{{ rule.rule.id }}"
+  register: filter_by_rule
+
+- name: Assert return fields security_group_rule_info
+  assert:
+    that: filter_by_rule.security_group_rules|length != 0
+
 - name: Assert changed
   assert:
      that: rule is changed
diff --git a/plugins/modules/security_group_rule_info.py b/plugins/modules/security_group_rule_info.py
index 99a120f..4d0e57f 100644
--- a/plugins/modules/security_group_rule_info.py
+++ b/plugins/modules/security_group_rule_info.py
@@ -23,12 +23,18 @@ options:
         which the security group rule is applied.
     choices: ['egress', 'ingress']
     type: str
-  ethertype:
+  ether_type:
     description:
-      - Filter the security group rule list result by the ethertype of
+      - Filter the security group rule list result by the ether_type of
         network traffic. The value must be IPv4 or IPv6.
     choices: ['IPv4', 'IPv6']
     type: str
+    aliases: ['ethertype']
+  id:
+    description:
+      - Filter the list result by the ID of the security group rule.
+    type: str
+    aliases: ['rule']
   port_range_min:
     description:
       - Starting port
@@ -46,7 +52,6 @@ options:
     description:
       - Filter the security group rule list result by the IP protocol.
     type: str
-    choices: ['any', 'tcp', 'udp', 'icmp', '112', '132']
   remote_group:
     description:
       - Filter the security group rule list result by the name or ID of the
@@ -60,10 +65,6 @@ options:
     description:
       - Filter the list result by the revision number of the resource.
     type: int
-  rule:
-    description:
-      - Filter the list result by the ID of the security group rule.
-    type: str
   security_group:
     description:
       - Name or ID of the security group
@@ -80,13 +81,13 @@ extends_documentation_fragment:
 EXAMPLES = '''
 # Get all security group rules
 - openstack.cloud.security_group_rule_info:
-    cloud: "{{ cloud }}"
+    cloud: devstack
   register: sg
 
 # Filter security group rules for port 80 and name
 - openstack.cloud.security_group_rule_info:
-    cloud: "{{ cloud }}"
-    security_group: "{{ rule_name }}"
+    cloud: devstack
+    security_group: foo
     protocol: tcp
     port_range_min: 80
     port_range_max: 80
@@ -94,18 +95,19 @@ EXAMPLES = '''
 
 # Filter for ICMP rules
 - openstack.cloud.security_group_rule_info:
-    cloud: "{{ cloud }}"
+    cloud: devstack
     protocol: icmp
 '''
 
 RETURN = '''
 security_group_rules:
   description: List of dictionaries describing security group rules.
-  type: complex
-  returned: On Success.
+  type: list
+  elements: dict
+  returned: always
   contains:
-    id:
-      description: Unique rule UUID.
+    created_at:
+      description: Timestamp when the security group rule was created.
       type: str
     description:
       description: Human-readable description of the resource.
@@ -115,37 +117,70 @@ security_group_rules:
       description: The direction in which the security group rule is applied.
       type: str
       sample: 'egress'
-    ethertype:
+    ether_type:
       description: One of IPv4 or IPv6.
       type: str
       sample: 'IPv4'
-    port_range_min:
-      description: The minimum port number in the range that is matched by
-                   the security group rule.
-      type: int
-      sample: 8000
+    id:
+      description: Unique rule UUID.
+      type: str
+    name:
+      description: Name of the resource.
+      type: str
     port_range_max:
       description: The maximum port number in the range that is matched by
                   the security group rule.
       type: int
       sample: 8000
-    project:
-      description:
-        - Unique ID of the project.
+    port_range_min:
+      description: The minimum port number in the range that is matched by
+                   the security group rule.
+      type: int
+      sample: 8000
+    project_id:
+      description: The ID of the project.
       type: str
-      sample: '16d53a84a13b49529d2e2c3646691123'
+      sample: 'e4f50856753b4dc6afee5fa6b9b6c550'
     protocol:
       description: The protocol that is matched by the security group rule.
       type: str
       sample: 'tcp'
+    remote_address_group_id:
+      description: The remote address group ID to be associated with this
+                   security group rule.
+      type: str
+    remote_group_id:
+      description: The remote security group ID to be associated with this
+                   security group rule.
+      type: str
     remote_ip_prefix:
-      description: The remote IP prefix to be associated with this security group rule.
+      description: The remote IP prefix to be associated with this security
+                   group rule.
+      type: str
+    revision_number:
+      description: The remote IP prefix to be associated with this security
+                   group rule.
       type: str
       sample: '0.0.0.0/0'
     security_group_id:
-      description: The security group ID to associate with this security group rule.
+      description: The security group ID to associate with this security
+                   group rule.
       type: str
       sample: '729b9660-a20a-41fe-bae6-ed8fa7f69123'
+    tags:
+      description: The security group ID to associate with this security
+                   group rule.
+      type: str
+      sample: '729b9660-a20a-41fe-bae6-ed8fa7f69123'
+    tenant_id:
+      description: The ID of the project. Deprecated.
+      type: str
+      sample: 'e4f50856753b4dc6afee5fa6b9b6c550'
+    updated_at:
+      description: Time at which the resource has been updated
+                   (in UTC ISO8601 format).
+      type: str
+      sample: '2018-03-19T19:16:56Z'
 '''
 
 from ansible_collections.openstack.cloud.plugins.module_utils.openstack import (
@@ -156,84 +191,51 @@ class SecurityGroupRuleInfoModule(OpenStackModule):
     argument_spec = dict(
         description=dict(),
         direction=dict(choices=['egress', 'ingress']),
-        ethertype=dict(choices=['IPv4', 'IPv6']),
-        port_range_min=dict(type='int', min_ver="0.32.0"),
-        port_range_max=dict(type='int', min_ver="0.32.0"),
+        ether_type=dict(choices=['IPv4', 'IPv6'], aliases=['ethertype']),
+        id=dict(aliases=['rule']),
+        port_range_min=dict(type='int'),
+        port_range_max=dict(type='int'),
         project=dict(),
-        protocol=dict(choices=['any', 'tcp', 'udp', 'icmp', '112', '132']),
+        protocol=dict(),
         remote_group=dict(),
-        remote_ip_prefix=dict(min_ver="0.32.0"),
+        remote_ip_prefix=dict(),
         revision_number=dict(type='int'),
-        rule=dict(),
         security_group=dict()
     )
 
     module_kwargs = dict(
         mutually_exclusive=[
-            ['remote_ip_prefix', 'remote_group'],
+            ('remote_ip_prefix', 'remote_group'),
         ],
         supports_check_mode=True
     )
 
     def run(self):
-        description = self.params['description']
-        direction = self.params['direction']
-        ethertype = self.params['ethertype']
-        project = self.params['project']
-        protocol = self.params['protocol']
-        remote_group = self.params['remote_group']
-        revision_number = self.params['revision_number']
-        rule = self.params['rule']
-        security_group = self.params['security_group']
-
-        changed = False
-        filters = self.check_versioned(
-            port_range_min=self.params['port_range_min'],
-            port_range_max=self.params['port_range_max'],
-            remote_ip_prefix=self.params['remote_ip_prefix']
-        )
-        data = []
-
-        if rule:
-            sec_rule = self.conn.network.get_security_group_rule(rule)
-            if sec_rule is None:
-                self.exit(changed=changed, security_group_rules=[])
-            self.exit(changed=changed,
-                      security_group_rules=sec_rule.to_dict())
-            # query parameter id is currently not supported
-            # PR is open for that.
-            # filters['id] = sec_rule.id
-        if description:
-            filters['description'] = description
-        if direction:
-            filters['direction'] = direction
-        if ethertype:
-            filters['ethertype'] = ethertype
-        if project:
-            proj = self.conn.get_project(project)
-            if proj is None:
-                self.fail_json(msg='Project %s could not be found' % project)
+        filters = dict((k, self.params[k])
+                       for k in ['description', 'direction', 'ether_type',
+                                 'port_range_min', 'port_range_max',
+                                 'protocol', 'remote_group',
+                                 'revision_number', 'remote_ip_prefix']
+                       if self.params[k] is not None)
+
+        if self.params['id']:
+            filters['id'] = self.params['id']
+        if self.params['project']:
+            proj = self.conn.find_project(self.params['project'],
+                                          ignore_missing=False)
             filters['project_id'] = proj.id
-        if protocol:
-            filters['protocol'] = protocol
-        if remote_group:
-            filters['remote_group_id'] = remote_group
-        if revision_number:
-            filters['revision_number'] = revision_number
-        if security_group:
+        if self.params['security_group']:
             sec_grp = self.conn.network.find_security_group(
-                name_or_id=security_group,
-                ignore_missing=True)
-            if sec_grp is None:
-                self.fail_json(msg='Security group %s could not be found' % sec_grp)
+                name_or_id=self.params['security_group'],
+                ignore_missing=False)
             filters['security_group_id'] = sec_grp.id
 
-        for item in self.conn.network.security_group_rules(**filters):
-            item = item.to_dict()
-            data.append(item)
+        security_group_rules = [
+            rule.to_dict(computed=False)
+            for rule in self.conn.network.security_group_rules(**filters)]
 
-        self.exit_json(changed=changed,
-                       security_group_rules=data)
+        self.exit_json(changed=False,
+                       security_group_rules=security_group_rules)
 
 
 def main():
-- 
GitLab