Allow instance_info to override node interface

This change allows instance_info values to override node interface
definitions, so non-admins can make temporary changes to various
interfaces.

Story: #2008652
Task: #41918
Change-Id: I6c3dc74705bde02bd02882d14838f184f8d4a5e3
This commit is contained in:
Tzu-Mainn Chen 2021-02-23 21:41:14 +00:00
parent 227966b586
commit a165fe3264
5 changed files with 78 additions and 8 deletions

View File

@ -30,6 +30,7 @@ the services.
Node Multi-Tenancy <node-multitenancy>
Fast-Track Deployment <fast-track>
Booting a Ramdisk or an ISO <ramdisk-boot>
Node Interface Override <node-interface-override>
Drivers, Hardware Types and Hardware Interfaces
-----------------------------------------------

View File

@ -0,0 +1,22 @@
=======================
Node Interface Override
=======================
Non-admins with temporary access to a node, may wish to specify different
node interfaces. However, allowing them to set these interface values is
problematic, as there is no automated way to ensure that the original
interface values are restored.
This guide details a method for temporarily overriding a node interface
value.
Overriding a Node Interface
===========================
In order to temporarily override a node interface, simply set the
appropriate value in `instance_info`. For example, if you'd like to
override a node's storage interface, run the following::
baremetal node set --instance-info storage_interface=cinder node-1
`instance_info` values persist until after a node is cleaned.

View File

@ -69,7 +69,9 @@ def _attach_interfaces_to_driver(bare_driver, node, hw_type):
the requested implementation is not compatible with it.
"""
for iface in _INTERFACE_LOADERS:
impl_name = getattr(node, '%s_interface' % iface)
iface_name = '%s_interface' % iface
impl_name = node.instance_info.get(iface_name,
getattr(node, iface_name))
impl = get_interface(hw_type, iface, impl_name)
setattr(bare_driver, iface, impl)
@ -204,20 +206,29 @@ def check_and_update_node_interfaces(node, hw_type=None):
# NOTE(dtantsur): objects raise NotImplementedError on accessing fields
# that are known, but missing from an object. Thus, we cannot just use
# getattr(node, field_name, None) here.
set_default = True
if 'instance_info' in node and field_name in node.instance_info:
impl_name = node.instance_info.get(field_name)
if impl_name is not None:
# Check that the provided value is correct for this type
get_interface(hw_type, iface, impl_name)
set_default = False
if field_name in node:
impl_name = getattr(node, field_name)
if impl_name is not None:
# Check that the provided value is correct for this type
get_interface(hw_type, iface, impl_name)
# Not changing the result, proceeding with the next interface
continue
set_default = False
impl_name = default_interface(hw_type, iface,
driver_name=node.driver, node=node.uuid)
if set_default:
impl_name = default_interface(hw_type, iface,
driver_name=node.driver,
node=node.uuid)
# Set the calculated default and set result to True
setattr(node, field_name, impl_name)
result = True
# Set the calculated default and set result to True
setattr(node, field_name, impl_name)
result = True
return result

View File

@ -214,6 +214,26 @@ class CheckAndUpdateNodeInterfacesTestCase(db_base.DbTestCase):
driver_factory.check_and_update_node_interfaces,
node)
def test_create_node_valid_network_interface_instance_info_override(self):
instance_info = {'network_interface': 'noop',
'storage_interface': 'noop'}
node = obj_utils.get_test_node(self.context,
instance_info=instance_info)
self.assertTrue(driver_factory.check_and_update_node_interfaces(node))
self.assertIsNone(node.network_interface)
self.assertIsNone(node.storage_interface)
self.assertEqual('noop', node.instance_info.get('network_interface'))
self.assertEqual('noop', node.instance_info.get('storage_interface'))
def test_create_node_invalid_network_interface_instance_info_override(
self):
instance_info = {'network_interface': 'banana'}
node = obj_utils.get_test_node(self.context,
instance_info=instance_info)
self.assertRaises(exception.InterfaceNotFoundInEntrypoint,
driver_factory.check_and_update_node_interfaces,
node)
def _get_valid_default_interface_name(self, iface):
i_name = 'fake'
# there is no 'fake' network interface
@ -506,6 +526,17 @@ class HardwareTypeLoadTestCase(db_base.DbTestCase):
self.assertRaises(exception.InterfaceNotFoundInEntrypoint,
task_manager.acquire, self.context, node.id)
def test_build_driver_for_task_instance_info_override(self):
self.config(enabled_network_interfaces=['noop', 'neutron'])
instance_info = {'network_interface': 'neutron'}
node = obj_utils.create_test_node(self.context, driver='fake-hardware',
instance_info=instance_info,
**self.node_kwargs)
with task_manager.acquire(self.context, node.id) as task:
self.assertEqual(
getattr(task.driver, 'network').__class__.__name__,
'NeutronNetwork')
def test_no_storage_interface(self):
node = obj_utils.get_test_node(self.context)
self.assertTrue(driver_factory.check_and_update_node_interfaces(node))

View File

@ -0,0 +1,5 @@
---
features:
- |
Allows node _interface values to be overridden by values in instance_info.
This gives non-admins a temporary method of setting interface values.