213ef064f9
The code was invoking neutronclient methods such as bind, unbind and list providernets which do not exist. None of the neutronclient calls from within sysinv are used and therefore all have been removed from the codebase. This ends up removing the following methods - def bind_interface - def _get_neutronclient - def get_providernetworksdict - def iinterface_get_providernets - def neutron_bind_interface - def neutron_extension_list - def _neutron_host_extension_supported - def _neutron_providernet_extension_supported - def neutron_unbind_interface - def unbind_interface - def _update_shared_interface_neutron_bindings As well as unused calls in puppet/ovs - def _get_neutron_config - def _get_providernet_type - def _is_vxlan_providernet And these previously skipped tests in test_interface - def test_create_neutron_bind_failed - def test_patch_neutron_bind_failed Change-Id: I1c81a3a7964c1581edf78b336c55e23bce4782cc Story: 2004515 Task: 36298 Signed-off-by: Al Bailey <Al.Bailey@windriver.com>
394 lines
13 KiB
Python
394 lines
13 KiB
Python
#
|
|
# Copyright (c) 2018 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
from oslo_log import log
|
|
from sysinv.common import constants
|
|
from sysinv.common import utils
|
|
|
|
from sysinv.puppet import base
|
|
from sysinv.puppet import interface
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
class OVSPuppet(base.BasePuppet):
|
|
"""Class to encapsulate puppet operations for vswitch configuration"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(OVSPuppet, self).__init__(*args, **kwargs)
|
|
|
|
def get_host_config(self, host):
|
|
config = {}
|
|
if (constants.WORKER in utils.get_personalities(host) and
|
|
self._vswitch_type() == constants.VSWITCH_TYPE_OVS_DPDK):
|
|
config.update(self._get_cpu_config(host))
|
|
config.update(self._get_memory_config(host))
|
|
config.update(self._get_port_config(host))
|
|
config.update(self._get_virtual_config(host))
|
|
config.update(self._get_lldp_config(host))
|
|
return config
|
|
|
|
def _get_port_config(self, host):
|
|
ovs_devices = {}
|
|
ovs_bridges = {}
|
|
ovs_ports = {}
|
|
ovs_addresses = {}
|
|
ovs_flows = {}
|
|
|
|
index = 0
|
|
for iface in sorted(self.context['interfaces'].values(),
|
|
key=interface.interface_sort_key):
|
|
if interface.is_data_network_type(iface):
|
|
# create a separate bridge for every configured data interface
|
|
brname = 'br-phy%d' % index
|
|
ovs_bridges[brname] = {}
|
|
|
|
# save the associated bridge for provider network mapping
|
|
iface['_ovs_bridge'] = brname
|
|
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
|
port, devices = self._get_ethernet_port(
|
|
host, iface, brname, index)
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_AE:
|
|
port, devices = self._get_bond_port(
|
|
host, iface, brname, index)
|
|
elif iface['iftype'] == constants.INTERFACE_TYPE_VLAN:
|
|
port, devices = self._get_vlan_port(
|
|
host, iface, brname, index)
|
|
else:
|
|
raise Exception("unsupported interface type: %s" %
|
|
iface['iftype'])
|
|
|
|
ovs_ports.update({port['name']: port})
|
|
ovs_devices.update({d['pci_addr']: d for d in devices})
|
|
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_ETHERNET:
|
|
ovs_ifname = port['interfaces'][0]['name']
|
|
lldp_port = self._get_lldp_port(
|
|
iface, brname, ovs_ifname=ovs_ifname)
|
|
ovs_ports.update({lldp_port['name']: lldp_port})
|
|
flow = self._get_lldp_flow(
|
|
brname, ovs_ifname, lldp_port['name'])
|
|
ovs_flows.update({port['name']: flow})
|
|
|
|
if iface['iftype'] == constants.INTERFACE_TYPE_AE:
|
|
slaves = interface.get_interface_slaves(
|
|
self.context, iface)
|
|
for member, slave in enumerate(slaves):
|
|
ovs_ifname = port['interfaces'][member]['name']
|
|
|
|
lldp_port = self._get_lldp_port(
|
|
slave, brname, ovs_ifname=ovs_ifname)
|
|
ovs_ports.update({lldp_port['name']: lldp_port})
|
|
flow = self._get_lldp_flow(
|
|
brname, ovs_ifname, lldp_port['name'])
|
|
ovs_flows.update({flow['name']: flow})
|
|
flow = self._get_lldp_flow(
|
|
brname, lldp_port['name'], ovs_ifname)
|
|
ovs_flows.update({flow['name']: flow})
|
|
|
|
index += 1
|
|
|
|
datanets = interface.get_interface_datanets(self.context, iface)
|
|
|
|
# setup tunnel address if assigned provider network is vxlan
|
|
if datanets and self._is_vxlan_datanet(datanets[0]):
|
|
address = interface.get_interface_primary_address(
|
|
self.context, iface)
|
|
if address:
|
|
ovs_addresses[brname] = {
|
|
'ifname': brname,
|
|
'address': address['address'],
|
|
'prefixlen': address['prefix'],
|
|
}
|
|
|
|
ovs_dict = {
|
|
'platform::vswitch::ovs::devices': ovs_devices,
|
|
'platform::vswitch::ovs::bridges': ovs_bridges,
|
|
'platform::vswitch::ovs::ports': ovs_ports,
|
|
'platform::vswitch::ovs::addresses': ovs_addresses,
|
|
'platform::vswitch::ovs::flows': ovs_flows,
|
|
}
|
|
|
|
LOG.debug("_get_port_config=%s" % ovs_dict)
|
|
|
|
return ovs_dict
|
|
|
|
def _get_ethernet_device(self, iface):
|
|
if interface.is_a_mellanox_device(self.context, iface):
|
|
# Mellanox devices are not bound to the DPDK driver
|
|
return None
|
|
|
|
port = interface.get_interface_port(self.context, iface)
|
|
|
|
pci_addr = self.quoted_str(port.pciaddr)
|
|
|
|
return {
|
|
'pci_addr': pci_addr
|
|
}
|
|
|
|
def _get_ethernet_interface(self, host, iface, ifname):
|
|
|
|
port = interface.get_interface_port(self.context, iface)
|
|
|
|
if interface.is_a_mellanox_device(self.context, iface):
|
|
# Mellanox devices use an ibverbs enumerated device name, therefore
|
|
# use the MAC address to identify the device.
|
|
device_name = "class=eth,mac=%s" % iface['imac']
|
|
else:
|
|
device_name = str(port.pciaddr)
|
|
|
|
rxq_count = len(self.context["_ovs_cpus"])
|
|
|
|
attributes = [
|
|
"options:dpdk-devargs=%s" % device_name,
|
|
"options:n_rxq=%d" % rxq_count,
|
|
"mtu_request=%d" % iface['imtu']
|
|
]
|
|
|
|
# TODO(mpeters): set other_config:pmd-rxq-affinity to pin receive
|
|
# queues to specific PMD cores
|
|
|
|
iftype = 'dpdk'
|
|
|
|
return {
|
|
'name': ifname,
|
|
'type': iftype,
|
|
'attributes': attributes,
|
|
}
|
|
|
|
def _get_ethernet_port(self, host, iface, bridge, index):
|
|
devices = []
|
|
interfaces = []
|
|
|
|
ifname = 'eth%d' % index
|
|
|
|
device = self._get_ethernet_device(iface)
|
|
if device:
|
|
devices.append(device)
|
|
interfaces.append(self._get_ethernet_interface(host, iface, ifname))
|
|
|
|
port = {
|
|
'name': ifname,
|
|
'bridge': bridge,
|
|
'interfaces': interfaces,
|
|
}
|
|
|
|
return port, devices
|
|
|
|
def _get_lldp_interface(self, ifname, peer_ifname):
|
|
attributes = []
|
|
|
|
iftype = 'internal'
|
|
|
|
attributes.append("other_config:lldp_phy_peer=%s" % peer_ifname)
|
|
|
|
return {
|
|
'name': ifname,
|
|
'type': iftype,
|
|
'attributes': attributes,
|
|
}
|
|
|
|
def _get_lldp_port(self, iface, lldp_brname, ovs_ifname=None):
|
|
interfaces = []
|
|
|
|
port = interface.get_interface_port(self.context, iface)
|
|
|
|
# Limit port name length to the maximum supported by ovs-ofctl to
|
|
# reference a port with a name rather than ofport number
|
|
# when creating flows.
|
|
|
|
port_name_len = constants.LLDP_OVS_PORT_NAME_LEN
|
|
uuid_len = port_name_len - len(constants.LLDP_OVS_PORT_PREFIX)
|
|
|
|
port_name = '{}{}'.format(constants.LLDP_OVS_PORT_PREFIX,
|
|
port.uuid[:uuid_len])
|
|
|
|
if ovs_ifname:
|
|
interfaces.append(self._get_lldp_interface(port_name, ovs_ifname))
|
|
else:
|
|
interfaces.append(self._get_lldp_interface(port_name, iface['name']))
|
|
|
|
port = {
|
|
'name': port_name,
|
|
'bridge': lldp_brname,
|
|
'interfaces': interfaces,
|
|
}
|
|
|
|
return port
|
|
|
|
def _get_lldp_flow(self, bridge, in_port, out_port):
|
|
actions = []
|
|
|
|
attributes = {
|
|
'idle_timeout': 0,
|
|
'hard_timeout': 0,
|
|
'in_port': in_port,
|
|
'dl_dst': constants.LLDP_MULTICAST_ADDRESS,
|
|
'dl_type': constants.LLDP_ETHER_TYPE
|
|
}
|
|
|
|
action = {
|
|
'type': 'output',
|
|
'value': out_port
|
|
}
|
|
|
|
actions.append(action)
|
|
|
|
flow = {
|
|
'name': '{}-{}-{}'.format(bridge, in_port, out_port),
|
|
'bridge': bridge,
|
|
'attributes': attributes,
|
|
'actions': actions
|
|
}
|
|
|
|
return flow
|
|
|
|
def _get_bond_port(self, host, iface, bridge, index):
|
|
devices = []
|
|
interfaces = []
|
|
attributes = []
|
|
|
|
ifname = 'bond%d' % index
|
|
|
|
# TODO(mpeters): OVS can support balance-tcp if interface txhashpolicy
|
|
# is set to layer3+4 (currently restricted at API for data interfaces)
|
|
ae_mode = iface['aemode']
|
|
if ae_mode in interface.ACTIVE_STANDBY_AE_MODES:
|
|
attributes.append("bond_mode=active-backup")
|
|
if ae_mode in interface.BALANCED_AE_MODES:
|
|
attributes.append("bond_mode=balance-slb")
|
|
elif ae_mode in interface.LACP_AE_MODES:
|
|
attributes.append("lacp=active")
|
|
attributes.append("bond_mode=balance-slb")
|
|
attributes.append("other_config:lacp-time=fast")
|
|
|
|
for member, lower_ifname in enumerate(iface['uses']):
|
|
lower_iface = self.context['interfaces'][lower_ifname]
|
|
member_ifname = '%s.%d' % (ifname, member)
|
|
|
|
device = self._get_ethernet_device(lower_iface)
|
|
if device:
|
|
devices.append(device)
|
|
|
|
interfaces.append(self._get_ethernet_interface(
|
|
host, lower_iface, member_ifname))
|
|
|
|
port = {
|
|
'type': 'bond',
|
|
'name': ifname,
|
|
'bridge': bridge,
|
|
'attributes': attributes,
|
|
'interfaces': interfaces,
|
|
}
|
|
|
|
return port, devices
|
|
|
|
def _get_vlan_port(self, host, iface, bridge, index):
|
|
devices = []
|
|
interfaces = []
|
|
|
|
ifname = 'vlan%d' % iface['vlan_id']
|
|
attributes = [
|
|
"tag=%d" % iface['vlan_id']
|
|
]
|
|
|
|
lower_iface = interface.get_lower_interface(self.context, iface)
|
|
|
|
device = self._get_ethernet_device(lower_iface)
|
|
if device:
|
|
devices.append(device)
|
|
|
|
interfaces.append(self._get_ethernet_interface(
|
|
host, lower_iface, ifname))
|
|
|
|
port = {
|
|
'name': ifname,
|
|
'bridge': bridge,
|
|
'attributes': attributes,
|
|
'interfaces': interfaces,
|
|
}
|
|
|
|
return port, devices
|
|
|
|
def _get_cpu_config(self, host):
|
|
platform_cpus = self._get_platform_cpu_list(host)
|
|
vswitch_cpus = self._get_vswitch_cpu_list(host)
|
|
|
|
host_cpus = platform_cpus[:1] + vswitch_cpus[:]
|
|
|
|
host_core_list = self.quoted_str(
|
|
','.join([str(c.cpu) for c in host_cpus]))
|
|
pmd_core_list = self.quoted_str(
|
|
','.join([str(c.cpu) for c in vswitch_cpus]))
|
|
|
|
# save the assigned CPUs for port assignment
|
|
self.context["_ovs_cpus"] = [c.cpu for c in vswitch_cpus]
|
|
|
|
return {
|
|
'vswitch::dpdk::host_core_list': host_core_list,
|
|
'vswitch::dpdk::pmd_core_list': pmd_core_list,
|
|
}
|
|
|
|
def _get_memory_config(self, host):
|
|
vswitch_memory = []
|
|
config = {}
|
|
vswitch_size = 0
|
|
|
|
host_memory = self.dbapi.imemory_get_by_ihost(host.id)
|
|
for memory in host_memory:
|
|
vswitch_size = memory.vswitch_hugepages_size_mib
|
|
vswitch_pages = memory.vswitch_hugepages_reqd \
|
|
if memory.vswitch_hugepages_reqd is not None \
|
|
else memory.vswitch_hugepages_nr
|
|
|
|
if vswitch_pages == 0:
|
|
vswitch_pages = memory.vswitch_hugepages_nr
|
|
|
|
vswitch_memory.append(str(vswitch_size * vswitch_pages))
|
|
|
|
dpdk_socket_mem = self.quoted_str(','.join(vswitch_memory))
|
|
|
|
config.update({
|
|
'vswitch::dpdk::socket_mem': dpdk_socket_mem
|
|
})
|
|
|
|
if vswitch_size == constants.MIB_2M:
|
|
config.update({
|
|
'platform::vswitch::params::hugepage_dir': '/mnt/huge-2048kB'
|
|
})
|
|
|
|
return config
|
|
|
|
def _get_virtual_config(self, host):
|
|
config = {}
|
|
if utils.is_virtual() or utils.is_virtual_worker(host):
|
|
config.update({
|
|
'platform::vswitch::params::iommu_enabled': False,
|
|
'platform::vswitch::params::hugepage_dir': '/mnt/huge-2048kB',
|
|
|
|
'openstack::neutron::params::tunnel_csum': True,
|
|
})
|
|
return config
|
|
|
|
def _is_vxlan_datanet(self, datanet):
|
|
return datanet.get('network_type') == constants.DATANETWORK_TYPE_VXLAN
|
|
|
|
def _get_lldp_config(self, host):
|
|
driver_list = self.context['_lldp_drivers']
|
|
driver_list.append('ovs')
|
|
|
|
lldpd_options = []
|
|
|
|
# Disable broadcasting the kernel version
|
|
lldpd_kernel_option = {"option": "-k"}
|
|
lldpd_options.append(lldpd_kernel_option)
|
|
|
|
return {
|
|
'sysinv::agent::lldp_drivers': driver_list,
|
|
'platform::lldp::params::options': lldpd_options
|
|
}
|