Use bmc ipv6 address while processing introspection data

Change-Id: Id6d56860a4e47f28c875ed6032e1f99a997aea9b
Story: #2005779
Task: #33503
This commit is contained in:
Nikolay Fedotov 2019-05-27 11:24:24 +03:00
parent 9819bf3012
commit fcb0ca8a6e
9 changed files with 87 additions and 18 deletions

View File

@ -21,6 +21,16 @@ _OPTS = [
default='fake-hardware', default='fake-hardware',
help=_('The name of the Ironic driver used by the enroll ' help=_('The name of the Ironic driver used by the enroll '
'hook when creating a new node in Ironic.')), 'hook when creating a new node in Ironic.')),
cfg.ListOpt('enabled_bmc_address_version',
default=['4', '6'],
help=_('IP version of BMC address that will be '
'used when enrolling a new node in Ironic. '
'Defaults to "4,6". Could be "4" (use v4 address '
'only), "4,6" (v4 address have higher priority and '
'if both addresses found v6 version is ignored), '
'"6,4" (v6 is desired but fall back to v4 address '
'for BMCs having v4 address, opposite to "4,6"), '
'"6" (use v6 address only and ignore v4 version).')),
] ]

View File

@ -28,9 +28,16 @@ LOG = utils.getProcessingLogger(__name__)
def _extract_node_driver_info(introspection_data): def _extract_node_driver_info(introspection_data):
node_driver_info = {} node_driver_info = {}
ipmi_address = utils.get_ipmi_address_from_data(introspection_data) for ip_version in CONF.discovery.enabled_bmc_address_version:
if ipmi_address: address = None
node_driver_info['ipmi_address'] = ipmi_address if ip_version == '4':
address = utils.get_ipmi_address_from_data(introspection_data)
elif ip_version == '6':
address = utils.get_ipmi_v6address_from_data(introspection_data)
if address:
node_driver_info['ipmi_address'] = address
break
else: else:
LOG.warning('No BMC address provided, discovered node will be ' LOG.warning('No BMC address provided, discovered node will be '
'created without ipmi address') 'created without ipmi address')

View File

@ -243,9 +243,11 @@ class ValidateInterfacesHook(base.ProcessingHook):
"""Validate information about network interfaces.""" """Validate information about network interfaces."""
bmc_address = utils.get_ipmi_address_from_data(introspection_data) bmc_address = utils.get_ipmi_address_from_data(introspection_data)
bmc_v6address = utils.get_ipmi_v6address_from_data(introspection_data)
# Overwrite the old ipmi_address field to avoid inconsistency # Overwrite the old ipmi_address field to avoid inconsistency
introspection_data['ipmi_address'] = bmc_address introspection_data['ipmi_address'] = bmc_address
if not bmc_address: introspection_data['ipmi_v6address'] = bmc_v6address
if not (bmc_address or bmc_v6address):
LOG.debug('No BMC address provided in introspection data, ' LOG.debug('No BMC address provided in introspection data, '
'assuming virtual environment', data=introspection_data) 'assuming virtual environment', data=introspection_data)

View File

@ -79,8 +79,10 @@ def _store_logs(introspection_data, node_info):
def _find_node_info(introspection_data, failures): def _find_node_info(introspection_data, failures):
try: try:
address = utils.get_ipmi_address_from_data(introspection_data)
v6address = utils.get_ipmi_v6address_from_data(introspection_data)
return node_cache.find_node( return node_cache.find_node(
bmc_address=utils.get_ipmi_address_from_data(introspection_data), bmc_address=[address, v6address],
mac=utils.get_valid_macs(introspection_data)) mac=utils.get_valid_macs(introspection_data))
except utils.NotFoundInCacheError as exc: except utils.NotFoundInCacheError as exc:
not_found_hook = plugins_base.node_not_found_hook_manager() not_found_hook = plugins_base.node_not_found_hook_manager()

View File

