From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/guest-edit.css | 35 +++++++++++-
ui/js/src/kimchi.api.js | 68 ++++++++++++++++++++++
ui/js/src/kimchi.guest_edit_main.js | 106 +++++++++++++++++++++++++++++++++++
ui/pages/guest-edit.html.tmpl | 27 +++++++++
4 files changed, 235 insertions(+), 1 deletions(-)
diff --git a/ui/css/theme-default/guest-edit.css b/ui/css/theme-default/guest-edit.css
index b1d3931..74d72f2 100644
--- a/ui/css/theme-default/guest-edit.css
+++ b/ui/css/theme-default/guest-edit.css
@@ -96,6 +96,7 @@
}
#form-guest-edit-storage .header,
+.guest-edit-snapshot .header,
.guest-edit-interface .header {
margin-bottom: 8px;
padding-bottom: 2px;
@@ -105,6 +106,8 @@
}
#form-guest-edit-storage .body .item,
+.guest-edit-snapshot .body .item,
+.guest-edit-snapshot .task .item,
.guest-edit-interface .body .item {
margin: 5px 0;
}
@@ -115,6 +118,32 @@
width: 250px;
}
+.guest-edit-snapshot .cell {
+ display: inline-block;
+}
+
+.guest-edit-snapshot .sel {
+ width: 35px;
+ vertical-align: top;
+}
+
+.guest-edit-snapshot .icon {
+ background: url('../../images/theme-default/kimchi-loading15x15.gif')
no-repeat;
+ display: block;
+ width: 16px;
+ height: 16px;
+ vertical-align: middle;
+ margin-left: 2px;
+}
+
+.guest-edit-snapshot .name {
+ width: 400px;
+}
+
+.guest-edit-snapshot .created {
+ width: 250px;
+}
+
#form-guest-edit-storage .cell.dev {
width: 60px;
}
@@ -135,6 +164,7 @@
}
#form-guest-edit-storage .action-area,
+.guest-edit-snapshot .action-area,
.guest-edit-interface .action-area {
float: right;
}
@@ -144,12 +174,14 @@
}
#form-guest-edit-storage button,
+.guest-edit-snapshot button,
.guest-edit-interface button {
width: 20px;
height: 20px;
}
#form-guest-edit-storage .header button,
+.guest-edit-snapshot .header button,
.guest-edit-interface .header button {
margin-bottom: 1px;
}
@@ -159,8 +191,9 @@
margin-right: 2px;
}
+.guest-edit-snapshot .hide,
.guest-edit-interface .hide {
- display: none;
+ display: none!important;
}
.guest-edit-permission {
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 78c6d66..8e21d4c 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1260,5 +1260,73 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ listSnapshots : function(vm, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ getCurrentSnapshot : function(vm, suc, err, sync) {
+ kimchi.requestJSON({
+ url :
kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots/current',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ async : !sync,
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ revertSnapshot : function(vm, snapshot, suc, err) {
+ kimchi.requestJSON({
+ url :
kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots/'+encodeURIComponent(snapshot)+'/revert',
+ type : 'POST',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ createSnapshot : function(vm, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots',
+ type : 'POST',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ deleteSnapshot : function(vm, snapshot, suc, err) {
+ kimchi.requestJSON({
+ url :
kimchi.url+'vms/'+encodeURIComponent(vm)+'/snapshots/'+encodeURIComponent(snapshot),
+ type : 'DELETE',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 9d87a73..e033743 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -461,6 +461,111 @@ kimchi.guest_edit_main = function() {
});
};
+ var setupSnapshot = function() {
+ var currentSnapshot;
+ var setCurrentSnapshot = function(aSnapshot){
+ if(!aSnapshot)
+ kimchi.getCurrentSnapshot(kimchi.selectedGuest, function(snapshot){
+ if(snapshot&&snapshot.name) aSnapshot = snapshot.name;
+ }, null, true);
+ if(aSnapshot){
+ if(currentSnapshot) $(".ui-icon-check",
"#"+currentSnapshot).addClass("hide");
+ $(".ui-icon-check",
"#"+aSnapshot).removeClass("hide");
+ currentSnapshot = aSnapshot;
+ }
+ };
+ var addItem = function(data, container) {
+ var itemNode =
$.parseHTML(kimchi.substitute($('#snapshot-tmpl').html(),data));
+ $("."+container,
"#form-guest-edit-snapshot").append(itemNode);
+ $(".delete", itemNode).button({
+ icons: { primary: "ui-icon-trash" },
+ text: false
+ }).click(function(evt){
+ evt.preventDefault();
+ var item = $(this).parent().parent();
+ $("button",
"#form-guest-edit-snapshot").button("disable");
+ kimchi.deleteSnapshot(kimchi.selectedGuest, item.prop("id"),
function(){
+ item.remove();
+ setCurrentSnapshot();
+ $("button",
"#form-guest-edit-snapshot").button("enable");
+ }, function(data){
+ kimchi.message.error(data.responseJSON.reason);
+ $("button",
"#form-guest-edit-snapshot").button("enable");
+ });
+ });
+ $(".revert", itemNode).button({
+ icons: { primary: "ui-icon-arrowthick-1-ne" },
+ text: false
+ }).click(function(evt){
+ evt.preventDefault();
+ var item = $(this).parent().parent();
+ $(".ui-icon-check", item).addClass("hide");
+ $(".icon", item).removeClass("hide");
+ $("button",
"#form-guest-edit-snapshot").button("disable");
+ kimchi.revertSnapshot(kimchi.selectedGuest, item.prop("id"),
function(){
+ $(".icon", item).addClass("hide");
+ $("button",
"#form-guest-edit-snapshot").button("enable");
+ setCurrentSnapshot(item.prop("id"));
+ }, function(data){
+ kimchi.message.error(data.responseJSON.reason);
+ $(".icon", item).addClass("hide");
+ $("button",
"#form-guest-edit-snapshot").button("enable");
+ });
+ });
+ };
+ var addOngoingItem = function(task){
+ var uri = task.target_uri;
+ addItem({
+ name: uri.substring(uri.lastIndexOf('/')+1, uri.length),
+ created: "",
+ listMode: "hide",
+ createMode: ""
+ }, 'task');
+ if(kimchi.trackingTasks.indexOf(task.id)==-1)
+ kimchi.trackTask(task.id, function(task){
+ listGeneratingSnapshots();
+ $("button",
"#form-guest-edit-snapshot").button("enable");
+ }, function(err){
+ kimchi.message.error(err.message);
+ listGeneratingSnapshots();
+ $("button",
"#form-guest-edit-snapshot").button("enable");
+ });
+ };
+ var listGeneratingSnapshots = function(){
+
kimchi.getTasksByFilter('status=running&target_uri='+encodeURIComponent('^/snapshots/*'),
function(tasks) {
+ $(".task", "#form-guest-edit-snapshot").empty();
+ for(var i=0;i<tasks.length;i++){
+ addOngoingItem(tasks[i]);
+ }
+ if(tasks.length==0) listSnapshots();
+ });
+ };
+ var listSnapshots = function(){
+ kimchi.listSnapshots(kimchi.selectedGuest, function(data){
+ $(".body", "#form-guest-edit-snapshot").empty();
+ for(var i=0;i<data.length;i++){
+ data[i].created = new Date(data[i].created*1000).toLocaleString();
+ data[i].createMode = "hide";
+ data[i].listMode = "";
+ addItem(data[i], 'body');
+ }
+ setCurrentSnapshot();
+ });
+ };
+ listGeneratingSnapshots();
+ $(".add", "#form-guest-edit-snapshot").button({
+ icons: { primary: "ui-icon-plusthick" },
+ text: false
+ }).click(function(evt){
+ evt.preventDefault();
+ kimchi.createSnapshot(kimchi.selectedGuest, function(task){
+ $("button",
"#form-guest-edit-snapshot").button("disable");
+ addOngoingItem(task);
+ });
+ });
+ if(kimchi.thisVMState=="running") $("button",
"#form-guest-edit-snapshot").remove();
+ };
+
var initContent = function(guest) {
guest['icon'] = guest['icon'] || 'images/icon-vm.png';
$('#form-guest-edit-general').fillWithObject(guest);
@@ -496,6 +601,7 @@ kimchi.guest_edit_main = function() {
setupInterface();
setupPermission();
setupPCIDevice();
+ setupSnapshot();
kimchi.topic('kimchi/vmCDROMAttached').subscribe(onAttached);
kimchi.topic('kimchi/vmCDROMReplaced').subscribe(onReplaced);
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index 512909a..6233884 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -44,6 +44,9 @@
<li>
<a href="#form-guest-edit-pci">$_("Host PCI
Device")</a>
</li>
+ <li>
+ <a
href="#form-guest-edit-snapshot">$_("Snapshot")</a>
+ </li>
</ul>
<form id="form-guest-edit-general">
<fieldset class="guest-edit-fieldset">
@@ -160,6 +163,16 @@
<div class="body"></div>
</div>
</form>
+ <form id="form-guest-edit-snapshot"
class="guest-edit-snapshot">
+ <div class="header">
+ <span class="cell sel"></span>
+ <span class="cell
name">$_("Name")</span>
+ <span class="cell
created">$_("Created")</span>
+ <button class="add action-area"></button>
+ </div>
+ <div class="task"></div>
+ <div class="body"></div>
+ </form>
</div>
</div>
<footer>
@@ -251,6 +264,20 @@
<button></button>
</div>
</script>
+<script id="snapshot-tmpl" type="text/html">
+ <div class="item" id="{name}">
+ <span class="cell sel">
+ <span class="icon {createMode}"></span>
+ <span class="ui-icon ui-icon-check hide"></span>
+ </span>
+ <span class="cell name">{name}</span>
+ <span class="cell created">{created}</span>
+ <span class="action-area">
+ <button class="revert {listMode}"
title="$_("revert")"></button>
+ <button class="delete {listMode}"></button>
+ </span>
+ <div>
+</script>
<script type="text/javascript">
kimchi.guest_edit_main();
</script>
--
1.7.1