b3fe605287
This commit modifies the default system description configuration for the lldpd daemon to remove a product specific name and version. Instead, the OS name and version (default used by lldpd) will be used. For example, for a CentOS host, the system description would look something like: CentOS Linux 7 (Core) For security reasons, we also pass the -k option to the lldpd process in order to prevent the specific kernel version to be broadcast. Change-Id: I2a15481b95d1e37bc2f4ae053a10c3289dd2f400 Closes-Bug: #1796112 Signed-off-by: Steven Webster <steven.webster@windriver.com>
408 lines
14 KiB
Python
408 lines
14 KiB
Python
#
|
|
# Copyright (c) 2018 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
from sysinv.common import constants
|
|
from sysinv.common import utils
|
|
|
|
from . import base
|
|
from . import interface
|
|
|
|
|
|
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.COMPUTE 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_neutron_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_bond_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
|
|
|
|
# currently only one provider network is supported per
|
|
# interface, therefore obtain first entry
|
|
providernet = interface.get_interface_providernets(iface)[0]
|
|
|
|
# setup tunnel address if assigned provider network is vxlan
|
|
if self._is_vxlan_providernet(providernet):
|
|
address = interface.get_interface_primary_address(
|
|
self.context, iface)
|
|
if address:
|
|
ovs_addresses[brname] = {
|
|
'ifname': brname,
|
|
'address': address['address'],
|
|
'prefixlen': address['prefix'],
|
|
}
|
|
|
|
return {
|
|
'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,
|
|
}
|
|
|
|
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 = []
|
|
|
|
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_nr
|
|
vswitch_memory.append(str(vswitch_size * vswitch_pages))
|
|
|
|
dpdk_socket_mem = self.quoted_str(','.join(vswitch_memory))
|
|
|
|
return {
|
|
'vswitch::dpdk::socket_mem': dpdk_socket_mem
|
|
}
|
|
|
|
def _get_virtual_config(self, host):
|
|
config = {}
|
|
if utils.is_virtual() or utils.is_virtual_compute(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 _get_neutron_config(self, host):
|
|
local_ip = None
|
|
tunnel_types = set()
|
|
bridge_mappings = []
|
|
for iface in self.context['interfaces'].values():
|
|
if interface.is_data_network_type(iface):
|
|
# obtain the assigned bridge for interface
|
|
brname = iface.get('_ovs_bridge')
|
|
if brname:
|
|
providernets = interface.get_interface_providernets(iface)
|
|
for providernet in providernets:
|
|
if self._is_vxlan_providernet(providernet):
|
|
address = interface.get_interface_primary_address(
|
|
self.context, iface)
|
|
if address:
|
|
local_ip = address['address']
|
|
tunnel_types.add(
|
|
constants.NEUTRON_PROVIDERNET_VXLAN)
|
|
else:
|
|
bridge_mappings.append('%s:%s' %
|
|
(providernet, brname))
|
|
|
|
return {
|
|
'neutron::agents::ml2::ovs::local_ip': local_ip,
|
|
'neutron::agents::ml2::ovs::tunnel_types': list(tunnel_types),
|
|
'neutron::agents::ml2::ovs::bridge_mappings': bridge_mappings
|
|
}
|
|
|
|
def _get_providernet_type(self, name):
|
|
if name in self.context['providernets']:
|
|
return self.context['providernets'][name]['type']
|
|
|
|
def _is_vxlan_providernet(self, name):
|
|
providernet_type = self._get_providernet_type(name)
|
|
return bool(providernet_type == constants.NEUTRON_PROVIDERNET_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
|
|
}
|