ad7f870c0d
Replace deprecated code that was used for setting up DPDK ports and bonds with calls to charmhelpers functions. Pass DPDK configuration using ifdata and portdata dicts instead of making direct ovs-vsctl calls. Move installation of sriov-netplan-shim to the bash wrapper. This resolves problems with non-working imports of sriov-netplan-shim in charmhelpers. Update unit tests to reflect that change. Signed-off-by: Przemysław Lal <przemyslaw.lal@canonical.com> Change-Id: Ica6f3ea66136bca6c77a5fb55ad7ef5d95aa1f6a
439 lines
14 KiB
Python
439 lines
14 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 os
|
|
import uuid
|
|
from charmhelpers.core.hookenv import (
|
|
config,
|
|
log,
|
|
relation_get,
|
|
relation_ids,
|
|
related_units,
|
|
unit_get,
|
|
network_get_primary_address,
|
|
)
|
|
from charmhelpers.core.host import (
|
|
CompareHostReleases,
|
|
is_container,
|
|
lsb_release,
|
|
write_file,
|
|
)
|
|
from charmhelpers.contrib.openstack import context
|
|
from charmhelpers.contrib.openstack.utils import (
|
|
get_host_ip,
|
|
)
|
|
from charmhelpers.contrib.network.ip import (
|
|
get_address_in_network,
|
|
)
|
|
from charmhelpers.contrib.openstack.context import (
|
|
OSContextGenerator,
|
|
NeutronAPIContext,
|
|
)
|
|
from charmhelpers.contrib.openstack.utils import (
|
|
os_release,
|
|
CompareOpenStackReleases,
|
|
)
|
|
|
|
IPTABLES_HYBRID = 'iptables_hybrid'
|
|
OPENVSWITCH = 'openvswitch'
|
|
VALID_FIREWALL_DRIVERS = (IPTABLES_HYBRID, OPENVSWITCH)
|
|
|
|
NFG_LOG_RATE_LIMIT_MIN = 100
|
|
NFG_LOG_BURST_LIMIT_MIN = 25
|
|
|
|
|
|
def _get_firewall_driver(ovs_ctxt):
|
|
'''
|
|
Determine the firewall driver to use based on configuration,
|
|
OpenStack and Ubuntu releases.
|
|
|
|
@returns str: firewall driver to use for OpenvSwitch
|
|
'''
|
|
driver = config('firewall-driver') or IPTABLES_HYBRID
|
|
release = lsb_release()['DISTRIB_CODENAME']
|
|
if driver not in VALID_FIREWALL_DRIVERS:
|
|
return IPTABLES_HYBRID
|
|
|
|
if driver == IPTABLES_HYBRID and ovs_ctxt['enable_nsg_logging']:
|
|
msg = "NSG logging can not be enabled - need to set " \
|
|
"firewall driver to 'openvswitch' explicitly"
|
|
log(msg, "WARN")
|
|
|
|
if (driver == OPENVSWITCH and
|
|
CompareHostReleases(release) < 'xenial'):
|
|
# NOTE(jamespage): Switch back to iptables_hybrid for
|
|
# Ubuntu releases prior to Xenial due
|
|
# to requirements for Linux >= 4.4 and
|
|
# Open vSwitch >= 2.5
|
|
return IPTABLES_HYBRID
|
|
|
|
return driver
|
|
|
|
|
|
def get_nsg_log_path(desired_nsg_log_path):
|
|
if not desired_nsg_log_path:
|
|
# None means "we need to use syslog" - no need
|
|
# to check anything on filesystem
|
|
return None
|
|
|
|
dst_dir, _ = os.path.split(desired_nsg_log_path)
|
|
path_exists = os.path.exists(dst_dir)
|
|
if not path_exists:
|
|
log(
|
|
"Desired NSG log directory {} not exists! "
|
|
"falling back to syslog".format(dst_dir),
|
|
"WARN"
|
|
)
|
|
return None
|
|
|
|
if path_exists and os.path.isdir(desired_nsg_log_path):
|
|
log(
|
|
"Desired NSG log path {} should be file, not directory! "
|
|
"falling back to syslog".format(desired_nsg_log_path),
|
|
"WARN"
|
|
)
|
|
return None
|
|
|
|
return desired_nsg_log_path
|
|
|
|
|
|
def validate_nfg_log_path(desired_nfg_log_path):
|
|
if not desired_nfg_log_path:
|
|
# None means "we need to use syslog" - no need
|
|
# to check anything on filesystem
|
|
return None
|
|
|
|
dst_dir, _ = os.path.split(desired_nfg_log_path)
|
|
path_exists = os.path.exists(dst_dir)
|
|
if not path_exists:
|
|
log(
|
|
"Desired NFG log directory {} not exists! "
|
|
"falling back to syslog".format(dst_dir),
|
|
"WARN"
|
|
)
|
|
return None
|
|
|
|
if path_exists and os.path.isdir(desired_nfg_log_path):
|
|
log(
|
|
"Desired NFG log path {} should be file, not directory! "
|
|
"falling back to syslog".format(desired_nfg_log_path),
|
|
"WARN"
|
|
)
|
|
return None
|
|
|
|
return desired_nfg_log_path
|
|
|
|
|
|
class OVSPluginContext(context.NeutronContext):
|
|
interfaces = []
|
|
|
|
@property
|
|
def plugin(self):
|
|
return 'ovs'
|
|
|
|
@property
|
|
def network_manager(self):
|
|
return 'neutron'
|
|
|
|
@property
|
|
def neutron_security_groups(self):
|
|
if config('disable-security-groups'):
|
|
return False
|
|
neutron_api_settings = NeutronAPIContext()()
|
|
return neutron_api_settings['neutron_security_groups']
|
|
|
|
def disable_mlockall(self):
|
|
'''
|
|
Determine if Open vSwitch use of mlockall() should be disabled
|
|
|
|
If the disable-mlockall config option is unset, mlockall will be
|
|
disabled if running in a container and will default to enabled if
|
|
not running in a container.
|
|
'''
|
|
disable_mlockall = config('disable-mlockall')
|
|
if disable_mlockall is None:
|
|
disable_mlockall = False
|
|
if is_container():
|
|
disable_mlockall = True
|
|
cmp_release = CompareOpenStackReleases(
|
|
os_release('neutron-common', base='icehouse'))
|
|
return (cmp_release >= 'mitaka' and disable_mlockall)
|
|
|
|
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 {}
|
|
|
|
conf = config()
|
|
|
|
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
|
|
|
|
neutron_api_settings = NeutronAPIContext()()
|
|
ovs_ctxt['neutron_security_groups'] = self.neutron_security_groups
|
|
ovs_ctxt['l2_population'] = neutron_api_settings['l2_population']
|
|
ovs_ctxt['distributed_routing'] = neutron_api_settings['enable_dvr']
|
|
ovs_ctxt['extension_drivers'] = neutron_api_settings[
|
|
'extension_drivers']
|
|
ovs_ctxt['overlay_network_type'] = \
|
|
neutron_api_settings['overlay_network_type']
|
|
ovs_ctxt['polling_interval'] = neutron_api_settings['polling_interval']
|
|
ovs_ctxt['rpc_response_timeout'] = \
|
|
neutron_api_settings['rpc_response_timeout']
|
|
ovs_ctxt['report_interval'] = neutron_api_settings['report_interval']
|
|
# TODO: We need to sort out the syslog and debug/verbose options as a
|
|
# general context helper
|
|
ovs_ctxt['use_syslog'] = conf['use-syslog']
|
|
ovs_ctxt['verbose'] = conf['verbose']
|
|
ovs_ctxt['debug'] = conf['debug']
|
|
ovs_ctxt['prevent_arp_spoofing'] = conf['prevent-arp-spoofing']
|
|
ovs_ctxt['enable_dpdk'] = conf['enable-dpdk']
|
|
ovs_ctxt['keepalived_healthcheck_interval'] = \
|
|
conf['keepalived-healthcheck-interval']
|
|
ovs_ctxt['disable_mlockall'] = self.disable_mlockall()
|
|
|
|
net_dev_mtu = neutron_api_settings.get('network_device_mtu')
|
|
if net_dev_mtu:
|
|
# neutron.conf
|
|
ovs_ctxt['network_device_mtu'] = net_dev_mtu
|
|
# ml2 conf
|
|
ovs_ctxt['veth_mtu'] = net_dev_mtu
|
|
|
|
mappings = config('bridge-mappings')
|
|
if mappings:
|
|
ovs_ctxt['bridge_mappings'] = ','.join(mappings.split())
|
|
|
|
sriov_mappings = config('sriov-device-mappings')
|
|
if sriov_mappings:
|
|
ovs_ctxt['sriov_device_mappings'] = (
|
|
','.join(sriov_mappings.split())
|
|
)
|
|
|
|
enable_sriov = config('enable-sriov')
|
|
if enable_sriov:
|
|
ovs_ctxt['enable_sriov'] = True
|
|
|
|
sriov_numvfs = config('sriov-numvfs')
|
|
if sriov_numvfs:
|
|
try:
|
|
if sriov_numvfs != 'auto':
|
|
int(sriov_numvfs)
|
|
except ValueError:
|
|
ovs_ctxt['sriov_vfs_list'] = sriov_numvfs
|
|
else:
|
|
ovs_ctxt['sriov_vfs_blanket'] = sriov_numvfs
|
|
|
|
flat_providers = config('flat-network-providers')
|
|
if flat_providers:
|
|
ovs_ctxt['network_providers'] = ','.join(flat_providers.split())
|
|
|
|
vlan_ranges = config('vlan-ranges')
|
|
if vlan_ranges:
|
|
ovs_ctxt['vlan_ranges'] = ','.join(vlan_ranges.split())
|
|
|
|
ovs_ctxt['enable_nsg_logging'] = \
|
|
neutron_api_settings['enable_nsg_logging']
|
|
|
|
ovs_ctxt['nsg_log_output_base'] = get_nsg_log_path(
|
|
config('security-group-log-output-base')
|
|
)
|
|
ovs_ctxt['nsg_log_rate_limit'] = \
|
|
config('security-group-log-rate-limit')
|
|
ovs_ctxt['nsg_log_burst_limit'] = \
|
|
config('security-group-log-burst-limit')
|
|
|
|
ovs_ctxt['firewall_driver'] = _get_firewall_driver(ovs_ctxt)
|
|
|
|
if ovs_ctxt['firewall_driver'] != OPENVSWITCH:
|
|
ovs_ctxt['enable_nsg_logging'] = False
|
|
|
|
ovs_ctxt['of_inactivity_probe'] = config('of-inactivity-probe')
|
|
|
|
return ovs_ctxt
|
|
|
|
|
|
class ZoneContext(OSContextGenerator):
|
|
|
|
def __call__(self):
|
|
"""Return the 'default_availability_zone' from the principal that this
|
|
ovs unit is attached to (as a subordinate)
|
|
|
|
:returns: {} if no relation set, or
|
|
{'availability_zone': availability_zone from principal relation}
|
|
"""
|
|
# as ovs is a subordinate charm, it should only have one relation to
|
|
# its principal charm. Thus we can take the 1st (only) element in each
|
|
# list.
|
|
rids = relation_ids('neutron-plugin')
|
|
ctxt = {}
|
|
if rids:
|
|
rid = rids[0]
|
|
units = related_units(rid)
|
|
if units:
|
|
availability_zone = relation_get(
|
|
'default_availability_zone',
|
|
rid=rid,
|
|
unit=units[0])
|
|
if availability_zone:
|
|
ctxt['availability_zone'] = availability_zone
|
|
return ctxt
|
|
|
|
|
|
class L3AgentContext(OSContextGenerator):
|
|
|
|
def __call__(self):
|
|
neutron_api_settings = NeutronAPIContext()()
|
|
ctxt = {}
|
|
if neutron_api_settings['enable_dvr']:
|
|
use_dvr_snat = config('use-dvr-snat')
|
|
agent_mode = 'dvr_snat' if use_dvr_snat else 'dvr'
|
|
ctxt['agent_mode'] = agent_mode
|
|
ctxt['use_l3ha'] = neutron_api_settings.get('enable_l3ha', False)
|
|
if not config('ext-port'):
|
|
ctxt['external_configuration_new'] = True
|
|
else:
|
|
ctxt['agent_mode'] = 'legacy'
|
|
|
|
ctxt['enable_nfg_logging'] = (
|
|
neutron_api_settings['enable_nfg_logging']
|
|
)
|
|
|
|
ctxt['nfg_log_output_base'] = validate_nfg_log_path(
|
|
config('firewall-group-log-output-base')
|
|
)
|
|
ctxt['nfg_log_rate_limit'] = config(
|
|
'firewall-group-log-rate-limit'
|
|
)
|
|
if ctxt['nfg_log_rate_limit'] is not None:
|
|
ctxt['nfg_log_rate_limit'] = max(
|
|
ctxt['nfg_log_rate_limit'],
|
|
NFG_LOG_RATE_LIMIT_MIN
|
|
)
|
|
ctxt['nfg_log_burst_limit'] = config(
|
|
'firewall-group-log-burst-limit'
|
|
)
|
|
if ctxt['nfg_log_burst_limit'] is not None:
|
|
ctxt['nfg_log_burst_limit'] = max(
|
|
ctxt['nfg_log_burst_limit'],
|
|
NFG_LOG_BURST_LIMIT_MIN
|
|
)
|
|
|
|
cmp_os_release = CompareOpenStackReleases(os_release('neutron-common'))
|
|
|
|
l3_extension_plugins = neutron_api_settings.get(
|
|
'l3_extension_plugins', [])
|
|
|
|
# per Change-Id If1b332eb0f581e9acba111f79ba578a0b7081dd2
|
|
# only enable it for stein although fwaasv2 was added in Queens
|
|
is_stein = cmp_os_release >= 'stein'
|
|
if is_stein:
|
|
l3_extension_plugins.append('fwaas_v2')
|
|
|
|
if (is_stein and neutron_api_settings.get('enable_nfg_logging')):
|
|
l3_extension_plugins.append('fwaas_v2_log')
|
|
|
|
ctxt['l3_extension_plugins'] = ','.join(l3_extension_plugins)
|
|
|
|
return ctxt
|
|
|
|
|
|
SHARED_SECRET = "/etc/neutron/secret.txt"
|
|
|
|
|
|
def get_shared_secret():
|
|
secret = None
|
|
if not os.path.exists(SHARED_SECRET):
|
|
secret = str(uuid.uuid4())
|
|
write_file(SHARED_SECRET, secret,
|
|
perms=0o400)
|
|
else:
|
|
os.chmod(SHARED_SECRET, 0o400)
|
|
with open(SHARED_SECRET, 'r') as secret_file:
|
|
secret = secret_file.read().strip()
|
|
return secret
|
|
|
|
|
|
class SharedSecretContext(OSContextGenerator):
|
|
|
|
def __call__(self):
|
|
if NeutronAPIContext()()['enable_dvr'] or \
|
|
config('enable-local-dhcp-and-metadata'):
|
|
ctxt = {
|
|
'shared_secret': get_shared_secret(),
|
|
}
|
|
else:
|
|
ctxt = {}
|
|
return ctxt
|
|
|
|
|
|
class RemoteRestartContext(OSContextGenerator):
|
|
|
|
def __init__(self, interfaces=None):
|
|
self.interfaces = interfaces or ['neutron-plugin']
|
|
|
|
def __call__(self):
|
|
rids = []
|
|
for interface in self.interfaces:
|
|
rids.extend(relation_ids(interface))
|
|
ctxt = {}
|
|
for rid in rids:
|
|
for unit in related_units(rid):
|
|
remote_data = relation_get(
|
|
rid=rid,
|
|
unit=unit)
|
|
for k, v in remote_data.items():
|
|
if k.startswith('restart-trigger'):
|
|
restart_key = k.replace('-', '_')
|
|
try:
|
|
ctxt[restart_key].append(v)
|
|
except KeyError:
|
|
ctxt[restart_key] = [v]
|
|
for restart_key in ctxt.keys():
|
|
ctxt[restart_key] = '-'.join(sorted(ctxt[restart_key]))
|
|
return ctxt
|
|
|
|
|
|
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
|