#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (c) 2021 by Uemit Seren # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) DOCUMENTATION = r''' --- module: subnet_pool short_description: Create, update or delete a subnet pool from OpenStack author: OpenStack Ansible SIG description: - Create, update or delete a subnet pool from OpenStack. options: address_scope: description: - ID or name of the address scope associated with this subnet pool. type: str default_prefix_length: description: - The prefix length to allocate when the cidr or prefixlen attributes are omitted when creating a subnet. type: int default_quota: description: - A per-project quota on the prefix space that can be allocated from the subnet pool for project subnets. type: int description: description: The subnet pool description. type: str extra_specs: description: - Dictionary with extra key/value pairs passed to the API. type: dict is_default: description: - Whether this subnet pool is the default. type: bool is_shared: description: - Whether this subnet pool is shared or not. - This attribute cannot be updated. type: bool aliases: ['shared'] maximum_prefix_length: description: - The maximum prefix length that can be allocated from the subnet pool. type: int minimum_prefix_length: description: - The minimum prefix length that can be allocated from the subnet pool. type: int name: description: - Name to be give to the subnet pool. - This attribute cannot be updated. required: true type: str prefixes: description: - Subnet pool prefixes in CIDR notation. type: list elements: str project: description: - Name or ID of the project. type: str state: description: - Whether the subnet pool should be C(present) or C(absent). choices: ['present', 'absent'] default: present type: str extends_documentation_fragment: - openstack.cloud.openstack ''' EXAMPLES = r''' - name: Create an subnet pool. openstack.cloud.subnet_pool: cloud: mycloud state: present name: my_subnet_pool prefixes: - 10.10.10.0/24 - name: Create a subnet pool for a given project. openstack.cloud.subnet_pool: cloud: mycloud state: present name: my_subnet_pool project: myproj prefixes: - 10.10.10.0/24 - name: Create a shared and default subnet pool in existing address scope openstack.cloud.subnet_pool: cloud: mycloud state: present name: my_subnet_pool address_scope: my_adress_scope is_default: True default_quota: 10 maximum_prefix_length: 32 minimum_prefix_length: 8 default_prefix_length: 24 is_shared: True prefixes: - 10.10.10.0/8 - name: Delete subnet poool. openstack.cloud.subnet_pool: cloud: mycloud state: absent name: my_subnet_pool ''' RETURN = r''' subnet_pool: description: Dictionary describing the subnet pool. returned: On success when I(state) is C(present). type: dict contains: address_scope_id: description: The address scope ID. type: str sample: "861174b82b43463c9edc5202aadc60ef" created_at: description: Timestamp when the subnet pool was created. type: str sample: "" default_prefix_length: description: The length of the prefix to allocate when the cidr or prefixlen attributes are omitted when creating a subnet. type: int sample: 32 default_quota: description: The per-project quota on the prefix space that can be allocated from the subnet pool for project subnets. type: int sample: 22 description: description: The subnet pool description. type: str sample: "My test subnet pool." id: description: Subnet Pool ID. type: str sample: "474acfe5-be34-494c-b339-50f06aa143e4" ip_version: description: The IP version of the subnet pool 4 or 6. type: int sample: 4 is_default: description: Indicates whether this is the default subnet pool. type: bool sample: false is_shared: description: Indicates whether this subnet pool is shared across all projects. type: bool sample: false maximum_prefix_length: description: The maximum prefix length that can be allocated from the subnet pool. type: int sample: 22 minimum_prefix_length: description: The minimum prefix length that can be allocated from the subnet pool. type: int sample: 8 name: description: Subnet Pool name. type: str sample: "my_subnet_pool" prefixes: description: A list of subnet prefixes that are assigned to the subnet pool. type: list sample: ['10.10.20.0/24', '10.20.10.0/24'] project_id: description: The ID of the project. type: str sample: "861174b82b43463c9edc5202aadc60ef" revision_number: description: Revision number of the subnet pool. type: int sample: 5 tags: description: A list of associated tags. returned: success type: list tenant_id: description: The ID of the project. Deprecated. type: str sample: "861174b82b43463c9edc5202aadc60ef" updated_at: description: Timestamp when the subnet pool was last updated. type: str sample: ''' from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule class SubnetPoolModule(OpenStackModule): argument_spec = dict( address_scope=dict(), default_prefix_length=dict(type='int'), default_quota=dict(type='int'), description=dict(), extra_specs=dict(type='dict'), is_default=dict(type='bool'), is_shared=dict(type='bool', aliases=['shared']), maximum_prefix_length=dict(type='int'), minimum_prefix_length=dict(type='int'), name=dict(required=True), prefixes=dict(type='list', elements='str'), project=dict(), state=dict(default='present', choices=['absent', 'present']), ) def run(self): state = self.params['state'] name = self.params['name'] subnet_pool = self.conn.network.find_subnet_pool(name) if self.ansible.check_mode: self.exit_json(changed=self._will_change(state, subnet_pool)) if state == 'present' and not subnet_pool: # Create subnet_pool subnet_pool = self._create() self.exit_json(changed=True, subnet_pool=subnet_pool.to_dict(computed=False)) elif state == 'present' and subnet_pool: # Update subnet_pool update = self._build_update(subnet_pool) if update: subnet_pool = self._update(subnet_pool, update) self.exit_json(changed=bool(update), subnet_pool=subnet_pool.to_dict(computed=False)) elif state == 'absent' and subnet_pool: # Delete subnet_pool self._delete(subnet_pool) self.exit_json(changed=True) elif state == 'absent' and not subnet_pool: # Do nothing self.exit_json(changed=False) def _build_update(self, subnet_pool): update = {} attributes = dict((k, self.params[k]) for k in ['default_prefix_length', 'default_quota', 'description', 'is_default', 'maximum_prefix_length', 'minimum_prefix_length'] if self.params[k] is not None and self.params[k] != subnet_pool[k]) for k in ['prefixes']: if self.params[k] is not None \ and set(self.params[k]) != set(subnet_pool[k]): attributes[k] = self.params[k] project_name_or_id = self.params['project'] if project_name_or_id is not None: project = self.conn.identity.find_project(project_name_or_id, ignore_missing=False) if subnet_pool['project_id'] != project.id: attributes['project_id'] = project.id address_scope_name_or_id = self.params['address_scope'] if address_scope_name_or_id is not None: address_scope = self.conn.network.find_address_scope( address_scope_name_or_id, ignore_missing=False) if subnet_pool['address_scope_id'] != address_scope.id: attributes['address_scope_id'] = address_scope.id extra_specs = self.params['extra_specs'] if extra_specs: duplicate_keys = set(attributes.keys()) & set(extra_specs.keys()) if duplicate_keys: raise ValueError('Duplicate key(s) in extra_specs: {0}' .format(', '.join(list(duplicate_keys)))) for k, v in extra_specs.items(): if v != subnet_pool[k]: attributes[k] = v if attributes: update['attributes'] = attributes return update def _create(self): kwargs = dict((k, self.params[k]) for k in ['default_prefix_length', 'default_quota', 'description', 'is_default', 'is_shared', 'maximum_prefix_length', 'minimum_prefix_length', 'name', 'prefixes'] 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(project_name_or_id, ignore_missing=False) kwargs['project_id'] = project.id address_scope_name_or_id = self.params['address_scope'] if address_scope_name_or_id is not None: address_scope = self.conn.network.find_address_scope( address_scope_name_or_id, ignore_missing=False) kwargs['address_scope_id'] = address_scope.id extra_specs = self.params['extra_specs'] if extra_specs: duplicate_keys = set(kwargs.keys()) & set(extra_specs.keys()) if duplicate_keys: raise ValueError('Duplicate key(s) in extra_specs: {0}' .format(', '.join(list(duplicate_keys)))) kwargs = dict(kwargs, **extra_specs) return self.conn.network.create_subnet_pool(**kwargs) def _delete(self, subnet_pool): self.conn.network.delete_subnet_pool(subnet_pool.id) def _update(self, subnet_pool, update): attributes = update.get('attributes') if attributes: subnet_pool = self.conn.network.update_subnet_pool(subnet_pool.id, **attributes) return subnet_pool def _will_change(self, state, subnet_pool): if state == 'present' and not subnet_pool: return True elif state == 'present' and subnet_pool: return bool(self._build_update(subnet_pool)) elif state == 'absent' and subnet_pool: return True else: # state == 'absent' and not subnet_pool: return False def main(): module = SubnetPoolModule() module() if __name__ == '__main__': main()