/*############################################################################## HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ############################################################################## */ ///////////////////////////////////////////////////////////////////////////// // // 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); } } }