/*##############################################################################
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 .
############################################################################## */
#include "jliball.hpp"
#include "environment.hpp"
#include "XMLTags.h"
#include "configenvhelper.hpp"
#include "deployutils.hpp"
#include "build-config.h"
bool CConfigEnvHelper::handleRoxieOperation(const char* cmd, const char* xmlStr)
{
bool retVal = false;
if (!strcmp(cmd, "AddRoxieFarm"))
retVal = this->addRoxieServers(xmlStr);
else if (!strcmp(cmd, "DeleteRoxieFarm"))
retVal = this->deleteRoxieServers(xmlStr);
else if (!strcmp(cmd, "RoxieSlaveConfig"))
retVal = this->handleRoxieSlaveConfig(xmlStr);
else if (!strcmp(cmd, "ReplaceRoxieServer"))
retVal = this->handleReplaceRoxieServer(xmlStr);
return retVal;
}
bool CConfigEnvHelper::handleThorTopologyOp(const char* cmd, const char* xmlArg, StringBuffer& sMsg)
{
bool retVal = false;
StringBuffer xpath;
Owned pParams = createPTreeFromXMLString(xmlArg && *xmlArg ? xmlArg : "");
const char* thorName = pParams->queryProp(XML_ATTR_NAME);
const char* newType = pParams->queryProp(XML_ATTR_TYPE);
const char* validate = pParams->queryProp("@validateComputers");
const char* skip = pParams->queryProp("@skipExisting");
const char* slavesPerNode = pParams->queryProp("@slavesPerNode");
bool checkComps = validate && !strcmp(validate, "true");
bool skipExisting = skip && !strcmp(skip, "true");
IPropertyTree* pThor = getSoftwareNode(XML_TAG_THORCLUSTER, thorName);
StringBuffer usageList;
if (!strcmp(cmd, "Add"))
{
Owned iterComputers = pParams->getElements("Computer");
IPropertyTreePtrArray computers;
ForEach (*iterComputers)
{
IPropertyTree* pComp = &iterComputers->query();
const char* pszCompName = pComp->queryProp(XML_ATTR_NAME);
xpath.clear().appendf(XML_TAG_HARDWARE"/"XML_TAG_COMPUTER"/["XML_ATTR_NAME"='%s']", pszCompName);
IPropertyTree* pComputer = m_pRoot->queryPropTree(xpath.str());
if (pComputer)
computers.push_back(pComputer);
}
int numNodes = 1;
if (slavesPerNode && *slavesPerNode)
numNodes = atoi(slavesPerNode);
if (numNodes < 1)
numNodes = 1;
if (!strcmp(newType, "Master"))
retVal = this->AddNewNodes(pThor, XML_TAG_THORMASTERPROCESS, 0, computers, checkComps, skipExisting, usageList);
else if (!strcmp(newType, "Slave"))
retVal = this->AddNewNodes(pThor, XML_TAG_THORSLAVEPROCESS, 0, computers, checkComps, skipExisting, usageList, numNodes);
else if (!strcmp(newType, "Spare"))
retVal = this->AddNewNodes(pThor, XML_TAG_THORSPAREPROCESS, 0, computers, checkComps, skipExisting, usageList);
if (usageList.length() > 0)
{
sMsg.append("The following computers are already being used.\nDo you want to add/replace them anyway?");
sMsg.append(usageList);
}
}
else if (!strcmp(cmd, "Delete"))
{
Owned iterComputers = pParams->getElements("Node");
ForEach (*iterComputers)
{
IPropertyTree* pComp = &iterComputers->query();
const char* process = pComp->queryProp(XML_ATTR_PROCESS_NAME);
const char* type = pComp->queryProp(XML_ATTR_TYPE);
// Delete process node
IPropertyTree* pProcessNode = GetProcessNode(pThor, process);
if (pProcessNode)
pThor->removeTree(pProcessNode);
// Delete from topology node
IPropertyTree* pTopoNode = pThor->queryPropTree(XML_TAG_TOPOLOGY);
if (!strcmp(type, "Slave"))
{
Owned iter = pThor->getElements(XML_TAG_TOPOLOGY"//"XML_TAG_NODE);
ForEach(*iter)
{
// Get macthing process node
const char* szProcess = iter->query().queryProp(XML_ATTR_PROCESS);
IPropertyTree* pProcessNode = GetProcessNode(pThor, szProcess);
if (!strcmp(pProcessNode->queryName(), XML_TAG_THORMASTERPROCESS))
{
xpath.clear().appendf(XML_TAG_NODE"/["XML_ATTR_PROCESS"='%s']", process);
IPropertyTree* pNode = iter->query().queryPropTree(xpath.str());
if (pNode)
iter->query().removeTree(pNode);
break;
}
}
}
else
{
xpath.clear().appendf(XML_TAG_NODE"/["XML_ATTR_PROCESS"='%s']", process);
IPropertyTree* pNode = pTopoNode->queryPropTree(xpath.str());
//Remove all slaves from thor
if (pNode && !strcmp(type, "Master"))
{
Owned iter = pThor->getElements(XML_TAG_TOPOLOGY"//"XML_TAG_NODE);
ForEach(*iter)
{
// Get macthing process node
const char* szProcess = iter->query().queryProp(XML_ATTR_PROCESS);
IPropertyTree* pProcessNode = GetProcessNode(pThor, szProcess);
if (pProcessNode && !strcmp(pProcessNode->queryName(), XML_TAG_THORSLAVEPROCESS))
pThor->removeTree(pProcessNode);
}
}
if (pNode)
pTopoNode->removeTree(pNode);
}
}
RenameThorInstances(pThor);
UpdateThorAttributes(pThor);
retVal = true;
}
return retVal;
}
IPropertyTree* CConfigEnvHelper::getSoftwareNode(const char* compType, const char* compName)
{
StringBuffer xpath;
xpath.appendf("Software/%s[@name='%s']", compType, compName);
return m_pRoot->queryPropTree(xpath.str());
}
bool CConfigEnvHelper::addRoxieServers(const char* xmlArg)
{
Owned pSrcTree = createPTreeFromXMLString(xmlArg && *xmlArg ? xmlArg : "");
const char* pszFarm = pSrcTree->queryProp("@parentName");
const char* pszRoxieCluster = pSrcTree->queryProp("@roxieName");
unsigned int nComputers = 0;//computers.size();
IPropertyTree* pParent = m_pRoot->queryPropTree("Software");
IPropertyTree* pFarm;
bool bNewFarm;
StringBuffer sFarmName;
StringBuffer xpath;
if (strlen(pszFarm))
{
xpath.clear().appendf("RoxieCluster[@name='%s']/"XML_TAG_ROXIE_FARM, pszRoxieCluster);
pFarm = getSoftwareNode(xpath.str(), pszFarm);
sFarmName = pFarm->queryProp(XML_ATTR_NAME);
bNewFarm = false;
if (!pFarm->hasProp("@port"))
pFarm->addPropInt("@port", 9876);
if (!pFarm->hasProp("@listenQueue"))
pFarm->addPropInt("@listenQueue", 200);
if (!pFarm->hasProp("@numThreads"))
pFarm->getPropInt("@numThreads", 30);
if (!pFarm->hasProp("@requestArrayThreads"))
pFarm->addPropInt("@requestArrayThreads", 5);
if (!pFarm->hasProp("@aclName"))
pFarm->addProp("@aclName", "");
StringBuffer dataDir = pFarm->queryProp(XML_ATTR_DATADIRECTORY);
if (dataDir.length()==0)
dataDir.append(RUNTIME_DIR"/roxiedata");
if (!pFarm->queryPropTree(XML_TAG_ROXIE_SERVER"[1]")) //no servers in farm
{
//if (nComputers > 0)
//g_pDocument->makePlatformSpecificAbsolutePath(computers[0]->queryProp(XML_ATTR_NAME), dataDir);
Owned iter = pSrcTree->getElements(XML_TAG_COMPONENT);
ForEach (*iter)
{
IPropertyTree* pFolder = &iter->query();
makePlatformSpecificAbsolutePath(pFolder->queryProp(XML_ATTR_NAME), dataDir);
break;
}
pFarm->setProp(XML_ATTR_DATADIRECTORY, dataDir.str());
}
}
else
{
xpath.clear().appendf("RoxieCluster[@name='%s']/"XML_TAG_ROXIE_FARM, pszRoxieCluster);
createUniqueName("farm", xpath.str(), sFarmName);
bNewFarm = true;
StringBuffer sDataDir;
sDataDir.append(RUNTIME_DIR"/roxiedata");
//get datadir from existing farm if any
xpath.clear().appendf("Software/RoxieCluster[@name='%s']/"XML_TAG_ROXIE_FARM"[1]", pszRoxieCluster);
IPropertyTree* pFarm1 = m_pRoot->queryPropTree(xpath.str());
if (pFarm1)
sDataDir.clear().append(pFarm1->queryProp(XML_ATTR_DATADIRECTORY));
//if (nComputers > 0)
//g_pDocument->makePlatformSpecificAbsolutePath(computers[0]->queryProp(XML_ATTR_NAME), sDataDir);
Owned iter = pSrcTree->getElements(XML_TAG_COMPONENT);
ForEach (*iter)
{
IPropertyTree* pFolder = &iter->query();
makePlatformSpecificAbsolutePath(pFolder->queryProp(XML_ATTR_NAME), sDataDir);
break;
}
xpath.clear().appendf("RoxieCluster[@name='%s']/"XML_TAG_ROXIE_FARM, pszRoxieCluster);
pFarm = pParent->addPropTree(xpath.str(), createPTree());
pFarm->addProp (XML_ATTR_NAME, sFarmName.str());
pFarm->addPropInt("@port", pSrcTree->getPropInt("@port", 9876));
pFarm->addProp (XML_ATTR_DATADIRECTORY, sDataDir.str());
pFarm->addPropInt("@listenQueue", 200);
pFarm->addPropInt("@numThreads", 30);
pFarm->addPropInt("@requestArrayThreads", 5);
pFarm->addProp("@aclName", "");
}
Owned iter = pSrcTree->getElements(XML_TAG_COMPONENT);
StringBuffer sNotAdded;
xpath.clear().appendf("RoxieCluster[@name='%s']/"XML_TAG_ROXIE_FARM"[@name='%s']/"XML_TAG_ROXIE_SERVER, pszRoxieCluster, sFarmName.str());
ForEach (*iter)
{
IPropertyTree* pFolder = &iter->query();
const char* pszName = pFolder->queryProp(XML_ATTR_NAME);
// Check if we can add this computer
if (checkComputerUse(pszName, pFarm))
{
StringBuffer sServerName( sFarmName), sbUniqueName;
sServerName.append("_s");
createUniqueName(sServerName.str(), xpath.str(), sbUniqueName);
// Add process node
IPropertyTree* pServer = createPTree(XML_TAG_ROXIE_SERVER);
pServer->setProp(XML_ATTR_NAME, sbUniqueName.str());
pServer->addProp(XML_ATTR_COMPUTER, pszName);
addNode(pServer, pFarm);
IPropertyTree* pLegacyServer = addLegacyServer(sbUniqueName, pServer, pFarm, pszRoxieCluster);
}
else
{
sNotAdded.append('\n');
sNotAdded.append(pszName);
sNotAdded.append(" ( ");
sNotAdded.append( pFolder->queryProp(XML_ATTR_NETADDRESS) );
sNotAdded.append(" )");
}
}
xpath.clear().appendf("Software/RoxieCluster[@name='%s']", pszRoxieCluster);
IPropertyTree* pRoxieCluster = m_pRoot->queryPropTree(xpath.str());
renameInstances(pRoxieCluster);
if (sNotAdded.length())
{
StringBuffer sMsg("The following servers were already allocated to the farm and could not be added:\n");
sMsg.append(sNotAdded.str());
}
return true;
}
bool CConfigEnvHelper::handleReplaceRoxieServer(const char* xmlArg)
{
Owned pSrcTree = createPTreeFromXMLString(xmlArg && *xmlArg ? xmlArg : "");
const char* pszRoxieCluster = pSrcTree->queryProp("@roxieName");
IPropertyTree* pParent = m_pRoot->queryPropTree("Software");
StringBuffer xpath;
if (pszRoxieCluster && *pszRoxieCluster)
{
xpath.clear().appendf(XML_TAG_ROXIECLUSTER"[@name='%s']/"XML_TAG_ROXIE_FARM, pszRoxieCluster);
IPropertyTree* pFarm = pParent->queryPropTree(xpath.str());
if (!pFarm)
throw MakeStringException(-1, "Could not find a RoxieCluster with name '%s'", pszRoxieCluster);
Owned iter = pSrcTree->getElements("Nodes/Node");
ForEach (*iter)
{
IPropertyTree* pNode = &iter->query();
const char* pszFarm = pNode->queryProp("@farm");
const char* pszName = pNode->queryProp(XML_ATTR_NAME);
const char* pszNewComputer = pNode->queryProp("@newComputer");
xpath.clear().appendf(XML_TAG_ROXIE_SERVER"[@computer='%s']", pszNewComputer);
if (pFarm->queryPropTree(xpath.str()))
return false;
xpath.clear().appendf(XML_TAG_ROXIE_SERVER"[@name='%s']", pszName);
IPropertyTree* pServer = pFarm->queryPropTree(xpath.str());
if (pServer && pszNewComputer && *pszNewComputer)
{
pServer->setProp(XML_ATTR_COMPUTER, pszNewComputer);
xpath.clear().appendf(XML_TAG_ROXIECLUSTER"[@name='%s']/"XML_TAG_ROXIE_SERVER"[@name='%s']", pszRoxieCluster, pszName);
IPropertyTree* pOldVerRoxieServer = pParent->queryPropTree(xpath.str());
if (pOldVerRoxieServer)
{
pOldVerRoxieServer->setProp(XML_ATTR_COMPUTER, pszNewComputer);
xpath.clear().appendf("Hardware/"XML_TAG_COMPUTER"["XML_ATTR_NAME"='%s']", pszNewComputer);
IPropertyTree* pComputer = m_pRoot->queryPropTree(xpath.str());
if (pComputer)
pOldVerRoxieServer->setProp(XML_ATTR_NETADDRESS, pComputer->queryProp(XML_ATTR_NETADDRESS));
}
}
}
}
return true;
}
//---------------------------------------------------------------------------
// CheckComputerUse - will only prompt once for each element type
//---------------------------------------------------------------------------
bool CConfigEnvHelper::checkComputerUse(/*IPropertyTree* pComputerNode*/ const char* szComputer, IPropertyTree* pParentNode) const
{
StringBuffer xpath;
xpath.append(XML_TAG_ROXIE_SERVER "[@computer='").append( szComputer ).append("']");
Owned iter = pParentNode->getElements(xpath.str());
return !(iter->first() && iter->isValid());
}
bool CConfigEnvHelper::makePlatformSpecificAbsolutePath(const char* computer, StringBuffer& path)
{
bool rc = false;
if (computer && *computer && path.length())
{
IPropertyTree* pComputer = lookupComputerByName(computer);
const char* computerType = pComputer ? pComputer->queryProp(XML_ATTR_COMPUTERTYPE) : NULL;
if (computerType && *computerType)
{
StringBuffer xpath;
xpath.appendf("Hardware/ComputerType[@name='%s']", computerType);
Owned iter = m_pRoot->getElements(xpath.str());
if (iter->first() && iter->isValid())
{
const char* os = iter->query().queryProp("@opSys");
if (os && *os)
{
const bool bLinux = 0 != stricmp(os, "W2K");
if (bLinux)
{
path.replace('\\', '/');
path.replace(':', '$');
if (*path.str() != '/')
path.insert(0, '/');
}
else
{
path.replace('/', '\\');
path.replace('$', ':');
if (*path.str() == '\\')
path.remove(0, 1);
}
rc = true;
}
}
}
}
return rc;
}
IPropertyTree* CConfigEnvHelper::addLegacyServer(const char* name, IPropertyTree* pServer,
IPropertyTree* pFarm, const char* roxieClusterName)
{
IPropertyTree* pLegacyServer;
StringBuffer xpath;
xpath.clear().appendf("Software/RoxieCluster[@name='%s']", roxieClusterName);
IPropertyTree* pParentNode = m_pRoot->queryPropTree(xpath.str());
if (pParentNode)
{
const char* szComputer = pServer->queryProp(XML_ATTR_COMPUTER);
xpath.clear().appendf("Hardware/Computer/[@name='%s']", szComputer);
IPropertyTree* pComputer= m_pRoot->queryPropTree(xpath.str());
const char* netAddress = pComputer->queryProp(XML_ATTR_NETADDRESS);
//derive the new server from pFarm since it has most of the attributes
pLegacyServer = addNode(XML_TAG_ROXIE_SERVER, pParentNode);
pLegacyServer->setProp( XML_ATTR_NAME, name);
pLegacyServer->setProp( XML_ATTR_COMPUTER, szComputer );
pLegacyServer->setProp( XML_ATTR_NETADDRESS, netAddress);
Owned iAttr = pFarm->getAttributes();
ForEach(*iAttr)
{
const char* attrName = iAttr->queryName();
if (0 != strcmp(attrName, XML_ATTR_COMPUTER) && //skip
0 != strcmp(attrName, XML_ATTR_NETADDRESS) &&
0 != strcmp(attrName, XML_ATTR_NAME))
{
pLegacyServer->addProp(attrName, iAttr->queryValue());
}
}
}
else
pLegacyServer = NULL;
return pLegacyServer;
}
//---------------------------------------------------------------------------
// setComputerState
//---------------------------------------------------------------------------
void CConfigEnvHelper::setComputerState(IPropertyTree* pNode, COMPUTER_STATE state)
{
setAttribute(pNode, XML_ATTR_STATE, g_szComputerState[state]);
}
//---------------------------------------------------------------------------
// setAttribute
//---------------------------------------------------------------------------
void CConfigEnvHelper::setAttribute(IPropertyTree* pNode, const char* szName, const char* szValue)
{
// Check attribute already has specified value
const char* szValueOld = pNode->queryProp(szName);
if (!szValueOld || strcmp(szValueOld, szValue))
{
//UpdateComputerMap(pNode, false);
// ptree does not like missing intermediates...
const char *finger = szName;
StringBuffer subpath;
while (strchr(finger, '/'))
{
while (*finger!='/')
subpath.append(*finger++);
if (!pNode->hasProp(subpath.str()))
pNode->addProp(subpath.str(), "");
subpath.append(*finger++);
}
if (!strcmp(szName, XML_ATTR_BUILD) && !strcmp(pNode->queryName(), XML_TAG_ESPSERVICE))
{
//remove previous Properties, if any, that this component inherited from its
//previous build
IPropertyTree* pProperties = pNode->queryPropTree("Properties");
IPropertyTree* pNewProperties;
//if the new build has any properties then let the node inherit them
const char* buildSet = pNode->queryProp(XML_ATTR_BUILDSET);
if (buildSet)
{
StringBuffer sPath;
sPath.append("Programs/Build[@name='").append(szValue).append("']/BuildSet[@name='")
.append(buildSet).append("']/Properties");
pNewProperties = m_pRoot->queryPropTree(sPath.str());
}
else
pNewProperties = NULL;
//if we just changed build for an ESP service then enumerate all bindings for all
//ESP server processes and if any binding uses this service then replace its
//Authenticate and AuthenticateFeature nodes with those from the properties of
//this service from the new build. However, we only remove the nodes that are
//not in the new build preserving the others (so their attributes are preserved -
//in case they have been changed by the user). We also add new nodes that did
//not exist before. In essence, a merge is needed.
//
if (pProperties || pNewProperties)
{
StringBuffer xpath;
xpath.appendf("Software/EspProcess/EspBinding[@service='%s']", pNode->queryProp(XML_ATTR_NAME));
Owned iBinding = m_pRoot->getElements(xpath.str());
ForEach(*iBinding)
{
IPropertyTree* pBinding = &iBinding->query();
//remove existing Authenticate and AuthenticateFeature nodes that are not in the new buildset's properties
//
mergeServiceAuthenticationWithBinding(pBinding, pProperties, pNewProperties, "Authenticate");
mergeServiceAuthenticationWithBinding(pBinding, pProperties, pNewProperties, "AuthenticateFeature");
mergeServiceAuthenticationWithBinding(pBinding, pProperties, pNewProperties, "AuthenticateSetting");
}
pNode->removeTree(pProperties);
}
if (pNewProperties)
pNode->addPropTree("Properties", createPTreeFromIPT(pNewProperties));
}
pNode->setProp(szName, szValue);
}
}
void CConfigEnvHelper::mergeServiceAuthenticationWithBinding(IPropertyTree* pBinding,
IPropertyTree* pProperties,
IPropertyTree* pNewProperties,
const char* NodeName)
{
StringBuffer xpath;
//remove existing Authenticate and AuthenticateFeature nodes that are not in the new buildset's properties
//
Owned iDest = pBinding->getElements(NodeName);
for (iDest->first(); iDest->isValid(); )
{
IPropertyTree* pDest = &iDest->query();
iDest->next();
const char* path = pDest->queryProp("@path");
xpath.clear().appendf("%s[@path='%s']", NodeName, path);
IPropertyTree* pNewPropChild = pNewProperties->queryPropTree(xpath.str());
if (pNewPropChild)
{
IPropertyTree* pPropChild = pProperties->queryPropTree(xpath.str());
if (pPropChild)
{
//same path so merge individual attributes, retaining any that may have been changed by user
//but replacing ones that are different in newer build but not changed by user
Owned iAttr = pDest->getAttributes();
ForEach(*iAttr)
{
const char* attrName = iAttr->queryName();
if (0 != strcmp(attrName, "@path"))
{
const char* attrDest = iAttr->queryValue();
const char* attrProp = pPropChild->queryProp(attrName);
const char* attrNewProp = pNewPropChild->queryProp(attrName);
if (attrProp && attrNewProp && !strcmp(attrDest, attrProp))
pDest->setProp(attrName, attrNewProp);
}
}
}
}
else
pBinding->removeTree(pDest);
}
//add nodes from buildset properties that are missing in binding
//
bool bAuthenticateFeature = !strcmp(NodeName, "AuthenticateFeature");
Owned iSrc = pNewProperties->getElements(NodeName);
ForEach(*iSrc)
{
IPropertyTree* pNode = &iSrc->query();
const char* path = pNode->queryProp("@path");
xpath.clear().appendf("%s[@path='%s']", NodeName, path);
if (!pBinding->queryPropTree(xpath.str()))
{
pNode = pBinding->addPropTree(NodeName, createPTreeFromIPT(pNode));
if (bAuthenticateFeature)
pNode->addProp("@authenticate", "Yes");
}
}
}
//---------------------------------------------------------------------------
// lookupComputerByName
//---------------------------------------------------------------------------
IPropertyTree* CConfigEnvHelper::lookupComputerByName(const char* szName) const
{
if (!szName || !*szName) return NULL;
Owned iter = m_pRoot->getElements(XML_TAG_HARDWARE"/"XML_TAG_COMPUTER);
for (iter->first(); iter->isValid(); iter->next())
{
const char* szValue = iter->query().queryProp(XML_ATTR_NAME);
if (szValue && strcmp(szValue, szName) == 0)
return &iter->query();
}
return NULL;
}
void CConfigEnvHelper::createUniqueName(const char* szPrefix, const char* parent, StringBuffer& sbName)
{
sbName.clear().append(szPrefix).append("1");
if (getSoftwareNode(parent, sbName.str()))
{
int iIdx = 2;
do
{
sbName.clear().append(szPrefix).append(iIdx++);
}
while (getSoftwareNode(parent, sbName.str()));
}
}
//---------------------------------------------------------------------------
// addNode
//---------------------------------------------------------------------------
IPropertyTree* CConfigEnvHelper::addNode(const char* szTag, IPropertyTree* pParentNode, IPropertyTree* pInsertAfterNode)
{
IPropertyTree* pNode = createPTree(szTag);
if (pNode)
{
addNode(pNode, pParentNode, pInsertAfterNode);
}
return pNode;
}
//---------------------------------------------------------------------------
// addNode
//---------------------------------------------------------------------------
IPropertyTree* CConfigEnvHelper::addNode(IPropertyTree*& pNode, IPropertyTree* pParentNode, IPropertyTree* pInsertAfterNode)
{
StringBuffer sTag(pNode->queryName()); // need to pass in a copy of the name
// Check is node is to be added at specific location relative to nodes with same name
if (pInsertAfterNode)
{
int idx = 1; // this will insert into first position
if (strcmp(pInsertAfterNode->queryName(), pNode->queryName()) == 0)
{
idx = pParentNode->queryChildIndex(pInsertAfterNode) + 2;
}
// Only append qualifier is not inserting at end position
if (pParentNode->queryPropTree(StringBuffer(sTag).appendf("[%d]", idx).str()))
sTag.appendf("[%d]", idx);
}
pNode = pParentNode->addPropTree(sTag.str(), pNode);
return pNode;
}
//---------------------------------------------------------------------------
// renameInstances
//---------------------------------------------------------------------------
void CConfigEnvHelper::renameInstances(IPropertyTree* pRoxieCluster)
{
// Iterate through farms
int nFarm = 0;
StringBuffer xpath;
Owned iFarm = pRoxieCluster->getElements(XML_TAG_ROXIE_FARM);
ForEach(*iFarm)
{
IPropertyTree* pFarm = &iFarm->query();
int nServer = 0;
StringBuffer sFarmName("farm");
sFarmName.append(++nFarm);
setAttribute(pFarm, XML_ATTR_NAME, sFarmName.str());
Owned iServer = pFarm->getElements(XML_TAG_ROXIE_SERVER);
ForEach(*iServer)
{
IPropertyTree* pServer = &iServer->query();
StringBuffer sServerName( sFarmName );
sServerName.append("_s");
sServerName.append(++nServer);
const char* prevName = pServer->queryProp(XML_ATTR_NAME);
if (prevName && *prevName)
{
IPropertyTree* pLegacyServer = findLegacyServer(pRoxieCluster, prevName);
if (pLegacyServer)
setAttribute(pLegacyServer, "@_name", sServerName.str());
}
setAttribute(pServer, XML_ATTR_NAME, sServerName.str());
}
}
Owned iServer = pRoxieCluster->getElements(XML_TAG_ROXIE_SERVER);
ForEach(*iServer)
{
IPropertyTree* pServer = &iServer->query();
const char* newName = pServer->queryProp("@_name");
if (newName)
{
pServer->setProp(XML_ATTR_NAME, newName);
pServer->removeProp("@_name");
}
}
}
IPropertyTree* CConfigEnvHelper::findLegacyServer(IPropertyTree* pRoxieCluster, const char* pszServer)
{
StringBuffer xpath;
xpath.appendf(XML_TAG_ROXIE_SERVER"[@name='%s']", pszServer);
return pRoxieCluster->queryPropTree( xpath.str() );
}
bool CConfigEnvHelper::deleteRoxieServers(const char* xmlArg)
{
Owned pSrcTree = createPTreeFromXMLString(xmlArg && *xmlArg ? xmlArg : "");
const char* pszRoxieCluster = pSrcTree->queryProp("@roxieName");
unsigned int nComputers = 0;//computers.size();
StringBuffer xpath;
xpath.clear().appendf("Software/RoxieCluster[@name='%s']", pszRoxieCluster);
IPropertyTree* pRoxieCluster = m_pRoot->queryPropTree(xpath.str());
StringBuffer sFarmName;
Owned iterFarm = pSrcTree->getElements(XML_TAG_ROXIE_FARM);
ForEach (*iterFarm)
{
IPropertyTree* pFarm = &iterFarm->query();
const char* pszFarm = pFarm->queryProp(XML_ATTR_NAME);
deleteFarm(pRoxieCluster, pszFarm);
}
Owned iterServer = pSrcTree->getElements(XML_TAG_ROXIE_SERVER);
ForEach (*iterServer)
{
IPropertyTree* pServer = &iterServer->query();
const char* pszName = pServer->queryProp(XML_ATTR_NAME);
const char* pszFarm = pServer->queryProp("@parent");
deleteServer(pRoxieCluster, pszFarm, pszName);
}
Owned iterSlaves = pSrcTree->getElements(XML_TAG_ROXIE_ONLY_SLAVE);
ForEach (*iterSlaves)
{
IPropertyTree* pChild;
//if atleast one slave, delete all slaves
while (pChild = pRoxieCluster->queryPropTree( "RoxieSlave[1]" ))
pRoxieCluster->removeTree( pChild );
while (pChild = pRoxieCluster->queryPropTree( XML_TAG_ROXIE_SLAVE "[1]" ))
pRoxieCluster->removeTree( pChild );
break;
}
renameInstances(pRoxieCluster);
return true;
}
void CConfigEnvHelper::deleteFarm(IPropertyTree* pRoxieCluster, const char* pszFarm)
{
StringBuffer xpath;
xpath.clear().appendf(XML_TAG_ROXIE_FARM"[@name='%s']", pszFarm);
IPropertyTree* pFarm = pRoxieCluster->queryPropTree(xpath.str());
Owned it = pFarm->getElements(XML_TAG_ROXIE_SERVER);
ForEach(*it)
{
IPropertyTree* pServer = &it->query();
const char* pszServer = pServer->queryProp(XML_ATTR_NAME);
IPropertyTree* pLegacyServer = findLegacyServer(pRoxieCluster, pszServer);
if (pLegacyServer)
pRoxieCluster->removeTree(pLegacyServer);
}
pRoxieCluster->removeTree(pFarm);
}
void CConfigEnvHelper::deleteServer(IPropertyTree* pRoxieCluster, const char* pszFarm, const char* pszServer)
{
StringBuffer xpath;
IPropertyTree* pLegacyServer = findLegacyServer(pRoxieCluster, pszServer);
if (pLegacyServer)
pRoxieCluster->removeTree(pLegacyServer);
xpath.clear().appendf(XML_TAG_ROXIE_FARM"[@name='%s']", pszFarm);
IPropertyTree* pFarm = pRoxieCluster->queryPropTree(xpath.str());
if (pFarm)
{
xpath.clear().appendf(XML_TAG_ROXIE_SERVER"[@name='%s']", pszServer);
IPropertyTree* pServer = pFarm->queryPropTree(xpath.str());
if (pServer)
pFarm->removeTree(pServer);
}
}
void CConfigEnvHelper::addComponent(const char* pszBuildSet, StringBuffer& sbNewName, IPropertyTree* pCompTree)
{
try
{
// NOTE - we are assuming buildSet is unique in a build.
StringBuffer xPath, value;
xPath.appendf("./Programs/Build/BuildSet[@name=\"%s\"]", pszBuildSet);
Owned buildSet = m_pRoot->getElements(xPath.str());
buildSet->first();
IPropertyTree* pBuildSet = &buildSet->query();
const char* buildSetName = pBuildSet->queryProp(XML_ATTR_NAME);
const char* processName = pBuildSet->queryProp(XML_ATTR_PROCESS_NAME);
const char* buildName = m_pRoot->queryPropTree("./Programs/Build[1]")->queryProp(XML_ATTR_NAME);
if (!processName) //support non-generic components as well
processName = buildSetName;
{
// Use lower case version of type for name prefix
StringBuffer sName(buildSetName);
sName.toLowerCase();
sName.replaceString("process","");
if(sbNewName.length())
value.append(sbNewName.str()).append(getUniqueName(m_pRoot.get(), sName, processName, "Software"));
else
value.append(getUniqueName(m_pRoot.get(), sName, processName, "Software"));
pCompTree->setProp(XML_ATTR_NAME,value);
sbNewName.clear().append(sName);
pCompTree->setProp(XML_ATTR_BUILD, buildName);
pCompTree->setProp(XML_ATTR_BUILDSET,pszBuildSet);
Owned pProperties = pBuildSet->getPropTree("Properties");
if (pProperties)
pCompTree->addPropTree("Properties", createPTreeFromIPT(pProperties));
addNode(pCompTree, m_pRoot->queryPropTree("Software"));
}
}
catch (IException* e)
{
throw e;
}
}
bool CConfigEnvHelper::EnsureInRange(const char* psz, UINT low, UINT high, const char* caption)
{
bool rc = false;
StringBuffer msg;
const UINT x = atoi( psz );
if ( ((low < high) && (x < low || x > high)) || (low == high && x != low) )
{
msg.append(caption).append(" must be ");
if (low == high)
msg.append(low);
else
{
msg.append("between ");
msg.append(low).append(" and ");
msg.append( high );
}
}
else
if (high == 0 && x < low)
msg.append(caption).append(" must be at least ").append(low);
else
rc = true;
if (!rc)
{
msg.append('.');
throw MakeStringException(-1, "%s", msg.str());
}
return rc;
}
bool CConfigEnvHelper::handleRoxieSlaveConfig(const char* xmlArg)
{
try
{
Owned pSrcTree = createPTreeFromXMLString(xmlArg && *xmlArg ? xmlArg : "");
const char* type = pSrcTree->queryProp(XML_ATTR_TYPE);
const char* pszRoxie = pSrcTree->queryProp("@roxieName");
const char* val1 = pSrcTree->queryProp("@val1");
const char* sOffset = pSrcTree->queryProp("@val2");
StringBuffer dir1, dir2, dir3;
getCommonDir(m_pRoot.get(), "data", "roxie", pszRoxie, dir1);
getCommonDir(m_pRoot.get(), "data2", "roxie", pszRoxie, dir2);
getCommonDir(m_pRoot.get(), "data3", "roxie", pszRoxie, dir3);
StringBuffer xpath;
xpath.clear().appendf("Software/RoxieCluster[@name='%s']", pszRoxie);
IPropertyTree* pRoxie = m_pRoot->queryPropTree(xpath.str());
if (!pRoxie)
throw MakeStringException(-1, "Cannot find roxie with name %s", pszRoxie);
Owned iterComputers = pSrcTree->getElements("Computer");
IPropertyTreePtrArray computers;
ForEach (*iterComputers)
{
IPropertyTree* pComp = &iterComputers->query();
const char* pszCompName = pComp->queryProp(XML_ATTR_NAME);
xpath.clear().appendf(XML_TAG_HARDWARE"/"XML_TAG_COMPUTER"/["XML_ATTR_NAME"='%s']", pszCompName);
IPropertyTree* pComputer = m_pRoot->queryPropTree(xpath.str());
if (pComputer)
computers.push_back(pComputer);
}
m_numChannels = atoi(val1);
m_numDataCopies = 0;
const char* confType;
char chDrive;
if (!strcmp(type, "Circular"))
{
if (!GenerateCyclicRedConfig(pRoxie, computers, val1, sOffset, dir1.str(), dir2.str(), dir3.str()))
return false;
confType = "cyclic redundancy";
chDrive = 'c';
pRoxie->setProp("@cyclicOffset", sOffset);
}
else
{
if (!strcmp(type, "Overloaded"))
{
if (!GenerateOverloadedConfig(pRoxie, computers, val1, dir1.str(), dir2.str(), dir3.str()))
return false;
confType = "overloaded";
chDrive = 'c';
}
else
{
if (!strcmp(type, "Full"))
{
m_numDataCopies = atoi( val1 );
confType = "full redundancy";
}
else //no redundancy
{
m_numDataCopies = 1;
confType = "simple";
}
if (!GenerateFullRedConfig(pRoxie, m_numDataCopies, computers, dir1.str()))
return false;
}
if (pRoxie->hasProp("@cyclicOffset"))
pRoxie->removeProp("@cyclicOffset");
}
StringBuffer sDataDir;
sDataDir.appendf("%s", dir1.str());
//give legacy slaves unique names
UINT i = 1;
Owned it = pRoxie->getElements(XML_TAG_ROXIE_SLAVE);
ForEach( *it)
{
StringBuffer name;
name.append('s').append(i);
IPropertyTree* pLegacySlave = &it->query();
pLegacySlave->setProp(XML_ATTR_NAME, name.str() );
if (i++==1)
makePlatformSpecificAbsolutePath( pLegacySlave->queryProp(XML_ATTR_COMPUTER), sDataDir);
}
pRoxie->setProp("@slaveConfig", confType);
pRoxie->setPropInt("@clusterWidth", computers.size());
pRoxie->setPropInt("@numChannels", m_numChannels);
pRoxie->setPropInt("@numDataCopies", m_numDataCopies);
pRoxie->setProp("@baseDataDir", sDataDir.str());
pRoxie->setProp("@localSlave", computers.size() > 1 ? "false" : "true");
//update Roxie data directories for all farms and all legacy servers
//change all farms
Owned iterFarms = pRoxie->getElements(XML_TAG_ROXIE_FARM);
ForEach (*iterFarms)
{
IPropertyTree* pTmpComp = &iterFarms->query();
if (strcmp(pTmpComp->queryProp(XML_ATTR_DATADIRECTORY), sDataDir.str()))
pTmpComp->setProp(XML_ATTR_DATADIRECTORY, sDataDir.str());
}
//change all legacy servers
Owned iterServers = pRoxie->getElements(XML_TAG_ROXIE_SERVER);
ForEach (*iterServers)
{
IPropertyTree* pTmpComp = &iterServers->query();
if (strcmp(pTmpComp->queryProp(XML_ATTR_DATADIRECTORY), sDataDir.str()))
pTmpComp->setProp(XML_ATTR_DATADIRECTORY, sDataDir.str());
}
}
catch (IException *e)
{
StringBuffer msg;
throw MakeStringException(-1, "%s", e->errorMessage(msg).str());
}
catch (...)
{
throw MakeStringException(-1, "Unknown exception in generating slave configuration!" );
}
return true;
}
void CConfigEnvHelper::addReplicateConfig(IPropertyTree* pSlaveNode, int channel, const char* dir,
const char* netAddress, IPropertyTree* pRoxie)
{
StringBuffer directory;
directory.appendf("%s", dir);
makePlatformSpecificAbsolutePath( pSlaveNode->queryProp(XML_ATTR_COMPUTER), directory);
IPropertyTree* pInstance = pSlaveNode->addPropTree(XML_TAG_ROXIE_CHANNEL, createPTree());
pInstance->setPropInt("@number", channel);
pInstance->addProp(XML_ATTR_DATADIRECTORY, directory.str());
//maintain a copy as an old style slave procss
IPropertyTree* pSlaveProcess = pRoxie->addPropTree(XML_TAG_ROXIE_SLAVE, createPTree());
pSlaveProcess->addProp(XML_ATTR_COMPUTER, pSlaveNode->queryProp(XML_ATTR_COMPUTER));
pSlaveProcess->addPropInt("@channel", channel);
pSlaveProcess->addProp(XML_ATTR_DATADIRECTORY, directory.str());
pSlaveProcess->addProp(XML_ATTR_NETADDRESS, netAddress);
}
bool CConfigEnvHelper::GenerateCyclicRedConfig(IPropertyTree* pRoxie, IPropertyTreePtrArray& computers,
const char* copies, const char* pszOffset,
const char* dir1, const char* dir2, const char* dir3)
{
const int nComputers = computers.size();
if (!nComputers)
return false;
if (!EnsureInRange(copies, min(2, nComputers), max(nComputers, 1), "Channel redundancy") ||
!EnsureInRange(pszOffset, min(1, nComputers-1), nComputers-1, "Channel offset"))
{
return false;
}
const int offset = atoi( pszOffset );
m_numDataCopies = atoi( copies );
const int minOffset = min(1, nComputers-1);
if( offset < minOffset )
throw MakeStringException(-1, "Offset cannot be less than %d", minOffset);
if ( offset > nComputers )
throw MakeStringException(-1, "Offset cannot be greater than %d", nComputers);
RemoveSlaves(pRoxie, true);
RemoveSlaves(pRoxie, false);
for (int i=0; iqueryProp(XML_ATTR_NAME);
const char* netAddress = pComputer->queryProp(XML_ATTR_NETADDRESS);
StringBuffer name;
name.appendf("s%d", i+1);
IPropertyTree* pSlave = pRoxie->addPropTree(XML_TAG_ROXIE_ONLY_SLAVE, createPTree());
pSlave->addProp(XML_ATTR_NAME, name.str());
pSlave->addProp(XML_ATTR_COMPUTER, szComputer);
const int baseChannel = i; //channel for first copy of slave (0 based)
int channel;
for (int c=0; cqueryProp(XML_ATTR_NAME);
const char* netAddress = pComputer->queryProp(XML_ATTR_NETADDRESS);
StringBuffer name;
name.appendf("s%d", i+1);
IPropertyTree* pSlave = pRoxie->addPropTree(XML_TAG_ROXIE_ONLY_SLAVE, createPTree());
pSlave->addProp(XML_ATTR_NAME, name.str());
pSlave->addProp(XML_ATTR_COMPUTER, szComputer);
for (int c=0; cqueryProp(XML_ATTR_NAME);
const char* netAddress = pComputer->queryProp(XML_ATTR_NETADDRESS);
StringBuffer name;
name.appendf("s%d", i+1);
IPropertyTree* pSlave = pRoxie->addPropTree(XML_TAG_ROXIE_ONLY_SLAVE, createPTree());
pSlave->addProp(XML_ATTR_NAME, name.str());
pSlave->addProp(XML_ATTR_COMPUTER, szComputer);
addReplicateConfig(pSlave, 1 + (channel++ % maxChannel), dir1, netAddress, pRoxie);
}
m_numChannels = maxChannel;
return true;
}
void CConfigEnvHelper::RemoveSlaves(IPropertyTree* pRoxie, bool bLegacySlaves/*=false*/)
{
IPropertyTree* pChild;
while (pChild = pRoxie->queryPropTree( bLegacySlaves ? XML_TAG_ROXIE_SLAVE "[1]" : "RoxieSlave[1]"))
pRoxie->removeTree( pChild );
}
void CConfigEnvHelper::RenameThorInstances(IPropertyTree* pThor)
{
// Iterate through topology nodes
int nSlave = 1;
int nSpare = 1;
Owned iter = pThor->getElements(XML_TAG_TOPOLOGY"//"XML_TAG_NODE);
for (iter->first(); iter->isValid(); iter->next())
{
// Get macthing process node
const char* szProcess = iter->query().queryProp(XML_ATTR_PROCESS);
IPropertyTree* pProcessNode = GetProcessNode(pThor, szProcess);
if (pProcessNode)
{
StringBuffer sName;
const char* szTag = pProcessNode->queryName();
if (strcmp(szTag, XML_TAG_THORSLAVEPROCESS) == 0)
sName.appendf("s%d", nSlave++);
else if (strcmp(szTag, XML_TAG_THORSPAREPROCESS) == 0)
sName.appendf("spare%d", nSpare++);
else if (strcmp(szTag, XML_TAG_THORMASTERPROCESS) == 0)
sName = "m1";
else continue;
setAttribute(pProcessNode, XML_ATTR_NAME, sName);
setAttribute(&iter->query(), XML_ATTR_PROCESS, sName);
}
}
}
//----------------------------------------------------------------------------
// UpdateAttributes
//----------------------------------------------------------------------------
void CConfigEnvHelper::UpdateThorAttributes(IPropertyTree* pParentNode)
{
const char* masterIp = NULL;
bool localThor = true, multiSlaves = false;
int nSlaves = 0;
IPropertyTree* pNode = pParentNode->queryPropTree(XML_TAG_THORMASTERPROCESS);
if (pNode)
{
const char* szName = pNode->queryProp(XML_ATTR_COMPUTER);
setAttribute(pParentNode, XML_ATTR_COMPUTER, szName);
IPropertyTree* pComputer = lookupComputerByName(szName);
if (pComputer)
masterIp = pComputer->queryProp(XML_ATTR_NETADDRESS);
}
else
{
localThor = false;
}
Owned iter = pParentNode->getElements(XML_TAG_THORSLAVEPROCESS);
for (iter->first(); iter->isValid(); iter->next())
{
nSlaves++;
if (!localThor && multiSlaves)
continue;
const char* computer = iter->query().queryProp(XML_ATTR_COMPUTER);
if (computer && *computer)
{
if (localThor)
{
IPropertyTree* pNode = lookupComputerByName(computer);
if (pNode && masterIp && *masterIp)
{
const char* ip = pNode->queryProp(XML_ATTR_NETADDRESS);
if (ip && *ip && strcmp(ip, masterIp))
localThor = false;
}
}
if (!multiSlaves)
{
StringBuffer xpath(XML_TAG_THORSLAVEPROCESS);
xpath.appendf("["XML_ATTR_COMPUTER"='%s']", computer);
Owned iterNodes = pParentNode->getElements(xpath.str());
int count = 0;
ForEach(*iterNodes)
{
count++;
if (count > 1)
{
multiSlaves = true;
break;
}
}
}
}
}
setAttribute(pParentNode, XML_ATTR_MULTISLAVES, multiSlaves ? "true" : "false");
setAttribute(pParentNode, "@localThor", localThor ? "true" : "false");
StringBuffer sb;
sb.appendf("%d", nSlaves);
setAttribute(pParentNode, XML_ATTR_SLAVES, sb.str());
}
//---------------------------------------------------------------------------
// AddNewNodes
//---------------------------------------------------------------------------
bool CConfigEnvHelper::AddNewNodes(IPropertyTree* pThor, const char* szType, int nPort, IPropertyTreePtrArray& computers, bool validate, bool skipExisting, StringBuffer& usageList, int slavesPerNode)
{
// Get parent node
IPropertyTree* pParentNode = pThor;
if (validate)
{
for (int i = 0; i < (int) computers.size(); i++)
CheckTopologyComputerUse(computers[i], pThor, usageList);
}
if (usageList.length() > 0)
return false;
// Iterate through computer list
for (int i = 0; i < (int) computers.size(); i++)
{
// Check if we can add this computer
if (skipExisting && !CheckTopologyComputerUse(computers[i], pThor, usageList))
continue;
for (int j = 0; j < slavesPerNode; j++)
{
StringBuffer sName;
sName.appendf("temp%d", i + j + 1);
// Add process node
IPropertyTree* pProcessNode = createPTree(szType);
pProcessNode->addProp(XML_ATTR_NAME, sName);
pProcessNode->addProp(XML_ATTR_COMPUTER, computers[i]->queryProp(XML_ATTR_NAME));
if (nPort != 0) pProcessNode->addPropInt(XML_ATTR_PORT, nPort);
addNode(pProcessNode, pThor);
//some thor "Topology" have been known to have multiple (unused) "Node" children with the same name
//so reuse any unused Topology/Node
//
Owned iter = pThor->getElements(XML_TAG_TOPOLOGY"//"XML_TAG_NODE);
IPropertyTree* pNode = NULL;
ForEach(*iter)
{
// Get macthing process node
const char* szProcess = iter->query().queryProp(XML_ATTR_PROCESS);
IPropertyTree* pProcessNode = GetProcessNode(pThor, szProcess);
if (!pProcessNode)
{
pNode = &iter->query();
pNode->setProp(XML_ATTR_PROCESS, sName);
break;
}
}
if (!pNode)
{
// Add topology node
pNode = createPTree(XML_TAG_NODE);
pNode->addProp(XML_ATTR_PROCESS, sName);
IPropertyTree* pTopoNode = pThor->queryPropTree(XML_TAG_TOPOLOGY);
if (!pTopoNode)
pTopoNode = pThor->addPropTree(XML_TAG_TOPOLOGY, createPTree());
if (!strcmp(szType, XML_TAG_THORSLAVEPROCESS))
{
Owned iter = pThor->getElements(XML_TAG_TOPOLOGY"//"XML_TAG_NODE);
ForEach(*iter)
{
// Get macthing process node
const char* szProcess = iter->query().queryProp(XML_ATTR_PROCESS);
IPropertyTree* pProcessNode = GetProcessNode(pThor, szProcess);
if (!strcmp(pProcessNode->queryName(), XML_TAG_THORMASTERPROCESS))
{
addNode(pNode, &iter->query());
break;
}
}
}
else
addNode(pNode, pTopoNode);
}
}
}
RenameThorInstances(pThor);
UpdateThorAttributes(pThor);
return true;
}
bool CConfigEnvHelper::CheckTopologyComputerUse(IPropertyTree* pComputerNode, IPropertyTree* pParentNode, StringBuffer& usageList) const
{
const char* szNetAddress = pComputerNode->queryProp(XML_ATTR_NETADDRESS);
bool retVal = true;
StringArray sElementTypes;
StringBuffer xpath;
Owned iter = pParentNode->getElements("*");
for (iter->first(); iter->isValid(); iter->next())
{
const char* szTag = iter->query().queryName();
if (sElementTypes.find(szTag) == NotFound)
{
IPropertyTree* pTree = &iter->query();
const char* pszComputer = pTree->queryProp(XML_ATTR_COMPUTER);
xpath.clear().appendf(XML_TAG_HARDWARE"/"XML_TAG_COMPUTER"["XML_ATTR_NAME"='%s']", pszComputer);
IPropertyTree* pComputer = m_pRoot->queryPropTree(xpath.str());
const char* szNetAddress1 = pComputer?pComputer->queryProp(XML_ATTR_NETADDRESS):NULL;
if (szNetAddress1 && strcmp(szNetAddress1, szNetAddress)==0)
{
usageList.appendf("\n%s:%s - %s",
pComputerNode->queryProp(XML_ATTR_NAME),
pComputerNode->queryProp(XML_ATTR_NETADDRESS),
szTag);
// Save the found type and suppress warnings for those types
sElementTypes.append(szTag);
retVal = false;
}
}
}
return retVal;
}
//---------------------------------------------------------------------------
// GetProcessNode
//---------------------------------------------------------------------------
IPropertyTree* CConfigEnvHelper::GetProcessNode(IPropertyTree* pThor, const char* szProcess) const
{
if (szProcess && *szProcess)
{
Owned iter = pThor->getElements("*");
ForEach(*iter)
{
const char* szName = iter->query().queryProp(XML_ATTR_NAME);
if (szName && strcmp(szName, szProcess) == 0)
return &iter->query();
}
}
return NULL;
}