/*############################################################################## 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 "deploy.hpp" #include "environment.hpp" #include "jptree.hpp" #include "jexcept.hpp" #include "jencrypt.hpp" #include "xslprocessor.hpp" #include "DeploymentEngine.hpp" #include "ThorDeploymentEngine.hpp" #include "EspDeploymentEngine.hpp" #include "dalideploymentengine.hpp" #include "RoxieDeploymentEngine.hpp" #include "configgenengine.hpp" #include "espconfiggenengine.hpp" #include "thorconfiggenengine.hpp" //--------------------------------------------------------------------------- // CEnvironmentDeploymentEngine //--------------------------------------------------------------------------- class CEnvironmentDeploymentEngine : public CInterface, implements IEnvDeploymentEngine { public: IMPLEMENT_IINTERFACE; //--------------------------------------------------------------------------- // CEnvironmentDeploymentEngine //--------------------------------------------------------------------------- CEnvironmentDeploymentEngine(IConstEnvironment &environment, IDeploymentCallback& callback, IPropertyTree* pSelectedComponents) : m_environment(environment), m_transform(NULL), m_abort(false), m_espModuleCount(0), m_tempFileCount(0), m_bLinuxDeployment(false), m_bInteractiveMode(true) { m_pCallback.set(&callback); m_validateAllXsl.clear().append("validateAll.xsl"); if (pSelectedComponents) { initXML(pSelectedComponents); Owned it = pSelectedComponents->getElements("*"); ForEach(*it) { IPropertyTree* pComponent = &it->query(); IDeploymentEngine* pEngine = addProcess(pComponent->queryName(), pComponent->queryProp("@name")); Owned iter = pComponent->getElements("*"); if (iter->first()) { pEngine->resetInstances(); ForEach(*iter) { IPropertyTree* pChild = &iter->query(); const char* tagName = pChild->queryName(); const char* instName = pChild->queryProp("@name"); pEngine->addInstance(tagName, instName); //determine if this is linux deployment if (!m_bLinuxDeployment) { const char* computer = pChild->queryProp("@computer"); IConstMachineInfo* pMachine = environment.getMachine(computer); if (pMachine->getOS() == MachineOsLinux) m_bLinuxDeployment = true; } } } else if (!m_bLinuxDeployment)//another previously added engine already does not have linux instance { //some components like thor and hole clusters don't show their instances in the //deployment wizard so detect if they have any linux instance. const IArrayOf& instances = pEngine->getInstances(); if (instances.ordinality() > 0) { Owned machine = m_environment.getMachine(instances.item(0).queryProp("@computer")); if (machine && machine->getOS() == MachineOsLinux) m_bLinuxDeployment = true; } } } } #ifdef _WINDOWS EnumerateNetworkConnections(); #endif } //--------------------------------------------------------------------------- // ~CEnvironmentDeploymentEngine //--------------------------------------------------------------------------- virtual ~CEnvironmentDeploymentEngine() { //delete all temporary files generated during deployemnt int count = m_tempFiles.length(); int i; for (i = 0; i < count; i++) DeleteFile(m_tempFiles.item(i)); count = m_tempDirs.length(); for (i = 0; i < count; i++) deleteRecursive(m_tempDirs.item(i)); m_processes.kill(); // must do this before destroying deplyCallback and deployLog m_pDeployLog.clear(); // this causes the log file to be written m_pCallback.clear(); termXML(); } void deleteRecursive(const char* path) { Owned pDir = createIFile(path); if (pDir->exists()) { if (pDir->isDirectory()) { Owned it = pDir->directoryFiles(NULL, false, true); ForEach(*it) { StringBuffer name; it->getName(name); StringBuffer childPath(path); childPath.append(PATHSEPCHAR); childPath.append(name); deleteRecursive(childPath.str()); } } pDir->remove(); } } //--------------------------------------------------------------------------- // addProcess //--------------------------------------------------------------------------- IDeploymentEngine* addProcess(const char* processType, const char* processName) { assertex(processType); assertex(processName); StringBuffer xpath; xpath.appendf("Software/%s[@name='%s']", processType, processName); Owned tree = &m_environment.getPTree(); IPropertyTree* pComponent = tree->queryPropTree(xpath.str()); if (!pComponent) throw MakeStringException(0, "%s with name %s was not found!", processType, processName); IDeploymentEngine* deployEngine; if (strcmp(processType, "DaliServerProcess")==0) deployEngine = new CDaliDeploymentEngine(*this, *m_pCallback, *pComponent); else if (strcmp(processType, "ThorCluster")==0) deployEngine = new CThorDeploymentEngine(*this, *m_pCallback, *pComponent); else if (strcmp(processType, "RoxieCluster")==0) deployEngine = new CRoxieDeploymentEngine(*this, *m_pCallback, *pComponent); else if (strcmp(processType, "EspProcess")==0) deployEngine = new CEspDeploymentEngine(*this, *m_pCallback, *pComponent); else deployEngine = new CDeploymentEngine(*this, *m_pCallback, *pComponent, "Instance", true); assertex(deployEngine); deployEngine->setXsl(m_processor, m_transform); m_processes.append(*deployEngine); // array releases members when destroyed return deployEngine; } //--------------------------------------------------------------------------- // setSshAccount //--------------------------------------------------------------------------- void setSshAccount(const char* userid, const char* password) { m_sSshUserid = userid; m_sSshPassword = password; } //--------------------------------------------------------------------------- // isLinuxDeployment //--------------------------------------------------------------------------- bool isLinuxDeployment() const { return m_bLinuxDeployment; } //--------------------------------------------------------------------------- // start //--------------------------------------------------------------------------- void start() { ForEachItemIn(idx, m_processes) { m_processes.item(idx).start(); } } //--------------------------------------------------------------------------- // stop //--------------------------------------------------------------------------- void stop() { ForEachItemInRev(idx, m_processes) { m_processes.item(idx).stop(); } } //--------------------------------------------------------------------------- // stripXsltMessage //--------------------------------------------------------------------------- void stripXsltMessage(StringBuffer& msg) { //for better readability of msg, remove redundant prefix of "[XSLT warning: ", if present const char* pattern = "[ElemMessageTerminateException: "; const int len =sizeof("[ElemMessageTerminateException: ")-1; if (!strncmp(msg, pattern, len)) msg.remove(0, len); //remove the excessive info about XSLT context when this was thrown const char* begin = msg.str(); const char* end = strstr(begin, "(file:"); if (end) msg.setLength(end-begin); } //--------------------------------------------------------------------------- // check //--------------------------------------------------------------------------- void check() { if (m_pCallback->getAbortStatus()) throw MakeStringException(0, "User abort"); bool valid = false; m_nValidationErrors = 0; StringBuffer outputXml; Owned externalFunction; externalFunction.setown(m_transform->createExternalFunction("validationMessage", validationMessageFromXSLT)); m_transform->setExternalFunction(SEISINT_NAMESPACE, externalFunction.get(), true); m_transform->loadXslFromFile(m_validateAllXsl.str()); m_transform->setUserData(this); try { m_transform->transform( outputXml ); m_transform->closeResultTarget(); const char* msg = m_transform->getMessages(); if (msg && *msg) { /* //there may be multiple warnings messages bundled here so process each of them: StringArray msgs; DelimToStringArray(msg, msgs, "\n"); ForEachItemIn(idx, msgs) { msg = msgs.item(idx); if (msg && *msg) m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, msg); } */ m_sValidationErrors.append(msg); m_nValidationErrors++; } if (!m_nValidationErrors)//this may get filled in by the external function valid = true; } catch (IException* e) { StringBuffer msg; e->errorMessage(msg); e->Release(); stripXsltMessage(msg); m_sValidationErrors.append(msg); } catch (...) { m_sValidationErrors.appendf("Validation failed: Unspecified XSL error!"); } m_transform->setExternalFunction(SEISINT_NAMESPACE, externalFunction.get(), false); m_transform->setUserData(NULL); if (!valid) { const char* errors = m_sValidationErrors.str(); if (!errors || !*errors) errors = "Continue?"; m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Preliminary validation failed!"); while ( !m_pCallback->processException(NULL, NULL, NULL, NULL, errors, "Preliminary validation failed!", NULL) ) ; valid = true; //ignore validation errors } if (valid) { ForEachItemIn(idx, m_processes) m_processes.item(idx).check(); m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL); } } //--------------------------------------------------------------------------- // compare //--------------------------------------------------------------------------- void compare(unsigned mode) { ForEachItemIn(idx, m_processes) { m_processes.item(idx).compare(mode); } } //--------------------------------------------------------------------------- // deploy //--------------------------------------------------------------------------- void deploy(unsigned flags, bool useTempDir) { m_tempFileCount = m_espModuleCount = 0; if (flags != DEFLAGS_NONE) { ForEachItemIn(idx, m_processes) { m_processes.item(idx).deploy(flags, useTempDir); } } } //--------------------------------------------------------------------------- // deploy //--------------------------------------------------------------------------- void deploy(unsigned flags, BackupMode backupMode, bool bStop, bool bStart) { switch (backupMode) { case DEBACKUP_NONE: if (bStop) stop(); deploy(flags, false); if (bStart) start(); break; case DEBACKUP_COPY: backupDirs(); if (bStop) stop(); deploy(flags, false); if (bStart) start(); break; case DEBACKUP_RENAME: deploy(flags, true); if (bStop) stop(); renameDirs(); if (bStart) start(); break; default: assertex(false); } } //--------------------------------------------------------------------------- // renameDirs //--------------------------------------------------------------------------- void renameDirs() { ForEachItemIn(idx, m_processes) { m_processes.item(idx).renameDirs(); } } //--------------------------------------------------------------------------- // backupDirs //--------------------------------------------------------------------------- void backupDirs() { ForEachItemIn(idx, m_processes) { m_processes.item(idx).backupDirs(); } } //--------------------------------------------------------------------------- // abort //--------------------------------------------------------------------------- void abort() { m_abort = true; ForEachItemIn(idx, m_processes) { m_processes.item(idx).abort(); } } //--------------------------------------------------------------------------- // archive //--------------------------------------------------------------------------- void archive(const char* filename) { if (!filename || !*filename) return; if (m_abort) { m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Aborted!"); throw MakeStringException(0, "User abort"); } m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Archiving environment data to %s...", filename); Owned tree = &m_environment.getPTree(); StringBuffer xml; toXML(tree, xml); Owned task = createDeployTask(*m_pCallback, "Archive File", NULL, NULL, NULL, NULL, filename, "", "", "", false); task->createFile(xml.str()); m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL); if (task->getAbort()) throw MakeStringException(0, "User abort"); Owned pFile = createIFile(filename); pFile->setReadOnly(true); } //--------------------------------------------------------------------------- // setLog //--------------------------------------------------------------------------- void setLog(const char* filename, const char* envname) { if (!filename || !*filename) return; m_pDeployLog.setown(createDeployLog(*m_pCallback, filename, envname)); } //--------------------------------------------------------------------------- // initXML //--------------------------------------------------------------------------- void initXML(IPropertyTree* pSelectedComponents) { if (m_abort) throw MakeStringException(0, "User abort"); m_pCallback->printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Loading environment..."); m_processor.setown(getXslProcessor()); m_transform.setown(m_processor->createXslTransform()); //decrypt external function is no longer used by any xslt // //m_externalFunction.setown(m_transform->createExternalFunction("decrypt", decrypt)); //m_transform->setExternalFunction(SEISINT_NAMESPACE, m_externalFunction.get(), true); Owned tree = &m_environment.getPTree(); IPropertyTree* pDeploy = tree->queryPropTree("DeployComponents"); if (pDeploy) tree->removeTree(pDeploy); pDeploy = tree->addPropTree("DeployComponents", createPTreeFromIPT(pSelectedComponents)); StringBuffer xml; toXML(tree, xml); tree->removeTree(pDeploy); if (m_transform->setXmlSource(xml.str(), xml.length()) != 0) throw MakeStringException(0, "Invalid environment XML string"); } //--------------------------------------------------------------------------- // termXML //--------------------------------------------------------------------------- void termXML() { //decrypt external function is no longer used by any xslt // //m_transform->setExternalFunction(SEISINT_NAMESPACE, m_externalFunction.get(), false); m_externalFunction.clear(); m_transform.clear(); m_processor.clear(); } //--------------------------------------------------------------------------- // incrementTempFileCount //--------------------------------------------------------------------------- virtual int incrementTempFileCount() { return ++m_tempFileCount; } //--------------------------------------------------------------------------- // incrementEspModuleCount //--------------------------------------------------------------------------- virtual int incrementEspModuleCount() { return ++m_espModuleCount; } //--------------------------------------------------------------------------- // validationMessageFromXSLT //--------------------------------------------------------------------------- static void validationMessageFromXSLT(StringBuffer &ret, const char *in, IXslTransform* pTransform) { CEnvironmentDeploymentEngine* pEnvDepEngine = (CEnvironmentDeploymentEngine*) pTransform->getUserData(); IDeploymentCallback* pCallback = pEnvDepEngine->m_pCallback; //input 'in' has format of the form [type]:[compType]:[compName]:[message] //type is either 'error' or 'warning' and any of the other parts may be empty strings // StringArray sArray; DelimToStringArray(in, sArray, ":"); if (sArray.ordinality() != 4) { pCallback->printStatus(STATUS_ERROR, NULL, NULL, NULL, "%s", in); return; } const char* msgType = sArray.item(0); const char* compType = sArray.item(1); const char* compName = sArray.item(2); const char* msg = sArray.item(3); if (compType && !*compType) compType = NULL; if (compName && !*compName) compName = NULL; StatusType statusType; if (!stricmp(msgType, "error")) { statusType = STATUS_ERROR; pEnvDepEngine->m_nValidationErrors++; if (!compType || !compName)//if this error is not being reported under a particular component in tree pEnvDepEngine->m_sValidationErrors.append(msg).append("\n"); } else if (!strnicmp(msgType, "warn", 4)) statusType = STATUS_WARN; else if (!stricmp(msgType, "OK")) statusType = STATUS_OK; else if (!strnicmp(msgType, "inc", 3)) statusType = STATUS_INCOMPLETE; else statusType = !stricmp(msgType, "normal") ? STATUS_NORMAL : STATUS_ERROR; try { if (pCallback) pCallback->printStatus( statusType, compType, compName, NULL, "%s", msg); } catch (IException* e) { StringBuffer buf; e->errorMessage(buf); e->Release(); pCallback->printStatus(STATUS_ERROR, NULL, NULL, NULL, "%s", buf.str()); } catch(...) { pCallback->printStatus(STATUS_ERROR, NULL, NULL, NULL, "Unknown exception!"); } } //--------------------------------------------------------------------------- // getCallback //--------------------------------------------------------------------------- virtual IDeploymentCallback& getCallback() const { return *m_pCallback; } //--------------------------------------------------------------------------- // setInteractiveMode //--------------------------------------------------------------------------- virtual void setInteractiveMode(bool bSet) { m_bInteractiveMode = bSet; } //--------------------------------------------------------------------------- // getEnvironment //--------------------------------------------------------------------------- IConstEnvironment& getEnvironment() const { return m_environment; } //--------------------------------------------------------------------------- // getInteractiveMode //--------------------------------------------------------------------------- virtual bool getInteractiveMode() const { return m_bInteractiveMode; } //--------------------------------------------------------------------------- // getDeployLog //--------------------------------------------------------------------------- virtual IDeployLog* getDeployLog() { return m_pDeployLog ? m_pDeployLog.getLink() : NULL; } //--------------------------------------------------------------------------- // addTempFile //--------------------------------------------------------------------------- virtual void addTempFile(const char* filePath) { if (filePath) m_tempFiles.append(filePath); } //--------------------------------------------------------------------------- // addTempDirectory //--------------------------------------------------------------------------- virtual void addTempDirectory(const char* dirPath) { if (dirPath) m_tempDirs.append(dirPath); } //--------------------------------------------------------------------------- // setDeployToFolder //--------------------------------------------------------------------------- virtual void setDeployToFolder(const char* path) { StringBuffer machineName; StringBuffer localPath; StringBuffer tail; StringBuffer ext; if (splitUNCFilename(path, &machineName, &localPath, &tail, &ext)) { const char* hostName = machineName.str() + 2; Owned machine = m_environment.getMachine(hostName); if (!machine) throw MakeStringException(-1, "The computer '%s' used for deployment folder is undefined!", hostName); StringAttr netAddress; StringAttrAdaptor adaptor(netAddress); machine->getNetAddress(adaptor); if (!netAddress.get() || !*netAddress.get()) throw MakeStringException(-1, "The computer '%s' used for deployment folder does not have any network address defined!", hostName); StringBuffer uncPath(PATHSEPSTR PATHSEPSTR); uncPath.append( netAddress.get() ); if (*localPath.str() != PATHSEPCHAR) uncPath.append( PATHSEPCHAR ); uncPath.append( localPath );//note that the path ends with PATHSEPCHAR uncPath.append( tail ); uncPath.append( ext ); m_sDeployToFolder.set( uncPath.str() ); getAccountInfo(hostName, m_sDeployToUser, m_sDeployToPswd); } else m_sDeployToFolder.set( path ); /* Owned task = createDeployTask(*m_pCallback, "Create Directory", NULL, NULL, NULL, NULL, m_sDeployToFolder.get()); m_pCallback->printStatus(task); task->createDirectory(); m_pCallback->printStatus(task); */ } //--------------------------------------------------------------------------- // getDeployToFolder //--------------------------------------------------------------------------- virtual const char* getDeployToFolder() const { return m_sDeployToFolder.get(); } //--------------------------------------------------------------------------- // getDeployToAccountInfo //--------------------------------------------------------------------------- virtual void getDeployToAccountInfo(const char*& user, const char*& pswd) const { user = m_sDeployToUser.get(); pswd = m_sDeployToPswd.get(); } //--------------------------------------------------------------------------- // lookupNetAddress //--------------------------------------------------------------------------- StringAttr& lookupNetAddress(StringAttr& str, const char* computer) const { Owned machine = m_environment.getMachine(computer); if (machine) { StringAttrAdaptor adaptor(str); machine->getNetAddress(adaptor); } return str; } //--------------------------------------------------------------------------- // lookupMachineOS //--------------------------------------------------------------------------- EnvMachineOS lookupMachineOS(IPropertyTree& node) const { Owned machine = m_environment.getMachine(node.queryProp("@computer")); return machine ? machine->getOS() : MachineOsUnknown; } //--------------------------------------------------------------------------- // getAccountInfo //--------------------------------------------------------------------------- void getAccountInfo(const char* computer, StringAttr& user, StringAttr& pwd) const { Owned machine = m_environment.getMachine(computer); if (machine) { Owned domain = machine->getDomain(); if (!domain) throw MakeStringException(-1, "The computer '%s' does not have any domain information!", computer); StringBuffer x; if (machine->getOS() == MachineOsW2K) { domain->getName(StringBufferAdaptor(x)); if (x.length()) x.append(PATHSEPCHAR); } domain->getAccountInfo(StringBufferAdaptor(x), StringAttrAdaptor(pwd)); user.set(x.str()); } else throw MakeStringException(-1, "The computer '%s' is undefined!", computer); } //--------------------------------------------------------------------------- // getSSHAccountInfo //--------------------------------------------------------------------------- void getSSHAccountInfo(const char* computer, StringAttr& user, StringAttr& sshKeyFile, StringAttr& sshKeyPassphrase) const { Owned machine = m_environment.getMachine(computer); if (machine) { Owned domain = machine->getDomain(); if (!domain) throw MakeStringException(-1, "The computer '%s' does not have any domain information!", computer); StringBuffer x; if (machine->getOS() == MachineOsW2K) { domain->getName(StringBufferAdaptor(x)); if (x.length()) x.append(PATHSEPCHAR); } domain->getSSHAccountInfo(StringBufferAdaptor(x), StringAttrAdaptor(sshKeyFile), StringAttrAdaptor(sshKeyPassphrase)); user.set(x.str()); } else throw MakeStringException(-1, "The computer '%s' is undefined!", computer); } virtual void setSourceDaliAddress( const char* addr ) { m_sSrcDaliAddress.set( addr ); } virtual const char* getSourceDaliAddress() { return m_sSrcDaliAddress.get(); } virtual IArrayOf& queryProcesses() { return m_processes; } #ifdef _WINDOWS void NetErrorHandler(DWORD dwResult) { StringBuffer out; formatSystemError(out, dwResult); out.insert(0, "Failed to enumerate existing network connections:\n"); while ( !m_pCallback->processException(NULL, NULL, NULL, NULL, out, "Network Error", NULL) ) ; } bool EnumerateNetworkConnections() { DWORD dwResult; HANDLE hEnum; DWORD cbBuffer = 16384; // 16K is a good size DWORD cEntries = -1; // enumerate all possible entries LPNETRESOURCE lpnr; // pointer to enumerated structures // // Call the WNetOpenEnum function to begin the enumeration. // dwResult = WNetOpenEnum(RESOURCE_CONNECTED, // connected network resources RESOURCETYPE_ANY,// all resources 0, // enumerate all resources NULL, // NULL first time the function is called &hEnum); // handle to the resource if (dwResult != NO_ERROR) { NetErrorHandler(dwResult); return false; } // // Call the GlobalAlloc function to allocate resources. // lpnr = (LPNETRESOURCE) GlobalAlloc(GPTR, cbBuffer); do { ZeroMemory(lpnr, cbBuffer); // Call the WNetEnumResource function to continue // the enumeration. // dwResult = WNetEnumResource( hEnum, // resource handle &cEntries, // defined locally as -1 lpnr, // LPNETRESOURCE &cbBuffer); // buffer size // If the call succeeds, loop through the structures. // if (dwResult == NO_ERROR) { StringBuffer networkPath; for (DWORD i = 0; i < cEntries; i++) if (lpnr[i].lpRemoteName) { // make a valid UNC path to connect to and see if we are not already connected if (CDeploymentEngine::stripTrailingDirsFromUNCPath(lpnr[i].lpRemoteName, networkPath.clear()) && m_persistentConnections.find( networkPath.str() ) == m_persistentConnections.end()) { //::MessageBox(NULL, networkPath.str(), lpnr[i].lpRemoteName, MB_OK); m_persistentConnections.insert( networkPath.str() ); } } } else if (dwResult != ERROR_NO_MORE_ITEMS) { NetErrorHandler(dwResult); break; } } while (dwResult != ERROR_NO_MORE_ITEMS); GlobalFree((HGLOBAL)lpnr); // free the memory dwResult = WNetCloseEnum(hEnum); // end the enumeration if (dwResult != NO_ERROR) { NetErrorHandler(dwResult); return false; } return true; } #endif//WINDOWS virtual bool IsPersistentConnection(const char* networkPath) const { return m_persistentConnections.find( networkPath ) != m_persistentConnections.end(); } protected: IArrayOf m_processes; IConstEnvironment& m_environment; Owned m_pCallback; Owned m_processor; Owned m_transform; Owned m_externalFunction; Owned m_pDeployLog; set m_persistentConnections; int m_espModuleCount; int m_tempFileCount; bool m_abort; bool m_bLinuxDeployment; bool m_bInteractiveMode; StringBuffer m_sSshUserid; StringBuffer m_sSshPassword; StringBuffer m_validateAllXsl; unsigned int m_nValidationErrors; StringBuffer m_sValidationErrors; StringArray m_tempFiles; StringArray m_tempDirs; StringAttr m_sDeployToFolder; StringAttr m_sDeployToUser; StringAttr m_sDeployToPswd; StringAttr m_sSrcDaliAddress; }; class CConfigGenMgr : public CEnvironmentDeploymentEngine { public: IMPLEMENT_IINTERFACE; //--------------------------------------------------------------------------- // CConfigGenMgr //--------------------------------------------------------------------------- CConfigGenMgr(IConstEnvironment& environment, IDeploymentCallback& callback, IPropertyTree* pSelectedComponents, const char* inputDir, const char* outputDir, const char* compName, const char* compType, const char* ipAddr) : CEnvironmentDeploymentEngine(environment, callback, pSelectedComponents), m_inDir(inputDir), m_outDir(outputDir), m_compName(compName), m_compType(compType), m_hostIpAddr(ipAddr) { m_validateAllXsl.clear().append(inputDir); if (m_validateAllXsl.length() > 0 && m_validateAllXsl.charAt(m_validateAllXsl.length() - 1) != PATHSEPCHAR) m_validateAllXsl.append(PATHSEPCHAR); m_validateAllXsl.append("validateAll.xsl"); Owned pSelComps; if (!pSelectedComponents) { Owned pEnvTree = &m_environment.getPTree(); pSelComps.setown(getInstances(pEnvTree, compName, compType, ipAddr)); pSelectedComponents = pSelComps; } initXML(pSelectedComponents); { Owned it = pSelectedComponents->getElements("*"); ForEach(*it) { IPropertyTree* pComponent = &it->query(); IDeploymentEngine* pEngine = addProcess(pComponent->queryName(), pComponent->queryProp("@name")); Owned iter = pComponent->getElements("*"); if (iter->first()) { pEngine->resetInstances(); ForEach(*iter) { IPropertyTree* pChild = &iter->query(); const char* tagName = pChild->queryName(); const char* instName = pChild->queryProp("@name"); pEngine->addInstance(tagName, instName); //determine if this is linux deployment if (!m_bLinuxDeployment) { const char* computer = pChild->queryProp("@computer"); Owned pMachine = environment.getMachine(computer); if (!pMachine) throw MakeStringException(0, "Invalid Environment file. Instance '%s' of '%s' references a computer '%s' that has not been defined!", pChild->queryProp("@name"), pComponent->queryProp("@name"), computer); else if (pMachine->getOS() == MachineOsLinux) m_bLinuxDeployment = true; } } } else if (!m_bLinuxDeployment)//another previously added engine already does not have linux instance { //some components like thor and hole clusters don't show their instances in the //deployment wizard so detect if they have any linux instance. const IArrayOf& instances = pEngine->getInstances(); if (instances.ordinality() > 0) { Owned machine;// = m_environment.getMachine(instances.item(0).queryProp("@computer")); if (machine && machine->getOS() == MachineOsLinux) m_bLinuxDeployment = true; } } } } } //--------------------------------------------------------------------------- // addProcess //--------------------------------------------------------------------------- IDeploymentEngine* addProcess(const char* processType, const char* processName) { assertex(processType); assertex(processName); StringBuffer xpath; xpath.appendf("Software/%s[@name='%s']", processType, processName); Owned tree = &m_environment.getPTree(); IPropertyTree* pComponent = tree->queryPropTree(xpath.str()); if (!pComponent) throw MakeStringException(0, "%s with name %s was not found!", processType, processName); IDeploymentEngine* deployEngine; if (strcmp(processType, "RoxieCluster")==0) deployEngine = new CConfigGenEngine(*this, *m_pCallback, *pComponent, m_inDir, m_outDir, "*"); else if (strcmp(processType, "ThorCluster")==0) deployEngine = new CThorConfigGenEngine(*this, *m_pCallback, *pComponent, m_inDir, m_outDir); else if (strcmp(processType, "EspProcess")==0) deployEngine = new CEspConfigGenEngine(*this, *m_pCallback, *pComponent, m_inDir, m_outDir); else deployEngine = new CConfigGenEngine(*this, *m_pCallback, *pComponent, m_inDir, m_outDir, "Instance", true); assertex(deployEngine); deployEngine->setXsl(m_processor, m_transform); m_processes.append(*deployEngine); // array releases members when destroyed return deployEngine; } void deploy(unsigned flags, BackupMode backupMode, bool bStop, bool bStart) { switch (backupMode) { case DEBACKUP_NONE: check(); CEnvironmentDeploymentEngine::deploy(flags, false); break; case DEBACKUP_COPY: throw MakeStringException(-1, "Invalid option Backup copy while generating configurations"); case DEBACKUP_RENAME: throw MakeStringException(-1, "Invalid option Backup rename while generating configurations"); default: throw MakeStringException(-1, "Invalid option while generating configurations"); } } private: StringBuffer m_inDir; StringBuffer m_outDir; StringBuffer m_compName; StringBuffer m_compType; StringBuffer m_hostIpAddr; }; //--------------------------------------------------------------------------- // Factory functions //--------------------------------------------------------------------------- IEnvDeploymentEngine* createEnvDeploymentEngine(IConstEnvironment& environment, IDeploymentCallback& callback, IPropertyTree* pSelectedComponents) { try { return new CEnvironmentDeploymentEngine(environment, callback, pSelectedComponents); } catch (IException* e) { throw e; } catch(...) { throw MakeStringException(-1, "Unknown exception!"); } } IEnvDeploymentEngine* createConfigGenMgr(IConstEnvironment& env, IDeploymentCallback& callback, IPropertyTree* pSelectedComponents, const char* inputDir, const char* outputDir, const char* compName, const char* compType, const char* ipAddr) { try { StringBuffer inDir(inputDir); if (inDir.length() && inDir.charAt(inDir.length() - 1) != PATHSEPCHAR) inDir.append(PATHSEPCHAR); StringBuffer outDir(outputDir); if (outDir.length() && outDir.charAt(outDir.length() - 1) != PATHSEPCHAR) outDir.append(PATHSEPCHAR); return new CConfigGenMgr(env, callback, pSelectedComponents, inDir.str(), outDir.str(), compName, compType, ipAddr); } catch (IException* e) { throw e; } catch(...) { throw MakeStringException(-1, "Unknown exception!"); } } bool matchDeployAddress(const char *searchIP, const char *envIP) { if (searchIP && envIP && *searchIP && *envIP) { IpAddress ip(envIP); if (strcmp(searchIP, ".")==0) return ip.isLocal(); else { IpAddress ip2(searchIP); return ip.ipequals(ip2); } } return false; } IPropertyTree* getInstances(const IPropertyTree* pEnvRoot, const char* compName, const char* compType, const char* ipAddr, bool listall) { Owned pSelComps(createPTree("SelectedComponents")); Owned iter = pEnvRoot->getElements("Software/*"); const char* instanceNodeNames[] = { "Instance", "RoxieServerProcess", "RoxieSlaveProcess" }; const char* logDirNames[] = { "@logDir", "@LogDir", "@dfuLogDir", "@eclLogDir" }; ForEach(*iter) { IPropertyTree* pComponent = &iter->query(); const char* type = pComponent->queryName(); if (stricmp(type, "Topology")!=0 && stricmp(type, "Directories")!=0 && ((!compName && !compType) || (compName && !strcmp(pComponent->queryProp("@name"), compName)) || (!compName && compType && !strcmp(pComponent->queryProp("@buildSet"), compType)))) { const char* name = pComponent->queryProp("@name"); const char* build = pComponent->queryProp("@build"); const char* buildSet= pComponent->queryProp("@buildSet"); const char* logDir = NULL; if (listall) for (int i = 0; i < sizeof(logDirNames)/sizeof(char*); i++) { logDir = pComponent->queryProp(logDirNames[i]); if (logDir) break; } StringBuffer sXPath; sXPath.appendf("Programs/Build[@name='%s']/BuildSet[@name='%s']/@deployable", build, buildSet); const char* deployable = pEnvRoot->queryProp(sXPath.str()); //either the @deployable does not exist or it is not one of 'no', 'false' or '0' if (!deployable || (strcmp(deployable, "no") != 0 && strcmp(deployable, "false") != 0 && strcmp(deployable, "0") != 0)) { IPropertyTree* pSelComp = NULL; Owned iterInst = pComponent->getElements("*", iptiter_sort); bool bAdded = false; ForEach(*iterInst) { IPropertyTree* pInst = &iterInst->query(); const char* computer = pInst->queryProp("@computer"); const char* netAddr = pInst->queryProp("@netAddress"); if (!computer || !*computer || !strcmp("Notes", pInst->queryName())) continue; if (!strcmp(buildSet, "thor")) { sXPath.clear().appendf("Hardware/Computer[@name=\"%s\"]", computer); IPropertyTree* pComputer = pEnvRoot->queryPropTree(sXPath.str()); netAddr = pComputer->queryProp("@netAddress"); if (matchDeployAddress(ipAddr, netAddr) || (!ipAddr && netAddr && *netAddr)) { if (!bAdded) { pSelComp = pSelComps->addPropTree(pComponent->queryName(), createPTree()); pSelComp->addProp("@name", name); pSelComp->addProp("@buildSet", buildSet); pSelComp->addProp("@logDir", logDir); bAdded = true; } if (listall) { IPropertyTree* pInstance = pSelComp->addPropTree(pInst->queryName(), createPTree()); pInstance->addProp("@name", pInst->queryProp("@name")); pInstance->addProp("@computer", computer); pInstance->addProp("@netAddress", netAddr); } } } else if (matchDeployAddress(ipAddr, netAddr) || (!ipAddr && netAddr && *netAddr)) { if (!bAdded) { pSelComp = pSelComps->addPropTree(pComponent->queryName(), createPTree()); pSelComp->addProp("@name", name); pSelComp->addProp("@buildSet", buildSet); pSelComp->addProp("@logDir", logDir); bAdded = true; } StringBuffer sb(pInst->queryName()); for (UINT i=0; iqueryPropTree(StringBuffer().appendf("*[@computer=\"%s\"]", computer))) { IPropertyTree* pInstance = pSelComp->addPropTree(sb.str(), createPTree()); pInstance->addProp("@name", pInst->queryProp("@name")); pInstance->addProp("@computer", pInst->queryProp("@computer")); pInstance->addProp("@port", pInst->queryProp("@port")); pInstance->addProp("@netAddress", pInst->queryProp("@netAddress")); const char* directory = pInst->queryProp(sb.str()[0]=='R' ? "@dataDirectory" : "@directory"); if (directory && *directory) pInstance->addProp("@directory", directory); } break; } } } } } } return pSelComps.getLink(); } //--------------------------------------------------------------------------- // Module Globals //--------------------------------------------------------------------------- const char* findFileExtension(const char* pszPath) { const char* lastSlash = pathTail(pszPath); return strrchr(lastSlash ? lastSlash : pszPath, '.'); } void removeTrailingPathSepChar(char* pszPath) { if (pszPath) { char* lastChar = pszPath + strlen(pszPath) - 1; if (isPathSepChar(*lastChar)) *lastChar = '\0'; } } void stripNetAddr(const char* dir, StringBuffer& destpath, StringBuffer& destip, bool makeLinux) { destpath.clear().append(dir); if (dir[0] == '\\' && dir[1] == '\\' && strlen(dir) > 2) { destip.clear().append(strchr(dir + 2, '\\') - (dir + 2), dir + 2); destpath.clear().append(strchr(dir + 2, '\\')); } if (makeLinux) destpath.replace('\\', '/'); } //returns temp path that ends with path sep // #ifdef _WIN32 extern DWORD getLastError() { return ::GetLastError(); } void getTempPath(char* tempPath, unsigned int bufsize, const char* subdir/*=NULL*/) { ::GetTempPath(bufsize, tempPath); ::GetLongPathName(tempPath, tempPath, bufsize); if (subdir && *subdir) { const int len = strlen(tempPath); char* p = tempPath + len; strcpy(p, subdir); p += strlen(subdir); *p++ = '\\'; *p = '\0'; } } #else//Linux specifics follow extern DWORD getLastError() { return errno; } void getTempPath(char* tempPath, unsigned int bufsize, const char* subdir/*=NULL*/) { assert(bufsize > 5); strcpy(tempPath, "/tmp/"); if (subdir && *subdir) { strcat(tempPath, subdir); strcat(tempPath, "/"); } } #endif