Merge "Add keystoneng module"
diff --git a/_modules/keystoneng.py b/_modules/keystoneng.py
new file mode 100644
index 0000000..66e0ac7
--- /dev/null
+++ b/_modules/keystoneng.py
@@ -0,0 +1,1418 @@
+# -*- coding: utf-8 -*-
+'''
+Module for handling openstack keystone calls.
+
+:optdepends: - keystoneclient Python adapter
+:configuration: This module is not usable until the following are specified
+ either in a pillar or in the minion's config file:
+
+ .. code-block:: yaml
+
+ keystone.user: admin
+ keystone.password: verybadpass
+ keystone.tenant: admin
+ keystone.tenant_id: f80919baedab48ec8931f200c65a50df
+ keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
+
+ OR (for token based authentication)
+
+ .. code-block:: yaml
+
+ keystone.token: 'ADMIN'
+ keystone.endpoint: 'http://127.0.0.1:35357/v2.0'
+
+ If configuration for multiple openstack accounts is required, they can be
+ set up as different configuration profiles. For example:
+
+ .. code-block:: yaml
+
+ openstack1:
+ keystone.user: admin
+ keystone.password: verybadpass
+ keystone.tenant: admin
+ keystone.tenant_id: f80919baedab48ec8931f200c65a50df
+ keystone.auth_url: 'http://127.0.0.1:5000/v2.0/'
+
+ openstack2:
+ keystone.user: admin
+ keystone.password: verybadpass
+ keystone.tenant: admin
+ keystone.tenant_id: f80919baedab48ec8931f200c65a50df
+ keystone.auth_url: 'http://127.0.0.2:5000/v2.0/'
+
+ openstack_version3:
+ keystone.user: admin
+ keystone.password: verybadpass
+ keystone.tenant: admin
+ keystone.tenant_id: f80919baedab48ec8931f200c65a50df
+ keystone.auth_url: 'http://127.0.0.2:5000/v3'
+
+ openstack_nonversioned:
+ keystone.user: admin
+ keystone.password: verybadpass
+ keystone.tenant: admin
+ keystone.tenant_id: f80919baedab48ec8931f200c65a50df
+ keystone.auth_url: 'http://127.0.0.2:5000'
+
+ With this configuration in place, any of the keystone functions can make use
+ of a configuration profile by declaring it explicitly.
+ For example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.tenant_list profile=openstack1
+'''
+
+# Import Python libs
+from __future__ import absolute_import
+import logging
+
+# Import Salt Libs
+import salt.utils.http
+
+# Import 3rd-party libs
+from salt.ext import six
+HAS_KEYSTONE = False
+try:
+ # pylint: disable=import-error
+ from keystoneclient import client
+ from keystoneclient.v2_0 import client as keystoneclient_v2
+ import keystoneclient.exceptions
+ HAS_KEYSTONE = True
+ from keystoneclient.v3 import client as keystoneclient_v3
+ from keystoneclient import discover
+ from keystoneauth1 import session
+ from keystoneauth1.identity import generic
+ from keystoneauth1 import token_endpoint
+ # pylint: enable=import-error
+except ImportError:
+ pass
+
+log = logging.getLogger(__name__)
+
+
+def __virtual__():
+ '''
+ Only load this module if keystone
+ is installed on this minion.
+ '''
+ if HAS_KEYSTONE:
+ return 'keystoneng'
+ return (False, 'keystone execution module cannot be loaded: keystoneclient python library not available.')
+
+__opts__ = {}
+
+
+def _get_kwargs(profile=None, **connection_args):
+ '''
+ get connection args
+ '''
+ if profile:
+ prefix = profile + ":keystone."
+ else:
+ prefix = "keystone."
+
+ def get(key, default=None):
+ '''
+ look in connection_args first, then default to config file
+ '''
+ return connection_args.get('connection_' + key,
+ __salt__['config.get'](prefix + key, default))
+
+ user = get('user', 'admin')
+ password = get('password', 'ADMIN')
+ tenant = get('tenant', 'admin')
+ tenant_id = get('tenant_id')
+ auth_url = get('auth_url', 'http://127.0.0.1:5000/v2.0/')
+ insecure = get('insecure', False)
+ token = get('token')
+ endpoint = get('endpoint', 'http://127.0.0.1:5000/v2.0')
+ if token:
+ kwargs = {'token': token,
+ 'endpoint': endpoint}
+ else:
+ kwargs = {'username': user,
+ 'password': password,
+ 'tenant_name': tenant,
+ 'tenant_id': tenant_id,
+ 'auth_url': auth_url}
+ # 'insecure' keyword not supported by all v2.0 keystone clients
+ # this ensures it's only passed in when defineda
+ if get('user_id'): kwargs['user_id']=get('user_id')
+ if get('user_domain_id'): kwargs['user_domain_id']=get('user_domain_id')
+ if get('domain_id'): kwargs['domain_id']=get('domain_id')
+ if get('domain_name'): kwargs['domain_name']=get('domain_name')
+ if get('project_id'): kwargs['project_id']=get('project_id')
+ if get('project_name'): kwargs['project_name']=get('project_name')
+ if get('project_domain_id'): kwargs['project_domain_id']=get('project_domain_id')
+ if get('region_name'): kwargs['region_name']=get('region_name')
+ if get('endpoint'): kwargs['endpoint']=get('endpoint')
+ if get('timeout'): kwargs['timeout']=get('timeout')
+ if get('user_domain_name'): kwargs['user_domain_name']=get('user_domain_name')
+ if get('project_domain_name'): kwargs['project_domain_name']=get('project_domain_name')
+ kwargs['insecure'] = get('insecure', False)
+ if get('version'):
+ version_list = str(get('version')).split('.')
+ if len(version_list) == 2:
+ kwargs['version'] = (version_list[0], version_list[1])
+ else:
+ kwargs['version'] = (version_list[0])
+
+ return kwargs
+
+
+def _client_version(keystone_client):
+ '''
+ Returns keystone client version
+ '''
+ if isinstance(keystone_client, keystoneclient_v3.Client):
+ return 3
+ if isinstance(keystone_client, keystoneclient_v2.Client):
+ return 2
+ return None
+
+
+def _project_mgr(keystone_client):
+ '''
+ Returns keystoneclient.v3.Client.projects object if keystone client version > 2
+ or keystoneclient.v2_0.Client.tenants for other cases.
+ '''
+ if _client_version(keystone_client) > 2:
+ return keystone_client.projects
+ return keystone_client.tenants
+
+
+def api_version(profile=None, **connection_args):
+ '''
+ Returns the API version derived from endpoint's response.
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.api_version
+ '''
+ keystone_client = auth(profile, **connection_args)
+ if isinstance(keystone_client, keystoneclient_v3.Client):
+ return 3
+ if isinstance(keystone_client, keystoneclient_v2.Client):
+ return 2
+ return None
+
+
+def auth(profile=None, **connection_args):
+ '''
+ Set up keystone credentials. Only intended to be used within Keystone-enabled modules.
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.auth
+ '''
+ kwargs = _get_kwargs(profile=profile, **connection_args)
+ if 'token' in kwargs:
+ auth = token_endpoint.Token(endpoint=kwargs['endpoint'], token=kwargs['token'])
+ else:
+ # keystoneauth1 Password class does not accept some args. Therefore remove it from args for auth.
+ auth_connection_args=kwargs.copy()
+ auth_connection_args.pop('region_name', None)
+ auth_connection_args.pop('version', None)
+ auth_connection_args.pop('insecure', None)
+ auth = generic.Password(**auth_connection_args)
+ if 'insecure' in kwargs:
+ certs_verify = False
+ else:
+ certs_verify = True
+ sess = session.Session(auth=auth, verify=certs_verify)
+ keystone_client=client.Client(session=sess, **kwargs)
+ return keystone_client
+
+
+def ec2_credentials_create(user_id=None, name=None,
+ tenant_id=None, tenant=None,
+ profile=None, **connection_args):
+ '''
+ Create EC2-compatible credentials for user per tenant
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.ec2_credentials_create name=admin tenant=admin
+
+ salt '*' keystone.ec2_credentials_create \
+ user_id=c965f79c4f864eaaa9c3b41904e67082 \
+ tenant_id=722787eb540849158668370dc627ec5f
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if name:
+ user_id = user_get(name=name, profile=profile,
+ **connection_args)[name]['id']
+ if not user_id:
+ return {'Error': 'Could not resolve User ID'}
+
+ if tenant:
+ tenant_id = tenant_get(name=tenant, profile=profile,
+ **connection_args)[tenant]['id']
+ if not tenant_id:
+ return {'Error': 'Could not resolve Tenant ID'}
+
+ newec2 = kstone.ec2.create(user_id, tenant_id)
+ return {'access': newec2.access,
+ 'secret': newec2.secret,
+ 'tenant_id': newec2.tenant_id,
+ 'user_id': newec2.user_id}
+
+
+def ec2_credentials_delete(user_id=None, name=None, access_key=None,
+ profile=None, **connection_args):
+ '''
+ Delete EC2-compatible credentials
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.ec2_credentials_delete \
+ 860f8c2c38ca4fab989f9bc56a061a64 access_key=5f66d2f24f604b8bb9cd28886106f442
+
+ salt '*' keystone.ec2_credentials_delete name=admin \
+ access_key=5f66d2f24f604b8bb9cd28886106f442
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if name:
+ user_id = user_get(name=name, profile=None, **connection_args)[name]['id']
+ if not user_id:
+ return {'Error': 'Could not resolve User ID'}
+ kstone.ec2.delete(user_id, access_key)
+ return 'ec2 key "{0}" deleted under user id "{1}"'.format(access_key,
+ user_id)
+
+
+def ec2_credentials_get(user_id=None, name=None, access=None,
+ profile=None, **connection_args):
+ '''
+ Return ec2_credentials for a user (keystone ec2-credentials-get)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.ec2_credentials_get c965f79c4f864eaaa9c3b41904e67082 access=722787eb540849158668370
+ salt '*' keystone.ec2_credentials_get user_id=c965f79c4f864eaaa9c3b41904e67082 access=722787eb540849158668370
+ salt '*' keystone.ec2_credentials_get name=nova access=722787eb540849158668370dc627ec5f
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+ if name:
+ for user in kstone.users.list():
+ if user.name == name:
+ user_id = user.id
+ break
+ if not user_id:
+ return {'Error': 'Unable to resolve user id'}
+ if not access:
+ return {'Error': 'Access key is required'}
+ ec2_credentials = kstone.ec2.get(user_id=user_id, access=access,
+ profile=profile, **connection_args)
+ ret[ec2_credentials.user_id] = {'user_id': ec2_credentials.user_id,
+ 'tenant': ec2_credentials.tenant_id,
+ 'access': ec2_credentials.access,
+ 'secret': ec2_credentials.secret}
+ return ret
+
+
+def ec2_credentials_list(user_id=None, name=None, profile=None,
+ **connection_args):
+ '''
+ Return a list of ec2_credentials for a specific user (keystone ec2-credentials-list)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.ec2_credentials_list 298ce377245c4ec9b70e1c639c89e654
+ salt '*' keystone.ec2_credentials_list user_id=298ce377245c4ec9b70e1c639c89e654
+ salt '*' keystone.ec2_credentials_list name=jack
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+ if name:
+ for user in kstone.users.list():
+ if user.name == name:
+ user_id = user.id
+ break
+ if not user_id:
+ return {'Error': 'Unable to resolve user id'}
+ for ec2_credential in kstone.ec2.list(user_id):
+ ret[ec2_credential.user_id] = {'user_id': ec2_credential.user_id,
+ 'tenant_id': ec2_credential.tenant_id,
+ 'access': ec2_credential.access,
+ 'secret': ec2_credential.secret}
+ return ret
+
+
+def endpoint_get(service, region=None, profile=None, interface=None, **connection_args):
+ '''
+ Return a specific endpoint (keystone endpoint-get)
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt 'v2' keystone.endpoint_get nova [region=RegionOne]
+
+ salt 'v3' keystone.endpoint_get nova interface=admin [region=RegionOne]
+ '''
+ auth(profile, **connection_args)
+ services = service_list(profile, **connection_args)
+ if service not in services:
+ return {'Error': 'Could not find the specified service'}
+ service_id = services[service]['id']
+ endpoints = endpoint_list(profile, **connection_args)
+
+ e = [_f for _f in [e
+ if e['service_id'] == service_id and
+ (e['region'] == region if region else True) and
+ (e['interface'] == interface if interface else True)
+ else None for e in endpoints.values()] if _f]
+ if len(e) > 1:
+ return {'Error': 'Multiple endpoints found ({0}) for the {1} service. Please specify region.'.format(e, service)}
+ if len(e) == 1:
+ return e[0]
+ return {'Error': 'Could not find endpoint for the specified service'}
+
+
+def endpoint_list(profile=None, **connection_args):
+ '''
+ Return a list of available endpoints (keystone endpoints-list)
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.endpoint_list
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+
+ for endpoint in kstone.endpoints.list():
+ ret[endpoint.id] = dict((value, getattr(endpoint, value)) for value in dir(endpoint)
+ if not value.startswith('_') and
+ isinstance(getattr(endpoint, value), (six.string_types, dict, bool)))
+ return ret
+
+
+def endpoint_create(service, publicurl=None, internalurl=None, adminurl=None,
+ region=None, profile=None, url=None, interface=None, **connection_args):
+ '''
+ Create an endpoint for an Openstack service
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt 'v2' keystone.endpoint_create nova 'http://public/url' 'http://internal/url' 'http://adminurl/url' region
+
+ salt 'v3' keystone.endpoint_create nova url='http://public/url' interface='public' region='RegionOne'
+ '''
+ kstone = auth(profile, **connection_args)
+ keystone_service = service_get(name=service, profile=profile,
+ **connection_args)
+ if not keystone_service or 'Error' in keystone_service:
+ return {'Error': 'Could not find the specified service'}
+
+ if _client_version(kstone) > 2:
+ kstone.endpoints.create(service=keystone_service[service]['id'],
+ region_id=region,
+ url=url,
+ interface=interface)
+ else:
+ kstone.endpoints.create(region=region,
+ service_id=keystone_service[service]['id'],
+ publicurl=publicurl,
+ adminurl=adminurl,
+ internalurl=internalurl)
+ return endpoint_get(service, region, profile, interface, **connection_args)
+
+
+def endpoint_delete(service, region=None, profile=None, interface=None, **connection_args):
+ '''
+ Delete endpoints of an Openstack service
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt 'v2' keystone.endpoint_delete nova [region=RegionOne]
+
+ salt 'v3' keystone.endpoint_delete nova interface=admin [region=RegionOne]
+ '''
+ kstone = auth(profile, **connection_args)
+ endpoint = endpoint_get(service, region, profile, interface, **connection_args)
+ if not endpoint or 'Error' in endpoint:
+ return {'Error': 'Could not find any endpoints for the service'}
+ kstone.endpoints.delete(endpoint['id'])
+ endpoint = endpoint_get(service, region, profile, interface, **connection_args)
+ if not endpoint or 'Error' in endpoint:
+ return True
+
+
+def role_create(name, profile=None, **connection_args):
+ '''
+ Create a named role.
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.role_create admin
+ '''
+
+ kstone = auth(profile, **connection_args)
+ if 'Error' not in role_get(name=name, profile=profile, **connection_args):
+ return {'Error': 'Role "{0}" already exists'.format(name)}
+ kstone.roles.create(name)
+ return role_get(name=name, profile=profile, **connection_args)
+
+
+def role_delete(role_id=None, name=None, profile=None,
+ **connection_args):
+ '''
+ Delete a role (keystone role-delete)
+
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.role_delete c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.role_delete role_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.role_delete name=admin
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if name:
+ for role in kstone.roles.list():
+ if role.name == name:
+ role_id = role.id
+ break
+ if not role_id:
+ return {'Error': 'Unable to resolve role id'}
+
+ role = kstone.roles.get(role_id)
+ kstone.roles.delete(role)
+
+ ret = 'Role ID {0} deleted'.format(role_id)
+ if name:
+ ret += ' ({0})'.format(name)
+ return ret
+
+
+def role_get(role_id=None, name=None, profile=None, **connection_args):
+ '''
+ Return a specific roles (keystone role-get)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.role_get c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.role_get role_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.role_get name=nova
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+ if name:
+ for role in kstone.roles.list():
+ if role.name == name:
+ role_id = role.id
+ break
+ if not role_id:
+ return {'Error': 'Unable to resolve role id'}
+ role = kstone.roles.get(role_id)
+
+ ret[role.name] = {'id': role.id,
+ 'name': role.name}
+ return ret
+
+
+def role_list(profile=None, **connection_args):
+ '''
+ Return a list of available roles (keystone role-list)
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.role_list
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+ for role in kstone.roles.list():
+ ret[role.name] = dict((value, getattr(role, value)) for value in dir(role)
+ if not value.startswith('_') and
+ isinstance(getattr(role, value), (six.string_types, dict, bool)))
+ return ret
+
+
+def service_create(name, service_type, description=None, profile=None,
+ **connection_args):
+ '''
+ Add service to Keystone service catalog
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.service_create nova compute \
+'OpenStack Compute Service'
+ '''
+ kstone = auth(profile, **connection_args)
+ service = kstone.services.create(name, service_type, description=description)
+ return service_get(service.id, profile=profile, **connection_args)
+
+
+def service_delete(service_id=None, name=None, profile=None, **connection_args):
+ '''
+ Delete a service from Keystone service catalog
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.service_delete c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.service_delete name=nova
+ '''
+ kstone = auth(profile, **connection_args)
+ if name:
+ service_id = service_get(name=name, profile=profile,
+ **connection_args)[name]['id']
+ kstone.services.delete(service_id)
+ return 'Keystone service ID "{0}" deleted'.format(service_id)
+
+
+def service_get(service_id=None, name=None, profile=None, **connection_args):
+ '''
+ Return a specific services (keystone service-get)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.service_get c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.service_get service_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.service_get name=nova
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+ if name:
+ for service in kstone.services.list():
+ if service.name == name:
+ service_id = service.id
+ break
+ if not service_id:
+ return {'Error': 'Unable to resolve service id'}
+ service = kstone.services.get(service_id)
+ ret[service.name] = dict((value, getattr(service, value)) for value in dir(service)
+ if not value.startswith('_') and
+ isinstance(getattr(service, value), (six.string_types, dict, bool)))
+ return ret
+
+
+def service_list(profile=None, **connection_args):
+ '''
+ Return a list of available services (keystone services-list)
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.service_list
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+ for service in kstone.services.list():
+ ret[service.name] = dict((value, getattr(service, value)) for value in dir(service)
+ if not value.startswith('_') and
+ isinstance(getattr(service, value), (six.string_types, dict, bool)))
+ return ret
+
+
+def tenant_create(name, description=None, enabled=True, profile=None,
+ **connection_args):
+ '''
+ Create a keystone tenant
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.tenant_create nova description='nova tenant'
+ salt '*' keystone.tenant_create test enabled=False
+ '''
+ kstone = auth(profile, **connection_args)
+ new = _project_mgr(kstone).create(name, description, enabled)
+ return tenant_get(new.id, profile=profile, **connection_args)
+
+
+def project_create(name, domain, description=None, enabled=True, profile=None,
+ **connection_args):
+ '''
+ Create a keystone project.
+ Overrides keystone tenant_create form api V2. For keystone api V3.
+
+ .. versionadded:: 2016.11.0
+
+ name
+ The project name, which must be unique within the owning domain.
+
+ domain
+ The domain name.
+
+ description
+ The project description.
+
+ enabled
+ Enables or disables the project.
+
+ profile
+ Configuration profile - if configuration for multiple openstack accounts required.
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.project_create nova default description='Nova Compute Project'
+ salt '*' keystone.project_create test default enabled=False
+ '''
+ kstone = auth(profile, **connection_args)
+ new = _project_mgr(kstone).create(name=name, domain=domain,
+ description=description, enabled=enabled)
+ return tenant_get(new.id, profile=profile, **connection_args)
+
+
+def tenant_delete(tenant_id=None, name=None, profile=None, **connection_args):
+ '''
+ Delete a tenant (keystone tenant-delete)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.tenant_delete c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.tenant_delete tenant_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.tenant_delete name=demo
+ '''
+ kstone = auth(profile, **connection_args)
+ if name:
+ for tenant in _project_mgr(kstone).list():
+ if tenant.name == name:
+ tenant_id = tenant.id
+ break
+ if not tenant_id:
+ return {'Error': 'Unable to resolve tenant id'}
+ _project_mgr(kstone).delete(tenant_id)
+ ret = 'Tenant ID {0} deleted'.format(tenant_id)
+ if name:
+
+ ret += ' ({0})'.format(name)
+ return ret
+
+
+def project_delete(project_id=None, name=None, profile=None, **connection_args):
+ '''
+ Delete a project (keystone project-delete).
+ Overrides keystone tenant-delete form api V2. For keystone api V3 only.
+
+ .. versionadded:: 2016.11.0
+
+ project_id
+ The project id.
+
+ name
+ The project name.
+
+ profile
+ Configuration profile - if configuration for multiple openstack accounts required.
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.project_delete c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.project_delete project_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.project_delete name=demo
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if _client_version(kstone) > 2:
+ return tenant_delete(tenant_id=project_id, name=name, profile=None, **connection_args)
+ else:
+ return False
+
+
+def tenant_get(tenant_id=None, name=None, profile=None,
+ **connection_args):
+ '''
+ Return a specific tenants (keystone tenant-get)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.tenant_get c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.tenant_get tenant_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.tenant_get name=nova
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+
+ if name:
+ for tenant in _project_mgr(kstone).list():
+ if tenant.name == name:
+ tenant_id = tenant.id
+ break
+ if not tenant_id:
+ return {'Error': 'Unable to resolve tenant id'}
+ tenant = _project_mgr(kstone).get(tenant_id)
+ ret[tenant.name] = dict((value, getattr(tenant, value)) for value in dir(tenant)
+ if not value.startswith('_') and
+ isinstance(getattr(tenant, value), (six.string_types, dict, bool)))
+ return ret
+
+
+def project_get(project_id=None, name=None, profile=None, **connection_args):
+ '''
+ Return a specific projects (keystone project-get)
+ Overrides keystone tenant-get form api V2.
+ For keystone api V3 only.
+
+ .. versionadded:: 2016.11.0
+
+ project_id
+ The project id.
+
+ name
+ The project name.
+
+ profile
+ Configuration profile - if configuration for multiple openstack accounts required.
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.project_get c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.project_get project_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.project_get name=nova
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if _client_version(kstone) > 2:
+ return tenant_get(tenant_id=project_id, name=name, profile=None, **connection_args)
+ else:
+ return False
+
+
+def tenant_list(profile=None, **connection_args):
+ '''
+ Return a list of available tenants (keystone tenants-list)
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.tenant_list
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+
+ for tenant in _project_mgr(kstone).list():
+ ret[tenant.name] = dict((value, getattr(tenant, value)) for value in dir(tenant)
+ if not value.startswith('_') and
+ isinstance(getattr(tenant, value), (six.string_types, dict, bool)))
+ return ret
+
+
+def project_list(profile=None, **connection_args):
+ '''
+ Return a list of available projects (keystone projects-list).
+ Overrides keystone tenants-list form api V2.
+ For keystone api V3 only.
+
+ .. versionadded:: 2016.11.0
+
+ profile
+ Configuration profile - if configuration for multiple openstack accounts required.
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.project_list
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if _client_version(kstone) > 2:
+ return tenant_list(profile, **connection_args)
+ else:
+ return False
+
+
+def tenant_update(tenant_id=None, name=None, description=None,
+ enabled=None, profile=None, **connection_args):
+ '''
+ Update a tenant's information (keystone tenant-update)
+ The following fields may be updated: name, description, enabled.
+ Can only update name if targeting by ID
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.tenant_update name=admin enabled=True
+ salt '*' keystone.tenant_update c965f79c4f864eaaa9c3b41904e67082 name=admin email=admin@domain.com
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if not tenant_id:
+ for tenant in _project_mgr(kstone).list():
+ if tenant.name == name:
+ tenant_id = tenant.id
+ break
+ if not tenant_id:
+ return {'Error': 'Unable to resolve tenant id'}
+
+ tenant = _project_mgr(kstone).get(tenant_id)
+ if not name:
+ name = tenant.name
+ if not description:
+ description = tenant.description
+ if enabled is None:
+ enabled = tenant.enabled
+ updated = _project_mgr(kstone).update(tenant_id, name=name, description=description, enabled=enabled)
+
+ return dict((value, getattr(updated, value)) for value in dir(updated)
+ if not value.startswith('_') and
+ isinstance(getattr(updated, value), (six.string_types, dict, bool)))
+
+
+def project_update(project_id=None, name=None, description=None,
+ enabled=None, profile=None, **connection_args):
+ '''
+ Update a tenant's information (keystone project-update)
+ The following fields may be updated: name, description, enabled.
+ Can only update name if targeting by ID
+
+ Overrides keystone tenant_update form api V2.
+ For keystone api V3 only.
+
+ .. versionadded:: 2016.11.0
+
+ project_id
+ The project id.
+
+ name
+ The project name, which must be unique within the owning domain.
+
+ description
+ The project description.
+
+ enabled
+ Enables or disables the project.
+
+ profile
+ Configuration profile - if configuration for multiple openstack accounts required.
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.project_update name=admin enabled=True
+ salt '*' keystone.project_update c965f79c4f864eaaa9c3b41904e67082 name=admin email=admin@domain.com
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if _client_version(kstone) > 2:
+ return tenant_update(tenant_id=project_id, name=name, description=description,
+ enabled=enabled, profile=profile, **connection_args)
+ else:
+ return False
+
+
+def token_get(profile=None, **connection_args):
+ '''
+ Return the configured tokens (keystone token-get)
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.token_get c965f79c4f864eaaa9c3b41904e67082
+ '''
+ kstone = auth(profile, **connection_args)
+ token = kstone.service_catalog.get_token()
+ return {'id': token['id'],
+ 'expires': token['expires'],
+ 'user_id': token['user_id'],
+ 'tenant_id': token['tenant_id']}
+
+
+def user_list(profile=None, **connection_args):
+ '''
+ Return a list of available users (keystone user-list)
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_list
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+ for user in kstone.users.list():
+ ret[user.name] = dict((value, getattr(user, value, None)) for value in dir(user)
+ if not value.startswith('_') and
+ isinstance(getattr(user, value, None), (six.string_types, dict, bool)))
+ tenant_id = getattr(user, 'tenantId', None)
+ if tenant_id:
+ ret[user.name]['tenant_id'] = tenant_id
+ return ret
+
+
+def user_get(user_id=None, name=None, profile=None, **connection_args):
+ '''
+ Return a specific users (keystone user-get)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_get c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.user_get user_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.user_get name=nova
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+ if name:
+ for user in kstone.users.list():
+ if user.name == name:
+ user_id = user.id
+ break
+ if not user_id:
+ return {'Error': 'Unable to resolve user id'}
+ try:
+ user = kstone.users.get(user_id)
+ except keystoneclient.exceptions.NotFound:
+ msg = 'Could not find user \'{0}\''.format(user_id)
+ log.error(msg)
+ return {'Error': msg}
+
+ ret[user.name] = dict((value, getattr(user, value, None)) for value in dir(user)
+ if not value.startswith('_') and
+ isinstance(getattr(user, value, None), (six.string_types, dict, bool)))
+
+ tenant_id = getattr(user, 'tenantId', None)
+ if tenant_id:
+ ret[user.name]['tenant_id'] = tenant_id
+ return ret
+
+
+def user_create(name, password, email, tenant_id=None,
+ enabled=True, profile=None, project_id=None, description=None, **connection_args):
+ '''
+ Create a user (keystone user-create)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_create name=jack password=zero email=jack@halloweentown.org \
+ tenant_id=a28a7b5a999a455f84b1f5210264375e enabled=True
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if _client_version(kstone) > 2:
+ if tenant_id and not project_id:
+ project_id = tenant_id
+ item = kstone.users.create(name=name,
+ password=password,
+ email=email,
+ project_id=project_id,
+ enabled=enabled,
+ description=description)
+ else:
+ item = kstone.users.create(name=name,
+ password=password,
+ email=email,
+ tenant_id=tenant_id,
+ enabled=enabled)
+ return user_get(item.id, profile=profile, **connection_args)
+
+
+def user_delete(user_id=None, name=None, profile=None, **connection_args):
+ '''
+ Delete a user (keystone user-delete)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_delete c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.user_delete user_id=c965f79c4f864eaaa9c3b41904e67082
+ salt '*' keystone.user_delete name=nova
+ '''
+ kstone = auth(profile, **connection_args)
+ if name:
+ for user in kstone.users.list():
+ if user.name == name:
+ user_id = user.id
+ break
+ if not user_id:
+ return {'Error': 'Unable to resolve user id'}
+ kstone.users.delete(user_id)
+ ret = 'User ID {0} deleted'.format(user_id)
+ if name:
+
+ ret += ' ({0})'.format(name)
+ return ret
+
+
+def user_update(user_id=None, name=None, email=None, enabled=None,
+ tenant=None, profile=None, project=None, description=None, **connection_args):
+ '''
+ Update a user's information (keystone user-update)
+ The following fields may be updated: name, email, enabled, tenant.
+ Because the name is one of the fields, a valid user id is required.
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_update user_id=c965f79c4f864eaaa9c3b41904e67082 name=newname
+ salt '*' keystone.user_update c965f79c4f864eaaa9c3b41904e67082 name=newname email=newemail@domain.com
+ '''
+ kstone = auth(profile, **connection_args)
+ if not user_id:
+ for user in kstone.users.list():
+ if user.name == name:
+ user_id = user.id
+ break
+ if not user_id:
+ return {'Error': 'Unable to resolve user id'}
+ user = kstone.users.get(user_id)
+ # Keep previous settings if not updating them
+ if not name:
+ name = user.name
+ if not email:
+ email = user.email
+ if enabled is None:
+ enabled = user.enabled
+
+ if _client_version(kstone) > 2:
+ if description is None:
+ description = getattr(user, 'description', None)
+ else:
+ description = str(description)
+
+ project_id = None
+ if project:
+ for proj in kstone.projects.list():
+ if proj.name == project:
+ project_id = proj.id
+ break
+ if not project_id:
+ project_id = getattr(user, 'project_id', None)
+
+ kstone.users.update(user=user_id, name=name, email=email, enabled=enabled, description=description,
+ project_id=project_id)
+ else:
+ kstone.users.update(user=user_id, name=name, email=email, enabled=enabled)
+
+ tenant_id = None
+ if tenant:
+ for tnt in kstone.tenants.list():
+ if tnt.name == tenant:
+ tenant_id = tnt.id
+ break
+ if tenant_id:
+ kstone.users.update_tenant(user_id, tenant_id)
+
+ ret = 'Info updated for user ID {0}'.format(user_id)
+ return ret
+
+
+def user_verify_password(user_id=None, name=None, password=None,
+ profile=None, **connection_args):
+ '''
+ Verify a user's password
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_verify_password name=test password=foobar
+ salt '*' keystone.user_verify_password user_id=c965f79c4f864eaaa9c3b41904e67082 password=foobar
+ '''
+ kstone = auth(profile, **connection_args)
+ if 'connection_endpoint' in connection_args:
+ auth_url = connection_args.get('connection_endpoint')
+ else:
+ if _client_version(kstone) > 2:
+ auth_url = __salt__['config.option']('keystone.endpoint',
+ 'http://127.0.0.1:35357/v3')
+ else:
+ auth_url = __salt__['config.option']('keystone.endpoint',
+ 'http://127.0.0.1:35357/v2.0')
+
+ if user_id:
+ for user in kstone.users.list():
+ if user.id == user_id:
+ name = user.name
+ break
+ if not name:
+ return {'Error': 'Unable to resolve user name'}
+ kwargs = {'username': name,
+ 'password': password,
+ 'auth_url': auth_url}
+ try:
+ if _client_version(kstone) > 2:
+ client3.Client(**kwargs)
+ else:
+ client.Client(**kwargs)
+ except (keystoneclient.exceptions.Unauthorized,
+ keystoneclient.exceptions.AuthorizationFailure):
+ return False
+ return True
+
+
+def user_password_update(user_id=None, name=None, password=None,
+ profile=None, **connection_args):
+ '''
+ Update a user's password (keystone user-password-update)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_password_update c965f79c4f864eaaa9c3b41904e67082 password=12345
+ salt '*' keystone.user_password_update user_id=c965f79c4f864eaaa9c3b41904e67082 password=12345
+ salt '*' keystone.user_password_update name=nova password=12345
+ '''
+ kstone = auth(profile, **connection_args)
+ if name:
+ for user in kstone.users.list():
+ if user.name == name:
+ user_id = user.id
+ break
+ if not user_id:
+ return {'Error': 'Unable to resolve user id'}
+
+ if _client_version(kstone) > 2:
+ kstone.users.update(user=user_id, password=password)
+ else:
+ kstone.users.update_password(user=user_id, password=password)
+ ret = 'Password updated for user ID {0}'.format(user_id)
+ if name:
+ ret += ' ({0})'.format(name)
+ return ret
+
+
+def user_role_add(user_id=None, user=None, tenant_id=None,
+ tenant=None, role_id=None, role=None, profile=None,
+ project_id=None, project_name=None, **connection_args):
+ '''
+ Add role for user in tenant (keystone user-role-add)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_role_add \
+user_id=298ce377245c4ec9b70e1c639c89e654 \
+tenant_id=7167a092ece84bae8cead4bf9d15bb3b \
+role_id=ce377245c4ec9b70e1c639c89e8cead4
+ salt '*' keystone.user_role_add user=admin tenant=admin role=admin
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if project_id and not tenant_id:
+ tenant_id = project_id
+ elif project_name and not tenant:
+ tenant = project_name
+
+ if user:
+ user_id = user_get(name=user, profile=profile,
+ **connection_args)[user].get('id')
+ else:
+ user = next(six.iterkeys(user_get(user_id, profile=profile,
+ **connection_args)))['name']
+ if not user_id:
+ return {'Error': 'Unable to resolve user id'}
+
+ if tenant:
+ tenant_id = tenant_get(name=tenant, profile=profile,
+ **connection_args)[tenant].get('id')
+ else:
+ tenant = next(six.iterkeys(tenant_get(tenant_id, profile=profile,
+ **connection_args)))['name']
+ if not tenant_id:
+ return {'Error': 'Unable to resolve tenant/project id'}
+
+ if role:
+ role_id = role_get(name=role, profile=profile,
+ **connection_args)[role]['id']
+ else:
+ role = next(six.iterkeys(role_get(role_id, profile=profile,
+ **connection_args)))['name']
+ if not role_id:
+ return {'Error': 'Unable to resolve role id'}
+
+ if _client_version(kstone) > 2:
+ kstone.roles.grant(role_id, user=user_id, project=tenant_id)
+ else:
+ kstone.roles.add_user_role(user_id, role_id, tenant_id)
+ ret_msg = '"{0}" role added for user "{1}" for "{2}" tenant/project'
+ return ret_msg.format(role, user, tenant)
+
+
+def user_role_remove(user_id=None, user=None, tenant_id=None,
+ tenant=None, role_id=None, role=None,
+ profile=None, project_id=None, project_name=None, **connection_args):
+ '''
+ Remove role for user in tenant (keystone user-role-remove)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_role_remove \
+user_id=298ce377245c4ec9b70e1c639c89e654 \
+tenant_id=7167a092ece84bae8cead4bf9d15bb3b \
+role_id=ce377245c4ec9b70e1c639c89e8cead4
+ salt '*' keystone.user_role_remove user=admin tenant=admin role=admin
+ '''
+ kstone = auth(profile, **connection_args)
+
+ if project_id and not tenant_id:
+ tenant_id = project_id
+ elif project_name and not tenant:
+ tenant = project_name
+
+ if user:
+ user_id = user_get(name=user, profile=profile,
+ **connection_args)[user].get('id')
+ else:
+ user = next(six.iterkeys(user_get(user_id, profile=profile,
+ **connection_args)))['name']
+ if not user_id:
+ return {'Error': 'Unable to resolve user id'}
+
+ if tenant:
+ tenant_id = tenant_get(name=tenant, profile=profile,
+ **connection_args)[tenant].get('id')
+ else:
+ tenant = next(six.iterkeys(tenant_get(tenant_id, profile=profile,
+ **connection_args)))['name']
+ if not tenant_id:
+ return {'Error': 'Unable to resolve tenant/project id'}
+
+ if role:
+ role_id = role_get(name=role, profile=profile,
+ **connection_args)[role]['id']
+ else:
+ role = next(six.iterkeys(role_get(role_id)))['name']
+ if not role_id:
+ return {'Error': 'Unable to resolve role id'}
+
+ if _client_version(kstone) > 2:
+ kstone.roles.revoke(role_id, user=user_id, project=tenant_id)
+ else:
+ kstone.roles.remove_user_role(user_id, role_id, tenant_id)
+ ret_msg = '"{0}" role removed for user "{1}" under "{2}" tenant'
+ return ret_msg.format(role, user, tenant)
+
+
+def user_role_list(user_id=None, tenant_id=None, user_name=None,
+ tenant_name=None, profile=None, project_id=None, project_name=None, **connection_args):
+ '''
+ Return a list of available user_roles (keystone user-roles-list)
+
+ CLI Examples:
+
+ .. code-block:: bash
+
+ salt '*' keystone.user_role_list \
+user_id=298ce377245c4ec9b70e1c639c89e654 \
+tenant_id=7167a092ece84bae8cead4bf9d15bb3b
+ salt '*' keystone.user_role_list user_name=admin tenant_name=admin
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = {}
+
+ if project_id and not tenant_id:
+ tenant_id = project_id
+ elif project_name and not tenant_name:
+ tenant_name = project_name
+
+ if user_name:
+ for user in kstone.users.list():
+ if user.name == user_name:
+ user_id = user.id
+ break
+ if tenant_name:
+ for tenant in _project_mgr(kstone).list():
+ if tenant.name == tenant_name:
+ tenant_id = tenant.id
+ break
+ if not user_id or not tenant_id:
+ return {'Error': 'Unable to resolve user or tenant/project id'}
+
+ if _client_version(kstone) > 2:
+ for role in kstone.roles.list(user=user_id, project=tenant_id):
+ ret[role.name] = dict((value, getattr(role, value)) for value in dir(role)
+ if not value.startswith('_') and
+ isinstance(getattr(role, value), (six.string_types, dict, bool)))
+ else:
+ for role in kstone.roles.roles_for_user(user=user_id, tenant=tenant_id):
+ ret[role.name] = {'id': role.id,
+ 'name': role.name,
+ 'user_id': user_id,
+ 'tenant_id': tenant_id}
+ return ret
+
+
+def _item_list(profile=None, **connection_args):
+ '''
+ Template for writing list functions
+ Return a list of available items (keystone items-list)
+
+ CLI Example:
+
+ .. code-block:: bash
+
+ salt '*' keystone.item_list
+ '''
+ kstone = auth(profile, **connection_args)
+ ret = []
+ for item in kstone.items.list():
+ ret.append(item.__dict__)
+ # ret[item.name] = {
+ # 'id': item.id,
+ # 'name': item.name,
+ # }
+ return ret
+
+ # The following is a list of functions that need to be incorporated in the
+ # keystone module. This list should be updated as functions are added.
+ #
+ # endpoint-create Create a new endpoint associated with a service
+ # endpoint-delete Delete a service endpoint
+ # discover Discover Keystone servers and show authentication
+ # protocols and
+ # bootstrap Grants a new role to a new user on a new tenant, after
+ # creating each.
diff --git a/_states/keystoneng.py b/_states/keystoneng.py
new file mode 100644
index 0000000..060ccfb
--- /dev/null
+++ b/_states/keystoneng.py
@@ -0,0 +1,810 @@
+# -*- coding: utf-8 -*-
+'''
+Management of Keystone users
+============================
+
+:depends: - keystoneclient Python module
+:configuration: See :py:mod:`salt.modules.keystone` for setup instructions.
+
+.. code-block:: yaml
+
+ Keystone tenants:
+ keystoneng.tenant_present:
+ - names:
+ - admin
+ - demo
+ - service
+
+ Keystone roles:
+ keystoneng.role_present:
+ - names:
+ - admin
+ - Member
+
+ admin:
+ keystoneng.user_present:
+ - password: R00T_4CC3SS
+ - email: admin@domain.com
+ - roles:
+ admin: # tenants
+ - admin # roles
+ service:
+ - admin
+ - Member
+ - require:
+ - keystone: Keystone tenants
+ - keystone: Keystone roles
+
+ nova:
+ keystoneng.user_present:
+ - password: '$up3rn0v4'
+ - email: nova@domain.com
+ - tenant: service
+ - roles:
+ service:
+ - admin
+ - require:
+ - keystone: Keystone tenants
+ - keystone: Keystone roles
+
+ demo:
+ keystoneng.user_present:
+ - password: 'd3m0n$trati0n'
+ - email: demo@domain.com
+ - tenant: demo
+ - roles:
+ demo:
+ - Member
+ - require:
+ - keystone: Keystone tenants
+ - keystone: Keystone roles
+
+ nova service:
+ keystoneng.service_present:
+ - name: nova
+ - service_type: compute
+ - description: OpenStack Compute Service
+
+'''
+
+
+def __virtual__():
+ '''
+ Only load if the keystone module is in __salt__
+ '''
+ return 'keystoneng' if 'keystoneng.auth' in __salt__ else False
+
+
+_OS_IDENTITY_API_VERSION = 2
+_TENANT_ID = 'tenant_id'
+
+
+def _api_version(profile=None, **connection_args):
+ '''
+ Sets global variables _OS_IDENTITY_API_VERSION and _TENANT_ID
+ depending on API version.
+ '''
+ global _TENANT_ID
+ global _OS_IDENTITY_API_VERSION
+ try:
+ if float(__salt__['keystoneng.api_version'](profile=profile, **connection_args)) >= 3:
+ _TENANT_ID = 'project_id'
+ _OS_IDENTITY_API_VERSION = 3
+ except KeyError:
+ pass
+
+
+def user_present(name,
+ password,
+ email,
+ tenant=None,
+ enabled=True,
+ roles=None,
+ profile=None,
+ password_reset=True,
+ project=None,
+ **connection_args):
+ '''
+ Ensure that the keystone user is present with the specified properties.
+
+ name
+ The name of the user to manage
+
+ password
+ The password to use for this user.
+
+ .. note::
+
+ If the user already exists and a different password was set for
+ the user than the one specified here, the password for the user
+ will be updated. Please set the ``password_reset`` option to
+ ``False`` if this is not the desired behavior.
+
+ password_reset
+ Whether or not to reset password after initial set. Defaults to
+ ``True``.
+
+ email
+ The email address for this user
+
+ tenant
+ The tenant (name) for this user
+
+ project
+ The project (name) for this user (overrides tenant in api v3)
+
+ enabled
+ Availability state for this user
+
+ roles
+ The roles the user should have under given tenants.
+ Passed as a dictionary mapping tenant names to a list
+ of roles in this tenant, i.e.::
+
+ roles:
+ admin: # tenant
+ - admin # role
+ service:
+ - admin
+ - Member
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'User "{0}" will be updated'.format(name)}
+
+ _api_version(profile=profile, **connection_args)
+
+ if project and not tenant:
+ tenant = project
+
+ # Validate tenant if set
+ if tenant is not None:
+ tenantdata = __salt__['keystoneng.tenant_get'](name=tenant,
+ profile=profile,
+ **connection_args)
+ if 'Error' in tenantdata:
+ ret['result'] = False
+ ret['comment'] = 'Tenant / project "{0}" does not exist'.format(tenant)
+ return ret
+ tenant_id = tenantdata[tenant]['id']
+ else:
+ tenant_id = None
+
+ # Check if user is already present
+ user = __salt__['keystoneng.user_get'](name=name, profile=profile,
+ **connection_args)
+ if 'Error' not in user:
+
+ change_email = False
+ change_enabled = False
+ change_tenant = False
+ change_password = False
+
+ if user[name].get('email', None) != email:
+ change_email = True
+
+ if user[name].get('enabled', None) != enabled:
+ change_enabled = True
+
+ if tenant and (_TENANT_ID not in user[name] or
+ user[name].get(_TENANT_ID, None) != tenant_id):
+ change_tenant = True
+
+ if (password_reset is True and
+ not __salt__['keystoneng.user_verify_password'](name=name,
+ password=password,
+ profile=profile,
+ **connection_args)):
+ change_password = True
+
+ if __opts__.get('test') and (change_email or change_enabled or change_tenant or change_password):
+ ret['result'] = None
+ ret['comment'] = 'User "{0}" will be updated'.format(name)
+ if change_email is True:
+ ret['changes']['Email'] = 'Will be updated'
+ if change_enabled is True:
+ ret['changes']['Enabled'] = 'Will be True'
+ if change_tenant is True:
+ ret['changes']['Tenant'] = 'Will be added to "{0}" tenant'.format(tenant)
+ if change_password is True:
+ ret['changes']['Password'] = 'Will be updated'
+ return ret
+
+ ret['comment'] = 'User "{0}" is already present'.format(name)
+
+ if change_email:
+ __salt__['keystoneng.user_update'](name=name, email=email, profile=profile, **connection_args)
+ ret['comment'] = 'User "{0}" has been updated'.format(name)
+ ret['changes']['Email'] = 'Updated'
+
+ if change_enabled:
+ __salt__['keystoneng.user_update'](name=name, enabled=enabled, profile=profile, **connection_args)
+ ret['comment'] = 'User "{0}" has been updated'.format(name)
+ ret['changes']['Enabled'] = 'Now {0}'.format(enabled)
+
+ if change_tenant:
+ __salt__['keystoneng.user_update'](name=name, tenant=tenant, profile=profile, **connection_args)
+ ret['comment'] = 'User "{0}" has been updated'.format(name)
+ ret['changes']['Tenant'] = 'Added to "{0}" tenant'.format(tenant)
+
+ if change_password:
+ __salt__['keystoneng.user_password_update'](name=name, password=password, profile=profile,
+ **connection_args)
+ ret['comment'] = 'User "{0}" has been updated'.format(name)
+ ret['changes']['Password'] = 'Updated'
+
+ if roles:
+ for tenant in roles:
+ args = dict({'user_name': name, 'tenant_name':
+ tenant, 'profile': profile}, **connection_args)
+ tenant_roles = __salt__['keystoneng.user_role_list'](**args)
+ for role in roles[tenant]:
+ if role not in tenant_roles:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'User roles "{0}" will been updated'.format(name)
+ return ret
+ addargs = dict({'user': name, 'role': role,
+ 'tenant': tenant,
+ 'profile': profile},
+ **connection_args)
+ newrole = __salt__['keystoneng.user_role_add'](**addargs)
+ if 'roles' in ret['changes']:
+ ret['changes']['roles'].append(newrole)
+ else:
+ ret['changes']['roles'] = [newrole]
+ roles_to_remove = list(set(tenant_roles) - set(roles[tenant]))
+ for role in roles_to_remove:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'User roles "{0}" will been updated'.format(name)
+ return ret
+ addargs = dict({'user': name, 'role': role,
+ 'tenant': tenant,
+ 'profile': profile},
+ **connection_args)
+ oldrole = __salt__['keystoneng.user_role_remove'](**addargs)
+ if 'roles' in ret['changes']:
+ ret['changes']['roles'].append(oldrole)
+ else:
+ ret['changes']['roles'] = [oldrole]
+ else:
+ # Create that user!
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Keystone user "{0}" will be added'.format(name)
+ ret['changes']['User'] = 'Will be created'
+ return ret
+ __salt__['keystoneng.user_create'](name=name,
+ password=password,
+ email=email,
+ tenant_id=tenant_id,
+ enabled=enabled,
+ profile=profile,
+ **connection_args)
+ if roles:
+ for tenant in roles:
+ for role in roles[tenant]:
+ __salt__['keystoneng.user_role_add'](user=name,
+ role=role,
+ tenant=tenant,
+ profile=profile,
+ **connection_args)
+ ret['comment'] = 'Keystone user {0} has been added'.format(name)
+ ret['changes']['User'] = 'Created'
+
+ return ret
+
+
+def user_absent(name, profile=None, **connection_args):
+ '''
+ Ensure that the keystone user is absent.
+
+ name
+ The name of the user that should not exist
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'User "{0}" is already absent'.format(name)}
+
+ # Check if user is present
+ user = __salt__['keystoneng.user_get'](name=name, profile=profile,
+ **connection_args)
+ if 'Error' not in user:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'User "{0}" will be deleted'.format(name)
+ return ret
+ # Delete that user!
+ __salt__['keystoneng.user_delete'](name=name, profile=profile,
+ **connection_args)
+ ret['comment'] = 'User "{0}" has been deleted'.format(name)
+ ret['changes']['User'] = 'Deleted'
+
+ return ret
+
+
+def tenant_present(name, description=None, enabled=True, profile=None,
+ **connection_args):
+ '''
+ Ensures that the keystone tenant exists
+
+ name
+ The name of the tenant to manage
+
+ description
+ The description to use for this tenant
+
+ enabled
+ Availability state for this tenant
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Tenant / project "{0}" already exists'.format(name)}
+
+ _api_version(profile=profile, **connection_args)
+
+ # Check if tenant is already present
+ tenant = __salt__['keystoneng.tenant_get'](name=name,
+ profile=profile,
+ **connection_args)
+
+ if 'Error' not in tenant:
+ if tenant[name].get('description', None) != description:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Tenant / project "{0}" will be updated'.format(name)
+ ret['changes']['Description'] = 'Will be updated'
+ return ret
+ __salt__['keystoneng.tenant_update'](name=name,
+ description=description,
+ enabled=enabled,
+ profile=profile,
+ **connection_args)
+ ret['comment'] = 'Tenant / project "{0}" has been updated'.format(name)
+ ret['changes']['Description'] = 'Updated'
+ if tenant[name].get('enabled', None) != enabled:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Tenant / project "{0}" will be updated'.format(name)
+ ret['changes']['Enabled'] = 'Will be {0}'.format(enabled)
+ return ret
+ __salt__['keystoneng.tenant_update'](name=name,
+ description=description,
+ enabled=enabled,
+ profile=profile,
+ **connection_args)
+ ret['comment'] = 'Tenant / project "{0}" has been updated'.format(name)
+ ret['changes']['Enabled'] = 'Now {0}'.format(enabled)
+ else:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Tenant / project "{0}" will be added'.format(name)
+ ret['changes']['Tenant'] = 'Will be created'
+ return ret
+ # Create tenant
+ if _OS_IDENTITY_API_VERSION > 2:
+ created = __salt__['keystoneng.project_create'](name=name, domain='default', description=description,
+ enabled=enabled, profile=profile, **connection_args)
+ else:
+ created = __salt__['keystoneng.tenant_create'](name=name, description=description, enabled=enabled,
+ profile=profile, **connection_args)
+ ret['changes']['Tenant'] = 'Created' if created is True else 'Failed'
+ ret['result'] = created
+ ret['comment'] = 'Tenant / project "{0}" has been added'.format(name)
+ return ret
+
+
+def tenant_absent(name, profile=None, **connection_args):
+ '''
+ Ensure that the keystone tenant is absent.
+
+ name
+ The name of the tenant that should not exist
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Tenant / project "{0}" is already absent'.format(name)}
+
+ # Check if tenant is present
+ tenant = __salt__['keystoneng.tenant_get'](name=name,
+ profile=profile,
+ **connection_args)
+ if 'Error' not in tenant:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Tenant / project "{0}" will be deleted'.format(name)
+ return ret
+ # Delete tenant
+ __salt__['keystoneng.tenant_delete'](name=name, profile=profile,
+ **connection_args)
+ ret['comment'] = 'Tenant / project "{0}" has been deleted'.format(name)
+ ret['changes']['Tenant/Project'] = 'Deleted'
+
+ return ret
+
+
+def project_present(name, description=None, enabled=True, profile=None,
+ **connection_args):
+ '''
+ Ensures that the keystone project exists
+ Alias for tenant_present from V2 API to fulfill
+ V3 API naming convention.
+
+ .. versionadded:: 2016.11.0
+
+ name
+ The name of the project to manage
+
+ description
+ The description to use for this project
+
+ enabled
+ Availability state for this project
+
+ .. code-block:: yaml
+
+ nova:
+ keystoneng.project_present:
+ - enabled: True
+ - description: 'Nova Compute Service'
+
+ '''
+
+ return tenant_present(name, description=description, enabled=enabled, profile=profile,
+ **connection_args)
+
+
+def project_absent(name, profile=None, **connection_args):
+ '''
+ Ensure that the keystone project is absent.
+ Alias for tenant_absent from V2 API to fulfill
+ V3 API naming convention.
+
+ .. versionadded:: 2016.11.0
+
+ name
+ The name of the project that should not exist
+
+ .. code-block:: yaml
+
+ delete_nova:
+ keystoneng.project_absent:
+ - name: nova
+ '''
+
+ return tenant_absent(name, profile=profile, **connection_args)
+
+
+def role_present(name, profile=None, **connection_args):
+ ''''
+ Ensures that the keystone role exists
+
+ name
+ The name of the role that should be present
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Role "{0}" already exists'.format(name)}
+
+ # Check if role is already present
+ role = __salt__['keystoneng.role_get'](name=name, profile=profile,
+ **connection_args)
+
+ if 'Error' not in role:
+ return ret
+ else:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Role "{0}" will be added'.format(name)
+ return ret
+ # Create role
+ __salt__['keystoneng.role_create'](name, profile=profile,
+ **connection_args)
+ ret['comment'] = 'Role "{0}" has been added'.format(name)
+ ret['changes']['Role'] = 'Created'
+ return ret
+
+
+def role_absent(name, profile=None, **connection_args):
+ '''
+ Ensure that the keystone role is absent.
+
+ name
+ The name of the role that should not exist
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Role "{0}" is already absent'.format(name)}
+
+ # Check if role is present
+ role = __salt__['keystoneng.role_get'](name=name, profile=profile,
+ **connection_args)
+ if 'Error' not in role:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Role "{0}" will be deleted'.format(name)
+ return ret
+ # Delete role
+ __salt__['keystoneng.role_delete'](name=name, profile=profile,
+ **connection_args)
+ ret['comment'] = 'Role "{0}" has been deleted'.format(name)
+ ret['changes']['Role'] = 'Deleted'
+
+ return ret
+
+
+def service_present(name, service_type, description=None,
+ profile=None, **connection_args):
+ '''
+ Ensure service present in Keystone catalog
+
+ name
+ The name of the service
+
+ service_type
+ The type of Openstack Service
+
+ description (optional)
+ Description of the service
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Service "{0}" already exists'.format(name)}
+
+ # Check if service is already present
+ role = __salt__['keystoneng.service_get'](name=name,
+ profile=profile,
+ **connection_args)
+
+ if 'Error' not in role:
+ return ret
+ else:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Service "{0}" will be added'.format(name)
+ return ret
+ # Create service
+ __salt__['keystoneng.service_create'](name, service_type,
+ description,
+ profile=profile,
+ **connection_args)
+ ret['comment'] = 'Service "{0}" has been added'.format(name)
+ ret['changes']['Service'] = 'Created'
+
+ return ret
+
+
+def service_absent(name, profile=None, **connection_args):
+ '''
+ Ensure that the service doesn't exist in Keystone catalog
+
+ name
+ The name of the service that should not exist
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Service "{0}" is already absent'.format(name)}
+
+ # Check if service is present
+ role = __salt__['keystoneng.service_get'](name=name,
+ profile=profile,
+ **connection_args)
+ if 'Error' not in role:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Service "{0}" will be deleted'.format(name)
+ return ret
+ # Delete service
+ __salt__['keystoneng.service_delete'](name=name,
+ profile=profile,
+ **connection_args)
+ ret['comment'] = 'Service "{0}" has been deleted'.format(name)
+ ret['changes']['Service'] = 'Deleted'
+
+ return ret
+
+
+def endpoint_present(name,
+ publicurl=None,
+ internalurl=None,
+ adminurl=None,
+ region=None,
+ profile=None,
+ url=None,
+ interface=None, **connection_args):
+ '''
+ Ensure the specified endpoints exists for service
+
+ name
+ The Service name
+
+ publicurl
+ The public url of service endpoint (for V2 API)
+
+ internalurl
+ The internal url of service endpoint (for V2 API)
+
+ adminurl
+ The admin url of the service endpoint (for V2 API)
+
+ region
+ The region of the endpoint
+
+ url
+ The endpoint URL (for V3 API)
+
+ interface
+ The interface type, which describes the visibility
+ of the endpoint. (for V3 API)
+
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': ''}
+ endpoint = __salt__['keystoneng.endpoint_get'](name, region,
+ profile=profile,
+ interface=interface,
+ **connection_args)
+
+ def _changes(desc):
+ return ret.get('comment', '') + desc + '\n'
+
+ def _create_endpoint():
+ if _OS_IDENTITY_API_VERSION > 2:
+ ret['changes'] = __salt__['keystoneng.endpoint_create'](
+ name,
+ region=region,
+ url=url,
+ interface=interface,
+ profile=profile,
+ **connection_args)
+ else:
+ ret['changes'] = __salt__['keystoneng.endpoint_create'](
+ name,
+ region=region,
+ publicurl=publicurl,
+ adminurl=adminurl,
+ internalurl=internalurl,
+ profile=profile,
+ **connection_args)
+
+ if endpoint and 'Error' not in endpoint and endpoint.get('region') == region:
+
+ if _OS_IDENTITY_API_VERSION > 2:
+
+ change_url = False
+ change_interface = False
+
+ if endpoint.get('url', None) != url:
+ ret['comment'] = _changes('URL changes from "{0}" to "{1}"'.format(endpoint.get('url', None), url))
+ change_url = True
+
+ if endpoint.get('interface', None) != interface:
+ ret['comment'] = _changes('Interface changes from "{0}" to "{1}"'.format(endpoint.get('interface', None), interface))
+ change_interface = True
+
+ if __opts__.get('test') and (change_url or change_interface):
+ ret['result'] = None
+ ret['changes']['Endpoint'] = 'Will be updated'
+ ret['comment'] += 'Endpoint for service "{0}" will be updated'.format(name)
+ return ret
+
+ if change_url:
+ ret['changes']['url'] = url
+
+ if change_interface:
+ ret['changes']['interface'] = interface
+
+ else:
+ change_publicurl = False
+ change_adminurl = False
+ change_internalurl = False
+
+ if endpoint.get('publicurl', None) != publicurl:
+ change_publicurl = True
+
+ ret['comment'] = _changes('Public URL changes from "{0}" to "{1}"'.format(
+ endpoint.get('publicurl', None), publicurl)
+ )
+
+ if endpoint.get('adminurl', None) != adminurl:
+ change_adminurl = True
+ ret['comment'] = _changes('Admin URL changes from "{0}" to "{1}"'.format(
+ endpoint.get('adminurl', None), adminurl)
+ )
+
+ if endpoint.get('internalurl', None) != internalurl:
+ change_internalurl = True
+ ret['comment'] = _changes(
+ 'Internal URL changes from "{0}" to "{1}"'.format(
+ endpoint.get('internalurl', None),
+ internalurl
+ )
+ )
+
+ if __opts__.get('test') and (change_publicurl or change_adminurl or change_internalurl):
+ ret['result'] = None
+ ret['comment'] += 'Endpoint for service "{0}" will be updated'.format(name)
+ ret['changes']['Endpoint'] = 'Will be updated'
+ return ret
+
+ if change_publicurl:
+ ret['changes']['publicurl'] = publicurl
+
+ if change_adminurl:
+ ret['changes']['adminurl'] = adminurl
+
+ if change_internalurl:
+ ret['changes']['internalurl'] = internalurl
+
+ if ret['comment']: # changed
+ __salt__['keystoneng.endpoint_delete'](name, region, profile=profile, interface=interface, **connection_args)
+ _create_endpoint()
+ ret['comment'] += 'Endpoint for service "{0}" has been updated'.format(name)
+
+ else:
+ # Add new endpoint
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['changes']['Endpoint'] = 'Will be created'
+ ret['comment'] = 'Endpoint for service "{0}" will be added'.format(name)
+ return ret
+ _create_endpoint()
+ ret['comment'] = 'Endpoint for service "{0}" has been added'.format(name)
+
+ if ret['comment'] == '': # => no changes
+ ret['comment'] = 'Endpoint for service "{0}" already exists'.format(name)
+ return ret
+
+
+def endpoint_absent(name, region=None, profile=None, interface=None, **connection_args):
+ '''
+ Ensure that the endpoint for a service doesn't exist in Keystone catalog
+
+ name
+ The name of the service whose endpoints should not exist
+
+ region (optional)
+ The region of the endpoint. Defaults to ``RegionOne``.
+
+ interface
+ The interface type, which describes the visibility
+ of the endpoint. (for V3 API)
+ '''
+ ret = {'name': name,
+ 'changes': {},
+ 'result': True,
+ 'comment': 'Endpoint for service "{0}"{1} is already absent'.format(name,
+ ', interface "{0}",'.format(interface) if interface is not None else '')}
+
+ # Check if service is present
+ endpoint = __salt__['keystoneng.endpoint_get'](name, region,
+ profile=profile,
+ interface=interface,
+ **connection_args)
+ if not endpoint:
+ return ret
+ else:
+ if __opts__.get('test'):
+ ret['result'] = None
+ ret['comment'] = 'Endpoint for service "{0}" will be deleted'.format(name)
+ return ret
+ # Delete service
+ __salt__['keystoneng.endpoint_delete'](name, region,
+ profile=profile,
+ interface=interface,
+ **connection_args)
+ ret['comment'] = 'Endpoint for service "{0}"{1} has been deleted'.format(name,
+ ', interface "{0}",'.format(interface) if interface is not None else '')
+ ret['changes']['endpoint'] = 'Deleted'
+ return ret
diff --git a/keystone/client/project.sls b/keystone/client/project.sls
index 856c78f..5e07b45 100644
--- a/keystone/client/project.sls
+++ b/keystone/client/project.sls
@@ -4,7 +4,7 @@
{%- if client.tenant is defined %}
keystone_client_roles:
- keystone.role_present:
+ keystoneng.role_present:
- names: {{ client.roles }}
- connection_user: {{ client.server.user }}
- connection_password: {{ client.server.password }}
@@ -14,19 +14,19 @@
{%- for tenant_name, tenant in client.get('tenant', {}).iteritems() %}
keystone_tenant_{{ tenant_name }}:
- keystone.tenant_present:
+ keystoneng.tenant_present:
- name: {{ tenant_name }}
- connection_user: {{ client.server.user }}
- connection_password: {{ client.server.password }}
- connection_tenant: {{ client.server.tenant }}
- connection_auth_url: 'http://{{ client.server.host }}:{{ client.server.public_port }}/v2.0/'
- require:
- - keystone: keystone_client_roles
+ - keystoneng: keystone_client_roles
{%- for user_name, user in tenant.get('user', {}).iteritems() %}
keystone_{{ tenant_name }}_user_{{ user_name }}:
- keystone.user_present:
+ keystoneng.user_present:
- name: {{ user_name }}
- password: {{ user.password }}
- email: {{ user.get('email', 'root@localhost') }}
@@ -45,7 +45,7 @@
- connection_tenant: {{ client.server.tenant }}
- connection_auth_url: 'http://{{ client.server.host }}:{{ client.server.public_port }}/v2.0/'
- require:
- - keystone: keystone_tenant_{{ tenant_name }}
+ - keystoneng: keystone_tenant_{{ tenant_name }}
{%- endfor %}
diff --git a/keystone/client/server.sls b/keystone/client/server.sls
index 416590b..fb7597b 100644
--- a/keystone/client/server.sls
+++ b/keystone/client/server.sls
@@ -28,7 +28,7 @@
{%- if server.roles is defined %}
keystone_{{ server_name }}_roles:
- keystone.role_present:
+ keystoneng.role_present:
- names: {{ server.roles }}
{%- if server.admin.token is defined %}
- connection_token: {{ connection_args.token }}
@@ -45,7 +45,7 @@
{% for service_name, service in server.get('service', {}).iteritems() %}
keystone_{{ server_name }}_service_{{ service_name }}:
- keystone.service_present:
+ keystoneng.service_present:
- name: {{ service_name }}
- service_type: {{ service.type }}
- description: {{ service.description }}
@@ -62,14 +62,14 @@
{%- for endpoint in service.get('endpoints', ()) %}
keystone_{{ server_name }}_service_{{ service_name }}_endpoint_{{ endpoint.region }}:
- keystone.endpoint_present:
+ keystoneng.endpoint_present:
- name: {{ service_name }}
- publicurl: '{{ endpoint.get('public_protocol', 'http') }}://{{ endpoint.public_address }}{% if not (endpoint.get('public_protocol', 'http') == 'https' and endpoint.public_port|int == 443) %}:{{ endpoint.public_port }}{% endif %}{{ endpoint.public_path }}'
- internalurl: '{{ endpoint.get('internal_protocol', 'http') }}://{{ endpoint.internal_address }}{% if not (endpoint.get('internal_protocol', 'http') == 'https' and endpoint.internal_port|int == 443) %}:{{ endpoint.internal_port }}{% endif %}{{ endpoint.internal_path }}'
- adminurl: '{{ endpoint.get('admin_protocol', 'http') }}://{{ endpoint.admin_address }}{% if not (endpoint.get('admin_protocol', 'http') == 'https' and endpoint.admin_port|int == 443) %}:{{ endpoint.admin_port }}{% endif %}{{ endpoint.admin_path }}'
- region: {{ endpoint.region }}
- require:
- - keystone: keystone_{{ server_name }}_service_{{ service_name }}
+ - keystoneng: keystone_{{ server_name }}_service_{{ service_name }}
{%- if server.admin.token is defined %}
- connection_token: {{ connection_args.token }}
- connection_endpoint: {{ connection_args.endpoint }}
@@ -87,7 +87,7 @@
{%- for tenant_name, tenant in server.get('project', {}).iteritems() %}
keystone_{{ server_name }}_tenant_{{ tenant_name }}:
- keystone.tenant_present:
+ keystoneng.tenant_present:
- name: {{ tenant_name }}
{%- if tenant.description is defined %}
- description: {{ tenant.description }}
@@ -112,14 +112,14 @@
- {{ quota_name }}: {{ quota_value }}
{%- endfor %}
- require:
- - keystone: keystone_{{ server_name }}_tenant_{{ tenant_name }}
+ - keystoneng: keystone_{{ server_name }}_tenant_{{ tenant_name }}
{%- endif %}
{%- for user_name, user in tenant.get('user', {}).iteritems() %}
keystone_{{ server_name }}_tenant_{{ tenant_name }}_user_{{ user_name }}:
- keystone.user_present:
+ keystoneng.user_present:
- name: {{ user_name }}
- password: {{ user.password }}
{%- if user.email is defined %}
@@ -136,8 +136,8 @@
- Member
{%- endif %}
- require:
- - keystone: keystone_{{ server_name }}_tenant_{{ tenant_name }}
- - keystone: keystone_{{ server_name }}_roles
+ - keystoneng: keystone_{{ server_name }}_tenant_{{ tenant_name }}
+ - keystoneng: keystone_{{ server_name }}_roles
{%- if server.admin.token is defined %}
- connection_token: {{ connection_args.token }}
- connection_endpoint: {{ connection_args.endpoint }}
diff --git a/keystone/meta/salt.yml b/keystone/meta/salt.yml
index 4a3f557..f287824 100644
--- a/keystone/meta/salt.yml
+++ b/keystone/meta/salt.yml
@@ -15,13 +15,13 @@
keystone:
{%- if pillar.keystone.get('server', {'enabled': False}).enabled %}
keystone.token: '{{ server.service_token }}'
- keystone.endpoint: '{{ server.bind.private_protocol }}://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
+ keystone.endpoint: '{{ server.bind.private_protocol }}://{{ server.bind.address }}:{{ server.bind.private_port }}/{{ server.bind.get('private_api_version', 'v2.0') }}'
{%- else %}
{%- if client.get('server', {}).get('user') %}
keystone.user: '{{ client.server.user }}'
keystone.password: '{{ client.server.password }}'
keystone.tenant: '{{ client.server.tenant }}'
- keystone.auth_url: '{{ client.server.get('public_protocol', 'http') }}://{{ client.server.host }}:{{ client.server.public_port }}/v2.0/'
+ keystone.auth_url: '{{ client.server.get('public_protocol', 'http') }}://{{ client.server.host }}:{{ client.server.public_port }}/{{ client.server.get('public_api_version', 'v2.0') }}'
{%- endif %}
{%- endif %}
@@ -30,9 +30,13 @@
{%- set protocol = identity.admin.get('protocol', 'http') %}
{%- if identity.admin.get('api_version', '2') == '3' %}
- {%- set version = "v3" %}
- {%- else %}
- {%- set version = "v2.0" %}
+ {%- set version = "/v3" %}
+ {%- endif %}
+ {%- if identity.admin.get('api_version', '2') == '2' %}
+ {%- set version = "/v2.0" %}
+ {%- endif %}
+ {%- if identity.admin.get('api_version', '2') == '' %}
+ {%- set version = "" %}
{%- endif %}
{%- if identity.admin.user is defined %}
@@ -42,7 +46,7 @@
keystone.user: '{{ identity.admin.user }}'
keystone.password: '{{ identity.admin.password }}'
keystone.tenant: '{{ identity.admin.project }}'
- keystone.auth_url: '{{ protocol+'://'+identity.admin.host+':'+identity.admin.port|string+'/'+version }}'
+ keystone.auth_url: '{{ protocol+'://'+identity.admin.host+':'+identity.admin.port|string+version }}'
keystone.region_name: '{{ identity.admin.region_name }}'
keystone.use_keystoneauth: {{ identity.admin.get('use_keystoneauth', false) }}
diff --git a/keystone/server.sls b/keystone/server.sls
index 99337f9..4e4cba6 100644
--- a/keystone/server.sls
+++ b/keystone/server.sls
@@ -338,7 +338,7 @@
{%- if not salt['pillar.get']('linux:system:repo:mirantis_openstack', False) %}
keystone_service_tenant:
- keystone.tenant_present:
+ keystoneng.tenant_present:
- name: {{ server.service_tenant }}
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
@@ -346,25 +346,25 @@
- cmd: keystone_syncdb
keystone_admin_tenant:
- keystone.tenant_present:
+ keystoneng.tenant_present:
- name: {{ server.admin_tenant }}
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- require:
- - keystone: keystone_service_tenant
+ - keystoneng: keystone_service_tenant
keystone_roles:
- keystone.role_present:
+ keystoneng.role_present:
- names: {{ server.roles }}
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- require:
- - keystone: keystone_service_tenant
+ - keystoneng: keystone_service_tenant
{%- if not server.get('ldap', {}).get('read_only', False) %}
keystone_admin_user:
- keystone.user_present:
+ keystoneng.user_present:
- name: {{ server.admin_name }}
- password: {{ server.admin_password }}
- email: {{ server.admin_email }}
@@ -375,8 +375,8 @@
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- require:
- - keystone: keystone_admin_tenant
- - keystone: keystone_roles
+ - keystoneng: keystone_admin_tenant
+ - keystoneng: keystone_roles
{%- endif %}
@@ -385,17 +385,17 @@
{%- for service_name, service in server.get('service', {}).iteritems() %}
keystone_{{ service_name }}_service:
- keystone.service_present:
+ keystoneng.service_present:
- name: {{ service_name }}
- service_type: {{ service.type }}
- description: {{ service.description }}
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- require:
- - keystone: keystone_roles
+ - keystoneng: keystone_roles
keystone_{{ service_name }}_{{ service.get('region', 'RegionOne') }}_endpoint:
- keystone.endpoint_present:
+ keystoneng.endpoint_present:
- name: {{ service.get('service', service_name) }}
- publicurl: '{{ service.bind.get('public_protocol', 'http') }}://{{ service.bind.public_address }}:{{ service.bind.public_port }}{{ service.bind.public_path }}'
- internalurl: '{{ service.bind.get('internal_protocol', 'http') }}://{{ service.bind.internal_address }}:{{ service.bind.internal_port }}{{ service.bind.internal_path }}'
@@ -404,12 +404,12 @@
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- require:
- - keystone: keystone_{{ service_name }}_service
+ - keystoneng: keystone_{{ service_name }}_service
{% if service.user is defined %}
keystone_user_{{ service.user.name }}:
- keystone.user_present:
+ keystoneng.user_present:
- name: {{ service.user.name }}
- password: {{ service.user.password }}
- email: {{ server.admin_email }}
@@ -420,7 +420,7 @@
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- require:
- - keystone: keystone_roles
+ - keystoneng: keystone_roles
{% endif %}
@@ -429,17 +429,17 @@
{%- for tenant_name, tenant in server.get('tenant', {}).iteritems() %}
keystone_tenant_{{ tenant_name }}:
- keystone.tenant_present:
+ keystoneng.tenant_present:
- name: {{ tenant_name }}
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- require:
- - keystone: keystone_roles
+ - keystoneng: keystone_roles
{%- for user_name, user in tenant.get('user', {}).iteritems() %}
keystone_user_{{ user_name }}:
- keystone.user_present:
+ keystoneng.user_present:
- name: {{ user_name }}
- password: {{ user.password }}
- email: {{ user.get('email', 'root@localhost') }}
@@ -454,7 +454,7 @@
- connection_token: {{ server.service_token }}
- connection_endpoint: 'http://{{ server.bind.address }}:{{ server.bind.private_port }}/v2.0'
- require:
- - keystone: keystone_tenant_{{ tenant_name }}
+ - keystoneng: keystone_tenant_{{ tenant_name }}
{%- endfor %}