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