charm-neutron-api/hooks/neutron_api_context.py
James Page 54fdd53b15 Fix service plugins for Queens release
Introduction of the Octavia support for Rocky introduced a regression
for Queens deployments where the LBaaS v2 service plugin is dropped
from the service plugin list.

Provide an explicit list of service plugins for Queens to resolve
this issue.

Change-Id: I8527e68dca01723f6d9bf04b5fe1a605f967244a
Closes-Bug: 1805573
2018-12-07 14:46:37 +00:00

857 lines
31 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 ast
import json
import re
import traceback
from collections import OrderedDict
from charmhelpers.core.hookenv import (
config,
relation_ids,
related_units,
relation_get,
log,
DEBUG,
ERROR,
)
from charmhelpers.contrib.openstack import context
from charmhelpers.contrib.hahelpers.cluster import (
determine_api_port,
determine_apache_port,
)
from charmhelpers.contrib.openstack.utils import (
os_release,
CompareOpenStackReleases,
)
VLAN = 'vlan'
VXLAN = 'vxlan'
GRE = 'gre'
FLAT = 'flat'
LOCAL = 'local'
OVERLAY_NET_TYPES = [VXLAN, GRE]
NON_OVERLAY_NET_TYPES = [VLAN, FLAT, LOCAL]
TENANT_NET_TYPES = [VXLAN, GRE, VLAN, FLAT, LOCAL]
EXTENSION_DRIVER_PORT_SECURITY = 'port_security'
EXTENSION_DRIVER_DNS = 'dns'
EXTENSION_DRIVER_QOS = 'qos'
ETC_NEUTRON = '/etc/neutron'
NOTIFICATION_TOPICS = [
'notifications',
]
# Domain name validation regex which is used to certify that
# the domain-name consists only of valid characters, is not
# longer than 63 characters in length for any name segment,
# and each segment does not begin or end with a hyphen.
DOMAIN_NAME_REGEX = re.compile(r'^(?!-)[A-Z\d-]{1,63}(?<!-)$',
re.IGNORECASE)
def get_l2population():
plugin = config('neutron-plugin')
return config('l2-population') if plugin == "ovs" else False
def _get_overlay_network_type():
overlay_networks = config('overlay-network-type').split()
for overlay_net in overlay_networks:
if overlay_net not in OVERLAY_NET_TYPES:
raise ValueError('Unsupported overlay-network-type %s'
% overlay_net)
return overlay_networks
def get_overlay_network_type():
return ','.join(_get_overlay_network_type())
def _get_tenant_network_types():
default_tenant_network_type = config('default-tenant-network-type')
tenant_network_types = _get_overlay_network_type()
tenant_network_types.extend(NON_OVERLAY_NET_TYPES)
if default_tenant_network_type:
if (default_tenant_network_type in TENANT_NET_TYPES and
default_tenant_network_type in tenant_network_types):
tenant_network_types[:0] = [default_tenant_network_type]
else:
raise ValueError('Unsupported or unconfigured '
'default-tenant-network-type'
' {}'.format(default_tenant_network_type))
# Dedupe list but preserve order
return list(OrderedDict.fromkeys(tenant_network_types))
def get_tenant_network_types():
'''Get the configured tenant network types
@return: comma delimited string of configured tenant
network types.
'''
return ','.join(_get_tenant_network_types())
def get_l3ha():
if config('enable-l3ha'):
release = os_release('neutron-server')
if CompareOpenStackReleases(release) < 'juno':
log('Disabling L3 HA, enable-l3ha is not valid before Juno')
return False
if CompareOpenStackReleases(release) < 'newton' and get_l2population():
log('Disabling L3 HA, l2-population must be disabled with L3 HA')
return False
return True
else:
return False
def get_dvr():
if config('enable-dvr'):
release = os_release('neutron-server')
if CompareOpenStackReleases(release) < 'juno':
log('Disabling DVR, enable-dvr is not valid before Juno')
return False
if CompareOpenStackReleases(release) == 'juno':
if VXLAN not in config('overlay-network-type').split():
log('Disabling DVR, enable-dvr requires the use of the vxlan '
'overlay network for OpenStack Juno')
return False
if get_l3ha() and CompareOpenStackReleases(release) < 'newton':
log('Disabling DVR, enable-l3ha must be disabled with dvr')
return False
if not get_l2population():
log('Disabling DVR, l2-population must be enabled to use dvr')
return False
return True
else:
return False
def get_dns_domain():
if not config('enable-ml2-dns'):
log('ML2 DNS Extensions are not enabled.', DEBUG)
return ""
dns_domain = config('dns-domain')
if not dns_domain:
log('No dns-domain has been configured', DEBUG)
return dns_domain
release = os_release('neutron-server')
if CompareOpenStackReleases(release) < 'mitaka':
log('Internal DNS resolution is not supported before Mitaka')
return ""
# Strip any trailing . at the end
if dns_domain[-1] == '.':
dns_domain = dns_domain[:-1]
# Ensure that the dns name is only a valid name. Valid entries include
# a-z, A-Z, 0-9, ., and -. No particular name may be longer than 63
# characters, each part cannot begin/end with a -. Validate this here in
# order to prevent other chaos which may prevent neutron services from
# functioning properly.
# Note: intentionally not validating the length of the domain name because
# this is practically difficult to validate reasonably well.
for level in dns_domain.split('.'):
if not DOMAIN_NAME_REGEX.match(level):
msg = "dns-domain '%s' is an invalid domain name." % dns_domain
log(msg, ERROR)
raise ValueError(msg)
# Make sure it ends with a .
dns_domain += '.'
return dns_domain
def get_ml2_mechanism_drivers():
"""Build comma delimited list of mechanism drivers for use in Neutron
ml2_conf.ini. Which drivers to enable are deduced from OpenStack
release and charm configuration options.
"""
mechanism_drivers = [
'openvswitch',
]
cmp_release = CompareOpenStackReleases(os_release('neutron-server'))
if (cmp_release == 'kilo' or cmp_release >= 'mitaka'):
mechanism_drivers.append('hyperv')
if get_l2population():
mechanism_drivers.append('l2population')
if (config('enable-sriov') and cmp_release >= 'kilo'):
mechanism_drivers.append('sriovnicswitch')
return ','.join(mechanism_drivers)
def is_qos_requested_and_valid():
"""Check whether QoS should be enabled by checking whether it has been
requested and, if it has, is it supported in the current configuration
"""
if config('enable-qos'):
if CompareOpenStackReleases(os_release('neutron-server')) < 'mitaka':
msg = ("The enable-qos option is only supported on mitaka or "
"later")
log(msg, ERROR)
return False
else:
return True
else:
return False
def is_nsg_logging_enabled():
"""
Check, if Neutron security groups logging should be enabled.
Works only on >=Queens and with OVS native firewall driver:
https://docs.openstack.org/neutron/queens/admin/config-logging.html
"""
if config('enable-security-group-logging'):
if config('neutron-plugin') != 'ovs':
msg = ("Disabling NSG logging; implementation only exists "
"for the OVS ML2 driver")
log(msg, ERROR)
return False
if CompareOpenStackReleases(os_release('neutron-server')) < 'queens':
msg = ("The enable-security-group-logging option is only "
"supported on Queens or later")
log(msg, ERROR)
return False
return True
return False
def is_vlan_trunking_requested_and_valid():
"""Check whether VLAN trunking should be enabled by checking whether
it has been requested and, if it has, is it supported in the current
configuration.
"""
if config('enable-vlan-trunking'):
if VLAN not in _get_tenant_network_types():
msg = ("Disabling vlan-trunking, the vlan network type must be "
"enabled to use vlan-trunking")
log(msg, ERROR)
return False
if config('neutron-plugin') != 'ovs':
msg = ("Disabling vlan-trunking, implementation only exists "
"for the OVS plugin")
log(msg, ERROR)
return False
if CompareOpenStackReleases(os_release('neutron-server')) < 'newton':
msg = ("The vlan-trunking option is only supported on newton or "
"later")
log(msg, ERROR)
return False
return True
else:
return False
class ApacheSSLContext(context.ApacheSSLContext):
interfaces = ['https']
external_ports = []
service_namespace = 'neutron'
def __call__(self):
# late import to work around circular dependency
from neutron_api_utils import determine_ports
self.external_ports = determine_ports()
return super(ApacheSSLContext, self).__call__()
class IdentityServiceContext(context.IdentityServiceContext):
def __call__(self):
ctxt = super(IdentityServiceContext, self).__call__()
if not ctxt:
return
ctxt['region'] = config('region')
return ctxt
class NeutronCCContext(context.NeutronContext):
interfaces = []
@property
def network_manager(self):
return 'neutron'
@property
def plugin(self):
return config('neutron-plugin')
@property
def neutron_security_groups(self):
return config('neutron-security-groups')
@property
def neutron_l2_population(self):
return get_l2population()
@property
def neutron_tenant_network_types(self):
return get_tenant_network_types()
@property
def neutron_overlay_network_type(self):
return get_overlay_network_type()
@property
def neutron_dvr(self):
return get_dvr()
@property
def neutron_l3ha(self):
return get_l3ha()
# Do not need the plugin agent installed on the api server
def _ensure_packages(self):
pass
# Do not need the flag on the api server
def _save_flag_file(self):
pass
def get_neutron_api_rel_settings(self):
settings = {}
for rid in relation_ids('neutron-api'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
cell_type = rdata.get('cell_type')
settings['nova_url'] = rdata.get('nova_url')
settings['restart_trigger'] = rdata.get('restart_trigger')
# If there are multiple nova-cloud-controllers joined to this
# service in a cell deployment then ignore the non-api cell
# ones
if cell_type and not cell_type == "api":
continue
if settings['nova_url']:
return settings
return settings
def __call__(self):
from neutron_api_utils import api_port
ctxt = super(NeutronCCContext, self).__call__()
if config('neutron-plugin') == 'nsx':
ctxt['nsx_username'] = config('nsx-username')
ctxt['nsx_password'] = config('nsx-password')
ctxt['nsx_tz_uuid'] = config('nsx-tz-uuid')
ctxt['nsx_l3_uuid'] = config('nsx-l3-uuid')
if 'nsx-controllers' in config():
ctxt['nsx_controllers'] = \
','.join(config('nsx-controllers').split())
ctxt['nsx_controllers_list'] = \
config('nsx-controllers').split()
if config('neutron-plugin') == 'plumgrid':
ctxt['pg_username'] = config('plumgrid-username')
ctxt['pg_password'] = config('plumgrid-password')
ctxt['virtual_ip'] = config('plumgrid-virtual-ip')
elif config('neutron-plugin') == 'midonet':
ctxt.update(MidonetContext()())
identity_context = IdentityServiceContext(service='neutron',
service_user='neutron')()
if identity_context is not None:
ctxt.update(identity_context)
ctxt['l2_population'] = self.neutron_l2_population
ctxt['enable_dvr'] = self.neutron_dvr
ctxt['l3_ha'] = self.neutron_l3ha
if self.neutron_l3ha:
max_agents = config('max-l3-agents-per-router')
min_agents = config('min-l3-agents-per-router')
if max_agents < min_agents:
raise ValueError("max-l3-agents-per-router ({}) must be >= "
"min-l3-agents-per-router "
"({})".format(max_agents, min_agents))
ctxt['max_l3_agents_per_router'] = max_agents
ctxt['min_l3_agents_per_router'] = min_agents
ctxt['dhcp_agents_per_network'] = config('dhcp-agents-per-network')
ctxt['tenant_network_types'] = self.neutron_tenant_network_types
ctxt['overlay_network_type'] = self.neutron_overlay_network_type
ctxt['external_network'] = config('neutron-external-network')
release = os_release('neutron-server')
cmp_release = CompareOpenStackReleases(release)
if config('neutron-plugin') in ['vsp']:
_config = config()
for k, v in _config.items():
if k.startswith('vsd'):
ctxt[k.replace('-', '_')] = v
for rid in relation_ids('vsd-rest-api'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
vsd_ip = rdata.get('vsd-ip-address')
if cmp_release >= 'kilo':
cms_id_value = rdata.get('nuage-cms-id')
log('relation data:cms_id required for'
' nuage plugin: {}'.format(cms_id_value))
if cms_id_value is not None:
ctxt['vsd_cms_id'] = cms_id_value
log('relation data:vsd-ip-address: {}'.format(vsd_ip))
if vsd_ip is not None:
ctxt['vsd_server'] = '{}:8443'.format(vsd_ip)
if 'vsd_server' not in ctxt:
ctxt['vsd_server'] = '1.1.1.1:8443'
ctxt['verbose'] = config('verbose')
ctxt['debug'] = config('debug')
ctxt['neutron_bind_port'] = \
determine_api_port(api_port('neutron-server'),
singlenode_mode=True)
ctxt['quota_security_group'] = config('quota-security-group')
ctxt['quota_security_group_rule'] = \
config('quota-security-group-rule')
ctxt['quota_network'] = config('quota-network')
ctxt['quota_subnet'] = config('quota-subnet')
ctxt['quota_port'] = config('quota-port')
ctxt['quota_vip'] = config('quota-vip')
ctxt['quota_pool'] = config('quota-pool')
ctxt['quota_member'] = config('quota-member')
ctxt['quota_health_monitors'] = config('quota-health-monitors')
ctxt['quota_router'] = config('quota-router')
ctxt['quota_floatingip'] = config('quota-floatingip')
n_api_settings = self.get_neutron_api_rel_settings()
if n_api_settings:
ctxt.update(n_api_settings)
flat_providers = config('flat-network-providers')
if flat_providers:
ctxt['network_providers'] = ','.join(flat_providers.split())
vlan_ranges = config('vlan-ranges')
if vlan_ranges:
ctxt['vlan_ranges'] = ','.join(vlan_ranges.split())
vni_ranges = config('vni-ranges')
if vni_ranges:
ctxt['vni_ranges'] = ','.join(vni_ranges.split())
enable_dns_extension_driver = False
dns_domain = get_dns_domain()
if dns_domain:
enable_dns_extension_driver = True
ctxt['dns_domain'] = dns_domain
if cmp_release >= 'mitaka':
for rid in relation_ids('external-dns'):
if related_units(rid):
enable_dns_extension_driver = True
extension_drivers = []
if config('enable-ml2-port-security'):
extension_drivers.append(EXTENSION_DRIVER_PORT_SECURITY)
if enable_dns_extension_driver:
extension_drivers.append(EXTENSION_DRIVER_DNS)
if is_qos_requested_and_valid():
extension_drivers.append(EXTENSION_DRIVER_QOS)
if extension_drivers:
ctxt['extension_drivers'] = ','.join(extension_drivers)
ctxt['enable_sriov'] = config('enable-sriov')
if cmp_release >= 'mitaka':
if config('global-physnet-mtu'):
ctxt['global_physnet_mtu'] = config('global-physnet-mtu')
if config('path-mtu'):
ctxt['path_mtu'] = config('path-mtu')
else:
ctxt['path_mtu'] = config('global-physnet-mtu')
physical_network_mtus = config('physical-network-mtus')
if physical_network_mtus:
ctxt['physical_network_mtus'] = ','.join(
physical_network_mtus.split())
if 'kilo' <= cmp_release <= 'mitaka':
pci_vendor_devs = config('supported-pci-vendor-devs')
if pci_vendor_devs:
ctxt['supported_pci_vendor_devs'] = \
','.join(pci_vendor_devs.split())
ctxt['mechanism_drivers'] = get_ml2_mechanism_drivers()
n_load_balancer_settings = NeutronLoadBalancerContext()()
if n_load_balancer_settings:
ctxt.update(n_load_balancer_settings)
if config('neutron-plugin') in ['ovs', 'ml2', 'Calico']:
ctxt['service_plugins'] = []
service_plugins = {
'icehouse': [
('neutron.services.l3_router.l3_router_plugin.'
'L3RouterPlugin'),
'neutron.services.firewall.fwaas_plugin.FirewallPlugin',
'neutron.services.loadbalancer.plugin.LoadBalancerPlugin',
'neutron.services.vpn.plugin.VPNDriverPlugin',
('neutron.services.metering.metering_plugin.'
'MeteringPlugin')],
'juno': [
('neutron.services.l3_router.l3_router_plugin.'
'L3RouterPlugin'),
'neutron.services.firewall.fwaas_plugin.FirewallPlugin',
'neutron.services.loadbalancer.plugin.LoadBalancerPlugin',
'neutron.services.vpn.plugin.VPNDriverPlugin',
('neutron.services.metering.metering_plugin.'
'MeteringPlugin')],
'kilo': ['router', 'firewall', 'lbaas', 'vpnaas', 'metering'],
'liberty': ['router', 'firewall', 'lbaas', 'vpnaas',
'metering'],
'mitaka': ['router', 'firewall', 'lbaas', 'vpnaas',
'metering'],
'newton': ['router', 'firewall', 'vpnaas', 'metering',
('neutron_lbaas.services.loadbalancer.plugin.'
'LoadBalancerPluginv2')],
'ocata': ['router', 'firewall', 'vpnaas', 'metering',
('neutron_lbaas.services.loadbalancer.plugin.'
'LoadBalancerPluginv2'), 'segments',
('neutron_dynamic_routing.'
'services.bgp.bgp_plugin.BgpPlugin')],
'pike': ['router', 'firewall', 'metering', 'segments',
('neutron_lbaas.services.loadbalancer.plugin.'
'LoadBalancerPluginv2'),
('neutron_dynamic_routing.'
'services.bgp.bgp_plugin.BgpPlugin')],
'queens': ['router', 'firewall', 'metering', 'segments',
('neutron_lbaas.services.loadbalancer.plugin.'
'LoadBalancerPluginv2'),
('neutron_dynamic_routing.'
'services.bgp.bgp_plugin.BgpPlugin')],
'rocky': ['router', 'firewall', 'metering', 'segments',
('neutron_dynamic_routing.'
'services.bgp.bgp_plugin.BgpPlugin')],
}
if cmp_release >= 'rocky':
if ctxt.get('load_balancer_name', None):
# TODO(fnordahl): Remove when ``neutron_lbaas`` is retired
service_plugins['rocky'].append('lbaasv2-proxy')
else:
# TODO(fnordahl): Remove fall-back in next charm release
service_plugins['rocky'].append(
'neutron_lbaas.services.loadbalancer.plugin.'
'LoadBalancerPluginv2')
ctxt['service_plugins'] = service_plugins.get(
release, service_plugins['rocky'])
if is_nsg_logging_enabled():
ctxt['service_plugins'].append('log')
if is_qos_requested_and_valid():
ctxt['service_plugins'].append('qos')
if is_vlan_trunking_requested_and_valid():
ctxt['service_plugins'].append('trunk')
ctxt['service_plugins'] = ','.join(ctxt['service_plugins'])
return ctxt
class HAProxyContext(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
'''
from neutron_api_utils import api_port
ctxt = super(HAProxyContext, self).__call__()
# Apache ports
a_neutron_api = determine_apache_port(api_port('neutron-server'),
singlenode_mode=True)
port_mapping = {
'neutron-server': [
api_port('neutron-server'), a_neutron_api]
}
ctxt['neutron_bind_port'] = determine_api_port(
api_port('neutron-server'),
singlenode_mode=True,
)
# for haproxy.conf
ctxt['service_ports'] = port_mapping
return ctxt
class EtcdContext(context.OSContextGenerator):
interfaces = ['etcd-proxy']
def __call__(self):
ctxt = {'cluster': ''}
cluster_string = ''
if not config('neutron-plugin') == 'Calico':
return ctxt
for rid in relation_ids('etcd-proxy'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
cluster_string = rdata.get('cluster')
if cluster_string:
break
ctxt['cluster'] = cluster_string
return ctxt
class NeutronApiSDNContext(context.SubordinateConfigContext):
interfaces = 'neutron-plugin-api-subordinate'
def __init__(self):
super(NeutronApiSDNContext, self).__init__(
interface='neutron-plugin-api-subordinate',
service='neutron-api',
config_file='/etc/neutron/neutron.conf')
def __call__(self):
ctxt = super(NeutronApiSDNContext, self).__call__()
defaults = {
'core-plugin': {
'templ_key': 'core_plugin',
'value': 'neutron.plugins.ml2.plugin.Ml2Plugin',
},
'neutron-plugin-config': {
'templ_key': 'neutron_plugin_config',
'value': '/etc/neutron/plugins/ml2/ml2_conf.ini',
},
'service-plugins': {
'templ_key': 'service_plugins',
'value': 'router,firewall,lbaas,vpnaas,metering',
},
'restart-trigger': {
'templ_key': 'restart_trigger',
'value': '',
},
'quota-driver': {
'templ_key': 'quota_driver',
'value': '',
},
'api-extensions-path': {
'templ_key': 'api_extensions_path',
'value': '',
},
}
for rid in relation_ids('neutron-plugin-api-subordinate'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
plugin = rdata.get('neutron-plugin')
if not plugin:
continue
ctxt['neutron_plugin'] = plugin
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 NeutronApiSDNConfigFileContext(context.OSContextGenerator):
interfaces = ['neutron-plugin-api-subordinate']
def __call__(self):
for rid in relation_ids('neutron-plugin-api-subordinate'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
neutron_server_plugin_conf = rdata.get('neutron-plugin-config')
if neutron_server_plugin_conf:
return {'config': neutron_server_plugin_conf}
return {'config': '/etc/neutron/plugins/ml2/ml2_conf.ini'}
class NeutronApiApiPasteContext(context.OSContextGenerator):
interfaces = ['neutron-plugin-api-subordinate']
def __validate_middleware(self, middleware):
'''
Accepts a list of dicts of the following format:
{
'type': 'middleware_type',
'name': 'middleware_name',
'config': {
option_1: value_1,
# ...
option_n: value_n
}
This validator was meant to be minimalistic - PasteDeploy's
validator will take care of the rest while our purpose here
is mainly config rendering - not imposing additional validation
logic which does not belong here.
'''
# types taken from PasteDeploy's wsgi loader
VALID_TYPES = ['filter', 'filter-app',
'app', 'application',
'composite', 'composit', 'pipeline']
def types_valid(t, n, c):
return all((type(t) is str,
type(n) is str,
type(c is dict)))
def mtype_valid(t):
return t in VALID_TYPES
for m in middleware:
t, n, c = [m.get(v) for v in ['type', 'name', 'config']]
# note that dict has to be non-empty
if not types_valid(t, n, c):
raise ValueError('Extra middleware key type(s) are'
' invalid: {}'.format(repr(m)))
if not mtype_valid(t):
raise ValueError('Extra middleware type key is not'
' a valid PasteDeploy middleware '
'type {}'.format(repr(t)))
if not c:
raise ValueError('Extra middleware config dictionary'
' is empty')
def __process_unit(self, rid, unit):
rdata = relation_get(rid=rid, unit=unit)
# update extra middleware for all possible plugins
rdata_middleware = rdata.get('extra_middleware')
if rdata_middleware:
try:
middleware = ast.literal_eval(rdata_middleware)
except:
import traceback
log(traceback.format_exc())
raise ValueError('Invalid extra middleware data'
' - check the subordinate charm')
if middleware:
return middleware
else:
log('extra_middleware specified but not'
'populated by unit {}, '
'relation: {}, value: {}'.format(
unit, rid, repr(middleware)))
raise ValueError('Invalid extra middleware'
'specified by a subordinate')
# no extra middleware
return list()
def __call__(self):
extra_middleware = []
for rid in relation_ids('neutron-plugin-api-subordinate'):
for unit in related_units(rid):
extra_middleware.extend(self.__process_unit(rid, unit))
self.__validate_middleware(extra_middleware)
return {'extra_middleware': extra_middleware}\
if extra_middleware else {}
class NeutronLoadBalancerContext(context.OSContextGenerator):
interfaces = ['neutron-load-balancer']
def __call__(self):
ctxt = {}
for rid in relation_ids('neutron-load-balancer'):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
try:
ctxt['load_balancer_name'] = json.loads(
rdata.get('name'))
ctxt['load_balancer_base_url'] = json.loads(
rdata.get('base_url'))
except TypeError:
pass
except json.decoder.JSONDecodeError:
log(traceback.format_exc())
raise ValueError('Invalid load balancer data'
' - check the related charm')
if self.context_complete(ctxt):
return ctxt
return {}
class MidonetContext(context.OSContextGenerator):
def __init__(self, rel_name='midonet'):
self.rel_name = rel_name
self.interfaces = [rel_name]
def __call__(self):
for rid in relation_ids(self.rel_name):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
ctxt = {
'midonet_api_ip': rdata.get('host'),
'midonet_api_port': rdata.get('port'),
}
if self.context_complete(ctxt):
return ctxt
return {}
class NeutronAMQPContext(context.AMQPContext):
'''AMQP context with Neutron API sauce'''
def __init__(self):
super(NeutronAMQPContext, self).__init__(ssl_dir=ETC_NEUTRON)
def __call__(self):
context = super(NeutronAMQPContext, self).__call__()
context['notification_topics'] = ','.join(NOTIFICATION_TOPICS)
return context
class DesignateContext(context.OSContextGenerator):
interfaces = ['external-dns']
def __call__(self):
ctxt = {}
for rid in relation_ids('external-dns'):
if related_units(rid):
for unit in related_units(rid):
rdata = relation_get(rid=rid, unit=unit)
ctxt['designate_endpoint'] = rdata.get('endpoint')
if ctxt.get('designate_endpoint') is not None:
ctxt['enable_designate'] = True
allow_reverse_dns_lookup = config('reverse-dns-lookup')
ctxt['allow_reverse_dns_lookup'] = allow_reverse_dns_lookup
if allow_reverse_dns_lookup:
ctxt['ipv4_ptr_zone_prefix_size'] = (
config('ipv4-ptr-zone-prefix-size'))
ctxt['ipv6_ptr_zone_prefix_size'] = (
config('ipv6-ptr-zone-prefix-size'))
return ctxt