UNPKG

amt-manager-test

Version:

Intel AMT Management Tool - Control power states of AMT-enabled devices

265 lines (264 loc) 11.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AMTManager = exports.PowerState = void 0; const node_fetch_1 = __importDefault(require("node-fetch")); const https_1 = __importDefault(require("https")); const http_1 = __importDefault(require("http")); const dns_1 = __importDefault(require("dns")); const util_1 = require("util"); const crypto_1 = __importDefault(require("crypto")); const digest_auth_1 = require("./digest-auth"); const lookup = (0, util_1.promisify)(dns_1.default.lookup); var PowerState; (function (PowerState) { PowerState[PowerState["PowerOn"] = 2] = "PowerOn"; PowerState[PowerState["PowerOff"] = 8] = "PowerOff"; PowerState[PowerState["Reset"] = 10] = "Reset"; })(PowerState || (exports.PowerState = PowerState = {})); class AMTManager { constructor(config) { this.baseUrl = ''; this.agent = null; this.config = { port: 16992, protocol: 'http', timeout: 5000, retries: 3, verifySSL: false, forceIPv4: true, ...config }; this.resolvedHost = config.host; this.auth = Buffer.from(`${config.username}:${config.password}`).toString('base64'); } async resolveHost() { try { const result = await lookup(this.config.host, { family: this.config.forceIPv4 ? 4 : 0, hints: this.config.forceIPv4 ? dns_1.default.ADDRCONFIG : 0 }); this.resolvedHost = result.address; console.log(`Resolved host ${this.config.host} to ${this.resolvedHost}`); const port = this.config.port; const protocol = this.config.protocol; this.baseUrl = `${protocol}://${this.resolvedHost}:${port}/wsman`; // Configure agent based on protocol if (protocol === 'https') { this.agent = new https_1.default.Agent({ rejectUnauthorized: this.config.verifySSL, keepAlive: true, timeout: this.config.timeout, ciphers: 'ALL', secureProtocol: 'TLSv1_2_method', family: this.config.forceIPv4 ? 4 : undefined }); } else { this.agent = new http_1.default.Agent({ keepAlive: true, timeout: this.config.timeout, family: this.config.forceIPv4 ? 4 : undefined }); } } catch (error) { console.error('Failed to resolve host:', error); throw new Error(`Failed to resolve host ${this.config.host}: ${error instanceof Error ? error.message : 'Unknown error'}`); } } async makeRequest(action, body, retryCount = 0) { // Ensure host is resolved before making request if (!this.baseUrl || !this.agent) { await this.resolveHost(); } const digestHeader = await (0, digest_auth_1.getDigestHeader)({ url: this.baseUrl, username: this.config.username, password: this.config.password, method: 'POST', maxRetries: this.config.retries }); const requestOptions = { method: 'POST', headers: { 'Authorization': digestHeader, 'Content-Type': 'application/soap+xml;charset=UTF-8', 'SOAPAction': action, 'User-Agent': 'Intel AMT Client', 'Accept': '*/*', 'Connection': 'keep-alive' }, body, agent: this.agent, timeout: this.config.timeout }; try { const response = await (0, node_fetch_1.default)(this.baseUrl, requestOptions); if (!response.ok) { const responseText = await response.text(); throw new Error(`AMT request failed: ${response.status} ${response.statusText}\nResponse: ${responseText}`); } return response; } catch (error) { const systemError = error; // Handle specific TLS/connection errors if (systemError.code === 'ECONNRESET' || systemError.code === 'ETIMEDOUT' || systemError.code === 'ECONNREFUSED' || systemError.type === 'system') { if (retryCount < this.config.retries) { console.log(`Connection attempt ${retryCount + 1} failed:`, { error: systemError.message, code: systemError.code, host: this.resolvedHost, port: this.config.port }); // Wait before retrying (exponential backoff) const delay = Math.pow(2, retryCount) * 1000; console.log(`Waiting ${delay}ms before retry...`); await new Promise(resolve => setTimeout(resolve, delay)); // Try to resolve host again before retry await this.resolveHost(); return this.makeRequest(action, body, retryCount + 1); } throw new Error(`Failed to connect to AMT device after ${this.config.retries} attempts. ` + `Last error: ${systemError.message} (${this.resolvedHost}:${this.config.port})`); } throw error; } } createPowerStateChangeRequest(powerState) { return `<?xml version="1.0" encoding="utf-8"?> <Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd" xmlns="http://www.w3.org/2003/05/soap-envelope"> <Header> <a:Action>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService/RequestPowerStateChange</a:Action> <a:To>/wsman</a:To> <w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService</w:ResourceURI> <a:MessageID>1</a:MessageID> <a:ReplyTo> <a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address> </a:ReplyTo> <w:OperationTimeout>PT60S</w:OperationTimeout> </Header> <Body> <r:RequestPowerStateChange_INPUT xmlns:r="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService"> <r:PowerState>${powerState}</r:PowerState> <r:ManagedElement> <Address xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing">http://schemas.xmlsoap.org/ws/2004/08/addressing</Address> <ReferenceParameters xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"> <ResourceURI xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd">http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</ResourceURI> <SelectorSet xmlns="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"> <Selector Name="CreationClassName">CIM_ComputerSystem</Selector> <Selector Name="Name">ManagedSystem</Selector> </SelectorSet> </ReferenceParameters> </r:ManagedElement> </r:RequestPowerStateChange_INPUT> </Body> </Envelope>`; } createGetPowerStateRequest() { return `<?xml version="1.0" encoding="utf-8"?> <Envelope xmlns="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:w="http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"> <Header> <a:To>/wsman</a:To> <a:Action>http://schemas.xmlsoap.org/ws/2004/09/transfer/Get</a:Action> <a:MessageID>uuid:${crypto_1.default.randomUUID()}</a:MessageID> <a:ReplyTo> <a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address> </a:ReplyTo> <w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_AssociatedPowerManagementService</w:ResourceURI> <w:SelectorSet> <w:Selector Name="UserOfService"> <a:EndpointReference> <a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address> <a:ReferenceParameters> <w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ComputerSystem</w:ResourceURI> <w:SelectorSet> <w:Selector Name="CreationClassName">CIM_ComputerSystem</w:Selector> <w:Selector Name="Name">ManagedSystem</w:Selector> </w:SelectorSet> </a:ReferenceParameters> </a:EndpointReference> </w:Selector> <w:Selector Name="ServiceProvided"> <a:EndpointReference> <a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address> <a:ReferenceParameters> <w:ResourceURI>http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService</w:ResourceURI> <w:SelectorSet> <w:Selector Name="CreationClassName">CIM_PowerManagementService</w:Selector> <w:Selector Name="Name">Intel(r) AMT Power Management Service</w:Selector> <w:Selector Name="SystemCreationClassName">CIM_ComputerSystem</w:Selector> <w:Selector Name="SystemName">Intel(r) AMT</w:Selector> </w:SelectorSet> </a:ReferenceParameters> </a:EndpointReference> </w:Selector> </w:SelectorSet> </Header> <Body/> </Envelope>`; } async changePowerState(powerState) { try { const response = await this.makeRequest('http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_PowerManagementService/RequestPowerStateChange', this.createPowerStateChangeRequest(powerState)); const text = await response.text(); // Check if the response contains a success indicator return text.includes('ReturnValue>0</'); } catch (error) { console.error('AMT power state change failed:', error); throw error; } } async powerOn() { return this.changePowerState(PowerState.PowerOn); } async powerOff() { return this.changePowerState(PowerState.PowerOff); } async reset() { return this.changePowerState(PowerState.Reset); } async getPowerState() { try { const response = await this.makeRequest('http://schemas.dmtf.org/wbem/wscim/1/wsman/Enumerate', this.createGetPowerStateRequest()); const text = await response.text(); // Extract power state from response using CIM namespace const match = text.match(/<g:PowerState>(\d+)<\/g:PowerState>/); return match ? parseInt(match[1], 10) : -1; } catch (error) { console.error('Failed to get power state:', error); throw error; } } async testConnection() { try { // Try to get power state as a connection test await this.getPowerState(); return true; } catch (error) { const systemError = error; console.error('Connection test failed:', { error: systemError.message, code: systemError.code, type: systemError.type }); return false; } } } exports.AMTManager = AMTManager;