
From: Yu Xin Huo <huoyuxin@linux.vnet.ibm.com> Signed-off-by: Yu Xin Huo <huoyuxin@linux.vnet.ibm.com> Signed-off-by: Wen Wang <wenwang@linux.vnet.ibm.com> --- ui/css/theme-default/guest-edit.css | 86 ++++++++++++++++++++++++++++++++++++- ui/js/src/kimchi.api.js | 55 ++++++++++++++++++++++++ ui/js/src/kimchi.guest_edit_main.js | 81 ++++++++++++++++++++++++++++++++++ ui/pages/guest-edit.html.tmpl | 28 ++++++++++++ 4 files changed, 248 insertions(+), 2 deletions(-) diff --git a/ui/css/theme-default/guest-edit.css b/ui/css/theme-default/guest-edit.css index 76fbaf2..ef2266f 100644 --- a/ui/css/theme-default/guest-edit.css +++ b/ui/css/theme-default/guest-edit.css @@ -17,8 +17,8 @@ */ #guest-edit-window { font-size: 13px; - height: 400px; - width: 610px; + height: 420px; + width: 820px; } #guest-edit-tabs { @@ -261,3 +261,85 @@ width: 46%; float: right; } + +.guest-edit-pci { + height: 79%; + overflow: auto; + font-size: 12px; +} + +.guest-edit-pci .filter { + height: 35px; + margin-right: 5px; + overflow: hidden; +} + +.guest-edit-pci .group { + float: right; +} + +.guest-edit-pci .filter .control { + border: 1px solid #AAAAAA; + font-size: 12px; + background-color: white; +} + +.guest-edit-pci .filter select { + border-right: 0px!important; + border-radius: 7px 0px 0px 7px; + padding: 2px 2px 2px 7px; + width: 100px; +} + +.guest-edit-pci .filter select option { + padding-left: 7px; +} + +.guest-edit-pci .filter input { + border-radius: 0px 7px 7px 0px; + padding: 3px 3px 3px 10px; + width: 200px; + font-style: italic; +} + +.guest-edit-pci .header { + margin-bottom: 8px; + padding-bottom: 2px; + font-weight: bold; + border-bottom: 1px solid #999999; +} + +.guest-edit-pci .item { + margin-bottom: 4px; + overflow: hidden; +} + +.guest-edit-pci .cell { + display: inline-block; + vertical-align: middle; + margin-right: 10px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.guest-edit-pci .item button { + width: 20px; + height: 20px; + float: right; +} + +.guest-edit-pci .name { + width: 18%; + max-width: 18%; +} + +.guest-edit-pci .product { + width: 45%; + max-width: 45%; +} + +.guest-edit-pci .vendor { + width: 25%; + max-width: 25%; +} diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js index 3398bd4..86bfd22 100644 --- a/ui/js/src/kimchi.api.js +++ b/ui/js/src/kimchi.api.js @@ -1114,6 +1114,20 @@ var kimchi = { }); }, + getHostPCIDevices : function(suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'host/devices?_passthrough=true&_cap=pci', + type : 'GET', + contentType : 'application/json', + dataType : 'json', + resend : true, + success : suc, + error : err ? err : function(data) { + kimchi.message.error(data.responseJSON.reason); + } + }); + }, + getISCSITargets : function(server, port, suc, err) { server = encodeURIComponent(server); port = port ? '&_server_port='+encodeURIComponent(port) : ''; @@ -1144,6 +1158,47 @@ var kimchi = { }); }, + getVMPCIDevices : function(id, suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'vms/'+encodeURIComponent(id)+'/hostdevs', + type : 'GET', + contentType : 'application/json', + dataType : 'json', + resend : true, + success : suc, + error : err ? err : function(data) { + kimchi.message.error(data.responseJSON.reason); + } + }); + }, + + addVMPCIDevice : function(vm, device, suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'vms/'+ encodeURIComponent(vm) +'/hostdevs', + type : 'POST', + contentType : 'application/json', + dataType : 'json', + data : JSON.stringify(device), + success : suc, + error : err ? err : function(data) { + kimchi.message.error(data.responseJSON.reason); + } + }); + }, + + removeVMPCIDevice : function(vm, device, suc, err) { + kimchi.requestJSON({ + url : kimchi.url + 'vms/'+ encodeURIComponent(vm) +'/hostdevs/' + encodeURIComponent(device), + type : 'DELETE', + contentType : 'application/json', + dataType : 'json', + success : suc, + error : err ? err : function(data) { + kimchi.message.error(data.responseJSON.reason); + } + }); + }, + /** * Add a volume to a given storage pool. */ diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js index c281289..030e112 100644 --- a/ui/js/src/kimchi.guest_edit_main.js +++ b/ui/js/src/kimchi.guest_edit_main.js @@ -359,6 +359,86 @@ kimchi.guest_edit_main = function() { }); }; + var setupPCIDevice = function(){ + kimchi.getHostPCIDevices(function(hostPCIs){ + kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs){ + kimchi.getCapabilities(function(result) { + var pciEnabled = result.kernel_vfio; + for(var i=0; i<hostPCIs.length; i++){ + var itemNode = $.parseHTML(kimchi.substitute($('#pci-tmpl').html(),{ + name: hostPCIs[i].name, + product: hostPCIs[i].product.description, + vendor: hostPCIs[i].vendor.description + })); + $(".body", "#form-guest-edit-pci").append(itemNode); + var iconClass = "ui-icon-plus"; + for(var j=0; j<vmPCIs.length; j++){ + if(hostPCIs[i].name==vmPCIs[j].name){ + iconClass = "ui-icon-minus"; + break; + } + } + pciEnabled || $("button", itemNode).remove(); + $("button", itemNode).button({ + icons: { primary: iconClass }, + text: false + }).click(function(){ + var obj = $(this); + if(obj.button("option", "icons").primary == "ui-icon-minus"){ + kimchi.removeVMPCIDevice(kimchi.selectedGuest, obj.parent().prop("id"), function(){ + kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs1){ + for(var k=0; k<hostPCIs.length; k++) { + $("button", "#" + hostPCIs[k].name).button("option", "icons", {primary: "ui-icon-plus"}); + } + for(var k=0; k<vmPCIs1.length; k++) { + $("button", "#" + vmPCIs1[k].name).button("option", "icons", {primary: "ui-icon-minus"}); + } + }); + filterNodes($("select", "#form-guest-edit-pci").val(), $("input", "#form-guest-edit-pci").val()); + }); + }else{ + kimchi.addVMPCIDevice(kimchi.selectedGuest, { name: obj.parent().prop("id") }, function(){ + kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs1){ + for(var k=0; k<vmPCIs1.length; k++) { + $("button", "#" + vmPCIs1[k].name).button("option", "icons", {primary: "ui-icon-minus"}); + } + }); + filterNodes($("select", "#form-guest-edit-pci").val(), $("input", "#form-guest-edit-pci").val()); + }); + } + }); + } + }); + }); + }); + var filterNodes = function(group, text){ + text = text.toLowerCase(); + $(".body", "#form-guest-edit-pci").children().each(function(){ + var textFilter = $(".name", this).text().toLowerCase().indexOf(text)!=-1; + textFilter = textFilter || $(".product", this).text().toLowerCase().indexOf(text)!=-1; + textFilter = textFilter || $(".vendor", this).text().toLowerCase().indexOf(text)!=-1; + var display = "none"; + var itemGroup = $("button", this).button("option", "icons").primary; + if(textFilter){ + if(group == "all"){ + display = ""; + }else if(group=="toAdd" && itemGroup=="ui-icon-plus"){ + display = "" + }else if(group == "added" && itemGroup=="ui-icon-minus"){ + display = "" + } + } + $(this).css("display", display); + }); + }; + $("select", "#form-guest-edit-pci").change(function(){ + filterNodes($(this).val(), $("input", "#form-guest-edit-pci").val()); + }); + $("input", "#form-guest-edit-pci").on("keyup", function() { + filterNodes($("select", "#form-guest-edit-pci").val(), $(this).val()); + }); + }; + var initContent = function(guest) { guest['icon'] = guest['icon'] || 'images/icon-vm.png'; $('#form-guest-edit-general').fillWithObject(guest); @@ -395,6 +475,7 @@ kimchi.guest_edit_main = function() { initStorageListeners(); setupInterface(); setupPermission(); + setupPCIDevice(); 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 917b2e8..69b11a7 100644 --- a/ui/pages/guest-edit.html.tmpl +++ b/ui/pages/guest-edit.html.tmpl @@ -41,6 +41,9 @@ <li> <a href="#form-guest-edit-permission">$_("Permission")</a> </li> + <li> + <a href="#form-guest-edit-pci">$_("Host PCI Device")</a> + </li> </ul> <form id="form-guest-edit-general"> <fieldset class="guest-edit-fieldset"> @@ -138,6 +141,23 @@ </div> </div> </form> + <form id="form-guest-edit-pci" class="guest-edit-pci"> + <div class="filter"> + <span class="group"> + <select class="control"> + <option value="all">$_("All")</option> + <option value="toAdd">$_("To Add")</option> + <option value="added">$_("Added")</option> + </select><input type="text" class="control" placeholder="$_("filter")"> + </span> + </div> + <div class="header"> + <span class="cell name">$_("Name")</span> + <span class="cell product">$_("Product")</span> + <span class="cell vendor">$_("Vendor")</span> + </div> + <div class="body"></div> + </form> </div> </div> <footer> @@ -221,6 +241,14 @@ <label>{val}</label> </div> </script> +<script id="pci-tmpl" type="text/html"> +<div class="item" id="{name}"> + <span class="cell name" title="{name}">{name}</span> + <span class="cell product" title="{product}">{product}</span> + <span class="cell vendor" title="{vendor}">{vendor}</span> + <button></button> +</div> +</script> <script type="text/javascript"> kimchi.guest_edit_main(); </script> -- 1.9.3