368 lines
12 KiB
Python
368 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
|
|
requirements:
|
|
- "python >= 3.6"
|
|
- "openstacksdk"
|
|
|
|
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()
|