diff --git a/puppet-manifests/src/modules/platform/manifests/vswitch.pp b/puppet-manifests/src/modules/platform/manifests/vswitch.pp index 1ec5e08000..1e73d74205 100644 --- a/puppet-manifests/src/modules/platform/manifests/vswitch.pp +++ b/puppet-manifests/src/modules/platform/manifests/vswitch.pp @@ -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'] + }) } diff --git a/puppet-manifests/src/modules/platform/templates/lldp.conf.erb b/puppet-manifests/src/modules/platform/templates/lldp.conf.erb index 0df6469d41..6e6ceae1a1 100644 --- a/puppet-manifests/src/modules/platform/templates/lldp.conf.erb +++ b/puppet-manifests/src/modules/platform/templates/lldp.conf.erb @@ -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* diff --git a/puppet-manifests/src/modules/platform/templates/ovs.add-bridge.erb b/puppet-manifests/src/modules/platform/templates/ovs.add-bridge.erb index 59fdc07af0..82d311ba7b 100644 --- a/puppet-manifests/src/modules/platform/templates/ovs.add-bridge.erb +++ b/puppet-manifests/src/modules/platform/templates/ovs.add-bridge.erb @@ -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 -%> diff --git a/puppet-manifests/src/modules/platform/templates/ovs.add-flow.erb b/puppet-manifests/src/modules/platform/templates/ovs.add-flow.erb new file mode 100644 index 0000000000..62f5bfb659 --- /dev/null +++ b/puppet-manifests/src/modules/platform/templates/ovs.add-flow.erb @@ -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 -%> \ No newline at end of file diff --git a/puppet-manifests/src/modules/platform/templates/ovs.add-port.erb b/puppet-manifests/src/modules/platform/templates/ovs.add-port.erb index 80d66de163..d7f779d4f1 100644 --- a/puppet-manifests/src/modules/platform/templates/ovs.add-port.erb +++ b/puppet-manifests/src/modules/platform/templates/ovs.add-port.erb @@ -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 -%> diff --git a/puppet-modules-wrs/puppet-sysinv/src/sysinv/manifests/agent.pp b/puppet-modules-wrs/puppet-sysinv/src/sysinv/manifests/agent.pp index 741e44e59e..90ce97cb61 100644 --- a/puppet-modules-wrs/puppet-sysinv/src/sysinv/manifests/agent.pp +++ b/puppet-modules-wrs/puppet-sysinv/src/sysinv/manifests/agent.pp @@ -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<||> diff --git a/sysinv/sysinv/sysinv/sysinv/common/constants.py b/sysinv/sysinv/sysinv/sysinv/common/constants.py index f859abd4db..911178fae0 100644 --- a/sysinv/sysinv/sysinv/sysinv/common/constants.py +++ b/sysinv/sysinv/sysinv/sysinv/common/constants.py @@ -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' diff --git a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py index 65b72d6cd7..d0d7b63c6d 100644 --- a/sysinv/sysinv/sysinv/sysinv/conductor/manager.py +++ b/sysinv/sysinv/sysinv/sysinv/conductor/manager.py @@ -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) diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/ovs.py b/sysinv/sysinv/sysinv/sysinv/puppet/ovs.py index ff6c49f153..f94166d578 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/ovs.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/ovs.py @@ -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 + } diff --git a/sysinv/sysinv/sysinv/sysinv/puppet/platform.py b/sysinv/sysinv/sysinv/sysinv/puppet/platform.py index 9de4828053..e3d53eb0c4 100644 --- a/sysinv/sysinv/sysinv/sysinv/puppet/platform.py +++ b/sysinv/sysinv/sysinv/sysinv/puppet/platform.py @@ -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 + }