/*############################################################################## Copyright (C) 2011 HPCC Systems. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ############################################################################## */ ///////////////////////////////////////////////////////////////////////////// // // ComputerPicker.cpp : implementation file // ///////////////////////////////////////////////////////////////////////////// #include "computerpicker.hpp" #include "XMLTags.h" /*static*/MapStrToChar CComputerPicker::s_prefixMap; //--------------------------------------------------------------------------- // CComputerPicker //--------------------------------------------------------------------------- CComputerPicker::CComputerPicker() { if (s_prefixMap.empty()) CreateComponentTypePrefixMap(); } CComputerPicker::~CComputerPicker() { } //--------------------------------------------------------------------------- // SetRootNode //--------------------------------------------------------------------------- void CComputerPicker::SetRootNode(const IPropertyTree* pNode) { m_pRootNode = pNode; CreateComputerFilterTree(); Refresh(); } //generate a map containing unique prefix char based on component types, which can either //be process names, "Domain", "ComputerType" or "Subnet". // /*static*/ void CComputerPicker::CreateComponentTypePrefixMap() { s_prefixMap["RoxieCluster" ] = 'C'; s_prefixMap["DaliServerProcess"] = 'D'; s_prefixMap["EspProcess" ] = 'E'; s_prefixMap["HoleCluster" ] = 'H'; s_prefixMap["LDAPServerProcess"] = 'L'; s_prefixMap["PluginProcess" ] = 'P'; s_prefixMap["SpareProcess" ] = 'S'; s_prefixMap["ThorCluster" ] = 'T'; s_prefixMap["DfuServerProcess" ] = 'U'; s_prefixMap["SybaseProcess" ] = 'Y'; s_prefixMap["EclAgentProcess" ] = 'a'; s_prefixMap["EclServerProcess" ] = 'c'; s_prefixMap["Domain" ] = 'd'; s_prefixMap["ComputerType" ] = 'e'; s_prefixMap["FTSlaveProcess" ] = 'f'; s_prefixMap["EspService" ] = 's'; s_prefixMap["Topology" ] = 't'; s_prefixMap["Subnet" ] = 'u'; } //returns a unique prefix char based on a component type, which can either //be a process name, "Domain", "ComputerType" or "Subnet". // /*static*/ char CComputerPicker::GetPrefixForComponentType(const char* componType) { char prefix = ' '; MapStrToChar::const_iterator i = s_prefixMap.find(componType); if (i != s_prefixMap.end()) prefix = (*i).second; return prefix; } const char* CComputerPicker::GetComponentTypeForPrefix(char chPrefix) { const char* szCompType = ""; //this is inefficient and should be replaced by another map from prefix //to component type. However, this is only used for tooltip where time //is not an issue. MapStrToChar::const_iterator i = s_prefixMap.begin(); MapStrToChar::const_iterator iEnd = s_prefixMap.end(); for (; i != iEnd; i++) if ((*i).second == chPrefix) { szCompType = (*i).first.c_str(); break; } return szCompType; } //--------------------------------------------------------------------------- // ResetUsageMap //--------------------------------------------------------------------------- void CComputerPicker::ResetUsageMap() { MapStrToStrArray::iterator i = m_usageMap.begin(); MapStrToStrArray::iterator iEnd = m_usageMap.end(); //empty all vectors keyed by all computers for (; i != iEnd; i++) { stringarray& usage = (*i).second; stringarray::iterator i2 = usage.begin(); stringarray::iterator iEnd2 = usage.end(); for (; i2 != iEnd2; i2++) { std::string& component = *i2; const char* szComponent = component.c_str(); if (szComponent && *szComponent != '+') component[0] = '+'; } } } bool CComputerPicker::GetUsage(const char* szComputer, StringBuffer& sUsage, bool bIncludeComponentType) const { MapStrToStrArray::const_iterator iUsage = m_usageMap.find(szComputer); bool bInclude = false;//computer isn't explicitly requested for by the user bool bExclude = false;//computer isn't explicitly excluded by the user if (iUsage != m_usageMap.end()) { const stringarray& usage = (*iUsage).second; stringarray::const_iterator i2 = usage.begin(); stringarray::const_iterator iEnd2 = usage.end(); for (; i2 != iEnd2; i2++) { const std::string& component = *i2; const char* szComponent = component.c_str(); if (szComponent) { char chStatus = *szComponent++; if (chStatus == 'I') bInclude = true; else if (chStatus == '-') bExclude = true; char chPrefix = *szComponent++; //gather usage anyway even for excluded components //since that needs to be shown in case another component //has explicitly asked this computer to be included //don't show usage info for domains, computer types and subnets if (chPrefix != 'd' && chPrefix != 'e' && chPrefix != 'u') { //if (!sUsage.IsEmpty()) if (sUsage.length() > 0) sUsage.append(bIncludeComponentType ? '\n' : ' '); if (bIncludeComponentType) { StringBuffer sType = GetComponentTypeForPrefix(chPrefix); //if (!sType.IsEmpty()) if (sUsage.length() > 0) { sUsage.append(sType); sUsage.append(" - "); } } sUsage.append(szComponent); //skip status and prefix characters } } } //if this computer has to be excluded because of some component and //then don't use it unless it is explicitly included by the user //for another if (bExclude && bInclude) bExclude = false; } return !bExclude; } //--------------------------------------------------------------------------- // NoteUsage //--------------------------------------------------------------------------- void CComputerPicker::NoteUsage(const char *computer, const char* componType, const char *name, char status/*='+'*/) { if (computer && *computer && componType && *componType && name && *name) { stringarray& usage = m_usageMap[computer]; std::string sName; sName += status; sName += GetPrefixForComponentType(componType); sName += name; name = sName.c_str() + 1; //skip status //insert this entry in the array if not present already stringarray::iterator i = usage.begin(); stringarray::iterator iEnd = usage.end(); for (; i != iEnd; i++) { std::string& component = *i; const char* szCompName = component.c_str(); if (szCompName && *szCompName && !strcmp(szCompName+1, name)) { if (status != '-' || *szCompName != 'I')//don't mark unused if status is 'I' component[0] = status; break; } } if (i == iEnd) usage.push_back(sName); } } //--------------------------------------------------------------------------- // CreateFilterTree //--------------------------------------------------------------------------- void CComputerPicker::CreateComputerFilterTree() { m_pFilterTree.clear(); m_pFilterTree.setown(createPTree("Filter")); IPropertyTree* pComponents= m_pFilterTree->addPropTree("Components", createPTree("Components")); IPropertyTree* pDomains = m_pFilterTree->addPropTree("Domains", createPTree("Domains")); IPropertyTree* pCompTypes = m_pFilterTree->addPropTree("ComputerTypes", createPTree("ComputerTypes")); IPropertyTree* pSubnets = m_pFilterTree->addPropTree("Subnets", createPTree("Subnets")); // Generate computer usage map if (m_pRootNode) { //collect all software components using any computers Owned iter = m_pRootNode->getElements("Software/*"); ForEach(*iter) { IPropertyTree &component = iter->query(); const char* szProcessName = component.queryName(); const char* szCompName = component.queryProp(XML_ATTR_NAME); const bool bThorCluster = strcmp(szProcessName, "ThorCluster")==0; if (bThorCluster || strcmp(szProcessName, "HoleCluster")==0 || strcmp(szProcessName, "RoxieCluster")==0) { Owned instance = component.getElements("*[@computer]"); ForEach(*instance) { const char* szComputer = instance->query().queryProp("@computer"); NoteFilter(pComponents, szProcessName, szCompName, szComputer); NoteUsage(szComputer, szProcessName, szCompName); } } else { Owned instance = component.getElements("Instance"); ForEach(*instance) { const char* szComputer = instance->query().queryProp("@computer"); NoteFilter(pComponents, szProcessName, szCompName, szComputer); NoteUsage(szComputer, szProcessName, szCompName); } const char* szComputer = component.queryProp("@computer"); if (szComputer) { NoteFilter(pComponents, szProcessName, szCompName, szComputer); NoteUsage(szComputer, szProcessName, szCompName); } //if this is a dali server then get backup computer, if any if (strcmp(szProcessName, "DaliServerProcess")==0) { const char* szBackupComputer = component.queryProp("@backupComputer"); NoteFilter(pComponents, szProcessName, szCompName, szBackupComputer); NoteUsage(szBackupComputer, szProcessName, szCompName); } } } iter.setown(m_pRootNode->getElements("Hardware/Domain")); ForEach(*iter) { const char* szDomain = iter->query().queryProp(XML_ATTR_NAME); StringBuffer xPath; xPath.appendf("Hardware/Computer[@domain='%s']", szDomain); //enumerate all computers in this domain Owned icomputer = m_pRootNode->getElements(xPath); ForEach(*icomputer) { const char* szComputer = icomputer->query().queryProp("@name"); NoteFilter(pDomains, "Domain", szDomain, szComputer); NoteUsage(szComputer, "Domain", szDomain); } } iter.setown(m_pRootNode->getElements("Hardware/ComputerType")); ForEach(*iter) { const char* szComputerType = iter->query().queryProp(XML_ATTR_NAME); StringBuffer xPath; xPath.appendf("Hardware/Computer[@computerType='%s']", szComputerType); //enumerate all computers with this computer type Owned icomputer = m_pRootNode->getElements(xPath); ForEach(*icomputer) { const char* szComputer = icomputer->query().queryProp("@name"); NoteFilter(pCompTypes, "ComputerType", szComputerType, szComputer); NoteUsage(szComputer, "ComputerType", szComputerType); } } //enumerate all computers and process their subnets Owned icomputer = m_pRootNode->getElements("Hardware/Computer"); ForEach(*icomputer) { IPropertyTree* pComputer = &icomputer->query(); const char* szComputer = pComputer->queryProp("@name"); const char* szNetAddress = pComputer->queryProp("@netAddress"); if (szComputer && *szComputer && szNetAddress && *szNetAddress) { char szSubnet[128]; strcpy(szSubnet, szNetAddress); char* pchDot = strrchr(szSubnet, '.'); if (pchDot) { strcpy(pchDot+1, "0"); //TRACE3("%s(%s)=> %s\n", szComputer, pComputer->queryProp("@netAddress"), szSubnet); NoteFilter(pSubnets, "Subnet", szSubnet, szComputer); NoteUsage(szComputer, "Subnet", szSubnet); } } } } } //--------------------------------------------------------------------------- // NoteFilter //--------------------------------------------------------------------------- void CComputerPicker::NoteFilter(IPropertyTree* pFilter, const char *componentType, const char *component, const char* computer) { if (component && *component && componentType && *componentType) { StringBuffer sComponentType(componentType); const char* psz = strstr(sComponentType.str(), "Process"); if (psz) sComponentType.remove(psz - sComponentType.str(), strlen("Process")); StringBuffer xPath; xPath.appendf("%s[@name='%s']", sComponentType.str(), component); IPropertyTree* pComponentType = pFilter->queryPropTree(xPath); if (!pComponentType) { pComponentType = pFilter->addPropTree(sComponentType, createPTree(sComponentType)); pComponentType->addProp("@name", component); } if (computer && *computer) { IPropertyTree* pComputer = pComponentType->addPropTree( "Computer", createPTree() ); pComputer->addProp("@name", computer); pComputer->addPropBool("@__bHidden", true); } } } void CComputerPicker::Refresh() { // Passed all filters so add the computers left in the usage map int iItem = 0; m_pComputerTree.clear(); m_pComputerTree.setown(createPTree("ComputerList")); Owned iComputer = m_pRootNode->getElements(XML_TAG_HARDWARE"/"XML_TAG_COMPUTER); ForEach (*iComputer) { // Must have a valid name IPropertyTree* pNode = &iComputer->query(); const char* szComputer = pNode->queryProp(XML_ATTR_NAME); StringBuffer sUsage; if (szComputer && *szComputer && GetUsage(szComputer, sUsage, false)) { IPropertyTree* pComponentType = m_pComputerTree->addPropTree("Machine", createPTree("Machine")); pComponentType->addProp("@name", szComputer); pComponentType->addProp("@netAddress", pNode->queryProp(XML_ATTR_NETADDRESS)); pComponentType->addProp("@usage", sUsage.str()); } } } void CComputerPicker::ApplyFilter(const char* szSubTreeName, const char* szIncAttrib, char chStatus, StringBuffer& sFilterApplied) { StringBuffer xPath; xPath.appendf("%s/*[@%s]", szSubTreeName, szIncAttrib); Owned iter = m_pFilterTree->getElements(xPath); ForEach(*iter) { IPropertyTree* pComponent = &iter->query(); const char* szCompType = pComponent->queryName(); const char* szCompName = pComponent->queryProp("@name"); if (sFilterApplied.length() > 0) sFilterApplied.append(", "); sFilterApplied.append(szCompName); Owned icomputer = pComponent->getElements("Computer[@name]"); ForEach(*icomputer) { const char* szComputer = icomputer->query().queryProp("@name"); if (szComputer) NoteUsage(szComputer, szCompType, szCompName, chStatus); } } }