mirror of
				https://github.com/ansible-collections/community.general.git
				synced 2024-09-14 20:13:21 +02:00 
			
		
		
		
	Taking a page out of the ec2 config, make sure that all of the OpenStack modules handle the inbound auth config in the same way. The one outlier is keystone wrt auth_url.
		
			
				
	
	
		
			394 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			394 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/usr/bin/python
 | 
						|
# -*- coding: utf-8 -*-
 | 
						|
 | 
						|
# Based on Jimmy Tang's implementation
 | 
						|
 | 
						|
DOCUMENTATION = '''
 | 
						|
---
 | 
						|
module: keystone_user
 | 
						|
version_added: "1.2"
 | 
						|
short_description: Manage OpenStack Identity (keystone) users, tenants and roles
 | 
						|
description:
 | 
						|
   - Manage users,tenants, roles from OpenStack.
 | 
						|
options:
 | 
						|
   login_user:
 | 
						|
     description:
 | 
						|
        - login username to authenticate to keystone
 | 
						|
     required: false
 | 
						|
     default: admin
 | 
						|
   login_password:
 | 
						|
     description:
 | 
						|
        - Password of login user
 | 
						|
     required: false
 | 
						|
     default: 'yes'
 | 
						|
   login_tenant_name:
 | 
						|
     description:
 | 
						|
        - The tenant login_user belongs to
 | 
						|
     required: false
 | 
						|
     default: None
 | 
						|
     version_added: "1.3"
 | 
						|
   token:
 | 
						|
     description:
 | 
						|
        - The token to be uses in case the password is not specified
 | 
						|
     required: false
 | 
						|
     default: None
 | 
						|
   endpoint:
 | 
						|
     description:
 | 
						|
        - The keystone url for authentication
 | 
						|
     required: false
 | 
						|
     default: 'http://127.0.0.1:35357/v2.0/'
 | 
						|
   user:
 | 
						|
     description:
 | 
						|
        - The name of the user that has to added/removed from OpenStack
 | 
						|
     required: false
 | 
						|
     default: None
 | 
						|
   password:
 | 
						|
     description:
 | 
						|
        - The password to be assigned to the user
 | 
						|
     required: false
 | 
						|
     default: None
 | 
						|
   tenant:
 | 
						|
     description:
 | 
						|
        - The tenant name that has be added/removed
 | 
						|
     required: false
 | 
						|
     default: None
 | 
						|
   description:
 | 
						|
     description:
 | 
						|
        - A description for the tenant
 | 
						|
     required: false
 | 
						|
     default: None
 | 
						|
   email:
 | 
						|
     description:
 | 
						|
        - An email address for the user
 | 
						|
     required: false
 | 
						|
     default: None
 | 
						|
   role:
 | 
						|
     description:
 | 
						|
        - The name of the role to be assigned or created
 | 
						|
     required: false
 | 
						|
     default: None
 | 
						|
   state:
 | 
						|
     description:
 | 
						|
        - Indicate desired state of the resource
 | 
						|
     choices: ['present', 'absent']
 | 
						|
     default: present
 | 
						|
requirements: [ python-keystoneclient ]
 | 
						|
author: Lorin Hochstein
 | 
						|
'''
 | 
						|
 | 
						|
EXAMPLES = '''
 | 
						|
# Create a tenant
 | 
						|
- keystone_user: tenant=demo tenant_description="Default Tenant"
 | 
						|
 | 
						|
# Create a user
 | 
						|
- keystone_user: user=john tenant=demo password=secrete
 | 
						|
 | 
						|
# Apply the admin role to the john user in the demo tenant
 | 
						|
- keystone_user: role=admin user=john tenant=demo
 | 
						|
'''
 | 
						|
 | 
						|
try:
 | 
						|
    from keystoneclient.v2_0 import client
 | 
						|
except ImportError:
 | 
						|
    keystoneclient_found = False
 | 
						|
else:
 | 
						|
    keystoneclient_found = True
 | 
						|
 | 
						|
 | 
						|
def authenticate(endpoint, token, login_user, login_password, login_tenant_name):
 | 
						|
    """Return a keystone client object"""
 | 
						|
 | 
						|
    if token:
 | 
						|
        return client.Client(endpoint=endpoint, token=token)
 | 
						|
    else:
 | 
						|
        return client.Client(auth_url=endpoint, username=login_user,
 | 
						|
                             password=login_password, tenant_name=login_tenant_name)
 | 
						|
 | 
						|
 | 
						|
