Allow setting *_interface fields via instackenv.json
This is required to fully use the power of the new-style drivers, but was missing when support for hardware types was originally introduced. Change-Id: I6ad2b669023cf7a08acb69fce7e1058bb7e699d6 Closes-Bug: #1706411
This commit is contained in:
parent
8c6f5ffe36
commit
8dffdf53f3
11
releasenotes/notes/interfaces-cd94c12dd4744c50.yaml
Normal file
11
releasenotes/notes/interfaces-cd94c12dd4744c50.yaml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
It is now possible to set various interface fields when enrolling nodes
|
||||||
|
via ``instackenv.json``. This only works for new-style drivers like
|
||||||
|
``ipmi`` or ``redfish``.
|
||||||
|
ironicclient 1.15 is required for setting the ``storage_interface`` field.
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The minimum required Bare Metal (Ironic) API version was bumped to 1.33
|
||||||
|
(late Pike).
|
@ -59,7 +59,7 @@ class TripleOAction(actions.Action):
|
|||||||
ironic_endpoint.url,
|
ironic_endpoint.url,
|
||||||
token=context.auth_token,
|
token=context.auth_token,
|
||||||
region_name=ironic_endpoint.region,
|
region_name=ironic_endpoint.region,
|
||||||
os_ironic_api_version='1.15',
|
os_ironic_api_version='1.33',
|
||||||
# FIXME(lucasagomes):Paramtetize max_retries and
|
# FIXME(lucasagomes):Paramtetize max_retries and
|
||||||
# max_interval. At the moment since we are dealing with
|
# max_interval. At the moment since we are dealing with
|
||||||
# a critical bug (#1612622) let's just hardcode the times
|
# a critical bug (#1612622) let's just hardcode the times
|
||||||
|
@ -39,7 +39,7 @@ class TestActionsBase(tests_base.TestCase):
|
|||||||
url='http://ironic/v1', region='ironic-region')
|
url='http://ironic/v1', region='ironic-region')
|
||||||
self.action.get_baremetal_client(mock_cxt)
|
self.action.get_baremetal_client(mock_cxt)
|
||||||
mock_client.assert_called_once_with(
|
mock_client.assert_called_once_with(
|
||||||
'http://ironic/v1', max_retries=12, os_ironic_api_version='1.15',
|
'http://ironic/v1', max_retries=12, os_ironic_api_version='1.33',
|
||||||
region_name='ironic-region', retry_interval=5, token=mock.ANY)
|
region_name='ironic-region', retry_interval=5, token=mock.ANY)
|
||||||
mock_endpoint.assert_called_once_with(mock_cxt, 'ironic')
|
mock_endpoint.assert_called_once_with(mock_cxt, 'ironic')
|
||||||
mock_cxt.assert_not_called()
|
mock_cxt.assert_not_called()
|
||||||
|
@ -352,8 +352,54 @@ class NodesTest(base.TestCase):
|
|||||||
ironic.node.create.assert_has_calls([pxe_node, mock.ANY])
|
ironic.node.create.assert_has_calls([pxe_node, mock.ANY])
|
||||||
ironic.port.create.assert_has_calls([port_call])
|
ironic.port.create.assert_has_calls([port_call])
|
||||||
|
|
||||||
|
def test_register_all_nodes_with_interfaces(self):
|
||||||
|
interfaces = {'boot_interface': 'pxe',
|
||||||
|
'console_interface': 'ipmitool-socat',
|
||||||
|
'deploy_interface': 'direct',
|
||||||
|
'inspect_interface': 'inspector',
|
||||||
|
'management_interface': 'ipmitool',
|
||||||
|
'network_interface': 'neutron',
|
||||||
|
'power_interface': 'ipmitool',
|
||||||
|
'raid_interface': 'agent',
|
||||||
|
'storage_interface': 'cinder',
|
||||||
|
'vendor_interface': 'ipmitool'}
|
||||||
|
|
||||||
|
node_list = [self._get_node()]
|
||||||
|
node_list[0].update(interfaces)
|
||||||
|
node_properties = {"cpus": "1",
|
||||||
|
"memory_mb": "2048",
|
||||||
|
"local_gb": "30",
|
||||||
|
"cpu_arch": "amd64",
|
||||||
|
"capabilities": "num_nics:6"}
|
||||||
|
ironic = mock.MagicMock()
|
||||||
|
nodes.register_all_nodes(node_list, client=ironic)
|
||||||
|
pxe_node_driver_info = {"ipmi_address": "foo.bar",
|
||||||
|
"ipmi_username": "test",
|
||||||
|
"ipmi_password": "random"}
|
||||||
|
pxe_node = mock.call(driver="ipmi",
|
||||||
|
name='node1',
|
||||||
|
driver_info=pxe_node_driver_info,
|
||||||
|
properties=node_properties,
|
||||||
|
**interfaces)
|
||||||
|
port_call = mock.call(node_uuid=ironic.node.create.return_value.uuid,
|
||||||
|
address='aaa')
|
||||||
|
ironic.node.create.assert_has_calls([pxe_node, mock.ANY])
|
||||||
|
ironic.port.create.assert_has_calls([port_call])
|
||||||
|
|
||||||
def test_register_update(self):
|
def test_register_update(self):
|
||||||
|
interfaces = {'boot_interface': 'pxe',
|
||||||
|
'console_interface': 'ipmitool-socat',
|
||||||
|
'deploy_interface': 'direct',
|
||||||
|
'inspect_interface': 'inspector',
|
||||||
|
'management_interface': 'ipmitool',
|
||||||
|
'network_interface': 'neutron',
|
||||||
|
'power_interface': 'ipmitool',
|
||||||
|
'raid_interface': 'agent',
|
||||||
|
'storage_interface': 'cinder',
|
||||||
|
'vendor_interface': 'ipmitool'}
|
||||||
|
|
||||||
node = self._get_node()
|
node = self._get_node()
|
||||||
|
node.update(interfaces)
|
||||||
ironic = mock.MagicMock()
|
ironic = mock.MagicMock()
|
||||||
node_map = {'mac': {'aaa': 1}}
|
node_map = {'mac': {'aaa': 1}}
|
||||||
|
|
||||||
@ -368,6 +414,8 @@ class NodesTest(base.TestCase):
|
|||||||
{'path': '/properties/cpus', 'value': '1'},
|
{'path': '/properties/cpus', 'value': '1'},
|
||||||
{'path': '/properties/capabilities', 'value': 'num_nics:6'},
|
{'path': '/properties/capabilities', 'value': 'num_nics:6'},
|
||||||
{'path': '/driver_info/ipmi_username', 'value': 'test'}]
|
{'path': '/driver_info/ipmi_username', 'value': 'test'}]
|
||||||
|
for iface, value in interfaces.items():
|
||||||
|
update_patch.append({'path': '/%s' % iface, 'value': value})
|
||||||
for key in update_patch:
|
for key in update_patch:
|
||||||
key['op'] = 'add'
|
key['op'] = 'add'
|
||||||
self.assertThat(update_patch,
|
self.assertThat(update_patch,
|
||||||
@ -410,6 +458,33 @@ class NodesTest(base.TestCase):
|
|||||||
nodes._update_or_register_ironic_node(node, node_map, client=ironic)
|
nodes._update_or_register_ironic_node(node, node_map, client=ironic)
|
||||||
ironic.node.update.assert_called_once_with(1, mock.ANY)
|
ironic.node.update.assert_called_once_with(1, mock.ANY)
|
||||||
|
|
||||||
|
def test_register_update_with_interfaces(self):
|
||||||
|
node = self._get_node()
|
||||||
|
ironic = mock.MagicMock()
|
||||||
|
node_map = {'mac': {'aaa': 1}}
|
||||||
|
|
||||||
|
def side_effect(*args, **kwargs):
|
||||||
|
update_patch = [
|
||||||
|
{'path': '/name', 'value': 'node1'},
|
||||||
|
{'path': '/driver_info/ipmi_password', 'value': 'random'},
|
||||||
|
{'path': '/driver_info/ipmi_address', 'value': 'foo.bar'},
|
||||||
|
{'path': '/properties/memory_mb', 'value': '2048'},
|
||||||
|
{'path': '/properties/local_gb', 'value': '30'},
|
||||||
|
{'path': '/properties/cpu_arch', 'value': 'amd64'},
|
||||||
|
{'path': '/properties/cpus', 'value': '1'},
|
||||||
|
{'path': '/properties/capabilities', 'value': 'num_nics:6'},
|
||||||
|
{'path': '/driver_info/ipmi_username', 'value': 'test'}]
|
||||||
|
for key in update_patch:
|
||||||
|
key['op'] = 'add'
|
||||||
|
self.assertThat(update_patch,
|
||||||
|
matchers.MatchesSetwise(*(map(matchers.Equals,
|
||||||
|
args[1]))))
|
||||||
|
return mock.Mock(uuid='uuid1')
|
||||||
|
|
||||||
|
ironic.node.update.side_effect = side_effect
|
||||||
|
nodes._update_or_register_ironic_node(node, node_map, client=ironic)
|
||||||
|
ironic.node.update.assert_called_once_with(1, mock.ANY)
|
||||||
|
|
||||||
def _update_by_type(self, pm_type):
|
def _update_by_type(self, pm_type):
|
||||||
ironic = mock.MagicMock()
|
ironic = mock.MagicMock()
|
||||||
node_map = {'mac': {}, 'pm_addr': {}}
|
node_map = {'mac': {}, 'pm_addr': {}}
|
||||||
|
@ -25,6 +25,12 @@ from tripleo_common.utils import glance
|
|||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_KNOWN_INTERFACE_FIELDS = [
|
||||||
|
'%s_interface' % field for field in ('boot', 'console', 'deploy',
|
||||||
|
'inspect', 'management', 'network',
|
||||||
|
'power', 'raid', 'storage', 'vendor')
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DriverInfo(object):
|
class DriverInfo(object):
|
||||||
"""Class encapsulating field conversion logic."""
|
"""Class encapsulating field conversion logic."""
|
||||||
@ -282,6 +288,10 @@ def register_ironic_node(node, client):
|
|||||||
if "ramdisk_id" in node:
|
if "ramdisk_id" in node:
|
||||||
driver_info["deploy_ramdisk"] = node["ramdisk_id"]
|
driver_info["deploy_ramdisk"] = node["ramdisk_id"]
|
||||||
|
|
||||||
|
interface_fields = {field: node.pop(field)
|
||||||
|
for field in _KNOWN_INTERFACE_FIELDS
|
||||||
|
if field in node}
|
||||||
|
|
||||||
driver_info.update(handler.convert(node))
|
driver_info.update(handler.convert(node))
|
||||||
|
|
||||||
mapping = {'cpus': 'cpu',
|
mapping = {'cpus': 'cpu',
|
||||||
@ -301,6 +311,7 @@ def register_ironic_node(node, client):
|
|||||||
create_map = {"driver": node["pm_type"],
|
create_map = {"driver": node["pm_type"],
|
||||||
"properties": properties,
|
"properties": properties,
|
||||||
"driver_info": driver_info}
|
"driver_info": driver_info}
|
||||||
|
create_map.update(interface_fields)
|
||||||
|
|
||||||
for field in ('name', 'uuid'):
|
for field in ('name', 'uuid'):
|
||||||
if field in node:
|
if field in node:
|
||||||
@ -375,6 +386,9 @@ _NON_DRIVER_FIELDS = {'cpu': '/properties/cpus',
|
|||||||
'ramdisk_id': '/driver_info/deploy_ramdisk',
|
'ramdisk_id': '/driver_info/deploy_ramdisk',
|
||||||
'capabilities': '/properties/capabilities'}
|
'capabilities': '/properties/capabilities'}
|
||||||
|
|
||||||
|
_NON_DRIVER_FIELDS.update({field: '/%s' % field
|
||||||
|
for field in _KNOWN_INTERFACE_FIELDS})
|
||||||
|
|
||||||
|
|
||||||
def _update_or_register_ironic_node(node, node_map, client):
|
def _update_or_register_ironic_node(node, node_map, client):
|
||||||
handler = _find_node_handler(node)
|
handler = _find_node_handler(node)
|
||||||
|
Loading…
Reference in New Issue
Block a user