[Kimchi-devel] [PATCH v2] [Kimchi] Issue #956: Unable to assign pci passthrough devices through kimchi

sguimaraes943 at gmail.com sguimaraes943 at gmail.com
Thu Jul 21 20:49:35 UTC 2016


From: Samuel Guimarães <sguimaraes943 at 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 at 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




More information about the Kimchi-devel mailing list