openstack-ansible-plugins/library/keystone

1470 lines
46 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2014, Kevin Carter <kevin.carter@rackspace.com>
#
# Copyright 2014, Rackspace US, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Based on Jimmy Tang's implementation
DOCUMENTATION = """
---
module: keystone
version_added: "1.6.2"
short_description:
- Manage OpenStack Identity (keystone) users, projects, roles, and
endpoints.
description:
- Manage OpenStack Identity (keystone) users, projects, roles, and
endpoints.
options:
return_code:
description:
- Allow for return Codes other than 0 when executing commands.
- This is a comma separated list of acceptable return codes.
default: 0
login_user:
description:
- login username to authenticate to keystone
required: false
default: admin
login_user_domain_name:
description:
- The domain login_user belongs to
required: false
default: 'Default'
login_password:
description:
- Password of login user
required: false
default: 'yes'
login_project_name:
description:
- The project login_user belongs to
required: false
default: None
login_project_domain_name:
description:
- The domain login_project belongs to
required: false
default: 'Default'
login_tenant_name:
description:
- The tenant login_user belongs to
required: false
default: None
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
ignore_catalog:
description:
- Ignore the service catalog when identifying the endpoint
required: false
password:
description:
- The password to be assigned to the user
required: false
default: None
user_name:
description:
- The name of the user that has to added/removed from OpenStack
required: false
default: None
project_name:
description:
- The project name that has be added/removed
required: false
default: None
tenant_name:
description:
- The tenant name that has be added/removed
required: false
default: None
role_name:
description:
- The name of the role to be assigned or created
required: false
service_name:
description:
- Name of the service.
required: false
default: None
region_name:
description:
- Name of the region.
required: false
default: None
domain_name:
description:
- Name of the domain to add a project to.
required: false
default: 'Default'
description:
description:
- A description for the project
required: false
default: None
email:
description:
- Email address for the user, this is only used in "ensure_user"
required: false
default: None
service_type:
description:
- Type of service.
required: false
default: None
endpoint_list:
description:
- List of endpoints to add to keystone for a service
required: false
default: None
type: list
group_name:
description:
- A name for the group
required: False
default: None
idp_name:
description:
- A name for the identity provider
required: False
default: None
idp_remote_ids:
description:
- A URL that identifies the remote identity provider
required: False
default: None
idp_enabled:
description:
- Set whether a remote identity provider is enabled
required: False
default: True
sp_name:
description:
- A name for the service provider
required: False
default: None
sp_enabled:
description:
- Set whether a service provider is enabled
required: False
default: True
sp_url:
description:
- URL where the service provider expects to receive SAML assertions
- eg: http(s)://${SP_HOST}:5000/Shibboleth.sso/SAML2/ECP
required: False
default: None
sp_auth_url:
description:
- URL for federated users to request tokens from
- eg: http(s)://${SP_HOST}:5000/v3/OS-FEDERATION
/identity_providers/${IDP_ID}/saml2/auth
required: False
default: None
protocol_name:
description:
- A name for the protocol
required: False
default: None
mapping_name:
description:
- A name for the mapping
required: False
default: None
mapping_rules:
description:
- A dictionary mapping federated users to local groups.
- see: http://specs.openstack.org/openstack/keystone-specs
/api/v3/identity-api-v3-os-federation-ext.html#mappings
required: False
default: None
domain_enabled:
description:
- Name for a domain
required: False
default: True
state:
description:
- Ensuring the endpoint is either present, absent.
- It always ensures endpoint is updated to latest url.
required: False
default: 'present'
command:
description:
- Indicate desired state of the resource
choices: ['get_tenant', 'get_project', 'get_user', 'get_role',
'ensure_service', 'ensure_endpoint', 'ensure_role',
'ensure_user', 'ensure_user_role', 'ensure_tenant',
'ensure_project', 'ensure_service_provider',
'ensure_group', 'ensure_identity_provider',
'ensure_protocol', ensure_mapping',
'ensure_group_role']
required: true
insecure:
description:
- Explicitly allow client to perform "insecure" TLS
choices:
- false
- true
default: false
requirements: [ python-keystoneclient ]
author: Kevin Carter
"""
EXAMPLES = """
# Create an admin project
- keystone:
command: "ensure_project"
project_name: "admin"
domain_name: "Default"
description: "Admin project"
# Create a service project
- keystone:
command: "ensure_project"
project_name: "service"
description: "Service project"
# Create an admin user
- keystone:
command: "ensure_user"
user_name: "admin"
project_name: "admin"
password: "secrete"
email: "admin@some-domain.com"
# Create an admin role
- keystone:
command: "ensure_role"
role_name: "admin"
# Create a user
- keystone:
command: "ensure_user"
user_name: "glance"
project_name: "service"
password: "secrete"
domain_name: "Default"
email: "glance@some-domain.com"
# Add a role to a user
- keystone:
command: "ensure_user_role"
user_name: "glance"
project_name: "service"
role_name: "admin"
# Add a project role to a group
- keystone:
command: "ensure_group_role"
group_name: "fedgroup"
project_name: "fedproject"
role_name: "_member_"
# Create a service
- keystone:
command: "ensure_service"
service_name: "glance"
service_type: "image"
description: "Glance Image Service"
# Create an endpoint
- keystone:
command: "ensure_endpoint"
region_name: "RegionOne"
service_name: "glance"
service_type: "image"
endpoint_list:
- url: "http://127.0.0.1:9292"
interface: "public"
- url: "http://127.0.0.1:9292"
interface: "admin"
- url: "http://127.0.0.1:9292"
interface: "internal"
# Get project id
- keystone:
command: "get_project"
project_name: "admin"
# Get user id
- keystone:
command: "get_user"
user_name: "admin"
# Get role id
- keystone:
command: "get_role"
user_name: "admin"
"""
COMMAND_MAP = {
'get_tenant': {
'variables': [
'project_name',
'tenant_name'
]
},
'get_project': {
'variables': [
'project_name',
'tenant_name'
]
},
'get_user': {
'variables': [
'user_name'
]
},
'get_role': {
'variables': [
'role_name',
'project_name',
'tenant_name',
'user_name'
]
},
'ensure_service': {
'variables': [
'service_name',
'service_type',
'description'
]
},
'ensure_endpoint': {
'variables': [
'region_name',
'service_name',
'service_type',
'endpoint_list',
'state'
]
},
'ensure_role': {
'variables': [
'role_name'
]
},
'ensure_user': {
'variables': [
'project_name',
'tenant_name',
'user_name',
'password',
'email',
'domain_name'
]
},
'ensure_user_role': {
'variables': [
'user_name',
'project_name',
'tenant_name',
'role_name',
'domain_name'
]
},
'ensure_group_role': {
'variables': [
'group_name',
'project_name',
'role_name',
'domain_name'
]
},
'ensure_project': {
'variables': [
'project_name',
'tenant_name',
'description',
'domain_name'
]
},
'ensure_tenant': {
'variables': [
'project_name',
'tenant_name',
'description',
'domain_name'
]
},
'ensure_group': {
'variables': [
'group_name',
'domain_name'
]
},
'ensure_identity_provider': {
'variables': [
'idp_name',
'idp_remote_ids',
'idp_enabled'
]
},
'ensure_service_provider': {
'variables': [
'sp_name',
'sp_url',
'sp_auth_url',
'sp_enabled'
]
},
'ensure_protocol': {
'variables': [
'protocol_name',
'idp_name',
'mapping_name'
]
},
'ensure_mapping': {
'variables': [
'mapping_name',
'mapping_rules',
]
},
'ensure_domain': {
'variables': [
'domain_name',
'domain_enabled'
]
}
}
try:
from keystoneclient import exceptions as kexceptions
from keystoneclient import client
from keystoneauth1.identity import v3
from keystoneauth1 import session
except ImportError:
keystoneclient_found = False
else:
keystoneclient_found = True
class ManageKeystone(object):
def __init__(self, module):
"""Manage Keystone via Ansible."""
self.state_change = False
self.keystone = None
# Load AnsibleModule
self.module = module
def command_router(self):
"""Run the command as its provided to the module."""
command_name = self.module.params['command']
if command_name not in COMMAND_MAP:
self.failure(
error='No Command Found',
rc=2,
msg='Command [ %s ] was not found.' % command_name
)
action_command = COMMAND_MAP[command_name]
if hasattr(self, '%s' % command_name):
action = getattr(self, '%s' % command_name)
facts = action(variables=action_command['variables'])
if facts is None:
self.module.exit_json(changed=self.state_change)
else:
self.module.exit_json(
changed=self.state_change,
ansible_facts=facts
)
else:
self.failure(
error='Command not in ManageKeystone class',
rc=2,
msg='Method [ %s ] was not found.' % command_name
)
@staticmethod
def _facts(facts):
"""Return a dict for our Ansible facts.
:param facts: ``dict`` Dict with data to return
"""
return {'keystone_facts': facts}
def _get_vars(self, variables, required=None):
"""Return a dict of all variables as found within the module.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
:param required: ``list`` Name of variables that are required.
"""
return_dict = {}
for variable in variables:
return_dict[variable] = self.module.params.get(variable)
else:
if isinstance(required, list):
for var_name in required:
check = return_dict.get(var_name)
if check is None:
self.failure(
error='Missing [ %s ] from Task or found a None'
' value' % var_name,
rc=000,
msg='variables %s - available params [ %s ]'
% (variables, self.module.params)
)
return return_dict
def failure(self, error, rc, msg):
"""Return a Failure when running an Ansible command.
:param error: ``str`` Error that occurred.
:param rc: ``int`` Return code while executing an Ansible command.
:param msg: ``str`` Message to report.
"""
self.module.fail_json(msg=msg, rc=rc, err=error)
def _authenticate(self):
"""Return a keystone client object."""
required_vars = ['endpoint']
variables = [
'endpoint',
'ignore_catalog',
'login_user',
'login_password',
'login_project_name',
'login_tenant_name',
'login_user_domain_name',
'login_project_domain_name',
'token',
'insecure'
]
variables_dict = self._get_vars(variables, required=required_vars)
endpoint = variables_dict.pop('endpoint')
login_user = variables_dict.pop('login_user')
login_password = variables_dict.pop('login_password')
login_project_name = (variables_dict.pop('login_project_name', None) or
variables_dict.pop('login_tenant_name'))
user_domain_name = variables_dict.pop('login_user_domain_name',
'Default')
project_domain_name = variables_dict.pop('login_project_domain_name',
'Default')
token = variables_dict.pop('token')
insecure = variables_dict.pop('insecure')
if token is None:
if login_project_name is None:
self.failure(
error='Missing Project Name',
rc=2,
msg='If you do not specify a token you must use a project'
' name for authentication. Try adding'
' [ login_project_name ] to the task'
)
if login_password is None:
self.failure(
error='Missing Password',
rc=2,
msg='If you do not specify a token you must use a password'
' name for authentication. Try adding'
' [ login_password ] to the task'
)
if token:
self.keystone = client.Client(
insecure=insecure,
endpoint=endpoint,
token=token
)
else:
auth_args = {
'auth_url': endpoint,
'username': login_user,
'user_domain_name': user_domain_name,
'password': login_password,
'project_name': login_project_name,
'project_domain_name': project_domain_name,
}
client_args = {
'insecure': insecure,
}
if variables_dict.pop('ignore_catalog'):
client_args.update(endpoint_override=endpoint)
auth = v3.Password(**auth_args)
sess = session.Session(auth=auth, verify=(not insecure))
self.keystone = client.Client(session=sess,
**client_args)
def _get_domain_from_vars(self, variables):
# NOTE(sigmavirus24): Since we don't require domain, this will be None
# in the dictionary. When we pop it, we can't provide a default
# because 'domain' exists and is None. In order to use a default
# value, we need to use `or 'default'` here to make sure we default to
# the default domain. If we don't do it this way, Keystone throws a
# 401 Unauthorized which is just plain wrong.
domain_name = variables.pop('domain_name', None) or 'Default'
return self._get_domain(name=domain_name)
def _get_domain(self, name):
"""Return domain information.
:param str name: Name of the domain.
"""
for entry in self.keystone.domains.list():
if entry.name == name:
return entry
else:
return None
def _get_project(self, name):
"""Return project information.
Formerly, _get_tenant
:param name: ``str`` Name of the project.
"""
for entry in self.keystone.projects.list():
if entry.name == name:
return entry
else:
return None
def get_tenant(self, variables):
return self.get_project(variables)
def get_project(self, variables):
"""Return a project id.
This will return `None` if the ``name`` is not found.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
variables_dict = self._get_vars(variables)
project_name = (variables_dict.pop('project_name', None) or
variables_dict.pop('tenant_name'))
project = self._get_project(name=project_name)
if project is None:
self.failure(
error='project [ %s ] was not found.' % project_name,
rc=2,
msg='project was not found, does it exist?'
)
return self._facts(facts={'id': project.id})
def ensure_tenant(self, variables):
return self.ensure_project(variables)
def ensure_project(self, variables):
"""Create a new project within Keystone if it does not exist.
Returns the project ID on a successful run.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
variables_dict = self._get_vars(variables)
project_name = (variables_dict.pop('project_name', None) or
variables_dict.pop('tenant_name'))
project_description = variables_dict.pop('description')
if project_description is None:
project_description = 'Project %s' % project_name
domain = self._get_domain_from_vars(variables_dict)
project = self._get_project(name=project_name)
if project is None:
self.state_change = True
project = self.keystone.projects.create(
name=project_name,
description=project_description,
domain=domain,
enabled=True
)
return self._facts(facts={'id': project.id})
def _get_user(self, name, domain):
"""Return a user information.
This will return `None` if the ``name`` is not found.
:param name: ``str`` Name of the user.
"""
for entry in self.keystone.users.list(domain=domain):
if getattr(entry, 'name', None) == name:
return entry
else:
return None
def get_user(self, variables):
"""Return a project id.
This will return `None` if the ``name`` is not found.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
variables_dict = self._get_vars(variables, required=['user_name'])
user_name = variables_dict.pop('user_name')
domain = self._get_domain_from_vars(variables_dict)
user = self._get_user(name=user_name, domain=domain)
if user is None:
self.failure(
error='user [ %s ] was not found.' % user_name,
rc=2,
msg='user was not found, does it exist?'
)
return self._facts(facts={'id': user.id})
def ensure_user(self, variables):
"""Create a new user within Keystone if it does not exist.
Returns the user ID on a successful run.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
required_vars = ['user_name', 'password']
variables_dict = self._get_vars(variables, required=required_vars)
project_name = (variables_dict.pop('project_name', None) or
variables_dict.pop('tenant_name'))
password = variables_dict.pop('password')
user_name = variables_dict.pop('user_name')
email = variables_dict.pop('email')
domain = self._get_domain_from_vars(variables_dict)
project = self._get_project(name=project_name)
if project is None and project_name is not None:
self.failure(
error='project [ %s ] was not found.' % project_name,
rc=2,
msg='project was not found, does it exist?'
)
user = self._get_user(name=user_name, domain=domain)
if user is None:
self.state_change = True
user = self.keystone.users.create(
name=user_name,
password=password,
email=email,
domain=domain,
default_project=project
)
return self._facts(facts={'id': user.id})
def _get_role(self, name, domain):
"""Return a role by name.
This will return `None` if the ``name`` is not found.
:param name: ``str`` Name of the role.
:param domain: ``str`` ID of the domain
"""
for entry in self.keystone.roles.list(domain=domain):
if entry.name == name:
return entry
else:
return None
def _get_group(self, name, domain='Default'):
"""Return a group by name.
This will return `None` if the ``name`` is not found.
:param name: ``str`` Name of the role.
"""
for entry in self.keystone.groups.list(domain=domain):
if domain is None:
if entry.name == name:
return entry
else:
if entry.name == name and entry.domain_id == domain.id:
return entry
else:
return None
def get_role(self, variables):
"""Return a role by name.
This will return `None` if the ``name`` is not found.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
variables_dict = self._get_vars(variables, required=['role_name'])
role_name = variables_dict.pop('role_name')
domain = self._get_domain_from_vars(variables_dict)
role_data = self._get_role(name=role_name, domain=domain)
if role_data is None:
self.failure(
error='role [ %s ] was not found.' % role_name,
rc=2,
msg='role was not found, does it exist?'
)
return self._facts(facts={'id': role_data.id})
def _get_role_data(self, user_name, project_name, role_name, group_name,
domain):
if user_name is not None:
user = self._get_user(name=user_name, domain=domain)
if user is None:
self.failure(
error='user [ %s ] was not found.' % user_name,
rc=2,
msg='User was not found, does it exist?'
)
else:
user = None
project = self._get_project(name=project_name)
if project is None and project_name is not None:
self.failure(
error='project [ %s ] was not found.' % project_name,
rc=2,
msg='project was not found, does it exist?'
)
role = self._get_role(name=role_name, domain=domain)
if role is None:
self.failure(
error='role [ %s ] was not found.' % role_name,
rc=2,
msg='role was not found, does it exist?'
)
if group_name is not None:
group = self._get_group(name=group_name, domain=domain)
if group is None:
self.failure(
error='group [ %s ] was not found.' % group_name,
rc=2,
msg='group was not found, does it exist?'
)
else:
group = None
return user, project, role, group
def ensure_role(self, variables):
"""Create a new role within Keystone if it does not exist.
Returns the user ID on a successful run.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
variables_dict = self._get_vars(variables, required=['role_name'])
domain = self._get_domain_from_vars(variables_dict)
role_name = variables_dict.pop('role_name')
role = self._get_role(name=role_name, domain=domain)
if role is None:
self.state_change = True
role = self.keystone.roles.create(role_name)
return self._facts(facts={'id': role.id})
def _get_user_roles(self, name, user, project, domain):
role_list = self.keystone.roles.list(
user=user,
project=project,
domain=domain
)
for entry in role_list:
if entry.name == name:
return entry
else:
return None
def _get_group_roles(self, name, group, project, domain):
group_list = self.keystone.roles.list(
group=group,
project=project,
domain=domain
)
for entry in group_list:
if entry.name == name:
return entry
else:
return None
def ensure_user_role(self, variables):
self._authenticate()
required_vars = ['user_name', 'role_name']
variables_dict = self._get_vars(variables, required=required_vars)
domain = self._get_domain_from_vars(variables_dict)
user_name = variables_dict.pop('user_name')
# NOTE(sigmavirus24): Try to get the project_name, but
# don't error out on it. This will change when the playbooks are
# updated to use project_name instead of tenant_name
project_name = (variables_dict.pop('project_name', None) or
variables_dict.pop('tenant_name'))
role_name = variables_dict.pop('role_name')
if project_name is not None:
domain = None
user, project, role, group = self._get_role_data(
user_name=user_name, project_name=project_name,
role_name=role_name, group_name=None, domain=domain
)
user_role = self._get_user_roles(
name=role_name, user=user, project=project, domain=domain
)
if user_role is None:
self.state_change = True
self.keystone.roles.grant(
user=user, role=role, project=project, domain=domain
)
user_role = self._get_user_roles(
name=role_name, user=user, project=project, domain=domain
)
return self._facts(facts={'id': user_role.id})
def ensure_group_role(self, variables):
self._authenticate()
required_vars = ['group_name', 'project_name', 'role_name']
variables_dict = self._get_vars(variables, required=required_vars)
domain = self._get_domain_from_vars(variables_dict)
group_name = variables_dict.pop('group_name')
project_name = variables_dict.pop('project_name')
role_name = variables_dict.pop('role_name')
if project_name is not None:
domain = None
user, project, role, group = self._get_role_data(
group_name=group_name, project_name=project_name,
role_name=role_name, user_name=None, domain=domain
)
group_role = self._get_group_roles(
name=role_name, group=group, project=project, domain=domain
)
if group_role is None:
self.state_change = True
self.keystone.roles.grant(
group=group, role=role, project=project, domain=domain
)
group_role = self._get_group_roles(
name=role_name,
group=group,
project=project,
domain=domain
)
return self._facts(facts={'id': group_role.id})
def ensure_group(self, variables):
"""Create a new group within Keystone if it does not exist.
Returns the group ID on a successful run.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
required_vars = ['group_name', 'domain_name']
variables_dict = self._get_vars(variables, required=required_vars)
group_name = variables_dict.pop('group_name')
domain = self._get_domain_from_vars(variables_dict)
group = self._get_group(
name=group_name, domain=domain
)
if group is None:
self.state_change = True
group = self.keystone.groups.create(
name=group_name, domain=domain
)
return self._facts(facts={'id': group.id})
def _get_service(self, name, srv_type=None):
for entry in self.keystone.services.list():
if srv_type is not None:
if entry.type == srv_type and name == entry.name:
return entry
elif entry.name == name:
return entry
else:
return None
def ensure_service(self, variables):
"""Create a new service within Keystone if it does not exist.
Returns the service ID on a successful run.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
required_vars = ['service_name', 'service_type']
variables_dict = self._get_vars(variables, required=required_vars)
service_name = variables_dict.pop('service_name')
description = variables_dict.pop('description')
service_type = variables_dict.pop('service_type')
service = self._get_service(name=service_name, srv_type=service_type)
if service is None or service.type != service_type:
self.state_change = True
service = self.keystone.services.create(
name=service_name,
type=service_type,
description=description
)
return self._facts(facts={'id': service.id})
def _get_endpoint_by_details(self, region, service_id, interface):
""" Getting endpoints per complete definition
Returns the endpoint details for an endpoint matching
region, service id and interface.
:param interface: ``str`` 'public', 'admin' or 'internal' network
interface
:param service_id: service to which the endpoint belongs
:param region: geographic location of the endpoint
"""
for entry in self.keystone.endpoints.list():
check = [
entry.region == region,
entry.service_id == service_id,
entry.interface == interface
]
if all(check):
return entry
else:
return None
def _get_endpoint(self, region, url, interface):
""" Getting endpoints per URL
Returns the endpoint details for an endpoint matching
URL, region and interface.
This interface should be deprecated in next release.
"""
for entry in self.keystone.endpoints.list():
check = [
entry.region == region,
entry.url == url,
entry.interface == interface
]
if all(check):
return entry
else:
return None
def ensure_endpoint(self, variables):
"""Ensures the deletion/modification/addition of endpoints
within Keystone.
Returns the endpoint ID on a successful run.
:param variables: ``list`` List of all variables that are available to
use within the Keystone Command.
"""
self._authenticate()
required_vars = [
'region_name',
'service_name',
'service_type',
'endpoint_list'
]
variables_dict = self._get_vars(variables, required=required_vars)
service_name = variables_dict.pop('service_name')
service_type = variables_dict.pop('service_type')
region = variables_dict.pop('region_name')
endpoint_list = variables_dict.pop('endpoint_list')
state = variables_dict.pop('state')
service = self._get_service(name=service_name, srv_type=service_type)
if service is None:
self.failure(
error='service [ %s ] was not found.' % service_name,
rc=2,
msg='Service was not found, does it exist?'
)
endpoints = {}
for endpoint_dict in endpoint_list:
url = endpoint_dict.pop('url')
interface = endpoint_dict.pop('interface')
endpoint = self._get_endpoint(
region=region,
url=url,
interface=interface
)
if state == 'present':
''' Creating an endpoint for this url
(if it does not exist) or updating
an existing endpoint that matches
the service type, name, interface
and region.
'''
similar_endpoint = self._get_endpoint_by_details(
region=region,
service_id=service.id,
interface=interface
)
if similar_endpoint is not None:
if similar_endpoint.url != url:
self.state_change = True
endpoint = self.keystone.endpoints.update(
endpoint=similar_endpoint,
url=url
)
elif endpoint is None:
self.state_change = True
endpoint = self.keystone.endpoints.create(
region=region,
service=service,
url=url,
interface=interface
)
# The update state is deprecated and should be removed in Q
elif state == 'update':
''' Checking if there is a similar endpoint with a
different url. Update it if there is one, create
if there is none. Update is deprecated and will
be removed in Q. "Present" achieves the same
result.
'''
similar_endpoint = self._get_endpoint_by_details(
region=region,
service_id=service.id,
interface=interface
)
if similar_endpoint is None:
self.state_change = True
endpoint = self.keystone.endpoints.create(
region=region,
service=service,
url=url,
interface=interface
)
elif similar_endpoint.url != url:
self.state_change = True
endpoint = self.keystone.endpoints.update(
endpoint=similar_endpoint,
url=url
)
elif state == 'absent':
if endpoint is not None:
self.state_change = True
result = self.keystone.endpoints.delete(endpoint.id)
if result[0].status_code != 204:
module.fail()
if state != 'absent':
endpoints[interface] = endpoint
return self._facts({'%sid' % interface: endpoint.id
for interface, endpoint in endpoints.items()})
else:
return self._facts({})
def _ensure_generic(self, manager, required_vars, variables):
"""Try and create a new 'thing' in keystone.
Thing type is determined by the manager passed in.
:param: manager - openstack object manager eg self.keystone.groups
:param: required_vars - dictionary:
ansible module argument name : manager argument name
eg {'group_name': 'name'}
:returns: Facts dictionary with things =
<list of things converted to dict>
TODO: make this handle updates as well as creates
TODO (maybe, if we decide to use this module long term):
migrate other ensures to use this
"""
# Get values for variables
variables_dict = self._get_vars(variables,
required=required_vars.keys())
# Translate ansible module argument names to manager expected names
args_dict = {required_vars[k]: v for k, v in variables_dict.items()}
try:
manager.create(**args_dict)
self.state_change = True
except kexceptions.Conflict:
self.state_change = False
try:
return self._facts(facts={
manager.collection_key:
[x.to_dict() for x in manager.list()]
})
except TypeError:
# some managers require arguments to their list functions :/
# return no facts in this case.
return self._facts(facts={})
def ensure_identity_provider(self, variables):
self._authenticate()
return self._ensure_generic(
manager=self.keystone.federation.identity_providers,
required_vars={'idp_name': 'id',
'idp_remote_ids': 'remote_ids',
'idp_enabled': 'enabled'},
variables=variables
)
def ensure_service_provider(self, variables):
self._authenticate()
return self._ensure_generic(
manager=self.keystone.federation.service_providers,
required_vars={'sp_name': 'id',
'sp_auth_url': 'auth_url',
'sp_url': 'sp_url',
'sp_enabled': 'enabled'},
variables=variables
)
def ensure_protocol(self, variables):
"""Facts not returned
This is because you can't list protocols without
specifying an identity provider
"""
self._authenticate()
return self._ensure_generic(
manager=self.keystone.federation.protocols,
required_vars={'protocol_name': 'protocol_id',
'idp_name': 'identity_provider',
'mapping_name': 'mapping'},
variables=variables
)
def ensure_mapping(self, variables):
self._authenticate()
return self._ensure_generic(
manager=self.keystone.federation.mappings,
required_vars={'mapping_name': 'mapping_id',
'mapping_rules': 'rules'},
variables=variables
)
def ensure_domain(self, variables):
self._authenticate()
return self._ensure_generic(
manager=self.keystone.domains,
required_vars={'domain_name': 'name',
'domain_enabled': 'enabled'},
variables=variables
)
# TODO(evrardjp): Deprecate state=update in Q.
def main():
module = AnsibleModule(
argument_spec=dict(
login_user=dict(
required=False
),
login_user_domain_name=dict(
required=False,
default='Default'
),
login_password=dict(
required=False,
no_log=True
),
login_tenant_name=dict(
required=False
),
login_project_name=dict(
required=False
),
login_project_domain_name=dict(
required=False,
default='Default'
),
token=dict(
required=False
),
password=dict(
required=False,
no_log=True
),
endpoint=dict(
required=True,
),
ignore_catalog=dict(
required=False
),
user_name=dict(
required=False
),
tenant_name=dict(
required=False
),
project_name=dict(
required=False
),
domain_name=dict(
required=False
),
role_name=dict(
required=False
),
service_name=dict(
required=False
),
region_name=dict(
required=False
),
description=dict(
required=False
),
email=dict(
required=False
),
service_type=dict(
required=False
),
endpoint_list=dict(
required=False,
type='list'
),
command=dict(
required=True,
choices=COMMAND_MAP.keys()
),
insecure=dict(
default=False,
required=False,
type='bool'
),
return_code=dict(
type='str',
default='0'
),
group_name=dict(
type='str',
required=False
),
idp_remote_ids=dict(
type='list',
required=False,
),
idp_name=dict(
type='str',
required=False,
),
idp_enabled=dict(
type='bool',
default=True,
required=False,
),
sp_name=dict(
type='str',
required=False,
),
sp_auth_url=dict(
type='str',
required=False,
),
sp_url=dict(
type='str',
required=False,
),
sp_enabled=dict(
type='bool',
default=True,
required=False,
),
protocol_name=dict(
type='str',
required=False,
),
mapping_name=dict(
type='str',
required=False,
),
mapping_rules=dict(
type='list',
required=False,
),
domain_enabled=dict(
type='bool',
required=False,
default=True
),
state=dict(
choices=['present', 'absent', 'update'],
required=False,
default='present'
)
),
supports_check_mode=False,
mutually_exclusive=[
['token', 'login_user'],
['token', 'login_password'],
['token', 'login_tenant_name']
]
)
km = ManageKeystone(module=module)
if not keystoneclient_found:
km.failure(
error='python-keystoneclient is missing',
rc=2,
msg='keystone client was not importable, is it installed?'
)
return_code = module.params.get('return_code', '').split(',')
module.params['return_code'] = return_code
km.command_router()
# import module snippets
from ansible.module_utils.basic import * # NOQA
if __name__ == '__main__':
main()