2015-02-04 16:30:03 +00:00
|
|
|
import os
|
|
|
|
import uuid
|
2014-06-05 11:59:23 +01:00
|
|
|
from charmhelpers.core.hookenv import (
|
2014-06-19 10:56:25 +01:00
|
|
|
relation_ids,
|
|
|
|
related_units,
|
|
|
|
relation_get,
|
|
|
|
config,
|
|
|
|
unit_get,
|
2014-06-05 11:59:23 +01:00
|
|
|
)
|
2015-02-02 15:26:32 +00:00
|
|
|
from charmhelpers.contrib.network.ip import (
|
|
|
|
get_address_in_network,
|
|
|
|
get_ipv4_addr,
|
|
|
|
get_ipv6_addr,
|
|
|
|
is_bridge_member,
|
|
|
|
)
|
2015-02-04 16:30:03 +00:00
|
|
|
from charmhelpers.contrib.openstack.ip import resolve_address
|
2014-07-14 14:47:38 +03:00
|
|
|
from charmhelpers.core.host import list_nics, get_nic_hwaddr
|
2015-02-16 11:16:11 +00:00
|
|
|
from charmhelpers.core.strutils import bool_from_string
|
2014-06-05 11:59:23 +01:00
|
|
|
from charmhelpers.contrib.openstack import context
|
|
|
|
from charmhelpers.core.host import service_running, service_start
|
2014-07-14 14:47:38 +03:00
|
|
|
from charmhelpers.contrib.network.ovs import add_bridge, add_bridge_port
|
2014-06-05 11:59:23 +01:00
|
|
|
from charmhelpers.contrib.openstack.utils import get_host_ip
|
2015-02-04 16:30:03 +00:00
|
|
|
from charmhelpers.contrib.openstack.context import (
|
|
|
|
OSContextGenerator,
|
|
|
|
context_complete,
|
|
|
|
)
|
2014-06-27 14:04:10 +01:00
|
|
|
|
2014-07-14 14:47:38 +03:00
|
|
|
import re
|
|
|
|
|
2014-06-05 11:59:23 +01:00
|
|
|
OVS_BRIDGE = 'br-int'
|
2014-07-14 14:47:38 +03:00
|
|
|
DATA_BRIDGE = 'br-data'
|
2014-06-05 11:59:23 +01:00
|
|
|
|
2014-06-19 10:56:25 +01:00
|
|
|
|
2014-09-04 10:56:50 +00:00
|
|
|
def _neutron_api_settings():
|
2014-06-05 11:59:23 +01:00
|
|
|
'''
|
2014-09-04 10:56:50 +00:00
|
|
|
Inspects current neutron-plugin relation
|
2014-06-05 11:59:23 +01:00
|
|
|
'''
|
2014-09-04 10:56:50 +00:00
|
|
|
neutron_settings = {
|
2014-09-04 15:18:06 +00:00
|
|
|
'neutron_security_groups': False,
|
2014-09-04 10:56:50 +00:00
|
|
|
'l2_population': True,
|
2014-10-02 10:59:05 +00:00
|
|
|
'overlay_network_type': 'gre',
|
2015-02-19 15:55:06 +00:00
|
|
|
'enable_dvr': False,
|
2014-09-04 10:56:50 +00:00
|
|
|
}
|
2014-06-17 16:14:17 +01:00
|
|
|
for rid in relation_ids('neutron-plugin-api'):
|
2014-06-05 11:59:23 +01:00
|
|
|
for unit in related_units(rid):
|
2014-09-04 10:56:50 +00:00
|
|
|
rdata = relation_get(rid=rid, unit=unit)
|
2014-09-04 13:26:03 +00:00
|
|
|
if 'l2-population' not in rdata:
|
2014-09-04 10:56:50 +00:00
|
|
|
continue
|
|
|
|
neutron_settings = {
|
2015-02-16 11:16:11 +00:00
|
|
|
'l2_population': bool_from_string(rdata['l2-population']),
|
2014-10-02 10:59:05 +00:00
|
|
|
'overlay_network_type': rdata['overlay-network-type'],
|
2015-02-16 11:16:11 +00:00
|
|
|
'neutron_security_groups': bool_from_string(
|
2015-02-13 09:15:36 +00:00
|
|
|
rdata['neutron-security-groups']
|
|
|
|
),
|
2014-09-04 10:56:50 +00:00
|
|
|
}
|
2015-02-02 13:31:39 +00:00
|
|
|
if 'enable-dvr' in rdata:
|
2015-02-19 15:55:06 +00:00
|
|
|
neutron_settings['enable_dvr'] = bool_from_string(
|
|
|
|
rdata['enable-dvr']
|
|
|
|
)
|
2014-10-10 10:55:20 +01:00
|
|
|
# Override with configuration if set to true
|
|
|
|
if config('disable-security-groups'):
|
|
|
|
neutron_settings['neutron_security_groups'] = False
|
2014-09-04 10:56:50 +00:00
|
|
|
return neutron_settings
|
|
|
|
return neutron_settings
|
2014-06-05 11:59:23 +01:00
|
|
|
|
2014-06-19 10:56:25 +01:00
|
|
|
|
2015-02-02 13:31:39 +00:00
|
|
|
def use_dvr():
|
|
|
|
api_settings = _neutron_api_settings()
|
2015-02-19 15:55:06 +00:00
|
|
|
if 'enable_dvr' in api_settings:
|
|
|
|
return api_settings['enable_dvr']
|
|
|
|
else:
|
|
|
|
return False
|
2015-02-02 13:31:39 +00:00
|
|
|
|
|
|
|
|
2014-06-05 11:59:23 +01:00
|
|
|
class OVSPluginContext(context.NeutronContext):
|
|
|
|
interfaces = []
|
|
|
|
|
|
|
|
@property
|
|
|
|
def plugin(self):
|
|
|
|
return 'ovs'
|
|
|
|
|
|
|
|
@property
|
|
|
|
def network_manager(self):
|
|
|
|
return 'neutron'
|
|
|
|
|
|
|
|
@property
|
|
|
|
def neutron_security_groups(self):
|
2014-09-04 10:56:50 +00:00
|
|
|
neutron_api_settings = _neutron_api_settings()
|
2014-09-04 15:18:06 +00:00
|
|
|
return neutron_api_settings['neutron_security_groups']
|
2014-06-05 11:59:23 +01:00
|
|
|
|
2014-07-14 14:47:38 +03:00
|
|
|
def get_data_port(self):
|
|
|
|
data_ports = config('data-port')
|
|
|
|
if not data_ports:
|
|
|
|
return None
|
|
|
|
hwaddrs = {}
|
|
|
|
for nic in list_nics(['eth', 'bond']):
|
|
|
|
hwaddrs[get_nic_hwaddr(nic).lower()] = nic
|
|
|
|
mac_regex = re.compile(r'([0-9A-F]{2}[:-]){5}([0-9A-F]{2})', re.I)
|
|
|
|
for entry in data_ports.split():
|
|
|
|
entry = entry.strip().lower()
|
|
|
|
if re.match(mac_regex, entry):
|
|
|
|
if entry in hwaddrs:
|
|
|
|
return hwaddrs[entry]
|
|
|
|
else:
|
|
|
|
return entry
|
|
|
|
return None
|
|
|
|
|
2014-06-05 11:59:23 +01:00
|
|
|
def _ensure_bridge(self):
|
|
|
|
if not service_running('openvswitch-switch'):
|
|
|
|
service_start('openvswitch-switch')
|
|
|
|
add_bridge(OVS_BRIDGE)
|
2014-07-14 14:47:38 +03:00
|
|
|
add_bridge(DATA_BRIDGE)
|
|
|
|
data_port = self.get_data_port()
|
|
|
|
if data_port:
|
|
|
|
add_bridge_port(DATA_BRIDGE, data_port, promisc=True)
|
2014-06-05 11:59:23 +01:00
|
|
|
|
|
|
|
def ovs_ctxt(self):
|
|
|
|
# In addition to generating config context, ensure the OVS service
|
|
|
|
# is running and the OVS bridge exists. Also need to ensure
|
|
|
|
# local_ip points to actual IP, not hostname.
|
|
|
|
ovs_ctxt = super(OVSPluginContext, self).ovs_ctxt()
|
|
|
|
if not ovs_ctxt:
|
|
|
|
return {}
|
|
|
|
|
|
|
|
self._ensure_bridge()
|
|
|
|
|
2014-06-05 14:16:54 +00:00
|
|
|
conf = config()
|
2014-06-27 14:04:10 +01:00
|
|
|
ovs_ctxt['local_ip'] = \
|
|
|
|
get_address_in_network(config('os-data-network'),
|
|
|
|
get_host_ip(unit_get('private-address')))
|
2014-09-04 10:56:50 +00:00
|
|
|
neutron_api_settings = _neutron_api_settings()
|
2014-09-04 15:18:06 +00:00
|
|
|
ovs_ctxt['neutron_security_groups'] = self.neutron_security_groups
|
2014-09-04 10:56:50 +00:00
|
|
|
ovs_ctxt['l2_population'] = neutron_api_settings['l2_population']
|
2015-02-02 13:31:39 +00:00
|
|
|
ovs_ctxt['distributed_routing'] = use_dvr()
|
2014-10-02 14:02:42 +00:00
|
|
|
ovs_ctxt['overlay_network_type'] = \
|
|
|
|
neutron_api_settings['overlay_network_type']
|
2014-06-23 12:49:58 +01:00
|
|
|
# TODO: We need to sort out the syslog and debug/verbose options as a
|
2014-06-23 13:50:23 +01:00
|
|
|
# general context helper
|
2014-06-05 14:16:54 +00:00
|
|
|
ovs_ctxt['use_syslog'] = conf['use-syslog']
|
2014-06-17 15:31:03 +01:00
|
|
|
ovs_ctxt['verbose'] = conf['verbose']
|
|
|
|
ovs_ctxt['debug'] = conf['debug']
|
2014-06-05 11:59:23 +01:00
|
|
|
return ovs_ctxt
|
2015-02-02 13:31:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
class L3AgentContext(OSContextGenerator):
|
|
|
|
|
|
|
|
def __call__(self):
|
|
|
|
neutron_api_settings = _neutron_api_settings()
|
|
|
|
ctxt = {}
|
|
|
|
if neutron_api_settings['enable_dvr'] == 'True':
|
|
|
|
ctxt['agent_mode'] = 'dvr'
|
|
|
|
else:
|
|
|
|
ctxt['agent_mode'] = 'legacy'
|
|
|
|
return ctxt
|
2015-02-02 15:26:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
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
|
2015-02-04 16:30:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
class NetworkServiceContext(OSContextGenerator):
|
|
|
|
interfaces = ['neutron-network-service']
|
|
|
|
|
|
|
|
def __call__(self):
|
|
|
|
for rid in relation_ids('neutron-network-service'):
|
|
|
|
for unit in related_units(rid):
|
|
|
|
rdata = relation_get(rid=rid, unit=unit)
|
|
|
|
ctxt = {
|
|
|
|
'service_protocol':
|
|
|
|
rdata.get('service_protocol') or 'http',
|
|
|
|
'keystone_host': rdata.get('keystone_host'),
|
|
|
|
'service_port': rdata.get('service_port'),
|
|
|
|
'region': rdata.get('region'),
|
|
|
|
'service_tenant': rdata.get('service_tenant'),
|
|
|
|
'service_username': rdata.get('service_username'),
|
|
|
|
'service_password': rdata.get('service_password'),
|
|
|
|
}
|
|
|
|
if context_complete(ctxt):
|
|
|
|
return ctxt
|
|
|
|
|
|
|
|
|
|
|
|
class DVRSharedSecretContext(OSContextGenerator):
|
|
|
|
|
|
|
|
def get_shared_secret(self):
|
|
|
|
secret = None
|
|
|
|
if not os.path.exists(self.SHARED_SECRET):
|
|
|
|
secret = str(uuid.uuid4())
|
|
|
|
with open(self.SHARED_SECRET, 'w') as secret_file:
|
|
|
|
secret_file.write(secret)
|
|
|
|
else:
|
|
|
|
with open(self.SHARED_SECRET, 'r') as secret_file:
|
|
|
|
secret = secret_file.read().strip()
|
|
|
|
return secret
|
|
|
|
|
|
|
|
def __call__(self):
|
|
|
|
self.SHARED_SECRET = "/etc/neutron/secret.txt"
|
|
|
|
if use_dvr():
|
|
|
|
ctxt = {
|
|
|
|
'shared_secret': self.get_shared_secret(),
|
|
|
|
'local_ip': resolve_address(),
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
ctxt = {}
|
|
|
|
return ctxt
|