Use proxy layer in identity_user module
This patch changes the module to use the sdk proxy layer and does some general refactoring to simplify the code. It will no longer fail if no password is supplied since it is perfectly fine to create a user with an password. Renamed the test role from user to identity_user to match the module name Change-Id: I97ee9b626f269abde3be7b2b9211d2bb5b7b3c26
This commit is contained in:
parent
a8589e9f4d
commit
fd1b9fc0d2
@ -70,6 +70,7 @@
|
||||
dns_zone_info
|
||||
floating_ip_info
|
||||
group
|
||||
identity_user
|
||||
identity_user_info
|
||||
identity_role
|
||||
image
|
||||
|
11
ci/roles/identity_user/defaults/main.yml
Normal file
11
ci/roles/identity_user/defaults/main.yml
Normal file
@ -0,0 +1,11 @@
|
||||
os_identity_user_fields:
|
||||
- default_project_id
|
||||
- description
|
||||
- domain_id
|
||||
- email
|
||||
- id
|
||||
- is_enabled
|
||||
- links
|
||||
- name
|
||||
- password
|
||||
- password_expires_at
|
197
ci/roles/identity_user/tasks/main.yml
Normal file
197
ci/roles/identity_user/tasks/main.yml
Normal file
@ -0,0 +1,197 @@
|
||||
---
|
||||
- name: setup
|
||||
block:
|
||||
- name: Delete user before running tests
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: "{{ item }}"
|
||||
loop:
|
||||
- ansible_user
|
||||
- ansible_user2
|
||||
register: user
|
||||
|
||||
- block:
|
||||
- name: Delete unexistent user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
||||
register: user
|
||||
|
||||
- name: Ensure user was not changed
|
||||
assert:
|
||||
that: user is not changed
|
||||
|
||||
- block:
|
||||
- name: Create a user without a password
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
email: ansible.user@nowhere.net
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- name: Ensure user was changed
|
||||
assert:
|
||||
that: user is changed
|
||||
|
||||
- name: Ensure user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- name: Fail when update_password is always but no password specified
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
update_password: always
|
||||
email: ansible.user@nowhere.net
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that: user.msg == "update_password is always but a password value is missing"
|
||||
|
||||
- name: Delete user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
||||
|
||||
- block:
|
||||
- name: Create user with a password
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: ansible.user@nowhere.net
|
||||
update_password: on_create
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- name: Assert user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- block:
|
||||
- name: Create identical user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: ansible.user@nowhere.net
|
||||
update_password: on_create
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- name: Assert user was not changed
|
||||
assert:
|
||||
that: user is not changed
|
||||
|
||||
- name: Assert user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- block:
|
||||
- name: Update user with password
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret2
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: user
|
||||
|
||||
- name: Ensure user was changed
|
||||
assert:
|
||||
that: user is changed
|
||||
|
||||
- name: Ensure user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- name: Update user without password and update_password set to always
|
||||
block:
|
||||
- openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
update_password: always
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: user
|
||||
ignore_errors: yes
|
||||
|
||||
- assert:
|
||||
that: user.msg == "update_password is always but a password value is missing"
|
||||
|
||||
- block:
|
||||
- name: Ensure user with update_password set to on_create
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
update_password: on_create
|
||||
password: secret3
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: user
|
||||
|
||||
- name: Ensure user was not changed
|
||||
assert:
|
||||
that: user is not changed
|
||||
|
||||
- block:
|
||||
- name: Ensure user with update_password set to always
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
update_password: always
|
||||
password: secret3
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: user
|
||||
|
||||
- name: Ensure user was changed
|
||||
assert:
|
||||
that: user is changed
|
||||
|
||||
- block:
|
||||
- name: Create user without a password
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user2
|
||||
password: secret
|
||||
email: ansible.user2@nowhere.net
|
||||
update_password: on_create
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- name: Assert user has fields
|
||||
assert:
|
||||
that: item in user['user']
|
||||
loop: "{{ os_identity_user_fields }}"
|
||||
|
||||
- block:
|
||||
- name: Delete user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
||||
|
||||
- name: Ensure user was changed
|
||||
assert:
|
||||
that: user is changed
|
@ -1,30 +0,0 @@
|
||||
---
|
||||
- name: Create user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: ansible.user@nowhere.net
|
||||
domain: default
|
||||
default_project: demo
|
||||
register: user
|
||||
|
||||
- debug: var=user
|
||||
|
||||
- name: Update user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: present
|
||||
name: ansible_user
|
||||
password: secret
|
||||
email: updated.ansible.user@nowhere.net
|
||||
register: updateduser
|
||||
|
||||
- debug: var=updateduser
|
||||
|
||||
- name: Delete user
|
||||
openstack.cloud.identity_user:
|
||||
cloud: "{{ cloud }}"
|
||||
state: absent
|
||||
name: ansible_user
|
@ -16,6 +16,7 @@
|
||||
tags: dns
|
||||
when: sdk_version is version(0.28, '>=')
|
||||
- { role: floating_ip_info, tags: floating_ip_info }
|
||||
- { role: identity_user, tags: identity_user }
|
||||
- { role: identity_user_info, tags: identity_user_info }
|
||||
- { role: identity_role, tags: identity_role }
|
||||
- { role: image, tags: image }
|
||||
@ -49,7 +50,6 @@
|
||||
- { role: server, tags: server }
|
||||
- { role: subnet, tags: subnet }
|
||||
- { role: subnet_pool, tags: subnet_pool }
|
||||
- { role: user, tags: user }
|
||||
- { role: user_group, tags: user_group }
|
||||
- { role: user_role, tags: user_role }
|
||||
- { role: volume, tags: volume }
|
||||
|
@ -26,6 +26,7 @@ options:
|
||||
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.
|
||||
@ -108,28 +109,55 @@ RETURN = '''
|
||||
user:
|
||||
description: Dictionary describing the user.
|
||||
returned: On success when I(state) is 'present'
|
||||
type: complex
|
||||
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: User 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
|
||||
@ -145,46 +173,32 @@ class IdentityUserModule(OpenStackModule):
|
||||
domain=dict(required=False, default=None),
|
||||
enabled=dict(default=True, type='bool'),
|
||||
state=dict(default='present', choices=['absent', 'present']),
|
||||
update_password=dict(default=None, choices=['always', 'on_create']),
|
||||
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:
|
||||
if k not in ('password', 'update_password') and user[k] != params_dict[k]:
|
||||
# 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
|
||||
|
||||
# We don't get password back in the user object, so assume any supplied
|
||||
# password is a change.
|
||||
if (
|
||||
params_dict['password'] is not None
|
||||
and params_dict['update_password'] == 'always'
|
||||
):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _get_domain_id(self, domain):
|
||||
try:
|
||||
# We assume admin is passing domain id
|
||||
domain_id = self.conn.get_domain(domain)['id']
|
||||
except Exception:
|
||||
# If we fail, maybe admin is passing a domain name.
|
||||
# Note that domains have unique names, just like id.
|
||||
try:
|
||||
domain_id = self.conn.search_domains(filters={'name': domain})[0]['id']
|
||||
except Exception:
|
||||
# Ok, let's hope the user is non-admin and passing a sane id
|
||||
domain_id = domain
|
||||
|
||||
return domain_id
|
||||
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.get_project(default_project, domain_id=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):
|
||||
@ -201,81 +215,47 @@ class IdentityUserModule(OpenStackModule):
|
||||
domain_id = None
|
||||
if domain:
|
||||
domain_id = self._get_domain_id(domain)
|
||||
user = self.conn.get_user(name, domain_id=domain_id)
|
||||
else:
|
||||
user = self.conn.get_user(name)
|
||||
user = self.conn.identity.find_user(name, domain_id=domain_id)
|
||||
|
||||
changed = False
|
||||
if state == 'present':
|
||||
if update_password in ('always', 'on_create'):
|
||||
if not password:
|
||||
msg = "update_password is %s but a password value is missing" % update_password
|
||||
self.fail_json(msg=msg)
|
||||
default_project_id = None
|
||||
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 description is not None:
|
||||
user = self.conn.create_user(
|
||||
name=name, password=password, email=email,
|
||||
default_project=default_project_id, domain_id=domain_id,
|
||||
enabled=enabled, description=description)
|
||||
else:
|
||||
user = self.conn.create_user(
|
||||
name=name, password=password, email=email,
|
||||
default_project=default_project_id, domain_id=domain_id,
|
||||
enabled=enabled)
|
||||
if password:
|
||||
user_args['password'] = password
|
||||
|
||||
user = self.conn.identity.create_user(**user_args)
|
||||
changed = True
|
||||
else:
|
||||
params_dict = {'email': email, 'enabled': enabled,
|
||||
'password': password,
|
||||
'update_password': update_password}
|
||||
if description is not None:
|
||||
params_dict['description'] = description
|
||||
if domain_id is not None:
|
||||
params_dict['domain_id'] = domain_id
|
||||
if default_project_id is not None:
|
||||
params_dict['default_project_id'] = default_project_id
|
||||
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(params_dict, user):
|
||||
if update_password == 'always':
|
||||
if description is not None:
|
||||
user = self.conn.update_user(
|
||||
user['id'], password=password, email=email,
|
||||
default_project=default_project_id,
|
||||
domain_id=domain_id, enabled=enabled, description=description)
|
||||
else:
|
||||
user = self.conn.update_user(
|
||||
user['id'], password=password, email=email,
|
||||
default_project=default_project_id,
|
||||
domain_id=domain_id, enabled=enabled)
|
||||
else:
|
||||
if description is not None:
|
||||
user = self.conn.update_user(
|
||||
user['id'], email=email,
|
||||
default_project=default_project_id,
|
||||
domain_id=domain_id, enabled=enabled, description=description)
|
||||
else:
|
||||
user = self.conn.update_user(
|
||||
user['id'], email=email,
|
||||
default_project=default_project_id,
|
||||
domain_id=domain_id, enabled=enabled)
|
||||
if self._needs_update(user_args, user):
|
||||
user = self.conn.identity.update_user(user['id'], **user_args)
|
||||
changed = True
|
||||
else:
|
||||
changed = False
|
||||
self.exit_json(changed=changed, user=user)
|
||||
|
||||
elif state == 'absent':
|
||||
if user is None:
|
||||
changed = False
|
||||
else:
|
||||
if domain:
|
||||
self.conn.delete_user(user['id'], domain_id=domain_id)
|
||||
else:
|
||||
self.conn.delete_user(user['id'])
|
||||
changed = True
|
||||
self.exit_json(changed=changed)
|
||||
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():
|
||||
|
Loading…
Reference in New Issue
Block a user