Use getaddrinfo instead of gethostbyname while resolving BMC address
* According to python docs 'gethostbyname() does not support IPv6 name resolution'
Changing it to socket.getaddrinfo.
* get_ipmi_address method returns tuple (<BMC Address>, ipv4, ipv6)
Change-Id: I7d57556ff00cf29aee6f1591dcb924086099c7d5
Story: #2004648
Task: #28601
(cherry picked from commit 771f2d396b
)
This commit is contained in:
parent
216e40f903
commit
40abe1b58c
|
@ -58,18 +58,43 @@ def reset_ironic_session():
|
||||||
|
|
||||||
|
|
||||||
def get_ipmi_address(node):
|
def get_ipmi_address(node):
|
||||||
|
"""Get the BMC address defined in node.driver_info dictionary
|
||||||
|
|
||||||
|
Possible names of BMC address value examined in order of list
|
||||||
|
['ipmi_address'] + CONF.ipmi_address_fields. The value could
|
||||||
|
be an IP address or a hostname. DNS lookup performed for the
|
||||||
|
first non empty value.
|
||||||
|
|
||||||
|
The first valid BMC address value returned along with
|
||||||
|
it's v4 and v6 IP addresses.
|
||||||
|
|
||||||
|
:param node: Node object with defined driver_info dictionary
|
||||||
|
:return: tuple (ipmi_address, ipv4_address, ipv6_address)
|
||||||
|
"""
|
||||||
|
none_address = None, None, None
|
||||||
ipmi_fields = ['ipmi_address'] + CONF.ipmi_address_fields
|
ipmi_fields = ['ipmi_address'] + CONF.ipmi_address_fields
|
||||||
# NOTE(sambetts): IPMI Address is useless to us if bridging is enabled so
|
# NOTE(sambetts): IPMI Address is useless to us if bridging is enabled so
|
||||||
# just ignore it and return None
|
# just ignore it and return None
|
||||||
if node.driver_info.get("ipmi_bridging", "no") != "no":
|
if node.driver_info.get("ipmi_bridging", "no") != "no":
|
||||||
return
|
return none_address
|
||||||
for name in ipmi_fields:
|
for name in ipmi_fields:
|
||||||
value = node.driver_info.get(name)
|
value = node.driver_info.get(name)
|
||||||
if not value:
|
if not value:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
ipv4 = None
|
||||||
|
ipv6 = None
|
||||||
try:
|
try:
|
||||||
ip = socket.gethostbyname(value)
|
addrinfo = socket.getaddrinfo(value, None, 0, 0, socket.SOL_TCP)
|
||||||
|
for family, socket_type, proto, canon_name, sockaddr in addrinfo:
|
||||||
|
ip = sockaddr[0]
|
||||||
|
if netaddr.IPAddress(ip).is_loopback():
|
||||||
|
LOG.warning('Ignoring loopback BMC address %s', ip,
|
||||||
|
node_info=node)
|
||||||
|
elif family == socket.AF_INET:
|
||||||
|
ipv4 = ip
|
||||||
|
elif family == socket.AF_INET6:
|
||||||
|
ipv6 = ip
|
||||||
except socket.gaierror:
|
except socket.gaierror:
|
||||||
msg = _('Failed to resolve the hostname (%(value)s)'
|
msg = _('Failed to resolve the hostname (%(value)s)'
|
||||||
' for node %(uuid)s')
|
' for node %(uuid)s')
|
||||||
|
@ -77,12 +102,8 @@ def get_ipmi_address(node):
|
||||||
'uuid': node.uuid},
|
'uuid': node.uuid},
|
||||||
node_info=node)
|
node_info=node)
|
||||||
|
|
||||||
if netaddr.IPAddress(ip).is_loopback():
|
return (value, ipv4, ipv6) if ipv4 or ipv6 else none_address
|
||||||
LOG.warning('Ignoring loopback BMC address %s', ip,
|
return none_address
|
||||||
node_info=node)
|
|
||||||
ip = None
|
|
||||||
|
|
||||||
return ip
|
|
||||||
|
|
||||||
|
|
||||||
def get_client(token=None,
|
def get_client(token=None,
|
||||||
|
|
|
@ -53,7 +53,7 @@ def introspect(node_id, manage_boot=True, token=None):
|
||||||
raise utils.Error(msg % validation.power['reason'],
|
raise utils.Error(msg % validation.power['reason'],
|
||||||
node_info=node)
|
node_info=node)
|
||||||
|
|
||||||
bmc_address = ir_utils.get_ipmi_address(node)
|
bmc_address, bmc_ipv4, bmc_ipv6 = ir_utils.get_ipmi_address(node)
|
||||||
node_info = node_cache.start_introspection(node.uuid,
|
node_info = node_cache.start_introspection(node.uuid,
|
||||||
bmc_address=bmc_address,
|
bmc_address=bmc_address,
|
||||||
manage_boot=manage_boot,
|
manage_boot=manage_boot,
|
||||||
|
|
|
@ -60,7 +60,8 @@ def _check_existing_nodes(introspection_data, node_driver_info, ironic):
|
||||||
# impact on performance on big clusters
|
# impact on performance on big clusters
|
||||||
nodes = ironic.node.list(fields=('uuid', 'driver_info'), limit=0)
|
nodes = ironic.node.list(fields=('uuid', 'driver_info'), limit=0)
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
if ipmi_address == ir_utils.get_ipmi_address(node):
|
bmc_address, bmc_ipv4, bmc_ipv6 = ir_utils.get_ipmi_address(node)
|
||||||
|
if ipmi_address in (bmc_ipv4, bmc_ipv6):
|
||||||
raise utils.Error(
|
raise utils.Error(
|
||||||
_('Node %(uuid)s already has BMC address '
|
_('Node %(uuid)s already has BMC address '
|
||||||
'%(ipmi_address)s, not enrolling') %
|
'%(ipmi_address)s, not enrolling') %
|
||||||
|
|
|
@ -87,22 +87,37 @@ class TestGetClientNoAuth(TestGetClientBase, base.BaseTest):
|
||||||
|
|
||||||
|
|
||||||
class TestGetIpmiAddress(base.BaseTest):
|
class TestGetIpmiAddress(base.BaseTest):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestGetIpmiAddress, self).setUp()
|
||||||
|
self.ipmi_address = 'www.example.com'
|
||||||
|
self.ipmi_ipv4 = '192.168.1.1'
|
||||||
|
self.ipmi_ipv6 = 'fe80::1'
|
||||||
|
|
||||||
def test_ipv4_in_resolves(self):
|
def test_ipv4_in_resolves(self):
|
||||||
node = mock.Mock(spec=['driver_info', 'uuid'],
|
node = mock.Mock(spec=['driver_info', 'uuid'],
|
||||||
driver_info={'ipmi_address': '192.168.1.1'})
|
driver_info={'ipmi_address': self.ipmi_ipv4})
|
||||||
ip = ir_utils.get_ipmi_address(node)
|
self.assertEqual((self.ipmi_ipv4, self.ipmi_ipv4, None),
|
||||||
self.assertEqual('192.168.1.1', ip)
|
ir_utils.get_ipmi_address(node))
|
||||||
|
|
||||||
@mock.patch('socket.gethostbyname')
|
def test_ipv6_in_resolves(self):
|
||||||
|
node = mock.Mock(spec=['driver_info', 'uuid'],
|
||||||
|
driver_info={'ipmi_address': self.ipmi_ipv6})
|
||||||
|
self.assertEqual((self.ipmi_ipv6, None, self.ipmi_ipv6),
|
||||||
|
ir_utils.get_ipmi_address(node))
|
||||||
|
|
||||||
|
@mock.patch('socket.getaddrinfo')
|
||||||
def test_good_hostname_resolves(self, mock_socket):
|
def test_good_hostname_resolves(self, mock_socket):
|
||||||
node = mock.Mock(spec=['driver_info', 'uuid'],
|
node = mock.Mock(spec=['driver_info', 'uuid'],
|
||||||
driver_info={'ipmi_address': 'www.example.com'})
|
driver_info={'ipmi_address': self.ipmi_address})
|
||||||
mock_socket.return_value = '192.168.1.1'
|
mock_socket.return_value = [
|
||||||
ip = ir_utils.get_ipmi_address(node)
|
(socket.AF_INET, None, None, None, (self.ipmi_ipv4,)),
|
||||||
mock_socket.assert_called_once_with('www.example.com')
|
(socket.AF_INET6, None, None, None, (self.ipmi_ipv6,))]
|
||||||
self.assertEqual('192.168.1.1', ip)
|
self.assertEqual((self.ipmi_address, self.ipmi_ipv4, self.ipmi_ipv6),
|
||||||
|
ir_utils.get_ipmi_address(node))
|
||||||
|
mock_socket.assert_called_once_with(self.ipmi_address, None, 0, 0,
|
||||||
|
socket.SOL_TCP)
|
||||||
|
|
||||||
@mock.patch('socket.gethostbyname')
|
@mock.patch('socket.getaddrinfo')
|
||||||
def test_bad_hostname_errors(self, mock_socket):
|
def test_bad_hostname_errors(self, mock_socket):
|
||||||
node = mock.Mock(spec=['driver_info', 'uuid'],
|
node = mock.Mock(spec=['driver_info', 'uuid'],
|
||||||
driver_info={'ipmi_address': 'meow'},
|
driver_info={'ipmi_address': 'meow'},
|
||||||
|
@ -112,24 +127,26 @@ class TestGetIpmiAddress(base.BaseTest):
|
||||||
|
|
||||||
def test_additional_fields(self):
|
def test_additional_fields(self):
|
||||||
node = mock.Mock(spec=['driver_info', 'uuid'],
|
node = mock.Mock(spec=['driver_info', 'uuid'],
|
||||||
driver_info={'foo': '192.168.1.1'})
|
driver_info={'foo': self.ipmi_ipv4})
|
||||||
self.assertIsNone(ir_utils.get_ipmi_address(node))
|
self.assertEqual((None, None, None),
|
||||||
|
ir_utils.get_ipmi_address(node))
|
||||||
|
|
||||||
self.cfg.config(ipmi_address_fields=['foo', 'bar', 'baz'])
|
self.cfg.config(ipmi_address_fields=['foo', 'bar', 'baz'])
|
||||||
ip = ir_utils.get_ipmi_address(node)
|
self.assertEqual((self.ipmi_ipv4, self.ipmi_ipv4, None),
|
||||||
self.assertEqual('192.168.1.1', ip)
|
ir_utils.get_ipmi_address(node))
|
||||||
|
|
||||||
def test_ipmi_bridging_enabled(self):
|
def test_ipmi_bridging_enabled(self):
|
||||||
node = mock.Mock(spec=['driver_info', 'uuid'],
|
node = mock.Mock(spec=['driver_info', 'uuid'],
|
||||||
driver_info={'ipmi_address': 'www.example.com',
|
driver_info={'ipmi_address': 'www.example.com',
|
||||||
'ipmi_bridging': 'single'})
|
'ipmi_bridging': 'single'})
|
||||||
self.assertIsNone(ir_utils.get_ipmi_address(node))
|
self.assertEqual((None, None, None),
|
||||||
|
ir_utils.get_ipmi_address(node))
|
||||||
|
|
||||||
def test_loopback_address(self):
|
def test_loopback_address(self):
|
||||||
node = mock.Mock(spec=['driver_info', 'uuid'],
|
node = mock.Mock(spec=['driver_info', 'uuid'],
|
||||||
driver_info={'ipmi_address': '127.0.0.2'})
|
driver_info={'ipmi_address': '127.0.0.2'})
|
||||||
ip = ir_utils.get_ipmi_address(node)
|
self.assertEqual((None, None, None),
|
||||||
self.assertIsNone(ip)
|
ir_utils.get_ipmi_address(node))
|
||||||
|
|
||||||
|
|
||||||
class TestCapabilities(unittest.TestCase):
|
class TestCapabilities(unittest.TestCase):
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
Fix starting inspection of node having IPv6 BMC address.
|
||||||
|
Inspection could not be initiated because v6 address
|
||||||
|
was being considered as a hostname. Thus resolving incorrect
|
||||||
|
hostname ended up with blocking error.
|
Loading…
Reference in New Issue