From 16a8a9e5d4444278ca5c81be091de55e30f4ad81 Mon Sep 17 00:00:00 2001 From: Jakob Meng Date: Fri, 13 Jan 2023 09:05:57 +0100 Subject: [PATCH] Refactored identity_domain{,_info} modules Change-Id: Idf48f10e66a5651fa4693774eecd2c8683703082 --- ci/roles/identity_domain/defaults/main.yml | 6 + ci/roles/identity_domain/tasks/main.yml | 123 ++++++++++ .../identity_domain_info/defaults/main.yml | 9 - ci/roles/identity_domain_info/tasks/main.yml | 72 ------ ci/roles/identity_group/tasks/main.yml | 10 +- ci/roles/keystone_domain/defaults/main.yml | 1 - ci/roles/keystone_domain/tasks/main.yml | 36 --- .../tasks/main.yml | 2 +- ci/roles/keystone_idp/tasks/main.yml | 6 +- ci/roles/user_role/tasks/main.yaml | 12 +- ci/run-collection.yml | 3 +- plugins/modules/identity_domain.py | 228 ++++++++---------- plugins/modules/identity_domain_info.py | 119 ++++----- 13 files changed, 292 insertions(+), 335 deletions(-) create mode 100644 ci/roles/identity_domain/defaults/main.yml create mode 100644 ci/roles/identity_domain/tasks/main.yml delete mode 100644 ci/roles/identity_domain_info/defaults/main.yml delete mode 100644 ci/roles/identity_domain_info/tasks/main.yml delete mode 100644 ci/roles/keystone_domain/defaults/main.yml delete mode 100644 ci/roles/keystone_domain/tasks/main.yml diff --git a/ci/roles/identity_domain/defaults/main.yml b/ci/roles/identity_domain/defaults/main.yml new file mode 100644 index 00000000..8c7bf551 --- /dev/null +++ b/ci/roles/identity_domain/defaults/main.yml @@ -0,0 +1,6 @@ +expected_fields: + - description + - id + - is_enabled + - name + - links diff --git a/ci/roles/identity_domain/tasks/main.yml b/ci/roles/identity_domain/tasks/main.yml new file mode 100644 index 00000000..0518c62b --- /dev/null +++ b/ci/roles/identity_domain/tasks/main.yml @@ -0,0 +1,123 @@ +--- +- name: Create keystone domain + openstack.cloud.identity_domain: + cloud: "{{ cloud }}" + state: present + name: ansible_domain + description: "test description" + register: domain + +- name: Assert return values of identity_domain module + assert: + that: + - domain.domain.name == 'ansible_domain' + - domain.domain.description == "test description" + # allow new fields to be introduced but prevent fields from being removed + - expected_fields|difference(domain.domain.keys())|length == 0 + +- name: Update keystone domain + openstack.cloud.identity_domain: + cloud: "{{ cloud }}" + name: ansible_domain + description: "updated description" + register: domain + +- name: Assert updated domain + assert: + that: + - domain.domain.description == "updated description" + +- name: Fetch domains + openstack.cloud.identity_domain_info: + cloud: "{{ cloud }}" + register: domains + +- name: Assert return values of identity_domain_info module + assert: + that: + - domains.domains | length > 0 + # allow new fields to be introduced but prevent fields from being removed + - expected_fields|difference(domains.domains.0.keys())|length == 0 + +- name: Fetch domain by name + openstack.cloud.identity_domain_info: + cloud: "{{ cloud }}" + name: ansible_domain + register: domains + +- name: Assert named domain + assert: + that: + - domains.domains | length == 1 + +- name: Create disabled domain + openstack.cloud.identity_domain: + cloud: "{{ cloud }}" + state: present + name: ansible_domain_disabled + is_enabled: false + description: "test description" + register: domain + +- name: Fetch all domains + openstack.cloud.identity_domain_info: + cloud: "{{ cloud }}" + register: domains + +- name: Assert both ansible domains exist + assert: + that: + - domains.domains | length >= 2 + +- name: Fetch disabled domains + openstack.cloud.identity_domain_info: + cloud: "{{ cloud }}" + filters: + is_enabled: false + register: domains + +- name: Assert at least one disabled domain exists + assert: + that: + - domains.domains | length >= 1 + +- name: Fetch enabled domains + openstack.cloud.identity_domain_info: + cloud: "{{ cloud }}" + filters: + is_enabled: true + register: domains + +- name: Assert returned value + assert: + that: + - item.is_enabled + loop: "{{ domains.domains }}" + +- name: Delete disabled domain + openstack.cloud.identity_domain: + cloud: "{{ cloud }}" + state: absent + name: ansible_domain_disabled + +- name: Assert domain is disabled + assert: + that: + - not domain.domain.is_enabled + +- name: Delete domain + openstack.cloud.identity_domain: + cloud: "{{ cloud }}" + state: absent + name: ansible_domain + +- name: Get non-existing domain + openstack.cloud.identity_domain_info: + cloud: "{{ cloud }}" + name: ansible_domain + register: domains + +- name: Assert no results returned + assert: + that: + - domains.domains | length == 0 diff --git a/ci/roles/identity_domain_info/defaults/main.yml b/ci/roles/identity_domain_info/defaults/main.yml deleted file mode 100644 index c67b3cbc..00000000 --- a/ci/roles/identity_domain_info/defaults/main.yml +++ /dev/null @@ -1,9 +0,0 @@ -domain_name: domain_info_test_domain -unexistent_domain_name: domain_info_unexistent_domain -disabled_domain_name: test_domain_disabled -domain_info_fields: - - description - - id - - is_enabled - - name - - links diff --git a/ci/roles/identity_domain_info/tasks/main.yml b/ci/roles/identity_domain_info/tasks/main.yml deleted file mode 100644 index 3bb5562b..00000000 --- a/ci/roles/identity_domain_info/tasks/main.yml +++ /dev/null @@ -1,72 +0,0 @@ ---- -- block: - - name: Ensure domain does not exist - openstack.cloud.identity_domain: - cloud: "{{ cloud }}" - state: absent - name: "{{ unexistent_domain_name }}" - - name: Get unexistent domain - openstack.cloud.identity_domain_info: - cloud: "{{ cloud }}" - name: "{{ unexistent_domain_name }}" - register: domain_info - - name: Assert no results returned - assert: - that: not domain_info.openstack_domains - - -- block: - - name: Ensure domain exists - openstack.cloud.identity_domain: - cloud: "{{ cloud }}" - state: present - name: "{{ domain_name }}" - description: "test description" - register: domain - - name: Get domain - openstack.cloud.identity_domain_info: - cloud: "{{ cloud }}" - name: "{{ domain_name }}" - register: domain_info - - name: Assert one result exists - assert: - that: domain_info.openstack_domains | length == 1 - - name: Assert fields are present - assert: - that: item in domain_info.openstack_domains[0] - loop: "{{ domain_info_fields }}" - - name: Assert returned value - assert: - that: - - domain_info.openstack_domains[0].description == domain.domain.description - -- block: - - name: Get all domains - openstack.cloud.identity_domain_info: - cloud: "{{ cloud }}" - register: domain_info - -- block: - - name: Ensure disabled domain exists - openstack.cloud.identity_domain: - cloud: "{{ cloud }}" - state: present - name: "{{ disabled_domain_name }}" - enabled: false - description: "test description" - register: domain - - name: Get filtered domains - openstack.cloud.identity_domain_info: - cloud: "{{ cloud }}" - filters: - enabled: true - register: domain_info - - name: Assert at least one result - assert: - that: domain_info.openstack_domains | length >= 1 - - name: Assert returned value - assert: - that: item.is_enabled == true - loop: "{{ domain_info.openstack_domains }}" - - diff --git a/ci/roles/identity_group/tasks/main.yml b/ci/roles/identity_group/tasks/main.yml index 0c19f438..0d2fd0c6 100644 --- a/ci/roles/identity_group/tasks/main.yml +++ b/ci/roles/identity_group/tasks/main.yml @@ -52,21 +52,21 @@ cloud: "{{ cloud }}" state: present name: "{{ group_name }}" - domain_id: "{{ domain.id }}" + domain_id: "{{ domain.domain.id }}" register: group_result - name: Assert results assert: that: - group_result is changed - - group_result.group.domain_id == domain.id + - group_result.group.domain_id == domain.domain.id - name: Create group in specific domain again openstack.cloud.identity_group: cloud: "{{ cloud }}" state: present name: "{{ group_name }}" - domain_id: "{{ domain.id }}" + domain_id: "{{ domain.domain.id }}" register: group_result - name: Assert not changed @@ -90,7 +90,7 @@ cloud: "{{ cloud }}" state: absent name: "{{ group_name }}" - domain_id: "{{ domain.id }}" + domain_id: "{{ domain.domain.id }}" register: group_result - name: Assert changed @@ -102,7 +102,7 @@ cloud: "{{ cloud }}" state: absent name: "{{ group_name }}" - domain_id: "{{ domain.id }}" + domain_id: "{{ domain.domain.id }}" register: group_result - name: Assert not changed diff --git a/ci/roles/keystone_domain/defaults/main.yml b/ci/roles/keystone_domain/defaults/main.yml deleted file mode 100644 index 049e7c37..00000000 --- a/ci/roles/keystone_domain/defaults/main.yml +++ /dev/null @@ -1 +0,0 @@ -domain_name: ansible_domain diff --git a/ci/roles/keystone_domain/tasks/main.yml b/ci/roles/keystone_domain/tasks/main.yml deleted file mode 100644 index 3d735737..00000000 --- a/ci/roles/keystone_domain/tasks/main.yml +++ /dev/null @@ -1,36 +0,0 @@ ---- -- name: Create keystone domain - openstack.cloud.identity_domain: - cloud: "{{ cloud }}" - state: present - name: "{{ domain_name }}" - description: "test description" - register: os_domain - -- name: Test output - assert: - that: - - "'domain' in os_domain" - - os_domain.domain.name == domain_name - - >- - ('enabled' in os_domain.domain.keys() and os_domain.domain['enabled']|bool) or - ('is_enabled' in os_domain.domain and os_domain.domain['is_enabled']|bool) - - os_domain.domain.description == "test description" - -- name: Update keystone domain - openstack.cloud.identity_domain: - cloud: "{{ cloud }}" - name: "{{ domain_name }}" - description: "updated description" - register: os_domain_updated - -- name: Test output - assert: - that: - - os_domain_updated.domain.description == "updated description" - -- name: Delete keystone domain - openstack.cloud.identity_domain: - cloud: "{{ cloud }}" - state: absent - name: "{{ domain_name }}" diff --git a/ci/roles/keystone_federation_protocol/tasks/main.yml b/ci/roles/keystone_federation_protocol/tasks/main.yml index c7eb21ce..87afc1b9 100644 --- a/ci/roles/keystone_federation_protocol/tasks/main.yml +++ b/ci/roles/keystone_federation_protocol/tasks/main.yml @@ -33,7 +33,7 @@ openstack.cloud.federation_idp: state: 'present' name: '{{ idp_name }}' - domain_id: '{{ create_domain.id }}' + domain_id: '{{ create_domain.domain.id }}' - name: 'Create test mapping (1)' openstack.cloud.federation_mapping: diff --git a/ci/roles/keystone_idp/tasks/main.yml b/ci/roles/keystone_idp/tasks/main.yml index 4c77f184..5486c5e3 100644 --- a/ci/roles/keystone_idp/tasks/main.yml +++ b/ci/roles/keystone_idp/tasks/main.yml @@ -22,13 +22,9 @@ openstack.cloud.identity_domain: name: '{{ domain_name }}' register: create_domain - - assert: - that: - - create_domain is successful - - '"id" in create_domain' - name: 'Store domain ID as fact' set_fact: - domain_id: '{{ create_domain.id }}' + domain_id: '{{ create_domain.domain.id }}' # We *should* have a blank slate to start with, but we also shouldn't # explode if I(state=absent) and the IDP doesn't exist diff --git a/ci/roles/user_role/tasks/main.yaml b/ci/roles/user_role/tasks/main.yaml index ef192997..fe340b15 100644 --- a/ci/roles/user_role/tasks/main.yaml +++ b/ci/roles/user_role/tasks/main.yaml @@ -17,7 +17,7 @@ cloud: "{{ cloud }}" state: present name: "{{ group_name }}" - domain_id: "{{ domain.id }}" + domain_id: "{{ domain.domain.id }}" - name: Create user in default domain openstack.cloud.identity_user: @@ -31,7 +31,7 @@ cloud: "{{ cloud }}" state: present name: "{{ user_name }}" - domain: "{{ domain.id }}" + domain: "{{ domain.domain.id }}" - name: Assign role to group in default domain openstack.cloud.role_assignment: @@ -45,7 +45,7 @@ cloud: "{{ cloud }}" role: "{{ keystone_role_name }}" group: "{{ group_name }}" - domain: "{{ domain.id }}" + domain: "{{ domain.domain.id }}" - name: Assign role to user in default domain openstack.cloud.role_assignment: @@ -59,7 +59,7 @@ cloud: "{{ cloud }}" role: "{{ keystone_role_name }}" user: "{{ user_name }}" - domain: "{{ domain.id }}" + domain: "{{ domain.domain.id }}" - name: Delete group in default domain openstack.cloud.identity_group: @@ -73,7 +73,7 @@ cloud: "{{ cloud }}" state: absent name: "{{ group_name }}" - domain_id: "{{ domain.id }}" + domain_id: "{{ domain.domain.id }}" - name: Delete user in default domain openstack.cloud.identity_user: @@ -87,7 +87,7 @@ cloud: "{{ cloud }}" state: absent name: "{{ user_name }}" - domain: "{{ domain.id }}" + domain: "{{ domain.domain.id }}" - name: Delete domain openstack.cloud.identity_domain: diff --git a/ci/run-collection.yml b/ci/run-collection.yml index 6d307727..32976eaa 100644 --- a/ci/run-collection.yml +++ b/ci/run-collection.yml @@ -18,7 +18,7 @@ - { role: federation_mapping, tags: federation_mapping } - { role: floating_ip, tags: floating_ip } - { role: host_aggregate, tags: host_aggregate } - - { role: identity_domain_info, tags: identity_domain_info } + - { role: identity_domain, tags: identity_domain } - { role: identity_group, tags: identity_group } - { role: identity_group_info, tags: identity_group_info } - { role: identity_user, tags: identity_user } @@ -27,7 +27,6 @@ - { role: identity_role_info, tags: identity_role_info } - { role: image, tags: image } - { role: keypair, tags: keypair } - - { role: keystone_domain, tags: keystone_domain } - role: keystone_idp tags: keystone_idp when: sdk_version is version(0.44, '>=') diff --git a/plugins/modules/identity_domain.py b/plugins/modules/identity_domain.py index 5151134e..df529ea0 100644 --- a/plugins/modules/identity_domain.py +++ b/plugins/modules/identity_domain.py @@ -4,93 +4,89 @@ # Copyright (c) 2015 Hewlett-Packard Development Company, L.P. # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: identity_domain -short_description: Manage OpenStack Identity Domains +short_description: Manage OpenStack identity (Keystone) domains author: OpenStack Ansible SIG description: - - Create, update, or delete OpenStack Identity domains. If a domain - with the supplied name already exists, it will be updated with the - new description and enabled attributes. + - Create, update or delete OpenStack identity (Keystone) domains. options: - name: - description: - - Name that has to be given to the instance - required: true - type: str - description: - description: - - Description of the domain - type: str - enabled: - description: - - Is the domain enabled - type: bool - default: 'yes' - state: - description: - - Should the resource be present or absent. - choices: [present, absent] - default: present - type: str -extends_documentation_fragment: -- openstack.cloud.openstack -''' - -EXAMPLES = ''' -# Create a domain -- openstack.cloud.identity_domain: - cloud: mycloud - state: present - name: demo - description: Demo Domain - -# Delete a domain -- openstack.cloud.identity_domain: - cloud: mycloud - state: absent - name: demo -''' - -RETURN = ''' -domain: - description: Dictionary describing the domain. - returned: On success when I(state) is 'present' - type: complex - contains: - id: - description: Domain ID. - type: str - sample: "474acfe5-be34-494c-b339-50f06aa143e4" - name: - description: Domain name. - type: str - sample: "demo" - description: - description: Domain description. - type: str - sample: "Demo Domain" - enabled: - description: Domain description. - type: bool - sample: True - -id: - description: The domain ID. - returned: On success when I(state) is 'present' + description: + description: + - Domain description. type: str - sample: "474acfe5-be34-494c-b339-50f06aa143e4" + is_enabled: + description: + - Whether this domain is enabled or not. + type: bool + aliases: ['enabled'] + name: + description: + - Domain name. + required: true + type: str + state: + description: + - Should the resource be present or absent. + choices: ['present', 'absent'] + default: present + type: str +extends_documentation_fragment: + - openstack.cloud.openstack +''' + +EXAMPLES = r''' +- name: Create a domain + openstack.cloud.identity_domain: + cloud: mycloud + state: present + name: demo + description: Demo Domain + +- name: Delete a domain + openstack.cloud.identity_domain: + cloud: mycloud + state: absent + name: demo +''' + +RETURN = r''' +domain: + description: Dictionary describing the domain. + returned: On success when I(state) is C(present). + type: dict + contains: + description: + description: Domain description. + type: str + sample: "Demo Domain" + id: + description: Domain ID. + type: str + sample: "474acfe5-be34-494c-b339-50f06aa143e4" + is_enabled: + description: Domain description. + type: bool + sample: True + links: + description: The links related to the domain resource + type: list + name: + description: Domain name. + type: str + sample: "demo" ''' from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule +from ansible_collections.openstack.cloud.plugins.module_utils.resource import StateMachine class IdentityDomainModule(OpenStackModule): argument_spec = dict( - name=dict(required=True), description=dict(), - enabled=dict(default=True, type='bool'), + is_enabled=dict(type='bool', aliases=['enabled']), + name=dict(required=True), state=dict(default='present', choices=['absent', 'present']), ) @@ -98,70 +94,38 @@ class IdentityDomainModule(OpenStackModule): supports_check_mode=True ) - def _needs_update(self, domain): - if self.params['description'] is not None and \ - domain.description != self.params['description']: - return True - if domain.get( - "is_enabled", domain.get("enabled")) != self.params['enabled']: - return True - return False - - def _system_state_change(self, domain): - state = self.params['state'] - if state == 'absent' and domain: - return True - - if state == 'present': - if domain is None: - return True - return self._needs_update(domain) - - return False + class _StateMachine(StateMachine): + def _delete(self, resource, attributes, timeout, wait, **kwargs): + # a domain must be disabled before it can be deleted and + # openstacksdk's cloud layer delete_domain() will just do that. + self.connection.delete_domain(resource['id']) def run(self): - name = self.params['name'] - description = self.params['description'] - enabled = self.params['enabled'] - state = self.params['state'] + sm = self._StateMachine(connection=self.conn, + service_name='identity', + type_name='domain', + sdk=self.sdk) - domains = list(self.conn.identity.domains(name=name)) + kwargs = dict((k, self.params[k]) + for k in ['state', 'timeout'] + if self.params[k] is not None) - if len(domains) > 1: - self.fail_json(msg='Domain name %s is not unique' % name) - elif len(domains) == 1: - domain = domains[0] + kwargs['attributes'] = \ + dict((k, self.params[k]) + for k in ['description', 'is_enabled', 'name'] + if self.params[k] is not None) + + domain, is_changed = sm(check_mode=self.ansible.check_mode, + updateable_attributes=None, + non_updateable_attributes=None, + wait=False, + **kwargs) + + if domain is None: + self.exit_json(changed=is_changed) else: - domain = None - - if self.ansible.check_mode: - self.exit_json(changed=self._system_state_change(domain)) - - if state == 'present': - if domain is None: - domain = self.conn.create_domain( - name=name, description=description, enabled=enabled) - changed = True - else: - if self._needs_update(domain): - domain = self.conn.update_domain( - domain.id, name=name, description=description, - enabled=enabled) - changed = True - else: - changed = False - if hasattr(domain, "to_dict"): - domain = domain.to_dict() - domain.pop("location") - self.exit_json(changed=changed, domain=domain, id=domain['id']) - - elif state == 'absent': - if domain is None: - changed = False - else: - self.conn.delete_domain(domain.id) - changed = True - self.exit_json(changed=changed) + self.exit_json(changed=is_changed, + domain=domain.to_dict(computed=False)) def main(): diff --git a/plugins/modules/identity_domain_info.py b/plugins/modules/identity_domain_info.py index 079ff0b1..db74fd34 100644 --- a/plugins/modules/identity_domain_info.py +++ b/plugins/modules/identity_domain_info.py @@ -4,82 +4,67 @@ # Copyright (c) 2016 Hewlett-Packard Enterprise Corporation # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) -DOCUMENTATION = ''' +DOCUMENTATION = r''' --- module: identity_domain_info -short_description: Retrieve information about one or more OpenStack domains +short_description: Fetch identity (Keystone) domains from OpenStack cloud author: OpenStack Ansible SIG description: - - Retrieve information about a one or more OpenStack domains + - Fetch identity (Keystone) domains from OpenStack cloud options: - name: - description: - - Name or ID of the domain - type: str - filters: - description: - - A dictionary of meta data to use for filtering. Elements of - this dictionary may be additional dictionaries. - type: dict + filters: + description: + - A dictionary of meta data to use for filtering. + - Elements of this dictionary may be additional dictionaries. + type: dict + name: + description: + - Name or ID of the domain + type: str extends_documentation_fragment: -- openstack.cloud.openstack + - openstack.cloud.openstack ''' -EXAMPLES = ''' -# Gather information about previously created domain -- openstack.cloud.identity_domain_info: +EXAMPLES = r''' +- name: Gather information about previously created domain + openstack.cloud.identity_domain_info: cloud: awesomecloud - register: result -- debug: - msg: "{{ result.openstack_domains }}" -# Gather information about a previously created domain by name -- openstack.cloud.identity_domain_info: +- name: Gather information about a previously created domain by name + openstack.cloud.identity_domain_info: cloud: awesomecloud name: demodomain - register: result -- debug: - msg: "{{ result.openstack_domains }}" -# Gather information about a previously created domain with filter -- openstack.cloud.identity_domain_info: +- name: Gather information about a previously created domain with filter + openstack.cloud.identity_domain_info: cloud: awesomecloud name: demodomain filters: - enabled: false - register: result -- debug: - msg: "{{ result.openstack_domains }}" + is_enabled: false ''' - -RETURN = ''' -openstack_domains: - description: has all the OpenStack information about domains - returned: always, but can be null - type: list - elements: dict - contains: - id: - description: Unique UUID. - returned: success - type: str - name: - description: Name given to the domain. - returned: success - type: str - description: - description: Description of the domain. - returned: success - type: str - is_enabled: - description: Flag to indicate if the domain is enabled. - returned: success - type: bool - links: - type: list - returned: success - description: The links related to the domain resource +RETURN = r''' +domains: + description: List of dictionaries describing OpenStack domains + returned: always + type: list + elements: dict + contains: + description: + description: Description of the domain. + type: str + id: + description: Unique UUID. + type: str + is_enabled: + description: Flag to indicate if the domain is enabled. + type: bool + links: + description: The links related to the domain resource + type: list + name: + description: Name given to the domain. + type: str ''' from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule @@ -87,8 +72,8 @@ from ansible_collections.openstack.cloud.plugins.module_utils.openstack import O class IdentityDomainInfoModule(OpenStackModule): argument_spec = dict( - name=dict(), filters=dict(type='dict'), + name=dict(), ) module_kwargs = dict( @@ -96,16 +81,18 @@ class IdentityDomainInfoModule(OpenStackModule): ) def run(self): + kwargs = {} name = self.params['name'] - filters = self.params['filters'] or {} + if name is not None: + kwargs['name_or_id'] = name - args = {} - if name: - args['name_or_id'] = name - args['filters'] = filters + filters = self.params['filters'] + if filters is not None: + kwargs['filters'] = filters - domains = [d.to_dict(computed=False) for d in self.conn.search_domains(**args)] - self.exit_json(changed=False, openstack_domains=domains) + self.exit_json(changed=False, + domains=[d.to_dict(computed=False) + for d in self.conn.search_domains(**kwargs)]) def main():