def tenant_exists(keystone, tenant):
 | 
						|
    """ Return True if tenant already exists"""
 | 
						|
    return tenant in [x.name for x in keystone.tenants.list()]
 | 
						|
 | 
						|
 | 
						|
def user_exists(keystone, user):
 | 
						|
    """" Return True if user already exists"""
 | 
						|
    return user in [x.name for x in keystone.users.list()]
 | 
						|
 | 
						|
 | 
						|
def get_tenant(keystone, name):
 | 
						|
    """ Retrieve a tenant by name"""
 | 
						|
    tenants = [x for x in keystone.tenants.list() if x.name == name]
 | 
						|
    count = len(tenants)
 | 
						|
    if count == 0:
 | 
						|
        raise KeyError("No keystone tenants with name %s" % name)
 | 
						|
    elif count > 1:
 | 
						|
        raise ValueError("%d tenants with name %s" % (count, name))
 | 
						|
    else:
 | 
						|
        return tenants[0]
 | 
						|
 | 
						|
 | 
						|
def get_user(keystone, name):
 | 
						|
    """ Retrieve a user by name"""
 | 
						|
    users = [x for x in keystone.users.list() if x.name == name]
 | 
						|
    count = len(users)
 | 
						|
    if count == 0:
 | 
						|
        raise KeyError("No keystone users with name %s" % name)
 | 
						|
    elif count > 1:
 | 
						|
        raise ValueError("%d users with name %s" % (count, name))
 | 
						|
    else:
 | 
						|
        return users[0]
 | 
						|
 | 
						|
 | 
						|
def get_role(keystone, name):
 | 
						|
    """ Retrieve a role by name"""
 | 
						|
    roles = [x for x in keystone.roles.list() if x.name == name]
 | 
						|
    count = len(roles)
 | 
						|
    if count == 0:
 | 
						|
        raise KeyError("No keystone roles with name %s" % name)
 | 
						|
    elif count > 1:
 | 
						|
        raise ValueError("%d roles with name %s" % (count, name))
 | 
						|
    else:
 | 
						|
        return roles[0]
 | 
						|
 | 
						|
 | 
						|
def get_tenant_id(keystone, name):
 | 
						|
    return get_tenant(keystone, name).id
 | 
						|
 | 
						|
 | 
						|
def get_user_id(keystone, name):
 | 
						|
    return get_user(keystone, name).id
 | 
						|
 | 
						|
 | 
						|
def ensure_tenant_exists(keystone, tenant_name, tenant_description,
 | 
						|
                         check_mode):
 | 
						|
    """ Ensure that a tenant exists.
 | 
						|
 | 
						|
        Return (True, id) if a new tenant was created, (False, None) if it
 | 
						|
        already existed.
 | 
						|
    """
 | 
						|
 | 
						|
    # Check if tenant already exists
 | 
						|
    try:
 | 
						|
        tenant = get_tenant(keystone, tenant_name)
 | 
						|
    except KeyError:
 | 
						|
        # Tenant doesn't exist yet
 | 
						|
        pass
 | 
						|
    else:
 | 
						|
        if tenant.description == tenant_description:
 | 
						|
            return (False, tenant.id)
 | 
						|
        else:
 | 
						|
            # We need to update the tenant description
 | 
						|
            if check_mode:
 | 
						|
                return (True, tenant.id)
 | 
						|
            else:
 | 
						|
                tenant.update(description=tenant_description)
 | 
						|
                return (True, tenant.id)
 | 
						|
 | 
						|
    # We now know we will have to create a new tenant
 | 
						|
    if check_mode:
 | 
						|
        return (True, None)
 | 
						|
 | 
						|
    ks_tenant = keystone.tenants.create(tenant_name=tenant_name,
 | 
						|
                                        description=tenant_description,
 | 
						|
                                        enabled=True)
 | 
						|
    return (True, ks_tenant.id)
 | 
						|
 | 
						|
 | 
						|
def ensure_tenant_absent(keystone, tenant, check_mode):
 | 
						|
    """ Ensure that a tenant does not exist
 | 
						|
 | 
						|
         Return True if the tenant was removed, False if it didn't exist
 | 
						|
         in the first place
 | 
						|
    """
 | 
						|
    if not tenant_exists(keystone, tenant):
 | 
						|
        return False
 | 
						|
 | 
						|
    # We now know we will have to delete the tenant
 | 
						|
    if check_mode:
 | 
						|
        return True
 | 
						|
 | 
						|
 | 
						|
