663 lines
24 KiB
Python
663 lines
24 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.
|
|
|
|
import json
|
|
import os
|
|
|
|
import base64
|
|
|
|
import charmhelpers.contrib.hahelpers.cluster as ch_cluster
|
|
import charmhelpers.contrib.network.ip as ch_network_ip
|
|
import charmhelpers.contrib.openstack.context as ch_context
|
|
import charmhelpers.contrib.openstack.ip as ch_ip
|
|
import charmhelpers.contrib.openstack.neutron as ch_neutron
|
|
import charmhelpers.contrib.openstack.utils as ch_utils
|
|
import charmhelpers.core.hookenv as hookenv
|
|
|
|
import hooks.nova_cc_common as common
|
|
|
|
|
|
def context_complete(ctxt):
|
|
_missing = []
|
|
for k, v in ctxt.items():
|
|
if v is None or v == '':
|
|
_missing.append(k)
|
|
if _missing:
|
|
hookenv.log('Missing required data: %s' % ' '.join(_missing),
|
|
level='INFO')
|
|
return False
|
|
return True
|
|
|
|
|
|
class ApacheSSLContext(ch_context.ApacheSSLContext):
|
|
|
|
interfaces = ['https']
|
|
external_ports = []
|
|
service_namespace = 'nova'
|
|
|
|
# NOTE(fnordahl): The novncproxy service runs as user ``nova`` throughout
|
|
# its lifespan, and it has no load certificates before dropping privileges
|
|
# mechanism.
|
|
#
|
|
# Set file permissions on certificate files to support this. LP: #1819140
|
|
group = 'nova'
|
|
|
|
def __init__(self, _external_ports_maybe_callable):
|
|
self._external_ports_maybe_callable = _external_ports_maybe_callable
|
|
self.external_ports = None
|
|
super(ApacheSSLContext, self).__init__()
|
|
|
|
def __call__(self):
|
|
if self.external_ports is None:
|
|
if callable(self._external_ports_maybe_callable):
|
|
self.external_ports = self._external_ports_maybe_callable()
|
|
else:
|
|
self.external_ports = self._external_ports_maybe_callable
|
|
return super(ApacheSSLContext, self).__call__()
|
|
|
|
|
|
class NovaCellV2Context(ch_context.OSContextGenerator):
|
|
|
|
interfaces = ['nova-cell-api']
|
|
|
|
def __call__(self):
|
|
ctxt = {}
|
|
required_keys = ['cell-name', 'amqp-service', 'db-service']
|
|
for rid in hookenv.relation_ids('nova-cell-api'):
|
|
for unit in hookenv.related_units(rid):
|
|
data = hookenv.relation_get(rid=rid, unit=unit)
|
|
if set(required_keys).issubset(data.keys()):
|
|
ctxt[data['cell-name']] = {
|
|
'amqp_service': data['amqp-service'],
|
|
'db_service': data['db-service']}
|
|
return ctxt
|
|
|
|
|
|
class NovaCellV2SharedDBContext(ch_context.OSContextGenerator):
|
|
interfaces = ['shared-db']
|
|
|
|
def __call__(self):
|
|
hookenv.log('Generating template context for cell v2 share-db')
|
|
ctxt = {}
|
|
for rid in hookenv.relation_ids('shared-db'):
|
|
for unit in hookenv.related_units(rid):
|
|
rdata = hookenv.relation_get(rid=rid, unit=unit)
|
|
ctxt = {
|
|
'novaapi_password': rdata.get('novaapi_password'),
|
|
'novacell0_password': rdata.get('novacell0_password'),
|
|
'nova_password': rdata.get('nova_password'),
|
|
}
|
|
if ch_context.context_complete(ctxt):
|
|
return ctxt
|
|
return {}
|
|
|
|
|
|
class CloudComputeContext(ch_context.OSContextGenerator):
|
|
"Dummy context used by service status to check relation exists"
|
|
interfaces = ['nova-compute']
|
|
|
|
def __call__(self):
|
|
ctxt = {}
|
|
rids = [rid for rid in hookenv.relation_ids('cloud-compute')]
|
|
if rids:
|
|
ctxt['rids'] = rids
|
|
return ctxt
|
|
|
|
|
|
class PlacementContext(ch_context.OSContextGenerator):
|
|
"Dummy context used by service status to check relation exists"
|
|
interfaces = ['placement']
|
|
|
|
def __call__(self):
|
|
ctxt = {}
|
|
rids = [rid for rid in hookenv.relation_ids('placement')]
|
|
if rids:
|
|
ctxt['rids'] = rids
|
|
return ctxt
|
|
|
|
|
|
class NeutronAPIContext(ch_context.OSContextGenerator):
|
|
interfaces = ['neutron-api']
|
|
|
|
def __call__(self):
|
|
hookenv.log('Generating template context from neutron api relation')
|
|
ctxt = {}
|
|
for rid in hookenv.relation_ids('neutron-api'):
|
|
for unit in hookenv.related_units(rid):
|
|
rdata = hookenv.relation_get(rid=rid, unit=unit)
|
|
ctxt = {
|
|
'neutron_url': rdata.get('neutron-url'),
|
|
'neutron_plugin': rdata.get('neutron-plugin'),
|
|
'neutron_security_groups':
|
|
rdata.get('neutron-security-groups'),
|
|
'network_manager': 'neutron',
|
|
}
|
|
if (rdata.get('enable-sriov', '').lower() == 'true' or
|
|
rdata.get('enable-hardware-offload',
|
|
'').lower() == 'true'):
|
|
ctxt['additional_neutron_filters'] = 'PciPassthroughFilter'
|
|
# LP Bug#1805645
|
|
if rdata.get('dns-domain', ''):
|
|
ctxt['dns_domain'] = rdata.get('dns-domain')
|
|
if context_complete(ctxt):
|
|
return ctxt
|
|
return {}
|
|
|
|
|
|
class VolumeServiceContext(ch_context.OSContextGenerator):
|
|
interfaces = ['cinder-volume-service']
|
|
|
|
def __call__(self):
|
|
ctxt = {}
|
|
if hookenv.relation_ids('cinder-volume-service'):
|
|
ctxt['volume_service'] = 'cinder'
|
|
# kick all compute nodes to know they should use cinder now.
|
|
for rid in hookenv.relation_ids('cloud-compute'):
|
|
hookenv.relation_set(relation_id=rid, volume_service='cinder')
|
|
return ctxt
|
|
|
|
|
|
class HAProxyContext(ch_context.HAProxyContext):
|
|
interfaces = ['ceph']
|
|
|
|
def __call__(self):
|
|
'''
|
|
Extends the main charmhelpers HAProxyContext with a port mapping
|
|
specific to this charm.
|
|
Also used to extend nova.conf context with correct api_listening_ports
|
|
'''
|
|
ctxt = super(HAProxyContext, self).__call__()
|
|
|
|
os_rel = ch_utils.os_release('nova-common')
|
|
cmp_os_rel = ch_utils.CompareOpenStackReleases(os_rel)
|
|
# determine which port api processes should bind to, depending
|
|
# on existence of haproxy + apache frontends
|
|
compute_api = ch_cluster.determine_api_port(
|
|
common.api_port('nova-api-os-compute'), singlenode_mode=True)
|
|
ec2_api = ch_cluster.determine_api_port(
|
|
common.api_port('nova-api-ec2'), singlenode_mode=True)
|
|
s3_api = ch_cluster.determine_api_port(
|
|
common.api_port('nova-objectstore'), singlenode_mode=True)
|
|
placement_api = ch_cluster.determine_api_port(
|
|
common.api_port('nova-placement-api'), singlenode_mode=True)
|
|
metadata_api = ch_cluster.determine_api_port(
|
|
common.api_port('nova-api-metadata'), singlenode_mode=True)
|
|
# Apache ports
|
|
a_compute_api = ch_cluster.determine_apache_port(
|
|
common.api_port('nova-api-os-compute'), singlenode_mode=True)
|
|
a_ec2_api = ch_cluster.determine_apache_port(
|
|
common.api_port('nova-api-ec2'), singlenode_mode=True)
|
|
a_s3_api = ch_cluster.determine_apache_port(
|
|
common.api_port('nova-objectstore'), singlenode_mode=True)
|
|
a_placement_api = ch_cluster.determine_apache_port(
|
|
common.api_port('nova-placement-api'), singlenode_mode=True)
|
|
a_metadata_api = ch_cluster.determine_apache_port(
|
|
common.api_port('nova-api-metadata'), singlenode_mode=True)
|
|
# to be set in nova.conf accordingly.
|
|
listen_ports = {
|
|
'osapi_compute_listen_port': compute_api,
|
|
'ec2_listen_port': ec2_api,
|
|
's3_listen_port': s3_api,
|
|
'placement_listen_port': placement_api,
|
|
'metadata_listen_port': metadata_api,
|
|
}
|
|
|
|
port_mapping = {
|
|
'nova-api-os-compute': [
|
|
common.api_port('nova-api-os-compute'), a_compute_api],
|
|
'nova-api-ec2': [
|
|
common.api_port('nova-api-ec2'), a_ec2_api],
|
|
'nova-objectstore': [
|
|
common.api_port('nova-objectstore'), a_s3_api],
|
|
'nova-placement-api': [
|
|
common.api_port('nova-placement-api'), a_placement_api],
|
|
'nova-api-metadata': [
|
|
common.api_port('nova-api-metadata'), a_metadata_api],
|
|
}
|
|
|
|
if cmp_os_rel >= 'kilo':
|
|
del listen_ports['ec2_listen_port']
|
|
del listen_ports['s3_listen_port']
|
|
del port_mapping['nova-api-ec2']
|
|
del port_mapping['nova-objectstore']
|
|
|
|
rids = hookenv.relation_ids('placement')
|
|
if (rids or
|
|
cmp_os_rel < 'ocata' or
|
|
cmp_os_rel > 'stein'):
|
|
del listen_ports['placement_listen_port']
|
|
del port_mapping['nova-placement-api']
|
|
|
|
# for haproxy.conf
|
|
ctxt['service_ports'] = port_mapping
|
|
# for nova.conf
|
|
ctxt['listen_ports'] = listen_ports
|
|
return ctxt
|
|
|
|
|
|
class PlacementAPIHAProxyContext(HAProxyContext):
|
|
"""Context for the nova placement api service."""
|
|
|
|
def __call__(self):
|
|
ctxt = super(PlacementAPIHAProxyContext, self).__call__()
|
|
ctxt['port'] = ctxt['listen_ports']['placement_listen_port']
|
|
return ctxt
|
|
|
|
|
|
class ComputeAPIHAProxyContext(HAProxyContext):
|
|
"""Context for the nova os compute api service."""
|
|
|
|
def __call__(self):
|
|
ctxt = super(ComputeAPIHAProxyContext, self).__call__()
|
|
ctxt['port'] = ctxt['listen_ports']['osapi_compute_listen_port']
|
|
return ctxt
|
|
|
|
|
|
class MetaDataHAProxyContext(HAProxyContext):
|
|
"""Context for the nova metadata service."""
|
|
|
|
def __call__(self):
|
|
ctxt = super(MetaDataHAProxyContext, self).__call__()
|
|
ctxt['port'] = ctxt['listen_ports']['metadata_listen_port']
|
|
return ctxt
|
|
|
|
|
|
def canonical_url():
|
|
"""Returns the correct HTTP URL to this host given the state of HTTPS
|
|
configuration and hacluster.
|
|
"""
|
|
scheme = 'http'
|
|
if ch_cluster.https():
|
|
scheme = 'https'
|
|
|
|
addr = ch_ip.resolve_address(ch_ip.INTERNAL)
|
|
return '%s://%s' % (scheme, ch_network_ip.format_ipv6_addr(addr) or addr)
|
|
|
|
|
|
class CinderConfigContext(ch_context.OSContextGenerator):
|
|
def __call__(self):
|
|
return {
|
|
'cross_az_attach': hookenv.config('cross-az-attach')
|
|
}
|
|
|
|
|
|
class NeutronCCContext(ch_context.NeutronContext):
|
|
interfaces = ['quantum-network-service', 'neutron-network-service']
|
|
|
|
@property
|
|
def network_manager(self):
|
|
return ch_neutron.network_manager()
|
|
|
|
def _ensure_packages(self):
|
|
# Only compute nodes need to ensure packages here, to install
|
|
# required agents.
|
|
return
|
|
|
|
def __call__(self):
|
|
ctxt = super(NeutronCCContext, self).__call__()
|
|
ctxt['external_network'] = hookenv.config('neutron-external-network')
|
|
ctxt['nova_url'] = "{}:8774/v2".format(canonical_url())
|
|
return ctxt
|
|
|
|
|
|
class IdentityServiceContext(ch_context.IdentityServiceContext):
|
|
|
|
def __call__(self):
|
|
ctxt = super(IdentityServiceContext, 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 = '%s://%s:%s/v2.0/ec2tokens' % (
|
|
ctxt['service_protocol'] or 'http',
|
|
ctxt['service_host'],
|
|
ctxt['service_port']
|
|
)
|
|
ctxt['keystone_ec2_url'] = ec2_tokens
|
|
ctxt['region'] = hookenv.config('region')
|
|
|
|
return ctxt
|
|
|
|
|
|
_base_enabled_filters = [
|
|
"RetryFilter",
|
|
"AvailabilityZoneFilter",
|
|
"CoreFilter",
|
|
"RamFilter",
|
|
"DiskFilter",
|
|
"ComputeFilter",
|
|
"ComputeCapabilitiesFilter",
|
|
"ImagePropertiesFilter",
|
|
"ServerGroupAntiAffinityFilter",
|
|
"ServerGroupAffinityFilter",
|
|
"DifferentHostFilter",
|
|
"SameHostFilter",
|
|
]
|
|
|
|
# NOTE: Core,Ram,Disk filters obsolete due
|
|
# placement API functionality
|
|
_pike_enabled_filters = [
|
|
"RetryFilter",
|
|
"AvailabilityZoneFilter",
|
|
"ComputeFilter",
|
|
"ComputeCapabilitiesFilter",
|
|
"ImagePropertiesFilter",
|
|
"ServerGroupAntiAffinityFilter",
|
|
"ServerGroupAffinityFilter",
|
|
"DifferentHostFilter",
|
|
"SameHostFilter",
|
|
]
|
|
_victoria_enabled_filters = [
|
|
"AvailabilityZoneFilter",
|
|
"ComputeFilter",
|
|
"ComputeCapabilitiesFilter",
|
|
"ImagePropertiesFilter",
|
|
"ServerGroupAntiAffinityFilter",
|
|
"ServerGroupAffinityFilter",
|
|
"DifferentHostFilter",
|
|
"SameHostFilter",
|
|
]
|
|
|
|
|
|
def default_enabled_filters():
|
|
"""
|
|
Determine the list of default filters for scheduler use
|
|
|
|
:returns: list of filters to use
|
|
:rtype: list of str
|
|
"""
|
|
os_rel = ch_utils.os_release('nova-common')
|
|
cmp_os_rel = ch_utils.CompareOpenStackReleases(os_rel)
|
|
if cmp_os_rel >= 'victoria':
|
|
return _victoria_enabled_filters
|
|
if cmp_os_rel >= 'pike':
|
|
return _pike_enabled_filters
|
|
return _base_enabled_filters
|
|
|
|
|
|
class NovaConfigContext(ch_context.WorkerConfigContext):
|
|
def __call__(self):
|
|
ctxt = super(NovaConfigContext, self).__call__()
|
|
ctxt['scheduler_default_filters'] = (
|
|
hookenv.config('scheduler-default-filters') or
|
|
','.join(default_enabled_filters()))
|
|
if hookenv.config('pci-alias'):
|
|
aliases = json.loads(hookenv.config('pci-alias'))
|
|
if isinstance(aliases, list):
|
|
ctxt['pci_aliases'] = [json.dumps(x, sort_keys=True)
|
|
for x in aliases]
|
|
else:
|
|
ctxt['pci_alias'] = json.dumps(aliases, sort_keys=True)
|
|
|
|
ctxt['disk_allocation_ratio'] = hookenv.config('disk-allocation-ratio')
|
|
ctxt['cpu_allocation_ratio'] = hookenv.config('cpu-allocation-ratio')
|
|
ctxt['ram_allocation_ratio'] = hookenv.config('ram-allocation-ratio')
|
|
|
|
ctxt['allow_resize_to_same_host'] = hookenv.config(
|
|
'allow-resize-to-same-host')
|
|
|
|
for rid in hookenv.relation_ids('cloud-compute'):
|
|
rdata = {
|
|
'disk_allocation_ratio': ctxt['disk_allocation_ratio'],
|
|
'cpu_allocation_ratio': ctxt['cpu_allocation_ratio'],
|
|
'ram_allocation_ratio': ctxt['ram_allocation_ratio'],
|
|
'allow_resize_to_same_host': ctxt['allow_resize_to_same_host'],
|
|
}
|
|
hookenv.relation_set(relation_settings=rdata, relation_id=rid)
|
|
|
|
ctxt['enable_new_services'] = hookenv.config('enable-new-services')
|
|
addr = ch_ip.resolve_address(ch_ip.INTERNAL)
|
|
ctxt['host_ip'] = ch_network_ip.format_ipv6_addr(addr) or addr
|
|
ctxt['quota_instances'] = hookenv.config('quota-instances')
|
|
ctxt['quota_cores'] = hookenv.config('quota-cores')
|
|
ctxt['quota_ram'] = hookenv.config('quota-ram')
|
|
ctxt['quota_metadata_items'] = hookenv.config('quota-metadata-items')
|
|
ctxt['quota_injected_files'] = hookenv.config('quota-injected-files')
|
|
ctxt['quota_injected_file_content_bytes'] = hookenv.config(
|
|
'quota-injected-file-size')
|
|
ctxt['quota_injected_file_path_length'] = hookenv.config(
|
|
'quota-injected-path-size')
|
|
ctxt['quota_key_pairs'] = hookenv.config('quota-key-pairs')
|
|
ctxt['quota_server_groups'] = hookenv.config('quota-server-groups')
|
|
ctxt['quota_server_group_members'] = hookenv.config(
|
|
'quota-server-group-members')
|
|
ctxt['quota_count_usage_from_placement'] = hookenv.config(
|
|
'quota-count-usage-from-placement')
|
|
ctxt['console_access_protocol'] = hookenv.config(
|
|
'console-access-protocol')
|
|
ctxt['console_access_port'] = hookenv.config('console-access-port')
|
|
ctxt['scheduler_host_subset_size'] = hookenv.config(
|
|
'scheduler-host-subset-size')
|
|
ctxt['scheduler_max_attempts'] = hookenv.config(
|
|
'scheduler-max-attempts')
|
|
ctxt['unique_server_names'] = hookenv.config('unique-server-names')
|
|
ctxt['skip_hosts_with_build_failures'] = hookenv.config(
|
|
'skip-hosts-with-build-failures')
|
|
ctxt['limit_tenants_to_placement_aggregate'] = hookenv.config(
|
|
'limit-tenants-to-placement-aggregate')
|
|
ctxt['placement_aggregate_required_for_tenants'] = hookenv.config(
|
|
'placement-aggregate-required-for-tenants')
|
|
ctxt['enable_isolated_aggregate_filtering'] = hookenv.config(
|
|
'enable-isolated-aggregate-filtering')
|
|
ctxt['max_local_block_devices'] = hookenv.config(
|
|
'max-local-block-devices')
|
|
return ctxt
|
|
|
|
|
|
class NovaIPv6Context(ch_context.BindHostContext):
|
|
def __call__(self):
|
|
ctxt = super(NovaIPv6Context, self).__call__()
|
|
ctxt['use_ipv6'] = hookenv.config('prefer-ipv6')
|
|
return ctxt
|
|
|
|
|
|
class RemoteMemcacheContext(ch_context.OSContextGenerator):
|
|
interfaces = ['memcache']
|
|
|
|
def __call__(self):
|
|
servers = []
|
|
try:
|
|
for rid in hookenv.relation_ids(self.interfaces[0]):
|
|
for rel in hookenv.relations_for_id(rid):
|
|
priv_addr = rel['private-address']
|
|
# Format it as IPv6 address if needed
|
|
priv_addr = (ch_network_ip.format_ipv6_addr(priv_addr) or
|
|
priv_addr)
|
|
servers.append("%s:%s" % (priv_addr, rel['port']))
|
|
except Exception as ex:
|
|
hookenv.log("Could not get memcache servers: %s" % (ex),
|
|
level='WARNING')
|
|
servers = []
|
|
|
|
if servers:
|
|
return {
|
|
'memcached_servers': ','.join(servers)
|
|
}
|
|
return {}
|
|
|
|
|
|
class InstanceConsoleContext(ch_context.OSContextGenerator):
|
|
interfaces = []
|
|
|
|
def __call__(self):
|
|
ctxt = {}
|
|
# Configure nova-novncproxy https if nova-api is using https.
|
|
if ch_cluster.https():
|
|
cn = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
|
|
if cn:
|
|
cert_filename = 'cert_{}'.format(cn)
|
|
key_filename = 'key_{}'.format(cn)
|
|
else:
|
|
cert_filename = 'cert'
|
|
key_filename = 'key'
|
|
|
|
ssl_dir = '/etc/apache2/ssl/nova'
|
|
cert = os.path.join(ssl_dir, cert_filename)
|
|
key = os.path.join(ssl_dir, key_filename)
|
|
if os.path.exists(cert) and os.path.exists(key):
|
|
ctxt['ssl_cert'] = cert
|
|
ctxt['ssl_key'] = key
|
|
|
|
return ctxt
|
|
|
|
|
|
class ConsoleSSLContext(ch_context.OSContextGenerator):
|
|
interfaces = []
|
|
|
|
def __call__(self):
|
|
ctxt = {}
|
|
|
|
if (hookenv.config('console-ssl-cert') and
|
|
hookenv.config('console-ssl-key') and
|
|
hookenv.config('console-access-protocol')):
|
|
ssl_dir = '/etc/nova/ssl/'
|
|
if not os.path.exists(ssl_dir):
|
|
hookenv.log('Creating %s.' % ssl_dir, level=hookenv.DEBUG)
|
|
os.mkdir(ssl_dir)
|
|
|
|
cert_path = os.path.join(ssl_dir, 'nova_cert.pem')
|
|
decode_ssl_cert = base64.b64decode(
|
|
hookenv.config('console-ssl-cert'))
|
|
|
|
key_path = os.path.join(ssl_dir, 'nova_key.pem')
|
|
decode_ssl_key = base64.b64decode(
|
|
hookenv.config('console-ssl-key'))
|
|
|
|
with open(cert_path, 'wb') as fh:
|
|
fh.write(decode_ssl_cert)
|
|
with open(key_path, 'wb') as fh:
|
|
fh.write(decode_ssl_key)
|
|
|
|
ctxt['ssl_only'] = True
|
|
ctxt['ssl_cert'] = cert_path
|
|
ctxt['ssl_key'] = key_path
|
|
|
|
if ch_cluster.is_clustered():
|
|
ip_addr = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
|
|
else:
|
|
ip_addr = hookenv.unit_get('private-address')
|
|
|
|
ip_addr = ch_network_ip.format_ipv6_addr(ip_addr) or ip_addr
|
|
|
|
_proto = hookenv.config('console-access-protocol')
|
|
url = "https://%s:%s%s" % (
|
|
ip_addr,
|
|
common.console_attributes('proxy-port', proto=_proto),
|
|
common.console_attributes('proxy-page', proto=_proto))
|
|
|
|
if _proto == 'novnc':
|
|
ctxt['novncproxy_base_url'] = url
|
|
elif _proto == 'spice':
|
|
ctxt['html5proxy_base_url'] = url
|
|
|
|
return ctxt
|
|
|
|
|
|
class SerialConsoleContext(ch_context.OSContextGenerator):
|
|
interfaces = []
|
|
|
|
def __call__(self):
|
|
ip_addr = ch_ip.resolve_address(endpoint_type=ch_ip.PUBLIC)
|
|
ip_addr = ch_network_ip.format_ipv6_addr(ip_addr) or ip_addr
|
|
|
|
ctxt = {
|
|
'enable_serial_console':
|
|
str(hookenv.config('enable-serial-console')).lower(),
|
|
'serial_console_base_url': 'ws://{}:6083/'.format(ip_addr)
|
|
}
|
|
return ctxt
|
|
|
|
|
|
class APIRateLimitingContext(ch_context.OSContextGenerator):
|
|
def __call__(self):
|
|
ctxt = {}
|
|
rate_rules = hookenv.config('api-rate-limit-rules')
|
|
if rate_rules:
|
|
ctxt['api_rate_limit_rules'] = rate_rules
|
|
return ctxt
|
|
|
|
|
|
class NovaAPISharedDBContext(ch_context.SharedDBContext):
|
|
'''
|
|
Wrapper context to support multiple database connections being
|
|
represented to a single config file
|
|
|
|
ctxt values are namespaced with a nova_api_ prefix
|
|
'''
|
|
def __call__(self):
|
|
ctxt = super(NovaAPISharedDBContext, self).__call__()
|
|
if ctxt is not None:
|
|
prefix = 'nova_api_{}'
|
|
ctxt = {prefix.format(k): v for k, v in ctxt.items()}
|
|
return ctxt
|
|
|
|
|
|
class NovaMetadataContext(ch_context.NovaVendorMetadataContext):
|
|
"""Context used for configuring the nova metadata service."""
|
|
|
|
def __call__(self):
|
|
vdata_values = super(NovaMetadataContext, self).__call__()
|
|
|
|
release = ch_utils.os_release('nova-common')
|
|
cmp_os_release = ch_utils.CompareOpenStackReleases(release)
|
|
|
|
ctxt = {}
|
|
|
|
if cmp_os_release >= 'rocky':
|
|
ctxt.update(vdata_values)
|
|
|
|
ctxt['metadata_proxy_shared_secret'] = hookenv.leader_get(
|
|
'shared-metadata-secret')
|
|
ctxt['enable_metadata'] = True
|
|
else:
|
|
hookenv.log("Vendor metadata has been configured but is not "
|
|
"effective in nova-cloud-controller because release "
|
|
"{} is prior to Rocky.".format(release),
|
|
level=hookenv.DEBUG)
|
|
ctxt['enable_metadata'] = False
|
|
|
|
# NOTE(ganso): always propagate config value for nova-compute since
|
|
# we need to apply it there for all releases, and we cannot determine
|
|
# whether nova-compute is really the one serving the vendor metadata
|
|
for rid in hookenv.relation_ids('cloud-compute'):
|
|
hookenv.relation_set(relation_id=rid,
|
|
vendor_data=json.dumps(vdata_values))
|
|
|
|
return ctxt
|
|
|
|
|
|
class NovaMetadataJSONContext(ch_context.NovaVendorMetadataJSONContext):
|
|
|
|
def __call__(self):
|
|
vdata_values = super(NovaMetadataJSONContext, self).__call__()
|
|
|
|
# NOTE(ganso): always propagate config value for nova-compute since
|
|
# we need to apply it there for releases prior to rocky
|
|
for rid in hookenv.relation_ids('cloud-compute'):
|
|
hookenv.relation_set(relation_id=rid,
|
|
vendor_json=vdata_values['vendor_data_json'])
|
|
|
|
release = ch_utils.os_release('nova-common')
|
|
cmp_os_release = ch_utils.CompareOpenStackReleases(release)
|
|
|
|
if cmp_os_release >= 'rocky':
|
|
return vdata_values
|
|
else:
|
|
hookenv.log("Vendor metadata has been configured but is not "
|
|
"effective in nova-cloud-controller because release "
|
|
"{} is prior to Rocky.".format(release),
|
|
level=hookenv.DEBUG)
|
|
return {'vendor_data_json': '{}'}
|