def test_create_ports_if_not_exist_mac_exception(self, create_mock, log_mock): create_mock.side_effect = exception.MACAlreadyExists('f') macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: utils.create_ports_if_not_exist(task, macs) self.assertEqual(2, log_mock.call_count)
def test_create_ports_if_not_exist_mac_exception(self, create_mock, log_mock): create_mock.side_effect = exception.MACAlreadyExists('f') macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: utils.create_ports_if_not_exist(task, macs) self.assertEqual(2, log_mock.call_count)
def inspect_hardware(self, task): """Inspect hardware to obtain the hardware properties. This particular implementation only starts inspection using ironic-inspector. Results will be checked in a periodic task. :param task: a task from TaskManager. :returns: states.INSPECTWAIT :raises: HardwareInspectionFailure on failure """ try: enabled_macs = task.driver.management.get_mac_addresses(task) if enabled_macs: inspect_utils.create_ports_if_not_exist( task, enabled_macs, get_mac_address=lambda x: x[0]) else: LOG.warning( "Not attempting to create any port as no NICs " "were discovered in 'enabled' state for node " "%(node)s: %(mac_data)s", { 'mac_data': enabled_macs, 'node': task.node.uuid }) except exception.UnsupportedDriverExtension: LOG.debug( 'Pre-creating ports prior to inspection not supported' ' on node %s.', task.node.uuid) ironic_manages_boot = _ironic_manages_boot( task, raise_exc=CONF.inspector.require_managed_boot) utils.set_node_nested_field(task.node, 'driver_internal_info', _IRONIC_MANAGES_BOOT, ironic_manages_boot) task.node.save() LOG.debug( 'Starting inspection for node %(uuid)s using ' 'ironic-inspector, booting is managed by %(project)s', { 'uuid': task.node.uuid, 'project': 'ironic' if ironic_manages_boot else 'ironic-inspector' }) if ironic_manages_boot: _start_managed_inspection(task) else: # NOTE(dtantsur): spawning a short-living green thread so that # we can release a lock as soon as possible and allow # ironic-inspector to operate on the node. eventlet.spawn_n(_start_inspection, task.node.uuid, task.context) return states.INSPECTWAIT
def _create_ports(self, task, system): enabled_macs = redfish_utils.get_enabled_macs(task, system) if enabled_macs: inspect_utils.create_ports_if_not_exist( task, enabled_macs, get_mac_address=lambda x: x[0]) else: LOG.warning( "Not attempting to create any port as no NICs " "were discovered in 'enabled' state for node " "%(node)s: %(mac_data)s", { 'mac_data': enabled_macs, 'node': task.node.uuid })
def test_create_ports_if_not_exist_attempts_port_creation_blindly( self, port_mock, log_info_mock): macs = {'aa:bb:cc:dd:ee:ff': sushy.STATE_ENABLED, 'aa:bb:aa:aa:aa:aa': sushy.STATE_DISABLED} node_id = self.node.id port_dict1 = {'address': 'aa:bb:cc:dd:ee:ff', 'node_id': node_id} port_dict2 = {'address': 'aa:bb:aa:aa:aa:aa', 'node_id': node_id} with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: utils.create_ports_if_not_exist( task, macs, get_mac_address=lambda x: x[0]) self.assertTrue(log_info_mock.called) expected_calls = [mock.call(task.context, **port_dict1), mock.call(task.context, **port_dict2)] port_mock.assert_has_calls(expected_calls, any_order=True) self.assertEqual(2, port_mock.return_value.create.call_count)
def test_create_ports_if_not_exist(self, port_mock, log_mock): macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} node_id = self.node.id port_dict1 = {'address': 'aa:aa:aa:aa:aa:aa', 'node_id': node_id} port_dict2 = {'address': 'bb:bb:bb:bb:bb:bb', 'node_id': node_id} port_obj1, port_obj2 = mock.MagicMock(), mock.MagicMock() port_mock.side_effect = [port_obj1, port_obj2] with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: utils.create_ports_if_not_exist(task, macs) self.assertTrue(log_mock.called) expected_calls = [mock.call(task.context, **port_dict1), mock.call(task.context, **port_dict2)] port_mock.assert_has_calls(expected_calls, any_order=True) port_obj1.create.assert_called_once_with() port_obj2.create.assert_called_once_with()
def test_create_ports_if_not_exist(self, port_mock, log_mock): macs = {'Port 1': 'aa:aa:aa:aa:aa:aa', 'Port 2': 'bb:bb:bb:bb:bb:bb'} node_id = self.node.id port_dict1 = {'address': 'aa:aa:aa:aa:aa:aa', 'node_id': node_id} port_dict2 = {'address': 'bb:bb:bb:bb:bb:bb', 'node_id': node_id} port_obj1, port_obj2 = mock.MagicMock(), mock.MagicMock() port_mock.side_effect = [port_obj1, port_obj2] with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: utils.create_ports_if_not_exist(task, macs) self.assertTrue(log_mock.called) expected_calls = [ mock.call(task.context, **port_dict1), mock.call(task.context, **port_dict2) ] port_mock.assert_has_calls(expected_calls, any_order=True) port_obj1.create.assert_called_once_with() port_obj2.create.assert_called_once_with()
def _create_ports(self, task, system): if (system.ethernet_interfaces and system.ethernet_interfaces.summary): macs = system.ethernet_interfaces.summary # Create ports for the discovered NICs being in 'enabled' state enabled_macs = {nic_mac: nic_state for nic_mac, nic_state in macs.items() if nic_state == sushy.STATE_ENABLED} if enabled_macs: inspect_utils.create_ports_if_not_exist( task, enabled_macs, get_mac_address=lambda x: x[0]) else: LOG.warning("Not attempting to create any port as no NICs " "were discovered in 'enabled' state for node " "%(node)s: %(mac_data)s", {'mac_data': macs, 'node': task.node.uuid}) else: LOG.warning("No NIC information discovered " "for node %(node)s", {'node': task.node.uuid})
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential properties. It fails if any of the essential properties are not received from the node. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :returns: The resulting state of inspection. """ # Ensure we create a port for every NIC port found for consistency # with our WSMAN inspect behavior and to work around a bug in some # versions of the firmware where the port state is not being # reported correctly. ethernet_interfaces_mac = self._get_mac_address(task) inspect_utils.create_ports_if_not_exist(task, ethernet_interfaces_mac) return super(DracRedfishInspect, self).inspect_hardware(task)
def test_create_ports_if_not_exist_attempts_port_creation_blindly( self, port_mock, log_info_mock): macs = { 'aa:bb:cc:dd:ee:ff': sushy.STATE_ENABLED, 'aa:bb:aa:aa:aa:aa': sushy.STATE_DISABLED } node_id = self.node.id port_dict1 = {'address': 'aa:bb:cc:dd:ee:ff', 'node_id': node_id} port_dict2 = {'address': 'aa:bb:aa:aa:aa:aa', 'node_id': node_id} with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: utils.create_ports_if_not_exist(task, macs, get_mac_address=lambda x: x[0]) self.assertTrue(log_info_mock.called) expected_calls = [ mock.call(task.context, **port_dict1), mock.call(task.context, **port_dict2) ] port_mock.assert_has_calls(expected_calls, any_order=True) self.assertEqual(2, port_mock.return_value.create.call_count)
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential and additional hardware properties. It fails if any of the essential properties are not received from the node. It doesn't fail if node fails to return any capabilities as the capabilities differ from hardware to hardware mostly. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :raises: IloOperationError if system fails to get power state. :returns: The resulting state of inspection. """ power_turned_on = False ilo_object = ilo_common.get_ilo_object(task.node) try: state = task.driver.power.get_power_state(task) except exception.IloOperationError as ilo_exception: operation = (_("Inspecting hardware (get_power_state) on %s") % task.node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) if state != states.POWER_ON: LOG.info( "The node %s is not powered on. Powering on the " "node for inspection.", task.node.uuid) conductor_utils.node_power_action(task, states.POWER_ON) power_turned_on = True # get the essential properties and update the node properties # with it. inspected_properties = {} result = _get_essential_properties(task.node, ilo_object) # A temporary hook for OOB inspection to not to update 'local_gb' # for hardware if the storage is a "Direct Attached Storage" or # "Dynamic Smart Array Controllers" and the operator has manually # updated the local_gb in node properties prior to node inspection. # This will be removed once we have inband inspection support for # ilo drivers. current_local_gb = task.node.properties.get('local_gb') properties = result['properties'] if current_local_gb: if properties['local_gb'] == 0 and current_local_gb > 0: properties['local_gb'] = current_local_gb LOG.warning( 'Could not discover size of disk on the node ' '%s. Value of `properties/local_gb` of the ' 'node is not overwritten.', task.node.uuid) for known_property in self.ESSENTIAL_PROPERTIES: inspected_properties[known_property] = properties[known_property] node_properties = task.node.properties node_properties.update(inspected_properties) task.node.properties = node_properties # Inspect the hardware for additional hardware capabilities. # Since additional hardware capabilities may not apply to all the # hardwares, the method inspect_hardware() doesn't raise an error # for these capabilities. capabilities = _get_capabilities(task.node, ilo_object) model = None if capabilities: model = capabilities.get('server_model') valid_cap = _create_supported_capabilities_dict(capabilities) capabilities = utils.get_updated_capabilities( task.node.properties.get('capabilities'), valid_cap) if capabilities: node_properties['capabilities'] = capabilities task.node.properties = node_properties # RIBCL(Gen8) protocol cannot determine if a NIC # is physically connected with cable or not when the server # is not provisioned. RIS(Gen9) can detect the same for few NIC # adapters but not for all. However it is possible to determine # the same using Redfish(Gen10) protocol. Hence proliantutils # returns ALL MACs for Gen8 and Gen9 while it returns # only active MACs for Gen10. A warning is being added # for the user so that he knows that he needs to remove the # ironic ports created for inactive ports for Gen8 and Gen9. servers = ['Gen8', 'Gen9'] if model is not None and any(serv in model for serv in servers): LOG.warning( 'iLO cannot determine if the NICs are physically ' 'connected or not for ProLiant Gen8 and Gen9 servers. ' 'Hence returns all the MACs present on the server. ' 'Please remove the ironic ports created for inactive ' 'NICs manually for the node %(node)s', {"node": task.node.uuid}) task.node.save() # Create ports for the nics detected. inspect_utils.create_ports_if_not_exist(task, result['macs']) LOG.debug( "Node properties for %(node)s are updated as " "%(properties)s", { 'properties': inspected_properties, 'node': task.node.uuid }) LOG.info("Node %s inspected.", task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info( "The node %s was powered on for inspection. " "Powered off the node as inspection completed.", task.node.uuid) return states.MANAGEABLE
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential properties. It fails if any of the essential properties are not received from the node. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :returns: The resulting state of inspection. """ system = redfish_utils.get_system(task.node) # get the essential properties and update the node properties # with it. inspected_properties = task.node.properties if system.memory_summary and system.memory_summary.size_gib: inspected_properties['memory_mb'] = str( system.memory_summary.size_gib * units.Ki) if system.processors and system.processors.summary: cpus, arch = system.processors.summary if cpus: inspected_properties['cpus'] = cpus if arch: try: inspected_properties['cpu_arch'] = CPU_ARCH_MAP[arch] except KeyError: LOG.warning( "Unknown CPU arch %(arch)s discovered " "for node %(node)s", { 'node': task.node.uuid, 'arch': arch }) simple_storage_size = 0 try: LOG.debug( "Attempting to discover system simple storage size for " "node %(node)s", {'node': task.node.uuid}) if (system.simple_storage and system.simple_storage.disks_sizes_bytes): simple_storage_size = [ size for size in system.simple_storage.disks_sizes_bytes if size >= 4 * units.Gi ] or [0] simple_storage_size = simple_storage_size[0] except sushy.exceptions.SushyError as ex: LOG.debug( "No simple storage information discovered " "for node %(node)s: %(err)s", { 'node': task.node.uuid, 'err': ex }) storage_size = 0 try: LOG.debug( "Attempting to discover system storage volume size for " "node %(node)s", {'node': task.node.uuid}) if system.storage and system.storage.volumes_sizes_bytes: storage_size = [ size for size in system.storage.volumes_sizes_bytes if size >= 4 * units.Gi ] or [0] storage_size = storage_size[0] except sushy.exceptions.SushyError as ex: LOG.debug( "No storage volume information discovered " "for node %(node)s: %(err)s", { 'node': task.node.uuid, 'err': ex }) try: if not storage_size: LOG.debug( "Attempting to discover system storage drive size " "for node %(node)s", {'node': task.node.uuid}) if system.storage and system.storage.drives_sizes_bytes: storage_size = [ size for size in system.storage.drives_sizes_bytes if size >= 4 * units.Gi ] or [0] storage_size = storage_size[0] except sushy.exceptions.SushyError as ex: LOG.debug( "No storage drive information discovered " "for node %(node)s: %(err)s", { 'node': task.node.uuid, 'err': ex }) # NOTE(etingof): pick the smallest disk larger than 4G among available if simple_storage_size and storage_size: local_gb = min(simple_storage_size, storage_size) else: local_gb = max(simple_storage_size, storage_size) # Note(deray): Convert the received size to GiB and reduce the # value by 1 GB as consumers like Ironic requires the ``local_gb`` # to be returned 1 less than actual size. local_gb = max(0, int(local_gb / units.Gi - 1)) # TODO(etingof): should we respect root device hints here? if local_gb: inspected_properties['local_gb'] = str(local_gb) else: LOG.warning( "Could not provide a valid storage size configured " "for node %(node)s. Assuming this is a disk-less node", {'node': task.node.uuid}) inspected_properties['local_gb'] = '0' if system.boot.mode: if not drivers_utils.get_node_capability(task.node, 'boot_mode'): capabilities = utils.get_updated_capabilities( inspected_properties.get('capabilities', ''), {'boot_mode': BOOT_MODE_MAP[system.boot.mode]}) inspected_properties['capabilities'] = capabilities valid_keys = self.ESSENTIAL_PROPERTIES missing_keys = valid_keys - set(inspected_properties) if missing_keys: error = (_('Failed to discover the following properties: ' '%(missing_keys)s on node %(node)s'), { 'missing_keys': ', '.join(missing_keys), 'node': task.node.uuid }) raise exception.HardwareInspectionFailure(error=error) task.node.properties = inspected_properties task.node.save() LOG.debug( "Node properties for %(node)s are updated as " "%(properties)s", { 'properties': inspected_properties, 'node': task.node.uuid }) if (system.ethernet_interfaces and system.ethernet_interfaces.summary): macs = system.ethernet_interfaces.summary # Create ports for the discovered NICs being in 'enabled' state enabled_macs = { nic_mac: nic_state for nic_mac, nic_state in macs.items() if nic_state == sushy.STATE_ENABLED } if enabled_macs: inspect_utils.create_ports_if_not_exist( task, enabled_macs, get_mac_address=lambda x: x[0]) else: LOG.warning( "Not attempting to create any port as no NICs " "were discovered in 'enabled' state for node " "%(node)s: %(mac_data)s", { 'mac_data': macs, 'node': task.node.uuid }) else: LOG.warning("No NIC information discovered " "for node %(node)s", {'node': task.node.uuid}) return states.MANAGEABLE
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential and additional hardware properties. It fails if any of the essential properties are not received from the node. It doesn't fail if node fails to return any capabilities as the capabilities differ from hardware to hardware mostly. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :raises: IloOperationError if system fails to get power state. :returns: The resulting state of inspection. """ power_turned_on = False ilo_object = ilo_common.get_ilo_object(task.node) try: state = task.driver.power.get_power_state(task) except exception.IloOperationError as ilo_exception: operation = (_("Inspecting hardware (get_power_state) on %s") % task.node.uuid) raise exception.IloOperationError(operation=operation, error=ilo_exception) if state != states.POWER_ON: LOG.info("The node %s is not powered on. Powering on the " "node for inspection.", task.node.uuid) conductor_utils.node_power_action(task, states.POWER_ON) power_turned_on = True # get the essential properties and update the node properties # with it. inspected_properties = {} result = _get_essential_properties(task.node, ilo_object) # A temporary hook for OOB inspection to not to update 'local_gb' # for hardware if the storage is a "Direct Attached Storage" or # "Dynamic Smart Array Controllers" and the operator has manually # updated the local_gb in node properties prior to node inspection. # This will be removed once we have inband inspection support for # ilo drivers. current_local_gb = task.node.properties.get('local_gb') properties = result['properties'] if current_local_gb: if properties['local_gb'] == 0 and current_local_gb > 0: properties['local_gb'] = current_local_gb LOG.warning('Could not discover size of disk on the node ' '%s. Value of `properties/local_gb` of the ' 'node is not overwritten.', task.node.uuid) for known_property in self.ESSENTIAL_PROPERTIES: inspected_properties[known_property] = properties[known_property] node_properties = task.node.properties node_properties.update(inspected_properties) task.node.properties = node_properties # Inspect the hardware for additional hardware capabilities. # Since additional hardware capabilities may not apply to all the # hardwares, the method inspect_hardware() doesn't raise an error # for these capabilities. capabilities = _get_capabilities(task.node, ilo_object) if capabilities: valid_cap = _create_supported_capabilities_dict(capabilities) capabilities = utils.get_updated_capabilities( task.node.properties.get('capabilities'), valid_cap) if capabilities: node_properties['capabilities'] = capabilities task.node.properties = node_properties task.node.save() # Create ports for the nics detected. inspect_utils.create_ports_if_not_exist(task, result['macs']) LOG.debug("Node properties for %(node)s are updated as " "%(properties)s", {'properties': inspected_properties, 'node': task.node.uuid}) LOG.info("Node %s inspected.", task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info("The node %s was powered on for inspection. " "Powered off the node as inspection completed.", task.node.uuid) return states.MANAGEABLE
def inspect_hardware(self, task): """Inspect hardware to get the hardware properties. Inspects hardware to get the essential properties. It fails if any of the essential properties are not received from the node. :param task: a TaskManager instance. :raises: HardwareInspectionFailure if essential properties could not be retrieved successfully. :returns: The resulting state of inspection. """ system = redfish_utils.get_system(task.node) # get the essential properties and update the node properties # with it. inspected_properties = task.node.properties if system.memory_summary and system.memory_summary.size_gib: inspected_properties['memory_mb'] = str( system.memory_summary.size_gib * units.Ki) if system.processors and system.processors.summary: cpus, arch = system.processors.summary if cpus: inspected_properties['cpus'] = cpus if arch: try: inspected_properties['cpu_arch'] = CPU_ARCH_MAP[arch] except KeyError: LOG.warning("Unknown CPU arch %(arch)s discovered " "for node %(node)s", {'node': task.node.uuid, 'arch': arch}) simple_storage_size = 0 try: LOG.debug("Attempting to discover system simple storage size for " "node %(node)s", {'node': task.node.uuid}) if (system.simple_storage and system.simple_storage.disks_sizes_bytes): simple_storage_size = [ size for size in system.simple_storage.disks_sizes_bytes if size >= 4 * units.Gi ] or [0] simple_storage_size = simple_storage_size[0] except sushy.exceptions.SushyError as ex: LOG.debug("No simple storage information discovered " "for node %(node)s: %(err)s", {'node': task.node.uuid, 'err': ex}) storage_size = 0 try: LOG.debug("Attempting to discover system storage volume size for " "node %(node)s", {'node': task.node.uuid}) if system.storage and system.storage.volumes_sizes_bytes: storage_size = [ size for size in system.storage.volumes_sizes_bytes if size >= 4 * units.Gi ] or [0] storage_size = storage_size[0] except sushy.exceptions.SushyError as ex: LOG.debug("No storage volume information discovered " "for node %(node)s: %(err)s", {'node': task.node.uuid, 'err': ex}) try: if not storage_size: LOG.debug("Attempting to discover system storage drive size " "for node %(node)s", {'node': task.node.uuid}) if system.storage and system.storage.drives_sizes_bytes: storage_size = [ size for size in system.storage.drives_sizes_bytes if size >= 4 * units.Gi ] or [0] storage_size = storage_size[0] except sushy.exceptions.SushyError as ex: LOG.debug("No storage drive information discovered " "for node %(node)s: %(err)s", {'node': task.node.uuid, 'err': ex}) # NOTE(etingof): pick the smallest disk larger than 4G among available if simple_storage_size and storage_size: local_gb = min(simple_storage_size, storage_size) else: local_gb = max(simple_storage_size, storage_size) # Note(deray): Convert the received size to GiB and reduce the # value by 1 GB as consumers like Ironic requires the ``local_gb`` # to be returned 1 less than actual size. local_gb = max(0, int(local_gb / units.Gi - 1)) # TODO(etingof): should we respect root device hints here? if local_gb: inspected_properties['local_gb'] = str(local_gb) else: LOG.warning("Could not provide a valid storage size configured " "for node %(node)s. Assuming this is a disk-less node", {'node': task.node.uuid}) inspected_properties['local_gb'] = '0' if system.boot.mode: if not drivers_utils.get_node_capability(task.node, 'boot_mode'): capabilities = utils.get_updated_capabilities( inspected_properties.get('capabilities', ''), {'boot_mode': BOOT_MODE_MAP[system.boot.mode]}) inspected_properties['capabilities'] = capabilities valid_keys = self.ESSENTIAL_PROPERTIES missing_keys = valid_keys - set(inspected_properties) if missing_keys: error = (_('Failed to discover the following properties: ' '%(missing_keys)s on node %(node)s'), {'missing_keys': ', '.join(missing_keys), 'node': task.node.uuid}) raise exception.HardwareInspectionFailure(error=error) task.node.properties = inspected_properties task.node.save() LOG.debug("Node properties for %(node)s are updated as " "%(properties)s", {'properties': inspected_properties, 'node': task.node.uuid}) if (system.ethernet_interfaces and system.ethernet_interfaces.summary): macs = system.ethernet_interfaces.summary # Create ports for the discovered NICs being in 'enabled' state enabled_macs = {nic_mac: nic_state for nic_mac, nic_state in macs.items() if nic_state == sushy.STATE_ENABLED} if enabled_macs: inspect_utils.create_ports_if_not_exist( task, enabled_macs, get_mac_address=lambda x: x[0]) else: LOG.warning("Not attempting to create any port as no NICs " "were discovered in 'enabled' state for node " "%(node)s: %(mac_data)s", {'mac_data': macs, 'node': task.node.uuid}) else: LOG.warning("No NIC information discovered " "for node %(node)s", {'node': task.node.uuid}) return states.MANAGEABLE