* @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($hostID) { $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() { 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($browseType = 'all', $param = 0, $orderBy = 'id_desc', $pager = null) { $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($hostInfo) { $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($hostInfo) { $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($address) { 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($address) { $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($imageID) { 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($imageName, $hostID) { 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($hostID, $orderBy = 'id', $pager = null) { $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($hostID) { 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($image) { $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($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($image) { $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() { 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, $action) { 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() { $this->app->loadClass('pager', true); $pager = pager::init(0, 20, 1); return empty($this->getList('all', 0, 'id_desc', $pager)); } }