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

501 lines
17 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2016 Pason System Corporation
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = '''
---
module: quota
short_description: Manage OpenStack Quotas
author: OpenStack Ansible SIG
description:
- Manage OpenStack Quotas. Quotas can be created,
updated or deleted using this module. A quota will be updated
if matches an existing project and is present.
options:
backup_gigabytes:
description: Maximum size of backups in GB's.
type: int
backups:
description: Maximum number of backups allowed.
type: int
cores:
description: Maximum number of CPU's per project.
type: int
fixed_ips:
description:
- Number of fixed IP's to allow.
- Available until Nova API version 2.35.
type: int
floating_ips:
description: Number of floating IP's to allow.
aliases: [compute_floating_ips, floatingip, network_floating_ips]
type: int
gigabytes:
description: Maximum volume storage allowed for project.
type: int
groups:
description: Number of groups that are allowed for the project
type: int
injected_file_content_bytes:
description:
- Maximum file size in bytes.
- Available until Nova API version 2.56.
type: int
aliases: [injected_file_size]
injected_files:
description:
- Number of injected files to allow.
- Available until Nova API version 2.56.
type: int
injected_file_path_bytes:
description:
- Maximum path size.
- Available until Nova API version 2.56.
type: int
aliases: [injected_path_size]
instances:
description: Maximum number of instances allowed.
type: int
key_pairs:
description: Number of key pairs to allow.
type: int
load_balancers:
description: The maximum amount of load balancers you can create
type: int
aliases: [loadbalancer]
metadata_items:
description: Number of metadata items allowed per instance.
type: int
name:
description: Name of the OpenStack Project to manage.
required: true
type: str
networks:
description: Number of networks to allow.
type: int
aliases: [network]
per_volume_gigabytes:
description: Maximum size in GB's of individual volumes.
type: int
pools:
description: The maximum number of pools you can create
type: int
aliases: [pool]
ports:
description: Number of Network ports to allow, this needs to be greater
than the instances limit.
type: int
aliases: [port]
ram:
description: Maximum amount of ram in MB to allow.
type: int
rbac_policies:
description: Number of policies to allow.
type: int
aliases: [rbac_policy]
routers:
description: Number of routers to allow.
type: int
aliases: [router]
security_group_rules:
description: Number of rules per security group to allow.
type: int
aliases: [security_group_rule]
security_groups:
description: Number of security groups to allow.
type: int
aliases: [security_group]
server_group_members:
description: Number of server group members to allow.
type: int
server_groups:
description: Number of server groups to allow.
type: int
snapshots:
description: Number of snapshots to allow.
type: int
state:
description: A value of C(present) sets the quota and a value of
C(absent) resets the quota to defaults.
default: present
type: str
choices: [absent, present]
subnets:
description: Number of subnets to allow.
type: int
aliases: [subnet]
subnet_pools:
description: Number of subnet pools to allow.
type: int
aliases: [subnetpool]
volumes:
description: Number of volumes to allow.
type: int
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = '''
- name: Fetch current project quota
openstack.cloud.quota:
cloud: mycloud
name: demoproject
- name: Reset project quota back to defaults
openstack.cloud.quota:
cloud: mycloud
name: demoproject
state: absent
- name: Change number of cores and volumes
openstack.cloud.quota:
cloud: mycloud
name: demoproject
cores: 100
volumes: 20
- name: Update quota again
openstack.cloud.quota:
cloud: mycloud
name: demo_project
floating_ips: 5
networks: 50
ports: 300
rbac_policies: 5
routers: 5
subnets: 5
subnet_pools: 5
security_group_rules: 5
security_groups: 5
backup_gigabytes: 500
backups: 5
gigabytes: 500
groups: 1
pools: 5
per_volume_gigabytes: 10
snapshots: 5
volumes: 5
cores: 5
instances: 5
key_pairs: 5
metadata_items: 5
ram: 5
server_groups: 5
server_group_members: 5
'''
RETURN = '''
quotas:
description: Dictionary describing the project quota.
returned: Regardless if changes where made or not
type: dict
contains:
compute:
description: Compute service quotas
type: dict
contains:
cores:
description: Maximum number of CPU's per project.
type: int
injected_file_content_bytes:
description: Maximum file size in bytes.
type: int
injected_files:
description: Number of injected files to allow.
type: int
injected_file_path_bytes:
description: Maximum path size.
type: int
instances:
description: Maximum number of instances allowed.
type: int
key_pairs:
description: Number of key pairs to allow.
type: int
metadata_items:
description: Number of metadata items allowed per instance.
type: int
ram:
description: Maximum amount of ram in MB to allow.
type: int
server_group_members:
description: Number of server group members to allow.
type: int
server_groups:
description: Number of server groups to allow.
type: int
network:
description: Network service quotas
type: dict
contains:
floating_ips:
description: Number of floating IP's to allow.
type: int
load_balancers:
description: The maximum amount of load balancers one can
create
type: int
networks:
description: Number of networks to allow.
type: int
pools:
description: The maximum amount of pools one can create.
type: int
ports:
description: Number of Network ports to allow, this needs
to be greater than the instances limit.
type: int
rbac_policies:
description: Number of policies to allow.
type: int
routers:
description: Number of routers to allow.
type: int
security_group_rules:
description: Number of rules per security group to allow.
type: int
security_groups:
description: Number of security groups to allow.
type: int
subnet_pools:
description: Number of subnet pools to allow.
type: int
subnets:
description: Number of subnets to allow.
type: int
volume:
description: Block storage service quotas
type: dict
contains:
backup_gigabytes:
description: Maximum size of backups in GB's.
type: int
backups:
description: Maximum number of backups allowed.
type: int
gigabytes:
description: Maximum volume storage allowed for project.
type: int
groups:
description: Number of groups that are allowed for the
project
type: int
per_volume_gigabytes:
description: Maximum size in GB's of individual volumes.
type: int
snapshots:
description: Number of snapshots to allow.
type: int
volumes:
description: Number of volumes to allow.
type: int
sample:
quotas:
compute:
cores: 150,
fixed_ips: -1,
floating_ips: 10,
injected_file_content_bytes: 10240,
injected_file_path_bytes: 255,
injected_files: 5,
instances: 100,
key_pairs: 100,
metadata_items: 128,
networks: -1,
ram: 153600,
security_group_rules: -1,
security_groups: -1,
server_group_members: 10,
server_groups: 10,
network:
floating_ips: 50,
load_balancers: 10,
networks: 10,
pools: 10,
ports: 160,
rbac_policies: 10,
routers: 10,
security_group_rules: 100,
security_groups: 10,
subnet_pools: -1,
subnets: 10,
volume:
backup_gigabytes: 1000,
backups: 10,
gigabytes: 1000,
groups: 10,
per_volume_gigabytes: -1,
snapshots: 10,
volumes: 10,
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
from collections import defaultdict
class QuotaModule(OpenStackModule):
# TODO: Add missing network quota options 'check_limit', 'health_monitors',
# 'l7_policies', 'listeners' to argument_spec, DOCUMENTATION and
# RETURN docstrings
argument_spec = dict(
backup_gigabytes=dict(type='int'),
backups=dict(type='int'),
cores=dict(type='int'),
fixed_ips=dict(type='int'),
floating_ips=dict(
type='int', aliases=['floatingip', 'compute_floating_ips',
'network_floating_ips']),
gigabytes=dict(type='int'),
groups=dict(type='int'),
injected_file_content_bytes=dict(type='int',
aliases=['injected_file_size']),
injected_file_path_bytes=dict(type='int',
aliases=['injected_path_size']),
injected_files=dict(type='int'),
instances=dict(type='int'),
key_pairs=dict(type='int', no_log=False),
load_balancers=dict(type='int', aliases=['loadbalancer']),
metadata_items=dict(type='int'),
name=dict(required=True),
networks=dict(type='int', aliases=['network']),
per_volume_gigabytes=dict(type='int'),
pools=dict(type='int', aliases=['pool']),
ports=dict(type='int', aliases=['port']),
ram=dict(type='int'),
rbac_policies=dict(type='int', aliases=['rbac_policy']),
routers=dict(type='int', aliases=['router']),
security_group_rules=dict(type='int', aliases=['security_group_rule']),
security_groups=dict(type='int', aliases=['security_group']),
server_group_members=dict(type='int'),
server_groups=dict(type='int'),
snapshots=dict(type='int'),
state=dict(default='present', choices=['absent', 'present']),
subnet_pools=dict(type='int', aliases=['subnetpool']),
subnets=dict(type='int', aliases=['subnet']),
volumes=dict(type='int'),
)
module_kwargs = dict(
supports_check_mode=True
)
# Some attributes in quota resources don't exist in the api anymore, mostly
# compute quotas that were simply network proxies. This map allows marking
# them to be skipped.
exclusion_map = {
'compute': {
# 'fixed_ips', # Available until Nova API version 2.35
'floating_ips', # Available until Nova API version 2.35
'name',
'networks', # Available until Nova API version 2.35
'security_group_rules', # Available until Nova API version 2.35
'security_groups', # Available until Nova API version 2.35
# 'injected_file_content_bytes', # Available until
# 'injected_file_path_bytes', # Nova API
# 'injected_files', # version 2.56
},
'network': {'name'},
'volume': {'name'},
}
def _get_quotas(self, project):
quota = {}
if self.conn.has_service('block-storage'):
quota['volume'] = self.conn.block_storage.get_quota_set(project)
else:
self.warn('Block storage service aka volume service is not'
' supported by your cloud. Ignoring volume quotas.')
if self.conn.has_service('network'):
quota['network'] = self.conn.network.get_quota(project.id)
else:
self.warn('Network service is not supported by your cloud.'
' Ignoring network quotas.')
quota['compute'] = self.conn.compute.get_quota_set(project.id)
return quota
def _build_update(self, quotas):
changes = defaultdict(dict)
for quota_type in quotas.keys():
exclusions = self.exclusion_map[quota_type]
for attr in quotas[quota_type].keys():
if attr in exclusions:
continue
if (attr in self.params and self.params[attr] is not None
and quotas[quota_type][attr] != self.params[attr]):
changes[quota_type][attr] = self.params[attr]
return changes
def _system_state_change(self, project_quota_output):
"""
Determine if changes are required to the current project quota.
This is done by comparing the current project_quota_output against
the desired quota settings set on the module params.
"""
if self.params['state'] == 'absent':
return True
return bool(self._build_update(project_quota_output))
def run(self):
project = self.conn.identity.find_project(
self.params['name'], ignore_missing=False)
# Get current quota values
quotas = self._get_quotas(project)
changed = False
if self.ansible.check_mode:
self.exit_json(changed=self._system_state_change(quotas))
if self.params['state'] == 'absent':
# If a quota state is set to absent we should assume there will be
# changes. The default quota values are not accessible so we can
# not determine if no changes will occur or not.
changed = True
self.conn.compute.revert_quota_set(project)
if 'network' in quotas:
self.conn.network.delete_quota(project.id)
if 'volume' in quotas:
self.conn.block_storage.revert_quota_set(project)
# Necessary since we can't tell what the default quotas are
quotas = self._get_quotas(project)
elif self.params['state'] == 'present':
changes = self._build_update(quotas)
if changes:
if 'volume' in changes:
self.conn.block_storage.update_quota_set(
quotas['volume'], **changes['volume'])
if 'compute' in changes:
self.conn.compute.update_quota_set(
quotas['compute'], **changes['compute'])
if 'network' in changes:
quotas['network'] = self.conn.network.update_quota(
project.id, **changes['network'])
changed = True
quotas = {k: v.to_dict(computed=False) for k, v in quotas.items()}
self.exit_json(changed=changed, quotas=quotas)
def main():
module = QuotaModule()
module()
if __name__ == '__main__':
main()