LLDP OVS enablement: puppet configuration

This commit introduces puppet configuration enabling LLDP to operate over
OVS.  Specifically, separate ports flows are configured to handle LLDP
traffic.

In addition, we restrict the lldpd daemon from
operating over bridge, tap, and ovs-netdev devices.

Story: 2002946
Task: 22940

Change-Id: Ibadc9c082425412b5b68b02a55e8c02692de0e17
Signed-off-by: Steven Webster <steven.webster@windriver.com>
This commit is contained in:
Steven Webster 2018-09-18 14:40:20 -04:00
parent 08010aff21
commit da1110a3d8
10 changed files with 180 additions and 19 deletions

View File

@ -35,6 +35,7 @@ define platform::vswitch::ovs::device(
define platform::vswitch::ovs::bridge(
$datapath_type = 'netdev',
$attributes = [],
) {
exec { "ovs-add-br: ${title}":
command => template("platform/ovs.add-bridge.erb")
@ -69,11 +70,24 @@ define platform::vswitch::ovs::address(
}
define platform::vswitch::ovs::flow(
$bridge,
$attributes = [],
$actions,
) {
exec { "ovs-add-flow: ${title}":
command => template("platform/ovs.add-flow.erb"),
logoutput => true
}
}
class platform::vswitch::ovs(
$devices = {},
$bridges = {},
$ports = {},
$addresses = {},
$flows = {},
) inherits ::platform::vswitch::params {
if $::platform::params::vswitch_type == 'ovs' {
@ -116,6 +130,7 @@ class platform::vswitch::ovs(
Platform::Vswitch::Ovs::Bridge<||> -> Platform::Vswitch::Ovs::Port<||>
Platform::Vswitch::Ovs::Bridge<||> -> Platform::Vswitch::Ovs::Address<||>
Platform::Vswitch::Ovs::Port<||> -> Platform::Vswitch::Ovs::Flow<||>
}
create_resources('platform::vswitch::ovs::bridge', $bridges, {
@ -129,4 +144,8 @@ class platform::vswitch::ovs(
create_resources('platform::vswitch::ovs::address', $addresses, {
require => Service['openvswitch']
})
create_resources('platform::vswitch::ovs::flow', $flows, {
require => Service['openvswitch']
})
}

View File

@ -2,3 +2,4 @@ configure system hostname '<%= @hostname %>:<%= @system %>'
configure system description 'Titanium Cloud version <%= @version %>'
configure lldp tx-interval <%= @tx_interval %>
configure lldp tx-hold <%= @tx_hold %>
configure system interface pattern *,!br*,!ovs*,!tap*

View File

@ -1,2 +1,6 @@
ovs-vsctl --timeout 10 -- --may-exist add-br <%= @name -%>
-- set bridge <%= @name -%> datapath_type=<%= @datapath_type -%>
-- set bridge <%= @name -%>
<%- @attributes.each do |attribute| -%>
<%= attribute -%>
<%- end -%>
datapath_type=<%= @datapath_type -%>

View File

@ -0,0 +1,9 @@
ovs-ofctl add-flow <%= @bridge -%>
<%- @attributes.each_with_index do |attrib, idx| -%>
<% if idx == 0 %> <% else -%>,<% end -%>
<%= attrib[0] -%>=<%= attrib[1] -%>
<%- end -%>
,actions=<%- @actions.each_with_index do |action, idx| -%>
<%- if idx > 0 -%>,<%- end -%>
<%= action['type'] -%>:<%= action['value'] -%>
<%- end -%>

View File

@ -13,4 +13,9 @@ ovs-vsctl --timeout 10 -- --may-exist add-<%= @type -%> <%= @bridge -%> <%= @nam
<%- interface['attributes'].each do |attribute| -%>
<%= attribute -%>
<%- end -%>
<%- end %>
<%- @interfaces.each do |interface| -%>
<%- if interface['type'] == 'internal' -%>
ip link set <%= interface['name'] -%> up
<%- end -%>
<%- end -%>

View File

@ -17,7 +17,8 @@
class sysinv::agent (
$agent_driver = false,
$package_ensure = 'latest',
$enabled = true
$enabled = true,
$lldp_drivers = []
) {
include sysinv::params
@ -32,6 +33,10 @@ class sysinv::agent (
}
}
sysinv_config {
'lldp/drivers': value => join($lldp_drivers,",");
}
if $::sysinv::params::agent_package {
Package['sysinv-agent'] -> Sysinv_config<||>
Package['sysinv-agent'] -> Sysinv_api_paste_ini<||>

View File

@ -1041,6 +1041,10 @@ UPGRADE_ABORT_COMPLETING = 'abort-completing'
UPGRADE_ABORTING_ROLLBACK = 'aborting-reinstall'
# LLDP
LLDP_OVS_PORT_PREFIX = 'lldp'
LLDP_OVS_PORT_NAME_LEN = 15
LLDP_MULTICAST_ADDRESS = '01:80:c2:00:00:0e'
LLDP_ETHER_TYPE = '0x88cc'
LLDP_TLV_TYPE_CHASSIS_ID = 'chassis_id'
LLDP_TLV_TYPE_PORT_ID = 'port_identifier'
LLDP_TLV_TYPE_TTL = 'ttl'

View File

@ -2039,6 +2039,15 @@ class ConductorManager(service.PeriodicService):
LOG.info("Updating port name: %s to %s" % (port_name, updated_name))
self.dbapi.ethernet_port_update(port['uuid'], {'name': updated_name})
def lldp_id_to_port(self, id, ports):
ovs_id = re.sub(r'^{}'.format(constants.LLDP_OVS_PORT_PREFIX), '', id)
for port in ports:
if (port['name'] == id or
port['uuid'] == id or
port['uuid'].find(ovs_id) == 0):
return port
return None
def lldp_tlv_dict(self, agent_neighbour_dict):
tlv_dict = {}
for k, v in agent_neighbour_dict.iteritems():
@ -2156,14 +2165,8 @@ class ConductorManager(service.PeriodicService):
"Error getting LLDP agents for host %s") % host_uuid)
for agent in agent_dict_array:
port_found = None
for db_port in db_ports:
if (db_port['name'] == agent['name_or_uuid'] or
db_port['uuid'] == agent['name_or_uuid']):
port_found = db_port
break
if not port_found:
db_port = self.lldp_id_to_port(agent['name_or_uuid'], db_ports)
if not db_port:
LOG.debug("Could not find port for agent %s",
agent['name_or_uuid'])
return
@ -2269,16 +2272,10 @@ class ConductorManager(service.PeriodicService):
neighbour['uuid'])
for neighbour in neighbour_dict_array:
port_found = None
for db_port in db_ports:
if (db_port['name'] == neighbour['name_or_uuid'] or
db_port['uuid'] == neighbour['name_or_uuid']):
port_found = db_port
break
if not port_found:
db_port = self.lldp_id_to_port(neighbour['name_or_uuid'], db_ports)
if not db_port:
LOG.debug("Could not find port for neighbour %s",
neighbour['name'])
neighbour['name_or_uuid'])
return
LOG.debug("Processing lldp neighbour %s" % neighbour)

View File

@ -26,6 +26,7 @@ class OVSPuppet(base.BasePuppet):
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):
@ -33,6 +34,7 @@ class OVSPuppet(base.BasePuppet):
ovs_bridges = {}
ovs_ports = {}
ovs_addresses = {}
ovs_flows = {}
index = 0
for iface in sorted(self.context['interfaces'].values(),
@ -61,6 +63,31 @@ class OVSPuppet(base.BasePuppet):
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
@ -83,6 +110,7 @@ class OVSPuppet(base.BasePuppet):
'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):
@ -147,6 +175,74 @@ class OVSPuppet(base.BasePuppet):
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 = []
@ -294,3 +390,11 @@ class OVSPuppet(base.BasePuppet):
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')
return {
'sysinv::agent::lldp_drivers': driver_list
}

View File

@ -70,6 +70,7 @@ class PlatformPuppet(base.BasePuppet):
config.update(self._get_host_tpm_config(host))
config.update(self._get_host_cpu_config(host))
config.update(self._get_host_memory_config(host))
config.update(self._get_host_lldp_config(host))
return config
def _get_static_software_config(self):
@ -818,3 +819,15 @@ class PlatformPuppet(base.BasePuppet):
def _get_platform_cpu_count(self, host):
cpus = self._get_host_cpu_list(host, constants.PLATFORM_FUNCTION, True)
return len(cpus)
def _get_host_lldp_config(self, host):
driver_list = []
# Default is lldpd
driver_list.append('lldpd')
self.context['_lldp_drivers'] = driver_list
return {
'sysinv::agent::lldp_drivers': driver_list
}