284 lines
8.5 KiB
Python
284 lines
8.5 KiB
Python
# vim: set ts=4:et
|
|
import os
|
|
import uuid
|
|
import socket
|
|
from charmhelpers.core.host import (
|
|
list_nics,
|
|
get_nic_hwaddr
|
|
)
|
|
from charmhelpers.core.hookenv import (
|
|
config,
|
|
relation_ids,
|
|
related_units,
|
|
relation_get,
|
|
unit_get,
|
|
cached
|
|
)
|
|
from charmhelpers.fetch import (
|
|
apt_install,
|
|
)
|
|
from charmhelpers.contrib.openstack.context import (
|
|
OSContextGenerator,
|
|
context_complete,
|
|
)
|
|
from charmhelpers.contrib.openstack.utils import (
|
|
get_os_codename_install_source
|
|
)
|
|
from charmhelpers.contrib.hahelpers.cluster import(
|
|
eligible_leader
|
|
)
|
|
import re
|
|
from charmhelpers.contrib.network.ip import (
|
|
get_address_in_network,
|
|
get_ipv4_addr,
|
|
get_ipv6_addr,
|
|
is_bridge_member,
|
|
)
|
|
|
|
DB_USER = "quantum"
|
|
QUANTUM_DB = "quantum"
|
|
NOVA_DB_USER = "nova"
|
|
NOVA_DB = "nova"
|
|
|
|
QUANTUM_OVS_PLUGIN = \
|
|
"quantum.plugins.openvswitch.ovs_quantum_plugin.OVSQuantumPluginV2"
|
|
QUANTUM_NVP_PLUGIN = \
|
|
"quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2"
|
|
NEUTRON_OVS_PLUGIN = \
|
|
"neutron.plugins.openvswitch.ovs_neutron_plugin.OVSNeutronPluginV2"
|
|
NEUTRON_ML2_PLUGIN = \
|
|
"neutron.plugins.ml2.plugin.Ml2Plugin"
|
|
NEUTRON_NVP_PLUGIN = \
|
|
"neutron.plugins.nicira.nicira_nvp_plugin.NeutronPlugin.NvpPluginV2"
|
|
NEUTRON_N1KV_PLUGIN = \
|
|
"neutron.plugins.cisco.n1kv.n1kv_neutron_plugin.N1kvNeutronPluginV2"
|
|
NEUTRON_NSX_PLUGIN = "vmware"
|
|
|
|
NEUTRON = 'neutron'
|
|
QUANTUM = 'quantum'
|
|
|
|
|
|
def networking_name():
|
|
''' Determine whether neutron or quantum should be used for name '''
|
|
if get_os_codename_install_source(config('openstack-origin')) >= 'havana':
|
|
return NEUTRON
|
|
else:
|
|
return QUANTUM
|
|
|
|
OVS = 'ovs'
|
|
NVP = 'nvp'
|
|
N1KV = 'n1kv'
|
|
NSX = 'nsx'
|
|
|
|
CORE_PLUGIN = {
|
|
QUANTUM: {
|
|
OVS: QUANTUM_OVS_PLUGIN,
|
|
NVP: QUANTUM_NVP_PLUGIN,
|
|
},
|
|
NEUTRON: {
|
|
OVS: NEUTRON_OVS_PLUGIN,
|
|
NVP: NEUTRON_NVP_PLUGIN,
|
|
N1KV: NEUTRON_N1KV_PLUGIN,
|
|
NSX: NEUTRON_NSX_PLUGIN
|
|
},
|
|
}
|
|
|
|
|
|
def remap_plugin(plugin):
|
|
''' Remaps plugin name for renames/switches in packaging '''
|
|
release = get_os_codename_install_source(config('openstack-origin'))
|
|
if plugin == 'nvp' and release >= 'icehouse':
|
|
plugin = 'nsx'
|
|
elif plugin == 'nsx' and release < 'icehouse':
|
|
plugin = 'nvp'
|
|
return plugin
|
|
|
|
|
|
def core_plugin():
|
|
plugin = remap_plugin(config('plugin'))
|
|
if (get_os_codename_install_source(config('openstack-origin'))
|
|
>= 'icehouse'
|
|
and plugin == OVS):
|
|
return NEUTRON_ML2_PLUGIN
|
|
else:
|
|
return CORE_PLUGIN[networking_name()][plugin]
|
|
|
|
|
|
def _neutron_api_settings():
|
|
'''
|
|
Inspects current neutron-plugin-api relation for neutron settings. Return
|
|
defaults if it is not present
|
|
'''
|
|
neutron_settings = {
|
|
'l2_population': False,
|
|
'overlay_network_type': 'gre',
|
|
|
|
}
|
|
for rid in relation_ids('neutron-plugin-api'):
|
|
for unit in related_units(rid):
|
|
rdata = relation_get(rid=rid, unit=unit)
|
|
if 'l2-population' not in rdata:
|
|
continue
|
|
neutron_settings = {
|
|
'l2_population': rdata['l2-population'],
|
|
'overlay_network_type': rdata['overlay-network-type'],
|
|
}
|
|
return neutron_settings
|
|
return neutron_settings
|
|
|
|
|
|
class NetworkServiceContext(OSContextGenerator):
|
|
interfaces = ['quantum-network-service']
|
|
|
|
def __call__(self):
|
|
for rid in relation_ids('quantum-network-service'):
|
|
for unit in related_units(rid):
|
|
rdata = relation_get(rid=rid, unit=unit)
|
|
ctxt = {
|
|
'keystone_host': rdata.get('keystone_host'),
|
|
'service_port': rdata.get('service_port'),
|
|
'auth_port': rdata.get('auth_port'),
|
|
'service_tenant': rdata.get('service_tenant'),
|
|
'service_username': rdata.get('service_username'),
|
|
'service_password': rdata.get('service_password'),
|
|
'quantum_host': rdata.get('quantum_host'),
|
|
'quantum_port': rdata.get('quantum_port'),
|
|
'quantum_url': rdata.get('quantum_url'),
|
|
'region': rdata.get('region'),
|
|
'service_protocol':
|
|
rdata.get('service_protocol') or 'http',
|
|
'auth_protocol':
|
|
rdata.get('auth_protocol') or 'http',
|
|
}
|
|
if context_complete(ctxt):
|
|
return ctxt
|
|
return {}
|
|
|
|
|
|
class L3AgentContext(OSContextGenerator):
|
|
|
|
def __call__(self):
|
|
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 config('plugin'):
|
|
ctxt['plugin'] = config('plugin')
|
|
return ctxt
|
|
|
|
|
|
class NeutronPortContext(OSContextGenerator):
|
|
|
|
def _resolve_port(self, config_key):
|
|
if not config(config_key):
|
|
return None
|
|
hwaddr_to_nic = {}
|
|
hwaddr_to_ip = {}
|
|
for nic in list_nics(['eth', 'bond']):
|
|
hwaddr = get_nic_hwaddr(nic)
|
|
hwaddr_to_nic[hwaddr] = nic
|
|
addresses = get_ipv4_addr(nic, fatal=False) + \
|
|
get_ipv6_addr(iface=nic, fatal=False)
|
|
hwaddr_to_ip[hwaddr] = addresses
|
|
mac_regex = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
|
|
for entry in config(config_key).split():
|
|
entry = entry.strip()
|
|
if re.match(mac_regex, entry):
|
|
if entry in hwaddr_to_nic and len(hwaddr_to_ip[entry]) == 0:
|
|
# If the nic is part of a bridge then don't use it
|
|
if is_bridge_member(hwaddr_to_nic[entry]):
|
|
continue
|
|
# Entry is a MAC address for a valid interface that doesn't
|
|
# have an IP address assigned yet.
|
|
return hwaddr_to_nic[entry]
|
|
else:
|
|
# If the passed entry is not a MAC address, assume it's a valid
|
|
# interface, and that the user put it there on purpose (we can
|
|
# trust it to be the real external network).
|
|
return entry
|
|
return None
|
|
|
|
|
|
class ExternalPortContext(NeutronPortContext):
|
|
|
|
def __call__(self):
|
|
port = self._resolve_port('ext-port')
|
|
if port:
|
|
return {"ext_port": port}
|
|
else:
|
|
return None
|
|
|
|
|
|
class DataPortContext(NeutronPortContext):
|
|
|
|
def __call__(self):
|
|
port = self._resolve_port('data-port')
|
|
if port:
|
|
return {"data_port": port}
|
|
else:
|
|
return None
|
|
|
|
|
|
class QuantumGatewayContext(OSContextGenerator):
|
|
|
|
def __call__(self):
|
|
neutron_api_settings = _neutron_api_settings()
|
|
ctxt = {
|
|
'shared_secret': get_shared_secret(),
|
|
'local_ip':
|
|
get_address_in_network(config('os-data-network'),
|
|
get_host_ip(unit_get('private-address'))),
|
|
'core_plugin': core_plugin(),
|
|
'plugin': config('plugin'),
|
|
'debug': config('debug'),
|
|
'verbose': config('verbose'),
|
|
'instance_mtu': config('instance-mtu'),
|
|
'l2_population': neutron_api_settings['l2_population'],
|
|
'overlay_network_type':
|
|
neutron_api_settings['overlay_network_type'],
|
|
}
|
|
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(networking_name())
|
|
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
|