diff --git a/ci/roles/security_group/tasks/main.yml b/ci/roles/security_group/tasks/main.yml index 6be133c3..62e6b5b8 100644 --- a/ci/roles/security_group/tasks/main.yml +++ b/ci/roles/security_group/tasks/main.yml @@ -101,6 +101,26 @@ remote_ip_prefix: 0.0.0.0/0 direction: egress +- name: List all available rules of all security groups in a project + openstack.cloud.security_group_rule_info: + cloud: "{{ cloud }}" + when: sdk_version is version("0.32", '>=') + +- name: List all available rules of a specific security group + openstack.cloud.security_group_rule_info: + cloud: "{{ cloud }}" + security_group: "{{ secgroup_name }}" + +- name: List all available rules with filters + openstack.cloud.security_group_rule_info: + cloud: "{{ cloud }}" + security_group: "{{ secgroup_name }}" + protocol: tcp + port_range_min: 80 + port_range_max: 80 + remote_ip_prefix: 0.0.0.0/0 + when: sdk_version is version("0.32", '>=') + - name: Delete empty ICMP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" diff --git a/meta/runtime.yml b/meta/runtime.yml index 496a335d..7410aa16 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -78,6 +78,7 @@ action_groups: - routers_info - security_group - security_group_rule + - security_group_rule_info - server - server_action - server_group @@ -172,6 +173,7 @@ action_groups: - routers_info - security_group - security_group_rule + - security_group_rule_info - server - server_action - server_group diff --git a/plugins/modules/security_group_rule_info.py b/plugins/modules/security_group_rule_info.py new file mode 100644 index 00000000..962ab9a4 --- /dev/null +++ b/plugins/modules/security_group_rule_info.py @@ -0,0 +1,250 @@ +#!/usr/bin/python +# coding: utf-8 -*- +# +# Copyright (c) 2020 by Tino Schreiber (Open Telekom Cloud), operated by T-Systems International GmbH +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +DOCUMENTATION = ''' +--- +module: security_group_rule_info +short_description: Querying security group rules +author: OpenStack Ansible SIG +description: + - Querying security group rules +options: + description: + description: + - Filter the list result by the human-readable description of + the resource. + type: str + direction: + description: + - Filter the security group rule list result by the direction in + which the security group rule is applied. + choices: ['egress', 'ingress'] + type: str + ethertype: + description: + - Filter the security group rule list result by the ethertype of + network traffic. The value must be IPv4 or IPv6. + choices: ['IPv4', 'IPv6'] + type: str + port_range_min: + description: + - Starting port + type: int + port_range_max: + description: + - Ending port + type: int + project: + description: + - Unique name or ID of the project. + required: false + type: str + protocol: + 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 + remote group that associates with this security group rule. + type: str + remote_ip_prefix: + description: + - Source IP address(es) in CIDR notation (exclusive with remote_group) + type: str + revision_number: + 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 + type: str + +requirements: + - "python >= 3.6" + - "openstacksdk" + +extends_documentation_fragment: + - openstack.cloud.openstack +''' + +EXAMPLES = ''' +# Get all security group rules +- openstack.cloud.security_group_rule_info: + cloud: "{{ cloud }}" + register: sg + +# Filter security group rules for port 80 and name +- openstack.cloud.security_group_rule_info: + cloud: "{{ cloud }}" + security_group: "{{ rule_name }}" + protocol: tcp + port_range_min: 80 + port_range_max: 80 + remote_ip_prefix: 0.0.0.0/0 + +# Filter for ICMP rules +- openstack.cloud.security_group_rule_info: + cloud: "{{ cloud }}" + protocol: icmp +''' + +RETURN = ''' +security_group_rules: + description: List of dictionaries describing security group rules. + type: complex + returned: On Success. + contains: + id: + description: Unique rule UUID. + type: str + description: + description: Human-readable description of the resource. + type: str + sample: 'My description.' + direction: + description: The direction in which the security group rule is applied. + type: str + sample: 'egress' + ethertype: + 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 + 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. + type: str + sample: '16d53a84a13b49529d2e2c3646691123' + protocol: + description: The protocol that is matched by the security group rule. + type: str + sample: 'tcp' + remote_ip_prefix: + 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. + type: str + sample: '729b9660-a20a-41fe-bae6-ed8fa7f69123' +''' + +from ansible_collections.openstack.cloud.plugins.module_utils.openstack import ( + OpenStackModule) + + +class SecurityGroupRuleInfoModule(OpenStackModule): + argument_spec = dict( + description=dict(required=False, type='str'), + direction=dict(required=False, + type='str', + choices=['egress', 'ingress']), + ethertype=dict(required=False, + type='str', + choices=['IPv4', 'IPv6']), + port_range_min=dict(required=False, type='int', min_ver="0.32.0"), + port_range_max=dict(required=False, type='int', min_ver="0.32.0"), + project=dict(required=False, type='str'), + protocol=dict(required=False, + type='str', + choices=['any', 'tcp', 'udp', 'icmp', '112', '132']), + remote_group=dict(required=False, type='str'), + remote_ip_prefix=dict(required=False, type='str', min_ver="0.32.0"), + revision_number=dict(required=False, type='int'), + rule=dict(required=False, type='str'), + security_group=dict(required=False, type='str') + ) + + module_kwargs = dict( + mutually_exclusive=[ + ['remote_ip_prefix', 'remote_group'], + ] + ) + + 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['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: + 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) + filters['security_group_id'] = sec_grp.id + + for item in self.conn.network.security_group_rules(**filters): + item = item.to_dict() + data.append(item) + + self.exit_json(changed=changed, + security_group_rules=data) + + +def main(): + module = SecurityGroupRuleInfoModule() + module() + + +if __name__ == '__main__': + main()