# # Copyright 2017 Canonical Ltd # # 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. import charmhelpers.core as core import charmhelpers.core.host as ch_host import charmhelpers.core.hookenv as hookenv import charmhelpers.contrib.openstack.templating as os_templating import charmhelpers.contrib.openstack.utils as os_utils import charms_openstack.charm import charms_openstack.adapters import os # release detection is done via keystone package given that # openstack-origin is not present in the subordinate charm # see https://github.com/juju/charm-helpers/issues/83 import charmhelpers.core.unitdata as unitdata from charms_openstack.charm.core import ( register_os_release_selector ) OPENSTACK_RELEASE_KEY = 'charmers.openstack-release-version' DOMAIN_CONF = "/etc/keystone/domains/keystone.{}.conf" KEYSTONE_CONF_TEMPLATE = "keystone.conf" @register_os_release_selector def select_release(): """Determine the release based on the keystone package version. Note that this function caches the release after the first install so that it doesn't need to keep going and getting it from the package information. """ release_version = unitdata.kv().get(OPENSTACK_RELEASE_KEY, None) if release_version is None: release_version = os_utils.os_release('keystone') unitdata.kv().set(OPENSTACK_RELEASE_KEY, release_version) return release_version class KeystoneLDAPConfigurationAdapter( charms_openstack.adapters.ConfigurationAdapter): '''Charm specific configuration adapter to deal with ldap config flag parsing ''' @property def ldap_options(self): return os_utils.config_flags_parser( hookenv.config('ldap-config-flags') ) class KeystoneLDAPCharm(charms_openstack.charm.OpenStackCharm): # Internal name of charm service_name = name = 'keystone-ldap' # Package to derive application version from version_package = 'keystone' # First release supported release = 'mitaka' # List of packages to install for this charm packages = ['python-ldappool'] configuration_class = KeystoneLDAPConfigurationAdapter @property def domain_name(self): """Domain name for the running application :returns: string: containing the current domain name for the application """ return hookenv.config('domain-name') or hookenv.service_name() @staticmethod def configuration_complete(): """Determine whether sufficient configuration has been provided to configure keystone for use with a LDAP backend :returns: boolean indicating whether configuration is complete """ required_config = { 'ldap_server': hookenv.config('ldap-server'), 'ldap_user': hookenv.config('ldap-user'), 'ldap_password': hookenv.config('ldap-password'), 'ldap_suffix': hookenv.config('ldap-suffix'), } return all(required_config.values()) @property def configuration_file(self): """Configuration file for domain configuration""" return DOMAIN_CONF.format(self.domain_name) def assess_status(self): """Determine the current application status for the charm""" hookenv.application_version_set(self.application_version) if not self.configuration_complete(): hookenv.status_set('blocked', 'LDAP configuration incomplete') else: hookenv.status_set('active', 'Unit is ready') def render_config(self, restart_trigger): """Render the domain specific LDAP configuration for the application """ checksum = ch_host.file_hash(self.configuration_file) core.templating.render( source=KEYSTONE_CONF_TEMPLATE, template_loader=os_templating.get_loader( 'templates/', self.release), target=self.configuration_file, context=self.adapters_instance) if checksum != ch_host.file_hash(self.configuration_file): restart_trigger() def remove_config(self): """ Remove the domain-specific LDAP configuration file and trigger keystone restart. """ if os.path.exists(self.configuration_file): os.unlink(self.configuration_file)