Repository URL to install this package:
|
Version:
4.1.94.1.dev5 ▾
|
#!/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()