ansible-collections-openstack/plugins/modules/neutron_rbac_policy.py
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

294 lines
9.3 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: Ansible Project
# (c) 2021, Ashraf Hasson <ahasson@redhat.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
DOCUMENTATION = r'''
---
module: neutron_rbac_policy
short_description: Create or delete a Neutron RBAC policy.
author: OpenStack Ansible SIG
description:
- Create, update or delete a policy to apply a RBAC rule against a network,
security group or QoS Policy.
options:
action:
description:
- Action for the RBAC policy.
- Can be either of the following options C(access_as_shared) or
C(access_as_external).
- Cannot be changed when updating an existing policy.
- Required when creating a RBAC policy rule, ignored when deleting a
policy.
choices: ['access_as_shared', 'access_as_external']
type: str
id:
description:
- The RBAC policy ID.
- Required when deleting or updating an existing RBAC policy rule,
ignored otherwise.
- If a I(id) was provided but a policy with this ID cannot be found,
an error will be raised.
type: str
aliases: ['policy_id']
object_id:
description:
- The object ID (the subject of the policy) to which the RBAC rule
applies.
- Cannot be changed when updating an existing policy.
- Required when creating a RBAC policy rule, ignored when deleting a
policy.
type: str
object_type:
description:
- Type of the object that this RBAC policy affects.
- Can be one of the following object types C(network), C(security_group)
or C(qos_policy).
- Cannot be changed when updating an existing policy.
- Required when creating a RBAC policy rule, ignored when deleting a
policy.
choices: ['network', 'security_group', 'qos_policy']
type: str
project_id:
description:
- The ID of the project to which C(object_id) belongs to.
- Cannot be changed when updating an existing policy.
- Required when creating a RBAC policy rule, ignored when deleting a
policy.
type: str
target_project_id:
description:
- The ID of the project to which access to be allowed or revoked aka
disallowed.
- Required when creating or updating a RBAC policy rule, ignored when
deleting a policy.
type: str
state:
description:
- Whether the RBAC rule should be C(present) or C(absent).
choices: ['present', 'absent']
default: present
type: str
extends_documentation_fragment:
- openstack.cloud.openstack
'''
EXAMPLES = r'''
- name: Create or update RBAC policy
neutron_rbac_policy:
object_id: '7422172b-2961-475c-ac68-bd0f2a9960ad'
object_type: 'network'
project_id: '84b8774d595b41e89f3dfaa1fd76932d'
target_project_id: 'a12f9ce1de0645e0a0b01c2e679f69ec'
- name: Delete RBAC policy
openstack.cloud.openstack.neutron_rbac_policy:
id: 'f625242a-6a73-47ac-8d1f-91440b2c617f'
state: absent
'''
RETURN = r'''
rbac_policy:
description: A dictionary describing the RBAC policy.
returned: always
type: dict
contains:
action:
description:
- The access model specified by the RBAC rules
type: str
sample: "access_as_shared"
id:
description:
- The ID of the RBAC rule/policy
type: str
sample: "4154ce0c-71a7-4d87-a905-09762098ddb9"
name:
description:
- The name of the RBAC rule; usually null
type: str
sample: null
object_id:
description:
- The UUID of the object to which the RBAC rules apply
type: str
sample: "7422172b-2961-475c-ac68-bd0f2a9960ad"
object_type:
description:
- The object type to which the RBACs apply
type: str
sample: "network"
project_id:
description:
- The UUID of the project to which access is granted
type: str
sample: "84b8774d595b41e89f3dfaa1fd76932c"
target_project_id:
description:
- The UUID of the target project
type: str
sample: "c201a689c016435c8037977166f77368"
tenant_id:
description:
- The UUID of the project to which access is granted. Deprecated.
type: str
sample: "84b8774d595b41e89f3dfaa1fd76932c"
policy:
description: Same as C(rbac_policy), kept for backward compatibility.
returned: always
type: dict
'''
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
class NeutronRBACPolicy(OpenStackModule):
argument_spec = dict(
action=dict(choices=['access_as_external', 'access_as_shared']),
id=dict(aliases=['policy_id']),
object_id=dict(),
object_type=dict(choices=['security_group', 'qos_policy', 'network']),
project_id=dict(),
state=dict(default='present', choices=['absent', 'present']),
target_project_id=dict(),
)
module_kwargs = dict(
required_if=[
('state', 'present', ('target_project_id',)),
('state', 'absent', ('id',)),
],
supports_check_mode=True,
)
def run(self):
state = self.params['state']
policy = self._find()
if self.ansible.check_mode:
self.exit_json(changed=self._will_change(state, policy))
if state == 'present' and not policy:
# Create policy
policy = self._create()
self.exit_json(changed=True,
rbac_policy=policy.to_dict(computed=False),
policy=policy.to_dict(computed=False))
elif state == 'present' and policy:
# Update policy
update = self._build_update(policy)
if update:
policy = self._update(policy, update)
self.exit_json(changed=bool(update),
rbac_policy=policy.to_dict(computed=False),
policy=policy.to_dict(computed=False))
elif state == 'absent' and policy:
# Delete policy
self._delete(policy)
self.exit_json(changed=True)
elif state == 'absent' and not policy:
# Do nothing
self.exit_json(changed=False)
def _build_update(self, policy):
update = {}
non_updateable_keys = [k for k in ['object_id', 'object_type',
'project_id', 'action']
if self.params[k] is not None
and self.params[k] != policy[k]]
if non_updateable_keys:
self.fail_json(msg='Cannot update parameters {0}'
.format(non_updateable_keys))
attributes = dict((k, self.params[k])
for k in ['target_project_id']
if self.params[k] is not None
and self.params[k] != policy[k])
if attributes:
update['attributes'] = attributes
return update
def _create(self):
kwargs = dict((k, self.params[k])
for k in ['object_id', 'object_type',
'target_project_id', 'project_id',
'action']
if self.params[k] is not None)
return self.conn.network.create_rbac_policy(**kwargs)
def _delete(self, policy):
self.conn.network.delete_rbac_policy(policy.id)
def _find(self):
id = self.params['id']
if id is not None:
return self.conn.network.find_rbac_policy(id)
matches = self._find_matches()
if len(matches) > 1:
self.fail_json(msg='Found more a single matching RBAC policy'
' which match the given parameters.')
elif len(matches) == 1:
return matches[0]
else: # len(matches) == 0
return None
def _find_matches(self):
missing_keys = [k for k in ['action', 'object_id', 'object_type',
'project_id', 'target_project_id']
if self.params[k] is None]
if missing_keys:
self.fail_json(msg='Missing parameter(s) for finding'
' a matching RBAC policy: {0}'
.format(', '.join(missing_keys)))
kwargs = dict((k, self.params[k])
for k in ['action', 'object_type', 'project_id'])
policies = self.conn.network.rbac_policies(**kwargs)
return [p for p in policies
if any(p[k] == self.params[k]
for k in ['object_id', 'target_project_id'])]
def _update(self, policy, update):
attributes = update.get('attributes')
if attributes:
policy = self.conn.network.update_rbac_policy(policy.id,
**attributes)
return policy
def _will_change(self, state, policy):
if state == 'present' and not policy:
return True
elif state == 'present' and policy:
return bool(self._build_update(policy))
elif state == 'absent' and policy:
return True
else:
# state == 'absent' and not policy:
return False
def main():
module = NeutronRBACPolicy()
module()
if __name__ == '__main__':
main()