// Copyright (C) 2016 Intel Corporation var WSManRequests = { GET : "http://schemas.xmlsoap.org/ws/2004/09/transfer/Get", PUT : "http://schemas.xmlsoap.org/ws/2004/09/transfer/Put", DELETE : "http://schemas.xmlsoap.org/ws/2004/09/transfer/Delete", CREATE : "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create", RELEASE : "http://schemas.xmlsoap.org/ws/2004/09/enumeration/Release", IDENTITY : "http://schemas.xmlsoap.org/ws/2005/05/identity" }; var namespacesURI = { RESOURCE_URI_PREFIX_AMT : "http://intel.com/wbem/wscim/1/amt-schema/1/", RESOURCE_URI_PREFIX_CIM : "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/", RESOURCE_URI_PREFIX_IPS : "http://intel.com/wbem/wscim/1/ips-schema/1/", RESOURCE_URI_WSMAN : "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd", RESOURCE_URI_ADDRESSING : "http://schemas.xmlsoap.org/ws/2004/08/addressing", RESOURCE_URI_ENVELOPE : "http://www.w3.org/2003/05/soap-envelope", RESOURCE_URI_ENUMERATION : "http://schemas.xmlsoap.org/ws/2004/09/enumeration", RESOURCE_URI_COMMON : "http://schemas.dmtf.org/wbem/wscim/1/common" }; /** * XML resources */ var resources = { WSManRequestXmlString : "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymoushttp://schemas.xmlsoap.org/ws/2004/09/transfer/Get153600PT60.000S", referenceXmlString : "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous", enumerateXmlString : "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymoushttp://schemas.xmlsoap.org/ws/2004/09/enumeration/Enumerate153600uuid:00000000-0000-0000-0000-000000000000PT60.000S", pullXmlString: "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymoushttp://schemas.xmlsoap.org/ws/2004/09/enumeration/Pull153600PT60.000S00000000-0000-0000-0000-00000000000020" }; //Converts WS-Man class object to reference xml. function objectToEPR(classObj) { if (!classObj.className) { return null; } var refXml = new XmlDocument(resources.referenceXmlString); var resUri = getResourceUriByName(classObj.className); if (!resUri) { return null; } refXml.setNode(namespacesURI.RESOURCE_URI_WSMAN,"ResourceURI", resUri); if (classObj.selectors) { refXml.setSelectoresNode(classObj.selectors); } return refXml; } //Converts WS-Man class object to xml. function objectToStringXml(classObj) { if (!classObj.className) { return null; } var resUri = getResourceUriByName(classObj.className); if (!resUri) { return null; } var xmlStr = ""; for (var p in classObj.properties) { if (classObj.properties[p] instanceof Array) { for (var ii = 0; ii < classObj.properties[p].length; ++ii) { xmlStr += "" + classObj.properties[p][ii] + ""; } } else { xmlStr += "" + classObj.properties[p] + ""; } } xmlStr += ""; return xmlStr; } //Converts the method input parameters object to a WS-Man input parameters xml string function objectParamToXml(className, methodName, params) { if (!methodName) { return null; } var xmlStr = ""; for (var p in params) { if (params[p] instanceof Array) { for (var ii = 0; ii < params[p].length; ++ii) { xmlStr += paramToElementNode(p, params[p][ii]); } } else { xmlStr += paramToElementNode(p, params[p]); } } xmlStr += ""; return xmlStr; } function paramToElementNode(key, val) { if (!key || !val) { return null; } var xmlStr = ""; if (val.className) { xmlStr += objectToEPR(val).getNode(namespacesURI.RESOURCE_URI_ADDRESSING,"EndpointReference").innerHTML; } else { xmlStr += val; } xmlStr += ""; return xmlStr; } //Converts class reference xml to object. function EPRtoObject(ref) { var obj = {}; var refXml = new XmlDocument(ref); if (!refXml.getNode(namespacesURI.RESOURCE_URI_ADDRESSING, "Address")) { return null; } var resUriNode = refXml.getNode(namespacesURI.RESOURCE_URI_WSMAN, "ResourceURI"); if (!resUriNode) { return null; } var pos = resUriNode.innerHTML.lastIndexOf("/"); obj.className = resUriNode.innerHTML.slice(pos + 1); var childs = refXml.getNode(namespacesURI.RESOURCE_URI_WSMAN, "SelectorSet").childNodes; if (childs.length) { obj.selectors = {}; // selectorSetChilds.forEach(function(element) { // var key = element.getAttribute("Name"); // obj.selectors[key] = element.innerHTML; // }); for (var ii = 0; ii < childs.length; ++ii) { var key = childs[ii].getAttribute("Name"); obj.selectors[key] = childs[ii].innerHTML; } } return obj; } //Converts the AMT's WS-Man response body xml content to JavaScript Object containing its properties. function wsXmlToObject(propertiesNodes) { if (!propertiesNodes) return null; var res = {}; /*propertiesNodes.forEach(function(element) { if (res[element.localName] && res[element.localName] instanceof Array) res[element.localName].push(element.innerHTML); else if (res[element.localName]) res[element.localName] = [res[element.localName] , element.innerHTML]; else if (element.firstElementChild) { var obj = EPRtoObject(element); if (!obj) obj = wsXmlToObject(element.childNodes); res[element.localName] = obj; } else res[element.localName] = element.innerHTML; });*/ for (var ii = 0; ii < propertiesNodes.length; ++ii) { if (res[propertiesNodes[ii].localName] && res[propertiesNodes[ii].localName] instanceof Array) { res[propertiesNodes[ii].localName].push(propertiesNodes[ii].innerHTML); } else if (res[propertiesNodes[ii].localName]) { res[propertiesNodes[ii].localName] = [res[propertiesNodes[ii].localName] , propertiesNodes[ii].innerHTML]; } else if (propertiesNodes[ii].firstElementChild) { var obj = EPRtoObject(propertiesNodes[ii]); if (!obj) obj = wsXmlToObject(propertiesNodes[ii].childNodes); res[propertiesNodes[ii].localName] = obj; } else { res[propertiesNodes[ii].localName] = propertiesNodes[ii].innerHTML; } } return res; } //Converts items xml to an array of WS-Man objects function wsEnumXmlToObjectArray(itemsNode) { var arr = []; for (var ii = 0; ii < itemsNode.length; ++ii) { var res = { className : itemsNode[ii].localName }; res.properties = wsXmlToObject(itemsNode[ii].childNodes); arr.push(res); } return arr; } //Returns the resource uri of the WS-Man class function getResourceUriByName(className) { var ns = { "CIM" : "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/", "AMT" : "http://intel.com/wbem/wscim/1/amt-schema/1/", "IPS" : "http://intel.com/wbem/wscim/1/ips-schema/1/" }; var pos = className.indexOf("_"); if (pos === -1) { return null; } var prefix = className.slice(0, pos); if (!ns[prefix]) { return null; } return ns[prefix] + className; } /** * Creates an xml document class * @param {String\Document} The xml code of the document to manipulate * @constructor */ function XmlDocument(xml) { var doc = {}; if (typeof xml === "string") { doc = (new DOMParser()).parseFromString(xml, "text/xml"); } else { doc = xml; } /** * Sets the inner text of an XML node * @param {String} The node's namespace * @param {String} The node's name * @param {String} The text to insert */ this.setNode = function (ns, node, value) { var nodeList = getNodeListObject(ns,node); if (!nodeList.length) { return; } nodeList[0].innerHTML = value; } /** * Fills the SelectorSet node of the WS-MAN command * @param {Object} An object containing the selectors to insert. */ this.setSelectoresNode = function (selectors) { for (var key in selectors) { //Create new node var selectorNode = doc.createElement("w:Selector"); //Add node attribute selectorNode.setAttribute("Name", key); //Insert text to node var textNode = {}; if (selectors[key].className) { //Selector is a reference to another class var epr = objectToEPR(selectors[key]); textNode = epr.getNode(namespacesURI.RESOURCE_URI_ADDRESSING,"EndpointReference"); } else { textNode = doc.createTextNode(selectors[key]); } selectorNode.appendChild(textNode); //Insert node to SelectorSet node getNodeListObject(namespacesURI.RESOURCE_URI_WSMAN,"SelectorSet")[0].appendChild(selectorNode); } } /** * Returns the first node in the xml object that matches the provided name. * @param {String} The node's namespace * @param {String} The node's name * @retruns {Node} The first instance of a node with the provided name */ this.getNode = function(ns, node) { var nodeList = getNodeListObject(ns,node); if (!nodeList.length) { return null; } return nodeList[0]; } /** * Returns a list of all nodes in the xml object that match the provided name. * @param {String} The node's namespace * @param {String} The node's name * @retruns {NodeList} The list of all nodes with the provided name */ this.getNodeList = function(ns, node) { var nodeList = getNodeListObject(ns,node); if (!nodeList.length) { return null; } return nodeList; } /** * Returns the xml object * @returns {Document} JavaScript xml document */ this.getXml = function() { return doc; } /** * Prints the document to the console log */ this.print = function() { console.log("XmlDocument:"); console.log(doc); } function getNodeListObject(ns,nodeName) { return doc.getElementsByTagNameNS(ns,nodeName); } } /** * Create an AMT manager * @constructor */ var AMTConnection = (function() { var connected = false; var address = window.location.origin + "/wsman"; /** * User name * @type {String} */ var user = null; /** * Password * @type {String} */ var password = null; /** * AMT FW version * @type {Object} */ var FWVersion = { /** * AMT FW version number * @type {Number} */ versionNumber : null, /** * Compare the AMT version to the argument * @param {String/Number} verCmp string of version to compare * @returns {Boolean} true if version */ compare : function(verCmp) { if (isNaN(verCmp)) throw "version.compare(): Argument is not a number"; if (this.versionNumber > verCmp) return 1; if (this.versionNumber < verCmp) return -1; //Equals return 0; } }; var ajax = new AjaxManager(); // - - - - - - - - - - - WS-Man requests - - - - - - - - - - - var performGet = function (params) { if (!params.className) { return; } var getRequestXml = new XmlDocument(resources.WSManRequestXmlString); getRequestXml.setNode(namespacesURI.RESOURCE_URI_ADDRESSING,"Action",WSManRequests.GET); ajax.sendRequest(getRequestXml, params); } var performPut = function (params) { if (params.className !== params.input.className) { return; } if (!params.className || !params.input.properties) { return; } var putRequestXml = new XmlDocument(resources.WSManRequestXmlString); putRequestXml.setNode(namespacesURI.RESOURCE_URI_ADDRESSING,"Action",WSManRequests.PUT); putRequestXml.setNode(namespacesURI.RESOURCE_URI_ENVELOPE,"Body", objectToStringXml(params.input)); ajax.sendRequest(putRequestXml, params); } var performInvokeMethod = function (params) { if (!params.className || !params.method) { return; } var invokeXml = new XmlDocument(resources.WSManRequestXmlString); var resourceUri = getResourceUriByName(params.className); if (!resourceUri) { return; } invokeXml.setNode(namespacesURI.RESOURCE_URI_ADDRESSING,"Action", resourceUri + "/" + params.method); if (!params.input) { params.input = {}; } invokeXml.setNode(namespacesURI.RESOURCE_URI_ENVELOPE,"Body", objectParamToXml(params.className, params.method, params.input)); ajax.sendRequest(invokeXml, params); } var performEnumerate = function (params) { if (!params.className) { return; } var enumerateXml = new XmlDocument(resources.enumerateXmlString); ajax.sendEnumerateRequest(enumerateXml, params); } var performDelete = function (params) { if (!params.className) { return; } var deleteXml = new XmlDocument(resources.WSManRequestXmlString); deleteXml.setNode(namespacesURI.RESOURCE_URI_ADDRESSING,"Action",WSManRequests.DELETE); ajax.sendRequest(deleteXml, params); } /** * Creates an Ajax manager class that will execute and handle the ajax calls * @constructor */ function AjaxManager() { var uuid = 1; this.sendRequest = function (xmlDoc, params) { var resUri = getResourceUriByName(params.className); if (!resUri) { return; } xmlDoc.setNode(namespacesURI.RESOURCE_URI_ADDRESSING, "MessageID", uuid++); xmlDoc.setNode(namespacesURI.RESOURCE_URI_ADDRESSING, "To", address); xmlDoc.setNode(namespacesURI.RESOURCE_URI_WSMAN, "ResourceURI", resUri); if (params.selectors) xmlDoc.setSelectoresNode(params.selectors); send(xmlDoc, params, parseResponse); } this.sendEnumerateRequest = function (xmlDoc, params) { var resUri = getResourceUriByName(params.className); if (!resUri) { return; } xmlDoc.setNode(namespacesURI.RESOURCE_URI_ADDRESSING, "To", address); xmlDoc.setNode(namespacesURI.RESOURCE_URI_WSMAN, "ResourceURI", resUri); if (params.selectors) { xmlDoc.setSelectoresNode(params.selectors); } send(xmlDoc, params, parseEnumerationResponse); } function send(xmlDoc, params, func_, res ) { var xdr = new XMLHttpRequest(); xdr.open("POST", "/wsman", true, user, password); xdr.setRequestHeader("Content-Type", "application/soap+xml;charset=UTF-8"); xdr.timeout = 15000; xdr.onload = function () { func_(xdr.responseXML, xdr.status, params, res); }; xdr.onerror = function () { console.log("Failed to perform the request. Status:" + xdr.status.toString()); if (params.onError instanceof Function) { var failure = { status : xdr.status, } params.onError(failure, params.args); } }; xdr.ontimeout = function () { console.log("Failed to perform the request. Received Time-out. Status: " + xdr.status.toString()); if (params.onError instanceof Function) { var failure = { status : xdr.status, reason : "Received Time-out." } params.onError(failure, params.args); } }; xdr.send("" + xmlDoc.getNode(namespacesURI.RESOURCE_URI_ENVELOPE,"Envelope").outerHTML); } function parseResponse(recDataXml, status, params) { var res = { className : params.className }; if (status !== 200) { handleFault(status, params, recDataXml); return; } if (params.onSuccess instanceof Function) { var xmlDoc = new XmlDocument(recDataXml); var body = xmlDoc.getNode(namespacesURI.RESOURCE_URI_ENVELOPE, "Body"); if (body.firstElementChild) { res.properties = wsXmlToObject(body.firstElementChild.childNodes); } if (params.method) { res.method = params.method; } Object.preventExtensions(res.properties); params.onSuccess(res, params.args); } } function parseEnumerationResponse(recDataXml, status, params, res) { if (status !== 200) { handleFault(status, params, recDataXml); return; } var enumerationResXml = new XmlDocument(recDataXml); var pullXml = new XmlDocument(resources.pullXmlString); pullXml.setNode(namespacesURI.RESOURCE_URI_ADDRESSING,"To",address); pullXml.setNode(namespacesURI.RESOURCE_URI_WSMAN,"ResourceURI", getResourceUriByName(params.className)); pullXml.setNode(namespacesURI.RESOURCE_URI_ADDRESSING,"MessageID", uuid++); pullXml.setNode(namespacesURI.RESOURCE_URI_ENUMERATION,"EnumerationContext", enumerationResXml.getNode(namespacesURI.RESOURCE_URI_ENUMERATION,"EnumerationContext").innerHTML); send(pullXml, params, parseEnumerationResponse1, res); } function parseEnumerationResponse1(recDataXml, status, params, res) { if (status !== 200) { handleFault(status, params, recDataXml); return; } var pullResXml = new XmlDocument(recDataXml); var enumContext = pullResXml.getNode(namespacesURI.RESOURCE_URI_ENUMERATION,"EnumerationContext"); if (params.onSuccess instanceof Function) { if (res === undefined) { var res = { className : params.className, items : [] }; } var itemsNode = pullResXml.getNode(namespacesURI.RESOURCE_URI_ENUMERATION, "Items"); if (itemsNode.firstElementChild) { var arr = wsEnumXmlToObjectArray(itemsNode.childNodes); for (var ii = 0; ii < arr.length; ++ii) res.items.push(arr[ii]); } if (enumContext) { //Not all instances were pulled parseEnumerationResponse(recDataXml, status, params, res); } else { Object.preventExtensions(res.items); params.onSuccess(res, params.args); } } } function handleFault(status, params, recDataXml) { var failure = { status : status } //Unauthorized if (status === 401) { failure.reason = "Wsman request is unauthorized. HTTP response status code: " + status.toString(); } else if (status === 400 || status === 500) { //Bad request or Internal Server Error var faultXml = new XmlDocument(recDataXml); var reason = faultXml.getNode(namespacesURI.RESOURCE_URI_ENVELOPE,"Reason"); failure.reason = reason.firstChild.innerHTML; } else { failure.reason = "Failed to perform the request. HTTP response status code: " + status.toString(); } if (params.onError instanceof Function) { params.onError(failure, params.args); } } } //AjaxManager return { /** Sets the username the connection will use to authenticate */ setUserName : function (userName) { user = userName;}, /** Sets the password the connection will use to authenticate */ setPassword : function (pass) { password = pass;}, /** * Connects to the WS-Man client. This function must be called before any of the others may be invoked. * @param {info} The object containing the arguments that will be used in this function. * Optional fields in the info object: * * onSuccess: A callback function that will be immediately run once the command is completed successfully. * * onError: A callback function that will be immediately run if the command is not completed successfully. */ connect : function (info) { performGet({ className : "CIM_SoftwareIdentity", onSuccess : function(cimsoftwareidentity) { connected = true; FWVersion.versionNumber = parseFloat(cimsoftwareidentity.properties.VersionString); if (info.onSuccess instanceof Function) {info.onSuccess();} }, onError : function(failure) { connected = false; console.log("Failed to connect to AMT."); console.log(failure.reason); if (info.onError instanceof Function) {info.onError(failure);} }, selectors : { InstanceID : "AMT FW Core Version" } }); }, /** * Performs a WS-Man Get request. The request will not succeed if more than one instance exists of the given * className and not enough selectors have been provided to isolate a single instance. * @param {info} The object containing the arguments that will be used in this function. * Required fields in the info object: * * className: The name of the class to get. * Optional fields in the info object: * * selectors: Any key/value pairs that may be needed to isolate a single instance of the named class. * * onSuccess: A callback function that will be immediately run once the command is completed successfully. * * onError: A callback function that will be immediately run if the command is not completed successfully. * */ get: function (info) { if (connected == false) return; performGet(info); }, /** * Performs a WS-Man Put request. The request will not succeed if more than one instance exists of the given * className and not enough selectors have been provided to isolate a single instance. * @param {info} The object containing the arguments that will be used in this function. * Required fields in the info object: * * className: The name of the class to put. * * input: A set of key/value pairs containing the set of properties that will be saved in the selected object. * This must include a "className" key with a value equal to the "className" field above. * Optional fields in the info object: * * selectors: Any key/value pairs that may be needed to isolate a single instance of the named class. * * onSuccess: A callback function that will be immediately run once the command is completed successfully. * * onError: A callback function that will be immediately run if the command is not completed successfully. */ put: function (info) { if (connected == false) return; performPut(info); }, /** * Invokes a method within AMT. The request will not succeed if more than one instance exists of the given * className and not enough selectors have been provided to isolate a single instance. * @param {info} The object containing the arguments that will be used in this function. * Required fields in the info object: * * className: The name of the class containing the method you want to invoke. * * method: The name of the method that you want to invoke. * * input: The set of key/value pairs containing the set of parameters that will be sent to the invoked method. * The key half of each pair must be the same as the name of the parameter. * Optional fields in the info object: * * selectors: Any key/value pairs that may be needed to isolate a single instance of the named class. * * onSuccess: A callback function that will be immediately run once the command is completed successfully. * * onError: A callback function that will be immediately run if the command is not completed successfully. */ invokeMethod: function (info) { if (connected == false) return; performInvokeMethod(info); }, /** * Performs a WS-Man Get request that retrieves all instances of the requested class. * @param {info} The object containing the arguments that will be used in this function. * Required fields in the info object: * * className: The name of the class to get. * Optional fields in the info object: * * selectors: Any key/value pairs that you want to use to narrow down the list of retrieved objects. * * onSuccess: A callback function that will be immediately run once the command is completed successfully. * * onError: A callback function that will be immediately run if the command is not completed successfully. * */ enumerate: function (info) { if (connected == false) return; performEnumerate(info); }, /** * Performs a WS-Man Delete request that deletes an object. * @param {info} The object containing the arguments that will be used in this function. The request will not succeed * if more than one instance exists of the given className and not enough selectors have been provided to isolate a single instance. * Required fields in the info object: * * className: The class of the object that you want to delete. * Optional fields in the info object: * * selectors: Any key/value pairs that may be needed to isolate a single instance of the named class. * * onSuccess: A callback function that will be immediately run once the command is completed successfully. * * onError: A callback function that will be immediately run if the command is not completed successfully. */ delete : function (info) { if (connected == false) return; performDelete(info); }, /** AMT FW version number */ version : FWVersion }; })();