From: Samuel Guimarães <sguimaraes943(a)gmail.com>
This commit fixes an issue with PCI passthrough devices through Kimchi.
It also addresses an enhancement to display success messages when some actions doesn't
require the Save button and there's no UI feedback to the user (Issue #975).
Signed-off-by: Samuel Guimarães <sguimaraes943(a)gmail.com>
---
model/vmhostdevs.py | 2 +-
ui/css/kimchi.css | 5 +
ui/css/src/modules/_edit-guests.scss | 4 +
ui/js/src/kimchi.guest_edit_main.js | 236 +++++++++++++++++++++++------------
ui/js/src/kimchi.guest_main.js | 10 +-
ui/pages/guest-edit.html.tmpl | 20 ++-
ui/pages/i18n.json.tmpl | 2 +
7 files changed, 180 insertions(+), 99 deletions(-)
diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py
index 2130ac4..b57cbf0 100644
--- a/model/vmhostdevs.py
+++ b/model/vmhostdevs.py
@@ -510,7 +510,7 @@ class VMHostDevModel(object):
return self.task.lookup(taskid)
def _detach_device(self, cb, params):
- cb('Detaching device.')
+ cb('Detaching device')
vmid = params['vmid']
dev_name = params['dev_name']
dom = params['dom']
diff --git a/ui/css/kimchi.css b/ui/css/kimchi.css
index 34347c2..59d96e1 100644
--- a/ui/css/kimchi.css
+++ b/ui/css/kimchi.css
@@ -1039,6 +1039,11 @@ body.wok-gallery {
width: 22%;
}
+#guest-edit-window #form-guest-edit-pci .wok-mask {
+ top: 0 !important;
+ z-index: 2 !important;
+}
+
#guest-edit-window #form-guest-edit-pci .column-actions {
width: 4.47%;
}
diff --git a/ui/css/src/modules/_edit-guests.scss b/ui/css/src/modules/_edit-guests.scss
index 6196ed9..b7e6941 100644
--- a/ui/css/src/modules/_edit-guests.scss
+++ b/ui/css/src/modules/_edit-guests.scss
@@ -62,6 +62,10 @@
}
}
#form-guest-edit-pci {
+ .wok-mask {
+ top: 0 !important;
+ z-index: 2 !important;
+ }
.column-actions {
width: 4.47%;
}
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index fc67d82..d1d6335 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -425,16 +425,16 @@ kimchi.guest_edit_main = function() {
var filterPCINodes = function(group, text) {
text = text.trim().split(" ");
var rows = $('.body',
'#form-guest-edit-pci').find('div');
- if(text === ""){
+ if (text === "") {
rows.show();
return;
}
rows.hide();
- rows.filter(function(index, value){
+ rows.filter(function(index, value) {
var $span = $(this);
var $itemGroup = $('button i', this);
- for (var i = 0; i < text.length; ++i){
+ for (var i = 0; i < text.length; ++i) {
if ($span.is(":containsNC('" + text[i] +
"')")) {
if (group === 'all') {
return true;
@@ -448,87 +448,26 @@ kimchi.guest_edit_main = function() {
return false;
}).show();
};
- var setupPCIDevice = function() {
- kimchi.getAvailableHostPCIDevices(function(hostPCIs) {
- kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs) {
- setupNode(hostPCIs, 'fa-power-off');
- setupNode(vmPCIs, 'fa-ban');
- });
- });
- $('select', '#form-guest-edit-pci').change(function() {
- filterPCINodes($(this).val(), $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').val());
- });
- $('select', '#form-guest-edit-pci').selectpicker();
- $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').on('keyup', function() {
- filterPCINodes($('select', '#form-guest-edit-pci').val(),
$(this).val());
- });
- };
- var setupNode = function(arrPCIDevices, iconClass) {
+
+ var _generatePciDeviceHtml = function(devices, pciType) {
var pciEnabled = kimchi.capabilities.kernel_vfio;
- var pciDeviceName, pciDeviceProduct, pciDeviceProductDesc, pciDeviceVendor,
pciDeviceVendorDesc, pciDeviceStatus;
- for (var i = 0; i < arrPCIDevices.length; i++) {
- pciDeviceName = arrPCIDevices[i].name;
- pciDeviceProduct = arrPCIDevices[i].product;
- pciDeviceVendor = arrPCIDevices[i].vendor;
- pciDeviceStatus = (iconClass === 'fa-ban') ? 'enabled' :
'disabled';
- if (pciDeviceProduct !== null) {
- pciDeviceProductDesc = pciDeviceProduct.description;
- }
- if (pciDeviceVendor !== null) {
- pciDeviceVendorDesc = pciDeviceVendor.description;
- }
- var itemNode = $.parseHTML(wok.substitute($('#pci-tmpl').html(), {
- status: pciDeviceStatus,
- name: pciDeviceName,
- product: pciDeviceProductDesc,
- vendor: pciDeviceVendorDesc
+ var deviceHTml = devices.map(function(device, index) {
+ device.iconClass = (pciType === 'hostPCIs' ? 'fa-power-off' :
(pciType === 'vmPCIs' ? 'fa-ban' : 'fa-power-off'));
+ device.status = (pciType === 'vmPCIs' ? 'enabled' :
'disabled');
+ device.product = (device.product !== null ? device.product.description :
'');
+ device.vendor = (device.vendor !== null ? device.vendor.description :
'');
+ deviceHtml = $.parseHTML(wok.substitute($('#pci-tmpl').html(), {
+ status: device.status,
+ name: device.name,
+ product: device.product,
+ vendor: device.vendor
}));
- $('.body', '#form-guest-edit-pci').append(itemNode);
- pciEnabled || $('button', itemNode).remove();
- $('button i', itemNode).addClass(iconClass);
- if (kimchi.thisVMState === "running" &&
arrPCIDevices[i].vga3d) {
- $('button', itemNode).prop("disabled", true);
+ pciEnabled || $('button', deviceHtml).remove();
+ $('button > i', deviceHtml).addClass(device.iconClass);
+ if (kimchi.thisVMState === "running" && device.vga3d) {
+ $('button', deviceHtml).prop("disabled", true);
}
- $('button', itemNode).on('click', function(event) {
- event.preventDefault();
- var obj = $(this);
- var objIcon = obj.find('i');
- var id = obj.parent().parent().attr('id');
- if (objIcon.hasClass('fa-ban')) {
- kimchi.removeVMPCIDevice(kimchi.selectedGuest, id, function() {
- kimchi.getAvailableHostPCIDevices(function(arrPCIDevices) {
- kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs)
{
- for (var k = 0; k < arrPCIDevices.length; k++) {
- $('#' + arrPCIDevices[k].name +
'.item').removeClass('enabled').addClass('disabled');
- $('#' + arrPCIDevices[k].name + '
.action-area button
i').removeClass('fa-ban').addClass('fa-power-off');
- }
- for (var k = 0; k < vmPCIs.length; k++) {
- $('#' + arrPCIDevices[k].name +
'.item').removeClass('disabled').addClass('enabled');
- $('#' + arrPCIDevices[k].name + '
.action-area button
i').removeClass('fa-power-off').addClass('fa-ban');
- }
- });
- });
- //id is for the object that is being added back to the available
PCI devices
- filterPCINodes($('select',
'#form-guest-edit-pci').val(), $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').val());
- }, null);
- } else {
- kimchi.addVMPCIDevice(kimchi.selectedGuest, {
- name: id
- }, function() {
- kimchi.getAvailableHostPCIDevices(function(arrPCIDevices) {
- kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs)
{
- for (var k = 0; k < vmPCIs.length; k++) {
- $('#' + vmPCIs[k].name +
'.item').removeClass('disabled').addClass('enabled');
- $('#' + vmPCIs[k].name + ' .action-area
button i').removeClass('fa-power-off').addClass('fa-ban');
- }
- });
- });
- //id is for the object that is being removed from the available
PCI devices
- filterPCINodes($('select',
'#form-guest-edit-pci').val(), $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').val());
- }, null);
- }
- });
- kimchi.getPCIDeviceCompanions(pciDeviceName, function(infoData) {
+ kimchi.getPCIDeviceCompanions(device.name, function(infoData) {
var pciTitle = i18n['KCHVMED6007M'] + '\n';
var haveCompanions = false;
for (var p = 0; p < infoData.length; p++) {
@@ -552,9 +491,142 @@ kimchi.guest_edit_main = function() {
haveCompanions && $('.vendor', '#' +
infoData[q].parent).attr('title', pciTitle);
}
});
- }
+ device = deviceHtml[0].outerHTML;
+ $('.body', '#form-guest-edit-pci').append(device);
+ });
+ };
+ var getOngoingAttachingDevices = function() {
+ var result = {};
+ var devices = 'status=running&target_uri=' +
encodeURIComponent('^/plugins/kimchi/vms/' + kimchi.selectedGuest +
'/hostdevs');
+ kimchi.getTasksByFilter(devices, function(tasks) {
+ for (var i = 0; i < tasks.length; i++) {
+ if (tasks[i].message === 'Attaching PCI device') {
+ var selectedVm = tasks[i].target_uri.split('/');
+ selectedVm = selectedVm[4];
+ result[selectedVm] = tasks[i];
+
+ if (kimchi.trackingTasks.indexOf(tasks[i].id) >= 0) {
+ continue;
+ }
+
+ kimchi.trackTask(tasks[i].id, function(result) {
+ kimchi.getAvailableHostPCIDevices(function(arrPCIDevices) {
+ kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs)
{
+ for (var k = 0; k < vmPCIs.length; k++) {
+ $('#' + vmPCIs[k].name +
'.item').removeClass('disabled').addClass('enabled');
+ $('#' + vmPCIs[k].name + ' .action-area
button i').removeClass('fa-power-off').addClass('fa-ban');
+ }
+ });
+ });
+ $('#form-guest-edit-pci > .wok-mask').fadeOut(300,
function() {});
+ wok.message.success(i18n['KCHVMED6010M'],
'#alert-modal-container');
+ filterPCINodes($('select',
'#form-guest-edit-pci').val(), $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').val());
+ }, function(result) {
+ if (result['message']) {
+ var errText = result['message'];
+ } else {
+ var errText =
result['responseJSON']['reason'];
+ }
+ $('#form-guest-edit-pci > .wok-mask').fadeOut(300,
function() {});
+ filterPCINodes($('select',
'#form-guest-edit-pci').val(), $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').val());
+ result && wok.message.error(errText,
'#alert-modal-container');
+ }, function(result) {
+ $('#form-guest-edit-pci > .wok-mask').show();
+ });
+ }
+ }
+ }, null, true);
+ };
+ var getOngoingDetachingDevices = function() {
+ var result = {};
+ var devices = 'status=running&target_uri=' +
encodeURIComponent('^/plugins/kimchi/vms/' + kimchi.selectedGuest +
'/hostdevs');
+ kimchi.getTasksByFilter(devices, function(tasks) {
+ for (var i = 0; i < tasks.length; i++) {
+ if (tasks[i].message === 'Detaching device') {
+ var selectedVm = tasks[i].target_uri.split('/');
+ selectedVm = selectedVm[4];
+ result[selectedVm] = tasks[i];
+
+ if (kimchi.trackingTasks.indexOf(tasks[i].id) >= 0) {
+ continue;
+ }
+
+ kimchi.trackTask(tasks[i].id, function(result) {
+ kimchi.getAvailableHostPCIDevices(function(arrPCIDevices) {
+ kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs)
{
+ for (var k = 0; k < arrPCIDevices.length; k++) {
+ $('#' + arrPCIDevices[k].name +
'.item').removeClass('enabled').addClass('disabled');
+ $('#' + arrPCIDevices[k].name + '
.action-area button
i').removeClass('fa-ban').addClass('fa-power-off');
+ }
+ for (var k = 0; k < vmPCIs.length; k++) {
+ $('#' + arrPCIDevices[k].name +
'.item').removeClass('disabled').addClass('enabled');
+ $('#' + arrPCIDevices[k].name + '
.action-area button
i').removeClass('fa-power-off').addClass('fa-ban');
+ }
+ });
+ });
+ $('#form-guest-edit-pci > .wok-mask').fadeOut(300,
function() {});
+ wok.message.success(i18n['KCHVMED6011M'],
'#alert-modal-container');
+ //id is for the object that is being added back to the available
PCI devices
+ filterPCINodes($('select',
'#form-guest-edit-pci').val(), $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').val());
+ }, function(result) {
+ if (result['message']) {
+ var errText = result['message'];
+ } else {
+ var errText =
result['responseJSON']['reason'];
+ }
+ $('#form-guest-edit-pci > .wok-mask').fadeOut(300,
function() {});
+ filterPCINodes($('select',
'#form-guest-edit-pci').val(), $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').val());
+ result && wok.message.error(errText,
'#alert-modal-container');
+ }, function(result) {
+ $('#form-guest-edit-pci > .wok-mask').show();
+ });
+ }
+ }
+ }, null, true);
+ };
+ var pciDeviceButtonHandler = function() {
+ $('button', '#form-guest-edit-pci').on('click',
function(event) {
+ event.preventDefault();
+ var obj = $(this);
+ var objIcon = obj.find('i');
+ var id = obj.parent().parent().attr('id');
+ if (objIcon.hasClass('fa-ban')) {
+ kimchi.removeVMPCIDevice(kimchi.selectedGuest, id, function() {
+ getOngoingDetachingDevices();
+ }, function(err) {
+ wok.message.error(err.responseJSON.reason,
'#alert-modal-container');
+ });
+ } else {
+ kimchi.addVMPCIDevice(kimchi.selectedGuest, {
+ name: id
+ }, function() {
+ getOngoingAttachingDevices();
+ }, function(err) {
+ wok.message.error(err.responseJSON.reason,
'#alert-modal-container');
+ });
+ }
+ });
};
+ var setupPCIDevice = function() {
+ kimchi.getAvailableHostPCIDevices(function(hostPCIs) {
+ kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs) {
+ _generatePciDeviceHtml(hostPCIs, 'hostPCIs');
+ _generatePciDeviceHtml(vmPCIs, 'vmPCIs');
+ pciDeviceButtonHandler();
+ $('#form-guest-edit-pci > .wok-mask').fadeOut(300, function()
{});
+ });
+ });
+ $('select', '#form-guest-edit-pci').change(function() {
+ filterPCINodes($(this).val(), $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').val());
+ });
+ $('select', '#form-guest-edit-pci').selectpicker();
+ $('input#guest-edit-pci-filter',
'#form-guest-edit-pci').on('keyup', function() {
+ filterPCINodes($('select', '#form-guest-edit-pci').val(),
$(this).val());
+ });
+ };
+
+
var setupSnapshot = function() {
var currentSnapshot;
var setCurrentSnapshot = function(aSnapshot) {
diff --git a/ui/js/src/kimchi.guest_main.js b/ui/js/src/kimchi.guest_main.js
index 0e11aa1..605cd2d 100644
--- a/ui/js/src/kimchi.guest_main.js
+++ b/ui/js/src/kimchi.guest_main.js
@@ -315,7 +315,7 @@ kimchi.initClone = function() {
kimchi.listVmsAuto = function() {
- $('.wok-mask').removeClass('hidden');
+ $('#guests-root-container > .wok-mask').removeClass('hidden');
//Check if the actions button is opened or not,
//if opended stop the reload of the itens until closed
var $isDropdownOpened = $('[name="guest-actions"]
ul.dropdown-menu').is(":visible");
@@ -431,13 +431,13 @@ kimchi.listVmsAuto = function() {
});
});
}
- $('.wok-mask').fadeOut(300, function() {
+ $('#guests-root-container > .wok-mask').fadeOut(300,
function() {
});
} else {
$('.grid-control').addClass('hidden');
$('#guestListField').hide();
$('#noGuests').show();
- $('.wok-mask').fadeOut(300, function() {});
+ $('#guests-root-container > .wok-mask').fadeOut(300,
function() {});
}
}
@@ -446,8 +446,8 @@ kimchi.listVmsAuto = function() {
function(errorResponse, textStatus, errorThrown) {
if (errorResponse.responseJSON &&
errorResponse.responseJSON.reason) {
wok.message.error(errorResponse.responseJSON.reason);
- $('.wok-mask').fadeOut(300, function() {
- $('.wok-mask').addClass('hidden');
+ $('#guests-root-container > .wok-mask').fadeOut(300,
function() {
+ $('#guests-root-container >
.wok-mask').addClass('hidden');
});
}
kimchi.vmTimeout = window.setTimeout("kimchi.listVmsAuto();",
5000);
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index f18d42c..a9a468e 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -160,6 +160,14 @@
</div>
<div class="body"></div>
</div>
+ <div class="wok-mask" role="presentation"
class="hidden">
+ <div class="wok-mask-loader-container">
+ <div class="wok-mask-loading">
+ <div
class="wok-mask-loading-icon"></div>
+ <div
class="wok-mask-loading-text">$_("Loading")...</div>
+ </div>
+ </div>
+ </div>
</form>
<form role="tabpanel" id="form-guest-edit-snapshot"
class="guest-edit-snapshot tab-pane">
<div class="btn-group">
@@ -273,17 +281,7 @@
<label>{val}</label>
</div>
</script>
-<script id="pci-tmpl" type="text/html">
-<div class="item {status}" id="{name}">
- <span class="cell status column-pci-status">
- <i class="fa fa-power-off"></i>
- </span>
- <span class="cell name column-pci-name"
title="{name}">{name}</span>
- <span class="cell product column-product"
title="{product}">{product}</span>
- <span class="cell vendor column-vendor"
title="{vendor}">{vendor}</span>
- <span class="cell column-actions action-area"><button
class="btn btn-link"><i
class="fa"></i></button></span>
-</div>
-</script>
+<script id="pci-tmpl" type="text/html"><div class="item
{status}" id="{name}"><span class="cell status
column-pci-status"><i class="fa
fa-power-off"></i></span><span class="cell name
column-pci-name" title="{name}">{name}</span><span
class="cell product column-product"
title="{product}">{product}</span><span class="cell vendor
column-vendor" title="{vendor}">{vendor}</span><span
class="cell column-actions action-area"><button class="btn
btn-link"><i
class="fa"></i></button></span></div></script>
<script id="snapshot-tmpl" type="text/html">
<div class="item" id="{name}">
<span class="cell column-sel">
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index 4efc1ec..610debc 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -84,6 +84,8 @@
"KCHVMED6007M": "$_("Affected devices:")",
"KCHVMED6008M": "$_("More")",
"KCHVMED6009M": "$_("Less")",
+ "KCHVMED6010M": "$_("Successfully attached device to
VM")",
+ "KCHVMED6011M": "$_("Successfully detached device from
VM")",
"KCHNET6001M": "$_("unavailable")",
"KCHNET6002M": "$_("This action will interrupt network
connectivity for any virtual machine that depend on this network.")",
--
1.9.3