
Update guests.html.template to manipulate the VM List my DOM manipulation rather than by string manipulation. Also update the tile to function as a start when the VM is stopped, or as a console connection when it is running. Signed-off-by: Adam King <rak@linux.vnet.ibm.com> --- ui/css/theme-default/list.css | 2 + ui/js/src/kimchi.guest_main.js | 366 +++++++++++++++++++++++------------------ ui/pages/tabs/guests.html.tmpl | 2 +- 3 files changed, 207 insertions(+), 163 deletions(-) diff --git a/ui/css/theme-default/list.css b/ui/css/theme-default/list.css index 57ad610..af7ac1f 100644 --- a/ui/css/theme-default/list.css +++ b/ui/css/theme-default/list.css @@ -101,6 +101,7 @@ max-height: 110px; width: auto; display:inline; border: none; + position: relative; } .list-vm .tile.running .imgactive{ @@ -249,6 +250,7 @@ max-height: 110px; .list-vm .shutoff { + position: relative; box-shadow: none !important; } diff --git a/ui/js/src/kimchi.guest_main.js b/ui/js/src/kimchi.guest_main.js index 99cb84a..15354a4 100644 --- a/ui/js/src/kimchi.guest_main.js +++ b/ui/js/src/kimchi.guest_main.js @@ -5,6 +5,7 @@ * * Authors: * Hongliang Wang <hlwanghl@cn.ibm.com> + * Adam King <rak@us.ibm.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,164 +19,111 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -kimchi.initVmButtonsAction = function() { - - var vmstart = function(event) { - if (!$(this).hasClass('loading')) { - $(this).addClass('loading'); - kimchi.startVM($(this).data('vm'), function(result) { - kimchi.listVmsAuto(); - }, function(err) { - kimchi.message.error(err.responseJSON.reason); - }); - } else { - event.preventDefault(); - event.stopPropagation(); - return; - } - }; - - var vmstop = function(event) { - if (!$(this).hasClass('loading')) { - $(this).addClass('loading'); - kimchi.stopVM($(this).data('vm'), function(result) { - kimchi.listVmsAuto(); - }, function(err) { - kimchi.message.error(err.responseJSON.reason); - }); - } else { - event.preventDefault(); - event.stopPropagation(); - } - }; - - $('.circle').circle(); - - $(".vm-start").each(function(index) { - if ('running' === $(this).data('vmstate')) { - $(this).hide(); - } else { - $(this).show(); - } - }); - - $(".vm-stop").each(function(index) { - if ('running' === $(this).data('vmstate')) { - $(this).show(); - } else { - $(this).hide(); - } - }); - - $(".vm-start").on({ - click : vmstart, - }); - - $(".vm-stop").on({ - click : vmstop, - }); - - $(".vm-reset").on("click", function(event) { - if ('running' === $(this).data('vmstate')) { - kimchi.resetVM($(this).data('vm'), function(result) { - kimchi.listVmsAuto(); - }, function(err) { - kimchi.message.error(err.responseJSON.reason); - }); - } else { - kimchi.startVM($(this).data('vm'), function(result) { - kimchi.listVmsAuto(); - }, function(err) { - kimchi.message.error(err.responseJSON.reason); - }); - } - }); - - $(".vm-delete").on("click", function(event) { - var vm = $(this); - var settings = { - title : i18n['KCHAPI6001M'], - content : i18n['KCHVM6001M'], - confirm : i18n['KCHAPI6002M'], - cancel : i18n['KCHAPI6003M'] - }; - kimchi.confirm(settings, function() { - kimchi.deleteVM(vm.data('vm'), function(result) { - kimchi.listVmsAuto(); - }, function(err) { - kimchi.message.error(err.responseJSON.reason); - }); - }, function() { - }); - }); - - $(".vm-edit").on("click", function(event) { - var vmName = $(this).data('vm'); - kimchi.selectedGuest = vmName; - kimchi.window.open("guest-edit.html"); - }); - $(".vm-vnc").on("click", function(event) { - kimchi.vncToVM($(this).data('vm')); - }); +kimchi.vmstart = function(event) { + var button=$(this); + if (!button.hasClass('loading')) { + button.addClass('loading'); + var vm=$(this).closest('li[name=guest]'); + var vm_id=vm.attr("id"); + kimchi.startVM(vm_id, function(result) { + button.removeClass('loading'); + kimchi.listVmsAuto(); + }, function() { + startButton.removeClass('loading'); + kimchi.message.error(i18n['msg.fail.start']); + } + ); + } else { + event.preventDefault(); + event.stopPropagation(); + return; + } +}; - $(".vm-spice").on("click", function(event) { - kimchi.spiceToVM($(this).data('vm')); - }); +kimchi.vmstop = function(event) { + var button=$(this); + if (!button.hasClass('loading')) { + button.addClass('loading'); + var vm=button.closest('li[name=guest]'); + var vm_id=vm.attr("id"); + kimchi.stopVM(vm_id, function(result) { + button.removeClass('loading'); + kimchi.listVmsAuto(); + }, function() { + kimchi.message.error(i18n['msg.fail.stop']); + }); + } else { + event.preventDefault(); + event.stopPropagation(); + } +}; - kimchi.init_button_stat(); +kimchi.vmreset = function(event){ + var vm=$(this).closest('li[name=guest]'); + var vm_id=vm.attr("id"); + kimchi.resetVM(vm_id, function(result) { + kimchi.listVmsAuto(); + }, function() { + kimchi.message.error(i18n['msg.fail.reset']); + } + ); +}; +kimchi.vmdelete = function(event) { + var vm = $(this).closest('li[name=guest]'); + var vm_id=vm.attr("id"); + var settings = { + title : i18n['msg.confirm.delete.title'], + content : i18n['msg.vm.confirm.delete'], + confirm : i18n['msg.confirm.delete.confirm'], + cancel : i18n['msg.confirm.delete.cancel'] + }; + kimchi.confirm(settings, function() { + kimchi.deleteVM(vm_id, function(result) { + kimchi.listVmsAuto(); + }, function() { + kimchi.message.error(i18n['msg.fail.delete']); + }); + }, function() { + }); }; -kimchi.init_button_stat = function() { - $('.vm-action').each(function() { - var vm_action = $(this); - var vm_vnc = vm_action.find('.vm-vnc'); - var vm_spice = vm_action.find('.vm-spice'); - var vm_graphics; - if (vm_action.data('graphics') === 'vnc') { - vm_spice.hide(); - vm_graphics = vm_vnc; - } else if (vm_action.data('graphics') === 'spice') { - vm_vnc.hide(); - vm_graphics = vm_spice; - } else { - vm_vnc.hide(); - vm_spice.hide(); - vm_graphics = null; - } +kimchi.vmedit = function(event) { + var vm = $(this).closest('li[name=guest]'); + var vm_id=vm.attr("id"); + kimchi.selectedGuest = vm_id; + kimchi.window.open("guest-edit.html"); +}; - if (vm_graphics !== null) { - if (vm_action.data('vmstate') === 'running') { - vm_graphics.removeAttr('disabled'); - } else { - vm_graphics.attr('disabled', 'disabled'); - } - } +kimchi.openVmConsole = function(event) { + var vm=$(this).closest('li[name=guest]'); + var vmObject=vm.data(); + if (vmObject.graphics['type'] == 'vnc') { + kimchi.vncToVM(vm.attr('id')); + } + else if (vmObject.graphics['type'] == 'spice') { + kimchi.spiceToVM(vm.attr('id')); + } - var editButton = vm_action.find('.vm-edit'); - editButton.prop('disabled', vm_action.data('vmstate') !== 'shutoff'); - }) }; -kimchi.getVmsOldImg = function() { +kimchi.getVmsCurrentConsoleImgs = function() { var res = new Object(); $('#guestList').children().each(function() { - res[$(this).attr('id')] = $(this).find('img').attr('src'); + res[$(this).attr('id')] = $(this).find('img.imgactive').attr('src'); }) return res; }; -kimchi.getVmsOldPopStats = function() { - var oldSettings = new Object(); - $('#guestList').children().each(function() { - if ($(this).find('.popable').hasClass('open')) { - oldSettings[$(this).attr('id')] = true; - } else { - oldSettings[$(this).attr('id')] = false; - } - }) - return oldSettings; +kimchi.getOpenMenuVmId = function() { + var result; + var openMenu = $('#guestList .open:first') + if(openMenu) { + var li_element=openMenu.closest('li'); + result=li_element.attr('id'); + } + return result; }; kimchi.listVmsAuto = function() { @@ -185,28 +133,18 @@ kimchi.listVmsAuto = function() { kimchi.listVMs(function(result, textStatus, jqXHR) { if (result && textStatus=="success") { if(result.length) { - $('#guestListField').show(); - $('#noGuests').hide(); var listHtml = ''; var guestTemplate = kimchi.guestTemplate; - var oldImages = kimchi.getVmsOldImg(); - var oldSettings = kimchi.getVmsOldPopStats(); - $.each(result, function(index, value) { - var oldImg = oldImages[value.name]; - curImg = value.state == 'running' ? value.screenshot : value.icon; - value['load-src'] = curImg || 'images/icon-vm.png'; - value['tile-src'] = oldImg || value['load-src']; - var statusTemplate = kimchi.editTemplate(guestTemplate, oldSettings[value.name]); - listHtml += kimchi.template(statusTemplate, value); - }); - $('#guestList').html(listHtml); - $('#guestList').find('.imgload').each(function() { - this.onload = function() { - $(this).prev('.imgactive').remove(); - $(this).show(); - } + var currentConsoleImages = kimchi.getVmsCurrentConsoleImgs(); + var openMenuGuest = kimchi.getOpenMenuVmId(); + $('#guestList').html(''); + $('#guestListField').show(); + $('#noGuests').hide(); + + $.each(result, function(index, vm) { + var guestLI = kimchi.createGuestLi(vm, currentConsoleImages[vm.name], vm.name==openMenuGuest); + $('#guestList').append(guestLI); }); - kimchi.initVmButtonsAction(); } else { $('#guestListField').hide(); $('#noGuests').show(); @@ -220,6 +158,109 @@ kimchi.listVmsAuto = function() { }); }; +kimchi.createGuestLi = function(vmObject, prevScreenImage, openMenu) { + var result=kimchi.guestElem.clone(); + + //Setup the VM list entry + var vmRunningBool=(vmObject.state=="running"); + result.attr('id',vmObject.name); + result.data(vmObject); + + //Add the Name + var guestTitle=result.find('.title').attr('title',vmObject.name); + guestTitle.html(vmObject.name); + + //Setup the VM console thumbnail display + var curImg = vmObject.state == 'running' ? vmObject.screenshot : vmObject.icon; + var load_src = curImg || 'images/icon-vm.png'; + var tile_src = prevScreenImage || vmObject['load-src']; + var liveTile=result.find('div[name=guest-tile] > .tile'); + liveTile.addClass(vmObject.state); + liveTile.find('.imgactive').attr('src',tile_src); + var imgLoad=liveTile.find('.imgload'); + imgLoad.on('load', function() { + var oldImg=$(this).parent().find('.imgactive'); + oldImg.removeClass("imgactive").addClass("imgload"); + oldImg.attr("src",""); + $(this).addClass("imgactive").removeClass("imgload"); + $(this).off('load'); + }); + imgLoad.attr('src',load_src); + + //Link the stopped tile to the start action, the running tile to open the console + if (vmRunningBool) { + liveTile.off("click", kimchi.vmstart); + liveTile.on("click", kimchi.openVmConsole); + } + else { + liveTile.off("click", kimchi.openVmConsole); + liveTile.on("click", kimchi.vmstart); + liveTile.hover(function(event){$(this).find('.overlay').show()}, function(event){$(this).find('.overlay').hide()}); + } + + + //Setup the gauges + var stats=vmObject.stats; + var gaugeValue=0; + gaugeValue=parseInt(stats.net_throughput); + kimchi.circleGaugeInit(result, "net_throughput",gaugeValue,(gaugeValue*100/stats.net_throughput_peak)); + gaugeValue=parseInt(stats.io_throughput); + kimchi.circleGaugeInit(result, "io_throughput",gaugeValue,(gaugeValue*100/stats.io_throughput_peak)); + gaugeValue=parseInt(stats.cpu_utilization); + kimchi.circleGaugeInit(result, "cpu_utilization",gaugeValue+"%",gaugeValue); + + //Setup the VM Actions + var guestActions=result.find("div[name=guest-actions]"); + guestActions.find(".shutoff-disabled").prop('disabled', !vmRunningBool ); + guestActions.find(".running-disabled").prop('disabled', vmRunningBool ); + + if (vmRunningBool) { + guestActions.find(".running-hidden").hide(); + } + else { + guestActions.find(".shutoff-hidden").hide(); + } + + var consoleActions=guestActions.find("[name=vm-console]"); + + if ((vmObject.graphics['type'] == 'vnc') || (vmObject.graphics['type'] == 'spice')) { + consoleActions.on("click", kimchi.openVmConsole); + consoleActions.show(); + } else { //we don't recognize the VMs supported graphics, so hide the menu choice + consoleActions.hide(); + consoleActions.off("click",kimchi.openVmConsole); + } + + //Setup action event handlers + guestActions.find("[name=vm-start]").on({click : kimchi.vmstart}); + guestActions.find("[name=vm-stop]").on({click : kimchi.vmstop}); + if (vmRunningBool) { //If the guest is not running, do not enable reset + guestActions.find("[name=vm-reset]").on({click : kimchi.vmreset}); + } + guestActions.find("[name=vm-edit]").on({click : kimchi.vmedit}); + guestActions.find("[name=vm-delete]").on({click : kimchi.vmdelete}); + + //Maintain menu open state + var actionMenu=guestActions.find("div[name=actionmenu]"); + if (openMenu) { + actionMenu.addClass("open"); + } + + return result; +}; + +kimchi.circleGaugeInit = function(topElement, divName, display, percentage){ + var gauge=topElement.find('div[name="' + divName + '"] .circleGauge'); + if(gauge) { + var data=Object(); + data.percentage = percentage; + data.display = display; + gauge.data(data); + } + gauge.circleGauge(); + return(gauge); +}; + kimchi.guestSetRequestHeader = function(xhr) { xhr.setRequestHeader('Accept', 'text/html'); }; @@ -229,6 +270,7 @@ kimchi.guest_main = function() { kimchi.window.open('guest-add.html'); }); kimchi.guestTemplate = $('#guest-tmpl').html(); + kimchi.guestElem=$('<div/>').html(kimchi.guestTemplate).find('li'); $('#guests-root-container').on('remove', function() { kimchi.vmTimeout && clearTimeout(kimchi.vmTimeout); }); @@ -236,7 +278,7 @@ kimchi.guest_main = function() { }; kimchi.editTemplate = function(guestTemplate, oldPopStat) { - if (oldPopStat != null && oldPopStat) { + if (oldPopStat) { return guestTemplate.replace("vm-action", "vm-action open"); } return guestTemplate; diff --git a/ui/pages/tabs/guests.html.tmpl b/ui/pages/tabs/guests.html.tmpl index 8b530c7..e86718b 100644 --- a/ui/pages/tabs/guests.html.tmpl +++ b/ui/pages/tabs/guests.html.tmpl @@ -40,8 +40,8 @@ <ul class="list-title"> <li class="guest-type">$_("Name")</li> <li class="guest-cpu">$_("CPU")</li> - <li class="guest-network">$_("Network I/O")</li> <li class="guest-storage">$_("Disk I/O")</li> + <li class="guest-network">$_("Network I/O")</li> <li class="guest-tile">$_("Livetile")</li> <li class="guest-actions">$_("Actions")</li> </ul> -- 1.8.1.4