[LLDP] Skip NICs that say they are ready but are unreadable.
While listening for LLDP packets, if one of the sockets marks itself as ready to read then our code will try to read data from that socket, but if something goes wrong while reading that data then it causes IPA to raise out of the loop skipping any other of the other NICs which might have worked. This patch adds code to catch and LOG any exception that is raised while we are trying to read data from one of the sockets so that we can proceed to process all the NICs. Change-Id: I8546097f5ae23755a5fdb448902007a2d823b7bf Closes-Bug: #1665025
This commit is contained in:
parent
831576c906
commit
b536fbba61
@ -182,10 +182,17 @@ def _get_lldp_info(interfaces):
|
||||
# Create a copy of interfaces to avoid deleting while iterating.
|
||||
for index, interface in enumerate(list(interfaces)):
|
||||
if s == interface[1]:
|
||||
LOG.info('Found LLDP info for interface: %s',
|
||||
interface[0])
|
||||
lldp_info[interface[0]] = (
|
||||
_receive_lldp_packets(s))
|
||||
try:
|
||||
lldp_info[interface[0]] = _receive_lldp_packets(s)
|
||||
except socket.error:
|
||||
LOG.exception('Socket for network interface %s said '
|
||||
'that it was ready to read we were '
|
||||
'unable to read from the socket while '
|
||||
'trying to get LLDP packet. Skipping '
|
||||
'this network interface.', interface[0])
|
||||
else:
|
||||
LOG.info('Found LLDP info for interface: %s',
|
||||
interface[0])
|
||||
# Remove interface from the list, only need one packet
|
||||
del interfaces[index]
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import binascii
|
||||
import socket
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
@ -89,6 +90,50 @@ class TestNetutils(base.IronicAgentTest):
|
||||
# 2 interfaces, 2 calls to enter promiscuous mode, 1 to leave
|
||||
self.assertEqual(6, fcntl_mock.call_count)
|
||||
|
||||
@mock.patch('fcntl.ioctl', autospec=True)
|
||||
@mock.patch('select.select', autospec=True)
|
||||
@mock.patch('socket.socket', autospec=socket_socket_sig)
|
||||
def test_get_lldp_info_socket_recv_error(self, sock_mock, select_mock,
|
||||
fcntl_mock):
|
||||
expected_lldp = {
|
||||
'eth1': [
|
||||
(0, b''),
|
||||
(1, b'\x04\x88Z\x92\xecTY'),
|
||||
(2, b'\x05Ethernet1/18'),
|
||||
(3, b'\x00x')]
|
||||
}
|
||||
|
||||
interface_names = ['eth0', 'eth1']
|
||||
|
||||
sock1 = mock.Mock()
|
||||
sock1.recv.side_effect = socket.error("BOOM")
|
||||
|
||||
sock2 = mock.Mock()
|
||||
sock2.recv.return_value = FAKE_LLDP_PACKET
|
||||
sock2.fileno.return_value = 5
|
||||
|
||||
sock_mock.side_effect = [sock1, sock2]
|
||||
|
||||
select_mock.side_effect = [
|
||||
([sock1], [], []),
|
||||
([sock2], [], [])
|
||||
]
|
||||
|
||||
lldp_info = netutils.get_lldp_info(interface_names)
|
||||
self.assertEqual(expected_lldp, lldp_info)
|
||||
|
||||
sock1.bind.assert_called_with(('eth0', netutils.LLDP_ETHERTYPE))
|
||||
sock2.bind.assert_called_with(('eth1', netutils.LLDP_ETHERTYPE))
|
||||
|
||||
sock1.recv.assert_called_with(1600)
|
||||
sock2.recv.assert_called_with(1600)
|
||||
|
||||
self.assertEqual(1, sock1.close.call_count)
|
||||
self.assertEqual(1, sock2.close.call_count)
|
||||
|
||||
# 2 interfaces, 2 calls to enter promiscuous mode, 1 to leave
|
||||
self.assertEqual(6, fcntl_mock.call_count)
|
||||
|
||||
@mock.patch('fcntl.ioctl', autospec=True)
|
||||
@mock.patch('select.select', autospec=True)
|
||||
@mock.patch('socket.socket', autospec=socket_socket_sig)
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes bug in LLDP discovery code which lead to no LLDP information being
|
||||
discovered for any network interface if one network interface raised an
|
||||
exception, for example if the network interface incorrectly indicates its
|
||||
ready to read. `<https://bugs.launchpad.net/ironic-python-agent/+bug/1665025>`_
|
Loading…
x
Reference in New Issue
Block a user