def _execute_nm_command(task, data, command_func, parse_func=None): """Execute Intel Node Manager command via send_raw(). :param task: a TaskManager instance. :param data: a dict with data passed to vendor's method. :param command_func: a function that returns raw command bytes. :param parse_func: a function that parses returned raw bytes. :raises: IPMIFailure if Intel Node Manager is not detected on a node or if an error happens during command execution. :returns: a dict with parsed output or None if command does not return user's info. """ try: channel, address = _get_nm_address(task) except exception.IPMIFailure as e: with excutils.save_and_reraise_exception(): LOG.exception(_LE('Can not obtain Intel Node Manager address for ' 'node %(node)s: %(err)s'), {'node': task.node.uuid, 'err': six.text_type(e)}) driver_info = task.node.driver_info driver_info['ipmi_bridging'] = 'single' driver_info['ipmi_target_channel'] = channel driver_info['ipmi_target_address'] = address cmd = _command_to_string(command_func(data)) out = ipmi.send_raw(task, cmd)[0] if parse_func: try: return parse_func(out.split()) except exception.IPMIFailure as e: with excutils.save_and_reraise_exception(): LOG.exception(_LE('Error in returned data for node %(node)s: ' '%(err)s'), {'node': task.node.uuid, 'err': six.text_type(e)})
def awake_amt_interface(node): """Wake up AMT interface. AMT interface goes to sleep after a period of time if the host is off. This method will ping AMT interface to wake it up. Because there is no guarantee that the AMT address in driver_info is correct, only ping the IP five times which is enough to wake it up. :param node: an Ironic node object. :raises: AMTConnectFailure if unable to connect to the server. """ awake_interval = CONF.amt_driver.awake_interval if awake_interval == 0: return now = time.time() last_awake = AMT_AWAKE_CACHE.get(node.uuid, 0) if now - last_awake > awake_interval: cmd_args = [ 'ping', '-i', 0.2, '-c', 5, node.driver_info['amt_address'] ] try: ironic_utils.execute(*cmd_args) except processutils.ProcessExecutionError as err: LOG.error( _LE('Unable to awake AMT interface on node ' '%(node_id)s. Error: %(error)s'), { 'node_id': node.uuid, 'error': err }) raise exception.AMTConnectFailure() else: LOG.debug(('Successfully awakened AMT interface on node ' '%(node_id)s.'), {'node_id': node.uuid}) AMT_AWAKE_CACHE[node.uuid] = now
def wsman_invoke(self, options, resource_uri, method, data=None): """Invoke method on target server :param options: client options :param resource_uri: a URI to an XML schema :param method: invoke method :param data: a XmlDoc as invoke input :returns: XmlDoc object :raises: AMTFailure if get unexpected response. :raises: AMTConnectFailure if unable to connect to the server. """ if data is None: doc = self.client.invoke(options, resource_uri, method) else: doc = self.client.invoke(options, resource_uri, method, data) item = "ReturnValue" return_value = xml_find(doc, resource_uri, item).text if return_value != RET_SUCCESS: LOG.error( _LE("Call to AMT with URI %(uri)s and " "method %(method)s failed: return value " "was %(value)s"), { 'uri': resource_uri, 'method': method, 'value': return_value }) raise exception.AMTFailure(cmd='wsman_invoke') return doc
def awake_amt_interface(node): """Wake up AMT interface. AMT interface goes to sleep after a period of time if the host is off. This method will ping AMT interface to wake it up. Because there is no guarantee that the AMT address in driver_info is correct, only ping the IP five times which is enough to wake it up. :param node: an Ironic node object. :raises: AMTConnectFailure if unable to connect to the server. """ awake_interval = CONF.amt_driver.awake_interval if awake_interval == 0: return now = time.time() last_awake = AMT_AWAKE_CACHE.get(node.uuid, 0) if now - last_awake > awake_interval: cmd_args = ['ping', '-i', 0.2, '-c', 5, node.driver_info['amt_address']] try: ironic_utils.execute(*cmd_args) except processutils.ProcessExecutionError as err: LOG.error(_LE('Unable to awake AMT interface on node ' '%(node_id)s. Error: %(error)s'), {'node_id': node.uuid, 'error': err}) raise exception.AMTConnectFailure() else: LOG.debug(('Successfully awakened AMT interface on node ' '%(node_id)s.'), {'node_id': node.uuid}) AMT_AWAKE_CACHE[node.uuid] = now
def _set_power_state(node, target_state): """Set power state of the AMT Client. :param node: a node object. :param target_state: desired power state. :raises: AMTFailure :raises: AMTConnectFailure """ amt_common.awake_amt_interface(node) client = amt_common.get_wsman_client(node) method = 'RequestPowerStateChange' options = pywsman.ClientOptions() options.add_selector('Name', 'Intel(r) AMT Power Management Service') doc = _generate_power_action_input(AMT_POWER_MAP[target_state]) try: client.wsman_invoke(options, resource_uris.CIM_PowerManagementService, method, doc) except (exception.AMTFailure, exception.AMTConnectFailure) as e: with excutils.save_and_reraise_exception(): LOG.exception( _LE("Failed to set power state %(state)s for " "node %(node_id)s with error: %(error)s."), { 'state': target_state, 'node_id': node.uuid, 'error': e }) else: LOG.info(_LI("Power state set to %(state)s for node %(node_id)s"), { 'state': target_state, 'node_id': node.uuid })
def _set_boot_device_order(node, boot_device): """Set boot device order configuration of AMT Client. :param node: a node object :param boot_device: the boot device :raises: AMTFailure :raises: AMTConnectFailure """ amt_common.awake_amt_interface(node) client = amt_common.get_wsman_client(node) device = amt_common.BOOT_DEVICES_MAPPING[boot_device] doc = _generate_change_boot_order_input(device) method = 'ChangeBootOrder' options = pywsman.ClientOptions() options.add_selector('InstanceID', 'Intel(r) AMT: Boot Configuration 0') try: client.wsman_invoke(options, resource_uris.CIM_BootConfigSetting, method, doc) except (exception.AMTFailure, exception.AMTConnectFailure) as e: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Failed to set boot device %(boot_device)s for " "node %(node_id)s with error: %(error)s."), {'boot_device': boot_device, 'node_id': node.uuid, 'error': e}) else: LOG.info(_LI("Successfully set boot device %(boot_device)s for " "node %(node_id)s"), {'boot_device': boot_device, 'node_id': node.uuid})
def _set_power_state(node, target_state): """Set power state of the AMT Client. :param node: a node object. :param target_state: desired power state. :raises: AMTFailure :raises: AMTConnectFailure """ amt_common.awake_amt_interface(node) client = amt_common.get_wsman_client(node) method = 'RequestPowerStateChange' options = pywsman.ClientOptions() options.add_selector('Name', 'Intel(r) AMT Power Management Service') doc = _generate_power_action_input(AMT_POWER_MAP[target_state]) try: client.wsman_invoke(options, resource_uris.CIM_PowerManagementService, method, doc) except (exception.AMTFailure, exception.AMTConnectFailure) as e: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Failed to set power state %(state)s for " "node %(node_id)s with error: %(error)s."), {'state': target_state, 'node_id': node.uuid, 'error': e}) else: LOG.info(_LI("Power state set to %(state)s for node %(node_id)s"), {'state': target_state, 'node_id': node.uuid})
def _enable_boot_config(node): """Enable boot configuration of AMT Client. :param node: a node object :raises: AMTFailure :raises: AMTConnectFailure """ amt_common.awake_amt_interface(node) client = amt_common.get_wsman_client(node) method = 'SetBootConfigRole' doc = _generate_enable_boot_config_input() options = pywsman.ClientOptions() options.add_selector('Name', 'Intel(r) AMT Boot Service') try: client.wsman_invoke(options, resource_uris.CIM_BootService, method, doc) except (exception.AMTFailure, exception.AMTConnectFailure) as e: with excutils.save_and_reraise_exception(): LOG.exception( _LE("Failed to enable boot config for node " "%(node_id)s with error: %(error)s."), { 'node_id': node.uuid, 'error': e }) else: LOG.info(_LI("Successfully enabled boot config for node %(node_id)s."), {'node_id': node.uuid})
def _power_status(node): """Get the power status for a node. :param node: a node object. :returns: one of ironic.common.states POWER_OFF, POWER_ON or ERROR. :raises: AMTFailure. :raises: AMTConnectFailure. """ amt_common.awake_amt_interface(node) client = amt_common.get_wsman_client(node) namespace = resource_uris.CIM_AssociatedPowerManagementService try: doc = client.wsman_get(namespace) except (exception.AMTFailure, exception.AMTConnectFailure) as e: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Failed to get power state for node %(node_id)s " "with error: %(error)s."), {'node_id': node.uuid, 'error': e}) item = "PowerState" power_state = amt_common.xml_find(doc, namespace, item).text for state in AMT_POWER_MAP: if power_state == AMT_POWER_MAP[state]: return state return states.ERROR
def dump_sdr(task, file_path): """Dump SDR data to a file. :param task: a TaskManager instance. :param file_path: the path to SDR dump file. :raises: IPMIFailure on an error from ipmitool. :raises: MissingParameterValue if a required parameter is missing. :raises: InvalidParameterValue when an invalid value is specified. """ node_uuid = task.node.uuid LOG.debug('Dump SDR data for node %(node)s to file %(name)s', {'name': file_path, 'node': node_uuid}) driver_info = ipmitool._parse_driver_info(task.node) cmd = 'sdr dump %s' % file_path try: out, err = ipmitool._exec_ipmitool(driver_info, cmd) LOG.debug('dump SDR returned stdout: %(stdout)s, stderr:' ' %(stderr)s', {'stdout': out, 'stderr': err}) except (exception.PasswordFileFailedToCreate, processutils.ProcessExecutionError) as e: LOG.exception(_LE('IPMI "sdr dump" failed for node %(node_id)s ' 'with error: %(error)s.'), {'node_id': node_uuid, 'error': e}) raise exception.IPMIFailure(cmd=cmd)
def send_raw(task, raw_bytes): """Send raw bytes to the BMC. Bytes should be a string of bytes. :param task: a TaskManager instance. :param raw_bytes: a string of raw bytes to send, e.g. '0x00 0x01' :returns: a tuple with stdout and stderr. :raises: IPMIFailure on an error from ipmitool. :raises: MissingParameterValue if a required parameter is missing. :raises: InvalidParameterValue when an invalid value is specified. """ node_uuid = task.node.uuid LOG.debug('Sending node %(node)s raw bytes %(bytes)s', {'bytes': raw_bytes, 'node': node_uuid}) driver_info = ipmitool._parse_driver_info(task.node) cmd = 'raw %s' % raw_bytes try: out, err = ipmitool._exec_ipmitool(driver_info, cmd) LOG.debug('send raw bytes returned stdout: %(stdout)s, stderr:' ' %(stderr)s', {'stdout': out, 'stderr': err}) except (exception.PasswordFileFailedToCreate, processutils.ProcessExecutionError) as e: LOG.exception(_LE('IPMI "raw bytes" failed for node %(node_id)s ' 'with error: %(error)s.'), {'node_id': node_uuid, 'error': e}) raise exception.IPMIFailure(cmd=cmd) return out, err
def wsman_invoke(self, options, resource_uri, method, data=None): """Invoke method on target server :param options: client options :param resource_uri: a URI to an XML schema :param method: invoke method :param data: a XmlDoc as invoke input :returns: XmlDoc object :raises: AMTFailure if get unexpected response. :raises: AMTConnectFailure if unable to connect to the server. """ if data is None: doc = self.client.invoke(options, resource_uri, method) else: doc = self.client.invoke(options, resource_uri, method, data) item = "ReturnValue" return_value = xml_find(doc, resource_uri, item).text if return_value != RET_SUCCESS: LOG.error(_LE("Call to AMT with URI %(uri)s and " "method %(method)s failed: return value " "was %(value)s"), {'uri': resource_uri, 'method': method, 'value': return_value}) raise exception.AMTFailure(cmd='wsman_invoke') return doc
def _power_status(node): """Get the power status for a node. :param node: a node object. :returns: one of ironic.common.states POWER_OFF, POWER_ON or ERROR. :raises: AMTFailure. :raises: AMTConnectFailure. """ amt_common.awake_amt_interface(node) client = amt_common.get_wsman_client(node) namespace = resource_uris.CIM_AssociatedPowerManagementService try: doc = client.wsman_get(namespace) except (exception.AMTFailure, exception.AMTConnectFailure) as e: with excutils.save_and_reraise_exception(): LOG.exception( _LE("Failed to get power state for node %(node_id)s " "with error: %(error)s."), { 'node_id': node.uuid, 'error': e }) item = "PowerState" power_state = amt_common.xml_find(doc, namespace, item).text for state in AMT_POWER_MAP: if power_state == AMT_POWER_MAP[state]: return state return states.ERROR
def _execute_nm_command(task, data, command_func, parse_func=None): """Execute Intel Node Manager command via send_raw(). :param task: a TaskManager instance. :param data: a dict with data passed to vendor's method. :param command_func: a function that returns raw command bytes. :param parse_func: a function that parses returned raw bytes. :raises: IPMIFailure if Intel Node Manager is not detected on a node or if an error happens during command execution. :returns: a dict with parsed output or None if command does not return user's info. """ try: channel, address = _get_nm_address(task) except exception.IPMIFailure as e: with excutils.save_and_reraise_exception(): LOG.exception( _LE('Can not obtain Intel Node Manager address for ' 'node %(node)s: %(err)s'), { 'node': task.node.uuid, 'err': six.text_type(e) }) driver_info = task.node.driver_info driver_info['ipmi_bridging'] = 'single' driver_info['ipmi_target_channel'] = channel driver_info['ipmi_target_address'] = address cmd = _command_to_string(command_func(data)) out = ipmitool.send_raw(task, cmd)[0] if parse_func: try: return parse_func(out.split()) except exception.IPMIFailure as e: with excutils.save_and_reraise_exception(): LOG.exception( _LE('Error in returned data for node %(node)s: ' '%(err)s'), { 'node': task.node.uuid, 'err': six.text_type(e) })
def wsman_get(self, resource_uri, options=None): """Get target server info :param options: client options :param resource_uri: a URI to an XML schema :returns: XmlDoc object :raises: AMTFailure if get unexpected response. :raises: AMTConnectFailure if unable to connect to the server. """ if options is None: options = pywsman.ClientOptions() doc = self.client.get(options, resource_uri) item = 'Fault' fault = xml_find(doc, _SOAP_ENVELOPE, item) if fault is not None: LOG.error(_LE('Call to AMT with URI %(uri)s failed: ' 'got Fault %(fault)s'), {'uri': resource_uri, 'fault': fault.text}) raise exception.AMTFailure(cmd='wsman_get') return doc
def wsman_get(self, resource_uri, options=None): """Get target server info :param options: client options :param resource_uri: a URI to an XML schema :returns: XmlDoc object :raises: AMTFailure if get unexpected response. :raises: AMTConnectFailure if unable to connect to the server. """ if options is None: options = pywsman.ClientOptions() doc = self.client.get(options, resource_uri) item = 'Fault' fault = xml_find(doc, _SOAP_ENVELOPE, item) if fault is not None: LOG.error( _LE('Call to AMT with URI %(uri)s failed: ' 'got Fault %(fault)s'), { 'uri': resource_uri, 'fault': fault.text }) raise exception.AMTFailure(cmd='wsman_get') return doc
def _set_boot_device_order(node, boot_device): """Set boot device order configuration of AMT Client. :param node: a node object :param boot_device: the boot device :raises: AMTFailure :raises: AMTConnectFailure """ amt_common.awake_amt_interface(node) client = amt_common.get_wsman_client(node) device = amt_common.BOOT_DEVICES_MAPPING[boot_device] doc = _generate_change_boot_order_input(device) method = 'ChangeBootOrder' options = pywsman.ClientOptions() options.add_selector('InstanceID', 'Intel(r) AMT: Boot Configuration 0') try: client.wsman_invoke(options, resource_uris.CIM_BootConfigSetting, method, doc) except (exception.AMTFailure, exception.AMTConnectFailure) as e: with excutils.save_and_reraise_exception(): LOG.exception( _LE("Failed to set boot device %(boot_device)s for " "node %(node_id)s with error: %(error)s."), { 'boot_device': boot_device, 'node_id': node.uuid, 'error': e }) else: LOG.info( _LI("Successfully set boot device %(boot_device)s for " "node %(node_id)s"), { 'boot_device': boot_device, 'node_id': node.uuid })
def _enable_boot_config(node): """Enable boot configuration of AMT Client. :param node: a node object :raises: AMTFailure :raises: AMTConnectFailure """ amt_common.awake_amt_interface(node) client = amt_common.get_wsman_client(node) method = 'SetBootConfigRole' doc = _generate_enable_boot_config_input() options = pywsman.ClientOptions() options.add_selector('Name', 'Intel(r) AMT Boot Service') try: client.wsman_invoke(options, resource_uris.CIM_BootService, method, doc) except (exception.AMTFailure, exception.AMTConnectFailure) as e: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Failed to enable boot config for node " "%(node_id)s with error: %(error)s."), {'node_id': node.uuid, 'error': e}) else: LOG.info(_LI("Successfully enabled boot config for node %(node_id)s."), {'node_id': node.uuid})