Support IPv6 address when validating interfaces

When processing introspection data from IPA, ironic inspector only accept IPv4
address of an interface. In a IPv6 environment, the port will not be collected
if processing.add_ports is not set to 'all'.

Story: #1744073
Task: #11374

Co-Authored-By: Kaifeng Wang <kaifeng.w@gmail.com>
Change-Id: I0e1839df4d0e3b89f182ab98549486199d64c4ad
This commit is contained in:
zhengyong 2018-01-21 23:49:40 +08:00 committed by Kaifeng Wang
parent 7991f12e17
commit a1c44d29e6
3 changed files with 62 additions and 4 deletions

View File

@ -13,7 +13,6 @@
"""Standard set of plugins.""" """Standard set of plugins."""
from ironic_lib import utils as il_utils from ironic_lib import utils as il_utils
import netaddr import netaddr
from oslo_config import cfg from oslo_config import cfg
@ -27,7 +26,6 @@ from ironic_inspector import utils
CONF = cfg.CONF CONF = cfg.CONF
LOG = utils.getProcessingLogger('ironic_inspector.plugins.standard') LOG = utils.getProcessingLogger('ironic_inspector.plugins.standard')
@ -162,7 +160,13 @@ class ValidateInterfacesHook(base.ProcessingHook):
for iface in inventory['interfaces']: for iface in inventory['interfaces']:
name = iface.get('name') name = iface.get('name')
mac = iface.get('mac_address') mac = iface.get('mac_address')
ip = iface.get('ipv4_address') ipv4_address = iface.get('ipv4_address')
ipv6_address = iface.get('ipv6_address')
# NOTE(kaifeng) ipv6 address may in the form of fd00::1%enp2s0,
# which is not supported by netaddr, remove the suffix if exists.
if ipv6_address and '%' in ipv6_address:
ipv6_address = ipv6_address.split('%')[0]
ip = ipv4_address or ipv6_address
client_id = iface.get('client_id') client_id = iface.get('client_id')
if not name: if not name:
@ -221,7 +225,8 @@ class ValidateInterfacesHook(base.ProcessingHook):
LOG.debug('Skipping interface %s as it was not PXE booting', LOG.debug('Skipping interface %s as it was not PXE booting',
name, data=data) name, data=data)
continue continue
elif CONF.processing.add_ports != 'all' and not ip: elif CONF.processing.add_ports != 'all' and (
not ip or netaddr.IPAddress(ip).is_link_local()):
LOG.debug('Skipping interface %s as it did not have ' LOG.debug('Skipping interface %s as it did not have '
'an IP address assigned during the ramdisk run', 'an IP address assigned during the ramdisk run',
name, data=data) name, data=data)

View File

@ -188,6 +188,50 @@ class TestValidateInterfacesHookBeforeProcessing(test_base.NodeTest):
self.assertRaisesRegex(utils.Error, 'No suitable interfaces found', self.assertRaisesRegex(utils.Error, 'No suitable interfaces found',
self.hook.before_processing, self.data) self.hook.before_processing, self.data)
def test_skipped_interfaces_with_local_address(self):
CONF.set_override('add_ports', 'active', 'processing')
self.inventory['interfaces'] = [
# local interface (by IPv4 address)
{'name': 'em1', 'mac_address': '22:22:22:22:22:22',
'ipv4_address': '127.0.0.1'},
# local interface (by IPv6 address)
{'name': 'em2', 'mac_address': '33:33:33:33:33:33',
'ipv6_address': '::1'},
# interface only with local-link address
{'name': 'em3', 'mac_address': '44:44:44:44:44:44',
'ipv6_address': 'fe80::4644:44ff:fe44:4444'},
# interface only with local-link address with suffix
{'name': 'em4', 'mac_address': '55:55:55:55:55:55',
'ipv6_address': 'fe80::5755:55ff:fe55:5555%em4'},
]
self.assertRaisesRegex(utils.Error, 'No suitable interfaces found',
self.hook.before_processing, self.data)
def test_interfaces_with_ipv6_addresses_only(self):
CONF.set_override('add_ports', 'all', 'processing')
self.inventory['interfaces'] = [
# loopback interface (by IPv6 address)
{'name': 'em2', 'mac_address': '33:33:33:33:33:33',
'ipv6_address': '::1'},
# interface with local-link address
{'name': 'em3', 'mac_address': '44:44:44:44:44:44',
'ipv6_address': 'fe80::4644:44ff:fe44:4444'},
# interface with local-link address with suffix
{'name': 'em4', 'mac_address': '55:55:55:55:55:55',
'ipv6_address': 'fe80::5755:55ff:fe55:5555%em4'},
# interface with ULA address
{'name': 'em5', 'mac_address': '66:66:66:66:66:66',
'ipv6_address': 'fd00::1111:2222:6666'},
]
self.hook.before_processing(self.data)
interfaces = self.data['interfaces']
self.assertEqual(interfaces['em3']['mac'], '44:44:44:44:44:44')
self.assertEqual(interfaces['em3']['ip'], 'fe80::4644:44ff:fe44:4444')
self.assertEqual(interfaces['em4']['mac'], '55:55:55:55:55:55')
self.assertEqual(interfaces['em4']['ip'], 'fe80::5755:55ff:fe55:5555')
self.assertEqual(interfaces['em5']['mac'], '66:66:66:66:66:66')
self.assertEqual(interfaces['em5']['ip'], 'fd00::1111:2222:6666')
@mock.patch.object(node_cache.NodeInfo, 'delete_port', autospec=True) @mock.patch.object(node_cache.NodeInfo, 'delete_port', autospec=True)
@mock.patch.object(node_cache.NodeInfo, 'create_ports', autospec=True) @mock.patch.object(node_cache.NodeInfo, 'create_ports', autospec=True)

View File

@ -0,0 +1,9 @@
---
fixes:
- |
Fixes the issue that ports were not collected when there were only IPv6
addresses (no IPv4), and the configuration option
``[processing]add_ports`` was not set to ``all``. Inspector will report
"No suitable interfaces found" if no interface is collected. For more
information see
`Story 1744073 <https://storyboard.openstack.org/#!/story/1744073>`_