Files
2023-12-26 16:38:30 +08:00

1162 lines
37 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* The model file of vm module of ZenTaoCMS.
*
* @copyright Copyright 2009-2015 禅道软件(青岛)有限公司(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 xiawenlong <xiawenlong@cnezsoft.com>
* @package ops
* @version $Id$
* @link http://www.zentao.net
*/
class zanodemodel extends model
{
const STATUS_CREATED = 'created';
const STATUS_LAUNCH = 'launch';
const STATUS_FAIL_CREATE = 'vm_fail_create';
const STATUS_RUNNING = 'running';
const STATUS_SHUTOFF = 'shutoff';
const STATUS_BUSY = 'busy';
const STATUS_READY = 'ready';
const STATUS_UNKNOWN = 'unknown';
const STATUS_DESTROY = 'destroy';
const STATUS_RESTORING = 'restoring';
const STATUS_CREATING_SNAP = 'creating_snap';
const STATUS_CREATING_IMG = 'creating_img';
const STATUS_DESTROY_FAIL = 'vim_destroy_fail';
const KVM_CREATE_PATH = '/api/v1/kvm/create';
const KVM_TOKEN_PATH = '/api/v1/virtual/getVncToken';
const KVM_EXPORT_PATH = '/api/v1/kvm/exportVm';
const KVM_STATUS_PATH = '/api/v1/task/getStatus';
const SNAPSHOT_CREATE_PATH = '/api/v1/kvm/addCreateSnap';
const SNAPSHOT_REVERT_PATH = '/api/v1/kvm/addRevertSnap';
const SNAPSHOT_REMOVE_PATH = '/api/v1/kvm/removeSnap';
/**
* Set lang;
*
* @access public
* @return void
*/
public function __construct()
{
parent::__construct();
$this->app->lang->host = $this->lang->zanode;
}
/**
* Create an Node.
*
* @access public
* @return int|bool
*/
public function create()
{
$data = fixer::input('post')
->setIF($this->post->hostType == 'physics', 'parent', 0)
->setIF($this->post->hostType == 'physics', 'osName', $this->post->osNamePhysics)
->get();
$data->type = 'node';
if($data->hostType == 'physics')
{
$data->secret = md5($data->name . time());
$data->status = 'offline';
}
else
{
$data->hostType = '';
}
/* Batch check fields. */
$this->dao->update(TABLE_ZAHOST)->data($data)
->batchCheck($data->hostType != 'physics' ? $this->config->zanode->create->requiredFields : $this->config->zanode->create->physicsRequiredFields, 'notempty');
if(is_numeric($data->name)) dao::$errors['name'] = sprintf($this->lang->error->code, $this->lang->zanode->name);
if(dao::isError()) return false;
unset($data->osNamePhysics);
if(!preg_match("/^(?!_)(?!-)(?!\.)[a-zA-Z0-9\_\.\-]+$/", $data->name))
{
dao::$errors[] = $this->lang->zanode->nameValid;
return false;
}
/* If name already exists return error. */
$node = $this->dao->select('*')->from(TABLE_ZAHOST)->where('name')->eq($data->name)->andWhere('type')->eq('node')->fetch();
if($node) return dao::$errors[] = $this->lang->zanode->nameUnique;
if($data->hostType == 'physics')
{
$ping = $this->loadModel('zahost')->checkAddress($data->extranet);
if(!$ping)
{
dao::$errors[] = $this->lang->zanode->netError;
return false;
}
}
else
{
/* Get image. */
$image = $this->getImageByID($data->image);
/* Get host info. */
$host = $this->getHostByID($data->parent);
/* Prepare create params. */
$agnetUrl = 'http://' . $host->extranet . ':' . $host->zap;
$param = array(
'os' => $image->osName,
'path' => $image->path,
'name' => $data->name,
'cpu' => (int)$data->cpuCores,
'disk' => (int)$data->diskSize,
'memory' => (int)$data->memory,
);
$result = json_decode(commonModel::http($agnetUrl . static::KVM_CREATE_PATH, json_encode($param), null, array("Authorization:$host->tokenSN"), 'data', 'POST', 10));
if(empty($result))
{
dao::$errors[] = $this->lang->zanode->notFoundAgent;
return false;
}
if($result->code != 'success')
{
dao::$errors[] = $this->lang->zanode->createVmFail;
return false;
}
/* Prepare create ZenAgent Node data. */
$data->parent = $host->id;
$data->mac = $result->data->mac;
$data->vnc = (int)$result->data->vnc;
}
$data->status = static::STATUS_RUNNING;
$data->createdBy = $this->app->user->account;
$data->createdDate = helper::now();
/* Save ZenAgent Node. */
$this->dao->insert(TABLE_ZAHOST)->data($data)->autoCheck()->exec();
if(dao::isError()) return false;
$nodeID = $this->dao->lastInsertID();
/* update action log. */
$this->loadModel('action')->create('zanode', $nodeID, 'Created');
return $nodeID;
}
/**
* Create Image by zanode.
*
* @param int $zanodeID
* @access public
* @return int|bool
*/
public function createImage($zanodeID = 0)
{
$data = fixer::input('post')->get();
if(empty($data->name)) dao::$errors['message'][] = $this->lang->zanode->imageNameEmpty;
if(dao::isError()) return false;
$node = $this->getNodeByID($zanodeID);
$newImage = new stdClass();
$newImage->host = $node->parent;
$newImage->name = $data->name;
$newImage->status = 'created';
$newImage->osName = $node->osName;
$newImage->from = $node->id;
$newImage->createdDate = helper::now();
$this->dao->insert(TABLE_IMAGE)
->data($newImage)
->autoCheck()
->exec();
if(dao::isError()) return false;
$newID = $this->dao->lastInsertID();
/* Prepare create params. */
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
$param = array(
'backing' => $data->name,
'task' => $newID,
'vm' => $node->name
);
$result = json_decode(commonModel::http($agnetUrl . static::KVM_EXPORT_PATH, json_encode($param,JSON_NUMERIC_CHECK), null, array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
if(!empty($result) and $result->code == 'success')
{
$this->dao->update(TABLE_ZAHOST)->set('status')->eq(static::STATUS_CREATING_IMG)->where('id')->eq($node->id)->exec();
return $newID;
}
$this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($newID)->exec();
return false;
}
/**
* Create Snapshot by zanode.
*
* @param int $zanodeID
* @access public
* @return int|bool
*/
public function createSnapshot($zanodeID = 0)
{
$data = fixer::input('post')->get();
if(empty($data->name)) dao::$errors['name'] = $this->lang->zanode->imageNameEmpty;
if(dao::isError()) return false;
if(is_numeric($data->name)) dao::$errors['name'] = sprintf($this->lang->error->code, $this->lang->zanode->name);
if(dao::isError()) return false;
$node = $this->getNodeByID($zanodeID);
if($node->status != 'running') dao::$errors['name'] = $this->lang->zanode->apiError['notRunning'];
if(dao::isError()) return false;
$newSnapshot = new stdClass();
$newSnapshot->host = $node->id;
$newSnapshot->name = $data->name;
$newSnapshot->desc = $data->desc;
$newSnapshot->status = 'creating';
$newSnapshot->osName = $node->osName;
$newSnapshot->memory = 0;
$newSnapshot->disk = 0;
$newSnapshot->fileSize = 0;
$newSnapshot->from = 'snapshot';
$newSnapshot->createdBy = $this->app->user->account;
$newSnapshot->createdDate = helper::now();
$this->dao->insert(TABLE_IMAGE)
->data($newSnapshot)
->autoCheck()
->exec();
if(dao::isError()) return false;
$newID = $this->dao->lastInsertID();
/* Prepare create params. */
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
$param = array(array(
'name' => $data->name,
'task' => $newID,
'type' => 'createSnap',
'vm' => $node->name
));
$result = json_decode(commonModel::http($agnetUrl . static::SNAPSHOT_CREATE_PATH, json_encode($param, JSON_NUMERIC_CHECK), null, array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
if(!empty($result) and $result->code == 'success')
{
$this->loadModel('action')->create('zanode', $zanodeID, 'createdSnapshot', '', $data->name);
$this->dao->update(TABLE_ZAHOST)->set('status')->eq(static::STATUS_CREATING_SNAP)->where('id')->eq($node->id)->exec();
return $newID;
}
$this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($newID)->exec();
dao::$errors[] = (!empty($result) and !empty($result->msg)) ? $result->msg : $this->app->lang->fail;
return false;
}
public function createDefaultSnapshot($zanodeID = 0)
{
$node = $this->getNodeByID($zanodeID);
if($node->status != 'running') dao::$errors['name'] = $this->lang->zanode->apiError['notRunning'];
if(dao::isError()) return false;
$newSnapshot = new stdClass();
$newSnapshot->host = $node->id;
$newSnapshot->name = 'defaultSnap';
$newSnapshot->desc = '';
$newSnapshot->status = 'creating';
$newSnapshot->osName = $node->osName;
$newSnapshot->memory = 0;
$newSnapshot->disk = 0;
$newSnapshot->fileSize = 0;
$newSnapshot->from = 'snapshot';
$newSnapshot->createdBy = 'system';
$newSnapshot->createdDate = helper::now();
$this->dao->insert(TABLE_IMAGE)
->data($newSnapshot)
->autoCheck()
->exec();
if(dao::isError()) return false;
$newID = $this->dao->lastInsertID();
/* Prepare create params. */
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
$param = array(array(
'name' => $newSnapshot->name,
'task' => $newID,
'type' => 'createSnap',
'vm' => $node->name
));
$result = json_decode(commonModel::http($agnetUrl . static::SNAPSHOT_CREATE_PATH, json_encode($param,JSON_NUMERIC_CHECK), null, array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
if(!empty($result) and $result->code == 'success')
{
$this->dao->update(TABLE_ZAHOST)->set('status')->eq(static::STATUS_CREATING_SNAP)->where('id')->eq($node->id)->exec();
return $newID;
}
$this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($newID)->exec();
dao::$errors[] = (!empty($result) and !empty($result->msg)) ? $result->msg : $this->app->lang->fail;
return false;
}
/**
* Edit Snapshot.
*
* @param int $snapshotID
* @access public
* @return bool
*/
public function editSnapshot($snapshotID)
{
$data = fixer::input('post')->get();
if(empty($data->name)) dao::$errors['name'] = $this->lang->zanode->imageNameEmpty;
if(dao::isError()) return false;
if(is_numeric($data->name)) dao::$errors['name'] = sprintf($this->lang->error->code, $this->lang->zanode->name);
if(dao::isError()) return false;
$newSnapshot = new stdClass();
$newSnapshot->localName = $data->name;
$newSnapshot->desc = $data->desc;
$this->dao->update(TABLE_IMAGE)
->data($newSnapshot)
->where('id')->eq($snapshotID)
->autoCheck()
->exec();
return true;
}
/**
* Restore Snapshot to zanode.
*
* @param int $zanodeID
* @param int $snapshotID
* @access public
* @return bool
*/
public function restoreSnapshot($zanodeID = 0, $snapshotID = 0)
{
$node = $this->getNodeByID($zanodeID);
$snap = $this->getImageByID($snapshotID);
$snap->status = ($snap->status == 'restoring' && time() - strtotime($snap->restoreDate) > 600) ? 'restore_failed' : $snap->status;
if(!in_array($snap->status, array('completed', 'restoring', 'restore_failed', 'restore_completed'))) dao::$errors = $this->lang->zanode->snapStatusError;
if($snap->status == 'restoring') dao::$errors = $this->lang->zanode->snapRestoring;
if(dao::isError()) return false;
//update snapshot status
$this->dao->update(TABLE_IMAGE)
->set('status')->eq('restoring')
->set('restoreDate')->eq(helper::now())
->where('id')->eq($snapshotID)->exec();
/* Prepare create params. */
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
$param = array(array(
'name' => $snap->name,
'task' => $snapshotID,
'type' => 'revertSnap',
'vm' => $node->name
));
$result = json_decode(commonModel::http($agnetUrl . static::SNAPSHOT_CREATE_PATH, json_encode($param,JSON_NUMERIC_CHECK), null, array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
if(!empty($result) and $result->code == 'success')
{
$this->dao->update(TABLE_ZAHOST)->set('status')->eq('restoring')->where('id')->eq($node->id)->exec();
$this->loadModel('action')->create('zanode', $zanodeID, 'restoredsnapshot', '', $snap->name);
return true;
}
$this->dao->update(TABLE_IMAGE)->set('status')->eq('completed')->where('id')->eq($snapshotID)->exec();
dao::$errors[] = (!empty($result) and !empty($result->msg)) ? $result->msg : $this->lang->zanode->apiError['fail'];
return false;
}
/**
* Delete Snapshot.
*
* @param int $id
* @access public
* @return string|bool
*/
public function deleteSnapshot($snapshotID)
{
$snapshot = $this->getImageByID($snapshotID);
$node = $this->getNodeByID($snapshot->host);
if(!$node) return false;
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
$param = (object)array(
'name' => $snapshot->name,
'task' => $snapshotID,
'vm' => $node->name
);
$result = commonModel::http($agnetUrl . static::SNAPSHOT_REMOVE_PATH, json_encode($param,JSON_NUMERIC_CHECK), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10);
$result = json_decode($result);
if(!empty($result) and $result->code == 'success')
{
$this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($snapshotID)->exec();
$this->loadModel('action')->create('zanode', $node->id, 'deletesnapshot', '', $snapshot->name);
return true;
}
$error = (!empty($result) and !empty($result->msg)) ? $result->msg : $this->lang->zanode->apiError['fail'];
return $error;
}
/**
* Action Node.
*
* @param int $id
* @param string $type
* @return string
*/
public function handleNode($id, $type)
{
$node = $this->getNodeByID($id);
if(in_array($node->status, array('restoring', 'creating_img', 'creating_snap')))
{
return sprintf($this->lang->zanode->busy, $this->lang->zanode->statusList[$node->status]);
}
/* Prepare create params. */
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
$path = '/api/v1/kvm/' . $node->name . '/' . $type;
$param = array('vmUniqueName' => $node->name);
$result = commonModel::http($agnetUrl . $path, $param, array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10);
$data = json_decode($result, true);
if(empty($data)) return $this->lang->zanode->notFoundAgent;
if($data['code'] != 'success') return zget($this->lang->zanode->apiError, $data['code'], $data['msg']);
if($type != 'reboot')
{
$status = $type == 'suspend' ? 'suspend' : 'running';
if($type == 'destroy') $status = 'shutoff';
$this->dao->update(TABLE_ZAHOST)->set('status')->eq($status)->where('id')->eq($id)->exec();
}
$this->loadModel('action')->create('zanode', $id, ucfirst($type));
return '';
}
/**
* Destroy Node.
*
* @param int $id
* @access public
* @return string
*/
public function destroy($id)
{
$node = $this->getNodeByID($id);
if($node && $node->hostType != 'physics')
{
$req = array( 'name' => $node->name );
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap;
$result = commonModel::http($agnetUrl . '/api/v1/kvm/remove', json_encode($req), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10);
$data = json_decode($result, true);
if(empty($data)) return $this->lang->zanode->notFoundAgent;
if($data['code'] != 'success') return zget($this->lang->zanode->apiError, $data['code'], $data['msg']);
}
/* delete ZenAgent Node. */
$this->dao->delete()->from(TABLE_ZAHOST)->where('id')->eq($id)->exec();;
$this->loadModel('action')->create('zanode', $id, 'deleted');
return '';
}
/**
* Get ZenAgent Node created action record by node ID.
*
* @param int $id
* @access public
* @return object
*/
public function getNodeCreateActionByID($id)
{
return $this->dao->select('*')->from(TABLE_ACTION)
->where('objectType')->eq('zanode')
->andWhere('objectID')->eq($id)
->andWhere('action')->eq('created')
->fetch();
}
/**
* Callback update host data.
*
* @access public
* @return mixed
*/
public function updateHostByCallback()
{
$data = array();
$data['status'] = isset($_POST['status']) ? $_POST['status'] : '';
$data['agentPort'] = isset($_POST['port']) ? $_POST['port'] : '';
$ip = isset($_POST['ip']) ? $_POST['ip'] : '';
if(empty($ip)) return false;
$host = $this->getHostByIP($ip);
if(empty($host)) return 'Not Found';
$this->dao->update(TABLE_ZAHOST)->data($data)
->where('id')->eq($host->id)
->exec();
return true;
}
/**
* Update Node.
*
* @param int $id
* @return array|bool
*/
public function update($id)
{
$oldHost = $this->getNodeById($id);
$hostInfo = fixer::input('post')->get();
$hostInfo->editedBy = $this->app->user->account;
$hostInfo->editedDate = helper::now();
$this->dao->update(TABLE_ZAHOST)->data($hostInfo)
->batchCheck($this->config->zanode->edit->requiredFields, 'notempty');
if(dao::isError()) return false;
$this->dao->update(TABLE_ZAHOST)->data($hostInfo)->autoCheck()
->where('id')->eq($id)->exec();
return common::createChanges($oldHost, $hostInfo);
}
/**
* Get vm list.
*
* @param string $browseType
* @param int $param
* @param string $orderBy
* @param object $pager
* @return array
*/
public function getListByQuery($browseType = 'all', $param = 0, $orderBy = 't1.id_desc', $pager = null)
{
$query = '';
if($browseType == 'bysearch')
{
if($param)
{
$query = $this->loadModel('search')->getQuery($param);
if($query)
{
$this->session->set('nodeQuery', $query->sql);
$this->session->set('nodeForm', $query->form);
}
else
{
$this->session->set('nodeQuery', ' 1 = 1');
}
}
else
{
if($this->session->zanodeQuery == false) $this->session->set('zanodeQuery', ' 1 = 1');
}
$query = $this->session->zanodeQuery;
$query = preg_replace('/`(id|name|status|os|parent|cpuCores|memory|diskSize|extranet)`/', 't1.`\1`', $query);
$query = str_replace('`hostID`', 't2.`id`', $query);
}
$list = $this->dao->select("t1.*, t2.name as hostName, if(t1.hostType='', t2.extranet, t1.extranet) extranet,if(t1.hostType='', t3.osName, t1.osName) osName")->from(TABLE_ZAHOST)->alias('t1')
->leftJoin(TABLE_ZAHOST)->alias('t2')->on('t1.parent = t2.id')
->leftJoin(TABLE_IMAGE)->alias('t3')->on('t3.id = t1.image')
->where('t1.deleted')->eq(0)
->andWhere("t1.type = 'node'")
->beginIF($query)->andWhere($query)->fi()
->orderBy($orderBy)
->page($pager)
->fetchAll();
$hostIDList = array();
foreach($list as $l) $hostIDList[] = $l->parent;
$hosts = $this->dao->select('id,status,heartbeat')->from(TABLE_ZAHOST)
->where('id')->in(array_unique($hostIDList))
->fetchAll('id');
foreach($list as $l)
{
$l->heartbeat = empty($l->heartbeat) ? '' : $l->heartbeat;
$host = $l->hostType == '' ? zget($hosts, $l->parent) : clone $l;
$host->status = in_array($host->status, array('running', 'ready')) ? 'online' : $host->status;
$host->heartbeat = empty($host->heartbeat) ? '' : $host->heartbeat;
if($l->status == 'running' || $l->status == 'ready')
{
if(empty($host))
{
$l->status = self::STATUS_SHUTOFF;
continue;
}
if($host->status != 'online' || time() - strtotime($host->heartbeat) > 60)
{
$l->status = $l->hostType == '' ? 'wait' : 'offline';
continue;
}
if(time() - strtotime($l->heartbeat) > 60)
{
$l->status = $l->hostType == '' ? 'wait' : 'offline';
}
}
}
return $list;
}
/**
* Get node list.
*
* @param string $orderBy
* @access public
* @return array
*/
public function getList($orderBy = 'id_desc')
{
return $this->dao->select('*')->from(TABLE_ZAHOST)
->where('deleted')->eq(0)
->andWhere('type')->in('node,physics')
->orderBy($orderBy)
->fetchAll('id');
}
/**
* Get node pairs.
*
* @param string $orderBy
* @access public
* @return array
*/
public function getPairs($orderBy = 'id_desc')
{
return $this->dao->select('*')->from(TABLE_ZAHOST)
->where('deleted')->eq(0)
->andWhere('type')->in('node,physics')
->orderBy($orderBy)
->fetchPairs('id', 'name');
}
/**
* Get node list by hostID.
*
* @param string $browseType
* @param int $param
* @param string $orderBy
* @param object $pager
* @return array
*/
public function getListByHost($hostID, $orderBy = 'id_desc')
{
if(!$hostID) return array();
$list = $this->dao->select('id, name, vnc, cpuCores, memory, diskSize, osName, status, heartbeat')->from(TABLE_ZAHOST)
->where('deleted')->eq(0)
->andWhere('parent')->eq($hostID)
->orderBy($orderBy)
->fetchAll();
$host = $this->loadModel('zahost')->getByID($hostID);
foreach($list as $node)
{
$node->heartbeat = empty($node->heartbeat) ? '' : $node->heartbeat;
if($node->status == 'running' || $node->status == 'ready')
{
if(empty($host) || $host->status != 'online')
{
$node->status = self::STATUS_SHUTOFF;
}
elseif(time() - strtotime($node->heartbeat) > 60)
{
$node->status = 'wait';
}
}
}
return $list;
}
/**
* Get Host by id.
*
* @param int $id
* @access public
* @return object
*/
public function getHostByID($id)
{
return $this->dao->select('*')->from(TABLE_ZAHOST)
->where('id')->eq($id)
->fetch();
}
/**
* Get Host by IP.
*
* @param string $ip
* @access public
* @return object
*/
public function getHostByIP($ip)
{
return $this->dao->select('*')->from(TABLE_ZAHOST)
->where('extranet')->eq($ip)
->fetch();
}
/**
* Get Image.
*
* @param int $id
* @access public
* @return object
*/
public function getImageByID($id)
{
return $this->dao->select('*')->from(TABLE_IMAGE)
->where('id')->eq($id)
->fetch();
}
/**
* Get Node image list.
*
* @param string $orderBy
* @param object $pager
* @access public
* @return array
*/
public function getimageList($orderBy = 'id_desc', $pager = null)
{
return $this->dao->select('*')->from(TABLE_IMAGE)
->where('from')->eq(0)
->orderBy($orderBy)
->page($pager)
->fetchAll();
}
/**
* Get custom image.
*
* @param int $nodeID
* @param string|array $status
* @param string $orderBy
* @access public
* @return object
*/
public function getCustomImage($nodeID = 0, $status = '', $orderBy = 'id_desc')
{
return $this->dao->select('*')->from(TABLE_IMAGE)
->where('`from`')->eq($nodeID)
->beginIF($status)->andWhere('status')->in($status)->fi()
->orderBy($orderBy)
->fetch();
}
/**
* Get snapshot list.
*
* @param int $nodeID
* @param string $orderBy
* @param object $pager
* @access public
* @return array
*/
public function getSnapshotList($nodeID, $orderBy = 'id', $pager = null)
{
$snapshotList = $this->dao->select('*')->from(TABLE_IMAGE)
->where('host')->eq($nodeID)
->andWhere('`from`')->eq('snapshot')
->orderBy($orderBy)
->page($pager)
->fetchAll('name');
foreach($snapshotList as $name => $snapshot)
{
if($snapshot->status == 'creating' and (time() - strtotime($snapshot->createdDate)) > 600)
{
if($snapshot->name == 'defaultSnap' && $snapshot->createdBy == "system")
{
$this->dao->delete()->from(TABLE_IMAGE)->where('id')->eq($snapshot->id)->exec();
$this->dao->update(TABLE_ZAHOST)->data(array("status" => "wait"))->where("id")->eq($snapshot->host)->exec();
continue;
}
$this->dao->update(TABLE_IMAGE)->set('status')->eq('failed')->where('id')->eq($snapshot->id)->exec();
$snapshotList[$name]->status = 'failed';
}
}
return $snapshotList;
}
/**
* Get Node by id.
*
* @param int $id
* @return object
*/
public function getNodeByID($id)
{
$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")->from(TABLE_ZAHOST)
->alias('t1')
->leftJoin(TABLE_ZAHOST)->alias('t2')->on('t1.parent = t2.id')
->leftJoin(TABLE_IMAGE)->alias('t3')->on('t3.id = t1.image')
->where('t1.id')->eq($id)
->fetch();
$node->heartbeat = empty($node->heartbeat) ? '' : $node->heartbeat;
$host = $node->hostType == '' ? $this->loadModel('zahost')->getByID($node->parent) : clone $node;
$host->status = in_array($host->status, array('running', 'ready')) ? 'online' : $host->status;
if($node->status == 'running' || $node->status == 'ready' || $node->status == 'online')
{
if(empty($host) || $host->status != 'online')
{
$node->status = self::STATUS_SHUTOFF;
}
elseif(time() - strtotime($node->heartbeat) > 60)
{
$node->status = $node->hostType == '' ? 'wait' : 'offline';
}
}
return $node;
}
/**
* Get Node by mac address.
*
* @param string $mac
* @return object
*/
public function getNodeByMac($mac)
{
$node = $this->dao->select('*')->from(TABLE_ZAHOST)
->where('mac')->eq($mac)
->fetch();
if(empty($node)) return $node;
$host = $node->hostType == '' ? $this->loadModel('zahost')->getByID($node->parent) : $node;
$host->status = in_array($host->status, array('running', 'ready')) ? 'online' : $host->status;
if($node->status == 'running' || $node->status == 'ready')
{
if(empty($host) || $host->status != 'online')
{
$node->status = self::STATUS_SHUTOFF;
}
elseif(time() - strtotime($node->heartbeat) > 60)
{
$node->status = 'wait';
}
}
return $node;
}
/**
* Get automation by id.
*
* @param int $id
* @return object
*/
public function getScriptByID($id)
{
return $this->dao->select('*')->from(TABLE_AUTOMATION)
->where('id')->eq($id)
->fetch();
}
/**
* Get vnc url.
*
* @param object $node
* @access public
* @return object|bool
*/
public function getVncUrl($node)
{
if(empty($node) or empty($node->parent) or empty($node->vnc)) return false;
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap . static::KVM_TOKEN_PATH;
$result = json_decode(commonModel::http("$agnetUrl?port={$node->vnc}", array(), array(CURLOPT_CUSTOMREQUEST => 'GET'), array("Authorization:$node->tokenSN"), 'json', 'POST', 10));
if(empty($result) or $result->code != 'success') return false;
$returnData = new stdClass();
$returnData->hostIP = $node->ip;
$returnData->agentPort = $node->hzap;
$returnData->vnc = $node->vnc;
$returnData->token = $result->data->token;
return $returnData;
}
/**
* Get task status by zagent api.
*
* @param object $node
* @param int $taskID
* @param string $type
* @param string $status
* @access public
* @return array|bool
*/
public function getTaskStatus($node, $taskID = 0, $type = '', $status = '')
{
$agnetUrl = 'http://' . $node->ip . ':' . $node->hzap . static::KVM_STATUS_PATH;
$result = json_decode(commonModel::http($agnetUrl, array(), array(CURLOPT_CUSTOMREQUEST => 'POST'), array("Authorization:$node->tokenSN"), 'json', 'POST', 10));
if(empty($result) or $result->code != 'success') return false;
$data = $result->data;
if(empty($data)) return array();
if($status and !$taskID and isset($data->$status)) return $data->$status;
if(!$taskID) return $data;
foreach($data as $status => $tasks)
{
if(empty($tasks)) continue;
foreach($tasks as $task)
{
if(!empty($tasks['inprogress']) && $task->task != $tasks['inprogress'][0]->task && $task->status == 'created') $task->status = 'pending';
if($type == $task->type and $taskID == $task->task) return $task;
}
}
return $result;
}
/**
* Build operateMenu for browse view.
*
* @param object $node
* @access public
* @return string
*/
public function buildOperateMenu($node)
{
if($node->deleted) return '';
$menu = '';
$params = "id=$node->id";
$menu .= $this->buildMenu('zanode', 'edit', $params, $node, 'view');
$menu .= $this->buildMenu('zanode', 'destroy', $params, $node, 'view', 'trash', 'hiddenwin', '', false, '', ' ');
return $menu;
}
/**
* 获取宿主机中zagent、nginx、websockify、novnc的运行及安装状态.
* Get service status from host.
*
* @param object $node
* @access public
* @return array
*/
public function getServiceStatus($node)
{
$result = json_decode(commonModel::http("http://{$node->ip}:{$node->zap}/api/v1/service/check", json_encode(array('services' => 'all')), array(), array("Authorization:$node->tokenSN"), 'data', 'POST', 10));
if(empty($result->data->ztfStatus) || $result->code != 'success')
{
$result = new stdclass;
$result->data = $this->lang->zanode->init->serviceStatus;
return $result->data;
}
$result->data = array(
'ZenAgent' => 'ready',
'ZTF' => $result->data->ztfStatus,
);
return $result->data;
}
/**
* 执行节点安装应用支持ztf,zendata.
* Install service to node by name.
*
* @param object $node
* @param string $name
* @access public
* @return array
*/
public function installService($node, $name)
{
$param = array(
'name' => strtolower($name),
'secret' => $node->secret,
'server' => getWebRoot(true),
);
$result = json_decode(commonModel::http("http://{$node->ip}:{$node->zap}/api/v1/service/setup", json_encode($param), array(), array("Authorization:$node->tokenSN")));
if(empty($result->data) || $result->code != 'success')
{
$result = new stdclass;
return $this->lang->zanode->init->serviceStatus;
}
return array(
'ZenAgent' => 'ready',
'ZTF' => $result->data->ztfStatus,
);
}
/**
* Get automation by product id.
*
* @param int $productID
* @access public
* @return object
*/
public function getAutomationByProduct($productID = 0)
{
return $this->dao->select('*')->from(TABLE_AUTOMATION)
->where('product')->eq($productID)
->fetch();
}
/**
* Get automation by id.
*
* @param int $id
* @return object
*/
public function getAutomationByID($id)
{
return $this->dao->select('*')->from(TABLE_AUTOMATION)
->where('id')->eq($id)
->fetch();
}
/**
* Set automation setting.
*
* @access public
* @return int|bool
*/
public function setAutomationSetting()
{
$now = helper::now();
$data = fixer::input('post')
->remove('syncToZentao')
->setDefault('createdBy', $this->app->user->account)
->setDefault('createdDate', $now)
->setDefault('node', 0)
->remove('uid')
->get();
$this->dao->replace(TABLE_AUTOMATION)
->data($data)
->batchcheck('node,scriptPath', 'notempty')
->autoCheck()
->exec();
if(dao::isError()) return false;
return $this->dao->lastInsertID();
}
/**
* Run ZTFScript.
*
* @param int $scriptID
* @param int $caseID
* @param int $task //testtaskID
* @access public
* @return void
*/
public function runZTFScript($scriptID = 0, $caseID = 0, $task = 0)
{
$automation = $this->getAutomationByID($scriptID);
$node = $this->getNodeByID($automation->node);
if(empty($node) or $node->status != 'running' or !$node->ip or !$node->ztf or !$node->tokenSN)
{
$this->dao->delete()->from(TABLE_TESTRESULT)->where('id')->eq($task)->exec();
return dao::$errors = $this->lang->zanode->runTimeout;
}
$params = array(
'cmd' => $automation->shell,
'ids' => strval($caseID),
'path' => $automation->scriptPath,
'task' => intval($task)
);
$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));
if(empty($result) or $result->code != 0)
{
$this->dao->delete()->from(TABLE_TESTRESULT)->where('id')->eq($task)->exec();
return dao::$errors = $this->lang->zanode->runTimeout;
}
}
/**
* Sync cases in dir to zentao.
*
* @access public
* @return void
*/
public function syncCasesToZentao($path)
{
}
}