415 lines
14 KiB
PHP
415 lines
14 KiB
PHP
<?php
|
|
/**
|
|
* The model file of zahost module of ZenTaoPMS.
|
|
*
|
|
* @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
|
|
* @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
|
|
* @author Wang Jianhua <wangjiahua@easycorp.ltd>
|
|
* @package zahost
|
|
* @version $Id$
|
|
* @link https://www.zentao.net
|
|
*/
|
|
class zahostModel extends model
|
|
{
|
|
/**
|
|
* 根据 ID 获取宿主机。
|
|
* Get host by id.
|
|
*
|
|
* @param int $hostID
|
|
* @access public
|
|
* @return object
|
|
*/
|
|
public function getByID(int $hostID): object|false
|
|
{
|
|
$host = $this->dao->select('*, id AS hostID')->from(TABLE_ZAHOST)->where('id')->eq($hostID)->fetch();
|
|
if(!$host) return false;
|
|
|
|
if(empty($host->heartbeat)) $host->heartbeat = '';
|
|
if(time() - strtotime($host->heartbeat) > 60 && $host->status == 'online') $host->status = 'offline';
|
|
|
|
return $host;
|
|
}
|
|
|
|
/**
|
|
* 获取宿主机的键值对。
|
|
* Get host pairs.
|
|
*
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public function getPairs(): array
|
|
{
|
|
return $this->dao->select('id, name')->from(TABLE_ZAHOST)->where('deleted')->eq('0')->andWhere('type')->eq('zahost')->orderBy('`group`')->fetchPairs();
|
|
}
|
|
|
|
/**
|
|
* 获取宿主机的列表。
|
|
* Get host list.
|
|
*
|
|
* @param string $browseType
|
|
* @param int $param
|
|
* @param string $orderBy
|
|
* @param object $pager
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public function getList(string $browseType = 'all', int $param = 0, string $orderBy = 'id_desc', ?object $pager = null): array
|
|
{
|
|
$query = '';
|
|
if($browseType == 'bysearch')
|
|
{
|
|
/* Concatenate the conditions for the query. */
|
|
if($this->session->zahostQuery == false) $this->session->set('zahostQuery', ' 1 = 1');
|
|
if($param)
|
|
{
|
|
$this->session->set('zahostQuery', ' 1 = 1');
|
|
|
|
$query = $this->loadModel('search')->getQuery($param);
|
|
if($query)
|
|
{
|
|
$this->session->set('zahostQuery', $query->sql);
|
|
$this->session->set('zahostForm', $query->form);
|
|
}
|
|
}
|
|
$query = $this->session->zahostQuery;
|
|
}
|
|
|
|
$hostList = $this->dao->select('*, id AS hostID')->from(TABLE_ZAHOST)
|
|
->where('deleted')->eq('0')
|
|
->andWhere('type')->eq('zahost')
|
|
->beginIF($query)->andWhere($query)->fi()
|
|
->orderBy($orderBy)
|
|
->page($pager)
|
|
->fetchAll();
|
|
|
|
foreach($hostList as $host)
|
|
{
|
|
if(empty($host->heartbeat)) $host->hertbeat = '';
|
|
if(time() - strtotime($host->heartbeat) > 60 && $host->status == 'online') $host->status = 'offline';
|
|
}
|
|
|
|
return $hostList;
|
|
}
|
|
|
|
/**
|
|
* 创建宿主机。
|
|
* Create a host.
|
|
*
|
|
* @param object $hostInfo
|
|
* @access public
|
|
* @return int|bool
|
|
*/
|
|
public function create(object $hostInfo): int|bool
|
|
{
|
|
$ping = $this->checkAddress($hostInfo->extranet);
|
|
if(!$ping) dao::$errors['extranet'][] = $this->lang->zahost->netError;
|
|
|
|
$this->dao->insert(TABLE_ZAHOST)->data($hostInfo)->autoCheck()
|
|
->batchCheck($this->config->zahost->create->requiredFields, 'notempty')
|
|
->batchCheck('cpuCores,diskSize', 'gt', 0)
|
|
->batchCheck('diskSize,memory', 'float')
|
|
->check('name', 'unique', "type='zahost'")
|
|
->exec();
|
|
|
|
if(dao::isError()) return false;
|
|
|
|
$hostID = $this->dao->lastInsertID();
|
|
$this->loadModel('action')->create('zahost', $hostID, 'created');
|
|
|
|
return $hostID;
|
|
}
|
|
|
|
/**
|
|
* 更新宿主机。
|
|
* Update a host.
|
|
*
|
|
* @param object $hostInfo
|
|
* @access public
|
|
* @return array|bool
|
|
*/
|
|
public function update(object $hostInfo): false|array
|
|
{
|
|
$oldHost = $this->getByID($hostInfo->id);
|
|
|
|
$ping = $this->checkAddress($hostInfo->extranet);
|
|
if(!$ping) dao::$errors['extranet'][] = $this->lang->zahost->netError;
|
|
|
|
$this->dao->update(TABLE_ZAHOST)->data($hostInfo)->autoCheck()
|
|
->batchCheck($this->config->zahost->create->requiredFields, 'notempty')
|
|
->batchCheck('cpuCores,diskSize', 'gt', 0)
|
|
->batchCheck('diskSize,memory', 'float')
|
|
->check('name', 'unique', "id != $hostInfo->id and type in ('vhost', 'zahost')")
|
|
->where('id')->eq($hostInfo->id)
|
|
->exec();
|
|
|
|
if(dao::isError()) return false;
|
|
|
|
return common::createChanges($oldHost, $hostInfo);
|
|
}
|
|
|
|
/**
|
|
* 检查宿主机的IP/域名是否能 ping 通。
|
|
* Ping ip/domain.
|
|
*
|
|
* @param string $address
|
|
* @access public
|
|
* @return bool
|
|
*/
|
|
public function ping(string $address): bool
|
|
{
|
|
if(strcasecmp(PHP_OS, 'WINNT') === 0)
|
|
{
|
|
exec("ping -n 1 {$address}", $outcome, $status);
|
|
}
|
|
else
|
|
{
|
|
exec("ping -c 1 {$address}", $outcome, $status);
|
|
}
|
|
|
|
return 0 == $status;
|
|
}
|
|
|
|
/**
|
|
* 检查宿主机的IP/域名是否可用。
|
|
* Telnet ip/domain.
|
|
*
|
|
* @param string $address
|
|
* @access public
|
|
* @return bool
|
|
*/
|
|
public function checkAddress(string $address): bool
|
|
{
|
|
$address = str_replace(array('https://', 'http://'), '', $address);
|
|
|
|
if(!filter_var($address, FILTER_VALIDATE_IP) && !filter_var(gethostbyname($address), FILTER_VALIDATE_IP)) return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 根据编号获取镜像。
|
|
* Get image by ID.
|
|
*
|
|
* @param int $imageID
|
|
* @access public
|
|
* @return false|object
|
|
*/
|
|
public function getImageByID(int $imageID): false|object
|
|
{
|
|
return $this->dao->select('*')->from(TABLE_IMAGE)->where('id')->eq($imageID)->fetch();
|
|
}
|
|
|
|
/**
|
|
* 根据镜像名称和宿主机编号获取镜像。
|
|
* Get image by name and host.
|
|
*
|
|
* @param string $imageName
|
|
* @param int $hostID
|
|
* @access public
|
|
* @return object|false
|
|
*/
|
|
public function getImageByNameAndHostID(string $imageName, int $hostID): object|false
|
|
{
|
|
return $this->dao->select('*')->from(TABLE_IMAGE)->where('host')->eq($hostID)->andWhere('name')->eq($imageName)->fetch();
|
|
}
|
|
|
|
/**
|
|
* 获取宿主机的镜像列表。
|
|
* Get image list of host.
|
|
*
|
|
* @param int $hostID
|
|
* @param string $orderBy
|
|
* @param object $pager
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public function getImageList(int $hostID, string $orderBy = 'id', ?object $pager = null): array
|
|
{
|
|
$imageList = json_decode(commonModel::http($this->config->zahost->imageListUrl, array(), array()));
|
|
if(empty($imageList)) return array();
|
|
|
|
$downloadedImageList = $this->dao->select('*')->from(TABLE_IMAGE)->where('host')->eq($hostID)->orderBy($orderBy)->page($pager)->fetchAll('name');
|
|
|
|
$refreshPageData = $this->zahostTao->insertImageList($imageList, $hostID, $downloadedImageList);
|
|
|
|
if($refreshPageData) $downloadedImageList = $this->dao->select('*')->from(TABLE_IMAGE)->where('host')->eq($hostID)->orderBy($orderBy)->page($pager)->fetchAll('name');
|
|
|
|
foreach($downloadedImageList as $image)
|
|
{
|
|
if($image->status == 'notDownloaded')
|
|
{
|
|
$image->cancelMisc = sprintf("title='%s' class='btn image-cancel image-cancel-%d %s'", $this->lang->zahost->cancel, $image->id, "disabled");
|
|
$image->downloadMisc = sprintf("title='%s' class='btn image-download image-download-%d %s'", $this->lang->zahost->image->downloadImage, $image->id, "");
|
|
}
|
|
else
|
|
{
|
|
$image->cancelMisc = sprintf("title='%s' data-id='%s' class='btn image-cancel image-cancel-%d %s'", $this->lang->zahost->cancel, $image->id, $image->id, in_array($image->status, array("inprogress", "created")) ? "" : "disabled");
|
|
$image->downloadMisc = sprintf("title='%s' data-id='%s' class='btn image-download image-download-%d %s'", $this->lang->zahost->image->downloadImage, $image->id, $image->id, in_array($image->status, array("completed", "inprogress", "created")) || $image->from == 'user' ? "disabled" : "");
|
|
}
|
|
}
|
|
|
|
return $downloadedImageList;
|
|
}
|
|
|
|
/**
|
|
* 获取镜像键值对。
|
|
* Get image pairs by host.
|
|
*
|
|
* @param int $hostID
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public function getImagePairs(int $hostID): array
|
|
{
|
|
return $this->dao->select('id, name')->from(TABLE_IMAGE)->where('host')->eq($hostID)->andWhere('status')->eq('completed')->fetchPairs();
|
|
}
|
|
|
|
/**
|
|
* 下载镜像。
|
|
* Download image.
|
|
*
|
|
* @param object $image
|
|
* @access public
|
|
* @return bool
|
|
*/
|
|
public function downloadImage(object $image): bool
|
|
{
|
|
$host = $this->getByID($image->host);
|
|
$apiUrl = 'http://' . $host->extranet . ':' . $host->zap . '/api/v1/download/add';
|
|
|
|
$apiParams = array();
|
|
$apiParams['md5'] = $image->md5;
|
|
$apiParams['url'] = $image->address;
|
|
$apiParams['task'] = intval($image->id);
|
|
|
|
$response = json_decode(commonModel::http($apiUrl, array($apiParams), array(CURLOPT_CUSTOMREQUEST => 'POST'), array("Authorization:$host->tokenSN"), 'json'));
|
|
|
|
if($response && $response->code == 'success')
|
|
{
|
|
$this->dao->update(TABLE_IMAGE)->set('status')->eq('created')->where('id')->eq($image->id)->exec();
|
|
return true;
|
|
}
|
|
|
|
dao::$errors[] = $this->lang->zahost->image->downloadImageFail;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 查询镜像的状态。
|
|
* Query image download progress.
|
|
*
|
|
* @param object $image
|
|
* @access public
|
|
* @return object
|
|
*/
|
|
public function queryDownloadImageStatus(object $image)
|
|
{
|
|
if(!empty($this->imageStatusList)) $result = $this->imageStatusList;
|
|
|
|
if(empty($this->imageStatusList))
|
|
{
|
|
$host = $this->getByID($image->host);
|
|
$apiUrl = 'http://' . $host->extranet . ':' . $host->zap . '/api/v1/task/getStatus';
|
|
$result = $this->imageStatusList = json_decode(commonModel::http($apiUrl, array(), array(CURLOPT_CUSTOMREQUEST => 'POST'), array("Authorization:$host->tokenSN"), 'json'));
|
|
|
|
if(!$result || $result->code != 'success') return $image;
|
|
}
|
|
|
|
$currentTask = $this->zahostTao->getCurrentTask($image->id, $result->data);
|
|
if($currentTask)
|
|
{
|
|
$image->rate = $currentTask->rate;
|
|
$image->status = $currentTask->status;
|
|
|
|
if(!empty($result->data->inprogress) && $currentTask->task != $result->data->inprogress[0]->task && $currentTask->status == 'created') $image->status = 'pending';
|
|
|
|
$this->dao->update(TABLE_IMAGE)->set('status')->eq($image->status)->set('path')->eq(zget($currentTask, 'path', ''))->where('id')->eq($image->id)->exec();
|
|
|
|
$image->taskID = $currentTask->id;
|
|
}
|
|
else
|
|
{
|
|
$image->taskID = 0;
|
|
}
|
|
|
|
return $image;
|
|
}
|
|
|
|
/**
|
|
* 取消镜像下载。
|
|
* Send cancel download image command to HOST.
|
|
*
|
|
* @param object $image
|
|
* @access public
|
|
* @return bool
|
|
*/
|
|
public function cancelDownload(object $image): bool
|
|
{
|
|
$image = $this->queryDownloadImageStatus($image);
|
|
$host = $this->getByID($image->host);
|
|
$apiUrl = 'http://' . $host->extranet . ':' . $host->zap . '/api/v1/download/cancel';
|
|
|
|
$apiParams = array();
|
|
$apiParams['id'] = intval($image->taskID);
|
|
|
|
$response = json_decode(commonModel::http($apiUrl, $apiParams, array(CURLOPT_CUSTOMREQUEST => 'POST'), array("Authorization:$host->tokenSN"), 'json'));
|
|
if($response && $response->code == 'success')
|
|
{
|
|
$this->dao->update(TABLE_IMAGE)->set('status')->eq('canceled')->where('id')->eq($image->id)->exec();
|
|
return true;
|
|
}
|
|
|
|
dao::$errors[] = $this->lang->zahost->image->cancelDownloadFail;
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 获取宿主机的执行节点。
|
|
* Get zanode group by zahost.
|
|
*
|
|
* @access public
|
|
* @return array
|
|
*/
|
|
public function getNodeGroupHost(): array
|
|
{
|
|
return $this->dao->select('t1.id AS hostID, t2.*')->from(TABLE_ZAHOST)->alias('t1')
|
|
->leftJoin(TABLE_ZAHOST)->alias('t2')->on('t2.parent = t1.id')
|
|
->where('t2.deleted')->eq('0')
|
|
->andWhere('t1.deleted')->eq('0')
|
|
->fetchGroup('hostID', 'id');
|
|
}
|
|
|
|
/**
|
|
* 判断按钮是否可点击。
|
|
* Judge an action is clickable or not.
|
|
*
|
|
* @param object $object
|
|
* @param string $action
|
|
* @access public
|
|
* @return bool
|
|
*/
|
|
public static function isClickable(object $object, string $action): bool
|
|
{
|
|
if($action == 'delete') return $object->canDelete;
|
|
if($action == 'browseImage') return $object->status != 'wait';
|
|
if($action == 'cancelDownload') return $object->status == 'notDownloaded' || !in_array($object->status, array('inprogress', 'created')) ? false : true;
|
|
if($action == 'downloadImage') return in_array($object->status, array('completed', 'inprogress', 'created')) || $object->from == 'user' ? false : true;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* 根据是否有宿主机,判断是否隐藏。
|
|
* Judge an action is clickable or not.
|
|
*
|
|
* @access public
|
|
* @return bool
|
|
*/
|
|
public function hiddenHost(): bool
|
|
{
|
|
$this->app->loadClass('pager', true);
|
|
$pager = pager::init(0, 20, 1);
|
|
|
|
return empty($this->getList('all', 0, 'id_desc', $pager));
|
|
}
|
|
}
|