{"version":3,"sources":["../../../packages/core/shared/cluster-inventory/cluster-inventory-cache.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAMpD,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAGrG;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,WAAW,CAAC,gBAAgB,EAAE,oBAAoB,EAAE,sBAAsB,CAAC;IAYtG,OAAO,CAAC,UAAU;IAX9B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAsC;IAC7D,OAAO,CAAC,MAAM,CAAC,aAAa,CAAK;IAEjC,OAAO,CAAC,oBAAoB,CAAuB;IAEnD;;;;;OAKG;gBACiB,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,kBAAkB;IAaxE;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAItB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAKvB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAKrB;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAuDjB;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B;CAmFtC","file":"cluster-inventory-cache.d.ts","sourcesContent":["import { Observable, of, zip } from 'rxjs';\r\nimport { map, mergeMap } from 'rxjs/operators';\r\nimport { AppContext } from '../../data/app-context';\r\nimport { NodeRequestOptions } from '../../data/node-connection';\r\nimport { PowerShell } from '../../data/powershell';\r\nimport { PowerShellBatchResponseItem } from '../../data/powershell-batch';\r\nimport { PowerShellScripts } from '../../generated/powershell-scripts';\r\nimport { ServerInventoryCache } from '../server-inventory/server-inventory-cache';\r\nimport { SharedCache, SharedCacheOptions } from '../shared-cache';\r\nimport { ClusterInventory, ClusterInventoryData, ClusterInventoryParams } from './cluster-inventory';\r\nimport { ClusterNodeInventory, ClusterNodeState } from './cluster-node-inventory';\r\n\r\n/**\r\n * Cluster Inventory cache class.\r\n */\r\nexport class ClusterInventoryCache extends SharedCache<ClusterInventory, ClusterInventoryData, ClusterInventoryParams> {\r\n    private static uniqueId = '@msft-sme/shell:clusterInventory';\r\n    private static uniqueVersion = 1; // increment this if you make breaking changes\r\n\r\n    private serverInventoryCache: ServerInventoryCache;\r\n\r\n    /**\r\n     * Initializes a new instance of the ClusterInventoryCache class.\r\n     *\r\n     * @param appContext the app context.\r\n     * @param options the option of shared cache.\r\n     */\r\n    constructor(private appContext: AppContext, options?: SharedCacheOptions) {\r\n        super(\r\n            ClusterInventoryCache.uniqueId,\r\n            ClusterInventoryCache.uniqueVersion,\r\n            (params) => this.dataInstanceId(params),\r\n            (instance) => this.dataSerialize(instance),\r\n            (serialized) => this.dataDeserialize(serialized),\r\n            (params) => this.dataQuery(params),\r\n            options);\r\n\r\n        this.serverInventoryCache = new ServerInventoryCache(appContext);\r\n    }\r\n\r\n    /**\r\n     * Defines how to identify the cache entry by params.\r\n     *\r\n     * @param params the server inventory query params.\r\n     * @return the id string.\r\n     */\r\n    private dataInstanceId(params: ClusterInventoryParams): string {\r\n        return params.name;\r\n    }\r\n\r\n    /**\r\n     * Defines how to deserialize the class object from serialized data.\r\n     *\r\n     * @param serialized the serialized string;\r\n     */\r\n    private dataDeserialize(serialized: string): ClusterInventory {\r\n        const inventory: ClusterInventoryData = JSON.parse(serialized);\r\n        return new ClusterInventory(inventory.clusterName, inventory);\r\n    }\r\n\r\n    /**\r\n     * Defines how to serialize the class object to serialized data.\r\n     *\r\n     * @param instance the class instance.\r\n     */\r\n    private dataSerialize(instance: ClusterInventory): string {\r\n        // automatically stripped out class related data.\r\n        return JSON.stringify(instance);\r\n    }\r\n\r\n    /**\r\n     * Defines how to collect the cluster inventory data.\r\n     *\r\n     * @param params the server inventory query params.\r\n     * @return the Observable of ClusterInventory data.\r\n     */\r\n    private dataQuery(params: ClusterInventoryParams): Observable<ClusterInventory> {\r\n        // options object will be updated at the http layer, so it makes an each copy before passing.\r\n        const options = { ...params.requestOptions };\r\n        const clusterPsSession = this.appContext.powerShell.createSession(params.name, null, options);\r\n        const clusterNodePsSession = this.appContext.powerShell.createSession(params.name, null, options);\r\n\r\n        return zip(\r\n            this.appContext.powerShell.run(clusterPsSession, PowerShell.createCommand(PowerShellScripts.Get_ClusterInventory)),\r\n            this.appContext.powerShell.run(clusterNodePsSession, PowerShell.createCommand(PowerShellScripts.Get_ClusterNodes)))\r\n            .pipe(map(([cluster, nodes]) => {\r\n                const inventory = new ClusterInventory(params.name);\r\n\r\n                if (!cluster || !cluster.results || cluster.results.length === 0) {\r\n                    return inventory;\r\n                }\r\n\r\n                if (!nodes || !nodes.results || nodes.results.length === 0) {\r\n                    return inventory;\r\n                }\r\n\r\n                const nodesResult = nodes.results[0];\r\n                const clusterResult = cluster.results[0];\r\n                const nodesResultLower = {};\r\n\r\n                // make node name and fqdn lower casing.\r\n                inventory.isClusterCmdletAvailable = clusterResult.isClusterCmdletAvailable;\r\n                clusterResult.fqdn = clusterResult.fqdn?.toLowerCase();\r\n                inventory.fqdn = clusterResult.fqdn;\r\n                inventory.currentClusterNode = clusterResult.currentClusterNode.toLowerCase();\r\n                inventory.nodeNames = [];\r\n                for (const node in nodesResult) {\r\n                    if (node && nodesResult[node] && nodesResult[node].name) {\r\n                        nodesResult[node].name = nodesResult[node].name.toLowerCase();\r\n                        nodesResult[node].fullyQualifiedDomainName = nodesResult[node].fullyQualifiedDomainName.toLowerCase();\r\n                        inventory.nodeNames.push(nodesResult[node].name);\r\n                        const nodeLower = node.toLowerCase();\r\n                        nodesResultLower[nodeLower] = nodesResult[node];\r\n                    }\r\n                }\r\n\r\n                inventory.isS2dEnabled = clusterResult.isS2DEnabled;\r\n                inventory.isTsdbEnabled = clusterResult.isTsdbEnabled;\r\n                inventory.isBritannicaEnabled = clusterResult.isBritannicaEnabled;\r\n                inventory.isBritannicaVirtualMachineEnabled = clusterResult.isBritannicaVirtualMachineEnabled;\r\n                inventory.isBritannicaVirtualSwitchEnabled = clusterResult.isBritannicaVirtualSwitchEnabled;\r\n                inventory.isClusterHealthCmdletAvailable = clusterResult.isClusterHealthCmdletAvailable;\r\n                inventory.isHyperVPowershellInstalled = false;\r\n                inventory.isHyperVRoleInstalled = false;\r\n                inventory.isManagementToolsAvailable = false;\r\n                inventory.nodeMap = nodesResultLower;\r\n                return inventory;\r\n            }),\r\n                mergeMap(inventory => this.queryClusterNodeInventories(inventory, params)));\r\n    }\r\n\r\n    /**\r\n     * Defines how to collect the cluster node-server inventory data.\r\n     * @param clusterInventory  the initial cluster inventory query params.\r\n     * @param params the parameters for cluster inventory query.\r\n     * @return Observable<ClusterInventory> the Observable of ClusterInventory data.\r\n     */\r\n    private queryClusterNodeInventories(clusterInventory: ClusterInventory, params: ClusterInventoryParams): Observable<ClusterInventory> {\r\n        if (!clusterInventory.nodeNames || clusterInventory.nodeNames.length === 0) {\r\n            return of(clusterInventory);\r\n        }\r\n\r\n        // Can only get server inventory data from live nodes\r\n        const nodeMap = clusterInventory.nodeMap;\r\n        const liveNodeNames: string[] = [];\r\n        for (const key in nodeMap) {\r\n            if (nodeMap[key].state === ClusterNodeState.Up || nodeMap[key].state === ClusterNodeState.Paused) {\r\n                liveNodeNames.push(nodeMap[key].fullyQualifiedDomainName);\r\n            }\r\n        }\r\n\r\n        const authToken = this.appContext.authorizationManager.getSavedNodeToken(params.name);\r\n        let options: NodeRequestOptions = {};\r\n\r\n        if (params.requestOptions) {\r\n            options = <NodeRequestOptions>{ ...params.requestOptions };\r\n\r\n            if (options.authToken == null && authToken) {\r\n                // override auth token if not there.\r\n                options.authToken = authToken;\r\n            }\r\n        } else if (authToken) {\r\n            options.authToken = authToken;\r\n        }\r\n\r\n        const errorMessages: string[] = [];\r\n\r\n        const batchSession = this.appContext.powerShell.createBatchSession(liveNodeNames, null, options);\r\n        return this.appContext.powerShell.runBatchSingleCommand(\r\n            batchSession, PowerShell.createCommand(PowerShellScripts.Get_ServerInventory))\r\n            .pipe(map((responseItems: PowerShellBatchResponseItem[]) => {\r\n                for (let index = 0; index < responseItems.length; index++) {\r\n                    const response = responseItems[index];\r\n\r\n                    if (response.error || response.errors) {\r\n                        const message = (response.error && response.error.message) ||\r\n                            (response.errors && response.errors[0] && response.errors[0].message);\r\n                        errorMessages.push(message);\r\n                        continue;\r\n                    }\r\n\r\n                    const currentServer = liveNodeNames[index];\r\n                    const serverNameParts: string[] = currentServer.split('.');\r\n                    const serverName = serverNameParts.length > 1 ? serverNameParts[0] : currentServer;\r\n\r\n                    const data = response.properties;\r\n                    const serverInventory = ServerInventoryCache.createServerInventoryData(serverName, data);\r\n\r\n                    this.serverInventoryCache.save({ name: serverName }, serverInventory);\r\n\r\n                    const inventoryServerName = clusterInventory.nodeMap[clusterInventory.currentClusterNode].name;\r\n\r\n                    if (serverName === inventoryServerName) {\r\n                        clusterInventory.isHyperVPowershellInstalled = serverInventory.isHyperVPowershellInstalled;\r\n                        clusterInventory.isHyperVRoleInstalled = serverInventory.isHyperVRoleInstalled;\r\n                        clusterInventory.isManagementToolsAvailable = serverInventory.isManagementToolsAvailable;\r\n                    }\r\n\r\n                    // combine cluster node inventory and server inventory\r\n                    const clusterNodeInventory = new ClusterNodeInventory(serverName);\r\n                    const clusterServerInventory = clusterInventory.nodeMap[serverName];\r\n\r\n                    Object.assign(clusterNodeInventory, serverInventory);\r\n\r\n                    for (const property in clusterServerInventory) {\r\n                        if (property) {\r\n                            clusterNodeInventory[property] = clusterServerInventory[property];\r\n                        }\r\n                    }\r\n\r\n                    clusterInventory.nodeMap[serverName] = clusterNodeInventory;\r\n                }\r\n\r\n                if (errorMessages.length > 0) {\r\n                    clusterInventory.clusterErrors = errorMessages;\r\n                }\r\n\r\n                return clusterInventory;\r\n            }));\r\n    }\r\n}\r\n"]}