From 06f1b4c294acc626b402203a658d2972b06e0bcf Mon Sep 17 00:00:00 2001 From: Himanshu Singh Date: Wed, 10 Feb 2021 04:10:26 -0500 Subject: [PATCH] Add Redfish properties when enrolling with 'idrac' This change adds Redfish properties, along with Web Services Management (WS-Man) properties, when enrolling an ironic node with the 'idrac' hardware type. Closes-Bug: #1914995 Change-Id: I154901314ba4da1b502299a98b29c7c2bd39c9f4 --- tripleo_common/tests/utils/test_nodes.py | 90 +++++++++++++++++++++++- tripleo_common/utils/nodes.py | 51 +++++++++++++- 2 files changed, 138 insertions(+), 3 deletions(-) diff --git a/tripleo_common/tests/utils/test_nodes.py b/tripleo_common/tests/utils/test_nodes.py index 1a6eb0c67..93a01df03 100644 --- a/tripleo_common/tests/utils/test_nodes.py +++ b/tripleo_common/tests/utils/test_nodes.py @@ -1,4 +1,5 @@ # Copyright (c) 2014 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2021 Dell Inc. or its subsidiaries. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -227,6 +228,88 @@ class iBootDriverInfoTest(base.TestCase): self.driver_info.unique_id_from_node(node)) +class iDRACDriverInfoTest(base.TestCase): + def setUp(self): + super(iDRACDriverInfoTest, self).setUp() + self.driver_info = nodes.iDRACDriverInfo() + + def test_convert_key(self): + keys = {'pm_addr': 'drac_address', + 'pm_user': 'drac_username', + 'pm_password': 'drac_password', + 'pm_port': 'drac_port', + 'pm_system_id': 'redfish_system_id', + 'redfish_verify_ca': 'redfish_verify_ca' + } + for key, expected in keys.items(): + self.assertEqual(expected, self.driver_info.convert_key(key)) + + self.assertIsNone(self.driver_info.convert_key('unknown')) + + def test_convert(self): + for address in ['foo.bar', + 'http://foo.bar/', + 'https://foo.bar/', + 'https://foo.bar:8080/']: + fields = {'pm_addr': address, + 'pm_user': 'test', + 'pm_password': 'random', + 'redfish_system_id': '/redfish/v1/Systems/1', + 'pm_port': 6230} + result = self.driver_info.convert(fields) + self.assertEqual({'drac_password': 'random', + 'drac_address': 'foo.bar', + 'drac_username': 'test', + 'redfish_password': 'random', + 'redfish_address': address, + 'redfish_username': 'test', + 'redfish_system_id': '/redfish/v1/Systems/1', + 'drac_port': 6230}, result) + + def test_unique_id_from_fields(self): + mock_drac = mock.Mock( + wraps=self.driver_info._drac_driverinfo.unique_id_from_fields) + self.driver_info._drac_driverinfo.unique_id_from_fields = mock_drac + mock_redfish = mock.Mock( + wraps=self.driver_info._redfish_driverinfo.unique_id_from_fields) + self.driver_info._redfish_driverinfo.unique_id_from_fields = ( + mock_redfish) + + fields = {'pm_addr': 'foo.bar', + 'pm_user': 'test', + 'pm_password': 'random', + 'pm_port': 6230} + self.assertEqual('foo.bar:6230', + self.driver_info.unique_id_from_fields(fields)) + + mock_drac.assert_called_once_with(fields) + mock_redfish.assert_not_called() + + def test_unique_id_from_fields_with_https(self): + fields = {'pm_addr': 'https://foo.bar:8080/', + 'pm_user': 'test', + 'pm_password': 'random', + 'pm_port': 6230} + self.assertEqual('foo.bar:6230', + self.driver_info.unique_id_from_fields(fields)) + + def test_unique_id_from_node(self): + mock_drac = mock.Mock( + wraps=self.driver_info._drac_driverinfo.unique_id_from_node) + self.driver_info._drac_driverinfo.unique_id_from_node = mock_drac + mock_redfish = mock.Mock( + wraps=self.driver_info._redfish_driverinfo.unique_id_from_node) + self.driver_info._redfish_driverinfo.unique_id_from_node = mock_redfish + + node = mock.Mock(driver_info={'drac_address': 'foo.bar', + 'drac_port': 6230}) + self.assertEqual('foo.bar:6230', + self.driver_info.unique_id_from_node(node)) + + mock_drac.assert_called_once_with(node) + mock_redfish.assert_not_called() + + class FindNodeHandlerTest(base.TestCase): def test_found(self): test = [('fake', 'fake'), @@ -876,6 +959,7 @@ class NodesTest(base.TestCase): "capabilities": "num_nics:6"} node = self._get_node() node['pm_type'] = 'idrac' + node['pm_system_id'] = '/redfish/v1/Systems/1' node['pm_port'] = '6230' client = mock.MagicMock() nodes.register_ironic_node(node, client=client) @@ -883,7 +967,11 @@ class NodesTest(base.TestCase): driver='idrac', name='node1', properties=node_properties, resource_class='baremetal', driver_info={'drac_password': 'random', 'drac_address': 'foo.bar', - 'drac_username': 'test', 'drac_port': '6230'}) + 'drac_username': 'test', 'redfish_password': 'random', + 'redfish_address': 'foo.bar', + 'redfish_username': 'test', + 'redfish_system_id': '/redfish/v1/Systems/1', + 'drac_port': '6230'}) def test_register_ironic_node_ilo(self): node_properties = {"cpus": "1", diff --git a/tripleo_common/utils/nodes.py b/tripleo_common/utils/nodes.py index bf5d559c0..bf0af6eaa 100644 --- a/tripleo_common/utils/nodes.py +++ b/tripleo_common/utils/nodes.py @@ -1,4 +1,5 @@ # Copyright (c) 2014 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2021 Dell Inc. or its subsidiaries. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -264,6 +265,51 @@ class iBootDriverInfo(PrefixedDriverInfo): return result +class iDRACDriverInfo(DriverInfo): + def __init__(self): + super(iDRACDriverInfo, self).__init__( + 'drac', mapping={}, + hardware_type='idrac', + ) + self._drac_driverinfo = PrefixedDriverInfo('drac', has_port=True, + hardware_type='idrac') + self._redfish_driverinfo = RedfishDriverInfo() + + def convert_key(self, key): + for driver_info in [self._drac_driverinfo, self._redfish_driverinfo]: + new_key = driver_info.convert_key(key) + if new_key: + return new_key + + def convert(self, fields): + """Convert fields from instackenv.json format to ironic names.""" + result = self.DEFAULTS.copy() + for key, value in fields.items(): + for driver_info in [self._drac_driverinfo, + self._redfish_driverinfo]: + new_key = driver_info.convert_key(key) + if new_key is not None: + if (key == 'pm_addr' and + driver_info is self._drac_driverinfo): + new_value = self._build_drac_address(value) + else: + new_value = value + result[new_key] = new_value + return result + + def _build_drac_address(self, value): + value = re.sub(r'https?://', '', value, count=1, flags=re.I) + result = value.split(':')[0] + return result.rstrip('/') + + def unique_id_from_fields(self, fields): + fields['pm_addr'] = self._build_drac_address(fields['pm_addr']) + return self._drac_driverinfo.unique_id_from_fields(fields) + + def unique_id_from_node(self, node): + return self._drac_driverinfo.unique_id_from_node(node) + + DRIVER_INFO = { # production drivers r'^(ipmi|.*_ipmitool)$': PrefixedDriverInfo('ipmi', has_port=True, @@ -271,8 +317,9 @@ DRIVER_INFO = { hardware_type='ipmi', mandatory_fields=['pm_addr'] ), - r'^(idrac|.*_drac)$': PrefixedDriverInfo('drac', has_port=True, - hardware_type='idrac'), + r'^.*_drac$': PrefixedDriverInfo('drac', has_port=True, + hardware_type='idrac'), + r'^idrac$': iDRACDriverInfo(), r'^(ilo|.*_ilo)$': PrefixedDriverInfo('ilo', has_port=True, hardware_type='ilo'), r'^(irmc|.*_irmc)$': PrefixedDriverInfo('irmc', has_port=True,