Add support for Keystone API version 3
This change enables the charm to configure the Openstack Dashboard to support Keystone v3 integration. Mitaka is the earliest release to support Dashboard and v3 integration so v3 integration should only be enabled on Mitaka or above. A new identity policy template now ships with the charm which is specifically for v3 integration. Both the local settings file and the new v3 policy file need the admin domain id. This is now passed to the charm from Keystone via the identity service relation. The openstack-dashboard package uses django.contrib.sessions.backends.signed_cookies for session management but cookies are not large enough to store domain scoped tokens so a different session management engine is needed. This patch adds the option to relate the charm to a database backend. If the relation is present then the charm uses the django.contrib.sessions.backends.cached_db session engine. This stores the session information in the database and also caches the session information locally using memcache. For details on Dashboard and v3 integration see https://wiki.openstack.org/wiki/Horizon/DomainWorkFlow Change-Id: I24f514e29811752d7c0c5347a1157d9778297738 Partial-Bug: 1595685
This commit is contained in:
parent
78e54d7546
commit
94df23fbc5
11
README.md
11
README.md
|
@ -70,6 +70,17 @@ If both 'vip' and 'dns-ha' are set as they are mutually exclusive
|
|||
If 'dns-ha' is set and none of the os-{admin,internal,public}-hostname(s) are
|
||||
set
|
||||
|
||||
Whichever method has been used to cluster the charm the 'secret' option
|
||||
should be set to ensure that the Django secret is consistent accross all units.
|
||||
|
||||
Keystone V3
|
||||
===========
|
||||
|
||||
If the charm is being deployed into a keystone v3 enabled environment then the
|
||||
charm needs to be related to a database to store session information. This is
|
||||
only supported for Mitaka or later.
|
||||
|
||||
|
||||
Use with a Load Balancing Proxy
|
||||
===============================
|
||||
|
||||
|
|
|
@ -290,3 +290,11 @@ options:
|
|||
description: |
|
||||
If True, redirects plain http requests to https port 443. For this option
|
||||
to have an effect, SSL must be configured.
|
||||
database-user:
|
||||
default: horizon
|
||||
type: string
|
||||
description: Username for Horizon database access (if enabled)
|
||||
database:
|
||||
default: horizon
|
||||
type: string
|
||||
description: Database name for Horizon (if enabled)
|
||||
|
|
|
@ -110,9 +110,14 @@ class IdentityServiceContext(OSContextGenerator):
|
|||
'service_port': rdata.get('service_port'),
|
||||
'service_host': serv_host,
|
||||
'service_protocol':
|
||||
rdata.get('service_protocol') or 'http'
|
||||
rdata.get('service_protocol') or 'http',
|
||||
'api_version': rdata.get('api_version', '2')
|
||||
}
|
||||
|
||||
# If using keystone v3 the context is incomplete without the
|
||||
# admin domain id
|
||||
if local_ctxt['api_version'] == '3':
|
||||
local_ctxt['admin_domain_id'] = rdata.get(
|
||||
'admin_domain_id')
|
||||
if not context_complete(local_ctxt):
|
||||
continue
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@ from charmhelpers.core.hookenv import (
|
|||
relation_ids,
|
||||
unit_get,
|
||||
status_set,
|
||||
network_get_primary_address,
|
||||
is_leader,
|
||||
local_unit,
|
||||
)
|
||||
from charmhelpers.fetch import (
|
||||
apt_update, apt_install,
|
||||
|
@ -28,6 +31,7 @@ from charmhelpers.contrib.openstack.utils import (
|
|||
openstack_upgrade_available,
|
||||
os_release,
|
||||
save_script_rc,
|
||||
sync_db_with_multi_ipv6_addresses,
|
||||
)
|
||||
from charmhelpers.contrib.openstack.ha.utils import (
|
||||
update_dns_ha_resource_params,
|
||||
|
@ -46,6 +50,7 @@ from horizon_utils import (
|
|||
INSTALL_DIR,
|
||||
restart_on_change,
|
||||
assess_status,
|
||||
db_migration,
|
||||
)
|
||||
from charmhelpers.contrib.network.ip import (
|
||||
get_iface_for_address,
|
||||
|
@ -157,7 +162,7 @@ def keystone_joined(rel_id=None):
|
|||
@hooks.hook('identity-service-relation-changed')
|
||||
@restart_on_change(restart_map(), stopstart=True, sleep=3)
|
||||
def keystone_changed():
|
||||
CONFIGS.write(LOCAL_SETTINGS)
|
||||
CONFIGS.write_all()
|
||||
if relation_get('ca_cert'):
|
||||
install_ca_cert(b64decode(relation_get('ca_cert')))
|
||||
|
||||
|
@ -289,6 +294,44 @@ def update_status():
|
|||
log('Updating status.')
|
||||
|
||||
|
||||
@hooks.hook('shared-db-relation-joined')
|
||||
def db_joined():
|
||||
if config('prefer-ipv6'):
|
||||
sync_db_with_multi_ipv6_addresses(config('database'),
|
||||
config('database-user'))
|
||||
else:
|
||||
host = None
|
||||
try:
|
||||
# NOTE: try to use network spaces
|
||||
host = network_get_primary_address('shared-db')
|
||||
except NotImplementedError:
|
||||
# NOTE: fallback to private-address
|
||||
host = unit_get('private-address')
|
||||
|
||||
relation_set(database=config('database'),
|
||||
username=config('database-user'),
|
||||
hostname=host)
|
||||
|
||||
|
||||
@hooks.hook('shared-db-relation-changed')
|
||||
@restart_on_change(restart_map(), stopstart=True, sleep=3)
|
||||
def db_changed():
|
||||
if 'shared-db' not in CONFIGS.complete_contexts():
|
||||
log('shared-db relation incomplete. Peer not ready?')
|
||||
return
|
||||
CONFIGS.write_all()
|
||||
if is_leader():
|
||||
allowed_units = relation_get('allowed_units')
|
||||
if allowed_units and local_unit() in allowed_units.split():
|
||||
db_migration()
|
||||
else:
|
||||
log('Not running neutron database migration, either no'
|
||||
' allowed_units or this unit is not present')
|
||||
return
|
||||
else:
|
||||
log('Not running neutron database migration, not leader')
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
hooks.execute(sys.argv)
|
||||
|
|
|
@ -93,6 +93,7 @@ GIT_PACKAGE_BLACKLIST = [
|
|||
|
||||
APACHE_CONF_DIR = "/etc/apache2"
|
||||
LOCAL_SETTINGS = "/etc/openstack-dashboard/local_settings.py"
|
||||
DASHBOARD_CONF_DIR = "/etc/openstack-dashboard/"
|
||||
HAPROXY_CONF = "/etc/haproxy/haproxy.cfg"
|
||||
APACHE_CONF = "%s/conf.d/openstack-dashboard.conf" % (APACHE_CONF_DIR)
|
||||
APACHE_24_CONF = "%s/conf-available/openstack-dashboard.conf" \
|
||||
|
@ -103,9 +104,10 @@ APACHE_24_DEFAULT = "%s/sites-available/000-default.conf" % (APACHE_CONF_DIR)
|
|||
APACHE_SSL = "%s/sites-available/default-ssl" % (APACHE_CONF_DIR)
|
||||
APACHE_DEFAULT = "%s/sites-available/default" % (APACHE_CONF_DIR)
|
||||
INSTALL_DIR = "/usr/share/openstack-dashboard"
|
||||
ROUTER_SETTING = \
|
||||
"/usr/share/openstack-dashboard/openstack_dashboard/enabled/_40_router.py"
|
||||
|
||||
ROUTER_SETTING = ('/usr/share/openstack-dashboard/openstack_dashboard/enabled/'
|
||||
'_40_router.py')
|
||||
KEYSTONEV3_POLICY = ('/usr/share/openstack-dashboard/openstack_dashboard/conf/'
|
||||
'keystonev3_policy.json')
|
||||
TEMPLATES = 'templates'
|
||||
|
||||
CONFIG_FILES = OrderedDict([
|
||||
|
@ -159,6 +161,10 @@ CONFIG_FILES = OrderedDict([
|
|||
'hook_contexts': [horizon_contexts.RouterSettingContext()],
|
||||
'services': ['apache2'],
|
||||
}),
|
||||
(KEYSTONEV3_POLICY, {
|
||||
'hook_contexts': [horizon_contexts.IdentityServiceContext()],
|
||||
'services': ['apache2'],
|
||||
}),
|
||||
])
|
||||
|
||||
|
||||
|
@ -172,6 +178,15 @@ def register_configs():
|
|||
HAPROXY_CONF,
|
||||
PORTS_CONF]
|
||||
|
||||
if release >= 'mitaka':
|
||||
configs.register(KEYSTONEV3_POLICY,
|
||||
CONFIG_FILES[KEYSTONEV3_POLICY]['hook_contexts'])
|
||||
CONFIG_FILES[LOCAL_SETTINGS]['hook_contexts'].append(
|
||||
context.SharedDBContext(
|
||||
user=config('database-user'),
|
||||
database=config('database'),
|
||||
ssl_dir=DASHBOARD_CONF_DIR))
|
||||
|
||||
for conf in confs:
|
||||
configs.register(conf, CONFIG_FILES[conf]['hook_contexts'])
|
||||
|
||||
|
@ -197,6 +212,7 @@ def register_configs():
|
|||
if os.path.exists(os.path.dirname(ROUTER_SETTING)):
|
||||
configs.register(ROUTER_SETTING,
|
||||
CONFIG_FILES[ROUTER_SETTING]['hook_contexts'])
|
||||
|
||||
return configs
|
||||
|
||||
|
||||
|
@ -243,6 +259,10 @@ def determine_packages():
|
|||
for p in GIT_PACKAGE_BLACKLIST:
|
||||
packages.remove(p)
|
||||
|
||||
release = get_os_codename_install_source(config('openstack-origin'))
|
||||
# Really should be handled as a dep in the openstack-dashboard package
|
||||
if release >= 'mitaka':
|
||||
packages.append('python-pymysql')
|
||||
return list(set(packages))
|
||||
|
||||
|
||||
|
@ -546,3 +566,8 @@ def _pause_resume_helper(f, configs):
|
|||
f(assess_status_func(configs),
|
||||
services=services(),
|
||||
ports=None)
|
||||
|
||||
|
||||
def db_migration():
|
||||
cmd = ['/usr/share/openstack-dashboard/manage.py', 'syncdb', '--noinput']
|
||||
subprocess.call(cmd)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -0,0 +1 @@
|
|||
horizon_hooks.py
|
|
@ -22,6 +22,8 @@ requires:
|
|||
ha:
|
||||
interface: hacluster
|
||||
scope: container
|
||||
shared-db:
|
||||
interface: mysql-shared
|
||||
peers:
|
||||
cluster:
|
||||
interface: openstack-dashboard-ha
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
{
|
||||
"admin_required": "role:Admin",
|
||||
"cloud_admin": "rule:admin_required and domain_id:{{ admin_domain_id }}",
|
||||
"service_role": "role:service",
|
||||
"service_or_admin": "rule:admin_required or rule:service_role",
|
||||
"owner" : "user_id:%(user_id)s or user_id:%(target.token.user_id)s",
|
||||
"admin_or_owner": "(rule:admin_required and domain_id:%(target.token.user.domain.id)s) or rule:owner",
|
||||
"admin_and_matching_domain_id": "rule:admin_required and domain_id:%(domain_id)s",
|
||||
"service_admin_or_owner": "rule:service_or_admin or rule:owner",
|
||||
|
||||
"default": "rule:admin_required",
|
||||
|
||||
"identity:get_region": "",
|
||||
"identity:list_regions": "",
|
||||
"identity:create_region": "rule:cloud_admin",
|
||||
"identity:update_region": "rule:cloud_admin",
|
||||
"identity:delete_region": "rule:cloud_admin",
|
||||
|
||||
"identity:get_service": "rule:admin_required",
|
||||
"identity:list_services": "rule:admin_required",
|
||||
"identity:create_service": "rule:cloud_admin",
|
||||
"identity:update_service": "rule:cloud_admin",
|
||||
"identity:delete_service": "rule:cloud_admin",
|
||||
|
||||
"identity:get_endpoint": "rule:admin_required",
|
||||
"identity:list_endpoints": "rule:admin_required",
|
||||
"identity:create_endpoint": "rule:cloud_admin",
|
||||
"identity:update_endpoint": "rule:cloud_admin",
|
||||
"identity:delete_endpoint": "rule:cloud_admin",
|
||||
|
||||
"identity:get_domain": "rule:cloud_admin or rule:admin_and_matching_domain_id",
|
||||
"identity:list_domains": "rule:cloud_admin",
|
||||
"identity:create_domain": "rule:cloud_admin",
|
||||
"identity:update_domain": "rule:cloud_admin",
|
||||
"identity:delete_domain": "rule:cloud_admin",
|
||||
|
||||
"admin_and_matching_target_project_domain_id": "rule:admin_required and domain_id:%(target.project.domain_id)s",
|
||||
"admin_and_matching_project_domain_id": "rule:admin_required and domain_id:%(project.domain_id)s",
|
||||
"identity:get_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id or project_id:%(target.project.id)s",
|
||||
"identity:list_projects": "rule:cloud_admin or rule:admin_and_matching_domain_id",
|
||||
"identity:list_user_projects": "rule:owner or rule:admin_and_matching_domain_id",
|
||||
"identity:create_project": "rule:cloud_admin or rule:admin_and_matching_project_domain_id",
|
||||
"identity:update_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id",
|
||||
"identity:delete_project": "rule:cloud_admin or rule:admin_and_matching_target_project_domain_id",
|
||||
|
||||
"admin_and_matching_target_user_domain_id": "rule:admin_required and domain_id:%(target.user.domain_id)s",
|
||||
"admin_and_matching_user_domain_id": "rule:admin_required and domain_id:%(user.domain_id)s",
|
||||
"identity:get_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id",
|
||||
"identity:list_users": "rule:cloud_admin or rule:admin_and_matching_domain_id",
|
||||
"identity:create_user": "rule:cloud_admin or rule:admin_and_matching_user_domain_id",
|
||||
"identity:update_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id",
|
||||
"identity:delete_user": "rule:cloud_admin or rule:admin_and_matching_target_user_domain_id",
|
||||
|
||||
"admin_and_matching_target_group_domain_id": "rule:admin_required and domain_id:%(target.group.domain_id)s",
|
||||
"admin_and_matching_group_domain_id": "rule:admin_required and domain_id:%(group.domain_id)s",
|
||||
"identity:get_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:list_groups": "rule:cloud_admin or rule:admin_and_matching_domain_id",
|
||||
"identity:list_groups_for_user": "rule:owner or rule:admin_and_matching_target_user_domain_id",
|
||||
"identity:create_group": "rule:cloud_admin or rule:admin_and_matching_group_domain_id",
|
||||
"identity:update_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:delete_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:list_users_in_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:remove_user_from_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:check_user_in_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
"identity:add_user_to_group": "rule:cloud_admin or rule:admin_and_matching_target_group_domain_id",
|
||||
|
||||
"identity:get_credential": "rule:admin_required",
|
||||
"identity:list_credentials": "rule:admin_required or user_id:%(user_id)s",
|
||||
"identity:create_credential": "rule:admin_required",
|
||||
"identity:update_credential": "rule:admin_required",
|
||||
"identity:delete_credential": "rule:admin_required",
|
||||
|
||||
"identity:ec2_get_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)",
|
||||
"identity:ec2_list_credentials": "rule:admin_required or rule:owner",
|
||||
"identity:ec2_create_credential": "rule:admin_required or rule:owner",
|
||||
"identity:ec2_delete_credential": "rule:admin_required or (rule:owner and user_id:%(target.credential.user_id)s)",
|
||||
|
||||
"identity:get_role": "rule:admin_required",
|
||||
"identity:list_roles": "rule:admin_required",
|
||||
"identity:create_role": "rule:cloud_admin",
|
||||
"identity:update_role": "rule:cloud_admin",
|
||||
"identity:delete_role": "rule:cloud_admin",
|
||||
|
||||
"identity:get_domain_role": "rule:cloud_admin or rule:get_domain_roles",
|
||||
"identity:list_domain_roles": "rule:cloud_admin or rule:list_domain_roles",
|
||||
"identity:create_domain_role": "rule:cloud_admin or rule:domain_admin_matches_domain_role",
|
||||
"identity:update_domain_role": "rule:cloud_admin or rule:domain_admin_matches_target_domain_role",
|
||||
"identity:delete_domain_role": "rule:cloud_admin or rule:domain_admin_matches_target_domain_role",
|
||||
"domain_admin_matches_domain_role": "rule:admin_required and domain_id:%(role.domain_id)s",
|
||||
"get_domain_roles": "rule:domain_admin_matches_target_domain_role or rule:project_admin_matches_target_domain_role",
|
||||
"domain_admin_matches_target_domain_role": "rule:admin_required and domain_id:%(target.role.domain_id)s",
|
||||
"project_admin_matches_target_domain_role": "rule:admin_required and project_domain_id:%(target.role.domain_id)s",
|
||||
"list_domain_roles": "rule:domain_admin_matches_filter_on_list_domain_roles or rule:project_admin_matches_filter_on_list_domain_roles",
|
||||
"domain_admin_matches_filter_on_list_domain_roles": "rule:admin_required and domain_id:%(domain_id)s",
|
||||
"project_admin_matches_filter_on_list_domain_roles": "rule:admin_required and project_domain_id:%(domain_id)s",
|
||||
|
||||
"identity:get_implied_role": "rule:cloud_admin",
|
||||
"identity:list_implied_roles": "rule:cloud_admin",
|
||||
"identity:create_implied_role": "rule:cloud_admin",
|
||||
"identity:delete_implied_role": "rule:cloud_admin",
|
||||
"identity:list_role_inference_rules": "rule:cloud_admin",
|
||||
"identity:check_implied_role": "rule:cloud_admin",
|
||||
|
||||
"identity:check_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants",
|
||||
"identity:list_grants": "rule:cloud_admin or rule:domain_admin_for_list_grants or rule:project_admin_for_list_grants",
|
||||
"identity:create_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants",
|
||||
"identity:revoke_grant": "rule:cloud_admin or rule:domain_admin_for_grants or rule:project_admin_for_grants",
|
||||
"domain_admin_for_grants": "rule:domain_admin_for_global_role_grants or rule:domain_admin_for_domain_role_grants",
|
||||
"domain_admin_for_global_role_grants": "rule:admin_required and None:%(target.role.domain_id)s and rule:domain_admin_grant_match",
|
||||
"domain_admin_for_domain_role_grants": "rule:admin_required and domain_id:%(target.role.domain_id)s and rule:domain_admin_grant_match",
|
||||
"domain_admin_grant_match": "domain_id:%(domain_id)s or domain_id:%(target.project.domain_id)s",
|
||||
"project_admin_for_grants": "rule:project_admin_for_global_role_grants or rule:project_admin_for_domain_role_grants",
|
||||
"project_admin_for_global_role_grants": "rule:admin_required and None:%(target.role.domain_id)s and project_id:%(project_id)s",
|
||||
"project_admin_for_domain_role_grants": "rule:admin_required and project_domain_id:%(target.role.domain_id)s and project_id:%(project_id)s",
|
||||
"domain_admin_for_list_grants": "rule:admin_required and rule:domain_admin_grant_match",
|
||||
"project_admin_for_list_grants": "rule:admin_required and project_id:%(project_id)s",
|
||||
|
||||
"admin_on_domain_filter" : "rule:admin_required and domain_id:%(scope.domain.id)s",
|
||||
"admin_on_project_filter" : "rule:admin_required and project_id:%(scope.project.id)s",
|
||||
"admin_on_domain_of_project_filter" : "rule:admin_required and domain_id:%(target.project.domain_id)s",
|
||||
"identity:list_role_assignments": "rule:cloud_admin or rule:admin_on_domain_filter or rule:admin_on_project_filter",
|
||||
"identity:list_role_assignments_for_tree": "rule:cloud_admin or rule:admin_on_domain_of_project_filter",
|
||||
"identity:get_policy": "rule:cloud_admin",
|
||||
"identity:list_policies": "rule:cloud_admin",
|
||||
"identity:create_policy": "rule:cloud_admin",
|
||||
"identity:update_policy": "rule:cloud_admin",
|
||||
"identity:delete_policy": "rule:cloud_admin",
|
||||
|
||||
"identity:change_password": "rule:owner",
|
||||
"identity:check_token": "rule:admin_or_owner",
|
||||
"identity:validate_token": "rule:service_admin_or_owner",
|
||||
"identity:validate_token_head": "rule:service_or_admin",
|
||||
"identity:revocation_list": "rule:service_or_admin",
|
||||
"identity:revoke_token": "rule:admin_or_owner",
|
||||
|
||||
"identity:create_trust": "user_id:%(trust.trustor_user_id)s",
|
||||
"identity:list_trusts": "",
|
||||
"identity:list_roles_for_trust": "",
|
||||
"identity:get_role_for_trust": "",
|
||||
"identity:delete_trust": "",
|
||||
|
||||
"identity:create_consumer": "rule:admin_required",
|
||||
"identity:get_consumer": "rule:admin_required",
|
||||
"identity:list_consumers": "rule:admin_required",
|
||||
"identity:delete_consumer": "rule:admin_required",
|
||||
"identity:update_consumer": "rule:admin_required",
|
||||
|
||||
"identity:authorize_request_token": "rule:admin_required",
|
||||
"identity:list_access_token_roles": "rule:admin_required",
|
||||
"identity:get_access_token_role": "rule:admin_required",
|
||||
"identity:list_access_tokens": "rule:admin_required",
|
||||
"identity:get_access_token": "rule:admin_required",
|
||||
"identity:delete_access_token": "rule:admin_required",
|
||||
|
||||
"identity:list_projects_for_endpoint": "rule:admin_required",
|
||||
"identity:add_endpoint_to_project": "rule:admin_required",
|
||||
"identity:check_endpoint_in_project": "rule:admin_required",
|
||||
"identity:list_endpoints_for_project": "rule:admin_required",
|
||||
"identity:remove_endpoint_from_project": "rule:admin_required",
|
||||
|
||||
"identity:create_endpoint_group": "rule:admin_required",
|
||||
"identity:list_endpoint_groups": "rule:admin_required",
|
||||
"identity:get_endpoint_group": "rule:admin_required",
|
||||
"identity:update_endpoint_group": "rule:admin_required",
|
||||
"identity:delete_endpoint_group": "rule:admin_required",
|
||||
"identity:list_projects_associated_with_endpoint_group": "rule:admin_required",
|
||||
"identity:list_endpoints_associated_with_endpoint_group": "rule:admin_required",
|
||||
"identity:get_endpoint_group_in_project": "rule:admin_required",
|
||||
"identity:list_endpoint_groups_for_project": "rule:admin_required",
|
||||
"identity:add_endpoint_group_to_project": "rule:admin_required",
|
||||
"identity:remove_endpoint_group_from_project": "rule:admin_required",
|
||||
|
||||
"identity:create_identity_provider": "rule:cloud_admin",
|
||||
"identity:list_identity_providers": "rule:cloud_admin",
|
||||
"identity:get_identity_providers": "rule:cloud_admin",
|
||||
"identity:update_identity_provider": "rule:cloud_admin",
|
||||
"identity:delete_identity_provider": "rule:cloud_admin",
|
||||
|
||||
"identity:create_protocol": "rule:cloud_admin",
|
||||
"identity:update_protocol": "rule:cloud_admin",
|
||||
"identity:get_protocol": "rule:cloud_admin",
|
||||
"identity:list_protocols": "rule:cloud_admin",
|
||||
"identity:delete_protocol": "rule:cloud_admin",
|
||||
|
||||
"identity:create_mapping": "rule:cloud_admin",
|
||||
"identity:get_mapping": "rule:cloud_admin",
|
||||
"identity:list_mappings": "rule:cloud_admin",
|
||||
"identity:delete_mapping": "rule:cloud_admin",
|
||||
"identity:update_mapping": "rule:cloud_admin",
|
||||
|
||||
"identity:create_service_provider": "rule:cloud_admin",
|
||||
"identity:list_service_providers": "rule:cloud_admin",
|
||||
"identity:get_service_provider": "rule:cloud_admin",
|
||||
"identity:update_service_provider": "rule:cloud_admin",
|
||||
"identity:delete_service_provider": "rule:cloud_admin",
|
||||
|
||||
"identity:get_auth_catalog": "",
|
||||
"identity:get_auth_projects": "",
|
||||
"identity:get_auth_domains": "",
|
||||
|
||||
"identity:list_projects_for_groups": "",
|
||||
"identity:list_domains_for_groups": "",
|
||||
|
||||
"identity:list_revoke_events": "",
|
||||
|
||||
"identity:create_policy_association_for_endpoint": "rule:cloud_admin",
|
||||
"identity:check_policy_association_for_endpoint": "rule:cloud_admin",
|
||||
"identity:delete_policy_association_for_endpoint": "rule:cloud_admin",
|
||||
"identity:create_policy_association_for_service": "rule:cloud_admin",
|
||||
"identity:check_policy_association_for_service": "rule:cloud_admin",
|
||||
"identity:delete_policy_association_for_service": "rule:cloud_admin",
|
||||
"identity:create_policy_association_for_region_and_service": "rule:cloud_admin",
|
||||
"identity:check_policy_association_for_region_and_service": "rule:cloud_admin",
|
||||
"identity:delete_policy_association_for_region_and_service": "rule:cloud_admin",
|
||||
"identity:get_policy_for_endpoint": "rule:cloud_admin",
|
||||
"identity:list_endpoints_for_policy": "rule:cloud_admin",
|
||||
|
||||
"identity:create_domain_config": "rule:cloud_admin",
|
||||
"identity:get_domain_config": "rule:cloud_admin",
|
||||
"identity:update_domain_config": "rule:cloud_admin",
|
||||
"identity:delete_domain_config": "rule:cloud_admin",
|
||||
"identity:get_domain_config_default": "rule:cloud_admin"
|
||||
}
|
|
@ -137,7 +137,25 @@ CACHES = {
|
|||
'LOCATION': '127.0.0.1:11211',
|
||||
},
|
||||
}
|
||||
|
||||
{% if database_host -%}
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
|
||||
DATABASES = {
|
||||
'default': {
|
||||
# Database configuration here
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': '{{ database }}',
|
||||
'USER': '{{ database_user }}',
|
||||
'PASSWORD': '{{ database_password }}',
|
||||
'HOST': '{{ database_host }}',
|
||||
'default-character-set': 'utf8'
|
||||
}
|
||||
}
|
||||
{% else -%}
|
||||
{% if api_version == "3" -%}
|
||||
# Warning: Please add DB relation for Keystone v3 + HA deployments
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
{% endif -%}
|
||||
{% endif -%}
|
||||
#CACHES = {
|
||||
# 'default': {
|
||||
# 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
|
@ -169,8 +187,16 @@ AVAILABLE_REGIONS = [
|
|||
{% endif -%}
|
||||
|
||||
OPENSTACK_HOST = "{{ service_host }}"
|
||||
OPENSTACK_KEYSTONE_URL = "{{ service_protocol }}://%s:{{ service_port }}/v2.0" % OPENSTACK_HOST
|
||||
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "{{ default_role }}"
|
||||
{% if api_version == "3" -%}
|
||||
OPENSTACK_KEYSTONE_URL = "{{ service_protocol }}://%s:{{ service_port }}/v3" % OPENSTACK_HOST
|
||||
OPENSTACK_API_VERSIONS = { "identity": 3, }
|
||||
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True
|
||||
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = "{{ admin_domain_id }}"
|
||||
{% else -%}
|
||||
OPENSTACK_KEYSTONE_URL = "{{ service_protocol }}://%s:{{ service_port }}/v2.0" % OPENSTACK_HOST
|
||||
{% endif -%}
|
||||
|
||||
|
||||
# Enables keystone web single-sign-on if set to True.
|
||||
#WEBSSO_ENABLED = False
|
||||
|
@ -435,15 +461,17 @@ TIME_ZONE = "UTC"
|
|||
# policy.v3cloudsample.json
|
||||
# Having matching policy files on the Horizon and Keystone servers is essential
|
||||
# for normal operation. This holds true for all services and their policy files.
|
||||
#POLICY_FILES = {
|
||||
# 'identity': 'keystone_policy.json',
|
||||
# 'compute': 'nova_policy.json',
|
||||
# 'volume': 'cinder_policy.json',
|
||||
# 'image': 'glance_policy.json',
|
||||
# 'orchestration': 'heat_policy.json',
|
||||
# 'network': 'neutron_policy.json',
|
||||
# 'telemetry': 'ceilometer_policy.json',
|
||||
#}
|
||||
{% if api_version == "3" -%}
|
||||
POLICY_FILES = {
|
||||
'identity': 'keystonev3_policy.json',
|
||||
'compute': 'nova_policy.json',
|
||||
'volume': 'cinder_policy.json',
|
||||
'image': 'glance_policy.json',
|
||||
'orchestration': 'heat_policy.json',
|
||||
'network': 'neutron_policy.json',
|
||||
'telemetry': 'ceilometer_policy.json',
|
||||
}
|
||||
{% endif -%}
|
||||
|
||||
# TODO: (david-lyle) remove when plugins support adding settings.
|
||||
# Note: Only used when trove-dashboard plugin is configured to be used by
|
||||
|
|
|
@ -210,7 +210,7 @@ class TestHorizonContexts(CharmTestCase):
|
|||
self.context_complete.return_value = True
|
||||
self.assertEquals(horizon_contexts.IdentityServiceContext()(),
|
||||
{'service_host': 'foo', 'service_port': 5000,
|
||||
'service_protocol': 'http'})
|
||||
'api_version': '2', 'service_protocol': 'http'})
|
||||
|
||||
@patch("horizon_contexts.format_ipv6_addr")
|
||||
def test_IdentityServiceContext_single_region(self, mock_format_ipv6_addr):
|
||||
|
@ -223,7 +223,7 @@ class TestHorizonContexts(CharmTestCase):
|
|||
self.context_complete.return_value = True
|
||||
self.assertEquals(horizon_contexts.IdentityServiceContext()(),
|
||||
{'service_host': 'foo', 'service_port': 5000,
|
||||
'service_protocol': 'http'})
|
||||
'api_version': '2', 'service_protocol': 'http'})
|
||||
|
||||
@patch("horizon_contexts.format_ipv6_addr")
|
||||
def test_IdentityServiceContext_multi_region(self, mock_format_ipv6_addr):
|
||||
|
@ -236,12 +236,46 @@ class TestHorizonContexts(CharmTestCase):
|
|||
self.context_complete.return_value = True
|
||||
self.assertEqual(horizon_contexts.IdentityServiceContext()(),
|
||||
{'service_host': 'foo', 'service_port': 5000,
|
||||
'service_protocol': 'http',
|
||||
'service_protocol': 'http', 'api_version': '2',
|
||||
'regions': [{'endpoint': 'http://foo:5000/v2.0',
|
||||
'title': 'regionOne'},
|
||||
{'endpoint': 'http://foo:5000/v2.0',
|
||||
'title': 'regionTwo'}]})
|
||||
|
||||
@patch("horizon_contexts.format_ipv6_addr")
|
||||
def test_IdentityServiceContext_api3(self, mock_format_ipv6_addr):
|
||||
mock_format_ipv6_addr.return_value = "foo"
|
||||
self.relation_ids.return_value = ['foo']
|
||||
self.related_units.return_value = ['bar', 'baz']
|
||||
self.relation_get.side_effect = self.test_relation.get
|
||||
self.test_relation.set({
|
||||
'service_host': 'foo',
|
||||
'service_port': 5000,
|
||||
'region': 'regionOne',
|
||||
'api_version': '3',
|
||||
'admin_domain_id': 'admindomainid'})
|
||||
self.context_complete.return_value = True
|
||||
self.assertEquals(horizon_contexts.IdentityServiceContext()(), {
|
||||
'service_host': 'foo',
|
||||
'service_port': 5000,
|
||||
'api_version': '3',
|
||||
'admin_domain_id': 'admindomainid',
|
||||
'service_protocol': 'http'})
|
||||
|
||||
@patch("horizon_contexts.format_ipv6_addr")
|
||||
def test_IdentityServiceContext_api3_missing(self, mock_format_ipv6_addr):
|
||||
mock_format_ipv6_addr.return_value = "foo"
|
||||
self.relation_ids.return_value = ['foo']
|
||||
self.related_units.return_value = ['bar', 'baz']
|
||||
self.relation_get.side_effect = self.test_relation.get
|
||||
self.test_relation.set({
|
||||
'service_host': 'foo',
|
||||
'service_port': 5000,
|
||||
'region': 'regionOne',
|
||||
'api_version': '3'})
|
||||
self.context_complete.return_value = False
|
||||
self.assertEquals(horizon_contexts.IdentityServiceContext()(), {})
|
||||
|
||||
def test_IdentityServiceContext_endpoint_type(self):
|
||||
self.test_config.set('endpoint-type', 'internalURL')
|
||||
self.assertEqual(horizon_contexts.IdentityServiceContext()(),
|
||||
|
|
|
@ -72,8 +72,10 @@ class TestHorizonHooks(CharmTestCase):
|
|||
hooks.hooks.execute([
|
||||
'hooks/{}'.format(hookname)])
|
||||
|
||||
@patch.object(hooks, 'determine_packages')
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_install_hook(self, _git_requested):
|
||||
def test_install_hook(self, _git_requested, _determine_packages):
|
||||
_determine_packages.return_value = []
|
||||
_git_requested.return_value = False
|
||||
self.filter_installed_packages.return_value = ['foo', 'bar']
|
||||
self.os_release.return_value = 'icehouse'
|
||||
|
@ -82,8 +84,10 @@ class TestHorizonHooks(CharmTestCase):
|
|||
self.apt_update.assert_called_with(fatal=True)
|
||||
self.apt_install.assert_called_with(['foo', 'bar'], fatal=True)
|
||||
|
||||
@patch.object(hooks, 'determine_packages')
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_install_hook_precise(self, _git_requested):
|
||||
def test_install_hook_precise(self, _git_requested, _determine_packages):
|
||||
_determine_packages.return_value = []
|
||||
_git_requested.return_value = False
|
||||
self.filter_installed_packages.return_value = ['foo', 'bar']
|
||||
self.os_release.return_value = 'icehouse'
|
||||
|
@ -97,8 +101,11 @@ class TestHorizonHooks(CharmTestCase):
|
|||
]
|
||||
self.apt_install.assert_has_calls(calls)
|
||||
|
||||
@patch.object(hooks, 'determine_packages')
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_install_hook_icehouse_pkgs(self, _git_requested):
|
||||
def test_install_hook_icehouse_pkgs(self, _git_requested,
|
||||
_determine_packages):
|
||||
_determine_packages.return_value = []
|
||||
_git_requested.return_value = False
|
||||
self.os_release.return_value = 'icehouse'
|
||||
self._call_hook('install.real')
|
||||
|
@ -108,8 +115,11 @@ class TestHorizonHooks(CharmTestCase):
|
|||
)
|
||||
self.assertTrue(self.apt_install.called)
|
||||
|
||||
@patch.object(hooks, 'determine_packages')
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_install_hook_pre_icehouse_pkgs(self, _git_requested):
|
||||
def test_install_hook_pre_icehouse_pkgs(self, _git_requested,
|
||||
_determine_packages):
|
||||
_determine_packages.return_value = []
|
||||
_git_requested.return_value = False
|
||||
self.os_release.return_value = 'grizzly'
|
||||
self._call_hook('install.real')
|
||||
|
@ -119,8 +129,10 @@ class TestHorizonHooks(CharmTestCase):
|
|||
)
|
||||
self.assertTrue(self.apt_install.called)
|
||||
|
||||
@patch.object(hooks, 'determine_packages')
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_install_hook_git(self, _git_requested):
|
||||
def test_install_hook_git(self, _git_requested, _determine_packages):
|
||||
_determine_packages.return_value = []
|
||||
_git_requested.return_value = True
|
||||
self.filter_installed_packages.return_value = ['foo', 'bar']
|
||||
repo = 'cloud:trusty-juno'
|
||||
|
@ -145,10 +157,13 @@ class TestHorizonHooks(CharmTestCase):
|
|||
self.apt_install.assert_called_with(['foo', 'bar'], fatal=True)
|
||||
self.git_install.assert_called_with(projects_yaml)
|
||||
|
||||
@patch.object(hooks, 'determine_packages')
|
||||
@patch.object(utils, 'path_hash')
|
||||
@patch.object(utils, 'service')
|
||||
@patch.object(utils, 'git_install_requested')
|
||||
def test_upgrade_charm_hook(self, _git_requested, _service, _hash):
|
||||
def test_upgrade_charm_hook(self, _git_requested, _service, _hash,
|
||||
_determine_packages):
|
||||
_determine_packages.return_value = []
|
||||
_git_requested.return_value = False
|
||||
side_effects = []
|
||||
[side_effects.append(None) for f in RESTART_MAP.keys()]
|
||||
|
@ -337,17 +352,13 @@ class TestHorizonHooks(CharmTestCase):
|
|||
def test_keystone_changed_no_cert(self):
|
||||
self.relation_get.return_value = None
|
||||
self._call_hook('identity-service-relation-changed')
|
||||
self.CONFIGS.write.assert_called_with(
|
||||
'/etc/openstack-dashboard/local_settings.py'
|
||||
)
|
||||
self.CONFIGS.write_all.assert_called_with()
|
||||
self.install_ca_cert.assert_not_called()
|
||||
|
||||
def test_keystone_changed_cert(self):
|
||||
self.relation_get.return_value = 'certificate'
|
||||
self._call_hook('identity-service-relation-changed')
|
||||
self.CONFIGS.write.assert_called_with(
|
||||
'/etc/openstack-dashboard/local_settings.py'
|
||||
)
|
||||
self.CONFIGS.write_all.assert_called_with()
|
||||
self.install_ca_cert.assert_called_with('certificate')
|
||||
|
||||
def test_cluster_departed(self):
|
||||
|
|
|
@ -35,6 +35,29 @@ class TestHorizohorizon_utils(CharmTestCase):
|
|||
def setUp(self):
|
||||
super(TestHorizohorizon_utils, self).setUp(horizon_utils, TO_PATCH)
|
||||
|
||||
@patch.object(horizon_utils, 'get_os_codename_install_source')
|
||||
@patch.object(horizon_utils, 'git_install_requested')
|
||||
def test_determine_packages(self, _git_install_requested,
|
||||
_get_os_codename_install_source):
|
||||
_git_install_requested.return_value = False
|
||||
_get_os_codename_install_source.return_value = 'icehouse'
|
||||
self.assertEqual(horizon_utils.determine_packages(), [
|
||||
'haproxy',
|
||||
'python-novaclient',
|
||||
'python-keystoneclient',
|
||||
'openstack-dashboard-ubuntu-theme',
|
||||
'python-memcache',
|
||||
'openstack-dashboard',
|
||||
'memcached'])
|
||||
|
||||
@patch.object(horizon_utils, 'get_os_codename_install_source')
|
||||
@patch.object(horizon_utils, 'git_install_requested')
|
||||
def test_determine_packages_mitaka(self, _git_install_requested,
|
||||
_get_os_codename_install_source):
|
||||
_git_install_requested.return_value = False
|
||||
_get_os_codename_install_source.return_value = 'mitaka'
|
||||
self.assertTrue('python-pymysql' in horizon_utils.determine_packages())
|
||||
|
||||
@patch('subprocess.call')
|
||||
def test_enable_ssl(self, _call):
|
||||
horizon_utils.enable_ssl()
|
||||
|
@ -57,6 +80,8 @@ class TestHorizohorizon_utils(CharmTestCase):
|
|||
('/etc/haproxy/haproxy.cfg', ['haproxy']),
|
||||
('/usr/share/openstack-dashboard/openstack_dashboard/enabled/'
|
||||
'_40_router.py', ['apache2']),
|
||||
('/usr/share/openstack-dashboard/openstack_dashboard/conf/'
|
||||
'keystonev3_policy.json', ['apache2']),
|
||||
])
|
||||
self.assertEquals(horizon_utils.restart_map(), ex_map)
|
||||
|
||||
|
|
Loading…
Reference in New Issue