diff --git a/releasenotes/notes/no-classic-drivers-d56f8c3ff15af2c3.yaml b/releasenotes/notes/no-classic-drivers-d56f8c3ff15af2c3.yaml new file mode 100644 index 000000000..4a7079565 --- /dev/null +++ b/releasenotes/notes/no-classic-drivers-d56f8c3ff15af2c3.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + On enrollment, all classic drivers are replaced with their hardware type + equivalents (e.g. ``pxe_ipmitool`` is replaced with ``ipmi``). + The ``fake_pxe`` classic driver is replaced with the ``manual-management`` + hardware type (which must be enabled in the undercloud). diff --git a/tripleo_common/tests/utils/test_nodes.py b/tripleo_common/tests/utils/test_nodes.py index 17df95d28..835da8665 100644 --- a/tripleo_common/tests/utils/test_nodes.py +++ b/tripleo_common/tests/utils/test_nodes.py @@ -667,12 +667,29 @@ class NodesTest(base.TestCase): node['pm_type'] = 'fake_pxe' client = mock.MagicMock() nodes.register_ironic_node(node, client=client) - client.node.create.assert_called_once_with(driver='fake_pxe', + client.node.create.assert_called_once_with(driver='manual-management', name='node1', properties=node_properties, resource_class='baremetal', driver_info={}) + def test_register_ironic_node_ucs(self): + node_properties = {"cpus": "1", + "memory_mb": "2048", + "local_gb": "30", + "cpu_arch": "amd64", + "capabilities": "num_nics:6"} + node = self._get_node() + node['pm_type'] = 'cisco-ucs-managed' + client = mock.MagicMock() + nodes.register_ironic_node(node, client=client) + client.node.create.assert_called_once_with( + driver='cisco-ucs-managed', name='node1', + properties=node_properties, + resource_class='baremetal', + driver_info={'ucs_password': 'random', 'ucs_address': 'foo.bar', + 'ucs_username': 'test'}) + def test_register_ironic_node_pxe_ucs(self): node_properties = {"cpus": "1", "memory_mb": "2048", @@ -684,7 +701,8 @@ class NodesTest(base.TestCase): client = mock.MagicMock() nodes.register_ironic_node(node, client=client) client.node.create.assert_called_once_with( - driver='pxe_ucs', name='node1', properties=node_properties, + driver='cisco-ucs-managed', name='node1', + properties=node_properties, resource_class='baremetal', driver_info={'ucs_password': 'random', 'ucs_address': 'foo.bar', 'ucs_username': 'test'}) @@ -718,7 +736,7 @@ class NodesTest(base.TestCase): client = mock.MagicMock() nodes.register_ironic_node(node, client=client) client.node.create.assert_called_once_with( - driver='pxe_ipmitool', name='node1', properties=node_properties, + driver='ipmi', name='node1', properties=node_properties, resource_class='baremetal', driver_info={'ipmi_password': 'random', 'ipmi_address': 'foo.bar', 'ipmi_username': 'test', 'ipmi_port': '6230'}) @@ -769,11 +787,28 @@ class NodesTest(base.TestCase): client = mock.MagicMock() nodes.register_ironic_node(node, client=client) client.node.create.assert_called_once_with( - driver='pxe_drac', name='node1', properties=node_properties, + 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'}) + def test_register_ironic_node_pxe_ilo(self): + node_properties = {"cpus": "1", + "memory_mb": "2048", + "local_gb": "30", + "cpu_arch": "amd64", + "capabilities": "num_nics:6"} + node = self._get_node() + node['pm_type'] = 'pxe_ilo' + node['pm_port'] = '1234' + client = mock.MagicMock() + nodes.register_ironic_node(node, client=client) + client.node.create.assert_called_once_with( + driver='ilo', name='node1', properties=node_properties, + resource_class='baremetal', + driver_info={'ilo_password': 'random', 'ilo_address': 'foo.bar', + 'ilo_username': 'test', 'ilo_port': '1234'}) + def test_register_ironic_node_redfish(self): node_properties = {"cpus": "1", "memory_mb": "2048", @@ -833,16 +868,16 @@ class NodesTest(base.TestCase): nodes._clean_up_extra_nodes(seen, client, remove=True) client.node.delete.assert_called_once_with('foobar') - def test__get_node_id_fake_pxe(self): + def test__get_node_id_manual_management(self): node = self._get_node() - node['pm_type'] = 'fake_pxe' - handler = nodes.find_driver_handler('fake_pxe') + node['pm_type'] = 'manual-management' + handler = nodes.find_driver_handler('manual-management') node_map = {'mac': {'aaa': 'abcdef'}, 'pm_addr': {}} self.assertEqual('abcdef', nodes._get_node_id(node, handler, node_map)) def test__get_node_id_conflict(self): node = self._get_node() - handler = nodes.find_driver_handler('pxe_ipmitool') + handler = nodes.find_driver_handler('ipmi') node_map = {'mac': {'aaa': 'abcdef'}, 'pm_addr': {'foo.bar': 'defabc'}} self.assertRaises(exception.InvalidNode, @@ -851,7 +886,7 @@ class NodesTest(base.TestCase): def test_get_node_id_valid_duplicate(self): node = self._get_node() - handler = nodes.find_driver_handler('pxe_ipmitool') + handler = nodes.find_driver_handler('ipmi') node_map = {'mac': {'aaa': 'id'}, 'pm_addr': {'foo.bar': 'id'}} self.assertEqual('id', nodes._get_node_id(node, handler, node_map)) @@ -874,12 +909,12 @@ class TestPopulateNodeMapping(base.TestCase): 'uuids': {'abcdef', 'fedcba', 'xyz'}} self.assertEqual(expected, nodes._populate_node_mapping(client)) - def test_populate_node_mapping_ironic_fake_pxe(self): + def test_populate_node_mapping_ironic_manual_management(self): client = mock.MagicMock() ironic_node = collections.namedtuple('node', ['uuid', 'driver', 'driver_info']) ironic_port = collections.namedtuple('port', ['address']) - node = ironic_node('abcdef', 'fake_pxe', None) + node = ironic_node('abcdef', 'manual-management', None) client.node.list_ports.return_value = [ironic_port('aaa')] client.node.list.return_value = [node] expected = {'mac': {'aaa': 'abcdef'}, 'pm_addr': {}, diff --git a/tripleo_common/utils/nodes.py b/tripleo_common/utils/nodes.py index 18607482d..26198f154 100644 --- a/tripleo_common/utils/nodes.py +++ b/tripleo_common/utils/nodes.py @@ -54,17 +54,22 @@ class DriverInfo(object): DEFAULTS = {} def __init__(self, prefix, mapping, deprecated_mapping=None, - mandatory_fields=(), default_port=None): + mandatory_fields=(), default_port=None, hardware_type=None): self._prefix = prefix self._mapping = mapping self._deprecated_mapping = deprecated_mapping or {} self._mandatory_fields = mandatory_fields self._default_port = default_port + self._hardware_type = hardware_type @property def default_port(self): return self._default_port + @property + def hardware_type(self): + return self._hardware_type + def convert_key(self, key): if key in self._mapping: return self._mapping[key] @@ -116,7 +121,7 @@ class DriverInfo(object): class PrefixedDriverInfo(DriverInfo): def __init__(self, prefix, deprecated_mapping=None, has_port=False, address_field='address', - default_port=None): + default_port=None, hardware_type=None): mapping = { 'pm_addr': '%s_%s' % (prefix, address_field), 'pm_user': '%s_username' % prefix, @@ -133,6 +138,7 @@ class PrefixedDriverInfo(DriverInfo): deprecated_mapping=deprecated_mapping, mandatory_fields=mandatory_fields, default_port=default_port, + hardware_type=hardware_type, ) def unique_id_from_fields(self, fields): @@ -179,6 +185,7 @@ class RedfishDriverInfo(DriverInfo): 'redfish', mapping, deprecated_mapping=None, mandatory_fields=mandatory_fields, + hardware_type='redfish', ) def _build_id(self, address, system): @@ -211,6 +218,7 @@ class oVirtDriverInfo(DriverInfo): super(oVirtDriverInfo, self).__init__( 'ovirt', mapping, mandatory_fields=list(mapping), + hardware_type='staging-ovirt', ) def unique_id_from_fields(self, fields): @@ -227,38 +235,14 @@ class oVirtDriverInfo(DriverInfo): return -class SshDriverInfo(DriverInfo): - DEFAULTS = {'ssh_virt_type': 'virsh'} - - def __init__(self): - super(SshDriverInfo, self).__init__( - 'ssh', - { - 'pm_addr': 'ssh_address', - 'pm_user': 'ssh_username', - # TODO(dtantsur): support ssh_key_filename as well - 'pm_password': 'ssh_key_contents', - }, - deprecated_mapping={ - 'pm_virt_type': 'ssh_virt_type', - }, - mandatory_fields=['pm_addr', 'pm_user', 'pm_password'], - ) - - def validate(self, node): - super(SshDriverInfo, self).validate(node) - if not node.get('ports')[0]['address']: - raise exception.InvalidNode( - 'Nodes with SSH drivers require at least one PORT') - - class iBootDriverInfo(PrefixedDriverInfo): def __init__(self): super(iBootDriverInfo, self).__init__( 'iboot', has_port=True, deprecated_mapping={ 'pm_relay_id': 'iboot_relay_id', - } + }, + hardware_type='staging-iboot', ) def unique_id_from_fields(self, fields): @@ -282,23 +266,35 @@ class iBootDriverInfo(PrefixedDriverInfo): DRIVER_INFO = { # production drivers '(ipmi|.*_ipmitool)': PrefixedDriverInfo('ipmi', has_port=True, - default_port=623), - '(idrac|.*_drac)': PrefixedDriverInfo('drac', has_port=True), - '(ilo|.*_ilo)': PrefixedDriverInfo('ilo', has_port=True), - '(cisco\-ucs\-managed|.*_ucs)': PrefixedDriverInfo('ucs'), - '(irmc|.*_irmc)': PrefixedDriverInfo('irmc', has_port=True), + default_port=623, + hardware_type='ipmi'), + '(idrac|.*_drac)': PrefixedDriverInfo('drac', has_port=True, + hardware_type='idrac'), + '(ilo|.*_ilo)': PrefixedDriverInfo('ilo', has_port=True, + hardware_type='ilo'), + '(cisco\-ucs\-managed|.*_ucs)': PrefixedDriverInfo( + 'ucs', hardware_type='cisco-ucs-managed'), + '(irmc|.*_irmc)': PrefixedDriverInfo('irmc', has_port=True, + hardware_type='irmc'), 'redfish': RedfishDriverInfo(), # test drivers 'staging\-ovirt': oVirtDriverInfo(), - '.*_iboot': iBootDriverInfo(), - '.*_wol': DriverInfo( + '(staging\-iboot|.*_iboot)': iBootDriverInfo(), + '(staging\-wol|.*wol)': DriverInfo( 'wol', mapping={ 'pm_addr': 'wol_host', 'pm_port': 'wol_port', - }), - '.*_amt': PrefixedDriverInfo('amt'), - 'fake(|_pxe|_agent)': DriverInfo('fake', mapping={}), + }, + hardware_type='staging-wol'), + '(staging\-amt|.*_amt)': PrefixedDriverInfo('amt', + hardware_type='staging-amt'), + # fake_pxe was used when no management interface was supported, now + # manual-management is used for the same purpose + '(manual\-management|fake_pxe|fake_agent)': DriverInfo( + 'fake', mapping={}, hardware_type='manual-management'), + '^fake(|\-hardware)$': DriverInfo('fake', mapping={}, + hardware_type='fake-hardware'), } @@ -359,7 +355,14 @@ def register_ironic_node(node, client): caps = dict_to_capabilities(caps) properties.update({"capabilities": six.text_type(caps)}) - create_map = {"driver": node["pm_type"], + driver = node['pm_type'] + if handler.hardware_type and handler.hardware_type != driver: + LOG.warning('Replacing deprecated driver %(old)s with the ' + 'hardware type %(new)s, please update your inventory', + {'old': driver, 'new': handler.hardware_type}) + driver = handler.hardware_type + + create_map = {"driver": driver, "properties": properties, "driver_info": driver_info, "resource_class": resource_class}