Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
Size: Mime:
#!/usr/bin/python
# -*- coding: utf-8 -*-

import traceback
from ansible.module_utils.basic import AnsibleModule

from keystoneauth1 import session
from keystoneclient import client
from keystoneauth1.identity.generic import password as keystone_password


DOCUMENTATION = '''
---
module: keystone_service
short_description: Manage OpenStack service account and its endpoints
options:
  name:
    description:
        - name of service (e.g., keystone)
    required: yes
  type:
    description:
        - type of service (e.g., identity)
    required: yes
  description:
    description:
        - description of service (e.g., Identity Service)
    required: yes
  public_url:
    description:
        - public url of service.
        - 'Alias: I(url)'
        - 'Alias: I(publicurl)'
    required: yes
  internal_url:
    description:
        - internal url of service.
        - 'Alias: I(internalurl)'
    required: no
    default: value of public_url
  admin_url:
    description:
        - admin url of service.
        - 'Alias: I(adminurl)'
    required: no
    default: value of public_url
  insecure:
    description:
        - allow use of self-signed SSL certificates with service
    required: no
    choices: [ "yes", "no" ]
    default: no
  region:
    description:
        - region of service
    required: no
    default: RegionOne
  identity_api_version:
    description:
        - identity api version
    required: no
    default: "3"
  ignore_other_regions:
    description:
        - allow endpoint to exist in other regions
    required: no
    choices: [ "yes", "no" ]
    default: yes
  state:
     description:
        - Indicate desired state of the resource
     choices: ['present', 'absent']
     default: present
requirements: [ python-keystoneclient ]
author: Murali Balcha
'''

EXAMPLES = '''
examples:
keystone_service: >
    name=TrilioVaultWLM
    type=workloads
    description="Workloadmgr workloads Service"
    publicurl=http://192.168.1.104:8780/v1/$(tenant_id)s
    internalurl=http://192.168.1.104:8780/v1/$(tenant_id)s
    adminurl=http://192.168.1.104:8780/v1/$(tenant_id)s
'''


def _validate_keystone_client_and_version(
     auth_url, username, password,
     tenant_id, domain_id, endpoint_type,
     insecure, cacert, identity_api_version):

    auth = keystone_password.Password(auth_url=auth_url,
                                      username=username,
                                      password=password,
                                      user_domain_id=domain_id,
                                      domain_id=domain_id)

    sess = session.Session(auth=auth, verify=cacert)
    keystone = client.Client(session=sess,
                             auth_url=auth_url,
                             interface=endpoint_type,
                             insecure=insecure,
                             cacert=cacert)

    tenants = keystone.projects.list()

    return (keystone, tenants)


def authenticate(keystone_auth_url, username, password, tenant_id,
                 domain_id, endpoint_type, insecure,
                 cacert, identity_api_version):
    """Return a keystone client object"""

    keystone, tenants = _validate_keystone_client_and_version(
        keystone_auth_url, username, password, tenant_id,
        domain_id, endpoint_type, insecure, cacert, identity_api_version)

    return keystone


def get_service(keystone, name):
    """ Retrieve a service by name """
    services = [x for x in keystone.services.list() if x.name == name]
    count = len(services)
    if count == 0:
        raise KeyError("No keystone services with name %s" % name)
    elif count > 1:
        raise ValueError("%d services with name %s" % (count, name))
    else:
        return services[0]


def _get_endpoint_v3(keystone, name, region, ignore_other_regions):
    """ Retrieve a service endpoint by name """
    service = get_service(keystone, name)
    endpoints = [x for x in keystone.endpoints.list()
                 if x.service_id == service.id]

    # If this is a multi-region cloud only look at this region's endpoints
    if ignore_other_regions:
        endpoints = [x for x in endpoints if x.region == region]

    count = len(endpoints)
    if count == 0:
        raise KeyError("No keystone endpoints with service name %s" % name)
    elif count > 3:
        raise ValueError("%d endpoints with service name %s" % (count, name))

    return endpoints


def ensure_present(keystone, name, service_type, description, public_url,
                   internal_url, admin_url, region, ignore_other_regions,
                   check_mode):
    """ Ensure the service and its endpoint are present and have the right values.
        Returns a tuple, where the first element is a boolean that indicates
        a state change, the second element is the service uuid (or None in
        check mode), and the third element is the endpoint uuid (or None in
        check mode)."""
    # Fetch service and endpoint, if they exist.
    service = None
    endpoints = None
    try:
        service = get_service(keystone, name)
    except KeyError:
        pass
    try:
        endpoints = _get_endpoint_v3(keystone, name, region, ignore_other_regions)
    except KeyError:
        pass

    changed = False

    # Delete endpoint if it exists and doesn't match.
    if endpoints is not None and len(endpoints) > 0:
        if len(endpoints) != 3:
            identical = False
        else:
            pidentical = False
            iidentical = False
            aidentical = False
            for e in endpoints:
                if e.interface == 'public' and e.url == public_url:
                    pidentical = True
                if e.interface == 'internal' and e.url == internal_url:
                    iidentical = True
                if e.interface == 'admin' and e.url == admin_url:
                    aidentical = True
            identical = pidentical and iidentical and aidentical
        if not identical:
            changed = True
            ensure_endpoint_absent(keystone, name, check_mode, region,
                                   ignore_other_regions)
            endpoints = None

    # Delete service and its endpoint if the service exists and doesn't match.
    if service is not None:
        svcdesc = getattr(service, "description", "")
        identical = service.name == name and \
                    service.type == service_type and \
                    svcdesc == description
        if not identical:
            changed = True
            ensure_endpoint_absent(keystone, name, check_mode, region,
                                   ignore_other_regions)
            endpoints = None
            ensure_service_absent(keystone, name, check_mode)
            service = None

    # Recreate service, if necessary.
    if service is None:
        if not check_mode:
            service = keystone.services.create(
                name,
                service_type,
                description=description,
            )
        changed = True

    # Recreate endpoint, if necessary.
    if endpoints is None:
        endpoints = []
        if not check_mode:
            for interface, url in {'public': public_url,
                                   'admin': admin_url,
                                   'internal': internal_url}.items():
                endpoint = keystone.endpoints.create(
                    service, url, interface=interface,
                    region=region, enabled=True)
                endpoints.append(endpoint)
        changed = True

    if check_mode:
        # In check mode, the service/endpoint uuids will be the old uuids,
        # so omit them.
        return changed, None, None

    return changed, service.id, [e.id for e in endpoints]


