2016-03-23 11:10:16 +00:00
|
|
|
import glob
|
2015-02-04 16:30:03 +00:00
|
|
|
import os
|
|
|
|
import uuid
|
2016-03-23 11:10:16 +00:00
|
|
|
from pci import PCINetDevices
|
2014-06-05 11:59:23 +01:00
|
|
|
from charmhelpers.core.hookenv import (
|
2014-06-19 10:56:25 +01:00
|
|
|
config,
|
2015-03-31 14:33:06 +00:00
|
|
|
relation_get,
|
|
|
|
relation_ids,
|
|
|
|
related_units,
|
2014-06-19 10:56:25 +01:00
|
|
|
unit_get,
|
2016-03-31 12:14:49 +01:00
|
|
|
network_get_primary_address,
|
2014-06-05 11:59:23 +01:00
|
|
|
)
|
|
|
|
from charmhelpers.contrib.openstack import context
|
|
|
|
from charmhelpers.contrib.openstack.utils import get_host_ip
|
2014-06-27 14:04:10 +01:00
|
|
|
from charmhelpers.contrib.network.ip import get_address_in_network
|
2015-02-04 16:30:03 +00:00
|
|
|
from charmhelpers.contrib.openstack.context import (
|
|
|
|
OSContextGenerator,
|
2015-03-25 08:37:29 +00:00
|
|
|
NeutronAPIContext,
|
2016-03-23 11:10:16 +00:00
|
|
|
parse_data_port_mappings
|
2015-03-24 13:39:11 +00:00
|
|
|
)
|
2016-03-23 11:10:16 +00:00
|
|
|
from charmhelpers.core.unitdata import kv
|
2014-06-05 11:59:23 +01:00
|
|
|
|
2014-06-19 10:56:25 +01: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):
|
2015-03-24 13:39:11 +00:00
|
|
|
if config('disable-security-groups'):
|
|
|
|
return False
|
2015-03-25 08:37:29 +00:00
|
|
|
neutron_api_settings = NeutronAPIContext()()
|
2014-09-04 15:18:06 +00:00
|
|
|
return neutron_api_settings['neutron_security_groups']
|
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 {}
|
|
|
|
|
2014-06-05 14:16:54 +00:00
|
|
|
conf = config()
|
2016-03-31 12:14:49 +01:00
|
|
|
|
|
|
|
fallback = get_host_ip(unit_get('private-address'))
|
|
|
|
if config('os-data-network'):
|
|
|
|
# NOTE: prefer any existing use of config based networking
|
|
|
|
ovs_ctxt['local_ip'] = \
|
|
|
|
get_address_in_network(config('os-data-network'),
|
|
|
|
fallback)
|
|
|
|
else:
|
|
|
|
# NOTE: test out network-spaces support, then fallback
|
|
|
|
try:
|
|
|
|
ovs_ctxt['local_ip'] = get_host_ip(
|
|
|
|
network_get_primary_address('data')
|
|
|
|
)
|
|
|
|
except NotImplementedError:
|
|
|
|
ovs_ctxt['local_ip'] = fallback
|
|
|
|
|
2015-03-25 08:37:29 +00:00
|
|
|
neutron_api_settings = NeutronAPIContext()()
|
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-03-25 08:37:29 +00:00
|
|
|
ovs_ctxt['distributed_routing'] = neutron_api_settings['enable_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']
|
2016-02-10 10:30:30 +00:00
|
|
|
ovs_ctxt['prevent_arp_spoofing'] = conf['prevent-arp-spoofing']
|
2016-03-23 11:10:16 +00:00
|
|
|
ovs_ctxt['enable_dpdk'] = conf['enable-dpdk']
|
2015-02-10 15:25:42 +00:00
|
|
|
|
2015-02-11 13:43:17 +00:00
|
|
|
net_dev_mtu = neutron_api_settings.get('network_device_mtu')
|
2015-02-10 15:25:42 +00:00
|
|
|
if net_dev_mtu:
|
2015-02-11 14:24:39 +00:00
|
|
|
# neutron.conf
|
|
|
|
ovs_ctxt['network_device_mtu'] = net_dev_mtu
|
|
|
|
# ml2 conf
|
2015-02-10 15:25:42 +00:00
|
|
|
ovs_ctxt['veth_mtu'] = net_dev_mtu
|
|
|
|
|
2015-02-11 16:31:04 +00:00
|
|
|
mappings = config('bridge-mappings')
|
|
|
|
if mappings:
|
2015-04-10 17:21:20 +01:00
|
|
|
ovs_ctxt['bridge_mappings'] = ','.join(mappings.split())
|
2015-02-11 16:31:04 +00:00
|
|
|
|
2015-04-15 15:08:36 +01:00
|
|
|
flat_providers = config('flat-network-providers')
|
|
|
|
if flat_providers:
|
|
|
|
ovs_ctxt['network_providers'] = ','.join(flat_providers.split())
|
|
|
|
|
2015-03-03 14:24:10 +08:00
|
|
|
vlan_ranges = config('vlan-ranges')
|
|
|
|
if vlan_ranges:
|
2015-04-10 16:42:06 +01:00
|
|
|
ovs_ctxt['vlan_ranges'] = ','.join(vlan_ranges.split())
|
2015-03-03 14:24:10 +08:00
|
|
|
|
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):
|
2015-03-25 08:37:29 +00:00
|
|
|
neutron_api_settings = NeutronAPIContext()()
|
2015-02-02 13:31:39 +00:00
|
|
|
ctxt = {}
|
2015-02-20 09:13:08 +00:00
|
|
|
if neutron_api_settings['enable_dvr']:
|
2015-02-02 13:31:39 +00:00
|
|
|
ctxt['agent_mode'] = 'dvr'
|
|
|
|
else:
|
|
|
|
ctxt['agent_mode'] = 'legacy'
|
|
|
|
return ctxt
|
2015-02-02 15:26:32 +00:00
|
|
|
|
|
|
|
|
2016-03-23 11:10:16 +00:00
|
|
|
def resolve_dpdk_ports():
|
|
|
|
'''
|
|
|
|
Resolve local PCI devices from configured mac addresses
|
|
|
|
using the data-port configuration option
|
|
|
|
|
|
|
|
@return: OrderDict indexed by PCI device address.
|
|
|
|
'''
|
|
|
|
ports = config('data-port')
|
|
|
|
devices = PCINetDevices()
|
|
|
|
resolved_devices = {}
|
|
|
|
db = kv()
|
|
|
|
if ports:
|
|
|
|
# NOTE: ordered dict of format {[mac]: bridge}
|
|
|
|
portmap = parse_data_port_mappings(ports)
|
|
|
|
for mac, bridge in portmap.iteritems():
|
|
|
|
pcidev = devices.get_device_from_mac(mac)
|
|
|
|
if pcidev:
|
|
|
|
# NOTE: store mac->pci allocation as post binding
|
|
|
|
# to dpdk, it disappears from PCIDevices.
|
|
|
|
db.set(mac, pcidev.pci_address)
|
|
|
|
db.flush()
|
|
|
|
|
|
|
|
pci_address = db.get(mac)
|
|
|
|
if pci_address:
|
|
|
|
resolved_devices[pci_address] = bridge
|
|
|
|
|
|
|
|
return resolved_devices
|
|
|
|
|
|
|
|
|
|
|
|
def parse_cpu_list(cpulist):
|
|
|
|
'''
|
|
|
|
Parses a linux cpulist for a numa node
|
|
|
|
|
|
|
|
@return list of cores
|
|
|
|
'''
|
|
|
|
cores = []
|
|
|
|
ranges = cpulist.split(',')
|
|
|
|
for cpu_range in ranges:
|
|
|
|
cpu_min_max = cpu_range.split('-')
|
|
|
|
cores += range(int(cpu_min_max[0]),
|
|
|
|
int(cpu_min_max[1]) + 1)
|
|
|
|
return cores
|
|
|
|
|
|
|
|
|
|
|
|
def numa_node_cores():
|
|
|
|
'''Dict of numa node -> cpu core mapping'''
|
|
|
|
nodes = {}
|
|
|
|
node_regex = '/sys/devices/system/node/node*'
|
|
|
|
for node in glob.glob(node_regex):
|
|
|
|
index = node.lstrip('/sys/devices/system/node/node')
|
|
|
|
with open(os.path.join(node, 'cpulist')) as cpulist:
|
|
|
|
nodes[index] = parse_cpu_list(cpulist.read().strip())
|
|
|
|
return nodes
|
|
|
|
|
|
|
|
|
|
|
|
class DPDKDeviceContext(OSContextGenerator):
|
|
|
|
|
|
|
|
def __call__(self):
|
|
|
|
return {'devices': resolve_dpdk_ports(),
|
|
|
|
'driver': config('dpdk-driver')}
|
|
|
|
|
|
|
|
|
|
|
|
class OVSDPDKDeviceContext(OSContextGenerator):
|
|
|
|
|
|
|
|
def cpu_mask(self):
|
|
|
|
'''
|
|
|
|
Hex formatted CPU mask based on using the first
|
|
|
|
config:dpdk-socket-cores cores of each NUMA node
|
|
|
|
in the unit.
|
|
|
|
'''
|
|
|
|
num_cores = config('dpdk-socket-cores')
|
|
|
|
mask = 0
|
|
|
|
for cores in numa_node_cores().itervalues():
|
|
|
|
for core in cores[:num_cores]:
|
|
|
|
mask = mask | 1 << core
|
|
|
|
return format(mask, '#04x')
|
|
|
|
|
|
|
|
def socket_memory(self):
|
|
|
|
'''
|
|
|
|
Formatted list of socket memory configuration for dpdk using
|
|
|
|
config:dpdk-socket-memory per NUMA node.
|
|
|
|
'''
|
|
|
|
sm_size = config('dpdk-socket-memory')
|
|
|
|
node_regex = '/sys/devices/system/node/node*'
|
|
|
|
mem_list = [str(sm_size) for _ in glob.glob(node_regex)]
|
|
|
|
if mem_list:
|
|
|
|
return ','.join(mem_list)
|
|
|
|
else:
|
|
|
|
return str(sm_size)
|
|
|
|
|
|
|
|
def device_whitelist(self):
|
|
|
|
'''Formatted list of devices to whitelist for dpdk'''
|
|
|
|
_flag = '-w {device}'
|
|
|
|
whitelist = []
|
|
|
|
for device in resolve_dpdk_ports():
|
|
|
|
whitelist.append(_flag.format(device=device))
|
|
|
|
return ' '.join(whitelist)
|
|
|
|
|
|
|
|
def __call__(self):
|
|
|
|
ctxt = {}
|
|
|
|
whitelist = self.device_whitelist()
|
|
|
|
if whitelist:
|
|
|
|
ctxt['dpdk_enabled'] = config('enable-dpdk')
|
|
|
|
ctxt['device_whitelist'] = self.device_whitelist()
|
|
|
|
ctxt['socket_memory'] = self.socket_memory()
|
|
|
|
ctxt['cpu_mask'] = self.cpu_mask()
|
|
|
|
return ctxt
|
|
|
|
|
|
|
|
|
2015-02-25 13:50:38 +00:00
|
|
|
SHARED_SECRET = "/etc/neutron/secret.txt"
|
2015-02-04 16:30:03 +00:00
|
|
|
|
2015-02-25 13:50:38 +00:00
|
|
|
|
|
|
|
def get_shared_secret():
|
|
|
|
secret = None
|
|
|
|
if not os.path.exists(SHARED_SECRET):
|
|
|
|
secret = str(uuid.uuid4())
|
|
|
|
with open(SHARED_SECRET, 'w') as secret_file:
|
|
|
|
secret_file.write(secret)
|
|
|
|
else:
|
|
|
|
with open(SHARED_SECRET, 'r') as secret_file:
|
|
|
|
secret = secret_file.read().strip()
|
|
|
|
return secret
|
|
|
|
|
|
|
|
|
2015-09-08 13:17:28 +01:00
|
|
|
class SharedSecretContext(OSContextGenerator):
|
2015-02-04 16:30:03 +00:00
|
|
|
|
|
|
|
def __call__(self):
|
2015-09-08 15:52:52 +01:00
|
|
|
if NeutronAPIContext()()['enable_dvr'] or \
|
|
|
|
config('enable-local-dhcp-and-metadata'):
|
2015-02-04 16:30:03 +00:00
|
|
|
ctxt = {
|
2015-02-25 13:50:38 +00:00
|
|
|
'shared_secret': get_shared_secret(),
|
2015-02-04 16:30:03 +00:00
|
|
|
}
|
|
|
|
else:
|
|
|
|
ctxt = {}
|
|
|
|
return ctxt
|
2015-03-31 14:33:06 +00:00
|
|
|
|
2015-04-01 09:57:10 +01:00
|
|
|
|
2016-04-19 05:44:09 +00:00
|
|
|
class RemoteRestartContext(OSContextGenerator):
|
|
|
|
|
|
|
|
def __call__(self):
|
|
|
|
for rid in relation_ids('neutron-plugin'):
|
|
|
|
for unit in related_units(rid):
|
|
|
|
restart_uuid = relation_get(
|
|
|
|
attribute='restart-trigger',
|
|
|
|
rid=rid,
|
|
|
|
unit=unit)
|
|
|
|
if restart_uuid:
|
|
|
|
return {'restart_trigger': restart_uuid}
|
|
|
|
return {}
|
|
|
|
|
|
|
|
|
2015-03-31 14:33:06 +00:00
|
|
|
class APIIdentityServiceContext(context.IdentityServiceContext):
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
super(APIIdentityServiceContext,
|
|
|
|
self).__init__(rel_name='neutron-plugin-api')
|
|
|
|
|
|
|
|
def __call__(self):
|
|
|
|
ctxt = super(APIIdentityServiceContext, self).__call__()
|
|
|
|
if not ctxt:
|
|
|
|
return
|
|
|
|
for rid in relation_ids('neutron-plugin-api'):
|
|
|
|
for unit in related_units(rid):
|
|
|
|
rdata = relation_get(rid=rid, unit=unit)
|
|
|
|
ctxt['region'] = rdata.get('region')
|
|
|
|
if ctxt['region']:
|
|
|
|
return ctxt
|
|
|
|
return ctxt
|