[Kimchi-devel] [v2] UI: Guest Snapshot

huoyuxin at linux.vnet.ibm.com huoyuxin at linux.vnet.ibm.com
Tue Nov 18 10:49:45 UTC 2014


From: Yu Xin Huo <huoyuxin at linux.vnet.ibm.com>

Signed-off-by: Yu Xin Huo <huoyuxin at linux.vnet.ibm.com>
---
 ui/css/theme-default/guest-edit.css |   34 +++++++++++-
 ui/js/src/kimchi.api.js             |   69 ++++++++++++++++++++++
 ui/js/src/kimchi.guest_edit_main.js |  107 +++++++++++++++++++++++++++++++++++
 ui/pages/guest-edit.html.tmpl       |   27 +++++++++
 4 files changed, 236 insertions(+), 1 deletions(-)

diff --git a/ui/css/theme-default/guest-edit.css b/ui/css/theme-default/guest-edit.css
index b1d3931..a565c99 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,7 @@
 }
 
 #form-guest-edit-storage .body .item,
+.guest-edit-snapshot .body .item,
 .guest-edit-interface .body .item {
     margin: 5px 0;
 }
@@ -115,6 +117,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: inline-block;
+    width: 20px;
+    height: 20px;
+    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 +163,7 @@
 }
 
 #form-guest-edit-storage .action-area,
+.guest-edit-snapshot .action-area,
 .guest-edit-interface .action-area {
     float: right;
 }
@@ -144,12 +173,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 +190,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..a592298 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1260,5 +1260,74 @@ 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) {
+                if(data.status!=404) 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',
+            data : JSON.stringify({}),
+            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..0dc3a31 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -461,6 +461,112 @@ kimchi.guest_edit_main = function() {
         });
     };
 
+    var setupSnapshot = function() {
+        var currentSnapshot;
+        var postTask = function(){
+            listGeneratingSnapshots();
+            $("button", "#form-guest-edit-snapshot").button("enable");
+        };
+        var addItem = function(data, container) {
+            var itemNode = $.parseHTML(kimchi.substitute($('#snapshot-tmpl').html(),data));
+            $("."+container, "#form-guest-edit-snapshot").append(itemNode);
+            if(kimchi.thisVMState === "running") {
+                $("button", itemNode).remove();
+            }else{
+                $(".delete", itemNode).button({
+                    icons: { primary: "ui-icon-trash" },
+                    text: false
+                }).click(function(evt){
+                    evt.preventDefault();
+                    var item = $(this).parent().parent();
+                    kimchi.deleteSnapshot(kimchi.selectedGuest, item.prop("id"), function(){
+                        item.remove();
+                    });
+                });
+                $(".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(){
+                        currentSnapshot = item.prop("id");
+                        $(".ui-icon-check", $(".body", "#form-guest-edit-snapshot")).addClass("hide");
+                        $(".ui-icon-check", item).removeClass("hide");
+                        $(".icon", item).addClass("hide");
+                        $("button", "#form-guest-edit-snapshot").button("enable");
+                    }, function(data){
+                        kimchi.message.error(data.responseJSON.reason);
+                        $(".icon", item).addClass("hide");
+                        if(currentSnapshot) $(".ui-icon-check", currentSnapshot).removeClass("hide");
+                        $("button", "#form-guest-edit-snapshot").button("enable");
+                    });
+                });
+            }
+            return itemNode;
+        };
+        var addOngoingItem = function(task){
+            var uri = task.target_uri;
+            addItem({
+                name: uri.substring(uri.lastIndexOf('/')+1, uri.length),
+                created: "",
+                current: "hide",
+                listMode: "hide",
+                createMode: ""
+            }, 'task');
+            if(kimchi.trackingTasks.indexOf(task.id)==-1)
+                kimchi.trackTask(task.id, function(task){
+                    postTask();
+                }, function(err){
+                    kimchi.message.error(err.message);
+                    postTask();
+                });
+        };
+        var listGeneratingSnapshots = function(){
+            $(".task", "#form-guest-edit-snapshot").empty();
+            kimchi.getTasksByFilter('status=running&target_uri='+encodeURIComponent('^/snapshots/*'), function(tasks) {
+                for(var i=0;i<tasks.length;i++){
+                    addOngoingItem(tasks[i]);
+                }
+                if(tasks.length==0) listSnapshots();
+            });
+        };
+        var listSnapshots = function(){
+            $(".body", "#form-guest-edit-snapshot").empty();
+            kimchi.getCurrentSnapshot(kimchi.selectedGuest, function(snapshot){
+                currentSnapshot = snapshot.name;
+            });
+            kimchi.listSnapshots(kimchi.selectedGuest, function(data){
+                for(var i=0;i<data.length;i++){
+                    data[i].created = new Date(data[i].created*1000).toLocaleString();
+                    data[i].current = data[i].name==currentSnapshot?"":"hide";
+                    data[i].createMode = "hide";
+                    data[i].listMode = "";
+                    var item = addItem(data[i], 'body');
+                }
+            });
+        };
+        listGeneratingSnapshots();
+        if(kimchi.thisVMState === "running") {
+            $("#form-guest-edit-snapshot .revert").remove();
+            $("#form-guest-edit-snapshot .add").remove();
+        }else{
+            $(".add", "#form-guest-edit-snapshot").button({
+                icons: { primary: "ui-icon-plusthick" },
+                text: false
+            }).click(function(evt){
+                evt.preventDefault();
+                $("button", "#form-guest-edit-snapshot").button("disable");
+                kimchi.createSnapshot(kimchi.selectedGuest, function(task){
+                    addOngoingItem(task);
+                });
+            });
+        }
+    };
+
     var initContent = function(guest) {
         guest['icon'] = guest['icon'] || 'images/icon-vm.png';
         $('#form-guest-edit-general').fillWithObject(guest);
@@ -496,6 +602,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..dd320b9 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 {current}"></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




More information about the Kimchi-devel mailing list