diff --git a/api/v1/entries/hostheartbeat.php b/api/v1/entries/hostheartbeat.php index b2862521f1..cec8180eca 100644 --- a/api/v1/entries/hostheartbeat.php +++ b/api/v1/entries/hostheartbeat.php @@ -78,6 +78,9 @@ class hostHeartbeatEntry extends baseEntry 'status' => $vm->status, 'extranet' => $vm->ip, ); + + if(!$vm->sshPortOnHost) unset($vmData['ssh']); + if($heartbeat > 0) $vmData['heartbeat'] = date("Y-m-d H:i:s", $heartbeat); $this->dao->update(TABLE_ZAHOST)->data($vmData)->where('mac')->eq($vm->macAddress)->exec(); diff --git a/api/v1/entries/hostsubmit.php b/api/v1/entries/hostsubmit.php index 83a2a628de..268df5fe90 100644 --- a/api/v1/entries/hostsubmit.php +++ b/api/v1/entries/hostsubmit.php @@ -23,7 +23,7 @@ class hostSubmitEntry extends baseEntry /* Check authorize. */ $header = getallheaders(); $token = isset($header['Authorization']) ? substr($header['Authorization'], 7) : ''; - // if(!$token) return $this->sendError(401, 'Unauthorized'); + if(!$token) return $this->sendError(401, 'Unauthorized'); $now = helper::now(); @@ -38,7 +38,16 @@ class hostSubmitEntry extends baseEntry ->andWhere('tokenTime')->gt($now)->fi() ->fetch('id'); - // if(!$id) return $this->sendError(400, 'Secret error.'); + if(!$id) return $this->sendError(400, 'Secret error.'); + + $imageInfo = $this->dao->select('`status`,`from`')->from(TABLE_IMAGE) + ->where('id')->eq($task) + ->fetch(); + if(empty($imageInfo)) return $this->sendSuccess(200, 'success'); + if($imageInfo->from == 'snapshot' && $imageInfo->status == 'restoring') + { + $image->status = $image->status == 'completed' ? 'restore_completed' : 'restore_failed'; + } $this->dao->update(TABLE_IMAGE)->data($image)->where("id")->eq($task)->exec(); diff --git a/config/zentaopms.php b/config/zentaopms.php index 40ec64cfce..fda56ae16c 100644 --- a/config/zentaopms.php +++ b/config/zentaopms.php @@ -227,7 +227,7 @@ $config->openMethods[] = 'doc.createbasicinfo'; $config->openMethods[] = 'project.createguide'; $config->openMethods[] = 'task.editteam'; $config->openMethods[] = 'feedback.mergeproductmodule'; -$config->openMethods[] = 'zahost.introduction'; +$config->openMethods[] = 'zanode.list'; $config->openModules = array(); $config->openModules[] = 'install'; diff --git a/db/update18.0.beta3.sql b/db/update18.0.beta3.sql index 1b89108373..84d6e98de7 100644 --- a/db/update18.0.beta3.sql +++ b/db/update18.0.beta3.sql @@ -1,3 +1,9 @@ +ALTER TABLE `zt_host` ADD `type` varchar(30) NOT NULL DEFAULT 'normal' AFTER `admin`; +ALTER TABLE `zt_host` ADD `secret` varchar(50) NOT NULL DEFAULT '' AFTER `type`; +ALTER TABLE `zt_host` ADD `token` varchar(50) NOT NULL DEFAULT '' AFTER `secret`; +ALTER TABLE `zt_host` ADD `expiredDate` datetime NOT NULL AFTER `token`; +ALTER TABLE `zt_host` ADD `virtualSoftware` varchar(30) NOT NULL DEFAULT '' AFTER `expiredDate`; + ALTER TABLE `zt_host` DROP COLUMN `cabinet`, DROP COLUMN `cpuRate`, @@ -58,7 +64,6 @@ h.`createdDate` = a.`createdDate`, h.`editedBy` = a.`editedBy`, h.`editedDate` = a.`editedDate`, h.`group` = a.`group`, -h.`type` = a.`type`, h.`deleted` = a.`deleted` WHERE h.`assetID` = a.`id`; diff --git a/db/update18.0.sql b/db/update18.0.sql index 657f5f6ea4..de217f9835 100644 --- a/db/update18.0.sql +++ b/db/update18.0.sql @@ -1 +1,4 @@ +ALTER TABLE `zt_image` ADD `localName` VARCHAR(64) NOT NULL AFTER `name`; +ALTER TABLE `zt_image` ADD `restoreDate` datetime NOT NULL AFTER `createdDate`; + ALTER TABLE `zt_ticket` MODIFY `resolution` text NOT NULL; diff --git a/db/zentao.sql b/db/zentao.sql index ec318bc8b5..58cda08c37 100755 --- a/db/zentao.sql +++ b/db/zentao.sql @@ -7359,6 +7359,7 @@ CREATE TABLE `zt_image` ( `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, `host` int(11) unsigned NOT NULL DEFAULT 0, `name` varchar(64) NOT NULL DEFAULT '', + `localName` varchar(64) NOT NULL DEFAULT '', `address` varchar(64) NOT NULL DEFAULT '', `path` varchar(64) NOT NULL DEFAULT '', `status` varchar(20) NOT NULL DEFAULT '', diff --git a/module/action/config.php b/module/action/config.php index aae2118c64..77eaec4b92 100755 --- a/module/action/config.php +++ b/module/action/config.php @@ -49,6 +49,7 @@ $config->action->objectNameFields['stage'] = 'name'; $config->action->objectNameFields['apistruct'] = 'name'; $config->action->objectNameFields['repo'] = 'name'; $config->action->objectNameFields['dataview'] = 'name'; +$config->action->objectNameFields['zahost'] = 'name'; $config->action->objectNameFields['zanode'] = 'name'; $config->action->commonImgSize = 870; diff --git a/module/action/lang/de.php b/module/action/lang/de.php index 3d5e96a9cf..e29603c2f1 100644 --- a/module/action/lang/de.php +++ b/module/action/lang/de.php @@ -237,6 +237,10 @@ $lang->action->desc->relieved = '$date, relieved by $actoraction->desc->switchtolight = '$date, Switch from ALM mode to light mode by '. $lang->admin->system .'.' . "\n"; $lang->action->desc->getvnc = '$date, Remote control $extra by $actor .' . "\n"; $lang->action->desc->unlinkproduct = '$date, the project is disassociated from the $extra, synchronization disassociates the ' . $lang->executionCommon . 's of the project from the $extra.' . "\n"; +$lang->action->desc->createdsnapshot = '$date, $actor creates a snapshot $extra。' . "\n"; +$lang->action->desc->restoredsnapshot = '$date, $actor restored a snapshot $extra。' . "\n"; +$lang->action->desc->editsnapshot = '$date, $actor edited the snapshot $extra。' . "\n"; +$lang->action->desc->deletesnapshot = '$date, $actor deleted snapshot $extra。' . "\n"; /* Used to describe the history of operations related to parent-child tasks. */ $lang->action->desc->createchildren = '$date, $actor created a child task $extra。' . "\n"; @@ -419,6 +423,10 @@ $lang->action->label->switchtolight = 'switch from ALM mode to light mod $lang->action->label->linkedrepo = 'Linked Code Repo'; $lang->action->label->unlinkedrepo = 'Unlinked Code Repo'; $lang->action->label->unlinkproduct = 'Unlinked Product'; +$lang->action->label->createdsnapshot = 'create snapshot'; +$lang->action->label->restoredsnapshot = 'create snapshot'; +$lang->action->label->editsnapshot = 'edit snapshot'; +$lang->action->label->deletesnapshot = 'deleted snapshot'; /* Dynamic information is grouped by object. */ $lang->action->dynamicAction = new stdclass; diff --git a/module/action/lang/en.php b/module/action/lang/en.php index 64e5e9169f..64223a9966 100755 --- a/module/action/lang/en.php +++ b/module/action/lang/en.php @@ -237,6 +237,10 @@ $lang->action->desc->relieved = '$date, relieved by $actoraction->desc->switchtolight = '$date, Switch from ALM mode to light mode by '. $lang->admin->system .'.' . "\n"; $lang->action->desc->getvnc = '$date, Remote control $extra by $actor .' . "\n"; $lang->action->desc->unlinkproduct = '$date, the project is disassociated from the $extra, synchronization disassociates the ' . $lang->executionCommon . 's of the project from the $extra.' . "\n"; +$lang->action->desc->createdsnapshot = '$date, $actor creates a snapshot $extra。' . "\n"; +$lang->action->desc->restoredsnapshot = '$date, $actor restored a snapshot $extra。' . "\n"; +$lang->action->desc->editsnapshot = '$date, $actor edited the snapshot $extra。' . "\n"; +$lang->action->desc->deletesnapshot = '$date, $actor deleted snapshot $extra。' . "\n"; /* Used to describe the history of operations related to parent-child tasks. */ $lang->action->desc->createchildren = '$date, $actor created a child task $extra。' . "\n"; @@ -419,6 +423,10 @@ $lang->action->label->switchtolight = 'switch from ALM mode to light mod $lang->action->label->linkedrepo = 'Linked Code Repo'; $lang->action->label->unlinkedrepo = 'Unlinked Code Repo'; $lang->action->label->unlinkproduct = 'Unlinked Product'; +$lang->action->label->createdsnapshot = 'create snapshot'; +$lang->action->label->restoredsnapshot = 'create snapshot'; +$lang->action->label->editsnapshot = 'edit snapshot'; +$lang->action->label->deletesnapshot = 'deleted snapshot'; /* Dynamic information is grouped by object. */ $lang->action->dynamicAction = new stdclass; diff --git a/module/action/lang/fr.php b/module/action/lang/fr.php index 8327ed0c0d..b35dfacfe1 100644 --- a/module/action/lang/fr.php +++ b/module/action/lang/fr.php @@ -237,6 +237,10 @@ $lang->action->desc->relieved = '$date, relieved by $actoraction->desc->switchtolight = '$date, Switch from ALM mode to light mode by '. $lang->admin->system .'.' . "\n"; $lang->action->desc->getvnc = '$date, Remote control $extra by $actor .' . "\n"; $lang->action->desc->unlinkproduct = '$date, the project is disassociated from the $extra, synchronization disassociates the ' . $lang->executionCommon . 's of the project from the $extra.' . "\n"; +$lang->action->desc->createdsnapshot = '$date, $actor creates a snapshot $extra。' . "\n"; +$lang->action->desc->restoredsnapshot = '$date, $actor restored a snapshot $extra。' . "\n"; +$lang->action->desc->editsnapshot = '$date, $actor edited the snapshot $extra。' . "\n"; +$lang->action->desc->deletesnapshot = '$date, $actor deleted snapshot $extra。' . "\n"; /* Used to describe the history of operations related to parent-child tasks. */ $lang->action->desc->createchildren = '$date, $actor a créé un sous-tâche $extra。' . "\n"; @@ -419,6 +423,10 @@ $lang->action->label->switchtolight = 'switch from ALM mode to light mod $lang->action->label->linkedrepo = 'Linked Code Repo'; $lang->action->label->unlinkedrepo = 'Unlinked Code Repo'; $lang->action->label->unlinkproduct = 'Unlinked Product'; +$lang->action->label->createdsnapshot = 'create snapshot'; +$lang->action->label->restoredsnapshot = 'create snapshot'; +$lang->action->label->editsnapshot = 'edit snapshot'; +$lang->action->label->deletesnapshot = 'deleted snapshot'; /* Dynamic information is grouped by object. */ $lang->action->dynamicAction = new stdclass(); diff --git a/module/action/lang/zh-cn.php b/module/action/lang/zh-cn.php index a68dbd1444..ede1fbb1ee 100755 --- a/module/action/lang/zh-cn.php +++ b/module/action/lang/zh-cn.php @@ -237,6 +237,10 @@ $lang->action->desc->relieved = '$date, 由 $actor $lang->action->desc->switchtolight = '$date, 由于 '. $lang->admin->system .' 从全生命周期管理模式切换为轻量管理模式,项目访问控制由项目集内公开调整为私有。' . "\n"; $lang->action->desc->getvnc = '$date, $actor对执行节点 $extra 进行了远程操控。' . "\n"; $lang->action->desc->unlinkproduct = '$date, 系统判断由于' . $lang->executionCommon . '所属项目与$extra取消关联,同步将' . $lang->executionCommon . '与$extra取消关联。' . "\n"; +$lang->action->desc->createdsnapshot = '$date, $actor 创建了快照 $extra。' . "\n"; +$lang->action->desc->restoredsnapshot = '$date, $actor 还原了快照 $extra。' . "\n"; +$lang->action->desc->editsnapshot = '$date, $actor 编辑了快照 $extra。' . "\n"; +$lang->action->desc->deletesnapshot = '$date, $actor 删除了快照 $extra。' . "\n"; /* 用来描述和父子任务相关的操作历史记录。*/ $lang->action->desc->createchildren = '$date, 由 $actor 创建子任务 $extra。' . "\n"; @@ -419,6 +423,10 @@ $lang->action->label->switchtolight = '从全生命周期管理模式切 $lang->action->label->linkedrepo = '关联代码库到'; $lang->action->label->unlinkedrepo = '取消了项目与代码库的关联'; $lang->action->label->unlinkproduct = '取消了与产品的关联'; +$lang->action->label->createdsnapshot = '创建了快照'; +$lang->action->label->restoredsnapshot = '还原了快照'; +$lang->action->label->editsnapshot = '编辑了快照'; +$lang->action->label->deletesnapshot = '编辑了快照'; /* 动态信息按照对象分组 */ $lang->action->dynamicAction = new stdclass(); diff --git a/module/group/lang/resource.php b/module/group/lang/resource.php index 37b350c0d9..e58bbc2d7e 100644 --- a/module/group/lang/resource.php +++ b/module/group/lang/resource.php @@ -1239,18 +1239,23 @@ $lang->zahost->methodOrder[30] = 'downloadImage'; $lang->zahost->methodOrder[35] = 'cancelDownload'; $lang->resource->zanode = new stdclass(); -$lang->resource->zanode->browse = 'browse'; -$lang->resource->zanode->create = 'create'; -$lang->resource->zanode->edit = 'edit'; -$lang->resource->zanode->destroy = 'destroy'; -$lang->resource->zanode->reboot = 'reboot'; -$lang->resource->zanode->suspend = 'suspend'; -$lang->resource->zanode->resume = 'resume'; -$lang->resource->zanode->getVNC = 'getVNC'; -$lang->resource->zanode->start = 'boot'; -$lang->resource->zanode->close = 'shutdown'; -$lang->resource->zanode->view = 'view'; -$lang->resource->zanode->createImage = 'createImage'; +$lang->resource->zanode->browse = 'browse'; +$lang->resource->zanode->create = 'create'; +$lang->resource->zanode->edit = 'edit'; +$lang->resource->zanode->destroy = 'destroy'; +$lang->resource->zanode->reboot = 'reboot'; +$lang->resource->zanode->suspend = 'suspend'; +$lang->resource->zanode->resume = 'resume'; +$lang->resource->zanode->getVNC = 'getVNC'; +$lang->resource->zanode->start = 'boot'; +$lang->resource->zanode->close = 'shutdown'; +$lang->resource->zanode->view = 'view'; +$lang->resource->zanode->createImage = 'createImage'; +$lang->resource->zanode->browseSnapshot = 'browseSnapshot'; +$lang->resource->zanode->createSnapshot = 'createSnapshot'; +$lang->resource->zanode->editSnapshot = 'editSnapshot'; +$lang->resource->zanode->restoreSnapshot = 'restoreSnapshot'; +$lang->resource->zanode->deleteSnapshot = 'deleteSnapshot'; $lang->zanode->methodOrder[0] = 'browse'; $lang->zanode->methodOrder[5] = 'create'; @@ -1264,6 +1269,11 @@ $lang->zanode->methodOrder[40] = 'start'; $lang->zanode->methodOrder[45] = 'close'; $lang->zanode->methodOrder[50] = 'view'; $lang->zanode->methodOrder[55] = 'createImage'; +$lang->zanode->methodOrder[60] = 'browseSnapshot'; +$lang->zanode->methodOrder[65] = 'createSnapshot'; +$lang->zanode->methodOrder[70] = 'editSnapshot'; +$lang->zanode->methodOrder[75] = 'restoreSnapshot'; +$lang->zanode->methodOrder[80] = 'deleteSnapshot'; $lang->resource->repo = new stdclass(); $lang->resource->repo->browse = 'browseAction'; diff --git a/module/zahost/control.php b/module/zahost/control.php index e201c83b2d..e7787eb289 100644 --- a/module/zahost/control.php +++ b/module/zahost/control.php @@ -43,8 +43,13 @@ class zahost extends control $this->loadModel('search')->setSearchParams($this->config->zahost->search); $showFeature = false; - $skipAutomation = !empty($this->config->global->skipAutomation) ? $this->config->global->skipAutomation : ''; - if(strpos(",$skipAutomation,", $this->app->user->account) === false) $showFeature = true; + $accounts = !empty($this->config->global->skipAutomation) ? $this->config->global->skipAutomation : ''; + if(strpos(",$accounts,", $this->app->user->account) === false) + { + $showFeature = true; + $accounts .= ',' . $this->app->user->account; + $this->loadModel('setting')->setItem('system.common.global.skipAutomation', $accounts); + } $this->view->title = $this->lang->zahost->common; $this->view->hostList = $hostList; @@ -164,7 +169,7 @@ class zahost extends control } $this->dao->update(TABLE_ZAHOST)->set('deleted')->eq(1)->where('id')->eq($hostID)->exec(); - $this->loadModel('action')->create('zahost', $hostID, 'deleted'); + $this->loadModel('action')->create('zahost', $hostID, 'deleted', '', $extra = ACTIONMODEL::CAN_UNDELETED); /* if ajax request, send result. */ if($this->server->ajax) diff --git a/module/zahost/css/view.css b/module/zahost/css/view.css index a01465a8bb..5d092fc6e2 100644 --- a/module/zahost/css/view.css +++ b/module/zahost/css/view.css @@ -20,3 +20,4 @@ .btn-info {background-color: unset;} .icon-refresh {font-size: 15px;} .load-indicator:after {top: 33%;} +.btn.disabled i {color: #fff;} diff --git a/module/zahost/js/view.js b/module/zahost/js/view.js index 4df1246224..2b07966f7c 100644 --- a/module/zahost/js/view.js +++ b/module/zahost/js/view.js @@ -76,7 +76,6 @@ function setIframeHeight(iframe) { var height = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight iframe.height = height+10; - console.log(1111, iframe.height) } } } \ No newline at end of file diff --git a/module/zahost/model.php b/module/zahost/model.php index b7f37b3e74..96c39aabfe 100644 --- a/module/zahost/model.php +++ b/module/zahost/model.php @@ -85,7 +85,10 @@ class zahostModel extends model $this->dao->update(TABLE_ZAHOST)->data($hostInfo) ->batchCheck($this->config->zahost->create->requiredFields, 'notempty') - ->batchCheck('diskSize,memory', 'float'); + ->batchCheck('diskSize,memory', 'float') + ->check('name', 'unique', "id != $hostID and type in ('vhost', 'zahost')") + ->autoCheck(); + if(dao::isError()) return false; $ping = $this->checkAddress($hostInfo->extranet); diff --git a/module/zahost/view/view.html.php b/module/zahost/view/view.html.php index ab54e22f53..012ba10e13 100644 --- a/module/zahost/view/view.html.php +++ b/module/zahost/view/view.html.php @@ -131,43 +131,25 @@
zahost->desc;?>
desc) ? htmlspecialchars_decode($zahost->desc) : $lang->noData;?>
- + + +
zahost->browseNode;?>
+
createLink('zanode', 'list', "hostID=$zahost->id", '', true) . "' frameborder='no' allowfullscreen='true' mozallowfullscreen='true' webkitallowfullscreen='true' allowtransparency='true' scrolling='auto' onload='setIframeHeight(this)' style='min-height:300px;'>";?> -
+ +
noData;?>
+
- createLink('action', 'comment', "objectType=zahost&objectID=$zahost->hostID"); ?>
+ printExtendFields($zahost, 'div', "position=left&inForm=0&inCell=1");?>
diff --git a/module/zanode/control.php b/module/zanode/control.php index 54de57f57e..ef0141ded4 100644 --- a/module/zanode/control.php +++ b/module/zanode/control.php @@ -44,8 +44,13 @@ class zanode extends control $this->loadModel('search')->setSearchParams($this->config->zanode->search); $showFeature = false; - $skipAutomation = !empty($this->config->global->skipAutomation) ? $this->config->global->skipAutomation : ''; - if(strpos(",$skipAutomation,", $this->app->user->account) === false) $showFeature = true; + $accounts = !empty($this->config->global->skipAutomation) ? $this->config->global->skipAutomation : ''; + if(strpos(",$accounts,", $this->app->user->account) === false) + { + $showFeature = true; + $accounts .= ',' . $this->app->user->account; + $this->loadModel('setting')->setItem('system.common.global.skipAutomation', $accounts); + } $this->view->title = $this->lang->zanode->common; $this->view->users = $this->loadModel('user')->getPairs('noletter|nodeleted'); @@ -73,6 +78,10 @@ class zanode extends control */ public function list($hostID, $orderBy = 'id_desc') { + if(!commonModel::hasPriv('zanode', 'browse')) + { + $this->loadModel('common')->deny('zanode', 'browse'); + } $this->view->title = $this->lang->zanode->common; $this->view->nodeList = $this->loadModel("zanode")->getListByHost($hostID, $orderBy); $this->view->orderBy = $orderBy; @@ -155,6 +164,7 @@ class zanode extends control $this->view->token = !empty($vnc->token) ? $vnc->token:''; $this->view->title = $this->lang->zanode->view; $this->view->zanode = $node; + $this->view->snapshotList = $this->zanode->getSnapshotList($id); $this->view->actions = $this->loadModel('action')->getList('zanode', $id); $this->view->users = $this->loadModel('user')->getPairs('noletter'); $this->display(); @@ -276,6 +286,90 @@ class zanode extends control $this->display(); } + /** + * Create snapshot. + * + * @param int $zanodeID + * @access public + * @return void + */ + public function createSnapshot($nodeID = 0) + { + $node = $this->zanode->getNodeByID($nodeID); + + if($_POST) + { + $this->zanode->createSnapshot($nodeID); + + if(dao::isError()) + { + $response['result'] = 'fail'; + $response['message'] = dao::getError(); + return $this->send($response); + } + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent')); + } + + $this->view->node = $node; + $this->display(); + } + + /** + * Edit Snapshot. + * + * @param int $snapshotID + * @access public + * @return void + */ + public function editSnapshot($snapshotID) + { + $snapshot = $this->zanode->getImageByID($snapshotID); + if($_POST) + { + $this->zanode->editSnapshot($snapshotID); + + if(dao::isError()) + { + $response['result'] = 'fail'; + $response['message'] = dao::getError(); + return $this->send($response); + } + $this->loadModel('action')->create('zanode', $snapshot->host, 'editSnapshot', '', $snapshot->localName ? $snapshot->localName : $snapshot->name); + return $this->send(array('result' => 'success', 'message' => $this->lang->saveSuccess, 'locate' => 'parent')); + } + + $this->view->snapshot = $snapshot; + $this->display(); + } + + /** + * Delete Snapshot. + * + * @param int $snapshotID + * @param string $confirm + * @access public + * @return void + */ + public function deleteSnapshot($snapshotID, $confirm = 'no') + { + if($confirm == 'no') + { + return print(js::confirm($this->lang->zanode->confirmDeleteSnapshot, inlink('deleteSnapshot', "snapshotID={$snapshotID}&confirm=yes"))); + } + + $result = $this->zanode->deleteSnapshot($snapshotID); + + if($result !== true) + { + return print(js::alert($result)); + } + else + { + if(isonlybody()) return print(js::alert($this->lang->zanode->actionSuccess) . js::reload('parent.parent')); + return print(js::alert($this->lang->zanode->actionSuccess) . js::locate($this->createLink('zanode', 'browse'), 'parent')); + } + } + /** * Desctroy node. * @@ -324,6 +418,66 @@ class zanode extends control $this->display(); } + /** + * Browse snapshot. + * + * @param int $nodeID + * @param string $browseType + * @param int $param + * @param string $orderBy + * @param int $recTotal + * @param int $recPerPage + * @param int $pageID + * @access public + * @return void + */ + public function browseSnapshot($nodeID, $browseType = 'all', $param = 0, $orderBy = 'id', $recTotal = 0, $recPerPage = 20, $pageID = 1) + { + $this->app->loadLang('zahost'); + $this->app->loadClass('pager', $static = true); + $pager = pager::init($recTotal, $recPerPage, $pageID); + + $snapshotList = $this->zanode->getSnapshotList($nodeID, $browseType, $param, $orderBy, $pager); + + $this->view->title = $this->lang->zanode->browseSnapshot; + $this->view->nodeID = $nodeID; + $this->view->node = $this->zanode->getNodeByID($nodeID); + $this->view->snapshotList = $snapshotList; + $this->view->users = $this->loadModel('user')->getPairs('noletter'); + $this->view->pager = $pager; + $this->view->param = $param; + $this->view->orderBy = $orderBy; + $this->view->browseType = $browseType; + + $this->display(); + } + + /** + * Restore node. + * + * @param int $nodeID + * @return void + */ + public function restoreSnapshot($nodeID, $snapshotID, $confirm = 'no') + { + if($confirm == 'no') + { + return print(js::confirm($this->lang->zanode->confirmRestore, inlink('restoreSnapshot', "nodeID={$nodeID}&snapshotID={$snapshotID}&confirm=yes"))); + } + + $this->zanode->restoreSnapshot($nodeID, $snapshotID); + + if(dao::isError()) + { + return print(js::alert(dao::getError()) . js::reload('parent')); + } + else + { + if(isonlybody()) return print(js::alert($this->lang->zanode->actionSuccess) . js::reload('parent.parent')); + return print(js::alert($this->lang->zanode->actionSuccess) . js::locate($this->createLink('zanode', 'browse'), 'parent')); + } + } + /** * Ajax get template pairs by api. * diff --git a/module/zanode/css/browsesnapshot.css b/module/zanode/css/browsesnapshot.css new file mode 100644 index 0000000000..d5033bef8f --- /dev/null +++ b/module/zanode/css/browsesnapshot.css @@ -0,0 +1,6 @@ +#main{min-width: 0!important;padding:0!important;} +body{background-color: #fff!important;} + +.c-createdBy {width: 100px;} +.c-datetime {width: 130px;} +.c-status {width: 120px;} diff --git a/module/zanode/css/view.css b/module/zanode/css/view.css index ae56845864..e89553f2ce 100644 --- a/module/zanode/css/view.css +++ b/module/zanode/css/view.css @@ -16,3 +16,4 @@ .checkStatus{color: #313c52;} .node-not-wrap{white-space: nowrap;} .btn-info {background-color: unset;} +.btn.disabled i {color: #fff;} diff --git a/module/zanode/js/browsesnapshot.js b/module/zanode/js/browsesnapshot.js new file mode 100644 index 0000000000..a09811ad70 --- /dev/null +++ b/module/zanode/js/browsesnapshot.js @@ -0,0 +1,7 @@ +$(function() +{ + var myPager = $('.pager').data('zui.pager'); + + myPager.options.menuDirection = 'dropdown'; + myPager.set(); +}); diff --git a/module/zanode/js/view.js b/module/zanode/js/view.js index c2a562506b..4ffb70440e 100644 --- a/module/zanode/js/view.js +++ b/module/zanode/js/view.js @@ -160,6 +160,14 @@ $('.btn-pwd-copy').live('click', function() }, 2000) }) +$('#jumpManual').click(function() +{ + var encodedHelpPageUrl = encodeURIComponent('https://www.zentao.net/book/zentaopms/974.html?fullScreen=zentao'); + var urlForNewTab = window.location.origin + '#app=help&url=' + encodedHelpPageUrl; + window.open(urlForNewTab) +}) + + $(function(){ checkServiceStatus(); checkInterval = setInterval(() => { @@ -171,3 +179,15 @@ $(function(){ checkServiceStatus(); }, 2000); }) + +/** + * Edit Snapshot. + * + * @param string $link + * @access public + * @return void + */ +function editSnapshot(link) +{ + $('#editSnapshot').attr('href', link).click(); +} diff --git a/module/zanode/lang/de.php b/module/zanode/lang/de.php index f32d767fbc..35fb26c602 100644 --- a/module/zanode/lang/de.php +++ b/module/zanode/lang/de.php @@ -79,6 +79,22 @@ $lang->zanode->createImageSuccess = 'Successed, You can use this image to create $lang->zanode->createImageFail = 'Failed to create'; $lang->zanode->createImageButton = 'Create image'; +$lang->zanode->snapshotName = 'Snapshot Name'; +$lang->zanode->browseSnapshot = 'Snapshot List'; +$lang->zanode->createSnapshot = 'Create Snapshot'; +$lang->zanode->editSnapshot = 'Edit Snapshot'; +$lang->zanode->restoreSnapshot = 'Revert Snapshot'; +$lang->zanode->deleteSnapshot = 'Delete Snapshot'; +$lang->zanode->snapshotEmpty = 'No snapshots'; +$lang->zanode->confirmDeleteSnapshot = "The snapshot cannot be restored from the recycle bin after being deleted. Are you sure to continue?"; + +$lang->zanode->snapshot->statusList['creating'] = 'Creating'; +$lang->zanode->snapshot->statusList['completed'] = 'Create Completed'; +$lang->zanode->snapshot->statusList['failed'] = 'Create Failed'; +$lang->zanode->snapshot->statusList['restoring'] = 'Restoring'; +$lang->zanode->snapshot->statusList['restore_failed'] = 'Restore Failed'; +$lang->zanode->snapshot->statusList['restore_completed'] = 'Restore Completed'; + $lang->zanode->imageNameEmpty = 'Name can not be empty.'; $lang->zanode->runTimeout = 'Network connection timeout, please check the host and execution node status.'; @@ -101,6 +117,7 @@ $lang->zanode->statusList['shutodown'] = 'shutdown'; $lang->zanode->statusList['destroy_fail'] = 'Destroy Fail'; $lang->zanode->statusList['wait'] = 'Initializing'; $lang->zanode->statusList['online'] = 'Online'; +$lang->zanode->statusList['restoring'] = 'Restoring'; $lang->zanode->initNotice = "Succeeded. Please initialize the execution node or return to the list."; $lang->zanode->initButton = "Initialize"; diff --git a/module/zanode/lang/en.php b/module/zanode/lang/en.php index f32d767fbc..5c671c31a1 100644 --- a/module/zanode/lang/en.php +++ b/module/zanode/lang/en.php @@ -42,12 +42,14 @@ $lang->zanode->vnc = 'VNC Port'; $lang->zanode->destroyAt = 'Destroy Time'; $lang->zanode->creater = 'Creator'; $lang->zanode->createdDate = 'Create Date'; -$lang->zanode->confirmDelete = "Are you sure about destroying the ZenAgent Node?"; -$lang->zanode->confirmBoot = "Are you sure to start the ZenAgent Node?"; -$lang->zanode->confirmReboot = "Are you sure to restart the ZenAgent Node?"; -$lang->zanode->confirmShutdown = "Are you sure to shutdown the ZenAgent Node?"; -$lang->zanode->confirmSuspend = "Are you sure to suspend the ZenAgent Node?"; -$lang->zanode->confirmResume = "Are you sure to resume the ZenAgent Node?"; +$lang->zanode->confirmDelete = "Are you sure about destroying the ZenAgent Node?"; +$lang->zanode->confirmBoot = "Are you sure to start the ZenAgent Node?"; +$lang->zanode->confirmReboot = "Are you sure to restart the ZenAgent Node?"; +$lang->zanode->confirmShutdown = "Are you sure to shutdown the ZenAgent Node?"; +$lang->zanode->confirmSuspend = "Are you sure to suspend the ZenAgent Node?"; +$lang->zanode->confirmResume = "Are you sure to resume the ZenAgent Node?"; +$lang->zanode->confirmRestore = "Are you sure to restore the ZenAgent Node?"; +$lang->zanode->confirmRestore = "The ZenAgent Node will be restored to this snapshot state, are you sure you want to continue?"; $lang->zanode->actionSuccess = 'Success'; $lang->zanode->deleted = "Deleted"; $lang->zanode->scriptPath = "Script path"; @@ -79,7 +81,25 @@ $lang->zanode->createImageSuccess = 'Successed, You can use this image to create $lang->zanode->createImageFail = 'Failed to create'; $lang->zanode->createImageButton = 'Create image'; -$lang->zanode->imageNameEmpty = 'Name can not be empty.'; +$lang->zanode->snapshotName = 'Snapshot Name'; +$lang->zanode->browseSnapshot = 'Snapshot List'; +$lang->zanode->createSnapshot = 'Create Snapshot'; +$lang->zanode->editSnapshot = 'Edit Snapshot'; +$lang->zanode->restoreSnapshot = 'Revert Snapshot'; +$lang->zanode->deleteSnapshot = 'Delete Snapshot'; +$lang->zanode->snapshotEmpty = 'No snapshots'; +$lang->zanode->confirmDeleteSnapshot = "The snapshot cannot be restored from the recycle bin after being deleted. Are you sure to continue?"; + +$lang->zanode->snapshot->statusList['creating'] = 'Creating'; +$lang->zanode->snapshot->statusList['completed'] = 'Create Completed'; +$lang->zanode->snapshot->statusList['failed'] = 'Create Failed'; +$lang->zanode->snapshot->statusList['restoring'] = 'Restoring'; +$lang->zanode->snapshot->statusList['restore_failed'] = 'Restore Failed'; +$lang->zanode->snapshot->statusList['restore_completed'] = 'Restore Completed'; + +$lang->zanode->imageNameEmpty = 'Name can not be empty.'; +$lang->zanode->snapStatusError = 'Snapshot is not ready.'; +$lang->zanode->snapRestoring = 'Snapshot is restoring.'; $lang->zanode->runTimeout = 'Network connection timeout, please check the host and execution node status.'; @@ -101,6 +121,7 @@ $lang->zanode->statusList['shutodown'] = 'shutdown'; $lang->zanode->statusList['destroy_fail'] = 'Destroy Fail'; $lang->zanode->statusList['wait'] = 'Initializing'; $lang->zanode->statusList['online'] = 'Online'; +$lang->zanode->statusList['restoring'] = 'Restoring'; $lang->zanode->initNotice = "Succeeded. Please initialize the execution node or return to the list."; $lang->zanode->initButton = "Initialize"; diff --git a/module/zanode/lang/fr.php b/module/zanode/lang/fr.php index f32d767fbc..35fb26c602 100644 --- a/module/zanode/lang/fr.php +++ b/module/zanode/lang/fr.php @@ -79,6 +79,22 @@ $lang->zanode->createImageSuccess = 'Successed, You can use this image to create $lang->zanode->createImageFail = 'Failed to create'; $lang->zanode->createImageButton = 'Create image'; +$lang->zanode->snapshotName = 'Snapshot Name'; +$lang->zanode->browseSnapshot = 'Snapshot List'; +$lang->zanode->createSnapshot = 'Create Snapshot'; +$lang->zanode->editSnapshot = 'Edit Snapshot'; +$lang->zanode->restoreSnapshot = 'Revert Snapshot'; +$lang->zanode->deleteSnapshot = 'Delete Snapshot'; +$lang->zanode->snapshotEmpty = 'No snapshots'; +$lang->zanode->confirmDeleteSnapshot = "The snapshot cannot be restored from the recycle bin after being deleted. Are you sure to continue?"; + +$lang->zanode->snapshot->statusList['creating'] = 'Creating'; +$lang->zanode->snapshot->statusList['completed'] = 'Create Completed'; +$lang->zanode->snapshot->statusList['failed'] = 'Create Failed'; +$lang->zanode->snapshot->statusList['restoring'] = 'Restoring'; +$lang->zanode->snapshot->statusList['restore_failed'] = 'Restore Failed'; +$lang->zanode->snapshot->statusList['restore_completed'] = 'Restore Completed'; + $lang->zanode->imageNameEmpty = 'Name can not be empty.'; $lang->zanode->runTimeout = 'Network connection timeout, please check the host and execution node status.'; @@ -101,6 +117,7 @@ $lang->zanode->statusList['shutodown'] = 'shutdown'; $lang->zanode->statusList['destroy_fail'] = 'Destroy Fail'; $lang->zanode->statusList['wait'] = 'Initializing'; $lang->zanode->statusList['online'] = 'Online'; +$lang->zanode->statusList['restoring'] = 'Restoring'; $lang->zanode->initNotice = "Succeeded. Please initialize the execution node or return to the list."; $lang->zanode->initButton = "Initialize"; diff --git a/module/zanode/lang/zh-cn.php b/module/zanode/lang/zh-cn.php index 30bb81be88..a000f90a49 100644 --- a/module/zanode/lang/zh-cn.php +++ b/module/zanode/lang/zh-cn.php @@ -48,6 +48,7 @@ $lang->zanode->confirmReboot = "您确定重启执行节点吗?"; $lang->zanode->confirmShutdown = "您确定关闭执行节点吗?"; $lang->zanode->confirmSuspend = "您确定休眠执行节点吗?"; $lang->zanode->confirmResume = "您确定恢复执行节点吗?"; +$lang->zanode->confirmRestore = "执行节点将会还原至此快照状态,您确定要继续么?"; $lang->zanode->actionSuccess = '操作成功'; $lang->zanode->deleted = "已删除"; $lang->zanode->scriptPath = "脚本目录"; @@ -79,7 +80,26 @@ $lang->zanode->createImageSuccess = '镜像导出成功,您可以使用此镜 $lang->zanode->createImageFail = '镜像导出失败'; $lang->zanode->createImageButton = '去创建'; +$lang->zanode->snapshotName = '快照名称'; +$lang->zanode->browseSnapshot = '快照列表'; +$lang->zanode->createSnapshot = '创建快照'; +$lang->zanode->editSnapshot = '编辑快照'; +$lang->zanode->restoreSnapshot = '还原快照'; +$lang->zanode->deleteSnapshot = '删除快照'; +$lang->zanode->snapshotEmpty = '无快照'; +$lang->zanode->confirmDeleteSnapshot = "快照被删除后无法从回收站中还原,您确定继续么?"; + +$lang->zanode->snapshot->statusList['creating'] = '创建中'; +$lang->zanode->snapshot->statusList['completed'] = '创建完成'; +$lang->zanode->snapshot->statusList['failed'] = '创建失败'; +$lang->zanode->snapshot->statusList['restoring'] = '还原中'; +$lang->zanode->snapshot->statusList['restore_failed'] = '还原失败'; +$lang->zanode->snapshot->statusList['restore_completed'] = '还原成功'; + $lang->zanode->imageNameEmpty = '名称不能为空'; +$lang->zanode->imageNameEmpty = '名称不能为空'; +$lang->zanode->snapStatusError = '快照不可用'; +$lang->zanode->snapRestoring = '快照正在还原中'; $lang->zanode->runTimeout = '自动执行失败,请检查宿主机和执行节点状态'; @@ -101,6 +121,7 @@ $lang->zanode->statusList['shutodown'] = '已关机'; $lang->zanode->statusList['destroy_fail'] = '销毁失败'; $lang->zanode->statusList['wait'] = '初始化中'; $lang->zanode->statusList['online'] = '已上架'; +$lang->zanode->statusList['restoring'] = '还原中'; $lang->zanode->initNotice = "保存成功,请初始化执行节点或返回列表。"; $lang->zanode->initButton = "去初始化"; diff --git a/module/zanode/model.php b/module/zanode/model.php index af034bfdfa..5b7c426a5c 100644 --- a/module/zanode/model.php +++ b/module/zanode/model.php @@ -28,6 +28,10 @@ class zanodemodel extends model 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; * @@ -167,6 +171,173 @@ class zanodemodel extends model return false; } + /** + * Create Snapshot by zanode. + * + * @param int $zanodeID + * @access public + * @return void + */ + 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; + + $node = $this->getNodeByID($zanodeID); + + $newSnapshot = new stdClass(); + $newSnapshot->host = $node->id; + $newSnapshot->name = $data->name; + $newSnapshot->desc = $data->desc; + $newSnapshot->status = 'creating'; + $newSnapshot->osName = $node->osName; + $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"))); + + if(!empty($result) and $result->code == 'success') + { + $this->loadModel('action')->create('zanode', $zanodeID, 'createdSnapshot', '', $data->name); + 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; + + $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 + * @access public + * @return void + */ + 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"))); + + if(!empty($result) and $result->code == 'success') + { + $this->dao->update(TABLE_HOST)->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 void + */ + public function deleteSnapshot($snapshotID) + { + $snapshot = $this->getImageByID($snapshotID); + $node = $this->getNodeByID($snapshot->host); + + if($node) + { + $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")); + $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. * @@ -212,7 +383,7 @@ class zanodemodel extends model public function destroy($id) { $node = $this->getNodeByID($id); - + if($node) { $req = array( 'name' => $node->name ); @@ -520,6 +691,27 @@ class zanodemodel extends model ->fetch(); } + public function getSnapshotList($nodeID, $browseType = 'all', $param = 0, $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)) > 1800) + { + $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. * diff --git a/module/zanode/view/browse.html.php b/module/zanode/view/browse.html.php index 09bcad7424..8a524ac73f 100644 --- a/module/zanode/view/browse.html.php +++ b/module/zanode/view/browse.html.php @@ -85,6 +85,9 @@ $rebootAttr = "title='{$lang->zanode->reboot}' target='hiddenwin'"; $rebootAttr .= $node->status == 'shutoff' ? ' class="btn disabled"' : "class='btn' target='hiddenwin' onclick='if(confirm(\"{$lang->zanode->confirmReboot}\")==false) return false;'"; + $snapshotAttr = "title='{$lang->zanode->createSnapshot}'"; + $snapshotAttr .= $node->status != 'running' ? ' class="btn disabled"' : ' class="btn iframe"'; + common::printLink('zanode', 'getVNC', "id={$node->id}", " ", (in_array($node->status ,array('running', 'launch', 'wait')) ? '_blank' : ''), "title='{$lang->zanode->getVNC}' class='btn desktop " . (in_array($node->status ,array('running', 'launch', 'wait')) ? '':'disabled') . "'", ''); if($node->status == "suspend") { @@ -105,13 +108,14 @@ } common::printLink('zanode', 'reboot', "zanodeID={$node->id}", " ", '', $rebootAttr); - common::printIcon('zanode', 'edit', "id={$node->id}", $node, 'list'); + common::printLink('zanode', 'createSnapshot', "zanodeID={$node->id}", " ", '', $snapshotAttr, true, true); if(common::hasPriv('zanode', 'createImage') or common::hasPriv('zanode', 'destroy')) { echo "
"; echo ""; echo ""; diff --git a/module/zanode/view/browsesnapshot.html.php b/module/zanode/view/browsesnapshot.html.php new file mode 100644 index 0000000000..067087af4a --- /dev/null +++ b/module/zanode/view/browsesnapshot.html.php @@ -0,0 +1,66 @@ + + * @package zahost + * @version $Id$ + * @link http://www.zentao.net + */ +?> +getModuleRoot() . 'common/view/header.html.php';?> + +
+ recTotal}&recPerPage={$pager->recPerPage}";?> + +
+

+ zanode->snapshotEmpty;?> +

+
+ + + + + + + + + + + + + + + status = ($snapshot->status == 'restoring' && time() - strtotime($snapshot->restoreDate) > 600) ? 'restore_failed' : $snapshot->status; + + $editAttr = $snapshot->status == 'failed' ? 'class="btn disabled"' : "title={$lang->zanode->editSnapshot} onclick='window.parent.editSnapshot(\"" . $this->createLink('zanode', 'editSnapshot', "snapshotID={$snapshot->id}") . "\")' class='btn'"; + + $restoreAttr = "title='{$lang->zanode->restoreSnapshot}' target='hiddenwin'"; + $restoreAttr .= ($node->status !='running' or $snapshot->status == 'restoring' or $snapshot->status == 'failed') ? ' class="btn disabled"' : 'class="btn"'; + + $deleteAttr = "title='{$lang->zanode->deleteSnapshot}' target='hiddenwin'"; + $deleteAttr .= (($snapshot->status == 'restoring' and (time() - strtotime($snapshot->restoreDate)) <= 600) or $snapshot->status == 'failed') ? ' class="btn disabled"' : 'class="btn"'; + ?> + + + + + + + + +
zahost->image->name);?>zahost->status;?>zahost->createdBy;?>zahost->createdDate;?>actions;?>
localName ? $snapshot->localName : $snapshot->name;?>zanode->snapshot->statusList, $snapshot->status, '');?>createdBy, '')?>createdDate;?> + ', 'hiddenwin', $editAttr);?> + createLink('zanode', 'restoreSnapshot', "nodeID={$nodeID}&snapshotID={$snapshot->id}"), '', 'hiddenwin', $restoreAttr);?> + createLink('zanode', 'deleteSnapshot', "snapshotID={$snapshot->id}"), '', 'hiddenwin', $deleteAttr);?> +
+ + +
+getModuleRoot() . 'common/view/footer.html.php';?> diff --git a/module/zanode/view/createsnapshot.html.php b/module/zanode/view/createsnapshot.html.php new file mode 100644 index 0000000000..6379bd450f --- /dev/null +++ b/module/zanode/view/createsnapshot.html.php @@ -0,0 +1,46 @@ + + * @package task + * @version + * @link http://www.zentao.net + */ +?> + + +id);?> +zanode); ?> + +
+
+
+

