model.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. <?php
  2. /**
  3. * The model file of vm 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 xiawenlong <xiawenlong@cnezsoft.com>
  8. * @package ops
  9. * @version $Id$
  10. * @link https://www.zentao.net
  11. */
  12. class zanodemodel extends model
  13. {
  14. const STATUS_CREATED = 'created';
  15. const STATUS_LAUNCH = 'launch';
  16. const STATUS_FAIL_CREATE = 'vm_fail_create';
  17. const STATUS_RUNNING = 'running';
  18. const STATUS_SHUTOFF = 'shutoff';
  19. const STATUS_BUSY = 'busy';
  20. const STATUS_READY = 'ready';
  21. const STATUS_UNKNOWN = 'unknown';
  22. const STATUS_DESTROY = 'destroy';
  23. const STATUS_RESTORING = 'restoring';
  24. const STATUS_CREATING_SNAP = 'creating_snap';
  25. const STATUS_CREATING_IMG = 'creating_img';
  26. const STATUS_DESTROY_FAIL = 'vim_destroy_fail';
  27. const KVM_CREATE_PATH = '/api/v1/kvm/create';
  28. const KVM_TOKEN_PATH = '/api/v1/virtual/getVncToken';
  29. const KVM_EXPORT_PATH = '/api/v1/kvm/exportVm';
  30. const KVM_STATUS_PATH = '/api/v1/task/getStatus';
  31. const SNAPSHOT_CREATE_PATH = '/api/v1/kvm/addCreateSnap';
  32. const SNAPSHOT_REVERT_PATH = '/api/v1/kvm/addRevertSnap';
  33. const SNAPSHOT_REMOVE_PATH = '/api/v1/kvm/removeSnap';
  34. /**
  35. * 设置语言项。
  36. * Set lang;
  37. *
  38. * @access public
  39. * @return void
  40. */
  41. public function __construct()
  42. {
  43. parent::__construct();
  44. $this->app->lang->host = $this->lang->zanode;
  45. }
  46. /**
  47. * 创建执行节点。
  48. * Create an Node.
  49. *
  50. * @param object $data
  51. * @access public
  52. * @return int|bool
  53. */
  54. public function create($data)
  55. {
  56. $this->dao->insert(TABLE_ZAHOST)->data($data, 'osNamePhysics,osNamePre')->autoCheck()->exec();
  57. if(dao::isError()) return false;
  58. $nodeID = $this->dao->lastInsertID();
  59. $this->loadModel('action')->create('zanode', $nodeID, 'Created');
  60. return $nodeID;
  61. }
  62. /**
  63. * 通过执行节点创建镜像。
  64. * Create Image by zanode.
  65. *
  66. * @param int $zanodeID
  67. * @param object $data
  68. * @access public
  69. * @return int|bool
  70. */
  71. public function createImage($zanodeID, $data)
  72. {
  73. $node = $this->getNodeByID($zanodeID);
  74. if(!$node) return false;
  75. $newImage = new stdClass();
  76. $newImage->host = $node->parent;
  77. $newImage->name = $data->name;
  78. $newImage->status = 'created';
  79. $newImage->osName = $node->osName;
  80. $newImage->from = $node->id;
  81. $newImage->createdDate = helper::now();
  82. $this->dao->insert(TABLE_IMAGE)->data($newImage)->autoCheck()->exec();
  83. if(dao::isError()) return false;
  84. $newID = $this->dao->lastInsertID();
  85. /* Prepare create params. */
  86. $agentUrl = 'http://' . $node->ip . ':' . $node->hzap;
  87. $param = array(
  88. 'backing' => $data->name,
  89. 'task' => $newID,
  90. 'vm' => $node->name
  91. );
  92. $result = json_decode(commonModel::http($agentUrl . static::KVM_EXPORT_PATH, json_encode($param,JSON_NUMERIC_CHECK), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
  93. if(!empty($result) && $result->code == 'success')
  94. {
  95. $this->dao->update(TABLE_ZAHOST)->set('status')->eq(static::STATUS_CREATING_IMG)->where('id')->eq($node->id)->exec();
  96. return $newID;
  97. }
  98. $this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($newID)->exec();
  99. return false;
  100. }
  101. /**
  102. * 创建快照。
  103. * Create snapshot.
  104. *
  105. * @param object $node
  106. * @param object $snapshot
  107. * @access public
  108. * @return false|int
  109. */
  110. public function createSnapshot($node, $snapshot)
  111. {
  112. $this->dao->insert(TABLE_IMAGE)->data($snapshot)->autoCheck()->exec();
  113. if(dao::isError()) return false;
  114. $snapshotID = $this->dao->lastInsertID();
  115. $agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
  116. $param = array(array(
  117. 'name' => $snapshot->name,
  118. 'task' => $snapshotID,
  119. 'type' => 'createSnap',
  120. 'vm' => $node->name
  121. ));
  122. $result = json_decode(commonModel::http($agnetUrl . static::SNAPSHOT_CREATE_PATH, json_encode($param,JSON_NUMERIC_CHECK), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
  123. if(!empty($result) && $result->code == 'success')
  124. {
  125. $this->loadModel('action')->create('zanode', $node->id, 'createdSnapshot', '', $snapshot->name);
  126. $this->dao->update(TABLE_ZAHOST)->set('status')->eq(static::STATUS_CREATING_SNAP)->where('id')->eq($node->id)->exec();
  127. return $snapshotID;
  128. }
  129. $this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($snapshotID)->exec();
  130. dao::$errors = !empty($result) && !empty($result->msg) ? $result->msg : $this->lang->fail;
  131. return false;
  132. }
  133. /**
  134. * 创建默认的快照。
  135. * Create default snapshot.
  136. *
  137. * @param int $zanodeID
  138. * @access public
  139. * @return bool
  140. */
  141. public function createDefaultSnapshot($zanodeID = 0)
  142. {
  143. $node = $this->getNodeByID($zanodeID);
  144. if(!$node || $node->status != 'running')
  145. {
  146. dao::$errors['name'] = $this->lang->zanode->apiError['notRunning'];
  147. return false;
  148. }
  149. $defaultSnapshot = new stdClass();
  150. $defaultSnapshot->host = $node->id;
  151. $defaultSnapshot->name = 'defaultSnap';
  152. $defaultSnapshot->desc = '';
  153. $defaultSnapshot->status = 'creating';
  154. $defaultSnapshot->osName = $node->osName;
  155. $defaultSnapshot->memory = 0;
  156. $defaultSnapshot->disk = 0;
  157. $defaultSnapshot->fileSize = 0;
  158. $defaultSnapshot->from = 'snapshot';
  159. $defaultSnapshot->createdBy = 'system';
  160. $defaultSnapshot->createdDate = helper::now();
  161. return $this->createSnapshot($node, $defaultSnapshot);
  162. }
  163. /**
  164. * 编辑快照。
  165. * Edit snapshot.
  166. *
  167. * @param int $snapshotID
  168. * @param object $data
  169. * @access public
  170. * @return void
  171. */
  172. public function editSnapshot($snapshotID, $data)
  173. {
  174. $newSnapshot = new stdClass();
  175. $newSnapshot->localName = $data->name;
  176. $newSnapshot->desc = $data->desc;
  177. $this->dao->update(TABLE_IMAGE)
  178. ->data($newSnapshot)
  179. ->where('id')->eq($snapshotID)
  180. ->autoCheck()
  181. ->exec();
  182. }
  183. /**
  184. * 将执行节点还原到此快照。
  185. * Restore zanode to snapshot.
  186. *
  187. * @param int $zanodeID
  188. * @param int $snapshotID
  189. * @access public
  190. * @return bool
  191. */
  192. public function restoreSnapshot($zanodeID = 0, $snapshotID = 0)
  193. {
  194. $node = $this->getNodeByID($zanodeID);
  195. $snap = $this->getImageByID($snapshotID);
  196. /* 检查快照的状态。*/
  197. /* Check snapshot status. */
  198. $snap->status = $snap->status == 'restoring' && time() - strtotime($snap->restoreDate) > 600 ? 'restore_failed' : $snap->status;
  199. if(!in_array($snap->status, array('completed', 'restoring', 'restore_failed', 'restore_completed'))) dao::$errors = $this->lang->zanode->snapStatusError;
  200. if($snap->status == 'restoring') dao::$errors = $this->lang->zanode->snapRestoring;
  201. if(dao::isError()) return false;
  202. /* 更新快照状态。*/
  203. /* Update snapshot status. */
  204. $this->dao->update(TABLE_IMAGE)->set('status')->eq('restoring')->set('restoreDate')->eq(helper::now())->where('id')->eq($snapshotID)->exec();
  205. /* 执行还原命令。*/
  206. /* Execute the restore command. */
  207. $agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
  208. $param = array(array(
  209. 'name' => $snap->name,
  210. 'task' => $snapshotID,
  211. 'type' => 'revertSnap',
  212. 'vm' => $node->name
  213. ));
  214. $result = json_decode(commonModel::http($agnetUrl . static::SNAPSHOT_CREATE_PATH, json_encode($param, JSON_NUMERIC_CHECK), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
  215. /* 若执行成功修改执行节点的状态。*/
  216. /* Change node status when success. */
  217. if(!empty($result) && $result->code == 'success')
  218. {
  219. $this->dao->update(TABLE_ZAHOST)->set('status')->eq('restoring')->where('id')->eq($node->id)->exec();
  220. $this->loadModel('action')->create('zanode', $zanodeID, 'restoredsnapshot', '', $snap->name);
  221. return true;
  222. }
  223. /* 执行失败时修改快照状态为完成。*/
  224. /* Change status to completed when fail. */
  225. $this->dao->update(TABLE_IMAGE)->set('status')->eq('completed')->where('id')->eq($snapshotID)->exec();
  226. dao::$errors[] = !empty($result) && !empty($result->msg) ? $result->msg : $this->lang->zanode->apiError['fail'];
  227. return false;
  228. }
  229. /**
  230. * 删除快照。
  231. * Delete snapshot.
  232. *
  233. * @param int $snapshotID
  234. * @access public
  235. * @return string|bool
  236. */
  237. public function deleteSnapshot($snapshotID)
  238. {
  239. $snapshot = $this->getImageByID($snapshotID);
  240. $node = $this->getNodeByID($snapshot->host);
  241. $agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
  242. $param = (object)array(
  243. 'name' => $snapshot->name,
  244. 'task' => $snapshotID,
  245. 'vm' => $node->name
  246. );
  247. $result = json_decode(commonModel::http($agnetUrl . static::SNAPSHOT_REMOVE_PATH, json_encode($param,JSON_NUMERIC_CHECK), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
  248. if(!empty($result) && $result->code == 'success')
  249. {
  250. $this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($snapshotID)->exec();
  251. $this->loadModel('action')->create('zanode', $node->id, 'deletesnapshot', '', $snapshot->name);
  252. return true;
  253. }
  254. $error = !empty($result) && !empty($result->msg) ? $result->msg : $this->lang->zanode->apiError['fail'];
  255. return $error;
  256. }
  257. /**
  258. * 销毁一个执行节点。
  259. * Destroy Node.
  260. *
  261. * @param int $id
  262. * @access public
  263. * @return string
  264. */
  265. public function destroy($id)
  266. {
  267. $node = $this->getNodeByID($id);
  268. if(!$node) return '';
  269. if($node->hostType != 'physics')
  270. {
  271. $req = array( 'name' => $node->name );
  272. $agentUrl = 'http://' . $node->ip . ':' . $node->hzap;
  273. $result = commonModel::http($agentUrl . '/api/v1/kvm/remove', json_encode($req), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10);
  274. $data = json_decode($result, true);
  275. if(empty($data)) return $this->lang->zanode->notFoundAgent;
  276. if($data['code'] != 'success') return zget($this->lang->zanode->apiError, $data['code'], $data['msg']);
  277. }
  278. $this->dao->delete()->from(TABLE_ZAHOST)->where('id')->eq($id)->exec();;
  279. $this->loadModel('action')->create('zanode', $id, 'deleted');
  280. return '';
  281. }
  282. /**
  283. * 更新一个执行节点。
  284. * Update Node.
  285. *
  286. * @param int $id
  287. * @param object $hostInfo
  288. * @return array|bool
  289. */
  290. public function update($id, $hostInfo)
  291. {
  292. $oldHost = $this->getNodeById($id);
  293. $this->dao->update(TABLE_ZAHOST)
  294. ->data($hostInfo)
  295. ->batchCheck($this->config->zanode->edit->requiredFields, 'notempty')
  296. ->where('id')->eq($id)
  297. ->exec();
  298. if(dao::isError()) return false;
  299. return common::createChanges($oldHost, $hostInfo);
  300. }
  301. /**
  302. * 更新导出镜像的状态。
  303. * Update Image status.
  304. *
  305. * @param int $imageID
  306. * @param object $data
  307. * @access public
  308. * @return void
  309. */
  310. public function updateImageStatus($imageID, $data)
  311. {
  312. $this->dao->update(TABLE_IMAGE)->data($data)->where('id')->eq($imageID)->autoCheck()->exec();
  313. }
  314. /**
  315. * 获取执行节点列表。
  316. * Get zanode list.
  317. *
  318. * @param string $browseType
  319. * @param int $param
  320. * @param string $orderBy
  321. * @param object $pager
  322. * @access public
  323. * @return array
  324. */
  325. public function getListByQuery($browseType = 'all', $param = 0, $orderBy = 't1.id_desc', $pager = null)
  326. {
  327. $query = '';
  328. if($browseType == 'bysearch')
  329. {
  330. if($param)
  331. {
  332. $query = $this->loadModel('search')->getQuery($param);
  333. if($query)
  334. {
  335. $this->session->set('zanodeQuery', $query->sql);
  336. $this->session->set('zanodeForm', $query->form);
  337. }
  338. else
  339. {
  340. $this->session->set('zanodeQuery', ' 1 = 1');
  341. }
  342. }
  343. else
  344. {
  345. if($this->session->zanodeQuery == false) $this->session->set('zanodeQuery', ' 1 = 1');
  346. }
  347. $query = $this->session->zanodeQuery;
  348. $query = preg_replace('/`(id|name|status|osName|parent|cpuCores|memory|diskSize|extranet)`/', 't1.`\1`', $query);
  349. $query = str_replace('`hostID`', 't2.`id`', $query);
  350. }
  351. $nodeList = $this->zanodeTao->getZaNodeListByQuery($query, $orderBy, $pager);
  352. $osList = $this->config->zanode->linuxList + $this->config->zanode->windowsList;
  353. foreach($nodeList as $node)
  354. {
  355. $node = $this->processNodeStatus($node);
  356. if(isset($osList[$node->osName]) && !empty($osList[$node->osName]))
  357. {
  358. $this->dao->update(TABLE_ZAHOST)
  359. ->beginIF(isset($osList[$node->osName]) && !empty($osList[$node->osName]))->set('osName')->eq($osList[$node->osName])->fi()
  360. ->where('id')->eq($node->id)
  361. ->exec();
  362. }
  363. }
  364. return $nodeList;
  365. }
  366. /**
  367. * 获取执行节点的id-name键值对。
  368. * Get node id-name pairs.
  369. *
  370. * @param string $orderBy
  371. * @access public
  372. * @return array
  373. */
  374. public function getPairs($orderBy = 'id_desc')
  375. {
  376. return $this->dao->select('id,name')->from(TABLE_ZAHOST)
  377. ->where('deleted')->eq(0)
  378. ->andWhere('type')->in('node,physics')
  379. ->orderBy($orderBy)
  380. ->fetchPairs();
  381. }
  382. /**
  383. * 通过宿主机ID获取执行节点。
  384. * Get node list by hostID
  385. *
  386. * @param int $hostID
  387. * @param string $orderBy
  388. * @access public
  389. * @return array
  390. */
  391. public function getListByHost($hostID, $orderBy = 'id_desc')
  392. {
  393. if(!$hostID) return array();
  394. $list = $this->zanodeTao->getSubZahostListByID($hostID, $orderBy);
  395. $host = $this->loadModel('zahost')->getByID($hostID);
  396. foreach($list as $node)
  397. {
  398. $node->heartbeat = empty($node->heartbeat) ? '' : $node->heartbeat;
  399. if($node->status == 'running' || $node->status == 'ready')
  400. {
  401. if(empty($host) || $host->status != 'online')
  402. {
  403. $node->status = self::STATUS_SHUTOFF;
  404. }
  405. elseif(time() - strtotime($node->heartbeat) > 60)
  406. {
  407. $node->status = 'wait';
  408. }
  409. }
  410. }
  411. return $list;
  412. }
  413. /**
  414. * 通过ID获取host。
  415. * Get Host by id.
  416. *
  417. * @param int $id
  418. * @access public
  419. * @return object
  420. */
  421. public function getHostByID($id)
  422. {
  423. return $this->dao->select('*')->from(TABLE_ZAHOST)
  424. ->where('id')->eq($id)
  425. ->fetch();
  426. }
  427. /**
  428. * 通过ip获取host。
  429. * Get Host by IP.
  430. *
  431. * @param string $ip
  432. * @access public
  433. * @return object
  434. */
  435. public function getHostByIP($ip)
  436. {
  437. return $this->dao->select('*')->from(TABLE_ZAHOST)
  438. ->where('extranet')->eq($ip)
  439. ->fetch();
  440. }
  441. /**
  442. * 通过 id 获取镜像。
  443. * Get Image by id.
  444. *
  445. * @param int $id
  446. * @access public
  447. * @return object|false
  448. */
  449. public function getImageByID($id)
  450. {
  451. return $this->dao->select('*')->from(TABLE_IMAGE)->where('id')->eq($id)->fetch();
  452. }
  453. /**
  454. * 获取自定义的镜像。
  455. * Get custom image.
  456. *
  457. * @param int $nodeID
  458. * @param string|array $status
  459. * @param string $orderBy
  460. * @access public
  461. * @return object|false
  462. */
  463. public function getCustomImage($nodeID = 0, $status = '', $orderBy = 'id_desc')
  464. {
  465. return $this->dao->select('*')->from(TABLE_IMAGE)
  466. ->where('`from`')->eq($nodeID)
  467. ->beginIF($status)->andWhere('status')->in($status)->fi()
  468. ->orderBy($orderBy)
  469. ->fetch();
  470. }
  471. /**
  472. * 获取快照列表。
  473. * Get snapshot list.
  474. *
  475. * @param int $nodeID
  476. * @param string $orderBy
  477. * @param object $pager
  478. * @access public
  479. * @return array
  480. */
  481. public function getSnapshotList($nodeID, $orderBy = 'id', $pager = null)
  482. {
  483. $snapshotList = $this->dao->select('*')->from(TABLE_IMAGE)
  484. ->where('host')->eq($nodeID)
  485. ->andWhere('`from`')->eq('snapshot')
  486. ->orderBy($orderBy)
  487. ->page($pager)
  488. ->fetchAll('name');
  489. foreach($snapshotList as $name => $snapshot)
  490. {
  491. if($snapshot->status == 'creating' && (time() - strtotime($snapshot->createdDate)) > 600)
  492. {
  493. if($snapshot->name == 'defaultSnap' && $snapshot->createdBy == "system")
  494. {
  495. $this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($snapshot->id)->exec();
  496. $this->dao->update(TABLE_ZAHOST)->data(array("status" => "wait"))->where("id")->eq($snapshot->host)->exec();
  497. continue;
  498. }
  499. $this->dao->update(TABLE_IMAGE)->set('status')->eq('failed')->where('id')->eq($snapshot->id)->exec();
  500. $snapshotList[$name]->status = 'failed';
  501. }
  502. }
  503. return $snapshotList;
  504. }
  505. /**
  506. * 通过 id 获取执行节点。
  507. * Get Node by id.
  508. *
  509. * @param int $id
  510. * @access public
  511. * @return object|false
  512. */
  513. public function getNodeByID($id)
  514. {
  515. $node = $this->dao->select("t1.*, t2.name as hostName, if(t1.hostType='', t2.extranet, t1.extranet) ip,t2.zap as hzap,if(t1.hostType='', t3.osName, t1.osName) osName, if(t1.hostType='', t2.tokenSN, t1.tokenSN) tokenSN, if(t1.hostType='', t2.secret, t1.secret) secret")
  516. ->from(TABLE_ZAHOST)->alias('t1')
  517. ->leftJoin(TABLE_ZAHOST)->alias('t2')->on('t1.parent = t2.id')
  518. ->leftJoin(TABLE_IMAGE)->alias('t3')->on('t3.id = t1.image')
  519. ->where('t1.id')->eq($id)
  520. ->fetch();
  521. if(empty($node)) return false;
  522. return $this->processNodeStatus($node);
  523. }
  524. /**
  525. * 通过 mac 地址获取执行节点。
  526. * Get Node by mac address.
  527. *
  528. * @param string $mac
  529. * @access public
  530. * @return object|false
  531. */
  532. public function getNodeByMac($mac)
  533. {
  534. $node = $this->dao->select('*')->from(TABLE_ZAHOST)->where('mac')->eq($mac)->fetch();
  535. if(empty($node)) return false;
  536. return $this->processNodeStatus($node);
  537. }
  538. /**
  539. * 计算执行节点的状态。
  540. * Process node status.
  541. *
  542. * @param object $node
  543. * @access protected
  544. * @return object
  545. */
  546. protected function processNodeStatus($node)
  547. {
  548. $oldNodeStatus = $node->status;
  549. $host = $node->hostType == '' ? $this->loadModel('zahost')->getByID($node->parent) : clone $node;
  550. if($host && is_object($host))
  551. {
  552. $host->status = in_array($host->status, array('running', 'ready')) ? 'online' : $host->status;
  553. }
  554. if($node->status == 'running' || $node->status == 'ready' || $node->status == 'online')
  555. {
  556. if(empty($host) || $host->status != 'online')
  557. {
  558. $node->status = self::STATUS_SHUTOFF;
  559. }
  560. elseif(time() - strtotime($node->heartbeat) > 60)
  561. {
  562. $node->status = $node->hostType == '' ? 'wait' : 'offline';
  563. }
  564. }
  565. if($node->status == 'creating_img')
  566. {
  567. $customImage = $this->getCustomImage($node->id);
  568. $node->status = !empty($customImage->status) && in_array($customImage->status, array('failed', 'completed')) ? 'running' : $node->status;
  569. }
  570. if($oldNodeStatus != $node->status)
  571. {
  572. $this->dao->update(TABLE_ZAHOST)->set('status')->eq($node->status)->where('id')->eq($node->id)->exec();
  573. }
  574. return $node;
  575. }
  576. /**
  577. * 获取远程操控信息。
  578. * Get vnc url.
  579. *
  580. * @param object $node
  581. * @access public
  582. * @return object|bool
  583. */
  584. public function getVncUrl($node)
  585. {
  586. if($this->loadModel('zahost')->hiddenHost()) return false;
  587. if(empty($node) || empty($node->parent) || empty($node->vnc)) return false;
  588. $agnetUrl = 'http://' . $node->ip . ':' . $node->hzap . static::KVM_TOKEN_PATH;
  589. $result = json_decode(commonModel::http("$agnetUrl?port={$node->vnc}", array(), array(CURLOPT_CUSTOMREQUEST => 'GET'), array("Authorization:$node->tokenSN"), 'json', 'POST', 10));
  590. if(empty($result) || $result->code != 'success') return false;
  591. $returnData = new stdClass();
  592. $returnData->hostIP = $node->ip;
  593. $returnData->agentPort = $node->hzap;
  594. $returnData->vnc = $node->vnc;
  595. $returnData->token = $result->data->token;
  596. return $returnData;
  597. }
  598. /**
  599. * 通过 product id 获取自动化设置。
  600. * Get automation by product id.
  601. *
  602. * @param int $productID
  603. * @access public
  604. * @return object|false
  605. */
  606. public function getAutomationByProduct($productID = 0)
  607. {
  608. return $this->dao->select('*')->from(TABLE_AUTOMATION)->where('product')->eq($productID)->fetch();
  609. }
  610. /**
  611. * 通过 id 获取自动化设置。
  612. * Get automation by id.
  613. *
  614. * @param int $id
  615. * @access public
  616. * @return object|false
  617. */
  618. public function getAutomationByID($id)
  619. {
  620. return $this->dao->select('*')->from(TABLE_AUTOMATION)->where('id')->eq($id)->fetch();
  621. }
  622. /**
  623. * 自动化设置。
  624. * Set automation setting.
  625. *
  626. * @param object $object
  627. * @access public
  628. * @return int|false
  629. */
  630. public function setAutomationSetting($object)
  631. {
  632. $this->dao->delete()->from(TABLE_AUTOMATION)->where('product')->eq($object->product)->exec();
  633. $this->dao->insert(TABLE_AUTOMATION)
  634. ->data($object)
  635. ->batchCheck('node,scriptPath', 'notempty')
  636. ->autoCheck()
  637. ->exec();
  638. if(dao::isError()) return false;
  639. return $this->dao->lastInsertID();
  640. }
  641. /**
  642. * 执行ZTF脚本。
  643. * Run ZTFScript.
  644. *
  645. * @param int $scriptID
  646. * @param int $caseID
  647. * @param int $testtaskID
  648. * @access public
  649. * @return string|object
  650. */
  651. public function runZTFScript($scriptID = 0, $caseID = 0, $testtaskID = 0)
  652. {
  653. $automation = $this->getAutomationByID($scriptID);
  654. $node = $this->getNodeByID($automation->node);
  655. if(empty($node) || $node->status != 'running' || !$node->ip || !$node->ztf || !$node->tokenSN)
  656. {
  657. $this->dao->delete()->from(TABLE_TESTRESULT)->where('id')->eq($testtaskID)->exec();
  658. return dao::$errors = $this->lang->zanode->runTimeout;
  659. }
  660. $params = array(
  661. 'cmd' => $automation->shell,
  662. 'ids' => strval($caseID),
  663. 'path' => $automation->scriptPath,
  664. 'task' => intval($testtaskID)
  665. );
  666. $result = json_decode(commonModel::http("http://{$node->ip}:{$node->ztf}/api/v1/jobs/add", json_encode($params), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
  667. if(empty($result) || $result->code != 0)
  668. {
  669. $this->dao->delete()->from(TABLE_TESTRESULT)->where('id')->eq($testtaskID)->exec();
  670. return dao::$errors = $this->lang->zanode->runTimeout;
  671. }
  672. return $result;
  673. }
  674. /**
  675. * 判断按钮是否可点击。
  676. * Judge an action is clickable or not.
  677. *
  678. * @param object $node
  679. * @param string $action
  680. * @access public
  681. * @return bool
  682. */
  683. public static function isClickable($node, $action)
  684. {
  685. $action = strtolower($action);
  686. if(isset($node->from) && $node->from == 'snapshot')
  687. {
  688. if(!empty($node->isDefault) && in_array($action, array('editsnapshot', 'deletesnapshot'))) return false;
  689. }
  690. if($action == 'resume') return $node->status == 'suspend' && $node->hostType != 'physics';
  691. if($action == 'start') return $node->status == 'shutoff' && $node->hostType != 'physics';
  692. if($action == 'getvnc') return $node->hostType == '' && in_array($node->status, array('running', 'launch', 'wait'));
  693. if($action == 'close' || $action == 'reboot') return $node->hostType != 'physics' && !in_array($node->status, array('wait', 'creating_img', 'creating_snap', 'restoring', 'shutoff'));
  694. if($action == 'suspend' || $action == 'createsnapshot') return $node->status == 'running' && $node->hostType != 'physics';
  695. if($action == 'createimage') return ($node->status == 'running' || $node->status == 'creating_img') && $node->hostType != 'physics';
  696. return true;
  697. }
  698. /**
  699. * 检查创建字段。
  700. * Check fields of create.
  701. *
  702. * @param object $data
  703. * @access public
  704. * @return bool
  705. */
  706. public function checkFields4Create($data)
  707. {
  708. /* 检查必填项。*/
  709. /* Check required fields. */
  710. $this->dao->update(TABLE_ZAHOST)->data($data)
  711. ->batchCheck($this->config->zanode->create->requiredFields, 'notempty');
  712. if(dao::isError()) return false;
  713. /* 检查名称格式。*/
  714. /* Check the style of name. */
  715. if(!preg_match("/^(?!_)(?!-)(?!\.)[a-zA-Z0-9\_\.\-]+$/", $data->name))
  716. {
  717. dao::$errors['name'] = $this->lang->zanode->nameValid;
  718. return false;
  719. }
  720. /* 检查名称的唯一性。*/
  721. /* If name already exists return error. */
  722. $node = $this->dao->select('*')->from(TABLE_ZAHOST)->where('name')->eq($data->name)->andWhere('type')->eq('node')->fetch();
  723. if($node)
  724. {
  725. dao::$errors['name'] = $this->lang->zanode->nameUnique;
  726. return false;
  727. }
  728. /* 检查网络状态。*/
  729. /* Check the status of network. */
  730. if(!empty($data->hostType) && $data->hostType == 'physics')
  731. {
  732. $ping = $this->loadModel('zahost')->checkAddress($data->extranet);
  733. if(!$ping)
  734. {
  735. dao::$errors['extranet'] = $this->lang->zanode->netError;
  736. return false;
  737. }
  738. }
  739. return true;
  740. }
  741. /**
  742. * 连接Agent服务。
  743. * Link agent service.
  744. *
  745. * @param object $data
  746. * @access public
  747. * @return bool
  748. */
  749. public function linkAgentService($data)
  750. {
  751. $image = $this->getImageByID($data->image);
  752. $host = $this->getHostByID($data->parent);
  753. $agentUrl = 'http://' . $host->extranet . ':' . $host->zap;
  754. $param = array(
  755. 'os' => $image->osName,
  756. 'path' => $image->path,
  757. 'name' => $data->name,
  758. 'cpu' => (int)$data->cpuCores,
  759. 'disk' => (int)$data->diskSize,
  760. 'memory' => (int)$data->memory,
  761. );
  762. $result = json_decode(commonModel::http($agentUrl . static::KVM_CREATE_PATH, json_encode($param), array(), array("Authorization:$host->tokenSN"), 'data', 'POST', 10));
  763. if(empty($result))
  764. {
  765. dao::$errors['image'] = $this->lang->zanode->notFoundAgent;
  766. return false;
  767. }
  768. if($result->code != 'success')
  769. {
  770. dao::$errors['image'] = $this->lang->zanode->createVmFail;
  771. return false;
  772. }
  773. return $result;
  774. }
  775. /**
  776. * 设置菜单
  777. * Set menu
  778. *
  779. * @access public
  780. * @return void
  781. */
  782. public function setMenu()
  783. {
  784. if($this->loadModel('zahost')->hiddenHost())
  785. {
  786. unset($this->lang->qa->menu->automation['subMenu']->zahost);
  787. }
  788. }
  789. }