WsDeployEngine.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #include "WsDeployService.hpp"
  14. #include "WsDeployEngine.hpp"
  15. class CWsGenerateJSFromXsdThread : public CInterface,
  16. implements IPooledThread
  17. {
  18. public:
  19. IMPLEMENT_IINTERFACE;
  20. CWsGenerateJSFromXsdThread()
  21. {
  22. }
  23. virtual ~CWsGenerateJSFromXsdThread()
  24. {
  25. }
  26. virtual void init(void *startInfo) override
  27. {
  28. m_pTask.set((IDeployTask*)startInfo);
  29. }
  30. virtual void threadmain() override
  31. {
  32. m_pTask->copyFile( m_pTask->getFlags() );
  33. static Mutex m;
  34. m.lock();
  35. try
  36. {
  37. m_pTask->getCallback().printStatus(m_pTask);
  38. }
  39. catch(IException* e)
  40. {
  41. e->Release();
  42. }
  43. m.unlock();
  44. if (m_pTask && m_pTask->getAbort())
  45. {
  46. m_pTask->getCallback().printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Aborting, please wait...");
  47. throw MakeStringException(0, "Abort");
  48. }
  49. }
  50. virtual bool canReuse() const override
  51. {
  52. return true;
  53. }
  54. virtual bool stop() override
  55. {
  56. return true;
  57. }
  58. virtual IDeployTask* getTask () const { return m_pTask; }
  59. virtual void setTask (IDeployTask* pTask) { m_pTask.set(pTask); }
  60. virtual bool getAbort() const { return s_abort; }
  61. virtual void setAbort(bool bAbort) { s_abort = bAbort; }
  62. private:
  63. Owned<IDeployTask> m_pTask;
  64. static bool s_abort;
  65. };
  66. CWsDeployEngine::CWsDeployEngine( CWsDeployExCE& service, IEspContext* ctx, IConstDeployInfo& deployInfo, const char* selComps, short version)
  67. : m_service(service),
  68. m_bAbort(false),
  69. m_bEnvUpdated(false),
  70. m_errorCount(0),
  71. m_version(version),
  72. m_depInfo(deployInfo),
  73. m_pDeploy(createPTreeFromXMLString(selComps))
  74. {
  75. m_pResponseXml.setown( createPTreeFromXMLString(selComps) );
  76. if (!m_pResponseXml)
  77. return;
  78. Owned<IPropertyTreeIterator> it = m_pResponseXml->getElements("SelectedComponents/*");
  79. ForEach(*it)
  80. {
  81. IPropertyTree* pComp = &it->query();
  82. IPropertyTree* pTasks = pComp->queryPropTree("Tasks");
  83. if (pTasks)
  84. pComp->removeTree(pTasks);
  85. pTasks = pComp->addPropTree("Tasks", createPTree());
  86. Owned<IPropertyTreeIterator> itInstances = pComp->getElements("Instance");
  87. ForEach(*itInstances)
  88. {
  89. string key;
  90. key += pComp->queryProp("@name");
  91. key += ':';
  92. IPropertyTree* pInstance = &itInstances->query();
  93. key += pInstance->queryProp("@name");
  94. m_comp2TasksMap.insert( pair<string, IPropertyTree*>(key, pTasks) );
  95. }
  96. }
  97. }
  98. CWsDeployEngine::CWsDeployEngine( CWsDeployExCE& service, IConstDeployInfo& deployInfo, IEspContext* ctx)
  99. : m_service(service),
  100. m_depInfo(deployInfo),
  101. m_bAbort(false),
  102. m_bEnvUpdated(false),
  103. m_errorCount(0),
  104. m_version(1)
  105. {
  106. //init our cache
  107. //
  108. IArrayOf<IConstComponent>& components = deployInfo.getComponents();
  109. unsigned int nComps = components.ordinality();
  110. if (nComps == 0)
  111. throw MakeStringException(-1, "No components were selected for deployment!");
  112. StringBuffer xml;
  113. CDeployInfo& depInfo = dynamic_cast<CDeployInfo&>( deployInfo );
  114. depInfo.serializeStruct(ctx, xml);
  115. m_pResponseXml.setown( createPTreeFromXMLString(xml.str()) );
  116. m_pSelComps = m_pResponseXml->queryPropTree("Components");
  117. //save a copy of component in request into our internal tree that we maintain and
  118. //eventually plan to return as part of the response
  119. //
  120. Owned<IPropertyTreeIterator> it = m_pSelComps->getElements("Deploy/Components/Component");
  121. ForEach(*it)
  122. {
  123. IPropertyTree* pComp = &it->query();
  124. IPropertyTree* pTasks = pComp->queryPropTree("Tasks");
  125. if (pTasks)
  126. pComp->removeTree(pTasks);
  127. pTasks = pComp->addPropTree("Tasks", createPTree());
  128. string key;
  129. //key += comp.getType();
  130. //key += ':';
  131. key += pComp->queryProp("Name");
  132. key += ':';
  133. key += pComp->queryProp("Instance");
  134. m_comp2TasksMap.insert( pair<string, IPropertyTree*>(key, pTasks) );
  135. }
  136. //prepare xml needed for actual deployment
  137. //
  138. initComponents(components);
  139. }
  140. void CWsDeployEngine::initComponents(IArrayOf<IConstComponent>& components)
  141. {
  142. unsigned int nComps = components.ordinality();
  143. m_pDeploy.set(createPTree("Deploy"));
  144. IPropertyTree* pComps = m_pDeploy->addPropTree("SelectedComponents", createPTree());
  145. for (unsigned int i=0; i<nComps; i++)
  146. {
  147. IConstComponent& comp = components.item(i);
  148. const char* type = comp.getType();
  149. const char* name = comp.getName();
  150. if (!(type && *type && name && *name))
  151. throw MakeStringException(-1, "Invalid component specified!");
  152. StringBuffer xpath;
  153. xpath.appendf("%s[@name='%s']", type, name);
  154. IPropertyTree* pComp = pComps->queryPropTree(xpath.str());
  155. if (!pComp)
  156. {
  157. pComp = pComps->addPropTree(type, createPTree());
  158. pComp->addProp("@name", name);
  159. }
  160. const char* instType = comp.getInstanceType();
  161. IPropertyTree* pInst = pComp->addPropTree(instType, createPTree());
  162. pInst->addProp("@name", comp.getInstance());
  163. pInst->addProp("@computer", comp.getComputer());
  164. }
  165. }
  166. void CWsDeployEngine::deploy(CDeployOptions& pOptions)
  167. {
  168. try
  169. {
  170. Owned<IEnvironmentFactory> factory = getEnvironmentFactory(true);
  171. Owned<IConstEnvironment> constEnv(factory->openEnvironment());
  172. m_pEnvDepEngine.setown( createEnvDeploymentEngine(*constEnv, *this, m_pDeploy->queryPropTree("SelectedComponents")) );
  173. m_pEnvDepEngine->setInteractiveMode(false);
  174. bool rc = true;
  175. CDeployOptions& options = pOptions;
  176. //if (pOptions)
  177. // options = *pOptions;
  178. //else
  179. // options = dynamic_cast<CDeployOptions&>(m_depInfo.getOptions());
  180. //if (m_deployEngine->isLinuxDeployment() && (options.getStop() || options.getStart()))
  181. // rc = askForSshAccountInfo();
  182. // Get timestamp
  183. time_t t = time(NULL);
  184. struct tm* now = localtime(&t);
  185. char timestamp[30];
  186. strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", now);
  187. // Validate data
  188. printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Performing preliminary validation based on XML schema ...");
  189. m_pEnvDepEngine->check();
  190. // Delete all existing connections
  191. //doDisconnects();
  192. unsigned deployMode = DEFLAGS_NONE;
  193. if (options.getConfigFiles())
  194. deployMode |= DEFLAGS_CONFIGFILES;
  195. if (options.getBuildFiles())
  196. {
  197. deployMode |= DEFLAGS_BUILDFILES;
  198. if (options.getUpgradeBuildFiles())
  199. deployMode |= DCFLAGS_TIME | DCFLAGS_SIZE;
  200. }
  201. StringBuffer msg;
  202. if (options.getCompare())
  203. {
  204. m_pEnvDepEngine->compare(deployMode);
  205. msg.append("Compare");
  206. }
  207. else
  208. {
  209. // Get archive path and environment name
  210. StringBuffer envName;
  211. if (envName.length()==0)
  212. envName = "deploy"; // make sure we have some name
  213. // Create unique name for archive file based on timestamp
  214. const char* archiveLogPath = options.getArchivePath();
  215. if (options.getArchiveEnv() || options.getLog())
  216. {
  217. if (!archiveLogPath || !*archiveLogPath)
  218. throw MakeStringExceptionDirect(-1, "Cannot archive or log without a path!");
  219. Owned<IFile> pIFile = createIFile(archiveLogPath);
  220. if (!pIFile->exists())
  221. {
  222. Owned<IDeployTask> task = createDeployTask(*this, "Create Directory", NULL, NULL, NULL, NULL, archiveLogPath, "", "", "", false);
  223. task->createDirectory();
  224. }
  225. else
  226. if (!pIFile->isDirectory())
  227. throw MakeStringException(-1, "The specified log/archive path '%s' is invalid!", archiveLogPath);
  228. }
  229. StringBuffer archiveFile;
  230. if (options.getArchiveEnv())
  231. {
  232. int n = 1;
  233. archiveFile.appendf("%s\\%s_%s.xml", archiveLogPath, envName.str(), timestamp);
  234. while (true)
  235. {
  236. Owned<IFile> pIFile = createIFile(archiveFile.str());
  237. archiveFile.clear().appendf("%s\\%s_%s_%d.xml", archiveLogPath, envName.str(), timestamp, n++);
  238. if (pIFile->exists())
  239. break;
  240. }
  241. m_pEnvDepEngine->archive(archiveFile);
  242. }
  243. // Create unique name for log file
  244. StringBuffer logFile;
  245. if (options.getLog())
  246. {
  247. int n = 1;
  248. logFile.appendf("%s\\%s_%s_log.xml", archiveLogPath, envName.str(), timestamp);
  249. while (true)
  250. {
  251. Owned<IFile> pIFile = createIFile(logFile.str());
  252. logFile.clear().appendf("%s\\%s_%s_log_%d.xml", archiveLogPath, envName.str(), timestamp, n++);
  253. if (pIFile->exists())
  254. break;
  255. }
  256. m_pEnvDepEngine->setLog(logFile.str(), archiveFile.str());
  257. }
  258. enum BackupMode backupMode;
  259. if (options.getBackupCopy())
  260. backupMode = DEBACKUP_COPY;
  261. else
  262. if (options.getBackupRename())
  263. backupMode = DEBACKUP_RENAME;
  264. else
  265. backupMode = DEBACKUP_NONE;
  266. m_pEnvDepEngine->deploy(deployMode, backupMode, options.getStop(), options.getStart());
  267. msg.append("Deployment");
  268. }
  269. // Check for errors
  270. if (getAbortStatus())
  271. printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Deployment aborted!");
  272. else
  273. {
  274. if (m_errorCount == 0)
  275. msg.append(" completed successfully");
  276. else
  277. {
  278. msg.append(" failed with ").append(m_errorCount).append(" error");
  279. if (m_errorCount > 1)
  280. msg.append('s');
  281. }
  282. printStatus(STATUS_NORMAL, NULL, NULL, NULL, "%s", msg.str());
  283. }
  284. }
  285. catch (IException* e)
  286. {
  287. m_deployStatus.clear();
  288. e->errorMessage(m_deployStatus);
  289. e->Release();
  290. m_errorCount++;
  291. }
  292. catch (...)
  293. {
  294. m_deployStatus.clear().append("Unknown exception!");
  295. m_errorCount++;
  296. }
  297. }
  298. void CWsDeployEngine::deploy()
  299. {
  300. try
  301. {
  302. Owned<IEnvironmentFactory> factory = getEnvironmentFactory(true);
  303. Owned<IConstEnvironment> constEnv(factory->openEnvironment());
  304. m_pEnvDepEngine.setown( createEnvDeploymentEngine(*constEnv, *this, m_pDeploy->queryPropTree("SelectedComponents")) );
  305. m_pEnvDepEngine->setInteractiveMode(false);
  306. bool rc = true;
  307. CDeployOptions& options = dynamic_cast<CDeployOptions&>(m_depInfo.getOptions());
  308. //if (m_deployEngine->isLinuxDeployment() && (options.getStop() || options.getStart()))
  309. // rc = askForSshAccountInfo();
  310. // Get timestamp
  311. time_t t = time(NULL);
  312. struct tm* now = localtime(&t);
  313. char timestamp[30];
  314. strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", now);
  315. // Validate data
  316. printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Performing preliminary validation based on XML schema ...");
  317. m_pEnvDepEngine->check();
  318. // Delete all existing connections
  319. //doDisconnects();
  320. unsigned deployMode = DEFLAGS_NONE;
  321. if (options.getConfigFiles())
  322. deployMode |= DEFLAGS_CONFIGFILES;
  323. if (options.getBuildFiles())
  324. {
  325. deployMode |= DEFLAGS_BUILDFILES;
  326. if (options.getUpgradeBuildFiles())
  327. deployMode |= DCFLAGS_TIME | DCFLAGS_SIZE;
  328. }
  329. StringBuffer msg;
  330. if (options.getCompare())
  331. {
  332. m_pEnvDepEngine->compare(deployMode);
  333. msg.append("Compare");
  334. }
  335. else
  336. {
  337. // Get archive path and environment name
  338. StringBuffer envName;
  339. if (envName.length()==0)
  340. envName = "deploy"; // make sure we have some name
  341. // Create unique name for archive file based on timestamp
  342. const char* archiveLogPath = options.getArchivePath();
  343. if (options.getArchiveEnv() || options.getLog())
  344. {
  345. if (!archiveLogPath || !*archiveLogPath)
  346. throw MakeStringExceptionDirect(-1, "Cannot archive or log without a path!");
  347. Owned<IFile> pIFile = createIFile(archiveLogPath);
  348. if (!pIFile->exists())
  349. {
  350. Owned<IDeployTask> task = createDeployTask(*this, "Create Directory", NULL, NULL, NULL, NULL, archiveLogPath, "", "", "", false);
  351. task->createDirectory();
  352. }
  353. else
  354. if (!pIFile->isDirectory())
  355. throw MakeStringException(-1, "The specified log/archive path '%s' is invalid!", archiveLogPath);
  356. }
  357. StringBuffer archiveFile;
  358. if (options.getArchiveEnv())
  359. {
  360. int n = 1;
  361. archiveFile.appendf("%s\\%s_%s.xml", archiveLogPath, envName.str(), timestamp);
  362. while (true)
  363. {
  364. Owned<IFile> pIFile = createIFile(archiveFile.str());
  365. archiveFile.clear().appendf("%s\\%s_%s_%d.xml", archiveLogPath, envName.str(), timestamp, n++);
  366. if (pIFile->exists())
  367. break;
  368. }
  369. m_pEnvDepEngine->archive(archiveFile);
  370. }
  371. // Create unique name for log file
  372. StringBuffer logFile;
  373. if (options.getLog())
  374. {
  375. int n = 1;
  376. logFile.appendf("%s\\%s_%s_log.xml", archiveLogPath, envName.str(), timestamp);
  377. while (true)
  378. {
  379. Owned<IFile> pIFile = createIFile(logFile.str());
  380. logFile.clear().appendf("%s\\%s_%s_log_%d.xml", archiveLogPath, envName.str(), timestamp, n++);
  381. if (pIFile->exists())
  382. break;
  383. }
  384. m_pEnvDepEngine->setLog(logFile.str(), archiveFile.str());
  385. }
  386. enum BackupMode backupMode;
  387. if (options.getBackupCopy())
  388. backupMode = DEBACKUP_COPY;
  389. else
  390. if (options.getBackupRename())
  391. backupMode = DEBACKUP_RENAME;
  392. else
  393. backupMode = DEBACKUP_NONE;
  394. m_pEnvDepEngine->deploy(deployMode, backupMode, options.getStop(), options.getStart());
  395. msg.append("Deployment");
  396. }
  397. // Check for errors
  398. if (getAbortStatus())
  399. printStatus(STATUS_NORMAL, NULL, NULL, NULL, "Deployment aborted!");
  400. else
  401. {
  402. if (m_errorCount == 0)
  403. msg.append(" completed successfully");
  404. else
  405. {
  406. msg.append(" failed with ").append(m_errorCount).append(" error");
  407. if (m_errorCount > 1)
  408. msg.append('s');
  409. }
  410. printStatus(STATUS_NORMAL, NULL, NULL, NULL, "%s", msg.str());
  411. }
  412. }
  413. catch (IException* e)
  414. {
  415. m_deployStatus.clear();
  416. e->errorMessage(m_deployStatus);
  417. e->Release();
  418. m_errorCount++;
  419. }
  420. catch (...)
  421. {
  422. m_deployStatus.clear().append("Unknown exception!");
  423. m_errorCount++;
  424. }
  425. }
  426. IPropertyTree* CWsDeployEngine::findTasksForComponent(const char* comp, const char* inst) const
  427. {
  428. string key;
  429. key += comp;
  430. if (inst)
  431. {
  432. key += ':';
  433. key += inst;
  434. }
  435. if (m_comp2TasksMap.size() <= 0)
  436. return NULL;
  437. CStringToIptMap::const_iterator it = m_comp2TasksMap.find( key.c_str() );
  438. if (it == m_comp2TasksMap.end())
  439. {
  440. //not all tasks are component related
  441. if (!inst)
  442. return NULL;
  443. else
  444. throw MakeStringException(-1, "Internal error in cache management!");
  445. }
  446. return (*it).second;
  447. }
  448. bool CWsDeployEngine::onDisconnect(const char* target)
  449. {
  450. return true;
  451. }
  452. void CWsDeployEngine::getSshAccountInfo(StringBuffer& userid, StringBuffer& password) const
  453. {
  454. }
  455. void CWsDeployEngine::printStatus(IDeployTask* task)
  456. {
  457. IPropertyTree* pTasks = findTasksForComponent(task->getCompName(), task->getInstanceName());
  458. if (!pTasks)
  459. return;
  460. StringBuffer xpath;
  461. xpath.appendf("Task[@id='%" I64F "d']", (__int64)task);
  462. IPropertyTree* pTask = pTasks->queryPropTree(xpath.str());
  463. if (!pTask)
  464. {
  465. pTask = pTasks->addPropTree("Task", createPTree());
  466. pTask->setPropInt64("@id", (__int64)task);
  467. const char* sourceFile = task->getFileName(DT_SOURCE);
  468. const char* targetFile = task->getFileName(DT_TARGET);
  469. if (strcmp(sourceFile, targetFile)==0 || strlen(sourceFile)==0)
  470. pTask->setProp( "FileName", targetFile );
  471. else if (targetFile && strlen(targetFile)==0)
  472. pTask->setProp( "FileName", sourceFile );
  473. else
  474. {
  475. StringBuffer fileName;
  476. fileName.append(sourceFile).append(" -> ").append(targetFile);
  477. pTask->setProp( "FileName", fileName.str() );
  478. }
  479. pTask->setProp( "Caption", task->getCaption() );
  480. pTask->setProp( "TargetPath",task->getFileSpec(DT_TARGET) );
  481. pTask->setProp( "SourcePath",task->getFileSpec(DT_SOURCE) );
  482. pTask->setProp( "Message", task->getErrorString() );
  483. StatusType status;
  484. if (!task->isProcessed())
  485. status = STATUS_INCOMPLETE;
  486. else
  487. if (task->getErrorCode())
  488. status = STATUS_ERROR;
  489. else
  490. if (task->getWarnings())
  491. status = STATUS_WARN;
  492. else
  493. status = STATUS_NORMAL;
  494. pTask->setPropInt("Status", status);
  495. }
  496. }
  497. void CWsDeployEngine::printStatus(StatusType type, const char* processType, const char* process,
  498. const char* instance, const char* format/*=NULL*/, ...)
  499. {
  500. char buf[1024];
  501. if (format)
  502. {
  503. va_list args;
  504. va_start(args, format);
  505. if (_vsnprintf(buf, sizeof(buf), format, args) < 0)
  506. buf[sizeof(buf) - 1] = '\0';
  507. va_end(args);
  508. }
  509. else
  510. *buf = '\0';
  511. if (processType && process && instance)
  512. {
  513. IPropertyTree* pTasks = findTasksForComponent(process, instance);
  514. IPropertyTree* pTask = pTasks->addPropTree("Task", createPTree());
  515. //pTask->setProp( "Caption", buf );
  516. pTask->setProp( "Message", buf );
  517. pTask->setPropInt("Status",type);
  518. }
  519. else
  520. m_deployStatus.append( buf );
  521. }
  522. //the following throws exception on abort, returns true for ignore
  523. bool CWsDeployEngine::processException(const char* processType, const char* process, const char* instance,
  524. IException* e, const char* szMessage/*=NULL*/, const char* szCaption/*=NULL*/,
  525. IDeployTask* pTask /*=NULL*/ )
  526. {
  527. if (getAbortStatus() || (pTask && pTask->getAbort()))
  528. throw MakeStringException(0, "User abort");
  529. StringBuffer msg;
  530. if (e)
  531. {
  532. e->errorMessage(msg);
  533. e->Release();
  534. }
  535. else
  536. if (szMessage && *szMessage)
  537. msg.append( szMessage );
  538. else
  539. msg.append("Unknown exception occurred!");
  540. int rc = true;//ignore
  541. if (msg.length() > 0)//the exception message hasn't already been shown?
  542. {
  543. //printStatus(STATUS_ERROR, processType, process, instance, szMessage);
  544. if (szCaption && *szCaption)
  545. DBGLOG("%s: %s", szCaption, msg.str());
  546. else
  547. DBGLOG("%s", msg.str());
  548. }
  549. m_errorCount++;
  550. return true;
  551. }