[PATCH] [Kimchi] Disable vm statistics/screenshots in edit guest
by Jose Ricardo Ziviani
- When Edit Guest modal window is opened, Kimchi continues to make
requests to get all VM' statistics and screenshots, that affects
necessary and heavy requests like PCI queries. This commit
disable those poolings when the modal window is opened and
re-enable when the modal is closed.
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.guest_edit_main.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 9099f39..507ae75 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -20,6 +20,11 @@ kimchi.guest_edit_main = function() {
var formTargetId;
var guestEditForm = $('#form-guest-edit-general');
var saveButton = $('#guest-edit-button-save');
+ clearTimeout(kimchi.vmTimeout);
+
+ $('#modalWindow').on('hidden.bs.modal', function() {
+ kimchi.vmTimeout = window.setTimeout("kimchi.listVmsAuto();", 5000);
+ });
$('#guest-edit-window a[data-toggle="tab"]').on('show.bs.tab', function(tab) {
tab.target; // newly activated tab
--
2.7.4
8 years, 4 months
[PATCH] [Wok] Externalise request types in advanced search panel of user activity log
by pkulkark@linux.vnet.ibm.com
From: Pooja Kulkarni <pkulkark(a)linux.vnet.ibm.com>
This patch externalises the request
types in the dropdown list in the
advanced search panel of user activity
log.
Signed-off-by: Pooja Kulkarni <pkulkark(a)linux.vnet.ibm.com>
---
ui/pages/tabs/settings-search.html.tmpl | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/ui/pages/tabs/settings-search.html.tmpl b/ui/pages/tabs/settings-search.html.tmpl
index 16a98b6..0bf664f 100644
--- a/ui/pages/tabs/settings-search.html.tmpl
+++ b/ui/pages/tabs/settings-search.html.tmpl
@@ -42,11 +42,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
<div class="form-group">
<label for="request-type">$_("Request")</label>
<select class="form-control selectpicker" id="request-type" title="$_('Request Type')" name="req">
- <option value="">All Requests</option>
- <option value="POST">POST</option>
- <option value="GET">GET</option>
- <option value="PUT">PUT</option>
- <option value="DELETE">DELETE</option>
+ <option value="">$_("All Requests")</option>
+ <option value="POST">$_("POST")</option>
+ <option value="GET">$_("GET")</option>
+ <option value="PUT">$_("PUT")</option>
+ <option value="DELETE">$_("DELETE")</option>
</select>
</div>
<div class="row clearfix">
--
2.1.0
8 years, 4 months
[PATCH] [Kimchi] Issue #968: Kimchi is searching for 'undefined' VM
by sguimaraes943@gmail.com
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
This commit also fixes an issue with an undefined variable with wok.substitute() when quickly switching from Guests to Templates tab.
Samuel Guimarães (1):
Issue #968: Kimchi is searching for 'undefined' VM
ui/js/src/kimchi.guest_main.js | 919 +++++++++++++++++++-------------------
ui/js/src/kimchi.template_main.js | 4 +-
2 files changed, 463 insertions(+), 460 deletions(-)
--
1.9.3
8 years, 4 months
[PATCH v4][Kimchi 0/6] Make Kimchi able to change guest's boot order
by Ramon Medeiros
Changes:
v4:
Add bootorder to REST GET
Add tests for REST API
v3:
Update test on test_model to keep working
v2:
Do not manipulate xml on model
Improve parameters checking at API.json
Increase test cases
Ramon Medeiros (6):
Add function get_bootorder_node
Create method to change bootorder of a guest
Update documentation about bootorder on vm update
Update REST API
Add function to retrieve bootorder on vm lookup
Add test to check bootorder
API.json | 23 +++++++++++++++++++++++
docs/API.md | 2 ++
i18n.py | 1 +
model/vms.py | 32 ++++++++++++++++++++++++++++++--
tests/test_mockmodel.py | 2 +-
tests/test_model.py | 15 ++++++++++++++-
tests/test_rest.py | 11 +++++++++++
xmlutils/bootorder.py | 19 +++++++++++++------
8 files changed, 95 insertions(+), 10 deletions(-)
--
2.5.5
8 years, 4 months
[PATCH v4] [Kimchi 00/10] Virt-Viewer launcher backend
by dhbarboza82@gmail.com
From: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
v4:
- changed NotFoundError to InvalidOperation in check_if_vm_running (patch 4/10)
- added mockmodel and test_rest changes (patch 10/10)
v3:
- fixed 'virviewerfiles' dir to 'virtviewerfiles' in Makefile.am
v2:
- fixed typo and capital letters in i18n.py
- fixed 'double dict' error in virtviewer module
- added tests with virtual machine containing utf-8 characters
This patch set adds the Virt-Viewer launcher backend to
Kimchi.
This feature consists of a new resource located in:
**URI:** /plugins/kimchi/vms/*:name*/snapshots/current
that retrieves a download link to a .vv file to be
used by a Virt-Viewer compatible desktop app to connect
to the remote virtual machine.
This backend takes cares of handling firewall rules to
allow the connection to be succesfull. Note that no firewall
port will be opened unless a download is made - if the user
decides to use noVNC or spice-html5 instead Kimchi will
not touch the host firewall.
Example:
[danielhb@arthas kimchi]$ curl -u root -H "Content-Type: application/json" -H "Accept: application/json" -X GET "http://localhost:8010/plugins/kimchi/vms/OpenSUSE-Leap-42.1/virtviewerfile"
Enter host password for user 'root':
[virt-viewer]
type=vnc
host=localhost
port=5904
After this call, port 5904 was opened in the host to allow for a
virt-viewer connection.
When shutting down the virtual machine or WoK, a cleanup is made
to close any ports left opened.
Daniel Henrique Barboza (10):
Virt-Viewer launcher: docs and i18n changes
Virt-Viewer launcher: Makefile and config changes
Virt-Viewer launcher: control/vms.py and model/vms.py changes
Virt-Viewer launcher: virtviewerfile module
Virt-Viewer launcher: test changes
Virt-Viewer launcher: adding FirewallManager class
Virt-Viewer launcher: test changes for firewall manager
Virt-Viewer launcher: libvirt events to control firewall
Virt-Viewer launcher: changes after adding libvirt event listening
Virt-Viewer launcher: mockmodel changes
Makefile.am | 2 +
config.py.in | 10 ++-
control/vms.py | 12 +++
docs/API.md | 6 ++
i18n.py | 2 +
mockmodel.py | 23 +++++
model/virtviewerfile.py | 234 ++++++++++++++++++++++++++++++++++++++++++++++++
model/vms.py | 9 +-
tests/test_config.py.in | 6 ++
tests/test_model.py | 228 +++++++++++++++++++++++++++++++++++++++++++++-
tests/test_rest.py | 12 +++
11 files changed, 538 insertions(+), 6 deletions(-)
create mode 100644 model/virtviewerfile.py
--
2.5.5
8 years, 4 months
[PATCH] [Kimchi] Kimchi kills Wokd due to sys.exit() calls in files networks.py and storagepools.py
by bianca@linux.vnet.ibm.com
From: Bianca Carvalho <bianca(a)linux.vnet.ibm.com>
Removed all sys.exit(1) calls from both files to avoid killing Wokd.
Also added an Exception to not load kimchi plugin in case network or
storagepool does not exist or is not active.
Signed-off-by: Bianca Carvalho <bianca(a)linux.vnet.ibm.com>
---
model/networks.py | 16 +++++++---------
model/storagepools.py | 19 ++++++++-----------
2 files changed, 15 insertions(+), 20 deletions(-)
diff --git a/model/networks.py b/model/networks.py
index 4b722d3..35431d4 100644
--- a/model/networks.py
+++ b/model/networks.py
@@ -20,7 +20,6 @@
import copy
import ipaddr
import libvirt
-import sys
import time
from libvirt import VIR_INTERFACE_XML_INACTIVE
@@ -59,19 +58,19 @@ class NetworksModel(object):
networks = list(set(tmpl_defaults['networks']))
conn = self.conn.get()
- error_msg = ("Please, check the configuration in %s/template.conf to "
- "ensure it lists only valid networks." %
- kimchiPaths.sysconf_dir)
-
for net_name in networks:
+ error_msg = ("Network %s does not exist or is not "
+ "active. Please, check the configuration in "
+ "%s/template.conf to ensure it lists only valid "
+ "networks." % (net_name, kimchiPaths.sysconf_dir))
+
try:
net = conn.networkLookupByName(net_name)
except libvirt.libvirtError, e:
msg = "Fatal: Unable to find network %s."
wok_log.error(msg, net_name)
- wok_log.error(error_msg)
wok_log.error("Details: %s", e.message)
- sys.exit(1)
+ raise Exception(error_msg)
if net.isActive() == 0:
try:
@@ -79,9 +78,8 @@ class NetworksModel(object):
except libvirt.libvirtError as e:
msg = "Fatal: Unable to activate network %s."
wok_log.error(msg, net_name)
- wok_log.error(error_msg)
wok_log.error("Details: %s", e.message)
- sys.exit(1)
+ raise Exception(error_msg)
def create(self, params):
conn = self.conn.get()
diff --git a/model/storagepools.py b/model/storagepools.py
index 3f6a734..a2dbaec 100644
--- a/model/storagepools.py
+++ b/model/storagepools.py
@@ -19,7 +19,6 @@
import libvirt
import lxml.etree as ET
-import sys
from lxml.builder import E
from wok.exception import InvalidOperation, MissingParameter
@@ -82,21 +81,21 @@ class StoragePoolsModel(object):
if config.get('kimchi', {}).get('create_iso_pool', False):
pools['ISO'] = {'path': '/var/lib/kimchi/isos'}
- error_msg = ("Please, check the configuration in %s/template.conf to "
- "ensure it has a valid storage pool." %
- kimchiPaths.sysconf_dir)
-
conn = self.conn.get()
for pool_name in pools:
+ error_msg = ("Storage pool %s does not exist or is not "
+ "active. Please, check the configuration in "
+ "%s/template.conf to ensure it lists only valid "
+ "networks." % (pool_name, kimchiPaths.sysconf_dir))
try:
pool = conn.storagePoolLookupByName(pool_name)
except libvirt.libvirtError, e:
pool_path = pools[pool_name].get('path')
if pool_path is None:
- msg = "Fatal: Unable to find storage pool %s. " + error_msg
+ msg = "Fatal: Unable to find storage pool %s. "
wok_log.error(msg % pool_name)
wok_log.error("Details: %s", e.message)
- sys.exit(1)
+ raise Exception(error_msg)
# Try to create the pool
pool = E.pool(E.name(pool_name), type='dir')
@@ -106,10 +105,9 @@ class StoragePoolsModel(object):
pool = conn.storagePoolDefineXML(xml, 0)
except libvirt.libvirtError, e:
msg = "Fatal: Unable to create storage pool %s. "
- msg += error_msg
wok_log.error(msg % pool_name)
wok_log.error("Details: %s", e.message)
- sys.exit(1)
+ raise Exception(error_msg)
# Build and set autostart value to pool
# Ignore error as the pool was already successfully created
@@ -127,10 +125,9 @@ class StoragePoolsModel(object):
pool.create(0)
except libvirt.libvirtError, e:
msg = "Fatal: Unable to craete storage pool %s. "
- msg += error_msg
wok_log.error(msg % pool_name)
wok_log.error("Details: %s", e.message)
- sys.exit(1)
+ raise Exception(error_msg)
def get_list(self):
try:
--
2.7.4
8 years, 4 months
[PATCH v2] [Kimchi] Handle libvirt events for device attachment/detachment
by Jose Ricardo Ziviani
- kimchi will only finish the device attachment/detachment after
receiving the appropriate event from libvirt.
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.com>
---
v2:
- removed functions to deregister events (unused functions)
- improved the event handler
- add a lock for tasks
Depends on [Kimchi] Issue #956: Unable to assign pci passthrough devices through kimchi
model/libvirtevents.py | 28 ++++
model/vmhostdevs.py | 382 ++++++++++++++++++++++++++++---------------------
2 files changed, 248 insertions(+), 162 deletions(-)
diff --git a/model/libvirtevents.py b/model/libvirtevents.py
index dfd22c6..2dd9904 100644
--- a/model/libvirtevents.py
+++ b/model/libvirtevents.py
@@ -89,3 +89,31 @@ class LibvirtEvents(object):
else:
reason = e.message
wok_log.error("Register of ENOSPC event failed: %s" % reason)
+
+ def registerAttachDevicesEvent(self, conn, cb, arg):
+ """
+ register libvirt event to listen to devices attachment
+ """
+ try:
+ return conn.get().domainEventRegisterAny(
+ None,
+ libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_ADDED,
+ cb,
+ arg)
+
+ except libvirt.libvirtError as e:
+ wok_log.error("register attach event failed: %s" % e.message)
+
+ def registerDetachDevicesEvent(self, conn, cb, arg):
+ """
+ register libvirt event to listen to devices detachment
+ """
+ try:
+ return conn.get().domainEventRegisterAny(
+ None,
+ libvirt.VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED,
+ cb,
+ arg)
+
+ except libvirt.libvirtError as e:
+ wok_log.error("register detach event failed: %s" % e.message)
diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py
index b57cbf0..5a63bb1 100644
--- a/model/vmhostdevs.py
+++ b/model/vmhostdevs.py
@@ -21,6 +21,7 @@ import glob
import libvirt
import os
import platform
+import threading
from lxml import etree, objectify
from lxml.builder import E, ElementMaker
from operator import itemgetter
@@ -47,8 +48,14 @@ class VMHostDevsModel(object):
def __init__(self, **kargs):
self.conn = kargs['conn']
self.objstore = kargs['objstore']
+ self.events = kargs['eventsloop']
self.caps = CapabilitiesModel(**kargs)
self.task = TaskModel(**kargs)
+ self._cb = None
+ self.events.registerAttachDevicesEvent(
+ self.conn,
+ self._event_devices,
+ self)
def get_list(self, vmid):
dom = VMModel.get_vm(vmid, self.conn)
@@ -67,6 +74,17 @@ class VMHostDevsModel(object):
if dev_name not in eligible_dev_names:
raise InvalidParameter('KCHVMHDEV0002E', {'dev_name': dev_name})
+ def _event_devices(self, conn, dom, alias, opaque):
+ """
+ Callback to handle add/remove devices event
+ """
+ if opaque._cb is None:
+ wok_log.error('opaque must be valid')
+ return
+
+ wok_log.info("Device %s added successfuly" % alias)
+ opaque._cb('OK', True)
+
def create(self, vmid, params):
dev_name = params['name']
self._passthrough_device_validate(dev_name)
@@ -76,7 +94,8 @@ class VMHostDevsModel(object):
taskid = add_task(u'/plugins/kimchi/vms/%s/hostdevs/' %
VMModel.get_vm(vmid, self.conn).name(),
self._attach_pci_device, self.objstore,
- {'vmid': vmid, 'dev_info': dev_info})
+ {'vmid': vmid, 'dev_info': dev_info,
+ 'lock': threading.RLock()})
return self.task.lookup(taskid)
with RollbackContext() as rollback:
@@ -93,7 +112,8 @@ class VMHostDevsModel(object):
taskid = add_task(u'/plugins/kimchi/vms/%s/hostdevs/' %
VMModel.get_vm(vmid, self.conn).name(),
'_attach_%s_device' % dev_info['device_type'],
- self.objstore, {'vmid': vmid, 'dev_info': dev_info})
+ self.objstore, {'vmid': vmid, 'dev_info': dev_info,
+ 'lock': threading.RLock()})
return self.task.lookup(taskid)
@@ -189,112 +209,118 @@ class VMHostDevsModel(object):
def _attach_pci_device(self, cb, params):
cb('Attaching PCI device')
+ self._cb = cb
vmid = params['vmid']
dev_info = params['dev_info']
- self._validate_pci_passthrough_env()
-
- dom = VMModel.get_vm(vmid, self.conn)
- # Due to libvirt limitation, we don't support live assigne device to
- # vfio driver.
- driver = ('vfio' if DOM_STATE_MAP[dom.info()[0]] == "shutoff" and
- self.caps.kernel_vfio else 'kvm')
-
- # on powerkvm systems it must be vfio driver.
- distro, _, _ = platform.linux_distribution()
- if distro == 'IBM_PowerKVM':
- driver = 'vfio'
+ lock = params['lock']
+
+ with lock:
+ self._validate_pci_passthrough_env()
+
+ dom = VMModel.get_vm(vmid, self.conn)
+ # Due to libvirt limitation, we don't support live assigne device
+ # to vfio driver.
+ driver = ('vfio' if DOM_STATE_MAP[dom.info()[0]] == "shutoff" and
+ self.caps.kernel_vfio else 'kvm')
+
+ # on powerkvm systems it must be vfio driver.
+ distro, _, _ = platform.linux_distribution()
+ if distro == 'IBM_PowerKVM':
+ driver = 'vfio'
+
+ # Attach all PCI devices in the same IOMMU group
+ dev_model = DeviceModel(conn=self.conn)
+ devs_model = DevicesModel(conn=self.conn)
+ affected_names = devs_model.get_list(
+ _passthrough_affected_by=dev_info['name'])
+ passthrough_names = devs_model.get_list(
+ _cap='pci', _passthrough='true')
+ group_names = list(set(affected_names) & set(passthrough_names))
+ pci_infos = [dev_model.lookup(dev_name) for dev_name in
+ group_names]
+ pci_infos.append(dev_info)
+
+ is_multifunction = len(pci_infos) > 1
+ pci_infos = sorted(pci_infos, key=itemgetter('name'))
+
+ # does not allow hot-plug of 3D graphic cards
+ is_3D_device = dev_model.is_device_3D_controller(dev_info)
+ if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff":
+ raise InvalidOperation('KCHVMHDEV0006E',
+ {'name': dev_info['name']})
+
+ # all devices in the group that is going to be attached to the vm
+ # must be detached from the host first
+ with RollbackContext() as rollback:
+ for pci_info in pci_infos:
+ try:
+ dev = self.conn.get().nodeDeviceLookupByName(
+ pci_info['name'])
+ dev.dettach()
+ except Exception:
+ raise OperationFailed('KCHVMHDEV0005E',
+ {'name': pci_info['name']})
+ else:
+ rollback.prependDefer(dev.reAttach)
- # Attach all PCI devices in the same IOMMU group
- dev_model = DeviceModel(conn=self.conn)
- devs_model = DevicesModel(conn=self.conn)
- affected_names = devs_model.get_list(
- _passthrough_affected_by=dev_info['name'])
- passthrough_names = devs_model.get_list(
- _cap='pci', _passthrough='true')
- group_names = list(set(affected_names) & set(passthrough_names))
- pci_infos = [dev_model.lookup(dev_name) for dev_name in group_names]
- pci_infos.append(dev_info)
-
- is_multifunction = len(pci_infos) > 1
- pci_infos = sorted(pci_infos, key=itemgetter('name'))
-
- # does not allow hot-plug of 3D graphic cards
- is_3D_device = dev_model.is_device_3D_controller(dev_info)
- if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff":
- raise InvalidOperation('KCHVMHDEV0006E',
- {'name': dev_info['name']})
-
- # all devices in the group that is going to be attached to the vm
- # must be detached from the host first
- with RollbackContext() as rollback:
- for pci_info in pci_infos:
- try:
- dev = self.conn.get().nodeDeviceLookupByName(
- pci_info['name'])
- dev.dettach()
- except Exception:
- raise OperationFailed('KCHVMHDEV0005E',
- {'name': pci_info['name']})
- else:
- rollback.prependDefer(dev.reAttach)
-
- rollback.commitAll()
-
- device_flags = get_vm_config_flag(dom, mode='all')
-
- # when attaching a 3D graphic device it might be necessary to increase
- # the window size memory in order to be able to attach more than one
- # device to the same guest
- if is_3D_device:
- self.update_mmio_guest(vmid, True)
-
- slot = 0
- if is_multifunction:
- # search for the first available slot in guest xml
- slot = self._available_slot(dom)
-
- with RollbackContext() as rollback:
- # multifunction hotplug is a special case where all functions
- # must be attached together within one xml file, the same does
- # not happen to multifunction coldplug - where each function is
- # attached individually
- if DOM_STATE_MAP[dom.info()[0]] != 'shutoff' and is_multifunction:
- xmlstr = self._get_pci_devices_xml(pci_infos, slot, driver)
-
- try:
- dom.attachDeviceFlags(xmlstr, device_flags)
+ rollback.commitAll()
- except libvirt.libvirtError:
- wok_log.error(
- 'Failed to attach mutifunction device VM %s: \n%s',
- vmid, xmlstr)
- raise
+ device_flags = get_vm_config_flag(dom, mode='all')
- rollback.prependDefer(dom.detachDeviceFlags, xmlstr,
- device_flags)
+ # when attaching a 3D graphic device it might be necessary to
+ # increase the window size memory in order to be able to attach
+ # more than one device to the same guest
+ if is_3D_device:
+ self.update_mmio_guest(vmid, True)
+
+ slot = 0
+ if is_multifunction:
+ # search for the first available slot in guest xml
+ slot = self._available_slot(dom)
+
+ with RollbackContext() as rollback:
+ # multifunction hotplug is a special case where all functions
+ # must be attached together within one xml file, the same does
+ # not happen to multifunction coldplug - where each function
+ # is attached individually
+ if DOM_STATE_MAP[dom.info()[0]] != 'shutoff' and \
+ is_multifunction:
+ xmlstr = self._get_pci_devices_xml(pci_infos, slot, driver)
+
+ try:
+ dom.attachDeviceFlags(xmlstr, device_flags)
+
+ except libvirt.libvirtError:
+ wok_log.error(
+ 'Failed to attach mutifunction device VM %s: \n%s',
+ vmid, xmlstr)
+ raise
+
+ rollback.prependDefer(dom.detachDeviceFlags, xmlstr,
+ device_flags)
+ rollback.commitAll()
+ if DOM_STATE_MAP[dom.info()[0]] == "shutoff":
+ cb('OK', True)
+ return
+
+ for pci_info in pci_infos:
+ pci_info['detach_driver'] = driver
+ xmlstr = self._get_pci_device_xml(pci_info,
+ slot,
+ is_multifunction)
+ try:
+ dom.attachDeviceFlags(xmlstr, device_flags)
+ except libvirt.libvirtError:
+ wok_log.error(
+ 'Failed to attach host device %s to VM %s: \n%s',
+ pci_info['name'], vmid, xmlstr)
+ raise
+ rollback.prependDefer(dom.detachDeviceFlags,
+ xmlstr, device_flags)
rollback.commitAll()
- cb('OK', True)
- return
- for pci_info in pci_infos:
- pci_info['detach_driver'] = driver
- cb('Reading source device XML')
- xmlstr = self._get_pci_device_xml(pci_info,
- slot,
- is_multifunction)
- try:
- cb('Attaching device to VM')
- dom.attachDeviceFlags(xmlstr, device_flags)
- except libvirt.libvirtError:
- wok_log.error(
- 'Failed to attach host device %s to VM %s: \n%s',
- pci_info['name'], vmid, xmlstr)
- raise
- rollback.prependDefer(dom.detachDeviceFlags,
- xmlstr, device_flags)
- rollback.commitAll()
-
- cb('OK', True)
+ if DOM_STATE_MAP[dom.info()[0]] == "shutoff":
+ cb('OK', True)
def _count_3D_devices_attached(self, dom):
counter = 0
@@ -403,25 +429,31 @@ class VMHostDevsModel(object):
def _attach_scsi_device(self, cb, params):
cb('Attaching SCSI device...')
+ self._cb = cb
vmid = params['vmid']
dev_info = params['dev_info']
- dom = VMModel.get_vm(vmid, self.conn)
+ lock = params['lock']
- with RollbackContext() as rollback:
- cb('Reading source device XML')
- xmlstr = self._get_scsi_device_xml(dev_info)
- device_flags = get_vm_config_flag(dom, mode='all')
- try:
- cb('Attaching device to VM')
- dom.attachDeviceFlags(xmlstr, device_flags)
- except libvirt.libvirtError:
- wok_log.error('Failed to attach host device %s to VM %s: \n%s',
- dev_info['name'], vmid, xmlstr)
- raise
- rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags)
- rollback.commitAll()
+ with lock:
+ dom = VMModel.get_vm(vmid, self.conn)
+
+ with RollbackContext() as rollback:
+ xmlstr = self._get_scsi_device_xml(dev_info)
+ device_flags = get_vm_config_flag(dom, mode='all')
+ try:
+ cb('Attaching device to VM')
+ dom.attachDeviceFlags(xmlstr, device_flags)
+ except libvirt.libvirtError:
+ wok_log.error(
+ 'Failed to attach host device %s to VM %s: \n%s',
+ dev_info['name'], vmid, xmlstr)
+ raise
+ rollback.prependDefer(dom.detachDeviceFlags, xmlstr,
+ device_flags)
+ rollback.commitAll()
- cb('OK', True)
+ if DOM_STATE_MAP[dom.info()[0]] == "shutoff":
+ cb('OK', True)
def _get_usb_device_xml(self, dev_info):
source = E.source(
@@ -436,32 +468,43 @@ class VMHostDevsModel(object):
def _attach_usb_device(self, cb, params):
cb('Attaching USB device...')
+ self._cb = cb
vmid = params['vmid']
dev_info = params['dev_info']
dom = VMModel.get_vm(vmid, self.conn)
+ lock = params['lock']
- with RollbackContext() as rollback:
- cb('Reading source device XML')
- xmlstr = self._get_usb_device_xml(dev_info)
- device_flags = get_vm_config_flag(dom, mode='all')
- try:
- cb('Attaching device to VM')
- dom.attachDeviceFlags(xmlstr, device_flags)
- except libvirt.libvirtError:
- wok_log.error('Failed to attach host device %s to VM %s: \n%s',
- dev_info['name'], vmid, xmlstr)
- raise
- rollback.prependDefer(dom.detachDeviceFlags, xmlstr, device_flags)
- rollback.commitAll()
+ with lock:
+ with RollbackContext() as rollback:
+ xmlstr = self._get_usb_device_xml(dev_info)
+ device_flags = get_vm_config_flag(dom, mode='all')
+ try:
+ cb('Attaching device to VM')
+ dom.attachDeviceFlags(xmlstr, device_flags)
+ except libvirt.libvirtError:
+ wok_log.error(
+ 'Failed to attach host device %s to VM %s: \n%s',
+ dev_info['name'], vmid, xmlstr)
+ raise
+ rollback.prependDefer(dom.detachDeviceFlags, xmlstr,
+ device_flags)
+ rollback.commitAll()
- cb('OK', True)
+ if DOM_STATE_MAP[dom.info()[0]] == "shutoff":
+ cb('OK', True)
class VMHostDevModel(object):
def __init__(self, **kargs):
self.conn = kargs['conn']
self.objstore = kargs['objstore']
+ self.events = kargs['eventsloop']
self.task = TaskModel(**kargs)
+ self._cb = None
+ self.events.registerDetachDevicesEvent(
+ self.conn,
+ self._event_devices,
+ self)
def lookup(self, vmid, dev_name):
dom = VMModel.get_vm(vmid, self.conn)
@@ -502,57 +545,72 @@ class VMHostDevModel(object):
task_params = {'vmid': vmid,
'dev_name': dev_name,
'dom': dom,
- 'hostdev': hostdev}
+ 'hostdev': hostdev,
+ 'lock': threading.RLock()}
task_uri = u'/plugins/kimchi/vms/%s/hostdevs/%s' % \
(VMModel.get_vm(vmid, self.conn).name(), dev_name)
taskid = add_task(task_uri, self._detach_device, self.objstore,
task_params)
return self.task.lookup(taskid)
+ def _event_devices(self, conn, dom, alias, opaque):
+ """
+ Callback to handle add/remove devices event
+ """
+ if opaque._cb is None:
+ wok_log.error('opaque must be valid')
+ return
+
+ wok_log.info("Device %s removed successfuly" % alias)
+ opaque._cb('OK', True)
+
def _detach_device(self, cb, params):
cb('Detaching device')
+ self._cb = cb
vmid = params['vmid']
dev_name = params['dev_name']
dom = params['dom']
hostdev = params['hostdev']
+ lock = params['lock']
- pci_devs = [(DeviceModel.deduce_dev_name(e, self.conn), e)
- for e in hostdev if e.attrib['type'] == 'pci']
+ with lock:
+ pci_devs = [(DeviceModel.deduce_dev_name(e, self.conn), e)
+ for e in hostdev if e.attrib['type'] == 'pci']
- dev_model = DeviceModel(conn=self.conn)
- dev_info = dev_model.lookup(dev_name)
- is_3D_device = dev_model.is_device_3D_controller(dev_info)
- if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff":
- raise InvalidOperation('KCHVMHDEV0006E',
- {'name': dev_info['name']})
-
- if self._hotunplug_multifunction_pci(dom, hostdev, dev_name):
- if is_3D_device:
- cb('Updating MMIO from VM...')
- devsmodel = VMHostDevsModel(conn=self.conn)
- devsmodel.update_mmio_guest(vmid, False)
- cb('OK', True)
- return
+ dev_model = DeviceModel(conn=self.conn)
+ dev_info = dev_model.lookup(dev_name)
+ is_3D_device = dev_model.is_device_3D_controller(dev_info)
+ if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff":
+ raise InvalidOperation('KCHVMHDEV0006E',
+ {'name': dev_info['name']})
- for e in hostdev:
- if DeviceModel.deduce_dev_name(e, self.conn) == dev_name:
- xmlstr = etree.tostring(e)
- cb('Detaching device from VM...')
- dom.detachDeviceFlags(
- xmlstr, get_vm_config_flag(dom, mode='all'))
- if e.attrib['type'] == 'pci':
- cb('Deleting affected PCI devices...')
- self._delete_affected_pci_devices(dom, dev_name, pci_devs)
+ if self._hotunplug_multifunction_pci(dom, hostdev, dev_name):
if is_3D_device:
- cb('Updating MMIO from VM...')
devsmodel = VMHostDevsModel(conn=self.conn)
devsmodel.update_mmio_guest(vmid, False)
- break
- else:
- raise NotFoundError('KCHVMHDEV0001E',
- {'vmid': vmid, 'dev_name': dev_name})
- cb('OK', True)
+ if DOM_STATE_MAP[dom.info()[0]] == "shutoff":
+ cb('OK', True)
+ return
+
+ for e in hostdev:
+ if DeviceModel.deduce_dev_name(e, self.conn) == dev_name:
+ xmlstr = etree.tostring(e)
+ dom.detachDeviceFlags(
+ xmlstr, get_vm_config_flag(dom, mode='all'))
+ if e.attrib['type'] == 'pci':
+ self._delete_affected_pci_devices(dom, dev_name,
+ pci_devs)
+ if is_3D_device:
+ devsmodel = VMHostDevsModel(conn=self.conn)
+ devsmodel.update_mmio_guest(vmid, False)
+ break
+ else:
+ raise NotFoundError('KCHVMHDEV0001E',
+ {'vmid': vmid, 'dev_name': dev_name})
+
+ if DOM_STATE_MAP[dom.info()[0]] == "shutoff":
+ cb('OK', True)
def _get_devices_same_addr(self, hostdev, domain, bus, slot):
devices = []
--
2.7.4
8 years, 4 months
[PATCH] [Kimchi] Send (de)attach fail messages to tasks
by Jose Ricardo Ziviani
- Today Kimchi is throwing exceptions when something wrong happens to
device attachment/detachment but it also needs to notify the
front-end by task callbacks, this commit implements it.
---
Depends on [PATCH v2] [Kimchi] Handle libvirt events for device
attachment/detachment
i18n.py | 1 +
model/vmhostdevs.py | 40 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/i18n.py b/i18n.py
index 08a3ac8..673390e 100644
--- a/i18n.py
+++ b/i18n.py
@@ -141,6 +141,7 @@ messages = {
"KCHVMHDEV0004E": _('"name" should be a device name string'),
"KCHVMHDEV0005E": _('The device %(name)s is probably in use by the host. Unable to attach it to the guest.'),
"KCHVMHDEV0006E": _('Hot-(un)plug of device %(name)s is not supported.'),
+ "KCHVMHDEV0007E": _('Failed to attach %(device)s to %(vm)s'),
"KCHVMIF0001E": _("Interface %(iface)s does not exist in virtual machine %(name)s"),
"KCHVMIF0002E": _("Network %(network)s specified for virtual machine %(name)s does not exist"),
diff --git a/model/vmhostdevs.py b/model/vmhostdevs.py
index 5a63bb1..70f0b17 100644
--- a/model/vmhostdevs.py
+++ b/model/vmhostdevs.py
@@ -28,6 +28,7 @@ from operator import itemgetter
from wok.exception import InvalidOperation, InvalidParameter, NotFoundError
from wok.exception import OperationFailed
+from wok.message import WokMessage
from wok.model.tasks import TaskModel
from wok.rollbackcontext import RollbackContext
from wok.utils import add_task, run_command, wok_log
@@ -215,7 +216,12 @@ class VMHostDevsModel(object):
lock = params['lock']
with lock:
- self._validate_pci_passthrough_env()
+ try:
+ self._validate_pci_passthrough_env()
+
+ except InvalidOperation as e:
+ cb(e.message, False)
+ raise
dom = VMModel.get_vm(vmid, self.conn)
# Due to libvirt limitation, we don't support live assigne device
@@ -246,6 +252,8 @@ class VMHostDevsModel(object):
# does not allow hot-plug of 3D graphic cards
is_3D_device = dev_model.is_device_3D_controller(dev_info)
if is_3D_device and DOM_STATE_MAP[dom.info()[0]] != "shutoff":
+ msg = WokMessage('KCHVMHDEV0006E', {'name': dev_info['name']})
+ cb(msg.get_text(), False)
raise InvalidOperation('KCHVMHDEV0006E',
{'name': dev_info['name']})
@@ -258,6 +266,9 @@ class VMHostDevsModel(object):
pci_info['name'])
dev.dettach()
except Exception:
+ msg = WokMessage('KCHVMHDEV0005E',
+ {'name': pci_info['name']})
+ cb(msg.get_text(), False)
raise OperationFailed('KCHVMHDEV0005E',
{'name': pci_info['name']})
else:
@@ -291,6 +302,10 @@ class VMHostDevsModel(object):
dom.attachDeviceFlags(xmlstr, device_flags)
except libvirt.libvirtError:
+ msg = WokMessage('KCHVMHDEV0007E',
+ {'device': pci_info['name'],
+ 'vm': vmid})
+ cb(msg.get_text(), False)
wok_log.error(
'Failed to attach mutifunction device VM %s: \n%s',
vmid, xmlstr)
@@ -310,13 +325,20 @@ class VMHostDevsModel(object):
is_multifunction)
try:
dom.attachDeviceFlags(xmlstr, device_flags)
+
except libvirt.libvirtError:
+ msg = WokMessage('KCHVMHDEV0007E',
+ {'device': pci_info['name'],
+ 'vm': vmid})
+ cb(msg.get_text(), False)
wok_log.error(
'Failed to attach host device %s to VM %s: \n%s',
pci_info['name'], vmid, xmlstr)
raise
+
rollback.prependDefer(dom.detachDeviceFlags,
xmlstr, device_flags)
+
rollback.commitAll()
if DOM_STATE_MAP[dom.info()[0]] == "shutoff":
@@ -443,11 +465,17 @@ class VMHostDevsModel(object):
try:
cb('Attaching device to VM')
dom.attachDeviceFlags(xmlstr, device_flags)
+
except libvirt.libvirtError:
+ msg = WokMessage('KCHVMHDEV0007E',
+ {'device': dev_info['name'],
+ 'vm': vmid})
+ cb(msg.get_text(), False)
wok_log.error(
'Failed to attach host device %s to VM %s: \n%s',
dev_info['name'], vmid, xmlstr)
raise
+
rollback.prependDefer(dom.detachDeviceFlags, xmlstr,
device_flags)
rollback.commitAll()
@@ -481,11 +509,17 @@ class VMHostDevsModel(object):
try:
cb('Attaching device to VM')
dom.attachDeviceFlags(xmlstr, device_flags)
+
except libvirt.libvirtError:
+ msg = WokMessage('KCHVMHDEV0007E',
+ {'device': dev_info['name'],
+ 'vm': vmid})
+ cb(msg.get_text(), False)
wok_log.error(
'Failed to attach host device %s to VM %s: \n%s',
dev_info['name'], vmid, xmlstr)
raise
+
rollback.prependDefer(dom.detachDeviceFlags, xmlstr,
device_flags)
rollback.commitAll()
@@ -538,6 +572,7 @@ class VMHostDevModel(object):
try:
hostdev = root.devices.hostdev
+
except AttributeError:
raise NotFoundError('KCHVMHDEV0001E',
{'vmid': vmid, 'dev_name': dev_name})
@@ -606,6 +641,9 @@ class VMHostDevModel(object):
devsmodel.update_mmio_guest(vmid, False)
break
else:
+ msg = WokMessage('KCHVMHDEV0001E',
+ {'vmid': vmid, 'dev_name': dev_name})
+ cb(msg.get_text(), False)
raise NotFoundError('KCHVMHDEV0001E',
{'vmid': vmid, 'dev_name': dev_name})
--
2.7.4
8 years, 4 months
[PATCH v3] [Kimchi] 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).
by Jose Ricardo Ziviani
From: Samuel Guimarães <sguimaraes943(a)gmail.com>
Signed-off-by: Samuel Guimarães <sguimaraes943(a)gmail.com>
Signed-off-by: Jose Ricardo Ziviani <joserz(a)linux.vnet.ibm.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 | 204 +++++++++++++++++++++--------------
ui/js/src/kimchi.guest_main.js | 10 +-
ui/pages/guest-edit.html.tmpl | 20 ++--
ui/pages/i18n.json.tmpl | 2 +
7 files changed, 148 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..cabd497 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,8 +491,109 @@ 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(task) {
+ kimchi.trackTask(task.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();
+ });
};
+ var getOngoingDetachingDevices = function(task) {
+ kimchi.trackTask(task.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();
+ });
+ };
+ 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(task) {
+ getOngoingDetachingDevices(task);
+ }, function(err) {
+ wok.message.error(err.responseJSON.reason, '#alert-modal-container');
+ });
+ } else {
+ kimchi.addVMPCIDevice(kimchi.selectedGuest, {
+ name: id
+ }, function(task) {
+ getOngoingAttachingDevices(task);
+ }, 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;
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.")",
--
2.7.4
8 years, 4 months