diff --git a/ci/roles/port/defaults/main.yml b/ci/roles/port/defaults/main.yml index 4e2d6602..08087993 100644 --- a/ci/roles/port/defaults/main.yml +++ b/ci/roles/port/defaults/main.yml @@ -43,5 +43,4 @@ 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 9b5f196f..52a831ea 100644 --- a/ci/roles/port/tasks/main.yml +++ b/ci/roles/port/tasks/main.yml @@ -86,7 +86,7 @@ openstack.cloud.security_group: cloud: "{{ cloud }}" state: present - name: "{{ secgroup_name }}" + name: ansible_security_group description: Test group register: security_group @@ -99,7 +99,7 @@ fixed_ips: - ip_address: 10.5.5.69 security_groups: - - "{{ secgroup_name }}" + - ansible_security_group register: port - debug: var=port @@ -221,7 +221,7 @@ name: "{{ port_name }}" network: "{{ network_name }}" security_groups: - - "{{ secgroup_name }}" + - ansible_security_group state: present register: port_updated @@ -242,7 +242,7 @@ - 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 + - port_updated.port.security_group_ids[0] == security_group.security_group.id - name: Delete updated port openstack.cloud.port: @@ -254,7 +254,7 @@ openstack.cloud.security_group: cloud: "{{ cloud }}" state: absent - name: "{{ secgroup_name }}" + name: ansible_security_group - name: Create port (with binding profile) openstack.cloud.port: diff --git a/ci/roles/security_group/defaults/main.yml b/ci/roles/security_group/defaults/main.yml index c2a1a6b0..60530741 100644 --- a/ci/roles/security_group/defaults/main.yml +++ b/ci/roles/security_group/defaults/main.yml @@ -1,13 +1,12 @@ expected_fields: -- created_at -- description -- name -- project_id -- security_group_rules -- stateful -- tenant_id -- updated_at -- revision_number -- id -- tags -secgroup_name: shade_secgroup + - created_at + - description + - name + - project_id + - security_group_rules + - stateful + - tenant_id + - updated_at + - revision_number + - id + - tags diff --git a/ci/roles/security_group/tasks/main.yml b/ci/roles/security_group/tasks/main.yml index a1846896..0aec2590 100644 --- a/ci/roles/security_group/tasks/main.yml +++ b/ci/roles/security_group/tasks/main.yml @@ -1,75 +1,73 @@ --- -- name: Ensure security group does not exist before tests - openstack.cloud.security_group: - cloud: "{{ cloud }}" - name: "{{ secgroup_name }}" - state: absent - - name: Create security group openstack.cloud.security_group: cloud: "{{ cloud }}" - name: "{{ secgroup_name }}" + name: ansible_security_group state: present - description: Created from Ansible playbook + description: 'Created from Ansible playbook' register: security_group -- name: List all security groups of a project - openstack.cloud.security_group_info: - cloud: "{{ cloud }}" - register: test_sec_groups - -- name: Check list all security groups of a project +- name: Assert return values of security_group module assert: that: - - test_sec_groups.security_groups | length > 0 + - security_group.security_group.name == 'ansible_security_group' + - security_group.security_group.description == 'Created from Ansible playbook' + # allow new fields to be introduced but prevent fields from being removed + - expected_fields|difference(security_group.security_group.keys())|length == 0 -- name: Assert fields returned by security_group_info - assert: - that: - - item in test_sec_groups.security_groups[0] - loop: "{{ expected_fields }}" - -- name: Filter security group by name +- name: List all security groups openstack.cloud.security_group_info: cloud: "{{ cloud }}" - name: "{{ secgroup_name }}" - register: test_sec_group + register: security_groups + +- name: Assert return values of security_group_info module + assert: + that: + - security_groups.security_groups | length > 0 + # allow new fields to be introduced but prevent fields from being removed + - expected_fields|difference(security_groups.security_groups[0].keys())|length == 0 + +- name: Find security group by name + openstack.cloud.security_group_info: + cloud: "{{ cloud }}" + name: ansible_security_group + register: security_groups - name: Check filter security group by name assert: that: - - test_sec_group.security_groups | length == 1 - - test_sec_group.security_groups[0]['id'] == security_group.id + - security_groups.security_groups | length == 1 + - security_groups.security_groups.0.id == security_group.security_group.id - name: Filter security group by description openstack.cloud.security_group_info: cloud: "{{ cloud }}" - description: Created from Ansible playbook - register: test_sec_group + description: 'Created from Ansible playbook' + register: security_groups - name: Check filter security group by description assert: that: - - test_sec_group.security_groups | length == 1 - - test_sec_group.security_groups[0]['id'] == security_group.id + - security_groups.security_groups | length == 1 + - security_groups.security_groups.0.id == security_group.security_group.id - name: Filter security group by not_tags openstack.cloud.security_group_info: cloud: "{{ cloud }}" - name: "{{ secgroup_name }}" + name: ansible_security_group not_tags: - - ansibletag1 - - ansibletag2 - register: test_sec_group + - ansibletag1 + - ansibletag2 + register: security_groups - name: Check filter security group by not_tags assert: that: - - test_sec_group.security_groups | length == 1 - - test_sec_group.security_groups[0]['id'] == security_group.id + - security_groups.security_groups | length == 1 + - security_groups.security_groups.0.id == security_group.security_group.id - name: Delete security group openstack.cloud.security_group: cloud: "{{ cloud }}" - name: "{{ secgroup_name }}" + name: ansible_security_group state: absent diff --git a/ci/roles/security_group_rule/defaults/main.yml b/ci/roles/security_group_rule/defaults/main.yml index 81f7822e..6ae6b8e3 100644 --- a/ci/roles/security_group_rule/defaults/main.yml +++ b/ci/roles/security_group_rule/defaults/main.yml @@ -17,4 +17,3 @@ expected_fields: - tags - tenant_id - updated_at -secgroup_name: shade_secgroup diff --git a/ci/roles/security_group_rule/tasks/main.yml b/ci/roles/security_group_rule/tasks/main.yml index 13978589..c8220eda 100644 --- a/ci/roles/security_group_rule/tasks/main.yml +++ b/ci/roles/security_group_rule/tasks/main.yml @@ -1,161 +1,157 @@ --- -- name: Ensure security group does not exist before tests - openstack.cloud.security_group: - cloud: "{{ cloud }}" - name: "{{ secgroup_name }}" - state: absent - description: Created from Ansible playbook - - name: Create security group openstack.cloud.security_group: cloud: "{{ cloud }}" - name: "{{ secgroup_name }}" + name: ansible_security_group state: present description: Created from Ansible playbook - name: Create empty ICMP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: icmp remote_ip_prefix: 0.0.0.0/0 - register: rule + register: security_group_rule -- name: Assert return fields for security_group_rule +- name: Assert return values of security_group_rule module assert: - that: item in rule.rule - loop: "{{ expected_fields }}" + that: + # allow new fields to be introduced but prevent fields from being removed + - expected_fields|difference(security_group_rule.rule.keys())|length == 0 + +- name: Assert changed + assert: + that: security_group_rule is changed - name: Fetch all security group rule openstack.cloud.security_group_rule_info: cloud: "{{ cloud }}" - register: all_rules + register: security_group_rules -- name: Assert return fields security_group_rule_info +- name: Assert return values of security_group_rule_info module assert: - that: item in all_rules.security_group_rules[0] - loop: "{{ expected_fields }}" + that: + - security_group_rules.security_group_rules | length > 0 + # allow new fields to be introduced but prevent fields from being removed + - expected_fields|difference(security_group_rules.security_group_rules.0.keys())|length == 0 - name: Fetch security group rule based on rule openstack.cloud.security_group_rule_info: cloud: "{{ cloud }}" - id: "{{ rule.rule.id }}" - register: filter_by_rule + id: "{{ security_group_rule.rule.id }}" + register: security_group_rules - 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 + that: security_group_rules.security_group_rules | length > 0 - name: Create empty ICMP rule again openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: icmp remote_ip_prefix: 0.0.0.0/0 - register: rule + register: security_group_rule - name: Assert not changed assert: - that: rule is not changed + that: security_group_rule is not changed - name: Create -1 ICMP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: icmp port_range_min: -1 port_range_max: -1 remote_ip_prefix: 0.0.0.0/0 - register: rule + register: security_group_rule - name: Assert not changed assert: - that: rule is not changed + that: security_group_rule is not changed - name: Create -1 ICMP rule again openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: icmp port_range_min: -1 port_range_max: -1 remote_ip_prefix: 0.0.0.0/0 - register: rule + register: security_group_rule - name: Assert not changed assert: - that: rule is not changed + that: security_group_rule is not changed - name: Create empty TCP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: tcp remote_ip_prefix: 0.0.0.0/0 - register: rule + register: security_group_rule - name: Assert changed assert: - that: rule is changed + that: security_group_rule is changed - name: Create TCP rule again with port range (1, 65535) openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: tcp port_range_min: 1 port_range_max: 65535 remote_ip_prefix: 0.0.0.0/0 - register: rule + register: security_group_rule - name: Assert changed assert: - that: rule is not changed + that: security_group_rule is not changed - name: Create TCP rule again with port range (-1, -1) openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: tcp port_range_min: -1 port_range_max: -1 remote_ip_prefix: 0.0.0.0/0 - register: rule + register: security_group_rule - name: Assert changed assert: - that: rule is not changed + that: security_group_rule is not changed - name: Create TCP rule again with defined range openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: tcp port_range_min: 8000 port_range_max: 9000 remote_ip_prefix: 0.0.0.0/0 - register: rule + register: security_group_rule - name: Assert changed assert: - that: rule is changed + that: security_group_rule is changed - name: Create empty UDP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: udp remote_ip_prefix: 0.0.0.0/0 @@ -163,7 +159,7 @@ - name: Create UDP rule again with port range (1, 65535) openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: udp port_range_min: 1 @@ -173,7 +169,7 @@ - name: Create UDP rule again with port range (-1, -1) openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: udp port_range_min: -1 @@ -183,7 +179,7 @@ - name: Create HTTP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: tcp port_range_min: 80 @@ -193,7 +189,7 @@ - name: Create egress rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: present protocol: tcp port_range_min: 30000 @@ -204,41 +200,37 @@ - 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", '>=') - register: test_sec_rules + register: security_group_rules - name: Check - List all available rules of all security groups in a project assert: that: - - test_sec_rules.security_group_rules | length > 0 - when: sdk_version is version("0.32", '>=') + - security_group_rules.security_group_rules | length > 0 - name: List all available rules of a specific security group openstack.cloud.security_group_rule_info: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" - register: test_sec_rule1 + security_group: ansible_security_group + register: security_group_rules - name: Check - List all available rules of a specific security group assert: that: - - test_sec_rule1.security_group_rules | length > 0 + - security_group_rules.security_group_rules | length > 0 - name: List all available rules with filters openstack.cloud.security_group_rule_info: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group 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", '>=') - register: test_sec_rule - name: Delete empty ICMP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: absent protocol: icmp remote_ip_prefix: 0.0.0.0/0 @@ -246,7 +238,7 @@ - name: Delete -1 ICMP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: absent protocol: icmp port_range_min: -1 @@ -256,7 +248,7 @@ - name: Delete empty TCP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: absent protocol: tcp remote_ip_prefix: 0.0.0.0/0 @@ -264,7 +256,7 @@ - name: Delete empty UDP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: absent protocol: udp remote_ip_prefix: 0.0.0.0/0 @@ -272,7 +264,7 @@ - name: Delete HTTP rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: absent protocol: tcp port_range_min: 80 @@ -282,7 +274,7 @@ - name: Delete egress rule openstack.cloud.security_group_rule: cloud: "{{ cloud }}" - security_group: "{{ secgroup_name }}" + security_group: ansible_security_group state: absent protocol: tcp port_range_min: 30000 @@ -293,5 +285,5 @@ - name: Delete security group openstack.cloud.security_group: cloud: "{{ cloud }}" - name: "{{ secgroup_name }}" + name: ansible_security_group state: absent diff --git a/ci/roles/server/tasks/main.yml b/ci/roles/server/tasks/main.yml index 6ffde756..4ac6342e 100644 --- a/ci/roles/server/tasks/main.yml +++ b/ci/roles/server/tasks/main.yml @@ -469,8 +469,8 @@ - server_updated.server.metadata['key2'] == 'value2' - server_updated.server.metadata['key3'] == 'value3' - server_updated.server.security_groups|map(attribute='name')|unique|length == 2 - - security_group.secgroup.name in server_updated.server.security_groups|map(attribute='name') - - security_group_alt.secgroup.name in server_updated.server.security_groups|map(attribute='name') + - security_group.security_group.name in server_updated.server.security_groups|map(attribute='name') + - security_group_alt.security_group.name in server_updated.server.security_groups|map(attribute='name') - server_network in server_updated.server.addresses.keys()|list|sort - server_updated.server.addresses[server_network]|length == 2 - port.port.fixed_ips[0].ip_address in diff --git a/plugins/modules/security_group.py b/plugins/modules/security_group.py index 43732094..19be936f 100644 --- a/plugins/modules/security_group.py +++ b/plugins/modules/security_group.py @@ -5,61 +5,134 @@ # Copyright (c) 2013, Benno Joy # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: security_group -short_description: Add/Delete security groups from an OpenStack cloud. +short_description: Manage Neutron security groups of an OpenStack cloud. author: OpenStack Ansible SIG description: - - Add or Remove security groups from an OpenStack cloud. + - Add or remove Neutron security groups to/from an OpenStack cloud. options: - name: - description: - - Name that has to be given to the security group. This module - requires that security group names be unique. - required: true - type: str - description: - description: - - Long description of the purpose of the security group - type: str - default: '' - state: - description: - - Should the resource be present or absent. - choices: [present, absent] - default: present - type: str - project: - description: - - Unique name or ID of the project. - required: false - type: str + description: + description: + - Long description of the purpose of the security group. + type: str + name: + description: + - Name that has to be given to the security group. This module + requires that security group names be unique. + required: true + type: str + project: + description: + - Unique name or ID of the project. + type: str + state: + description: + - Should the resource be present or absent. + choices: [present, absent] + default: present + type: str requirements: - - "python >= 3.6" - - "openstacksdk" - + - "python >= 3.6" + - "openstacksdk" extends_documentation_fragment: -- openstack.cloud.openstack + - openstack.cloud.openstack ''' -EXAMPLES = ''' -# Create a security group -- openstack.cloud.security_group: +RETURN = r''' +security_group: + description: Dictionary describing the security group. + type: dict + returned: On success when I(state) is C(present). + contains: + created_at: + description: Creation time of the security group + type: str + sample: "yyyy-mm-dd hh:mm:ss" + description: + description: Description of the security group + type: str + sample: "My security group" + id: + description: ID of the security group + type: str + sample: "d90e55ba-23bd-4d97-b722-8cb6fb485d69" + name: + description: Name of the security group. + type: str + sample: "my-sg" + project_id: + description: Project ID where the security group is located in. + type: str + sample: "25d24fc8-d019-4a34-9fff-0a09fde6a567" + revision_number: + description: The revision number of the resource. + type: int + tenant_id: + description: Tenant ID where the security group is located in. Deprecated + type: str + sample: "25d24fc8-d019-4a34-9fff-0a09fde6a567" + security_group_rules: + description: Specifies the security group rule list + type: list + sample: [ + { + "id": "d90e55ba-23bd-4d97-b722-8cb6fb485d69", + "direction": "ingress", + "protocol": null, + "ethertype": "IPv4", + "description": null, + "remote_group_id": "0431c9c5-1660-42e0-8a00-134bec7f03e2", + "remote_ip_prefix": null, + "tenant_id": "bbfe8c41dd034a07bebd592bf03b4b0c", + "port_range_max": null, + "port_range_min": null, + "security_group_id": "0431c9c5-1660-42e0-8a00-134bec7f03e2" + }, + { + "id": "aecff4d4-9ce9-489c-86a3-803aedec65f7", + "direction": "egress", + "protocol": null, + "ethertype": "IPv4", + "description": null, + "remote_group_id": null, + "remote_ip_prefix": null, + "tenant_id": "bbfe8c41dd034a07bebd592bf03b4b0c", + "port_range_max": null, + "port_range_min": null, + "security_group_id": "0431c9c5-1660-42e0-8a00-134bec7f03e2" + } + ] + stateful: + description: Indicates if the security group is stateful or stateless. + type: bool + tags: + description: The list of tags on the resource. + type: list + updated_at: + description: Update time of the security group + type: str + sample: "yyyy-mm-dd hh:mm:ss" +''' + +EXAMPLES = r''' +- name: Create a security group + openstack.cloud.security_group: cloud: mordred state: present name: foo description: security group for foo servers -# Update the existing 'foo' security group description -- openstack.cloud.security_group: +- name: Update the existing 'foo' security group description + openstack.cloud.security_group: cloud: mordred state: present name: foo description: updated description for the foo security group -# Create a security group for a given project -- openstack.cloud.security_group: +- name: Create a security group for a given project + openstack.cloud.security_group: cloud: mordred state: present name: foo @@ -72,78 +145,119 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O class SecurityGroupModule(OpenStackModule): argument_spec = dict( + description=dict(), name=dict(required=True), - description=dict(default=''), - state=dict(default='present', choices=['absent', 'present']), project=dict(), + state=dict(default='present', choices=['absent', 'present']), ) - def _needs_update(self, secgroup): - """Check for differences in the updatable values. - - NOTE: We don't currently allow name updates. - """ - if secgroup['description'] != self.params['description']: - return True - return False - - def _system_state_change(self, secgroup): - state = self.params['state'] - if state == 'present': - if not secgroup: - return True - return self._needs_update(secgroup) - if state == 'absent' and secgroup: - return True - return False + module_kwargs = dict( + supports_check_mode=True, + ) def run(self): - - name = self.params['name'] state = self.params['state'] - description = self.params['description'] - project = self.params['project'] - if project is not None: - proj = self.conn.get_project(project) - if proj is None: - self.fail_json(msg='Project %s could not be found' % project) - project_id = proj['id'] - else: - project_id = self.conn.current_project_id - - if project_id: - filters = {'tenant_id': project_id} - else: - filters = None - - secgroup = self.conn.get_security_group(name, filters=filters) + security_group = self._find() if self.ansible.check_mode: - self.exit(changed=self._system_state_change(secgroup)) + self.exit_json(changed=self._will_change(state, security_group)) - changed = False - if state == 'present': - if not secgroup: - kwargs = {} - if project_id: - kwargs['project_id'] = project_id - secgroup = self.conn.create_security_group(name, description, - **kwargs) - changed = True - else: - if self._needs_update(secgroup): - secgroup = self.conn.update_security_group( - secgroup['id'], description=description) - changed = True - self.exit( - changed=changed, id=secgroup['id'], secgroup=secgroup) + if state == 'present' and not security_group: + # Create security_group + security_group = self._create() + self.exit_json( + changed=True, + security_group=security_group.to_dict(computed=False)) - if state == 'absent': - if secgroup: - self.conn.delete_security_group(secgroup['id']) - changed = True - self.exit(changed=changed) + elif state == 'present' and security_group: + # Update security_group + update = self._build_update(security_group) + if update: + security_group = self._update(security_group, update) + + self.exit_json( + changed=bool(update), + security_group=security_group.to_dict(computed=False)) + + elif state == 'absent' and security_group: + # Delete security_group + self._delete(security_group) + self.exit_json(changed=True) + + elif state == 'absent' and not security_group: + # Do nothing + self.exit_json(changed=False) + + def _build_update(self, security_group): + update = {} + + # module options name and project are used to find security group + # and thus cannot be updated + + non_updateable_keys = [k for k in [] + if self.params[k] is not None + and self.params[k] != security_group[k]] + + if non_updateable_keys: + self.fail_json(msg='Cannot update parameters {0}' + .format(non_updateable_keys)) + + attributes = dict((k, self.params[k]) + for k in ['description'] + if self.params[k] is not None + and self.params[k] != security_group[k]) + + if attributes: + update['attributes'] = attributes + + return update + + def _create(self): + kwargs = dict((k, self.params[k]) + for k in ['description', 'name'] + if self.params[k] is not None) + + project_name_or_id = self.params['project'] + if project_name_or_id is not None: + project = self.conn.identity.find_project( + name_or_id=project_name_or_id, ignore_missing=False) + kwargs['project_id'] = project.id + + return self.conn.network.create_security_group(**kwargs) + + def _delete(self, security_group): + self.conn.network.delete_security_group(security_group.id) + + def _find(self): + kwargs = dict(name_or_id=self.params['name']) + + project_name_or_id = self.params['project'] + if project_name_or_id is not None: + project = self.conn.identity.find_project( + name_or_id=project_name_or_id, ignore_missing=False) + kwargs['project_id'] = project.id + + return self.conn.network.find_security_group(**kwargs) + + def _update(self, security_group, update): + attributes = update.get('attributes') + if attributes: + security_group = self.conn.network.update_security_group( + security_group.id, **attributes) + + return security_group + + def _will_change(self, state, security_group): + if state == 'present' and not security_group: + return True + elif state == 'present' and security_group: + return bool(self._build_update(security_group)) + elif state == 'absent' and security_group: + return True + else: + # state == 'absent' and not security_group: + return False def main(): diff --git a/plugins/modules/security_group_info.py b/plugins/modules/security_group_info.py index d57db1b3..850b0b65 100644 --- a/plugins/modules/security_group_info.py +++ b/plugins/modules/security_group_info.py @@ -4,42 +4,32 @@ # Copyright (c) 2020 by 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 = ''' +DOCUMENTATION = r''' --- module: security_group_info short_description: Lists security groups -extends_documentation_fragment: openstack.cloud.openstack author: OpenStack Ansible SIG description: - List security groups options: + any_tags: + description: + - A list of tags to filter the list result by. + - Resources that match any tag in this list will be returned. + type: list + elements: str description: description: - - Description of the security group + - Description of the security group. type: str name: description: - Name or id of the security group. type: str - project_id: - description: - - Specifies the project id as filter criteria - type: str - revision_number: - description: - - Filter the list result by the revision number of the - - resource. - type: int - tags: + not_any_tags: description: - A list of tags to filter the list result by. - - Resources that match all tags in this list will be returned. - type: list - elements: str - any_tags: - description: - - A list of tags to filter the list result by. - - Resources that match any tag in this list will be returned. + - Resources that match any tag in this list will be excluded. type: list elements: str not_tags: @@ -48,22 +38,33 @@ options: - Resources that match all tags in this list will be excluded. type: list elements: str - not_any_tags: + project_id: + description: + - Specifies the project id as filter criteria. + type: str + revision_number: + description: + - Filter the list result by the revision number of the resource. + type: int + tags: description: - A list of tags to filter the list result by. - - Resources that match any tag in this list will be excluded. + - Resources that match all tags in this list will be returned. type: list elements: str - -requirements: ["openstacksdk"] +requirements: + - "python >= 3.6" + - "openstacksdk" +extends_documentation_fragment: + - openstack.cloud.openstack ''' -RETURN = ''' +RETURN = r''' security_groups: description: List of dictionaries describing security groups. type: list elements: dict - returned: On Success. + returned: always contains: created_at: description: Creation time of the security group @@ -135,32 +136,30 @@ security_groups: sample: "yyyy-mm-dd hh:mm:ss" ''' -EXAMPLES = ''' -# Get specific security group -- openstack.cloud.security_group_info: - cloud: "{{ cloud }}" - name: "{{ my_sg }}" - register: sg -# Get all security groups -- openstack.cloud.security_group_info: - cloud: "{{ cloud }}" - register: sg +EXAMPLES = r''' +- name: Get all security groups + openstack.cloud.security_group_info: + cloud: devstack + +- name: Get specific security group + openstack.cloud.security_group_info: + cloud: devstack + name: my_sg ''' -from ansible_collections.openstack.cloud.plugins.module_utils.openstack import ( - OpenStackModule) +from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule class SecurityGroupInfoModule(OpenStackModule): argument_spec = dict( + any_tags=dict(type='list', elements='str'), description=dict(), name=dict(), + not_any_tags=dict(type='list', elements='str'), + not_tags=dict(type='list', elements='str'), project_id=dict(), revision_number=dict(type='int'), tags=dict(type='list', elements='str'), - any_tags=dict(type='list', elements='str'), - not_tags=dict(type='list', elements='str'), - not_any_tags=dict(type='list', elements='str') ) module_kwargs = dict( supports_check_mode=True @@ -168,16 +167,13 @@ class SecurityGroupInfoModule(OpenStackModule): def run(self): name = self.params['name'] - args = { - k: self.params[k] - for k in ['description', 'project_id', 'revision_number'] - if self.params[k] - } - args.update({ - k: ','.join(self.params[k]) - for k in ['tags', 'any_tags', 'not_tags', 'not_any_tags'] - if self.params[k] - }) + args = {k: self.params[k] + for k in ['description', 'project_id', 'revision_number'] + if self.params[k]} + + args.update({k: ','.join(self.params[k]) + for k in ['tags', 'any_tags', 'not_tags', 'not_any_tags'] + if self.params[k]}) # self.conn.search_security_groups() cannot be used here, # refer to git blame for rationale. @@ -185,12 +181,14 @@ class SecurityGroupInfoModule(OpenStackModule): if name: # TODO: Upgrade name_or_id code to match openstacksdk [1]? - # [1] https://opendev.org/openstack/openstacksdk/src/commit/0898398415ae7b0e2447d61226acf50f01567cdd/openstack/cloud/_utils.py#L89 + # [1] https://opendev.org/openstack/openstacksdk/src/commit/ + # 0898398415ae7b0e2447d61226acf50f01567cdd/openstack/cloud/_utils.py#L89 security_groups = [item for item in security_groups if name in (item['id'], item['name'])] - security_groups = [item.to_dict() for item in security_groups] - self.exit(changed=False, security_groups=security_groups) + self.exit(changed=False, + security_groups=[sg.to_dict(computed=False) + for sg in security_groups]) def main(): diff --git a/plugins/modules/security_group_rule.py b/plugins/modules/security_group_rule.py index 574e63e9..3d542822 100644 --- a/plugins/modules/security_group_rule.py +++ b/plugins/modules/security_group_rule.py @@ -143,10 +143,6 @@ EXAMPLES = ''' ''' RETURN = ''' -id: - description: Unique rule UUID. - type: str - returned: state == present rule: description: Representation of the security group rule type: dict @@ -430,7 +426,7 @@ class SecurityGroupRuleModule(OpenStackModule): changed = True rule = rule.to_dict(computed=False) - self.exit_json(changed=changed, rule=rule, id=rule['id']) + self.exit_json(changed=changed, rule=rule) if state == 'absent' and rule: self.conn.network.delete_security_group_rule(rule['id'])