def ensure_user_exists(keystone, user_name, password, email, tenant_name,
 | 
						|
                       check_mode):
 | 
						|
    """ Check if user exists
 | 
						|
 | 
						|
        Return (True, id) if a new user was created, (False, id) user alrady
 | 
						|
        exists
 | 
						|
    """
 | 
						|
 | 
						|
    # Check if tenant already exists
 | 
						|
    try:
 | 
						|
        user = get_user(keystone, user_name)
 | 
						|
    except KeyError:
 | 
						|
        # Tenant doesn't exist yet
 | 
						|
        pass
 | 
						|
    else:
 | 
						|
        # User does exist, we're done
 | 
						|
        return (False, user.id)
 | 
						|
 | 
						|
    # We now know we will have to create a new user
 | 
						|
    if check_mode:
 | 
						|
        return (True, None)
 | 
						|
 | 
						|
    tenant = get_tenant(keystone, tenant_name)
 | 
						|
 | 
						|
    user = keystone.users.create(name=user_name, password=password,
 | 
						|
                                 email=email, tenant_id=tenant.id)
 | 
						|
    return (True, user.id)
 | 
						|
 | 
						|
 | 
						|
def ensure_role_exists(keystone, user_name, tenant_name, role_name,
 | 
						|
                       check_mode):
 | 
						|
    """ Check if role exists
 | 
						|
 | 
						|
        Return (True, id) if a new role was created or if the role was newly
 | 
						|
        assigned to the user for the tenant. (False, id) if the role already
 | 
						|
        exists and was already assigned to the user ofr the tenant.
 | 
						|
 | 
						|
    """
 | 
						|
    # Check if the user has the role in the tenant
 | 
						|
    user = get_user(keystone, user_name)
 | 
						|
    tenant = get_tenant(keystone, tenant_name)
 | 
						|
    roles = [x for x in keystone.roles.roles_for_user(user, tenant)
 | 
						|
                     if x.name == role_name]
 | 
						|
    count = len(roles)
 | 
						|
 | 
						|
    if count == 1:
 | 
						|
        # If the role is in there, we are done
 | 
						|
        role = roles[0]
 | 
						|
        return (False, role.id)
 | 
						|
    elif count > 1:
 | 
						|
        # Too many roles with the same name, throw an error
 | 
						|
        raise ValueError("%d roles with name %s" % (count, role_name))
 | 
						|
 | 
						|
    # At this point, we know we will need to make changes
 | 
						|
    if check_mode:
 | 
						|
        return (True, None)
 | 
						|
 | 
						|
    # Get the role if it exists
 | 
						|
    try:
 | 
						|
        role = get_role(keystone, role_name)
 | 
						|
    except KeyError:
 | 
						|
        # Role doesn't exist yet
 | 
						|
        role = keystone.roles.create(role_name)
 | 
						|
 | 
						|
    # Associate the role with the user in the admin
 | 
						|
    keystone.roles.add_user_role(user, role, tenant)
 | 
						|
    return (True, role.id)
 | 
						|
 | 
						|
 | 
						|
def ensure_user_absent(keystone, user, check_mode):
 | 
						|
    raise NotImplementedError("Not yet implemented")
 | 
						|
 | 
						|
 | 
						|
def ensure_role_absent(keystone, uesr, tenant, role, check_mode):
 | 
						|
    raise NotImplementedError("Not yet implemented")
 | 
						|
 | 
						|
 | 
						|
