
Add a "vm_holders" sub-collection under host device resource, so the front-end can determine if a device is busy or not, and the user can know which VMs are holding the device. This patch scans all VM XML to check if a device is hold by a VM. Also adds a check to keep the host device assigned to only one VM. Example curl -k -u root -H "Content-Type: application/json" \ -H "Accept: application/json" \ 'https://127.0.0.1:8001/host/devices/usb_1_1_6/vm_holders' Should output a list like following. [ { "state":"shutoff", "name":"fedora20" }, { "state":"running", "name":"f20xfce-slave" } ] If there is no VM holding the device, it prints an empty list []. v5: When assigning a device to VM, check if there are other VMs holding the device and raise an exception. Move the VMHoldersModel to vmhostdevs.py to avoid circular import problem. Signed-off-by: Zhou Zheng Sheng <zhshzhou@linux.vnet.ibm.com> --- src/kimchi/control/host.py | 7 +++++++ src/kimchi/i18n.py | 2 ++ src/kimchi/model/vmhostdevs.py | 24 ++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/src/kimchi/control/host.py b/src/kimchi/control/host.py index 172f4fe..f2f1bfa 100644 --- a/src/kimchi/control/host.py +++ b/src/kimchi/control/host.py @@ -113,11 +113,18 @@ class Devices(Collection): self.resource = Device +class VMHolders(SimpleCollection): + def __init__(self, model, device_id): + super(VMHolders, self).__init__(model) + self.model_args = (device_id, ) + + class Device(Resource): def __init__(self, model, id): self.role_key = 'storage' self.admin_methods = ['GET'] super(Device, self).__init__(model, id) + self.vm_holders = VMHolders(self.model, id) @property def data(self): diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 4f8e691..b331770 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -99,6 +99,8 @@ messages = { "Please enable Intel VT-d or AMD IOMMU in your BIOS, then verify the Kernel is compiled with IOMMU support. " "For Intel CPU, add intel_iommu=on to your Kernel parameter in /boot/grub2/grub.conf. " "For AMD CPU, add iommu=pt iommu=1."), + "KCHVMHDEV0004E": _("The host device %(dev_name)s should be assigned to just one VM. " + "Currently the following VM(s) are holding the device: %(names)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/src/kimchi/model/vmhostdevs.py b/src/kimchi/model/vmhostdevs.py index 3272b0d..1ef85ba 100644 --- a/src/kimchi/model/vmhostdevs.py +++ b/src/kimchi/model/vmhostdevs.py @@ -118,6 +118,11 @@ class VMHostDevsModel(object): DevicesModel(conn=self.conn).get_list(_passthrough='true') if dev_name not in eligible_dev_names: raise InvalidParameter('KCHVMHDEV0002E', {'dev_name': dev_name}) + holders = VMHoldersModel(conn=self.conn).get_list(dev_name) + if holders: + names = ', '.join([holder['name'] for holder in holders]) + raise InvalidOperation('KCHVMHDEV0004E', {'dev_name': dev_name, + 'names': names}) def create(self, vmid, params): dev_name = params['name'] @@ -298,3 +303,22 @@ class VMHostDevModel(object): xmlstr = etree.tostring(e) dom.detachDeviceFlags( xmlstr, get_vm_config_flag(dom, mode='all')) + + +class VMHoldersModel(object): + def __init__(self, **kargs): + self.conn = kargs['conn'] + + def get_list(self, device_id): + devsmodel = VMHostDevsModel(conn=self.conn) + + conn = self.conn.get() + doms = conn.listAllDomains(0) + + res = [] + for dom in doms: + dom_name = dom.name() + if device_id in devsmodel.get_list(dom_name): + state = DOM_STATE_MAP[dom.info()[0]] + res.append({"name": dom_name, "state": state}) + return res -- 1.9.3