Files
charm-heat/hooks/heat_context.py
Gabriel Cocenza 19cf71dc79 Add support for HAProxy L7 checks
This change add several configuration options to enable HTTP checks
to the HAProxy configuration, instead of the default TCP connection
checks (which continue to be the default). It also enables /healthcheck
endpoint for heat-api and heat-cfn-api on openstack releases >= queens.

Closes-Bug: #1880610
Change-Id: I94c9418c82cdddd5a5d9ed400ab47889bfb225b1
2023-02-17 12:05:27 -03:00

168 lines
5.3 KiB
Python

# Copyright 2016 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.
from charmhelpers.contrib.openstack import context
from charmhelpers.core.hookenv import (
config,
relation_ids,
related_units,
relation_get,
leader_get,
)
from charmhelpers.contrib.hahelpers.cluster import (
determine_apache_port,
determine_api_port,
https,
)
HEAT_PATH = '/var/lib/heat/'
API_PORTS = {
'heat-api-cfn': 8000,
'heat-api': 8004
}
def generate_ec2_tokens(protocol, host, port):
ec2_tokens = '%s://%s:%s/v2.0/ec2tokens' % (protocol, host, port)
return ec2_tokens
class HeatIdentityServiceContext(context.IdentityServiceContext):
def __call__(self):
ctxt = super(HeatIdentityServiceContext, self).__call__()
if not ctxt:
return
# the ec2 api needs to know the location of the keystone ec2
# tokens endpoint, set in nova.conf
ec2_tokens = generate_ec2_tokens(ctxt['service_protocol'] or 'http',
ctxt['service_host'],
ctxt['service_port'])
ctxt['keystone_ec2_url'] = ec2_tokens
ctxt['region'] = config('region')
return ctxt
def get_encryption_key():
encryption_key = config("encryption-key")
if not encryption_key:
encryption_key = leader_get('heat-auth-encryption-key')
return encryption_key
class HeatSecurityContext(context.OSContextGenerator):
def __call__(self):
ctxt = {}
# check if we have stored encryption key
ctxt['encryption_key'] = get_encryption_key()
ctxt['heat_domain_admin_passwd'] = (
leader_get('heat-domain-admin-passwd'))
return ctxt
class HeatHAProxyContext(context.OSContextGenerator):
interfaces = ['heat-haproxy']
def __call__(self):
"""Extends the main charmhelpers HAProxyContext with a port mapping
specific to this charm.
Also used to extend cinder.conf context with correct api_listening_port
"""
haproxy_port = API_PORTS['heat-api']
api_port = determine_api_port(haproxy_port, singlenode_mode=True)
apache_port = determine_apache_port(haproxy_port, singlenode_mode=True)
haproxy_cfn_port = API_PORTS['heat-api-cfn']
api_cfn_port = determine_api_port(haproxy_cfn_port,
singlenode_mode=True)
apache_cfn_port = determine_apache_port(haproxy_cfn_port,
singlenode_mode=True)
healthcheck = [{
'option': 'httpchk GET /healthcheck',
'http-check': 'expect status 200',
}]
backend_options = {
'heat_api': healthcheck,
'heat_cfn_api': healthcheck
}
ctxt = {
'service_ports': {'heat_api': [haproxy_port, apache_port],
'heat_cfn_api': [haproxy_cfn_port,
apache_cfn_port]},
'api_listen_port': api_port,
'api_cfn_listen_port': api_cfn_port,
'backend_options': backend_options,
'https': https(),
}
return ctxt
class HeatApacheSSLContext(context.ApacheSSLContext):
external_ports = API_PORTS.values()
service_namespace = 'heat'
class HeatPluginContext(context.SubordinateConfigContext):
interfaces = 'heat-plugin-subordinate'
def __init__(self):
super(HeatPluginContext, self).__init__(
interface='heat-plugin-subordinate',
service='heat',
config_file='/etc/heat/heat.conf')
def __call__(self):
ctxt = super(HeatPluginContext, self).__call__()
defaults = {
'plugin-dirs': {
'templ_key': 'plugin_dirs',
'value': '/usr/lib64/heat,/usr/lib/heat',
},
}
for rid in relation_ids('heat-plugin-subordinate'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
for key in defaults.keys():
remote_value = rdata.get(key)
ctxt_key = defaults[key]['templ_key']
if remote_value:
ctxt[ctxt_key] = remote_value
else:
ctxt[ctxt_key] = defaults[key]['value']
return ctxt
return ctxt
class InstanceUserContext(context.OSContextGenerator):
def __call__(self):
ctxt = {}
instance_user = ''
if config('instance-user'):
instance_user = config('instance-user')
ctxt['instance_user'] = instance_user
return ctxt
class QuotaConfigurationContext(context.OSContextGenerator):
def __call__(self):
ctxt = {"max_stacks_per_tenant": config('max-stacks-per-tenant')}
return ctxt