369 lines
12 KiB
Python
Raw Normal View History

import os
2015-07-21 10:42:36 +08:00
from base64 import b64decode
from charmhelpers.core.hookenv import (
config,
relation_ids,
relation_set,
log,
DEBUG,
related_units,
2014-12-15 10:33:42 -03:00
relations_for_id,
relation_get,
2015-07-21 10:42:36 +08:00
unit_get,
)
from charmhelpers.contrib.openstack import (
context,
neutron,
)
2013-08-10 19:06:09 +01:00
from charmhelpers.contrib.hahelpers.cluster import (
2014-04-02 11:01:06 +01:00
determine_apache_port,
determine_api_port,
https,
2015-07-21 11:20:51 +08:00
is_clustered,
2014-04-02 11:01:06 +01:00
)
from charmhelpers.contrib.network.ip import (
format_ipv6_addr,
)
from charmhelpers.contrib.openstack.ip import (
resolve_address,
INTERNAL,
2015-07-21 11:20:51 +08:00
PUBLIC,
)
2013-08-01 20:42:16 -07:00
2014-08-12 10:50:23 +08:00
2014-06-23 11:59:22 +01:00
def context_complete(ctxt):
_missing = []
for k, v in ctxt.iteritems():
if v is None or v == '':
_missing.append(k)
if _missing:
log('Missing required data: %s' % ' '.join(_missing), level='INFO')
return False
return True
2013-08-01 20:42:16 -07:00
2014-06-23 12:07:35 +01:00
2013-08-01 20:42:16 -07:00
class ApacheSSLContext(context.ApacheSSLContext):
interfaces = ['https']
external_ports = []
service_namespace = 'nova'
def __call__(self):
# late import to work around circular dependency
from nova_cc_utils import determine_ports
self.external_ports = determine_ports()
return super(ApacheSSLContext, self).__call__()
class NovaCellContext(context.OSContextGenerator):
2014-07-11 10:14:57 +01:00
interfaces = ['nova-cell']
def __call__(self):
2014-05-08 19:36:17 +00:00
log('Generating template context for cell')
ctxt = {}
2014-07-11 13:05:35 +00:00
for rid in relation_ids('cell'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
ctxt = {
'cell_type': rdata.get('cell_type'),
'cell_name': rdata.get('cell_name'),
}
if context.context_complete(ctxt):
return ctxt
return {}
2014-07-11 10:14:57 +01:00
2015-10-07 09:32:28 +00:00
class CloudComputeContext(context.OSContextGenerator):
"Dummy context used by service status to check relation exists"
interfaces = ['nova-compute']
def __call__(self):
ctxt = {}
rids = [rid for rid in relation_ids('cloud-compute')]
if rids:
ctxt['rids'] = rids
return ctxt
2014-05-08 19:36:17 +00:00
class NeutronAPIContext(context.OSContextGenerator):
2015-10-07 09:32:28 +00:00
interfaces = ['neutron-api']
2014-07-16 14:19:17 +01:00
2014-05-08 19:36:17 +00:00
def __call__(self):
2014-06-23 11:59:22 +01:00
log('Generating template context from neutron api relation')
2014-05-08 19:36:17 +00:00
ctxt = {}
for rid in relation_ids('neutron-api'):
for unit in related_units(rid):
2014-06-23 11:59:22 +01:00
rdata = relation_get(rid=rid, unit=unit)
ctxt = {
'neutron_url': rdata.get('neutron-url'),
'neutron_plugin': rdata.get('neutron-plugin'),
2014-06-23 12:07:35 +01:00
'neutron_security_groups':
rdata.get('neutron-security-groups'),
2014-06-23 11:59:22 +01:00
'network_manager': 'neutron',
}
if rdata.get('enable-sriov'):
ctxt['additional_neutron_filters'] = 'PciPassthroughFilter'
2014-06-23 11:59:22 +01:00
if context_complete(ctxt):
return ctxt
return {}
2013-08-01 20:42:16 -07:00
2014-06-20 09:59:07 +01:00
2013-08-01 20:42:16 -07:00
class VolumeServiceContext(context.OSContextGenerator):
interfaces = ['cinder-volume-service']
2013-08-01 20:42:16 -07:00
def __call__(self):
ctxt = {}
if relation_ids('cinder-volume-service'):
ctxt['volume_service'] = 'cinder'
2013-08-01 20:42:16 -07:00
# kick all compute nodes to know they should use cinder now.
[relation_set(relation_id=rid, volume_service='cinder')
2013-08-01 20:42:16 -07:00
for rid in relation_ids('cloud-compute')]
return ctxt
2013-08-10 19:06:09 +01:00
class HAProxyContext(context.HAProxyContext):
2013-08-01 20:42:16 -07:00
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
'''
2013-08-10 19:06:09 +01:00
from nova_cc_utils import api_port
ctxt = super(HAProxyContext, self).__call__()
# determine which port api processes should bind to, depending
# on existence of haproxy + apache frontends
compute_api = determine_api_port(api_port('nova-api-os-compute'),
singlenode_mode=True)
ec2_api = determine_api_port(api_port('nova-api-ec2'),
singlenode_mode=True)
s3_api = determine_api_port(api_port('nova-objectstore'),
singlenode_mode=True)
# Apache ports
a_compute_api = determine_apache_port(api_port('nova-api-os-compute'),
singlenode_mode=True)
a_ec2_api = determine_apache_port(api_port('nova-api-ec2'),
singlenode_mode=True)
a_s3_api = determine_apache_port(api_port('nova-objectstore'),
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,
2013-08-10 19:06:09 +01:00
}
2013-08-10 19:06:09 +01:00
port_mapping = {
'nova-api-os-compute': [
2014-02-16 20:17:42 +01:00
api_port('nova-api-os-compute'), a_compute_api],
2013-08-10 19:06:09 +01:00
'nova-api-ec2': [
2014-02-16 20:17:42 +01:00
api_port('nova-api-ec2'), a_ec2_api],
2013-08-10 19:06:09 +01:00
'nova-objectstore': [
2014-02-16 20:17:42 +01:00
api_port('nova-objectstore'), a_s3_api],
2013-08-10 19:06:09 +01:00
}
# for haproxy.conf
2013-08-10 19:06:09 +01:00
ctxt['service_ports'] = port_mapping
# for nova.conf
ctxt['listen_ports'] = listen_ports
return ctxt
2013-08-12 13:53:00 -07:00
def canonical_url():
"""Returns the correct HTTP URL to this host given the state of HTTPS
2014-04-02 11:01:06 +01:00
configuration and hacluster.
"""
2014-04-02 11:01:06 +01:00
scheme = 'http'
if https():
scheme = 'https'
2014-09-18 20:23:54 +08:00
addr = resolve_address(INTERNAL)
return '%s://%s' % (scheme, format_ipv6_addr(addr) or addr)
2014-09-18 20:23:54 +08:00
2013-08-12 13:53:00 -07:00
class NeutronCCContext(context.NeutronContext):
2015-10-07 09:32:28 +00:00
interfaces = ['quantum-network-service', 'neutron-network-service']
2013-08-12 13:53:00 -07:00
@property
def network_manager(self):
return 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'] = config('neutron-external-network')
2014-04-02 11:37:43 +01:00
ctxt['nova_url'] = "{}:8774/v2".format(canonical_url())
return ctxt
class IdentityServiceContext(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
2014-04-12 22:25:19 +01:00
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
2014-04-02 11:01:06 +01:00
ctxt['region'] = config('region')
2014-09-22 11:02:47 +08:00
return ctxt
2014-03-20 10:31:25 +01:00
2014-03-20 11:18:23 +01:00
class NovaPostgresqlDBContext(context.PostgresqlDBContext):
interfaces = ['pgsql-nova-db']
class NeutronPostgresqlDBContext(context.PostgresqlDBContext):
interfaces = ['pgsql-neutron-db']
def __init__(self):
super(NeutronPostgresqlDBContext,
self).__init__(config('neutron-database'))
class NovaConfigContext(context.WorkerConfigContext):
def __call__(self):
ctxt = super(NovaConfigContext, self).__call__()
ctxt['scheduler_default_filters'] = config('scheduler-default-filters')
ctxt['cpu_allocation_ratio'] = config('cpu-allocation-ratio')
ctxt['ram_allocation_ratio'] = config('ram-allocation-ratio')
addr = resolve_address(INTERNAL)
ctxt['host_ip'] = format_ipv6_addr(addr) or addr
return ctxt
class NovaIPv6Context(context.BindHostContext):
def __call__(self):
ctxt = super(NovaIPv6Context, self).__call__()
2014-09-30 14:48:02 +08:00
ctxt['use_ipv6'] = config('prefer-ipv6')
2014-08-12 10:50:23 +08:00
return ctxt
class InstanceConsoleContext(context.OSContextGenerator):
interfaces = []
def __call__(self):
ctxt = {}
servers = []
try:
for rid in relation_ids('memcache'):
for rel in relations_for_id(rid):
priv_addr = rel['private-address']
# Format it as IPv6 address if needed
priv_addr = format_ipv6_addr(priv_addr) or priv_addr
servers.append("%s:%s" % (priv_addr, rel['port']))
except Exception as ex:
log("Could not get memcache servers: %s" % (ex), level='WARNING')
servers = []
ctxt['memcached_servers'] = ','.join(servers)
# Configure nova-novncproxy https if nova-api is using https.
if https():
cn = resolve_address(endpoint_type=INTERNAL)
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
2015-07-21 10:42:36 +08:00
2015-07-22 20:01:24 +08:00
class ConsoleSSLContext(context.OSContextGenerator):
2015-07-21 10:42:36 +08:00
interfaces = []
def __call__(self):
ctxt = {}
2015-07-22 13:33:03 +08:00
from nova_cc_utils import console_attributes
2015-07-21 10:42:36 +08:00
if (config('console-ssl-cert') and
config('console-ssl-key') and
config('console-access-protocol')):
2015-07-21 10:42:36 +08:00
ssl_dir = '/etc/nova/ssl/'
if not os.path.exists(ssl_dir):
log('Creating %s.' % ssl_dir, level=DEBUG)
os.mkdir(ssl_dir)
cert_path = os.path.join(ssl_dir, 'nova_cert.pem')
decode_ssl_cert = b64decode(config('console-ssl-cert'))
2015-07-21 10:42:36 +08:00
key_path = os.path.join(ssl_dir, 'nova_key.pem')
decode_ssl_key = b64decode(config('console-ssl-key'))
2015-07-24 18:05:37 +08:00
with open(cert_path, 'w') as fh:
fh.write(decode_ssl_cert)
2015-07-21 10:42:36 +08:00
with open(key_path, 'w') as fh:
2015-07-22 20:01:24 +08:00
fh.write(decode_ssl_key)
2015-07-21 10:42:36 +08:00
ctxt['ssl_only'] = True
ctxt['ssl_cert'] = cert_path
ctxt['ssl_key'] = key_path
2015-07-21 11:20:51 +08:00
if is_clustered():
ip_addr = resolve_address(endpoint_type=PUBLIC)
else:
ip_addr = unit_get('private-address')
2015-07-22 20:01:24 +08:00
ip_addr = format_ipv6_addr(ip_addr) or ip_addr
2015-07-22 13:33:03 +08:00
_proto = config('console-access-protocol')
2015-07-22 16:03:52 +08:00
url = "https://%s:%s%s" % (
ip_addr,
console_attributes('proxy-port', proto=_proto),
console_attributes('proxy-page', proto=_proto))
2015-07-22 20:01:24 +08:00
if _proto == 'novnc':
ctxt['novncproxy_base_url'] = url
elif _proto == 'spice':
ctxt['html5proxy_base_url'] = url
2015-07-21 10:42:36 +08:00
return ctxt
2015-10-02 14:05:06 +02:00
2015-10-02 16:15:57 +02:00
2015-10-02 14:05:06 +02:00
class APIRateLimitingContext(context.OSContextGenerator):
def __call__(self):
ctxt = {}
rate_rules = config('api-rate-limit-rules')
if rate_rules:
ctxt['api_rate_limit_rules'] = rate_rules
return ctxt
class NovaAPISharedDBContext(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__()
prefix = 'nova_api_%s'
translated_ctxt = {}
for k, v in ctxt.iteritems():
translated_ctxt[prefix % k] = v
return translated_ctxt