diff --git a/ironic_python_agent/netutils.py b/ironic_python_agent/netutils.py index 45b85a5d7..5d38548d5 100644 --- a/ironic_python_agent/netutils.py +++ b/ironic_python_agent/netutils.py @@ -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] diff --git a/ironic_python_agent/tests/unit/test_netutils.py b/ironic_python_agent/tests/unit/test_netutils.py index 4237b155b..b9b2a11e4 100644 --- a/ironic_python_agent/tests/unit/test_netutils.py +++ b/ironic_python_agent/tests/unit/test_netutils.py @@ -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) diff --git a/releasenotes/notes/LLDP-ignore-NICs-that-are-not-plugged-in-29213f0a701a72e4.yaml b/releasenotes/notes/LLDP-ignore-NICs-that-are-not-plugged-in-29213f0a701a72e4.yaml new file mode 100644 index 000000000..1f54af9b5 --- /dev/null +++ b/releasenotes/notes/LLDP-ignore-NICs-that-are-not-plugged-in-29213f0a701a72e4.yaml @@ -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. ``_