def main():
 | 
						|
 | 
						|
    argument_spec = openstack_argument_spec()
 | 
						|
    argument_spec.update(dict(
 | 
						|
            tenant_description=dict(required=False),
 | 
						|
            email=dict(required=False),
 | 
						|
            role=dict(required=False),
 | 
						|
            state=dict(default='present', choices=['present', 'absent']),
 | 
						|
            endpoint=dict(required=False,
 | 
						|
                          default="http://127.0.0.1:35357/v2.0"),
 | 
						|
            token=dict(required=False),
 | 
						|
            login_user=dict(required=False),
 | 
						|
            login_password=dict(required=False),
 | 
						|
            login_tenant_name=dict(required=False)
 | 
						|
    ))
 | 
						|
    # keystone operations themselves take an endpoint, not a keystone auth_url
 | 
						|
    del(argument_spec['auth_url'])
 | 
						|
    module = AnsibleModule(
 | 
						|
        argument_spec=argument_spec,
 | 
						|
        supports_check_mode=True,
 | 
						|
        mutually_exclusive=[['token', 'login_user'],
 | 
						|
                            ['token', 'login_password'],
 | 
						|
                            ['token', 'login_tenant_name']]
 | 
						|
    )
 | 
						|
 | 
						|
    if not keystoneclient_found:
 | 
						|
        module.fail_json(msg="the python-keystoneclient module is required")
 | 
						|
 | 
						|
    user = module.params['user']
 | 
						|
    password = module.params['password']
 | 
						|
    tenant = module.params['tenant']
 | 
						|
    tenant_description = module.params['tenant_description']
 | 
						|
    email = module.params['email']
 | 
						|
    role = module.params['role']
 | 
						|
    state = module.params['state']
 | 
						|
    endpoint = module.params['endpoint']
 | 
						|
    token = module.params['token']
 | 
						|
    login_user = module.params['login_user']
 | 
						|
    login_password = module.params['login_password']
 | 
						|
    login_tenant_name = module.params['login_tenant_name']
 | 
						|
 | 
						|
    keystone = authenticate(endpoint, token, login_user, login_password, login_tenant_name)
 | 
						|
 | 
						|
    check_mode = module.check_mode
 | 
						|
 | 
						|
    try:
 | 
						|
        d = dispatch(keystone, user, password, tenant, tenant_description,
 | 
						|
                     email, role, state, endpoint, token, login_user,
 | 
						|
                     login_password, check_mode)
 | 
						|
    except Exception, e:
 | 
						|
        if check_mode:
 | 
						|
            # If we have a failure in check mode
 | 
						|
            module.exit_json(changed=True,
 | 
						|
                             msg="exception: %s" % e)
 | 
						|
        else:
 | 
						|
            module.fail_json(msg="exception: %s" % e)
 | 
						|
    else:
 | 
						|
        module.exit_json(**d)
 | 
						|
 | 
						|
 | 
						|
def dispatch(keystone, user=None, password=None, tenant=None,
 | 
						|
             tenant_description=None, email=None, role=None,
 | 
						|
             state="present", endpoint=None, token=None, login_user=None,
 | 
						|
             login_password=None, check_mode=False):
 | 
						|
    """ Dispatch to the appropriate method.
 | 
						|
 | 
						|
        Returns a dict that will be passed to exit_json
 | 
						|
 | 
						|
        tenant  user  role   state
 | 
						|
        ------  ----  ----  --------
 | 
						|
          X                  present     ensure_tenant_exists
 | 
						|
          X                  absent      ensure_tenant_absent
 | 
						|
          X      X           present     ensure_user_exists
 | 
						|
          X      X           absent      ensure_user_absent
 | 
						|
          X      X     X     present     ensure_role_exists
 | 
						|
          X      X     X     absent      ensure_role_absent
 | 
						|
 | 
						|
 | 
						|
        """
 | 
						|
    changed = False
 | 
						|
    id = None
 | 
						|
    if tenant and not user and not role and state == "present":
 | 
						|
        changed, id = ensure_tenant_exists(keystone, tenant,
 | 
						|
                                           tenant_description, check_mode)
 | 
						|
    elif tenant and not user and not role and state == "absent":
 | 
						|
        changed = ensure_tenant_absent(keystone, tenant, check_mode)
 | 
						|
    elif tenant and user and not role and state == "present":
 | 
						|
        changed, id = ensure_user_exists(keystone, user, password,
 | 
						|
                                         email, tenant, check_mode)
 | 
						|
    elif tenant and user and not role and state == "absent":
 | 
						|
        changed = ensure_user_absent(keystone, user, check_mode)
 | 
						|
    elif tenant and user and role and state == "present":
 | 
						|
        changed, id = ensure_role_exists(keystone, user, tenant, role,
 | 
						|
                                         check_mode)
 | 
						|
    elif tenant and user and role and state == "absent":
 | 
						|
        changed = ensure_role_absent(keystone, user, tenant, role, check_mode)
 | 
						|
    else:
 | 
						|
        # Should never reach here
 | 
						|
        raise ValueError("Code should never reach here")
 | 
						|
 | 
						|
    return dict(changed=changed, id=id)
 | 
						|
 | 
						|
# import module snippets
 | 
						|
from ansible.module_utils.basic import *
 | 
						|
from ansible.module_utils.openstack import *
 | 
						|
if __name__ == '__main__':
 | 
						|
    main()
 |