def ensure_service_absent(keystone, name, check_mode):
    """ Ensure the service is absent"""
    try:
        service = get_service(keystone, name)
        endpoints = [x for x in keystone.endpoints.list()
                     if x.service_id == service.id]

        # Don't delete the service if it still has endpoints
        if endpoints:
            return False

        if not check_mode:
            keystone.services.delete(service.id)
        return True
    except KeyError:
        # Service doesn't exist, so we're done.
        return False


def ensure_endpoint_absent(keystone, name, check_mode, region,
                           ignore_other_regions):
    """ Ensure the service endpoint """
    try:
        endpoints = _get_endpoint_v3(keystone, name, region, ignore_other_regions)
        if not check_mode:
            for e in endpoints:
                keystone.endpoints.delete(e.id)
        return True
    except KeyError:
        # Endpoint doesn't exist, so we're done.
        return False


def dispatch(keystone, name, service_type, description, public_url,
             internal_url, admin_url, region, ignore_other_regions, state,
             check_mode):

    if state == 'present':
        (changed, service_id, endpoint_id) = ensure_present(
            keystone,
            name,
            service_type,
            description,
            public_url,
            internal_url,
            admin_url,
            region,
            ignore_other_regions,
            check_mode,
        )
        return dict(changed=changed, service_id=service_id,
                    endpoint_id=endpoint_id)
    elif state == 'absent':
        endpoint_changed = ensure_endpoint_absent(keystone, name, check_mode,
                                                  region, ignore_other_regions)
        service_changed = ensure_service_absent(keystone, name, check_mode)
        return dict(changed=service_changed or endpoint_changed)
    else:
        raise ValueError("Code should never reach here")


def main():

    module = AnsibleModule(
        argument_spec=dict(
            name=dict(required=True),
            type=dict(required=True),
            description=dict(required=False),
            public_url=dict(required=True, aliases=['url', 'publicurl']),
            internal_url=dict(required=False, aliases=['internalurl']),
            admin_url=dict(required=False, aliases=['adminurl']),
            region=dict(required=False, default='RegionOne'),
            ignore_other_regions=dict(required=False,
                                      default=True, type='bool'),
            state=dict(default='present', choices=['present', 'absent']),
            endpoint=dict(required=False,
                          default="http://127.0.0.1:35357/v3",
                          aliases=['auth_url']),
            endpoint_type=dict(required=False, default='public',
                               choices=['admin', 'internal', 'public']),
            insecure=dict(required=False, default=False, type='bool'),
            cacert=dict(required=False,
                        default='/etc/workloadmgr/ca-chain.pem'),

            username=dict(required=False),
            password=dict(required=False, no_log=True),
            default_project=dict(required=False, aliases=['tenant']),
            domain_id=dict(required=False, default='default'),
            identity_api_version=dict(required=False, default='3')
        ),
        supports_check_mode=True,
    )

    endpoint = module.params['endpoint']
    endpoint_type = module.params['endpoint_type']
    username = module.params['username']
    password = module.params['password']
    tenant_id = module.params['default_project']
    insecure = module.boolean(module.params['insecure'])
    cacert = module.params['cacert']
    name = module.params['name']
    service_type = module.params['type']
    description = module.params['description']
    public_url = module.params['public_url']
    internal_url = module.params['internal_url']
    if internal_url is None:
        internal_url = public_url
    admin_url = module.params['admin_url']
    if admin_url is None:
        admin_url = public_url
    region = module.params['region']
    ignore_other_regions = module.boolean(
        module.params['ignore_other_regions'])
    state = module.params['state']
    domain_id = module.params['domain_id']
    identity_api_version = module.params['identity_api_version']

    keystone = authenticate(endpoint, username, password,
                            tenant_id, domain_id, endpoint_type,
                            insecure, cacert, identity_api_version)
    check_mode = module.check_mode

    try:
        d = dispatch(keystone, name, service_type, description,
                     public_url, internal_url, admin_url, region,
                     ignore_other_regions, state, check_mode)
    except Exception:
        if check_mode:
            # If we have a failure in check mode
            module.exit_json(changed=True,
                             msg="exception: %s" % traceback.format_exc())
        else:
            module.fail_json(msg=traceback.format_exc())
    else:
        module.exit_json(**d)


# this is magic, see lib/ansible/module_common.py
# <<INCLUDE_ANSIBLE_MODULE_COMMON>>
if __name__ == '__main__':
    main()