def _test_inspect_hardware_props( self, gpu_ids, fpga_ids, existed_capabilities, inspected_capabilities, expected_capabilities, existed_traits, expected_traits, get_irmc_report_mock, scci_mock, _get_mac_addresses_mock, trait_mock): capabilities_props = set(irmc_inspect.CAPABILITIES_PROPERTIES) # if gpu_ids = [], pci_gpu_devices will not be inspected if len(gpu_ids) == 0: capabilities_props.remove('pci_gpu_devices') # if fpga_ids = [], cpu_fpga will not be inspected if fpga_ids is None or len(fpga_ids) == 0: capabilities_props.remove('cpu_fpga') self.config(gpu_ids=gpu_ids, group='irmc') self.config(fpga_ids=fpga_ids, group='irmc') kwargs = {'sleep_flag': False} inspected_props = { 'memory_mb': '1024', 'local_gb': 10, 'cpus': 2, 'cpu_arch': 'x86_64' } inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] report = 'fake_report' get_irmc_report_mock.return_value = report scci_mock.get_essential_properties.return_value = inspected_props scci_mock.get_capabilities_properties.return_value = \ inspected_capabilities _get_mac_addresses_mock.return_value = inspected_macs trait_mock.return_value = existed_traits with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: task.node.properties[u'capabilities'] =\ ",".join('%(k)s:%(v)s' % {'k': k, 'v': v} for k, v in existed_capabilities.items()) result = irmc_inspect._inspect_hardware(task.node, existed_traits, **kwargs) get_irmc_report_mock.assert_called_once_with(task.node) scci_mock.get_essential_properties.assert_called_once_with( report, irmc_inspect.IRMCInspect.ESSENTIAL_PROPERTIES) scci_mock.get_capabilities_properties.assert_called_once_with( mock.ANY, capabilities_props, gpu_ids, fpga_ids=fpga_ids, **kwargs) expected_capabilities = utils.get_updated_capabilities( '', expected_capabilities) set1 = set(expected_capabilities.split(',')) set2 = set(result[0]['capabilities'].split(',')) self.assertEqual(set1, set2) self.assertEqual(expected_traits, result[2])
def test_get_updated_capabilities_replace_to_existing_capabilities(self): new_capabilities = {"BootMode": "bios"} expected_capabilities = "BootMode:bios" cap_returned = utils.get_updated_capabilities("BootMode:uefi", new_capabilities) set1 = set(expected_capabilities.split(",")) set2 = set(cap_returned.split(",")) self.assertEqual(set1, set2) self.assertIsInstance(cap_returned, str)
def test_get_updated_capabilities_multiple_keys(self): capabilities = {"ilo_firmware_version": "xyz", "foo": "bar", "somekey": "value"} cap_string = "ilo_firmware_version:xyz,foo:bar,somekey:value" cap_returned = utils.get_updated_capabilities(None, capabilities) set1 = set(cap_string.split(",")) set2 = set(cap_returned.split(",")) self.assertEqual(set1, set2) self.assertIsInstance(cap_returned, str)
def _inspect_hardware(node, **kwargs): """Inspect the node and get hardware information. :param node: node object. :param kwargs: the dictionary of additional parameters. :raises: HardwareInspectionFailure, if unable to get essential hardware properties. :returns: a pair of dictionary and list, the dictionary contains keys as in IRMCInspect.ESSENTIAL_PROPERTIES and its inspected values, the list contains mac addresses. """ capabilities_props = set(CAPABILITIES_PROPERTIES) # Remove all capabilities item which will be inspected in the existing # capabilities of node if 'capabilities' in node.properties: existing_cap = node.properties['capabilities'].split(',') for item in capabilities_props: for prop in existing_cap: if item == prop.split(':')[0]: existing_cap.remove(prop) node.properties['capabilities'] = ",".join(existing_cap) # get gpu_ids in ironic configuration values = [gpu_id.lower() for gpu_id in CONF.irmc.gpu_ids] # if gpu_ids = [], pci_gpu_devices will not be inspected if len(values) == 0: capabilities_props.remove('pci_gpu_devices') try: report = irmc_common.get_irmc_report(node) props = scci.get_essential_properties(report, IRMCInspect.ESSENTIAL_PROPERTIES) d_info = irmc_common.parse_driver_info(node) capabilities = scci.get_capabilities_properties( d_info, capabilities_props, values, **kwargs) if capabilities: if capabilities.get('pci_gpu_devices') == 0: capabilities.pop('pci_gpu_devices') if capabilities.get('trusted_boot') is False: capabilities.pop('trusted_boot') capabilities = utils.get_updated_capabilities( node.properties.get('capabilities'), capabilities) if capabilities: props['capabilities'] = capabilities macs = _get_mac_addresses(node) except (scci.SCCIInvalidInputError, scci.SCCIClientError, exception.SNMPFailure) as e: error = (_("Inspection failed for node %(node_id)s " "with the following error: %(error)s") % { 'node_id': node.uuid, 'error': e }) raise exception.HardwareInspectionFailure(error=error) return (props, macs)
def test_get_updated_capabilities_replace_to_existing_capabilities(self): new_capabilities = {'BootMode': 'bios'} expected_capabilities = 'BootMode:bios' cap_returned = utils.get_updated_capabilities('BootMode:uefi', new_capabilities) set1 = set(expected_capabilities.split(',')) set2 = set(cap_returned.split(',')) self.assertEqual(set1, set2) self.assertIsInstance(cap_returned, str)
def test_get_updated_capabilities_multiple_keys(self): capabilities = {'ilo_firmware_version': 'xyz', 'foo': 'bar', 'somekey': 'value'} cap_string = 'ilo_firmware_version:xyz,foo:bar,somekey:value' cap_returned = utils.get_updated_capabilities(None, capabilities) set1 = set(cap_string.split(',')) set2 = set(cap_returned.split(',')) self.assertEqual(set1, set2) self.assertIsInstance(cap_returned, str)
def test__inspect_hardware(self, get_irmc_report_mock, scci_mock, _get_mac_addresses_mock): # Set config flags gpu_ids = ['0x1000/0x0079', '0x2100/0x0080'] cpu_fpgas = ['0x1000/0x0179', '0x2100/0x0180'] self.config(gpu_ids=gpu_ids, group='irmc') self.config(fpga_ids=cpu_fpgas, group='irmc') kwargs = {'sleep_flag': False} inspected_props = { 'memory_mb': '1024', 'local_gb': 10, 'cpus': 2, 'cpu_arch': 'x86_64' } inspected_capabilities = { 'trusted_boot': False, 'irmc_firmware_version': 'iRMC S4-7.82F', 'server_model': 'TX2540M1F5', 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x', 'pci_gpu_devices': 1, 'cpu_fpga': 1 } new_traits = ['CUSTOM_CPU_FPGA'] existing_traits = [] inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] report = 'fake_report' get_irmc_report_mock.return_value = report scci_mock.get_essential_properties.return_value = inspected_props scci_mock.get_capabilities_properties.return_value = ( inspected_capabilities) _get_mac_addresses_mock.return_value = inspected_macs with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: result = irmc_inspect._inspect_hardware(task.node, existing_traits, **kwargs) get_irmc_report_mock.assert_called_once_with(task.node) scci_mock.get_essential_properties.assert_called_once_with( report, irmc_inspect.IRMCInspect.ESSENTIAL_PROPERTIES) scci_mock.get_capabilities_properties.assert_called_once_with( mock.ANY, irmc_inspect.CAPABILITIES_PROPERTIES, gpu_ids, fpga_ids=cpu_fpgas, **kwargs) expected_props = dict(inspected_props) inspected_capabilities = utils.get_updated_capabilities( '', inspected_capabilities) expected_props['capabilities'] = inspected_capabilities self.assertEqual((expected_props, inspected_macs, new_traits), result)
def test__inspect_hardware( self, get_irmc_report_mock, scci_mock, _get_mac_addresses_mock): # Set config flags gpu_ids = ['0x1000/0x0079', '0x2100/0x0080'] cpu_fpgas = ['0x1000/0x0179', '0x2100/0x0180'] self.config(gpu_ids=gpu_ids, group='irmc') self.config(fpga_ids=cpu_fpgas, group='irmc') kwargs = {'sleep_flag': False} inspected_props = { 'memory_mb': '1024', 'local_gb': 10, 'cpus': 2, 'cpu_arch': 'x86_64'} inspected_capabilities = { 'trusted_boot': False, 'irmc_firmware_version': 'iRMC S4-7.82F', 'server_model': 'TX2540M1F5', 'rom_firmware_version': 'V4.6.5.4 R1.15.0 for D3099-B1x', 'pci_gpu_devices': 1, 'cpu_fpga': 1} new_traits = ['CUSTOM_CPU_FPGA'] existing_traits = [] inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] report = 'fake_report' get_irmc_report_mock.return_value = report scci_mock.get_essential_properties.return_value = inspected_props scci_mock.get_capabilities_properties.return_value = ( inspected_capabilities) _get_mac_addresses_mock.return_value = inspected_macs with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: result = irmc_inspect._inspect_hardware(task.node, existing_traits, **kwargs) get_irmc_report_mock.assert_called_once_with(task.node) scci_mock.get_essential_properties.assert_called_once_with( report, irmc_inspect.IRMCInspect.ESSENTIAL_PROPERTIES) scci_mock.get_capabilities_properties.assert_called_once_with( mock.ANY, irmc_inspect.CAPABILITIES_PROPERTIES, gpu_ids, fpga_ids=cpu_fpgas, **kwargs) expected_props = dict(inspected_props) inspected_capabilities = utils.get_updated_capabilities( '', inspected_capabilities) expected_props['capabilities'] = inspected_capabilities self.assertEqual((expected_props, inspected_macs, new_traits), result)
def update_raid_info(node, raid_config): """Update the node's information based on the RAID config. This method updates the node's information to make use of the configured RAID for scheduling purposes (through properties['capabilities'] and properties['local_gb']) and deploying purposes (using properties['root_device']). :param node: a node object :param raid_config: The dictionary containing the current RAID configuration. :raises: InvalidParameterValue, if 'raid_config' has more than one root volume or if node.properties['capabilities'] is malformed. """ current = raid_config.copy() current['last_updated'] = str(datetime.datetime.utcnow()) node.raid_config = current node.target_raid_config = None # Current RAID configuration can have 0 or 1 root volumes. If there # are > 1 root volumes, then it's invalid. We check for this condition # while accepting target RAID configuration, but this check is just in # place, if some drivers pass > 1 root volumes to this method. root_logical_disk = _check_and_return_root_volumes(raid_config) if root_logical_disk: # Update local_gb and root_device_hint properties = node.properties properties['local_gb'] = root_logical_disk['size_gb'] try: properties['root_device'] = ( root_logical_disk['root_device_hint']) except KeyError: pass properties['capabilities'] = utils.get_updated_capabilities( properties.get('capabilities', ''), {'raid_level': root_logical_disk['raid_level']}) node.properties = properties node.save()
def update_raid_info(node, raid_config): """Update the node's information based on the RAID config. This method updates the node's information to make use of the configured RAID for scheduling purposes (through properties['capabilities'] and properties['local_gb']) and deploying purposes (using properties['root_device']). :param node: a node object :param raid_config: The dictionary containing the current RAID configuration. :raises: InvalidParameterValue, if 'raid_config' has more than one root volume or if node.properties['capabilities'] is malformed. """ current = raid_config.copy() current['last_updated'] = str(datetime.datetime.utcnow()) node.raid_config = current node.target_raid_config = None # Current RAID configuration can have 0 or 1 root volumes. If there # are > 1 root volumes, then it's invalid. We check for this condition # while accepting target RAID configuration, but this check is just in # place, if some drivers pass > 1 root volumes to this method. root_logical_disk = _check_and_return_root_volumes(raid_config) if root_logical_disk: # Update local_gb and root_device_hint properties = node.properties properties['local_gb'] = root_logical_disk['size_gb'] try: properties['root_device'] = (root_logical_disk['root_device_hint']) except KeyError: pass properties['capabilities'] = utils.get_updated_capabilities( properties.get('capabilities', ''), {'raid_level': root_logical_disk['raid_level']}) node.properties = properties node.save()
def test_get_updated_capabilities(self): capabilities = {'ilo_firmware_version': 'xyz'} cap_string = 'ilo_firmware_version:xyz' cap_returned = utils.get_updated_capabilities(None, capabilities) self.assertEqual(cap_string, cap_returned) self.assertIsInstance(cap_returned, str)
def _inspect_hardware(node, existing_traits=None, **kwargs): """Inspect the node and get hardware information. :param node: node object. :param existing_traits: existing traits list. :param kwargs: the dictionary of additional parameters. :raises: HardwareInspectionFailure, if unable to get essential hardware properties. :returns: a pair of dictionary and list, the dictionary contains keys as in IRMCInspect.ESSENTIAL_PROPERTIES and its inspected values, the list contains mac addresses. """ capabilities_props = set(CAPABILITIES_PROPERTIES) new_traits = list(existing_traits) if existing_traits else [] # Remove all capabilities item which will be inspected in the existing # capabilities of node if 'capabilities' in node.properties: existing_cap = node.properties['capabilities'].split(',') for item in capabilities_props: for prop in existing_cap: if item == prop.split(':')[0]: existing_cap.remove(prop) node.properties['capabilities'] = ",".join(existing_cap) # get gpu_ids, fpga_ids in ironic configuration gpu_ids = [gpu_id.lower() for gpu_id in CONF.irmc.gpu_ids] fpga_ids = [fpga_id.lower() for fpga_id in CONF.irmc.fpga_ids] # if gpu_ids = [], pci_gpu_devices will not be inspected if len(gpu_ids) == 0: capabilities_props.remove('pci_gpu_devices') # if fpga_ids = [], cpu_fpga will not be inspected if len(fpga_ids) == 0: capabilities_props.remove('cpu_fpga') try: report = irmc_common.get_irmc_report(node) props = scci.get_essential_properties( report, IRMCInspect.ESSENTIAL_PROPERTIES) d_info = irmc_common.parse_driver_info(node) capabilities = scci.get_capabilities_properties( d_info, capabilities_props, gpu_ids, fpga_ids=fpga_ids, **kwargs) if capabilities: if capabilities.get('pci_gpu_devices') == 0: capabilities.pop('pci_gpu_devices') cpu_fpga = capabilities.pop('cpu_fpga', 0) if cpu_fpga == 0 and 'CUSTOM_CPU_FPGA' in new_traits: new_traits.remove('CUSTOM_CPU_FPGA') elif cpu_fpga != 0 and 'CUSTOM_CPU_FPGA' not in new_traits: new_traits.append('CUSTOM_CPU_FPGA') if capabilities.get('trusted_boot') is False: capabilities.pop('trusted_boot') capabilities = utils.get_updated_capabilities( node.properties.get('capabilities'), capabilities) if capabilities: props['capabilities'] = capabilities macs = _get_mac_addresses(node) except (scci.SCCIInvalidInputError, scci.SCCIClientError, exception.SNMPFailure) as e: error = (_("Inspection failed for node %(node_id)s " "with the following error: %(error)s") % {'node_id': node.uuid, 'error': e}) raise exception.HardwareInspectionFailure(error=error) return props, macs, new_traits
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 }) # TODO(etingof): should we respect root device hints here? local_gb = self._detect_local_gb(task, system) 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 }) self._create_ports(task, system) 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( _LI("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) properties = result['properties'] 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. _create_ports_if_not_exist(task.node, result['macs']) LOG.debug(("Node properties for %(node)s are updated as " "%(properties)s"), { 'properties': inspected_properties, 'node': task.node.uuid }) LOG.info(_LI("Node %s inspected."), task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info( _LI("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 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. _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. Inspect hardware to obtain the essential & additional hardware properties. :param task: a TaskManager instance containing the node to act on. :raises: HardwareInspectionFailure, if unable to get essential hardware properties. :returns: states.MANAGEABLE """ node = task.node client = drac_common.get_drac_client(node) properties = {} try: properties['memory_mb'] = sum( [memory.size_mb for memory in client.list_memory()]) cpus = client.list_cpus() if cpus: properties['cpus'] = sum( [self._calculate_cpus(cpu) for cpu in cpus]) properties['cpu_arch'] = 'x86_64' if cpus[0].arch64 else 'x86' bios_settings = client.list_bios_settings() video_controllers = client.list_video_controllers() current_capabilities = node.properties.get('capabilities', '') new_capabilities = { 'boot_mode': bios_settings["BootMode"].current_value.lower(), 'pci_gpu_devices': self._calculate_gpus(video_controllers) } capabilties = utils.get_updated_capabilities( current_capabilities, new_capabilities) properties['capabilities'] = capabilties virtual_disks = client.list_virtual_disks() root_disk = self._guess_root_disk(virtual_disks) if root_disk: properties['local_gb'] = int(root_disk.size_mb / units.Ki) else: physical_disks = client.list_physical_disks() root_disk = self._guess_root_disk(physical_disks) if root_disk: properties['local_gb'] = int(root_disk.size_mb / units.Ki) except drac_exceptions.BaseClientException as exc: LOG.error( 'DRAC driver failed to introspect node ' '%(node_uuid)s. Reason: %(error)s.', { 'node_uuid': node.uuid, 'error': exc }) raise exception.HardwareInspectionFailure(error=exc) valid_keys = self.ESSENTIAL_PROPERTIES missing_keys = valid_keys - set(properties) if missing_keys: error = (_('Failed to discover the following properties: ' '%(missing_keys)s') % { 'missing_keys': ', '.join(missing_keys) }) raise exception.HardwareInspectionFailure(error=error) node.properties = dict(node.properties, **properties) node.save() try: nics = client.list_nics() except drac_exceptions.BaseClientException as exc: LOG.error( 'DRAC driver failed to introspect node ' '%(node_uuid)s. Reason: %(error)s.', { 'node_uuid': node.uuid, 'error': exc }) raise exception.HardwareInspectionFailure(error=exc) pxe_dev_nics = self._get_pxe_dev_nics(client, nics, node) if pxe_dev_nics is None: LOG.warning( 'No PXE enabled NIC was found for node ' '%(node_uuid)s.', {'node_uuid': node.uuid}) for nic in nics: try: port = objects.Port(task.context, address=nic.mac, node_id=node.id, pxe_enabled=(nic.id in pxe_dev_nics)) port.create() LOG.info( 'Port created with MAC address %(mac)s ' 'for node %(node_uuid)s during inspection', { 'mac': nic.mac, 'node_uuid': node.uuid }) except exception.MACAlreadyExists: LOG.warning( 'Failed to create a port with MAC address ' '%(mac)s when inspecting the node ' '%(node_uuid)s because the address is already ' 'registered', { 'mac': nic.mac, 'node_uuid': node.uuid }) LOG.info('Node %s successfully inspected.', 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) 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 _test_inspect_hardware_props(self, gpu_ids, fpga_ids, existed_capabilities, inspected_capabilities, expected_capabilities, existed_traits, expected_traits, get_irmc_report_mock, scci_mock, _get_mac_addresses_mock, trait_mock): capabilities_props = set(irmc_inspect.CAPABILITIES_PROPERTIES) # if gpu_ids = [], pci_gpu_devices will not be inspected if len(gpu_ids) == 0: capabilities_props.remove('pci_gpu_devices') # if fpga_ids = [], cpu_fpga will not be inspected if fpga_ids is None or len(fpga_ids) == 0: capabilities_props.remove('cpu_fpga') self.config(gpu_ids=gpu_ids, group='irmc') self.config(fpga_ids=fpga_ids, group='irmc') kwargs = {'sleep_flag': False} inspected_props = { 'memory_mb': '1024', 'local_gb': 10, 'cpus': 2, 'cpu_arch': 'x86_64'} inspected_macs = ['aa:aa:aa:aa:aa:aa', 'bb:bb:bb:bb:bb:bb'] report = 'fake_report' get_irmc_report_mock.return_value = report scci_mock.get_essential_properties.return_value = inspected_props scci_mock.get_capabilities_properties.return_value = \ inspected_capabilities _get_mac_addresses_mock.return_value = inspected_macs trait_mock.return_value = existed_traits with task_manager.acquire(self.context, self.node.uuid, shared=True) as task: task.node.properties[u'capabilities'] =\ ",".join('%(k)s:%(v)s' % {'k': k, 'v': v} for k, v in existed_capabilities.items()) result = irmc_inspect._inspect_hardware(task.node, existed_traits, **kwargs) get_irmc_report_mock.assert_called_once_with(task.node) scci_mock.get_essential_properties.assert_called_once_with( report, irmc_inspect.IRMCInspect.ESSENTIAL_PROPERTIES) scci_mock.get_capabilities_properties.assert_called_once_with( mock.ANY, capabilities_props, gpu_ids, fpga_ids=fpga_ids, **kwargs) expected_capabilities = utils.get_updated_capabilities( '', expected_capabilities) set1 = set(expected_capabilities.split(',')) set2 = set(result[0]['capabilities'].split(',')) self.assertEqual(set1, set2) self.assertEqual(expected_traits, result[2])
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. Inspect hardware to obtain the essential & additional hardware properties. :param task: a TaskManager instance containing the node to act on. :raises: HardwareInspectionFailure, if unable to get essential hardware properties. :returns: states.MANAGEABLE """ node = task.node client = drac_common.get_drac_client(node) properties = {} try: properties['memory_mb'] = sum( [memory.size_mb for memory in client.list_memory()]) cpus = client.list_cpus() if cpus: properties['cpus'] = sum( [self._calculate_cpus(cpu) for cpu in cpus]) properties['cpu_arch'] = 'x86_64' if cpus[0].arch64 else 'x86' bios_settings = client.list_bios_settings() current_capabilities = node.properties.get('capabilities', '') new_capabilities = { 'boot_mode': bios_settings["BootMode"].current_value.lower()} capabilties = utils.get_updated_capabilities(current_capabilities, new_capabilities) properties['capabilities'] = capabilties virtual_disks = client.list_virtual_disks() root_disk = self._guess_root_disk(virtual_disks) if root_disk: properties['local_gb'] = int(root_disk.size_mb / units.Ki) else: physical_disks = client.list_physical_disks() root_disk = self._guess_root_disk(physical_disks) if root_disk: properties['local_gb'] = int( root_disk.size_mb / units.Ki) except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to introspect node ' '%(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.HardwareInspectionFailure(error=exc) valid_keys = self.ESSENTIAL_PROPERTIES missing_keys = valid_keys - set(properties) if missing_keys: error = (_('Failed to discover the following properties: ' '%(missing_keys)s') % {'missing_keys': ', '.join(missing_keys)}) raise exception.HardwareInspectionFailure(error=error) node.properties = dict(node.properties, **properties) node.save() try: nics = client.list_nics() except drac_exceptions.BaseClientException as exc: LOG.error('DRAC driver failed to introspect node ' '%(node_uuid)s. Reason: %(error)s.', {'node_uuid': node.uuid, 'error': exc}) raise exception.HardwareInspectionFailure(error=exc) pxe_dev_nics = self._get_pxe_dev_nics(client, nics, node) if pxe_dev_nics is None: LOG.warning('No PXE enabled NIC was found for node ' '%(node_uuid)s.', {'node_uuid': node.uuid}) for nic in nics: try: port = objects.Port(task.context, address=nic.mac, node_id=node.id, pxe_enabled=(nic.id in pxe_dev_nics)) port.create() LOG.info('Port created with MAC address %(mac)s ' 'for node %(node_uuid)s during inspection', {'mac': nic.mac, 'node_uuid': node.uuid}) except exception.MACAlreadyExists: LOG.warning('Failed to create a port with MAC address ' '%(mac)s when inspecting the node ' '%(node_uuid)s because the address is already ' 'registered', {'mac': nic.mac, 'node_uuid': node.uuid}) LOG.info('Node %s successfully inspected.', 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(_LI("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(_LW('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. _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(_LI("Node %s inspected."), task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info(_LI("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 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(_LI("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) properties = result['properties'] for known_property in ESSENTIAL_PROPERTIES_KEYS: 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. _create_ports_if_not_exist(task.node, result['macs']) LOG.debug(("Node properties for %(node)s are updated as " "%(properties)s"), {'properties': inspected_properties, 'node': task.node.uuid}) LOG.info(_LI("Node %s inspected."), task.node.uuid) if power_turned_on: conductor_utils.node_power_action(task, states.POWER_OFF) LOG.info(_LI("The node %s was powered on for inspection. " "Powered off the node as inspection completed."), task.node.uuid) return states.MANAGEABLE