[v2] UI: Guest Snapshot

From: Yu Xin Huo <huoyuxin@linux.vnet.ibm.com> Signed-off-by: Yu Xin Huo <huoyuxin@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

Here are my comments: - When creating a snapshot, the loading icon makes the snapshot entry line a little bigger. - After creating a snapshot, the UI blinks to update the list of snapshots. That's very noticeable. - When deleting the current snapshot, the UI doesn't update the new current snapshot. Just call GET /vms/<vm-name>/snapshots/current again and draw the check icon on the new current snapshot. - When deleting a snapshot, you need to disable the buttons just like you did when reverting o a snapshot. You need to apply the patchset I sent today ("Snapshot bugfixes") to get the fix for some of the bugs you reported on your last e-mail. The only one I haven't worked out yet is creating a snapshot on a VM with a raw disk. For now, you need to create a snapshot on a VM created by Kimchi, which uses a qcow2 image. On 18-11-2014 08:49, huoyuxin@linux.vnet.ibm.com wrote:
From: Yu Xin Huo <huoyuxin@linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin@linux.vnet.ibm.com>
participants (2)
-
Crístian Viana
-
huoyuxin@linux.vnet.ibm.com