def default_interface(hw_type, interface_type, driver_name=None, node=None): """Calculate and return the default interface implementation. Finds the first implementation that is supported by the hardware type and is enabled in the configuration. :param hw_type: hardware type instance object. :param interface_type: type of the interface (e.g. 'boot'). :param driver_name: entrypoint name of the hw_type object. Is used for exception message. :param node: the identifier of a node. If specified, is used for exception message. :returns: an entrypoint name of the calculated default implementation. :raises: InterfaceNotFoundInEntrypoint if the entry point was not found. :raises: NoValidDefaultForInterface if no default interface can be found. """ factory = _INTERFACE_LOADERS[interface_type] # The fallback default from the configuration impl_name = getattr(CONF, 'default_%s_interface' % interface_type) if impl_name is not None: try: # Check that the default is correct for this type get_interface(hw_type, interface_type, impl_name) except exception.IncompatibleInterface: raise exception.NoValidDefaultForInterface( interface_type=interface_type, driver=driver_name) else: supported = getattr(hw_type, 'supported_%s_interfaces' % interface_type) # Mapping of classes to entry points enabled = {obj.__class__: name for (name, obj) in factory().items()} # Order of the supported list matters for impl_class in supported: try: impl_name = enabled[impl_class] break except KeyError: continue if impl_name is None: # NOTE(rloo). No i18n on driver_type_str because translating substrings # on their own may cause the final string to look odd. driver_name = driver_name or hw_type.__class__.__name__ node_info = "" if node is not None: node_info = _(' node %s with') % node raise exception.NoValidDefaultForInterface( interface_type=interface_type, driver=driver_name, node_info=node_info) return impl_name
def test_get_properties_none(self, mock_def_iface): hardware_type = driver_factory.get_hardware_type("manual-management") mock_def_iface.side_effect = exception.NoValidDefaultForInterface("no") properties = hardware_type.get_properties() self.assertEqual({}, properties) self.assertEqual(len(driver_base.ALL_INTERFACES), mock_def_iface.call_count)
def test__register_and_validate_no_valid_default(self, esi_mock, default_mock, reg_mock, unreg_mock): # these must be same order as esi_mock side effect hardware_types = collections.OrderedDict(( ('fake-hardware', fake_hardware.FakeHardware()), )) esi_mock.side_effect = [ collections.OrderedDict(( ('management', ['fake', 'noop']), ('deploy', ['agent', 'iscsi']), )), ] default_mock.side_effect = exception.NoValidDefaultForInterface("boo") self.assertRaises( exception.NoValidDefaultForInterface, self.service._register_and_validate_hardware_interfaces, hardware_types) default_mock.assert_called_once_with( hardware_types['fake-hardware'], mock.ANY, driver_name='fake-hardware') unreg_mock.assert_called_once_with(mock.ANY) self.assertFalse(reg_mock.called)
def default_interface(driver_or_hw_type, interface_type, driver_name=None, node=None): """Calculate and return the default interface implementation. Finds the first implementation that is supported by the hardware type and is enabled in the configuration. :param driver_or_hw_type: classic driver or hardware type instance object. :param interface_type: type of the interface (e.g. 'boot'). :param driver_name: entrypoint name of the driver_or_hw_type object. Is used for exception message. :param node: the identifier of a node. If specified, is used for exception message. :returns: an entrypoint name of the calculated default implementation. :raises: InterfaceNotFoundInEntrypoint if the entry point was not found. :raises: NoValidDefaultForInterface if no default interface can be found. """ factory = _INTERFACE_LOADERS[interface_type] is_hardware_type = isinstance(driver_or_hw_type, hardware_type.AbstractHardwareType) # Explicit interface defaults additional_defaults = {'storage': 'noop'} if not is_hardware_type: # For non hardware types we need to set a fallback for the network # interface however hardware_types specify their own defaults if not in # the config file. if (CONF.dhcp.dhcp_provider == 'neutron' and 'flat' in CONF.enabled_network_interfaces): additional_defaults['network'] = 'flat' elif 'noop' in CONF.enabled_network_interfaces: additional_defaults['network'] = 'noop' # The fallback default from the configuration impl_name = getattr(CONF, 'default_%s_interface' % interface_type) if impl_name is None: impl_name = additional_defaults.get(interface_type) if impl_name is not None: # Check that the default is correct for this type get_interface(driver_or_hw_type, interface_type, impl_name) elif is_hardware_type: supported = getattr(driver_or_hw_type, 'supported_%s_interfaces' % interface_type) # Mapping of classes to entry points enabled = {obj.__class__: name for (name, obj) in factory().items()} # Order of the supported list matters for impl_class in supported: try: impl_name = enabled[impl_class] break except KeyError: continue if impl_name is None: # NOTE(rloo). No i18n on driver_type_str because translating substrings # on their own may cause the final string to look odd. if is_hardware_type: driver_type_str = 'hardware type' else: driver_type_str = 'driver' driver_name = driver_name or driver_or_hw_type.__class__.__name__ node_info = "" if node is not None: node_info = _(' node %s with') % node raise exception.NoValidDefaultForInterface( interface_type=interface_type, driver_type=driver_type_str, driver=driver_name, node_info=node_info) return impl_name
def check_and_update_node_interfaces(node, driver_or_hw_type=None): """Ensure that node interfaces (e.g. for creation or updating) are valid. Updates (but doesn't save to the database) hardware interfaces with calculated defaults, if they are not provided. This function is run on node updating and creation, as well as each time a driver instance is built for a node. :param node: node object to check and potentially update :param driver_or_hw_type: classic driver or hardware type instance object; will be detected from node.driver if missing :returns: True if any changes were made to the node, otherwise False :raises: InterfaceNotFoundInEntrypoint on validation failure :raises: NoValidDefaultForInterface if the default value cannot be calculated and is not provided in the configuration :raises: DriverNotFound if the node's driver or hardware type is not found """ if driver_or_hw_type is None: driver_or_hw_type = get_driver_or_hardware_type(node.driver) is_hardware_type = isinstance(driver_or_hw_type, hardware_type.AbstractHardwareType) # Explicit interface defaults additional_defaults = { 'network': 'flat' if CONF.dhcp.dhcp_provider == 'neutron' else 'noop', 'storage': 'noop' } if is_hardware_type: factories = _INTERFACE_LOADERS else: # Only network and storage interfaces are dynamic for classic drivers factories = { 'network': _INTERFACE_LOADERS['network'], 'storage': _INTERFACE_LOADERS['storage'] } # Result - whether the node object was modified result = False # Walk through all dynamic interfaces and check/update them for iface, factory in factories.items(): field_name = '%s_interface' % iface # 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. 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(driver_or_hw_type, iface, impl_name) # Not changing the result, proceeding with the next interface continue # The fallback default from the configuration impl_name = getattr(CONF, 'default_%s_interface' % iface) if impl_name is None: impl_name = additional_defaults.get(iface) if impl_name is not None: # Check that the default is correct for this type _get_interface(driver_or_hw_type, iface, impl_name) elif is_hardware_type: impl_name = _default_interface(driver_or_hw_type, iface, factory) if impl_name is None: raise exception.NoValidDefaultForInterface(interface_type=iface, node=node.uuid, driver=node.driver) # Set the calculated default and set result to True setattr(node, field_name, impl_name) result = True return result