Skip to content

Commit 7c28332

Browse files
committed
BMC can be configured using different lan channel
It may happen that BMC is configured to use non-zero channel. In this case we should iterate across all of them as long as we get a correct IP address (in this case different than "0.0.0.0" which is a placeholder for not configured console). Change-Id: I3c351af1882b24c8f56e4363249b19b5c3a4a446 Closes-Bug: #1702514
1 parent 8110243 commit 7c28332

3 files changed

Lines changed: 62 additions & 4 deletions

File tree

ironic_python_agent/hardware.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -927,21 +927,38 @@ def _ata_erase(self, block_device):
927927
return True
928928

929929
def get_bmc_address(self):
930+
"""Attempt to detect BMC IP address
931+
932+
:return: IP address of lan channel or 0.0.0.0 in case none of them is
933+
configured properly
934+
"""
930935
# These modules are rarely loaded automatically
931936
utils.try_execute('modprobe', 'ipmi_msghandler')
932937
utils.try_execute('modprobe', 'ipmi_devintf')
933938
utils.try_execute('modprobe', 'ipmi_si')
934939

935940
try:
936-
out, _e = utils.execute(
937-
"ipmitool lan print | grep -e 'IP Address [^S]' "
938-
"| awk '{ print $4 }'", shell=True)
941+
# From all the channels 0-15, only 1-7 can be assigned to different
942+
# types of communication media and protocols and effectively used
943+
for channel in range(1, 8):
944+
out, e = utils.execute(
945+
"ipmitool lan print {} | awk '/IP Address[[:space:]]*:/"
946+
" {{print $4}}'".format(channel), shell=True)
947+
# Invalid channel cannot be followed by a valid one, so we can
948+
# safely break here
949+
if e.startswith("Invalid channel"):
950+
break
951+
# In case we get empty IP or 0.0.0.0 on a valid channel,
952+
# we need to keep querying
953+
if out.strip() not in ('', '0.0.0.0'):
954+
return out.strip()
955+
939956
except (processutils.ProcessExecutionError, OSError) as e:
940957
# Not error, because it's normal in virtual environment
941958
LOG.warning("Cannot get BMC address: %s", e)
942959
return
943960

944-
return out.strip()
961+
return '0.0.0.0'
945962

946963
def get_clean_steps(self, node, ports):
947964
return [

ironic_python_agent/tests/unit/test_hardware.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1541,6 +1541,40 @@ def test_get_bmc_address_virt(self, mocked_execute):
15411541
mocked_execute.side_effect = processutils.ProcessExecutionError()
15421542
self.assertIsNone(self.hardware.get_bmc_address())
15431543

1544+
@mock.patch.object(utils, 'execute', autospec=True)
1545+
def test_get_bmc_address_zeroed(self, mocked_execute):
1546+
mocked_execute.return_value = '0.0.0.0\n', ''
1547+
self.assertEqual('0.0.0.0', self.hardware.get_bmc_address())
1548+
1549+
@mock.patch.object(utils, 'execute', autospec=True)
1550+
def test_get_bmc_address_invalid(self, mocked_execute):
1551+
# In case of invalid lan channel, stdout is empty and the error
1552+
# on stderr is "Invalid channel"
1553+
mocked_execute.return_value = '\n', 'Invalid channel: 55'
1554+
self.assertEqual('0.0.0.0', self.hardware.get_bmc_address())
1555+
1556+
@mock.patch.object(utils, 'execute', autospec=True)
1557+
def test_get_bmc_address_random_error(self, mocked_execute):
1558+
mocked_execute.return_value = '192.1.2.3\n', 'Random error message'
1559+
self.assertEqual('192.1.2.3', self.hardware.get_bmc_address())
1560+
1561+
@mock.patch.object(utils, 'execute', autospec=True)
1562+
def test_get_bmc_address_iterate_channels(self, mocked_execute):
1563+
# For channel 1 we simulate unconfigured IP
1564+
# and for any other we return a correct IP address
1565+
def side_effect(*args, **kwargs):
1566+
if args[0].startswith("ipmitool lan print 1"):
1567+
return '0.0.0.0\n', ''
1568+
else:
1569+
return '192.1.2.3\n', ''
1570+
mocked_execute.side_effect = side_effect
1571+
self.assertEqual('192.1.2.3', self.hardware.get_bmc_address())
1572+
1573+
@mock.patch.object(utils, 'execute', autospec=True)
1574+
def test_get_bmc_address_not_available(self, mocked_execute):
1575+
mocked_execute.return_value = '', ''
1576+
self.assertEqual('0.0.0.0', self.hardware.get_bmc_address())
1577+
15441578
@mock.patch.object(utils, 'execute', autospec=True)
15451579
def test_get_system_vendor_info(self, mocked_execute):
15461580
mocked_execute.return_value = (
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
IPMI interface can be configured to use lan channel different than
5+
a default one. In that case querying it will return 0.0.0.0 as an IP
6+
address representing an interface lacking configuration. In order to get
7+
the real IP address, we need to iterate through all the possible channels.

0 commit comments

Comments
 (0)