
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
266 lines
7.9 KiB
Python
266 lines
7.9 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright (c) 2015 Hewlett-Packard Development Company, L.P.
|
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
DOCUMENTATION = '''
|
|
---
|
|
module: identity_user
|
|
short_description: Manage OpenStack Identity Users
|
|
author: OpenStack Ansible SIG
|
|
description:
|
|
- Manage OpenStack Identity users. Users can be created,
|
|
updated or deleted using this module. A user will be updated
|
|
if I(name) matches an existing user and I(state) is present.
|
|
The value for I(name) cannot be updated without deleting and
|
|
re-creating the user.
|
|
options:
|
|
name:
|
|
description:
|
|
- Username for the user
|
|
required: true
|
|
type: str
|
|
password:
|
|
description:
|
|
- Password for the user
|
|
type: str
|
|
update_password:
|
|
required: false
|
|
choices: ['always', 'on_create']
|
|
default: on_create
|
|
description:
|
|
- C(always) will attempt to update password. C(on_create) will only
|
|
set the password for newly created users.
|
|
type: str
|
|
email:
|
|
description:
|
|
- Email address for the user
|
|
type: str
|
|
description:
|
|
description:
|
|
- Description about the user
|
|
type: str
|
|
default_project:
|
|
description:
|
|
- Project name or ID that the user should be associated with by default
|
|
type: str
|
|
domain:
|
|
description:
|
|
- Domain to create the user in if the cloud supports domains
|
|
type: str
|
|
enabled:
|
|
description:
|
|
- Is the user enabled
|
|
type: bool
|
|
default: 'yes'
|
|
state:
|
|
description:
|
|
- Should the resource be present or absent.
|
|
choices: [present, absent]
|
|
default: present
|
|
type: str
|
|
extends_documentation_fragment:
|
|
- openstack.cloud.openstack
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
# Create a user
|
|
- openstack.cloud.identity_user:
|
|
cloud: mycloud
|
|
state: present
|
|
name: demouser
|
|
password: secret
|
|
email: demo@example.com
|
|
domain: default
|
|
default_project: demo
|
|
|
|
# Delete a user
|
|
- openstack.cloud.identity_user:
|
|
cloud: mycloud
|
|
state: absent
|
|
name: demouser
|
|
|
|
# Create a user but don't update password if user exists
|
|
- openstack.cloud.identity_user:
|
|
cloud: mycloud
|
|
state: present
|
|
name: demouser
|
|
password: secret
|
|
update_password: on_create
|
|
email: demo@example.com
|
|
domain: default
|
|
default_project: demo
|
|
|
|
# Create a user without password
|
|
- openstack.cloud.identity_user:
|
|
cloud: mycloud
|
|
state: present
|
|
name: demouser
|
|
email: demo@example.com
|
|
domain: default
|
|
default_project: demo
|
|
'''
|
|
|
|
|
|
RETURN = '''
|
|
user:
|
|
description: Dictionary describing the user.
|
|
returned: On success when I(state) is 'present'
|
|
type: dict
|
|
contains:
|
|
default_project_id:
|
|
description: User default project ID. Only present with Keystone >= v3.
|
|
returned: success
|
|
type: str
|
|
sample: "4427115787be45f08f0ec22a03bfc735"
|
|
description:
|
|
description: The description of this user
|
|
returned: success
|
|
type: str
|
|
sample: "a user"
|
|
domain_id:
|
|
description: User domain ID. Only present with Keystone >= v3.
|
|
returned: success
|
|
type: str
|
|
sample: "default"
|
|
email:
|
|
description: User email address
|
|
returned: success
|
|
type: str
|
|
sample: "demo@example.com"
|
|
id:
|
|
description: User ID
|
|
returned: success
|
|
type: str
|
|
sample: "f59382db809c43139982ca4189404650"
|
|
is_enabled:
|
|
description: Indicates whether the user is enabled
|
|
type: bool
|
|
links:
|
|
description: The links for the user resource
|
|
returned: success
|
|
type: dict
|
|
elements: str
|
|
name:
|
|
description: Unique user name, within the owning domain
|
|
returned: success
|
|
type: str
|
|
sample: "demouser"
|
|
password:
|
|
description: Credential used during authentication
|
|
returned: success
|
|
type: str
|
|
password_expires_at:
|
|
description: The date and time when the password expires. The time zone is UTC. A none value means the password never expires
|
|
returned: success
|
|
type: str
|
|
|
|
'''
|
|
|
|
from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule
|
|
|
|
|
|
class IdentityUserModule(OpenStackModule):
|
|
argument_spec = dict(
|
|
name=dict(required=True),
|
|
password=dict(no_log=True),
|
|
email=dict(),
|
|
default_project=dict(),
|
|
description=dict(),
|
|
domain=dict(),
|
|
enabled=dict(default=True, type='bool'),
|
|
state=dict(default='present', choices=['absent', 'present']),
|
|
update_password=dict(default='on_create', choices=['always', 'on_create']),
|
|
)
|
|
|
|
module_kwargs = dict()
|
|
|
|
def _needs_update(self, params_dict, user):
|
|
for k in params_dict:
|
|
# We don't get password back in the user object, so assume any supplied
|
|
# password is a change.
|
|
if k == 'password':
|
|
return True
|
|
if user[k] != params_dict[k]:
|
|
return True
|
|
return False
|
|
|
|
def _get_domain_id(self, domain):
|
|
dom_obj = self.conn.identity.find_domain(domain)
|
|
if dom_obj is None:
|
|
# Ok, let's hope the user is non-admin and passing a sane id
|
|
return domain
|
|
return dom_obj.id
|
|
|
|
def _get_default_project_id(self, default_project, domain_id):
|
|
project = self.conn.identity.find_project(default_project, domain_id=domain_id)
|
|
if not project:
|
|
self.fail_json(msg='Default project %s is not valid' % default_project)
|
|
return project['id']
|
|
|
|
def run(self):
|
|
name = self.params['name']
|
|
password = self.params.get('password')
|
|
email = self.params['email']
|
|
default_project = self.params['default_project']
|
|
domain = self.params['domain']
|
|
enabled = self.params['enabled']
|
|
state = self.params['state']
|
|
update_password = self.params['update_password']
|
|
description = self.params['description']
|
|
|
|
domain_id = None
|
|
if domain:
|
|
domain_id = self._get_domain_id(domain)
|
|
user = self.conn.identity.find_user(name, domain_id=domain_id)
|
|
|
|
changed = False
|
|
if state == 'present':
|
|
user_args = {
|
|
'name': name,
|
|
'email': email,
|
|
'domain_id': domain_id,
|
|
'description': description,
|
|
'is_enabled': enabled,
|
|
}
|
|
if default_project:
|
|
default_project_id = self._get_default_project_id(
|
|
default_project, domain_id)
|
|
user_args['default_project_id'] = default_project_id
|
|
user_args = {k: v for k, v in user_args.items() if v is not None}
|
|
|
|
changed = False
|
|
if user is None:
|
|
if password:
|
|
user_args['password'] = password
|
|
|
|
user = self.conn.identity.create_user(**user_args)
|
|
changed = True
|
|
else:
|
|
if update_password == 'always':
|
|
if not password:
|
|
self.fail_json(msg="update_password is always but a password value is missing")
|
|
user_args['password'] = password
|
|
# else we do not want to update the password
|
|
|
|
if self._needs_update(user_args, user):
|
|
user = self.conn.identity.update_user(user['id'], **user_args)
|
|
changed = True
|
|
|
|
user = user.to_dict(computed=False)
|
|
self.exit_json(changed=changed, user=user)
|
|
elif state == 'absent' and user is not None:
|
|
self.conn.identity.delete_user(user)
|
|
changed = True
|
|
self.exit_json(changed=changed)
|
|
|
|
|
|
def main():
|
|
module = IdentityUserModule()
|
|
module()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|