@ -92,6 +92,7 @@ class InventoryTest(BaseTest):
# Prepare some realistic inventory # Prepare some realistic inventory
# https://github.com/openstack/ironic-inspector/blob/master/HTTP-API.rst # noqa # https://github.com/openstack/ironic-inspector/blob/master/HTTP-API.rst # noqa
self.bmc_address = '1.2.3.4' self.bmc_address = '1.2.3.4'
self.bmc_v6address = '2001:1234:1234:1234:1234:1234:1234:1234/64'
self.macs = ( self.macs = (
['11:22:33:44:55:66', '66:55:44:33:22:11', '7c:fe:90:29:26:52']) ['11:22:33:44:55:66', '66:55:44:33:22:11', '7c:fe:90:29:26:52'])
self.ips = ['1.2.1.2', '1.2.1.1', '1.2.1.3'] self.ips = ['1.2.1.2', '1.2.1.1', '1.2.1.3']
@ -137,7 +138,8 @@ class InventoryTest(BaseTest):
'memory': { 'memory': {
'physical_mb': 12288 'physical_mb': 12288
}, },
'bmc_address': self.bmc_address 'bmc_address': self.bmc_address,
'bmc_v6address': self.bmc_v6address
}, },
'root_disk': {'name': '/dev/sda', 'model': 'Big Data Disk', 'root_disk': {'name': '/dev/sda', 'model': 'Big Data Disk',
'size': 1000 * units.Gi, 'size': 1000 * units.Gi,

View File

@ -60,19 +60,39 @@ class TestEnrollNodeNotFoundHook(test_base.NodeTest):
def test_enroll_with_ipmi_address(self, mock_check_existing, mock_client, def test_enroll_with_ipmi_address(self, mock_check_existing, mock_client,
mock_create_node): mock_create_node):
mock_client.return_value = self.ironic mock_client.return_value = self.ironic
introspection_data = {'ipmi_address': '1.2.3.4'} expected_data = copy.deepcopy(self.data)
expected_data = introspection_data.copy()
mock_check_existing = copy_call_args(mock_check_existing) mock_check_existing = copy_call_args(mock_check_existing)
discovery.enroll_node_not_found_hook(introspection_data) discovery.enroll_node_not_found_hook(self.data)
mock_create_node.assert_called_once_with( mock_create_node.assert_called_once_with(
'fake-hardware', ironic=self.ironic, 'fake-hardware', ironic=self.ironic,
driver_info={'ipmi_address': '1.2.3.4'}) driver_info={'ipmi_address': self.bmc_address})
mock_check_existing.assert_called_once_with( mock_check_existing.assert_called_once_with(
expected_data, {'ipmi_address': '1.2.3.4'}, self.ironic) expected_data, {'ipmi_address': self.bmc_address}, self.ironic)
self.assertEqual({'ipmi_address': '1.2.3.4', 'auto_discovered': True}, self.assertTrue(self.data['auto_discovered'])
introspection_data)
@mock.patch.object(node_cache, 'create_node', autospec=True)
@mock.patch.object(ir_utils, 'get_client', autospec=True)
@mock.patch.object(discovery, '_check_existing_nodes', autospec=True)
def test_enroll_with_ipmi_v6address(self, mock_check_existing, mock_client,
mock_create_node):
mock_client.return_value = self.ironic
# By default enabled_bmc_address_version="4,6".
# Because bmc_address is not set (pop it) _extract_node_driver_info
# method returns bmc_v6address
self.data['inventory'].pop('bmc_address')
expected_data = copy.deepcopy(self.data)
mock_check_existing = copy_call_args(mock_check_existing)
discovery.enroll_node_not_found_hook(self.data)
mock_create_node.assert_called_once_with(
'fake-hardware', ironic=self.ironic,
driver_info={'ipmi_address': self.bmc_v6address})
mock_check_existing.assert_called_once_with(
expected_data, {'ipmi_address': self.bmc_v6address}, self.ironic)
self.assertTrue(self.data['auto_discovered'])
@mock.patch.object(node_cache, 'create_node', autospec=True) @mock.patch.object(node_cache, 'create_node', autospec=True)
@mock.patch.object(ir_utils, 'get_client', autospec=True) @mock.patch.object(ir_utils, 'get_client', autospec=True)

View File

@ -89,7 +89,8 @@ class TestProcess(BaseProcessTest):
self.assertEqual(self.fake_result_json, res) self.assertEqual(self.fake_result_json, res)
self.find_mock.assert_called_once_with(bmc_address=self.bmc_address, self.find_mock.assert_called_once_with(
bmc_address=[self.bmc_address, self.bmc_v6address],
mac=mock.ANY) mac=mock.ANY)
actual_macs = self.find_mock.call_args[1]['mac'] actual_macs = self.find_mock.call_args[1]['mac']
self.assertEqual(sorted(self.all_macs), sorted(actual_macs)) self.assertEqual(sorted(self.all_macs), sorted(actual_macs))
@ -99,9 +100,11 @@ class TestProcess(BaseProcessTest):
def test_no_ipmi(self): def test_no_ipmi(self):
del self.inventory['bmc_address'] del self.inventory['bmc_address']
del self.inventory['bmc_v6address']
process.process(self.data) process.process(self.data)
self.find_mock.assert_called_once_with(bmc_address=None, mac=mock.ANY) self.find_mock.assert_called_once_with(bmc_address=[None, None],
mac=mock.ANY)
actual_macs = self.find_mock.call_args[1]['mac'] actual_macs = self.find_mock.call_args[1]['mac']
self.assertEqual(sorted(self.all_macs), sorted(actual_macs)) self.assertEqual(sorted(self.all_macs), sorted(actual_macs))
self.cli.node.get.assert_called_once_with(self.uuid) self.cli.node.get.assert_called_once_with(self.uuid)
@ -110,9 +113,11 @@ class TestProcess(BaseProcessTest):
def test_ipmi_not_detected(self): def test_ipmi_not_detected(self):
self.inventory['bmc_address'] = '0.0.0.0' self.inventory['bmc_address'] = '0.0.0.0'
self.inventory['bmc_v6address'] = '::/0'
process.process(self.data) process.process(self.data)
self.find_mock.assert_called_once_with(bmc_address=None, mac=mock.ANY) self.find_mock.assert_called_once_with(bmc_address=[None, None],
mac=mock.ANY)
actual_macs = self.find_mock.call_args[1]['mac'] actual_macs = self.find_mock.call_args[1]['mac']
self.assertEqual(sorted(self.all_macs), sorted(actual_macs)) self.assertEqual(sorted(self.all_macs), sorted(actual_macs))
self.cli.node.get.assert_called_once_with(self.uuid) self.cli.node.get.assert_called_once_with(self.uuid)
@ -124,7 +129,9 @@ class TestProcess(BaseProcessTest):
self.data['ipmi_address'] = '0.0.0.0' self.data['ipmi_address'] = '0.0.0.0'
process.process(self.data) process.process(self.data)
self.find_mock.assert_called_once_with(bmc_address=None, mac=mock.ANY) self.find_mock.assert_called_once_with(
bmc_address=[None, self.bmc_v6address],
mac=mock.ANY)
actual_macs = self.find_mock.call_args[1]['mac'] actual_macs = self.find_mock.call_args[1]['mac']
self.assertEqual(sorted(self.all_macs), sorted(actual_macs)) self.assertEqual(sorted(self.all_macs), sorted(actual_macs))
self.cli.node.get.assert_called_once_with(self.uuid) self.cli.node.get.assert_called_once_with(self.uuid)

View File

@ -43,6 +43,19 @@ def get_ipmi_address_from_data(introspection_data):
return result return result
def get_ipmi_v6address_from_data(introspection_data):
try:
result = introspection_data['inventory']['bmc_v6address']
except KeyError:
result = introspection_data.get('ipmi_v6address')
if result in ('', '::/0'):
# ipmitool can return these values, if it does not know the address
return None
else:
return result
def get_pxe_mac(introspection_data): def get_pxe_mac(introspection_data):
pxe_mac = introspection_data.get('boot_interface') pxe_mac = introspection_data.get('boot_interface')
if pxe_mac and '-' in pxe_mac: if pxe_mac and '-' in pxe_mac:

View File

@ -0,0 +1,6 @@
---
features:
- |
Adds support to enroll node with IPv6 BMC address. Introduces
a configuration option ``[discovery]enabled_bmc_address_version``
to specify the order of preferred IP version of the BMC address.