charm-neutron-gateway/hooks/neutron_contexts.py
Trent Lloyd 5dee017f12 Support new style (and multiple) external networks
Switch the generated configuration to use "new" style external
networks when ext-port is not set.  In this case we configure
external_network_bridge = (intentionally blank),
gateway_external_network_id = (blank) and update the README with
information on using this new style of configuration.

The current template configures external networks by using the default
external_network_bridge=br-ex (implied when not set).  This activates
legacy code which assumes that a single external network exists on
that bridge and the L3 Agent directly plugs itself in.
provider:network_type, provider:physical_network and
provider:segmentation_id are ignored.  You cannot create multiple
networks and you cannot use segmented networks (e.g. VLAN)

By setting external_network_bridge = (intentionally blank) the L2
Agent handles the configuration instead, this allows us to create
multiple networks and also to use more complex network configurations
such as VLAN.  It is also possible to use the same physical connection
with different segmentation IDs for both internal and external
networks, as well as multiple external networks.

Legacy/existing configurations where ext-port is set generate the same
configuration as previous and should continue to work as before.  I do
not believe it to be easy to migrate existing setups to the "new"
style configuration automatically as changes to the neutron network
configuration may be required (specifically: provider:physical_network
will now be used when it was not before, and may not be correct) and
the physical port needs to be moved from br-ex to br-data which the
charm does not currently handle and is likely to error as it does not
attempt removal first.  Further work may be possible in this area.

For information about this new style of configuration being preferred,
see discussions in LP#1491668, LP#1525059 and
http://docs.openstack.org/liberty/networking-guide/scenario-classic-ovs.html

Change-Id: I8d2bb8098e080969e0445293b1ed79714b2c964f
Related-Bug: #1491668
Related-Bug: #1525059
Closes-Bug: #1536768
2016-06-14 17:45:47 +01:00

169 lines
4.8 KiB
Python

# vim: set ts=4:et
import os
import uuid
import socket
from charmhelpers.core.hookenv import (
config,
unit_get,
cached,
network_get_primary_address,
)
from charmhelpers.fetch import (
apt_install,
)
from charmhelpers.contrib.openstack.context import (
OSContextGenerator,
NeutronAPIContext,
config_flags_parser
)
from charmhelpers.contrib.hahelpers.cluster import(
eligible_leader
)
from charmhelpers.contrib.network.ip import (
get_address_in_network,
)
NEUTRON_ML2_PLUGIN = "ml2"
NEUTRON_N1KV_PLUGIN = \
"neutron.plugins.cisco.n1kv.n1kv_neutron_plugin.N1kvNeutronPluginV2"
NEUTRON_NSX_PLUGIN = "vmware"
NEUTRON_OVS_ODL_PLUGIN = "ml2"
OVS = 'ovs'
N1KV = 'n1kv'
NSX = 'nsx'
OVS_ODL = 'ovs-odl'
NEUTRON = 'neutron'
CORE_PLUGIN = {
OVS: NEUTRON_ML2_PLUGIN,
N1KV: NEUTRON_N1KV_PLUGIN,
NSX: NEUTRON_NSX_PLUGIN,
OVS_ODL: NEUTRON_OVS_ODL_PLUGIN,
}
def core_plugin():
return CORE_PLUGIN[config('plugin')]
class L3AgentContext(OSContextGenerator):
def __call__(self):
api_settings = NeutronAPIContext()()
ctxt = {}
if config('run-internal-router') == 'leader':
ctxt['handle_internal_only_router'] = eligible_leader(None)
if config('run-internal-router') == 'all':
ctxt['handle_internal_only_router'] = True
if config('run-internal-router') == 'none':
ctxt['handle_internal_only_router'] = False
if config('external-network-id'):
ctxt['ext_net_id'] = config('external-network-id')
if not config('ext-port') and not config('external-network-id'):
ctxt['external_configuration_new'] = True
if config('plugin'):
ctxt['plugin'] = config('plugin')
if api_settings['enable_dvr']:
ctxt['agent_mode'] = 'dvr_snat'
else:
ctxt['agent_mode'] = 'legacy'
return ctxt
class NeutronGatewayContext(NeutronAPIContext):
def __call__(self):
api_settings = super(NeutronGatewayContext, self).__call__()
ctxt = {
'shared_secret': get_shared_secret(),
'core_plugin': core_plugin(),
'plugin': config('plugin'),
'debug': config('debug'),
'verbose': config('verbose'),
'instance_mtu': config('instance-mtu'),
'l2_population': api_settings['l2_population'],
'enable_dvr': api_settings['enable_dvr'],
'enable_l3ha': api_settings['enable_l3ha'],
'overlay_network_type':
api_settings['overlay_network_type'],
}
fallback = get_host_ip(unit_get('private-address'))
if config('os-data-network'):
# NOTE: prefer any existing use of config based networking
ctxt['local_ip'] = \
get_address_in_network(config('os-data-network'),
fallback)
else:
# NOTE: test out network-spaces support, then fallback
try:
ctxt['local_ip'] = get_host_ip(
network_get_primary_address('data')
)
except NotImplementedError:
ctxt['local_ip'] = fallback
mappings = config('bridge-mappings')
if mappings:
ctxt['bridge_mappings'] = ','.join(mappings.split())
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())
dnsmasq_flags = config('dnsmasq-flags')
if dnsmasq_flags:
ctxt['dnsmasq_flags'] = config_flags_parser(dnsmasq_flags)
net_dev_mtu = api_settings['network_device_mtu']
if net_dev_mtu:
ctxt['network_device_mtu'] = net_dev_mtu
ctxt['veth_mtu'] = net_dev_mtu
return ctxt
@cached
def get_host_ip(hostname=None):
try:
import dns.resolver
except ImportError:
apt_install('python-dnspython', fatal=True)
import dns.resolver
hostname = hostname or unit_get('private-address')
try:
# Test to see if already an IPv4 address
socket.inet_aton(hostname)
return hostname
except socket.error:
answers = dns.resolver.query(hostname, 'A')
if answers:
return answers[0].address
SHARED_SECRET = "/etc/{}/secret.txt"
def get_shared_secret():
secret = None
_path = SHARED_SECRET.format(NEUTRON)
if not os.path.exists(_path):
secret = str(uuid.uuid4())
with open(_path, 'w') as secret_file:
secret_file.write(secret)
else:
with open(_path, 'r') as secret_file:
secret = secret_file.read().strip()
return secret