<div dir="ltr">What exactly are the drivers that support it? I was able to hot plug interfaces using virtio, rtl8139 and e1000, at least.<br><br><div class="gmail_quote">On Mon, Apr 13, 2015 at 11:12 AM Aline Manera <<a href="mailto:alinefm@linux.vnet.ibm.com" target="_blank">alinefm@linux.vnet.ibm.com</a>> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
<br>
On 08/04/2015 09:34, Crístian Deives wrote:<br>
> Currently, network interfaces can only be added to shutoff VMs. However,<br>
> the use might want to attach an interface to a running VM as well.<br>
><br>
> Allow network interfaces to be attached also while the VM is running.<br>
> The related test cases have been updated to perform the same operations<br>
> on both a shutoff and a running VM.<br>
><br>
> Fix issue #548 (Kimchi does not support NIC hot plug) - backend only.<br>
><br>
> Signed-off-by: Crístian Deives <<a href="mailto:cristiandeives@gmail.com" target="_blank">cristiandeives@gmail.com</a>><br>
> ---<br>
> src/kimchi/i18n.py | 1 -<br>
> src/kimchi/model/vmifaces.py | 45 ++++++++++++++---------<br>
> tests/test_model.py | 86 ++++++++++++++++++++++----------------------<br>
> 3 files changed, 73 insertions(+), 59 deletions(-)<br>
><br>
> diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py<br>
> index 46990a5..d4c1cc6 100644<br>
> --- a/src/kimchi/i18n.py<br>
> +++ b/src/kimchi/i18n.py<br>
> @@ -118,7 +118,6 @@ messages = {<br>
><br>
> "KCHVMIF0001E": _("Interface %(iface)s does not exist in virtual machine %(name)s"),<br>
> "KCHVMIF0002E": _("Network %(network)s specified for virtual machine %(name)s does not exist"),<br>
> - "KCHVMIF0003E": _("Do not support guest interface hot plug attachment"),<br>
> "KCHVMIF0004E": _("Supported virtual machine interfaces type is only network"),<br>
> "KCHVMIF0005E": _("Network name for virtual machine interface must be a string"),<br>
> "KCHVMIF0006E": _("Invalid network model card specified for virtual machine interface"),<br>
> diff --git a/src/kimchi/model/vmifaces.py b/src/kimchi/model/vmifaces.py<br>
> index 0c10fa9..a64315d 100644<br>
> --- a/src/kimchi/model/vmifaces.py<br>
> +++ b/src/kimchi/model/vmifaces.py<br>
> @@ -22,7 +22,7 @@ import random<br>
> import libvirt<br>
> from lxml import etree, objectify<br>
><br>
> -from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError<br>
> +from kimchi.exception import InvalidParameter, NotFoundError<br>
> from kimchi.model.config import CapabilitiesModel<br>
> from kimchi.model.vms import DOM_STATE_MAP, VMModel<br>
> from kimchi.xmlutils.interface import get_iface_xml<br>
> @@ -48,10 +48,6 @@ class VMIfacesModel(object):<br>
> raise InvalidParameter("KCHVMIF0002E",<br>
> {'name': vm, 'network': params["network"]})<br>
><br>
> - dom = VMModel.get_vm(vm, self.conn)<br>
> - if DOM_STATE_MAP[<a href="http://dom.info" target="_blank">dom.info</a>()[0]] != "shutoff":<br>
> - raise InvalidOperation("KCHVMIF0003E")<br>
> -<br>
> macs = (iface.mac.get('address')<br>
> for iface in self.get_vmifaces(vm, self.conn))<br>
><br>
> @@ -60,10 +56,19 @@ class VMIfacesModel(object):<br>
> if params['mac'] not in macs:<br>
> break<br>
><br>
> + dom = VMModel.get_vm(vm, self.conn)<br>
> +<br>
<br>
We need to restrict the hot plug operation to the drivers that supports it.<br>
<br>
> os_data = VMModel.vm_get_os_metadata(dom, self.caps.metadata_support)<br>
> os_distro, os_version = os_data<br>
> xml = get_iface_xml(params, conn.getInfo()[0], os_distro, os_version)<br>
> - dom.attachDeviceFlags(xml, libvirt.VIR_DOMAIN_AFFECT_CURRENT)<br>
> +<br>
> + flags = 0<br>
> + if dom.isPersistent():<br>
> + flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG<br>
> + if DOM_STATE_MAP[<a href="http://dom.info" target="_blank">dom.info</a>()[0]] != "shutoff":<br>
> + flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE<br>
> +<br>
> + dom.attachDeviceFlags(xml, flags)<br>
><br>
> return params['mac']<br>
><br>
> @@ -118,14 +123,16 @@ class VMIfaceModel(object):<br>
> dom = VMModel.get_vm(vm, self.conn)<br>
> iface = self._get_vmiface(vm, mac)<br>
><br>
> - if DOM_STATE_MAP[<a href="http://dom.info" target="_blank">dom.info</a>()[0]] != "shutoff":<br>
> - raise InvalidOperation("KCHVMIF0003E")<br>
> -<br>
> if iface is None:<br>
> raise NotFoundError("KCHVMIF0001E", {'name': vm, 'iface': mac})<br>
><br>
> - dom.detachDeviceFlags(<a href="http://etree.to" target="_blank">etree.to</a>string(iface),<br>
> - libvirt.VIR_DOMAIN_AFFECT_CURRENT)<br>
> + flags = 0<br>
> + if dom.isPersistent():<br>
> + flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG<br>
> + if DOM_STATE_MAP[<a href="http://dom.info" target="_blank">dom.info</a>()[0]] != "shutoff":<br>
> + flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE<br>
> +<br>
> + dom.detachDeviceFlags(<a href="http://etree.to" target="_blank">etree.to</a>string(iface), flags)<br>
><br>
> def update(self, vm, mac, params):<br>
> dom = VMModel.get_vm(vm, self.conn)<br>
> @@ -134,16 +141,22 @@ class VMIfaceModel(object):<br>
> if iface is None:<br>
> raise NotFoundError("KCHVMIF0001E", {'name': vm, 'iface': mac})<br>
><br>
> - # FIXME we will support to change the live VM configuration later.<br>
> + flags = 0<br>
> + if dom.isPersistent():<br>
> + flags |= libvirt.VIR_DOMAIN_AFFECT_CONFIG<br>
> + if DOM_STATE_MAP[<a href="http://dom.info" target="_blank">dom.info</a>()[0]] != "shutoff":<br>
> + flags |= libvirt.VIR_DOMAIN_AFFECT_LIVE<br>
> +<br>
> if iface.attrib['type'] == 'network' and 'network' in params:<br>
> iface.source.attrib['network'] = params['network']<br>
> xml = etree.tostring(iface)<br>
> - dom.updateDeviceFlags(xml, flags=libvirt.VIR_DOMAIN_AFFECT_CONFIG)<br>
><br>
> - # change on the persisted VM configuration only.<br>
> - if 'model' in params and dom.isPersistent():<br>
> + dom.updateDeviceFlags(xml, flags=flags)<br>
> +<br>
> + if 'model' in params:<br>
> iface.model.attrib["type"] = params['model']<br>
> xml = etree.tostring(iface)<br>
> - dom.updateDeviceFlags(xml, flags=libvirt.VIR_DOMAIN_AFFECT_CONFIG)<br>
> +<br>
> + dom.updateDeviceFlags(xml, flags=flags)<br>
><br>
> return mac<br>
> diff --git a/tests/test_model.py b/tests/test_model.py<br>
> index 63bd721..39e4fe6 100644<br>
> --- a/tests/test_model.py<br>
> +++ b/tests/test_model.py<br>
> @@ -297,9 +297,6 @@ class ModelTests(unittest.TestCase):<br>
> params = {'name': 'test', 'disks': [], 'cdrom': UBUNTU_ISO}<br>
> inst.templates_create(params)<br>
> rollback.prependDefer(inst.template_delete, 'test')<br>
> - params = {'name': 'kimchi-ifaces', 'template': '/templates/test'}<br>
> - inst.vms_create(params)<br>
> - rollback.prependDefer(inst.vm_delete, 'kimchi-ifaces')<br>
><br>
> # Create a network<br>
> net_name = 'test-network'<br>
> @@ -311,45 +308,50 @@ class ModelTests(unittest.TestCase):<br>
> inst.network_activate(net_name)<br>
> rollback.prependDefer(<a href="http://inst.ne" target="_blank">inst.ne</a>twork_deactivate, net_name)<br>
><br>
> - ifaces = inst.vmifaces_get_list('kimchi-ifaces')<br>
> - self.assertEquals(1, len(ifaces))<br>
> -<br>
> - iface = inst.vmiface_lookup('kimchi-ifaces', ifaces[0])<br>
> - self.assertEquals(17, len(iface['mac']))<br>
> - self.assertEquals("default", iface['network'])<br>
> - self.assertIn("model", iface)<br>
> -<br>
> - # attach network interface to vm<br>
> - iface_args = {"type": "network",<br>
> - "network": "test-network",<br>
> - "model": "virtio"}<br>
> - mac = inst.vmifaces_create('kimchi-ifaces', iface_args)<br>
> - # detach network interface from vm<br>
> - rollback.prependDefer(inst.vmiface_delete, 'kimchi-ifaces', mac)<br>
> - self.assertEquals(17, len(mac))<br>
> -<br>
> - iface = inst.vmiface_lookup('kimchi-ifaces', mac)<br>
> - self.assertEquals("network", iface["type"])<br>
> - self.assertEquals("test-network", iface['network'])<br>
> - self.assertEquals("virtio", iface["model"])<br>
> -<br>
> - # attach network interface to vm without providing model<br>
> - iface_args = {"type": "network",<br>
> - "network": "test-network"}<br>
> - mac = inst.vmifaces_create('kimchi-ifaces', iface_args)<br>
> - rollback.prependDefer(inst.vmiface_delete, 'kimchi-ifaces', mac)<br>
> -<br>
> - iface = inst.vmiface_lookup('kimchi-ifaces', mac)<br>
> - self.assertEquals("network", iface["type"])<br>
> - self.assertEquals("test-network", iface['network'])<br>
> -<br>
> - # update vm interface<br>
> - iface_args = {"network": "default",<br>
> - "model": "e1000"}<br>
> - inst.vmiface_update('kimchi-ifaces', mac, iface_args)<br>
> - iface = inst.vmiface_lookup('kimchi-ifaces', mac)<br>
> - self.assertEquals("default", iface['network'])<br>
> - self.assertEquals("e1000", iface["model"])<br>
> + for vm_name in ['kimchi-ifaces', 'kimchi-ifaces-running']:<br>
> + params = {'name': vm_name, 'template': '/templates/test'}<br>
> + inst.vms_create(params)<br>
> + rollback.prependDefer(inst.vm_delete, vm_name)<br>
> +<br>
> + ifaces = inst.vmifaces_get_list(vm_name)<br>
> + self.assertEquals(1, len(ifaces))<br>
> +<br>
> + iface = inst.vmiface_lookup(vm_name, ifaces[0])<br>
> + self.assertEquals(17, len(iface['mac']))<br>
> + self.assertEquals("default", iface['network'])<br>
> + self.assertIn("model", iface)<br>
> +<br>
> + # attach network interface to vm<br>
> + iface_args = {"type": "network",<br>
> + "network": "test-network",<br>
> + "model": "virtio"}<br>
> + mac = inst.vmifaces_create(vm_name, iface_args)<br>
> + # detach network interface from vm<br>
> + rollback.prependDefer(inst.vmiface_delete, vm_name, mac)<br>
> + self.assertEquals(17, len(mac))<br>
> +<br>
> + iface = inst.vmiface_lookup(vm_name, mac)<br>
> + self.assertEquals("network", iface["type"])<br>
> + self.assertEquals("test-network", iface['network'])<br>
> + self.assertEquals("virtio", iface["model"])<br>
> +<br>
> + # attach network interface to vm without providing model<br>
> + iface_args = {"type": "network",<br>
> + "network": "test-network"}<br>
> + mac = inst.vmifaces_create(vm_name, iface_args)<br>
> + rollback.prependDefer(inst.vmiface_delete, vm_name, mac)<br>
> +<br>
> + iface = inst.vmiface_lookup(vm_name, mac)<br>
> + self.assertEquals("network", iface["type"])<br>
> + self.assertEquals("test-network", iface['network'])<br>
> +<br>
> + # update vm interface<br>
> + iface_args = {"network": "default",<br>
> + "model": "e1000"}<br>
> + inst.vmiface_update(vm_name, mac, iface_args)<br>
> + iface = inst.vmiface_lookup(vm_name, mac)<br>
> + self.assertEquals("default", iface['network'])<br>
> + self.assertEquals("e1000", iface["model"])<br>
><br>
> @unittest.skipUnless(<a href="http://utils.ru" target="_blank">utils.ru</a>nning_as_root(), 'Must be run as root')<br>
> def test_vm_disk(self):<br>
<br>
</blockquote></div></div>