
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 | 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