model.php 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433
  1. <?php
  2. /**
  3. * The model file of app instance module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL (http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Sun Guangming <sunguangming@easycrop.ltd>
  8. * @package instance
  9. * @version $Id$
  10. * @link https://www.zentao.net
  11. * @property actionModel $action
  12. * @property cneModel $cne
  13. */
  14. class instanceModel extends model
  15. {
  16. /**
  17. * Construct method: load CNE model, and set primaryDomain.
  18. *
  19. * @access public
  20. * @return void
  21. */
  22. public function __construct()
  23. {
  24. parent::__construct();
  25. $this->loadModel('cne');
  26. $this->loadModel('action');
  27. }
  28. /**
  29. * 获取应用实例。
  30. * Get by id.
  31. *
  32. * @param int $id
  33. * @access public
  34. * @return object|null
  35. */
  36. public function getByID($id)
  37. {
  38. $instance = $this->dao->select('*')->from(TABLE_INSTANCE)
  39. ->where('id')->eq($id)
  40. ->andWhere('deleted')->eq(0)
  41. ->fetch();
  42. if(!$instance) return null;
  43. $instance->spaceData = $this->dao->select('*')->from(TABLE_SPACE)->where('id')->eq($instance->space)->fetch();
  44. return $instance;
  45. }
  46. /**
  47. * 获取应用实例数量。
  48. * Get instance count.
  49. *
  50. * @param string $status
  51. * @access public
  52. * @return int
  53. */
  54. public function getInstanceCount($status = '')
  55. {
  56. return $this->dao->select('count(*) as count')->from(TABLE_INSTANCE)
  57. ->where('deleted')->eq(0)
  58. ->beginIF($status)->andWhere('status')->eq($status)->fi()
  59. ->fetch('count');
  60. }
  61. /**
  62. * 根据应用url获取应用信息。
  63. * Get a application by url.
  64. *
  65. * @param string $url
  66. * @access public
  67. * @return object|false
  68. */
  69. public function getByUrl($url)
  70. {
  71. $url = str_replace(array('https://', 'http://'), '', trim($url));
  72. return $this->dao->select('id')->from(TABLE_INSTANCE)->where('domain')->eq($url)->fetch();
  73. }
  74. /**
  75. * 根据ID列表获取应用实例列表。
  76. * Get by id list.
  77. *
  78. * @param array $idList
  79. * @access public
  80. * @return array
  81. */
  82. public function getByIdList($idList)
  83. {
  84. $instances = $this->dao->select('*')->from(TABLE_INSTANCE)
  85. ->where('id')->in($idList)
  86. ->andWhere('deleted')->eq(0)
  87. ->fetchAll('id');
  88. $spaces = $this->dao->select('*')->from(TABLE_SPACE)->where('deleted')->eq(0)->andWhere('id')->in(helper::arrayColumn($instances, 'space'))->fetchAll('id');
  89. foreach($instances as $instance) $instance->spaceData = zget($spaces, $instance->space, new stdclass);
  90. return $instances;
  91. }
  92. /**
  93. * 获取应用实例列表。
  94. * Get instances list.
  95. *
  96. * @param object $pager
  97. * @param string $pinned
  98. * @param string $searchParam
  99. * @param string $status
  100. * @param bool $alertMark
  101. * @access public
  102. * @return array
  103. */
  104. public function getList($pager = null, $pinned = '', $searchParam = '', $status = 'all', $alertMark = true)
  105. {
  106. $instances = $this->dao->select('instance.*')->from(TABLE_INSTANCE)->alias('instance')
  107. ->leftJoin(TABLE_SPACE)->alias('space')->on('space.id=instance.space')
  108. ->where('instance.deleted')->eq(0)
  109. ->beginIF($pinned)->andWhere('instance.pinned')->eq((int)$pinned)->fi()
  110. ->beginIF($searchParam)->andWhere('instance.name')->like("%{$searchParam}%")->fi()
  111. ->beginIF($status != 'all')->andWhere('instance.status')->eq($status)->fi()
  112. ->orderBy('instance.id desc')
  113. ->beginIF($pager)->page($pager)->fi()
  114. ->fetchAll('id', false);
  115. if(empty($instances)) return array();
  116. $spaces = $this->dao->select('*')->from(TABLE_SPACE)
  117. ->where('deleted')->eq(0)
  118. ->andWhere('id')->in(helper::arrayColumn($instances, 'space'))
  119. ->fetchAll('id');
  120. foreach($instances as $instance) $instance->spaceData = zget($spaces, $instance->space, new stdclass);
  121. return $instances;
  122. }
  123. /**
  124. * 根据域名获取服务数量。
  125. * Count old domain.
  126. *
  127. * @access public
  128. * @return int
  129. */
  130. public function countOldDomain()
  131. {
  132. /* REPLACE(domain, .$sysDomain, '') used for special case: new domain is a.com and old domain is b.a.com, domain of instance is c.b.a.com, */
  133. $sysDomain = $this->loadModel('cne')->sysDomain();
  134. $result = $this->dao->select('COUNT(1) AS qty')->from(TABLE_INSTANCE)
  135. ->where('deleted')->eq(0)
  136. ->andWhere("REPLACE(domain, '.$sysDomain', '')")->like("%.%")
  137. ->fetch();
  138. return $result->qty;
  139. }
  140. /**
  141. * 自定义域名后,更新所有应用实例的域名。
  142. * Update all instances domain by customized domain.
  143. *
  144. * @access public
  145. * @return void
  146. */
  147. public function updateInstancesDomain()
  148. {
  149. /* REPLACE(domain, .$sysDomain, '') used for special case: new domain is a.com and old domain is b.a.com, domain of instance is c.b.a.com, new domain of instance should be c.a.com */
  150. $sysDomain = $this->loadModel('cne')->sysDomain();
  151. $instanceList = $this->dao->select('*')->from(TABLE_INSTANCE)
  152. ->where('deleted')->eq(0)
  153. ->andWhere("REPLACE(domain, '.$sysDomain', '')")->like("%.%")
  154. ->fetchAll('id');
  155. $spaces = $this->dao->select('*')->from(TABLE_SPACE)->where('deleted')->eq(0)->andWhere('id')->in(helper::arrayColumn($instanceList, 'space'))->fetchAll('id');
  156. foreach($instanceList as $instance) $instance->spaceData = zget($spaces, $instance->space, new stdclass);
  157. foreach($instanceList as $instance) $this->updateDomain($instance);
  158. }
  159. /**
  160. * 更新应用实例域名。
  161. * Update domain.
  162. *
  163. * @param object $instance
  164. * @access public
  165. * @return bool
  166. */
  167. public function updateDomain($instance)
  168. {
  169. /* If domain of instance is same with system domain, not need to update. */
  170. $sysDomain = $this->cne->sysDomain();
  171. if(stripos(str_replace($sysDomain, '', $instance->domain), '.') === false) return true;
  172. $expiredDomains = $this->loadModel('setting')->getItem('owner=system&module=common&section=domain&key=expiredDomain');
  173. $expiredDomains = json_decode($expiredDomains, true);
  174. if(empty($expiredDomains)) return true;
  175. $leftDomain = '';
  176. foreach($expiredDomains as $expiredDomain)
  177. {
  178. if(stripos($instance->domain, (string)$expiredDomain) === false) continue;
  179. $leftDomain = trim(str_replace((string)$expiredDomain, '', $instance->domain), '.'); // Pick left domain.
  180. /* If left domain like a.b, skip it, because left domain must be letters without dot(.) . */
  181. if(stripos($leftDomain, '.') !== false) continue; // Does not pick left domain.
  182. $newDomain = $leftDomain . '.' . $sysDomain;
  183. $settings = new stdclass;
  184. $settings->settings_map = new stdclass;
  185. $settings->settings_map->ingress = new stdclass;
  186. $settings->settings_map->ingress->enabled = true;
  187. $settings->settings_map->ingress->host = $newDomain; //$this->fullDomain($leftDomain);
  188. $settings->settings_map->global = new stdclass;
  189. $settings->settings_map->global->ingress = $settings->settings_map->ingress;
  190. if($this->cne->updateConfig($instance, $settings))
  191. {
  192. $this->dao->update(TABLE_INSTANCE)->set('domain')->eq($newDomain)->where('id')->eq($instance->id)->exec();
  193. }
  194. }
  195. return true;
  196. }
  197. /**
  198. * 更新应用的内存大小。
  199. * Update instance memory size.
  200. *
  201. * @param object $instance
  202. * @param int $size
  203. * @access public
  204. * @return bool
  205. */
  206. public function updateMemorySize($instance, $size = 0)
  207. {
  208. $settings = new stdclass;
  209. $settings->settings_map = new stdclass;
  210. $settings->settings_map->resources = new stdclass;
  211. $settings->settings_map->resources->memory = $size;
  212. $oldValue = helper::formatKB($instance->oldValue);
  213. $newValue = helper::formatKB(intval($size));
  214. unset($instance->oldValue);
  215. $success = $this->cne->updateConfig($instance, $settings);
  216. if($success)
  217. {
  218. $actionID = $this->action->create('instance', $instance->id, 'adjustMemory', $newValue);
  219. $this->action->logHistory($actionID, [['field' => 'adjustmemory', 'old' => $oldValue, 'new' => $newValue, 'diff' => '']]);
  220. return true;
  221. }
  222. dao::$errors[] = $this->lang->instance->errors->failToAdjustMemory;
  223. return false;
  224. }
  225. /**
  226. * 更新应用的CPU大小。
  227. * Update instance CPU size.
  228. *
  229. * @param object $instance
  230. * @param int|string $size CPU core number
  231. * @access public
  232. * @return bool
  233. */
  234. public function updateCpuSize($instance, $size = 0)
  235. {
  236. $settings = new stdclass;
  237. $settings->settings_map = new stdclass;
  238. $settings->settings_map->resources = new stdclass;
  239. $settings->settings_map->resources->cpu = $size;
  240. $oldValue = (int)$instance->oldValue;
  241. $newValue = (string)$size;
  242. unset($instance->oldValue);
  243. $success = $this->cne->updateConfig($instance, $settings);
  244. if($success)
  245. {
  246. $actionID = $this->action->create('instance', $instance->id, 'adjustCPU', $newValue);
  247. $this->action->logHistory($actionID, [['field' => 'adjustcpu', 'old' => $oldValue, 'new' => $newValue, 'diff' => '']]);
  248. return true;
  249. }
  250. dao::$errors[] = $this->lang->instance->errors->failToAdjustCPU;
  251. return false;
  252. }
  253. /**
  254. * 更新应用的存储空间大小。
  255. * Update the storage size of the instance.
  256. *
  257. * @param object $instance
  258. * @param int|string $size 10737418240|10Gi
  259. * @param string $name
  260. * @return bool
  261. */
  262. public function updateVolSize($instance, $size, $name)
  263. {
  264. $size = is_numeric($size) ? intval($size / 1073741824) : (int)$size;
  265. if($size == 0)
  266. {
  267. dao::$errors[] = $this->lang->instance->errors->failToAdjustVol;
  268. return false;
  269. }
  270. $size = $size . 'Gi';
  271. $setting = new stdclass();
  272. $setting->key = $name;
  273. $setting->value = $size;
  274. $settings = new stdclass;
  275. $settings->settings = array();
  276. $settings->settings[] = $setting;
  277. $oldValue = $instance->oldValue;
  278. $newValue = str_replace('Gi', '', $size);
  279. unset($instance->oldValue);
  280. $success = $this->cne->updateConfig($instance, $settings);
  281. if($success)
  282. {
  283. $actionID = $this->action->create('instance', $instance->id, 'adjustVol', $newValue);
  284. $this->action->logHistory($actionID, [['field' => 'adjustvol', 'old' => $oldValue, 'new' => $newValue, 'diff' => '']]);
  285. return true;
  286. }
  287. dao::$errors[] = $this->lang->instance->errors->failToAdjustVol;
  288. return false;
  289. }
  290. /**
  291. * 更新应用实例的状态。
  292. * Update instance status.
  293. *
  294. * @param int $int
  295. * @param string $status
  296. * @access public
  297. * @return int
  298. * @param int $id
  299. */
  300. public function updateStatus($id, $status)
  301. {
  302. $instanceData = new stdclass;
  303. $instanceData->status = trim($status);
  304. return $this->updateByID($id, $instanceData);
  305. }
  306. /**
  307. * 更新应用实例。
  308. * Update instance by id.
  309. *
  310. * @param int $id
  311. * @param object|array $newInstance
  312. * @access public
  313. * @return bool
  314. */
  315. public function updateByID($id, $newInstance)
  316. {
  317. $this->dao->update(TABLE_INSTANCE)->data($newInstance, 'memory_kb,cpu,disk_gb')
  318. ->autoCheck()
  319. ->checkIF(isset($newInstance->name), 'name', 'notempty')
  320. ->checkIF(isset($newInstance->status), 'status', 'in', array_keys($this->lang->instance->statusList))
  321. ->where('id')->eq($id)
  322. ->exec();
  323. if(!empty($newInstance->name))
  324. {
  325. $instance = $this->fetchByID($id);
  326. $server = $this->loadModel('space')->getExternalAppByApp($instance);
  327. if($server) $this->dao->update(TABLE_PIPELINE)->set('name')->eq($newInstance->name)->where('id')->eq($server->id)->exec();
  328. }
  329. return dao::isError();
  330. }
  331. /**
  332. * 判断应用实例是否可以进行操作。
  333. * If actions are allowed to do.
  334. *
  335. * @param string $action
  336. * @param object $instance
  337. * @access public
  338. * @return bool
  339. */
  340. public function canDo($action, $instance)
  341. {
  342. switch($action)
  343. {
  344. case 'start':
  345. return $instance->status === 'stopped';
  346. case 'stop':
  347. return in_array($instance->status, array('abnormal', 'running'));
  348. case 'uninstall':
  349. return !in_array($instance->status, array('destroying'));
  350. case 'visit':
  351. return $instance->status == 'running';
  352. default:
  353. return false;
  354. }
  355. }
  356. /**
  357. * 获取访问应用实例的URL。
  358. * Get URL for visiting instance service.
  359. *
  360. * @param object $instance
  361. * @access public
  362. * @return string
  363. */
  364. public function url($instance)
  365. {
  366. $url = "//" . $instance->domain;
  367. $port = getenv('APP_HTTPS_PORT');
  368. if($port and $port != '443') $url .= ":$port/";
  369. return $url;
  370. }
  371. /**
  372. * Create third domain.
  373. *
  374. * @param int $length
  375. * @param int $triedTimes
  376. * @access public
  377. * @return string
  378. */
  379. public function randThirdDomain($length = 4, $triedTimes = 0)
  380. {
  381. if($triedTimes > 16) $length++;
  382. $thirdDomain = strtolower(helper::randStr($length));
  383. if(!$this->domainExists($thirdDomain)) return $thirdDomain;
  384. return $this->randThirdDomain($length, $triedTimes + 1);
  385. }
  386. /**
  387. * 返回完整域名。
  388. * Return full domain.
  389. *
  390. * @param string $thirdDomain
  391. * @access public
  392. * @return string
  393. */
  394. public function fullDomain($thirdDomain)
  395. {
  396. return $thirdDomain . '.' . $this->cne->sysDomain();
  397. }
  398. /**
  399. * 检查域名是否存在。
  400. * Check if the domain exists.
  401. *
  402. * @param string $thirdDomain
  403. * @access public
  404. * @return bool true: exists, false: not exist.
  405. */
  406. public function domainExists($thirdDomain)
  407. {
  408. $domain = $this->fullDomain($thirdDomain);
  409. return boolval($this->dao->select('id')->from(TABLE_INSTANCE)->where('domain')->eq($domain)->andWhere('deleted')->eq(0)->fetch());
  410. }
  411. /**
  412. * 检查k8name是否存在。
  413. * Check if the k8name exists.
  414. *
  415. * @param string $k8name
  416. * @access public
  417. * @return bool true: exists, false: not exist.
  418. */
  419. public function k8nameExists($k8name)
  420. {
  421. return boolval($this->dao->select('id')->from(TABLE_INSTANCE)->where('k8name')->eq($k8name)->andWhere('deleted')->eq(0)->fetch());
  422. }
  423. /**
  424. * 构造安装应用的配置。
  425. * Mount installation settings by custom data.
  426. *
  427. * @param object $customData
  428. * @param object $dbInfo
  429. * @param object $instance
  430. * @access public
  431. * @return object
  432. */
  433. public function installationSettingsMap($customData, $dbInfo, $instance)
  434. {
  435. $settingsMap = new stdclass;
  436. if($customData->customDomain)
  437. {
  438. $settingsMap->ingress = new stdclass;
  439. $settingsMap->ingress->enabled = true;
  440. $settingsMap->ingress->host = $this->fullDomain($customData->customDomain);
  441. $settingsMap->global = new stdclass;
  442. $settingsMap->global->ingress = new stdclass;
  443. $settingsMap->global->ingress->enabled = true;
  444. $settingsMap->global->ingress->host = $settingsMap->ingress->host;
  445. }
  446. if(in_array($instance->chart, $this->config->instance->devopsApps))
  447. {
  448. $settingsMap->ci = new stdclass();
  449. $settingsMap->ci->enabled = true;
  450. }
  451. if($instance->source == 'system' && in_array($instance->chart, $this->config->instance->initUserApps))
  452. {
  453. $user = $this->dao->select('account')->from(TABLE_USER)->where('deleted')->eq(0)->fetch('account');
  454. if($user)
  455. {
  456. $settingsMap->auth = new stdclass();
  457. $settingsMap->auth->username = $user;
  458. }
  459. }
  460. if(empty($customData->dbType) || $customData->dbType == 'unsharedDB' || empty($customData->dbService)) return $settingsMap;
  461. /* Set DB settings. */
  462. $dbSettings = new stdclass;
  463. $dbSettings->service = $dbInfo->name;
  464. $dbSettings->namespace = $dbInfo->namespace;
  465. $dbSettings->host = $dbInfo->host;
  466. $dbSettings->port = $dbInfo->port;
  467. $dbSettings->name = str_replace('-', '_', $instance->chart) . '_' . $instance->id;
  468. $dbSettings->user = 'user_' . $dbSettings->name;
  469. $dbSettings = $this->getValidDBSettings($dbSettings, $dbSettings->user, $dbSettings->name);
  470. $settingsMap->mysql = new stdclass;
  471. $settingsMap->mysql->enabled = false;
  472. $settingsMap->mysql->auth = new stdclass;
  473. $settingsMap->mysql->auth->user = $dbSettings->user;
  474. $settingsMap->mysql->auth->password = helper::randStr(12);
  475. $settingsMap->mysql->auth->host = $dbSettings->host;
  476. $settingsMap->mysql->auth->port = $dbSettings->port;
  477. $settingsMap->mysql->auth->database = $dbSettings->name;
  478. $settingsMap->mysql->auth->dbservice = new stdclass;
  479. $settingsMap->mysql->auth->dbservice->name = $dbSettings->service;
  480. $settingsMap->mysql->auth->dbservice->namespace = $dbSettings->namespace;
  481. return $settingsMap;
  482. }
  483. /**
  484. * 获取有效的数据库配置。
  485. * Return valid DBSettings.
  486. *
  487. * @param object $dbSettings
  488. * @param string $defaultUser
  489. * @param string $defaultDBName
  490. * @param int $times
  491. * @access private
  492. * @return null|object
  493. */
  494. private function getValidDBSettings($dbSettings, $defaultUser, $defaultDBName, $times = 1)
  495. {
  496. if($times >10) return;
  497. if(empty($dbSettings->service) || empty($dbSettings->name)) return $dbSettings;
  498. $validatedResult = $this->cne->validateDB($dbSettings->service, $dbSettings->name, $dbSettings->user, $dbSettings->namespace);
  499. if($validatedResult->user && $validatedResult->database) return $dbSettings;
  500. if(!$validatedResult->user) $dbSettings->user = $defaultUser . '_' . helper::randStr(4);
  501. if(!$validatedResult->database) $dbSettings->database = $defaultDBName . '_' . helper::randStr(4);
  502. return $this->getValidDBSettings($dbSettings, $defaultUser, $defaultDBName, $times + 1);
  503. }
  504. /**
  505. * 安装应用。
  506. * Install app by request from Web page.
  507. *
  508. * @param object $app
  509. * @param object $dbInfo
  510. * @param object $customData
  511. * @param int $spaceID
  512. * @param array $settings
  513. * @access public
  514. * @return false|object Failure: return false, Success: return instance
  515. */
  516. public function install($app, $dbInfo, $customData, $spaceID = null, $settings = array())
  517. {
  518. $this->loadModel('space');
  519. if(!isset($this->app->user->account))
  520. {
  521. $this->app->user = new stdclass();
  522. $this->app->user->account = $this->dao->select('*')->from(TABLE_USER)->where('deleted')->eq(0)->fetch('account');
  523. }
  524. if($spaceID)
  525. {
  526. $space = $this->space->getByID($spaceID);
  527. }
  528. else
  529. {
  530. $space = $this->space->defaultSpace($this->app->user->account);
  531. }
  532. $snippets = array();
  533. if(isset($customData->ldapSnippet[0])) $snippets['ldapSnippetName'] = $customData->ldapSnippet[0];
  534. if(isset($customData->smtpSnippet[0])) $snippets['smtpSnippetName'] = $customData->smtpSnippet[0];
  535. $channel = $this->app->session->cloudChannel ? $this->app->session->cloudChannel : $this->config->cloud->api->channel;
  536. $instance = $this->createInstance($app, $space, $customData->customDomain, $customData->customName, '', $channel, $snippets);
  537. if(!$instance) return false;
  538. $settingMap = $this->installationSettingsMap($customData, $dbInfo, $instance);
  539. return $this->doCneInstall($instance, $space, $settingMap, $snippets, $settings);
  540. }
  541. /**
  542. * 安装系统SLB组件。
  543. * Install System SLB component.
  544. *
  545. * @param object $app
  546. * @param string $k8name
  547. * @param string $channel
  548. * @access public
  549. * @return object|false
  550. */
  551. public function installSysSLB($app, $k8name = 'cne-lb', $channel = 'stable')
  552. {
  553. $this->app->loadLang('system');
  554. $space = $this->loadModel('space')->getSystemSpace($this->app->user->account);
  555. $instance = $this->createInstance($app, $space, '', '', $k8name, $channel);
  556. if(!$instance)
  557. {
  558. dao::$errors[] = $this->lang->system->errors->failToInstallLDAP;
  559. return false;
  560. }
  561. $instance = $this->doCneInstall($instance, $space, (new stdclass));
  562. if(!$instance)
  563. {
  564. dao::$errors[] = $this->lang->system->errors->failToInstallLDAP;
  565. return false;
  566. }
  567. return $instance;
  568. }
  569. /**
  570. * 创建应用实例记录。
  571. * Create instance recorder for installation.
  572. *
  573. * @param object $app
  574. * @param object $space
  575. * @param string $thirdDomain
  576. * @param string $name
  577. * @param string $channel
  578. * @param array $snippets
  579. * @access public
  580. * @return bool|object
  581. * @param string $k8name
  582. */
  583. public function createInstance($app, $space, $thirdDomain, $name = '', $k8name = '', $channel = 'stable', $snippets = array())
  584. {
  585. if(empty($k8name)) $k8name = "{$app->chart}-" . date('YmdHis'); //name rule: chartName-userAccount-YmdHis;
  586. $createdBy = $this->app->user->account;
  587. if(!$createdBy) $createdBy = $this->dao->select('*')->from(TABLE_USER)->where('deleted')->eq(0)->fetch('account');
  588. $instanceData = new stdclass;
  589. $instanceData->appId = $app->id;
  590. $instanceData->appName = $app->alias;
  591. $instanceData->name = !empty($name) ? $name : $app->alias;
  592. $instanceData->domain = !empty($thirdDomain) ? $this->fullDomain($thirdDomain) : '';
  593. $instanceData->logo = $app->logo;
  594. $instanceData->desc = $app->desc;
  595. $instanceData->introduction = isset($app->introduction) ? $app->introduction : $app->desc;
  596. $instanceData->source = $this->app->rawModule == 'instance' ? 'cloud' : 'system';
  597. $instanceData->channel = $channel;
  598. $instanceData->chart = $app->chart;
  599. $instanceData->appVersion = $app->app_version;
  600. $instanceData->version = $app->version;
  601. $instanceData->space = $space->id;
  602. $instanceData->k8name = $k8name;
  603. $instanceData->status = 'creating';
  604. $instanceData->createdBy = $createdBy;
  605. $instanceData->createdAt = date('Y-m-d H:i:s');
  606. foreach($snippets as $fieldName => $snippetName) $instanceData->$fieldName = $snippetName;
  607. $this->dao->insert(TABLE_INSTANCE)->data($instanceData)->autoCheck()->exec();
  608. if(dao::isError()) return false;
  609. return $this->getByID($this->dao->lastInsertID());
  610. }
  611. /**
  612. * 在CNE平台创建应用实例。
  613. * Create app instance on CNE platform.
  614. *
  615. * @param object $instance
  616. * @param object $space
  617. * @param object $settingsMap
  618. * @param array $snippets
  619. * @param array $settings
  620. * @access private
  621. * @return object|bool
  622. */
  623. private function doCneInstall($instance, $space, $settingsMap, $snippets = array(), $settings = array())
  624. {
  625. $apiParams = new stdclass;
  626. $apiParams->username = $instance->createdBy;
  627. $apiParams->cluster = '';
  628. $apiParams->namespace = $space->k8space;
  629. $apiParams->name = $instance->k8name;
  630. $apiParams->chart = $instance->chart;
  631. $apiParams->version = $instance->version;
  632. $apiParams->channel = $instance->channel;
  633. $apiParams->settings_map = $settingsMap;
  634. $apiParams->settings = $settings;
  635. $apiParams->settings_snippets = array_values($snippets);
  636. if(strtolower($this->config->CNE->app->domain) == 'demo.haogs.cn') $apiParams->settings_snippets = array('quickon_saas'); // Only for demo environment.
  637. $result = $this->cne->installApp($apiParams);
  638. if($result->code != 200 && $instance->source != 'system')
  639. {
  640. $this->dao->delete()->from(TABLE_INSTANCE)->where('id')->eq($instance->id)->exec();
  641. dao::$errors['server'][] = $result->message;
  642. return false;
  643. }
  644. $this->action->create('instance', $instance->id, 'install', '', json_encode(array('result' => $result, 'apiParams' => $apiParams)));
  645. $instance->status = 'initializing';
  646. $instance->dbSettings = json_encode($apiParams->settings_map);
  647. $this->updateByID($instance->id, array('status' => $instance->status, 'dbSettings' => $instance->dbSettings));
  648. return $instance;
  649. }
  650. /*
  651. * 卸载应用实例。
  652. * Uninstall app instance.
  653. *
  654. * @param object $instance
  655. * @access public
  656. * @return bool
  657. */
  658. /**
  659. * @param object $instance
  660. */
  661. public function uninstall($instance)
  662. {
  663. if($instance->source == 'external')
  664. {
  665. $this->dao->update(TABLE_INSTANCE)->set('deleted')->eq(1)->where('id')->eq($instance->id)->exec();
  666. return true;
  667. }
  668. $apiParams = new stdclass;
  669. $apiParams->cluster = '';// Multiple cluster should set this field.
  670. $apiParams->name = $instance->k8name;
  671. $apiParams->channel = $instance->channel;
  672. $apiParams->namespace = $instance->spaceData->k8space;
  673. $result = $this->cne->uninstallApp($apiParams);
  674. $success = $result->code == 200 || $result->code == 404;
  675. if($success) $this->dao->delete()->from(TABLE_INSTANCE)->where('id')->eq($instance->id)->exec();
  676. $this->dao->delete()->from(TABLE_PIPELINE)->where('instanceID')->eq($instance->id)->exec();
  677. return $success;
  678. }
  679. /*
  680. * 启动应用实例。
  681. * Start app instance.
  682. *
  683. * @param object $instance
  684. * @access public
  685. * @return object
  686. */
  687. /**
  688. * @param object $instance
  689. */
  690. public function start($instance)
  691. {
  692. $apiParams = new stdclass();
  693. $apiParams->cluster = '';
  694. $apiParams->name = $instance->k8name;
  695. $apiParams->chart = $instance->chart;
  696. $apiParams->namespace = $instance->spaceData->k8space;
  697. $apiParams->channel = $instance->channel;
  698. $result = $this->cne->startApp($apiParams);
  699. if($result->code == 200) $this->dao->update(TABLE_INSTANCE)->set('status')->eq('starting')->where('id')->eq($instance->id)->exec();
  700. return $result;
  701. }
  702. /*
  703. * 停止应用实例。
  704. * Stop app instance.
  705. *
  706. * @param object $instance
  707. * @access public
  708. * @return object
  709. */
  710. /**
  711. * @param object $instance
  712. */
  713. public function stop($instance)
  714. {
  715. $apiParams = new stdclass;
  716. $apiParams->cluster = '';
  717. $apiParams->name = $instance->k8name;
  718. $apiParams->chart = $instance->chart;
  719. $apiParams->namespace = $instance->spaceData->k8space;
  720. $apiParams->channel = $instance->channel;
  721. $result = $this->cne->stopApp($apiParams);
  722. if($result->code == 200) $this->dao->update(TABLE_INSTANCE)->set('status')->eq('stopping')->where('id')->eq($instance->id)->exec();
  723. return $result;
  724. }
  725. /**
  726. * 升级应用实例。
  727. * Upgrade app instance to higher version.
  728. *
  729. * @param object $instance
  730. * @param string $toVersion
  731. * @param string $appVersion
  732. * @access public
  733. * @return bool
  734. */
  735. public function upgrade($instance, $toVersion, $appVersion)
  736. {
  737. $instance->version = $toVersion;
  738. $success = $this->cne->updateConfig($instance);
  739. if(!$success) return false;
  740. $instanceData = new stdclass;
  741. $instanceData->version = $toVersion;
  742. $instanceData->appVersion = $appVersion;
  743. $this->updateByID($instance->id, $instanceData);
  744. return true;
  745. }
  746. /*
  747. * 批量更新并获取应用实例的状态。
  748. * Query and update instances status.
  749. *
  750. * @param array $instances
  751. * @access public
  752. * @return array new status list [{id:xx, status: xx, changed: true/false}]
  753. */
  754. /**
  755. * @param mixed[] $instances
  756. */
  757. public function batchFresh(&$instances)
  758. {
  759. $statusList = $this->cne->batchQueryStatus($instances);
  760. $newStatusList = array();
  761. foreach($instances as $instance)
  762. {
  763. $statusData = zget($statusList, $instance->k8name, '');
  764. if($statusData && ($instance->status != $statusData->status || $instance->version != $statusData->version))
  765. {
  766. $this->dao->update(TABLE_INSTANCE)
  767. ->set('status')->eq($statusData->status)
  768. ->beginIF($statusData->version)->set('version')->eq($statusData->version)->fi()
  769. ->where('id')->eq($instance->id)
  770. ->autoCheck()
  771. ->exec();
  772. $instance->status = $statusData->status;
  773. if($instance->status == 'running') $this->saveAuthInfo($instance);
  774. }
  775. $status = new stdclass;
  776. $status->id = $instance->id;
  777. $status->status = $instance->status;
  778. $newStatusList[] = $status;
  779. }
  780. return $newStatusList;
  781. }
  782. /*
  783. * 更新并获取应用实例的状态。
  784. * Query and update instance status.
  785. *
  786. * @param object $instance
  787. * @access public
  788. * @return object
  789. */
  790. /**
  791. * @param object $instance
  792. * @return object
  793. */
  794. public function freshStatus($instance)
  795. {
  796. $instance->runDuration = 0;
  797. $statusResponse = $this->cne->queryStatus($instance);
  798. if($statusResponse->code != 200) return $instance;
  799. $statusData = $statusResponse->data;
  800. $instance->runDuration = intval($statusData->age); // Run duration used in view page.
  801. if($instance->status != $statusData->status || $instance->version != $statusData->version)
  802. {
  803. $instance->status = $statusData->status;
  804. $this->dao->update(TABLE_INSTANCE)
  805. ->set('status')->eq($statusData->status)
  806. ->beginIF($statusData->version)->set('version')->eq($statusData->version)->fi()
  807. ->where('id')->eq($instance->id)
  808. ->autoCheck()
  809. ->exec();
  810. }
  811. return $instance;
  812. }
  813. /**
  814. * List转键值对。
  815. * Mount DB name and alias to array for select options.
  816. *
  817. * @param array $databases
  818. * @access public
  819. * @return array
  820. */
  821. public function dbListToOptions($databases)
  822. {
  823. $dbList = array();
  824. foreach($databases as $database) $dbList[$database->name] = zget($database, 'alias', $database->name);
  825. return $dbList;
  826. }
  827. /**
  828. * 过滤内存下拉菜单选项。
  829. * Filter memory options that smaller then current memory size.
  830. *
  831. * @param object $resources
  832. * @access public
  833. * @return array
  834. */
  835. public function filterMemOptions($resources)
  836. {
  837. $currentMemory = intval($resources->min->memory / 1024);
  838. $options = [];
  839. foreach($this->lang->instance->memOptions as $size => $text)
  840. {
  841. if($size >= $currentMemory) $options[$size] = $text;
  842. }
  843. return $options;
  844. }
  845. /**
  846. * 打印CPU使用信息。
  847. * Print CPU usage.
  848. *
  849. * @param object $instance
  850. * @param object $metrics
  851. * @static
  852. * @access public
  853. * @return array
  854. */
  855. public static function printCpuUsage($instance, $metrics)
  856. {
  857. if($instance->source === 'user') return array('color' => '', 'tip' => '', 'rate' => '', 'usage' => '', 'limit' => '');
  858. $rate = $instance->status == 'stopped' ? 0 : $metrics->rate;
  859. $tip = "{$rate}% = {$metrics->usage} / {$metrics->limit}";
  860. if(empty($color) && $rate == 0) $color = 'gray';
  861. if(empty($color) && $rate > 0 && $rate < 50) $color = 'secondary';
  862. if(empty($color) && $rate >= 0 && $rate < 70) $color = 'warning';
  863. if(empty($color) && $rate >= 0 && $rate < 90) $color = 'important';
  864. if(empty($color) && $rate >= 80) $color = 'danger';
  865. return array('color' => $color, 'tip' => $tip, 'rate' => $rate . '%', 'usage' => $metrics->usage, 'limit' => $metrics->limit);
  866. }
  867. /**
  868. * 打印存储使用信息。
  869. * Print memory usage.
  870. *
  871. * @param object $instance
  872. * @param object $metrics
  873. * @static
  874. * @access public
  875. * @return array
  876. */
  877. public static function printStorageUsage($instance, $metrics)
  878. {
  879. if($instance->source === 'user') return array('color' => '', 'tip' => '', 'rate' => '', 'usage' => '', 'limit' => '');
  880. $rate = $instance->status == 'stopped' ? 0 : $metrics->rate;
  881. $tip = "{$rate}% = " . helper::formatKB($metrics->usage) . ' / ' . helper::formatKB($metrics->limit);
  882. if(empty($color) && $rate == 0) $color = 'gray';
  883. if(empty($color) && $rate > 0 && $rate < 50) $color = 'secondary';
  884. if(empty($color) && $rate >= 0 && $rate < 70) $color = 'warning';
  885. if(empty($color) && $rate >= 0 && $rate < 90) $color = 'important';
  886. if(empty($color) && $rate >= 80) $color = 'danger';
  887. return array('color' => $color, 'tip' => $tip, 'rate' => $rate . '%', 'usage' => helper::formatKB($metrics->usage), 'limit' => helper::formatKB($metrics->limit));
  888. }
  889. /**
  890. * 检查内存是否充足。
  891. * Check the free memory of cluster is enough to install app.
  892. *
  893. * @param object $cloudApp
  894. * @access public
  895. * @return bool
  896. */
  897. public function enoughMemory($cloudApp)
  898. {
  899. $clusterResource = $this->cne->cneMetrics();
  900. $freeMemory = intval($clusterResource->metrics->memory->allocatable * 0.9); // Remain 10% memory for system.
  901. return $freeMemory >= $cloudApp->memory;
  902. }
  903. /**
  904. * 判断按钮是否可点击。
  905. * Adjust the action clickable.
  906. *
  907. * @param object $instance
  908. * @param string $action
  909. * @access public
  910. * @return bool
  911. */
  912. public function isClickable($instance, $action)
  913. {
  914. if(!isset($instance->type)) $instance->type = 'store';
  915. if($instance->type !== 'store')
  916. {
  917. if($action == 'visit') return true;
  918. if(!common::hasPriv('instance', 'manage')) return false;
  919. if($action === 'edit') return true;
  920. if($action == 'bindUser') return in_array($instance->appName, array('GitLab', 'Gitea', 'Gogs'));
  921. if($action == 'ajaxUninstall') return true;
  922. return false;
  923. }
  924. if(!common::hasPriv('instance', 'manage')) return false;
  925. if($action == 'ajaxStart') return $this->canDo('start', $instance);
  926. if($action == 'ajaxStop') return $this->canDo('stop', $instance);
  927. if($action == 'ajaxUninstall') return $this->canDo('uninstall', $instance);
  928. if($action == 'visit') return !empty($instance->domain) && $this->canDo('visit', $instance);
  929. if($action == 'upgrade') return !empty($instance->latestVersion) && in_array($instance->status, array('stopped', 'running'));
  930. if($action == 'edit') return false;
  931. if($action == 'bindUser') return $instance->status == 'running' && $instance->appName == 'GitLab';
  932. return true;
  933. }
  934. /**
  935. * 检查应用名称的唯一性。
  936. * Check app name unique.
  937. *
  938. * @param string $name
  939. * @access protected
  940. * @return bool
  941. */
  942. public function checkAppNameUnique($name)
  943. {
  944. $existInstance = $this->dao->select('*')->from(TABLE_INSTANCE)->where('name')->eq($name)->andWhere('deleted')->eq(0)->fetch();
  945. $existExternal = $this->dao->select('*')->from(TABLE_PIPELINE)->where('name')->eq($name)->andWhere('deleted')->eq('0')->fetch();
  946. return ($existInstance || $existExternal) ? false : true;
  947. }
  948. /**
  949. * 自动保存devops应用授权信息。
  950. * Auto save auth info of devops.
  951. *
  952. * @param object $instance
  953. * @access protected
  954. * @return bool
  955. */
  956. public function saveAuthInfo($instance)
  957. {
  958. if(!in_array($instance->chart, $this->config->instance->devopsApps)) return;
  959. $pipeline = $this->dao->select('id')->from(TABLE_PIPELINE)->where('instanceID')->eq($instance->id)->andWhere('deleted')->eq(0)->fetch();
  960. if(!empty($pipeline)) return;
  961. $tempMappings = $this->loadModel('cne')->getSettingsMapping($instance);
  962. if(empty($tempMappings)) return;
  963. $pipeline = new stdclass();
  964. $pipeline->type = $instance->chart == 'nexus3' ? 'nexus' : $instance->chart;
  965. $pipeline->private = md5(strval(rand(10,113450)));
  966. $pipeline->createdBy = 'system';
  967. $pipeline->createdDate = helper::now();
  968. $pipeline->url = strstr(getWebRoot(true), ':', true) . '://' . $instance->domain;
  969. $pipeline->instanceID = $instance->id;
  970. $pipeline->name = $this->generatePipelineName($instance);
  971. $pipeline->token = zget($tempMappings, 'api_token', '');
  972. $pipeline->account = zget($tempMappings, 'z_username', '');
  973. $pipeline->password = zget($tempMappings, 'z_password', '');
  974. if($instance->chart == 'sonarqube') $pipeline->token = base64_encode($pipeline->token . ':');
  975. if(empty($pipeline->account)) $pipeline->account = zget($tempMappings, 'admin_username', '');
  976. $this->pipeline->create($pipeline);
  977. }
  978. /**
  979. * 自动生成pipeline表name字段。
  980. * Auto generate name of pipeline table.
  981. *
  982. * @param object $instance
  983. * @access protected
  984. * @return string
  985. */
  986. public function generatePipelineName($instance)
  987. {
  988. $name = $instance->name;
  989. $type = $instance->chart;
  990. if(empty($this->loadModel('pipeline')->getByNameAndType($name, $type))) return $name;
  991. if(empty($this->loadModel('pipeline')->getByNameAndType($name . '-' . $instance->appVersion, $type))) return $name . '-' . $instance->appVersion;
  992. for($times = 1; $times < 5; $times ++)
  993. {
  994. if(empty($this->loadModel('pipeline')->getByNameAndType($name . '-' . $times, $name))) return $name . '-' . $times;
  995. }
  996. }
  997. /**
  998. * Backup instance.
  999. * 备份应用实例。
  1000. * @param object $instance
  1001. * @param object $user
  1002. * @access public
  1003. * @return bool
  1004. */
  1005. public function backup($instance, $user)
  1006. {
  1007. $result = $this->cne->backup($instance, $user->account);
  1008. if($result->code != 200) return false;
  1009. return true;
  1010. }
  1011. /**
  1012. * Get backup list of instance.
  1013. * 获取应用备份列表。
  1014. *
  1015. * @param object $instance
  1016. * @access public
  1017. * @return array
  1018. */
  1019. public function backupList($instance)
  1020. {
  1021. $result = $this->cne->getBackupList($instance);
  1022. if(empty($result) || $result->code != 200 || empty($result->data)) return array();
  1023. $backupList = $result->data;
  1024. usort($backupList, function($backup1, $backup2)
  1025. {
  1026. if($backup1->create_time < $backup2->create_time) return 1;
  1027. elseif ($backup1->create_time > $backup2->create_time) return -1;
  1028. else return 0;
  1029. });
  1030. $accounts = array_column($backupList, 'creator');
  1031. foreach($backupList as $backup) $accounts = array_merge($accounts, array_column($backup->restores, 'creator'));
  1032. $accounts = array_unique($accounts);
  1033. $users = $this->dao->select('account,realname')->from(TABLE_USER)->where('account')->in($accounts)->fetchPairs('account', 'realname');
  1034. foreach($backupList as &$backup)
  1035. {
  1036. $backup->latest_restore_time = 0;
  1037. $backup->latest_restore_status = '';
  1038. $backup->username = zget($users, $backup->creator);
  1039. $backup->name = helper::safe64Encode(base64_encode($backup->name));
  1040. /* Mount backup operator info and latest restore info. */
  1041. foreach($backup->restores as &$restore)
  1042. {
  1043. $restore->username = zget($users, $restore->creator);
  1044. if($restore->create_time > $backup->latest_restore_time)
  1045. {
  1046. $backup->latest_restore_time = $restore->create_time;
  1047. $backup->latest_restore_status = $restore->status;
  1048. }
  1049. }
  1050. /* Calculate backup size. */
  1051. $backupSize = isset($backup->backup_details->db[0]) ? intval(zget($backup->backup_details->db[0], 'size', 0)) : 0;
  1052. $backupSize += isset($backup->backup_details->volume[0]) ? intval(zget($backup->backup_details->volume[0], 'doneBytes', 0)) : 0;
  1053. $backup->fmtBackupSize = helper::formatKB($backupSize / 1024);
  1054. }
  1055. return $backupList;
  1056. }
  1057. /**
  1058. * Save backup settings.
  1059. * 保存备份设置。
  1060. *
  1061. * @param $instance
  1062. * @return void
  1063. */
  1064. public function saveBackupSettings($instance)
  1065. {
  1066. /* Initialize properties and validate. */
  1067. $settings = fixer::input('post')
  1068. ->setDefault('autoBackup', '0')
  1069. ->setDefault('backupTime', '1:00')
  1070. ->setDefault('backupCycle', '1')
  1071. ->get();
  1072. $autoBackup = (int)$settings->autoBackup;
  1073. $command = 'moduleName=instance&methodName=cronBackup&instanceID=' . $instance->id;
  1074. if(!validater::checkInt($settings->backupKeepDays, 1, 30))
  1075. {
  1076. dao::$errors['message'] = $this->lang->instance->backup->keepDayRange;
  1077. return false;
  1078. }
  1079. if($autoBackup == 1 && !preg_match("/^([0-2][0-9]):([0-5][0-9])$/", $settings->backupTime, $parts))
  1080. {
  1081. dao::$errors['message'] = $this->lang->instance->backup->invalidTime;
  1082. return false;
  1083. }
  1084. if($instance->backupKeepDays != $settings->backupKeepDays)
  1085. {
  1086. $this->dao->update(TABLE_INSTANCE)->set('backupKeepDays')->eq($settings->backupKeepDays)->where('id')->eq($instance->id)->exec();
  1087. if($this->dao->isError()) return false;
  1088. }
  1089. /* Disable backup operation. */
  1090. if(!$autoBackup)
  1091. {
  1092. $this->dao->delete()->from(TABLE_CRON)->where('command')->eq($command)->exec();
  1093. $this->dao->update(TABLE_INSTANCE)->set('autoBackup')->eq($autoBackup)->where('id')->eq($instance->id)->exec();
  1094. if($this->dao->isError()) return false;
  1095. $this->action->create('instance', $instance->id, 'savebackupsettings', '', json_encode(array('result' => 'success', 'data' => $settings)));
  1096. return true;
  1097. }
  1098. /* Enable backup operation. */
  1099. $cron = $this->dao->select('*')->from(TABLE_CRON)->where('command')->eq($command)->fetch();
  1100. list($hour, $minute) = explode(':', $settings->backupTime);
  1101. $cronData = new stdclass;
  1102. $cronData->m = intval($minute);
  1103. $cronData->h = intval($hour);
  1104. $cronData->dom = '*';
  1105. $cronData->mon = '*';
  1106. $cronData->dow = '*';
  1107. $cronData->type = 'zentao';
  1108. $cronData->status = 'normal';
  1109. $cronData->command = $command;
  1110. $cronData->remark = $this->lang->instance->backup->cronRemark;
  1111. $this->dao->update(TABLE_INSTANCE)->set('autoBackup')->eq($autoBackup)->where('id')->eq($instance->id)->exec();
  1112. if($this->dao->isError()) return false;
  1113. if($cron) $this->dao->update(TABLE_CRON)->autoCheck()->data($cronData)->where('id')->eq($cron->id)->exec();
  1114. else $this->dao->insert(TABLE_CRON)->data($cronData)->exec();
  1115. if($this->dao->isError()) return false;
  1116. $this->action->create('instance', $instance->id, 'savebackupsettings', '', json_encode(array('result' => 'success', 'data' => $settings)));
  1117. return true;
  1118. }
  1119. /**
  1120. * Get backup settings.
  1121. * 获取备份设置。
  1122. *
  1123. * @param int $instnaceID
  1124. * @access public
  1125. * @return object
  1126. */
  1127. public function getBackupSettings($instanceID)
  1128. {
  1129. $instance = $this->getByID($instanceID);
  1130. $cron = $this->dao->select('*')->from(TABLE_CRON)->where('command')->eq('moduleName=instance&methodName=cronBackup&instanceID=' . $instance->id)->limit(1)->fetch();
  1131. if(!$cron) $cron = new stdclass;
  1132. $hour = substr('0' . zget($cron, 'h', '1'), -2, 2);
  1133. $minute = substr('0' . zget($cron, 'm', '00'), -2, 2);
  1134. $settings = new stdclass;
  1135. $settings->backupTime = "{$hour}:{$minute}";
  1136. $settings->autoBackup = boolval(zget($instance, 'autoBackup', false));
  1137. $settings->cycleDays = 1; // Cycle days is always is 1 at present.
  1138. return $settings;
  1139. }
  1140. /**
  1141. * Restore instance.
  1142. * 还原应用实例。
  1143. * @param object $instance
  1144. * @param object $user
  1145. * @param string $backupName
  1146. * @access public
  1147. * @return bool
  1148. */
  1149. public function restore($instance, $user, $backupName)
  1150. {
  1151. $result = $this->cne->restore($instance, $backupName, $user->account);
  1152. if ($result->code != 200) return false;
  1153. return true;
  1154. }
  1155. /**
  1156. * auto backup.
  1157. * 自动备份。
  1158. *
  1159. * @param $instance
  1160. * @param $user
  1161. * @return void
  1162. */
  1163. public function autoBackup($instance, $user)
  1164. {
  1165. $cron = $this->dao->select('*')->from(TABLE_CRON)->where('command')->eq('moduleName=instance&methodName=cronBackup&instanceID=' . $instance->id)->limit(1)->fetch();
  1166. if(!$cron)
  1167. {
  1168. $this->action->create('instance', $instance->id, 'autobackup', '', json_encode(array('result' => 'fail', 'data'=> $cron)), $user->account);
  1169. return false;
  1170. }
  1171. /* 1. Call automatic backup*/
  1172. $result = $this->cne->backup($instance, $user->account);
  1173. if($result->code != 200)
  1174. {
  1175. $this->action->create('instance', $instance->id, 'autobackup', '', json_encode(array('result' => 'fail', 'data' => $result)), $user->account);
  1176. return false;
  1177. }
  1178. $this->action->create('instance', $instance->id, 'autobackup', '', json_encode(array('result' => 'success', 'data' => $result)), $user->account);
  1179. return true;
  1180. }
  1181. /**
  1182. * 删除备份。
  1183. * Delete backup.
  1184. * @param object $instance
  1185. * @param string $backupName
  1186. * @access public
  1187. * @return bool
  1188. */
  1189. public function deleteBackup($instance, $backupName)
  1190. {
  1191. return $this->cne->deleteBackup($instance, $backupName);
  1192. }
  1193. /**
  1194. * 删除备份任务.
  1195. * Delete Backup Cron.
  1196. * @param object $instance
  1197. * @return bool
  1198. */
  1199. public function deleteBackupCron($instance)
  1200. {
  1201. $command = 'moduleName=instance&methodName=cronBackup&instanceID=' . $instance->id;
  1202. $this->dao->delete()->from(TABLE_CRON)->where('command')->eq($command)->exec();
  1203. return !dao::isError();
  1204. }
  1205. /**
  1206. * 获取应用实例。
  1207. * Get by name.
  1208. *
  1209. * @param string $name
  1210. * @access public
  1211. * @return object|null
  1212. */
  1213. public function getByName($name)
  1214. {
  1215. $instance = $this->dao->select('*')->from(TABLE_INSTANCE)
  1216. ->where('name')->eq($name)
  1217. ->andWhere('deleted')->eq(0)
  1218. ->fetch();
  1219. if(!$instance) return new stdClass();
  1220. return $instance;
  1221. }
  1222. /**
  1223. * 清理备份
  1224. * Cleanup Backup.
  1225. * @param object $instance
  1226. * @param object $user
  1227. * @return bool
  1228. */
  1229. public function cleanBackup($instance, $user)
  1230. {
  1231. $instance->spaceData = $this->dao->select('*')->from(TABLE_SPACE)->where('id')->eq($instance->space)->fetch();
  1232. /* 1. Pick latest successful backup recorder. */
  1233. $latestBackup = null;
  1234. $backupList = $this->backupList($instance);
  1235. if(empty($backupList)) return true;
  1236. foreach($backupList as $backup)
  1237. {
  1238. if(empty($latestBackup) or $backup->status == 'completed' && $backup->create_time > $latestBackup->create_time) $latestBackup = $backup;
  1239. }
  1240. /* 2. delete expired backup. Get backup list of instance, then check every backup is expired or not.*/
  1241. $deleteData = array();
  1242. foreach($backupList as $backup)
  1243. {
  1244. if($latestBackup && $latestBackup->name == $backup->name) continue; // Keep latest successful backup.
  1245. $keepDays = !empty($instance->backupKeepDays) ? $instance->backupKeepDays : 1;
  1246. $deadline = intval($backup->create_time) + $keepDays * 24 * 3600;
  1247. $backupName = base64_decode(helper::safe64Decode($backup->name));
  1248. if($deadline < time())
  1249. {
  1250. $cneResult = $this->cne->deleteBackup($instance, $backupName);
  1251. array_push($deleteData, array('instanceId' => $instance->id, 'instanceName' => $instance->name, 'backupName' => $backupName, 'backupCreateTime' => $backup->create_time, 'cneResult' => $cneResult));
  1252. }
  1253. }
  1254. if(count($deleteData) > 0) $this->action->create('instance', $instance->id, 'deleteexpiredbackup', '', json_encode(array('result' => 'success', 'data' => $deleteData)), $user->account);
  1255. return true;
  1256. }
  1257. }