Jakob Meng 1b38b7c500 Properly documented openstacksdk version requirements
With "extends_documentation_fragment: ['openstack.cloud.openstack']"
it is not necessary to list required Python libraries in section
'requirements' of DOCUMENTATION docstring in modules. Ansible will
merge requirements from doc fragments and DOCUMENTATION docstring
which previously resulted in duplicates such as in server module [0]:

* openstacksdk
* openstacksdk >= 0.36, < 0.99.0
* python >= 3.6

When removing the 'requirements' section from server module, then
Ansible will list openstacksdk once only:

* openstacksdk >= 0.36, < 0.99.0
* python >= 3.6

To see what documentation Ansible will produce for server module run:

  ansible-doc --type module openstack.cloud.server

[0] https://docs.ansible.com/ansible/latest/collections/openstack/\
    cloud/server_module.html

Change-Id: I727ed95ee480bb644b5a533f6a9526973677064c
2023-01-16 13:51:01 +01:00

364 lines
12 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2021 by Uemit Seren <uemit.seren@gmail.com>
# 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()