Supports specifying profile explicitly in instackenv.json

One day Nova will deprecate capabilities, and we'll have to use
something else for profiles. As a preparational steps, start
accepting profile as a separate instackenv.json field, thus decoupling
it from the underlying implementation.

Partial-Bug: #1793134
Change-Id: Ife97a0955a480bcc41b3453d58ef7fe6488b476c
This commit is contained in:
Dmitry Tantsur 2018-11-08 15:52:19 +01:00
parent 2c225f1f2a
commit e3a410f699
3 changed files with 100 additions and 6 deletions

View File

@ -0,0 +1,9 @@
---
features:
- |
Node's profile can now be specified as a separate ``profile`` field in
the ``instackenv.json`` instead of inside capabilities.
deprecations:
- |
Specifying profile in capabilities when enrolling nodes is deprecated.
Please use the new ``profile`` field instead.

View File

@ -467,6 +467,32 @@ class NodesTest(base.TestCase):
ironic.node.create.assert_has_calls([pxe_node, mock.ANY])
ironic.port.create.assert_has_calls([port_call])
def test_register_all_nodes_with_profile(self):
node_list = [self._get_node()]
node_list[0]['root_device'] = {"serial": "abcdef"}
node_list[0]['profile'] = "compute"
node_properties = {"cpus": "1",
"memory_mb": "2048",
"local_gb": "30",
"cpu_arch": "amd64",
"capabilities": "num_nics:6,profile:compute",
"root_device": {"serial": "abcdef"}}
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,
resource_class='baremetal',
properties=node_properties)
port_call = mock.call(node_uuid=ironic.node.create.return_value.uuid,
address='aaa', physical_network='ctlplane',
local_link_connection=None)
ironic.node.create.assert_has_calls([pxe_node, mock.ANY])
ironic.port.create.assert_has_calls([port_call])
def test_register_all_nodes_with_interfaces(self):
interfaces = {'boot_interface': 'pxe',
'console_interface': 'ipmitool-socat',
@ -596,6 +622,53 @@ class NodesTest(base.TestCase):
nodes._update_or_register_ironic_node(node, node_map, client=ironic)
ironic.node.update.assert_called_once_with(1, mock.ANY)
def test_register_update_profile(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',
'rescue_interface': 'agent',
'storage_interface': 'cinder',
'vendor_interface': 'ipmitool'}
node = self._get_node()
node.update(interfaces)
node['root_device'] = {'serial': 'abcdef'}
node['profile'] = 'compute'
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,profile:compute'},
{'path': '/properties/root_device',
'value': {'serial': 'abcdef'}},
{'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:
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 test_register_update_with_images(self):
node = self._get_node()
node['kernel_id'] = 'image-k'

View File

@ -389,10 +389,15 @@ def register_ironic_node(node, client):
extra = dict(tripleo_platform=platform)
if 'capabilities' in node:
caps = node['capabilities']
if isinstance(caps, dict):
caps = dict_to_capabilities(caps)
properties.update({"capabilities": six.text_type(caps)})
caps = capabilities_to_dict(node['capabilities'])
else:
caps = {}
if 'profile' in node:
caps['profile'] = node['profile']
if caps:
properties["capabilities"] = dict_to_capabilities(caps)
driver = node['pm_type']
if handler.hardware_type and handler.hardware_type != driver:
@ -516,8 +521,15 @@ def _update_or_register_ironic_node(node, node_map, client):
patched[path] = value
if 'capabilities' in node:
patched['/properties/capabilities'] = dict_to_capabilities(
node.pop('capabilities'))
caps = capabilities_to_dict(node.pop('capabilities'))
else:
caps = {}
if 'profile' in node:
caps['profile'] = node.pop('profile')
if caps:
patched['/properties/capabilities'] = dict_to_capabilities(caps)
driver_info = handler.convert(node)
for key, value in driver_info.items():