diff --git a/.zuul.yaml b/.zuul.yaml index 48198469..cb54cbb3 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -70,6 +70,7 @@ dns_zone_info floating_ip_info group + host_aggregate identity_domain_info identity_group_info identity_role diff --git a/ci/roles/host_aggregate/defaults/main.yml b/ci/roles/host_aggregate/defaults/main.yml new file mode 100644 index 00000000..e818a836 --- /dev/null +++ b/ci/roles/host_aggregate/defaults/main.yml @@ -0,0 +1,11 @@ +expected_fields: + - availability_zone + - created_at + - deleted_at + - hosts + - id + - is_deleted + - metadata + - name + - updated_at + - uuid diff --git a/ci/roles/host_aggregate/tasks/main.yml b/ci/roles/host_aggregate/tasks/main.yml new file mode 100644 index 00000000..4426b761 --- /dev/null +++ b/ci/roles/host_aggregate/tasks/main.yml @@ -0,0 +1,99 @@ +--- +- name: ensure aggregate doesn't exist before tests + openstack.cloud.host_aggregate: + cloud: "{{ cloud }}" + state: absent + name: test_aggregate + register: aggregate + +- block: + - name: create aggregate + openstack.cloud.host_aggregate: + cloud: "{{ cloud }}" + state: present + name: test_aggregate + hosts: + - "{{ ansible_hostname }}" + register: aggregate + + - name: assert aggregate is changed + assert: + that: aggregate is changed + + - name: assert aggregate fields + assert: + that: item in aggregate.aggregate + loop: "{{ expected_fields }}" + +- block: + - name: recreate aggregate + openstack.cloud.host_aggregate: + cloud: "{{ cloud }}" + state: present + name: test_aggregate + hosts: + - "{{ ansible_hostname }}" + register: aggregate + + - name: assert aggregate is not changed + assert: + that: aggregate is not changed + + - name: assert aggregate fields + assert: + that: item in aggregate.aggregate + loop: "{{ expected_fields }}" + +- block: + - name: update aggregate + openstack.cloud.host_aggregate: + cloud: "{{ cloud }}" + state: present + name: test_aggregate + metadata: + ssd: "true" + hosts: + - "{{ ansible_hostname }}" + register: aggregate + + - name: assert aggregate is changed + assert: + that: aggregate is changed + + - name: assert aggregate fields + assert: + that: item in aggregate.aggregate + loop: "{{ expected_fields }}" + +- block: + - name: purge hosts + openstack.cloud.host_aggregate: + cloud: "{{ cloud }}" + state: present + name: test_aggregate + hosts: [] + purge_hosts: true + register: aggregate + + - name: assert hosts were purged + assert: + that: + - aggregate is changed + - aggregate.aggregate.hosts | length == 0 + + - name: assert aggregate fields + assert: + that: item in aggregate.aggregate + loop: "{{ expected_fields }}" + +- block: + - name: delete aggregate + openstack.cloud.host_aggregate: + cloud: "{{ cloud }}" + state: absent + name: test_aggregate + register: aggregate + + - name: assert aggregate is changed + assert: + that: aggregate is changed diff --git a/ci/run-collection.yml b/ci/run-collection.yml index 0413677b..7737d8bf 100644 --- a/ci/run-collection.yml +++ b/ci/run-collection.yml @@ -15,6 +15,7 @@ - role: dns tags: dns when: sdk_version is version(0.28, '>=') + - { role: host_aggregate, tags: host_aggregate } - { role: floating_ip_info, tags: floating_ip_info } - { role: identity_domain_info, tags: identity_domain_info } - { role: identity_group_info, tags: identity_group_info } diff --git a/plugins/modules/host_aggregate.py b/plugins/modules/host_aggregate.py index 421b37d6..6ecd9e92 100644 --- a/plugins/modules/host_aggregate.py +++ b/plugins/modules/host_aggregate.py @@ -73,8 +73,54 @@ EXAMPLES = ''' name: db_aggregate ''' -RETURN = ''' - +RETURN = r''' +aggregate: + description: A host aggregate resource. + type: dict + returned: On success, when I(state) is present + contains: + availability_zone: + description: Availability zone of the aggregate + type: str + returned: always + created_at: + description: The date and time when the resource was created + type: str + returned: always + deleted_at: + description: + - The date and time when the resource was deleted + - Null unless I(is_deleted) is true + type: str + returned: always + hosts: + description: Hosts belonging to the aggregate + type: str + returned: always + id: + description: The UUID of the aggregate. + type: str + returned: always + is_deleted: + description: Whether or not the resource is deleted + type: bool + returned: always + metadata: + description: Metadata attached to the aggregate + type: str + returned: always + name: + description: Name of the aggregate + type: str + returned: always + updated_at: + description: The date and time when the resource was updated + type: str + returned: always + uuid: + description: UUID of the aggregate + type: str + returned: always ''' from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule @@ -95,13 +141,11 @@ class ComputeHostAggregateModule(OpenStackModule): ) def _needs_update(self, aggregate): - new_metadata = (self.params['metadata'] or {}) + new_metadata = self.params['metadata'] or {} if self.params['availability_zone'] is not None: new_metadata['availability_zone'] = self.params['availability_zone'] - if self.params['name'] != aggregate.name: - return True if self.params['hosts'] is not None: if self.params['purge_hosts']: if set(self.params['hosts']) != set(aggregate.hosts): @@ -110,11 +154,10 @@ class ComputeHostAggregateModule(OpenStackModule): intersection = set(self.params['hosts']).intersection(set(aggregate.hosts)) if set(self.params['hosts']) != intersection: return True - if self.params['availability_zone'] is not None: - if self.params['availability_zone'] != aggregate.availability_zone: - return True - if self.params['metadata'] is not None: - if new_metadata != aggregate.metadata: + + for param in ('availability_zone', 'metadata'): + if self.params[param] is not None and \ + self.params[param] != aggregate[param]: return True return False @@ -135,16 +178,16 @@ class ComputeHostAggregateModule(OpenStackModule): if hosts is None: return - hosts_to_add = set(hosts) - set(aggregate.get("hosts") or []) - for i in hosts_to_add: - self.conn.add_host_to_aggregate(aggregate.id, i) + hosts_to_add = set(hosts) - set(aggregate['hosts'] or []) + for host in hosts_to_add: + self.conn.compute.add_host_to_aggregate(aggregate.id, host) if not purge_hosts: return - hosts_to_remove = set(aggregate.get("hosts") or []) - set(hosts) - for i in hosts_to_remove: - self.conn.remove_host_from_aggregate(aggregate.id, i) + hosts_to_remove = set(aggregate["hosts"] or []) - set(hosts) + for host in hosts_to_remove: + self.conn.compute.remove_host_from_aggregate(aggregate.id, host) def run(self): name = self.params['name'] @@ -157,52 +200,43 @@ class ComputeHostAggregateModule(OpenStackModule): if metadata is not None: metadata.pop('availability_zone', None) - aggregates = self.conn.search_aggregates(name_or_id=name) - - if len(aggregates) == 1: - aggregate = aggregates[0] - elif len(aggregates) == 0: - aggregate = None - else: - raise Exception("Should not happen") + aggregate = self.conn.compute.find_aggregate(name) if self.ansible.check_mode: self.exit_json(changed=self._system_state_change(aggregate)) + changed = False if state == 'present': if aggregate is None: - aggregate = self.conn.create_aggregate( + aggregate = self.conn.compute.create_aggregate( name=name, availability_zone=availability_zone) self._update_hosts(aggregate, hosts, False) if metadata: - self.conn.set_aggregate_metadata(aggregate.id, metadata) + self.conn.compute.set_aggregate_metadata(aggregate, metadata) changed = True - else: - if self._needs_update(aggregate): - if availability_zone is not None: - aggregate = self.conn.update_aggregate( - aggregate.id, name=name, - availability_zone=availability_zone) - if metadata is not None: - metas = metadata - for i in (set(aggregate.metadata.keys()) - set(metadata.keys())): - if i != 'availability_zone': - metas[i] = None - self.conn.set_aggregate_metadata(aggregate.id, metas) - self._update_hosts(aggregate, hosts, purge_hosts) - changed = True - else: - changed = False - self.exit_json(changed=changed) + elif self._needs_update(aggregate): + if availability_zone is not None: + aggregate = self.conn.compute.update_aggregate( + aggregate, name=name, + availability_zone=availability_zone) + if metadata is not None: + metas = metadata + for i in set(aggregate.metadata.keys() - set(metadata.keys())): + if i != 'availability_zone': + metas[i] = None + self.conn.compute.set_aggregate_metadata(aggregate, metas) + self._update_hosts(aggregate, hosts, purge_hosts) + changed = True + aggregate = self.conn.compute.find_aggregate(name) + if aggregate: + aggregate = aggregate.to_dict(computed=False) + self.exit_json(changed=changed, aggregate=aggregate) - elif state == 'absent': - if aggregate is None: - changed = False - else: - self._update_hosts(aggregate, [], True) - self.conn.delete_aggregate(aggregate.id) - changed = True - self.exit_json(changed=changed) + elif state == 'absent' and aggregate is not None: + self._update_hosts(aggregate, [], True) + self.conn.compute.delete_aggregate(aggregate.id) + changed = True + self.exit_json(changed=changed) def main():