+ zanode->createSnapshot;?> +

+
+
+ + + + + + + + + + + + + +
zanode->snapshotName;?>
zanode->desc;?>
+ +
+
+
+
+ diff --git a/module/zanode/view/editsnapshot.html.php b/module/zanode/view/editsnapshot.html.php new file mode 100644 index 0000000000..2d2b9dde0d --- /dev/null +++ b/module/zanode/view/editsnapshot.html.php @@ -0,0 +1,45 @@ + + * @package task + * @version + * @link http://www.zentao.net + */ +?> + + +zanode); ?> + +
+
+
+

+ zanode->editSnapshot;?> +

+
+
+ + + + + + + + + + + + + +
zanode->snapshotName;?>localName ? $snapshot->localName : $snapshot->name, "class='form-control'");?>
zanode->desc;?>desc, "rows='6' class='form-control'");?>
+ +
+
+
+
+ diff --git a/module/zanode/view/list.html.php b/module/zanode/view/list.html.php index 084e62a41a..37e595a1e5 100644 --- a/module/zanode/view/list.html.php +++ b/module/zanode/view/list.html.php @@ -11,6 +11,7 @@ * @link http://www.zentao.net */ ?> + getModuleRoot() . 'common/view/header.html.php'; ?>
diff --git a/module/zanode/view/view.html.php b/module/zanode/view/view.html.php index b5a2a99303..ace6c719fc 100644 --- a/module/zanode/view/view.html.php +++ b/module/zanode/view/view.html.php @@ -55,7 +55,7 @@ $account = strpos($zanode->osName, "windows") ? $config->zanode->defaultWinAccou
zanode->sshAddress; ?>:
-
ssh ip . ' -p ' . $zanode->ssh; ?>"; ?>
+
ssh ? 'ssh ' . $account . '@' . $zanode->ip . ' -p ' . $zanode->ssh : ''; ?>ssh ? " " : ''; ?>
@@ -96,7 +96,7 @@ $account = strpos($zanode->osName, "windows") ? $config->zanode->defaultWinAccou
zanode->defaultPwd; ?>:
-
zanode->defaultPwd . ' ' . ""; ?>
+
zanode->defaultPwd)) . ' ' . ""; ?>
@@ -149,6 +149,20 @@ $account = strpos($zanode->osName, "windows") ? $config->zanode->defaultWinAccou
desc) ? htmlspecialchars_decode($zanode->desc) : $lang->noData; ?>
+ +
+
+
zanode->browseSnapshot;?>
+ +
+ createLink('zanode', 'browseSnapshot', "nodeID=$zanode->id", '', true) . "' frameborder='no' allowfullscreen='true' mozallowfullscreen='true' webkitallowfullscreen='true' allowtransparency='true' scrolling='auto' onload='setIframeHeight(this)' style='min-height:300px;'>";?> +
+ +
noData; ?>
+ +
+
+ printExtendFields($zanode, 'div', "position=left&inForm=0&inCell=1"); ?>
@@ -164,6 +178,9 @@ $account = strpos($zanode->osName, "windows") ? $config->zanode->defaultWinAccou $rebootAttr = "title='{$lang->zanode->reboot}' target='hiddenwin'"; $rebootAttr .= $zanode->status == 'shutoff' ? ' class="btn disabled"' : "class='btn' target='hiddenwin' onclick='if(confirm(\"{$lang->zanode->confirmReboot}\")==false) return false;'"; + + $snapshotAttr = "title='{$lang->zanode->createSnapshot}'"; + $snapshotAttr .= $zanode->status != 'running' ? ' class="btn disabled"' : ' class="btn iframe"'; common::printLink('zanode', 'getVNC', "id={$zanode->id}", " " . $lang->zanode->getVNC, in_array($zanode->status ,array('running', 'launch', 'wait')) ? '_blank' : '', "title='{$lang->zanode->getVNC}' class='btn desktop " . (in_array($zanode->status ,array('running', 'launch', 'wait')) ? '':'disabled') . "'", ''); if($zanode->status == "suspend") @@ -185,11 +202,13 @@ $account = strpos($zanode->osName, "windows") ? $config->zanode->defaultWinAccou } common::printLink('zanode', 'reboot', "zanodeID={$zanode->id}", " " . $lang->zanode->rebootNode, '', $rebootAttr); + common::printLink('zanode', 'createSnapshot', "zanodeID={$zanode->id}", " " . $lang->zanode->createSnapshot, '', $snapshotAttr, true, true); } ?>
zanode->buildOperateMenu($zanode, 'view'); ?> +