[PATCH v5 1/3] Generate libvirt's interface XML definition for vlan tagged bridge

To support vlan tagged virtual network, kimchi needs create the underlying vlan and bridge interface. Libvirt's interface driver can do it with a given XML definition. This patch targets to generate the XML according to the interface and vlan id. Signed-off-by: Mark Wu <wudxw@linux.vnet.ibm.com> --- Changes: v4->v5: Add missing dependency of python-lxml contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + docs/README.md | 6 +++--- src/kimchi/networkxml.py | 19 +++++++++++++++++++ tests/test_networkxml.py | 22 ++++++++++++++++++++++ tests/utils.py | 6 ++++++ 7 files changed, 53 insertions(+), 3 deletions(-) diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index eecfb27..271bee4 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -18,6 +18,7 @@ Depends: python-cherrypy3 (>= 3.2.0), python-ethtool, sosreport, python-ipaddr, + python-lxml, open-iscsi Build-Depends: Maintainer: Aline Manera <alinefm@br.ibm.com> diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 38c72f1..cc8c298 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -24,6 +24,7 @@ Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: sos Requires: python-ipaddr +Requires: python-lxml Requires: nfs-utils Requires: iscsi-initiator-utils diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 7be4984..d1aec7b 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -19,6 +19,7 @@ Requires: python-psutil >= 0.6.0 Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: python-ipaddr +Requires: python-lxml Requires: nfs-client Requires: iscsi-initiator-utils diff --git a/docs/README.md b/docs/README.md index e7599ec..1ccea01 100644 --- a/docs/README.md +++ b/docs/README.md @@ -37,7 +37,7 @@ Install Dependencies libvirt libxml2-python python-imaging \ PyPAM m2crypto python-jsonschema rpm-build \ qemu-kvm python-psutil python-ethtool sos \ - python-ipaddr nfs-utils + python-ipaddr python-lxml nfs-utils # If using RHEL6, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict # Restart libvirt to allow configuration changes to take effect @@ -58,7 +58,7 @@ for more information on how to configure your system to access this repository. libvirt-bin python-libxml2 python-imaging \ python-pam python-m2crypto python-jsonschema \ qemu-kvm libtool python-psutil python-ethtool \ - sosreport python-ipaddr nfs-common + sosreport python-ipaddr python-lxml nfs-common Packages version requirement: python-jsonschema >= 1.3.0 @@ -71,7 +71,7 @@ for more information on how to configure your system to access this repository. libvirt python-libxml2 python-imaging \ python-pam python-M2Crypto python-jsonschema \ rpm-build kvm python-psutil python-ethtool \ - python-ipaddr nfs-client + python-ipaddr python-lxml nfs-client Packages version requirement: python-psutil >= 0.6.0 diff --git a/src/kimchi/networkxml.py b/src/kimchi/networkxml.py index 786cb69..63cb210 100644 --- a/src/kimchi/networkxml.py +++ b/src/kimchi/networkxml.py @@ -21,6 +21,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import ipaddr +import lxml.etree as ET + + +from lxml.builder import E # FIXME, do not support ipv6 @@ -109,3 +113,18 @@ def to_network_xml(**kwargs): </network> """ % params return xml + + +def create_vlan_tagged_bridge_xml(bridge, interface, vlan_id): + vlan = E.vlan(E.interface(name=interface)) + vlan.set('tag', vlan_id) + m = E.interface( + E.start(mode='onboot'), + E.bridge( + E.interface( + vlan, + type='vlan', + name='.'.join([interface, vlan_id]))), + type='bridge', + name=bridge) + return ET.tostring(m) diff --git a/tests/test_networkxml.py b/tests/test_networkxml.py index 3073bce..42b3ea9 100644 --- a/tests/test_networkxml.py +++ b/tests/test_networkxml.py @@ -25,6 +25,9 @@ import unittest import kimchi.networkxml as nxml +import utils + + from kimchi.xmlutils import xpath_get_text @@ -151,3 +154,22 @@ class NetworkXmlTests(unittest.TestCase): netmask = xpath_get_text(xml, "/network/ip/@netmask")[0] self.assertEquals(netmask, str(ipaddr.IPNetwork(params["net"]).netmask)) + + +class InterfaceXmlTests(unittest.TestCase): + + def test_vlan_tagged_bridge_no_ip(self): + expected_xml = """ + <interface type='bridge' name='br10'> + <start mode='onboot'/> + <bridge> + <interface type='vlan' name='em1.10'> + <vlan tag='10'> + <interface name='em1'/> + </vlan> + </interface> + </bridge> + </interface> + """ + actual_xml = nxml.create_vlan_tagged_bridge_xml('br10', 'em1', '10') + self.assertEquals(actual_xml, utils.normalize_xml(expected_xml)) diff --git a/tests/utils.py b/tests/utils.py index 008f668..79fc2e2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -32,6 +32,7 @@ import unittest from contextlib import closing +from lxml import etree import kimchi.server @@ -159,3 +160,8 @@ def patch_auth(): import kimchi.auth kimchi.auth.authenticate = _authenticate + + +def normalize_xml(xml_str): + return etree.tostring(etree.fromstring(xml_str, + etree.XMLParser(remove_blank_text=True))) -- 1.8.4.2

It creates a vlan interface on top of the given nic or bond interface, and bridge the vlan interface to a new created bridge, which is used to forward VM's traffic. So all packets transmitted from VM will be tagged before it departs from the physical network interface. Signed-off-by: Mark Wu <wudxw@linux.vnet.ibm.com> --- src/kimchi/API.json | 6 ++++++ src/kimchi/model.py | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 3a3c48f..19b1c51 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -125,6 +125,12 @@ "interface": { "description": "The name of a network interface on the host", "type": "string" + }, + "vlan_id": { + "description": "Network's VLAN ID", + "type": "integer", + "maximum": 4094, + "minimum": 1 } } }, diff --git a/src/kimchi/model.py b/src/kimchi/model.py index ed613b1..5281979 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -57,6 +57,7 @@ except ImportError: from kimchi import config from kimchi import netinfo from kimchi import network as knetwork +from kimchi import networkxml from kimchi import vnc from kimchi import xmlutils from kimchi.asynctask import AsyncTask @@ -66,7 +67,6 @@ from kimchi.exception import MissingParameter, NotFoundError, OperationFailed from kimchi.featuretests import FeatureTests from kimchi.iscsi import TargetClient from kimchi.isoinfo import IsoImage -from kimchi.networkxml import to_network_xml from kimchi.objectstore import ObjectStore from kimchi.scan import Scanner from kimchi.screenshot import VMScreenshot @@ -819,7 +819,12 @@ class Model(object): if netinfo.is_bridge(iface): params['bridge'] = iface elif netinfo.is_bare_nic(iface) or netinfo.is_bonding(iface): - params['forward']['dev'] = iface + if params.get('vlan_id') is None: + params['forward']['dev'] = iface + else: + params['bridge'] = \ + self._create_vlan_tagged_bridge(str(iface), + str(params['vlan_id'])) else: raise InvalidParameter("the interface should be bare nic, " "bonding or bridge device.") @@ -843,7 +848,7 @@ class Model(object): if connection == 'bridge': self._set_network_bridge(params) - xml = to_network_xml(**params) + xml = networkxml.to_network_xml(**params) try: network = conn.networkDefineXML(xml) @@ -901,8 +906,44 @@ class Model(object): if network.isActive(): raise InvalidOperation( "Unable to delete the active network %s" % name) + self._remove_vlan_tagged_bridge(network) network.undefine() + def _get_vlan_tagged_bridge_name(self, interface, vlan_id): + return '-'.join(('kimchi', interface, vlan_id)) + + def _is_vlan_tagged_bridge(self, bridge): + return bridge.startswith('kimchi-') + + def _create_vlan_tagged_bridge(self, interface, vlan_id): + br_name = self._get_vlan_tagged_bridge_name(interface, vlan_id) + br_xml = networkxml.create_vlan_tagged_bridge_xml(br_name, interface, + vlan_id) + conn = self.conn.get() + conn.changeBegin() + try: + vlan_tagged_br = conn.interfaceDefineXML(br_xml) + vlan_tagged_br.create() + except libvirt.libvirtError as e: + conn.changeRollback() + raise OperationFailed(e.message) + else: + conn.changeCommit() + return br_name + + def _remove_vlan_tagged_bridge(self, network): + try: + bridge = network.bridgeName() + except libvirt.libvirtError: + pass + else: + if self._is_vlan_tagged_bridge(bridge): + conn = self.conn.get() + iface = conn.interfaceLookupByName(bridge) + if iface.isActive(): + iface.destroy() + iface.undefine() + def add_task(self, target_uri, fn, opaque=None): id = self.next_taskid self.next_taskid = self.next_taskid + 1 -- 1.8.4.2

Reviewed-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> On 01/10/2014 11:53 AM, Mark Wu wrote:
It creates a vlan interface on top of the given nic or bond interface, and bridge the vlan interface to a new created bridge, which is used to forward VM's traffic. So all packets transmitted from VM will be tagged before it departs from the physical network interface.
Signed-off-by: Mark Wu <wudxw@linux.vnet.ibm.com> --- src/kimchi/API.json | 6 ++++++ src/kimchi/model.py | 47 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-)
diff --git a/src/kimchi/API.json b/src/kimchi/API.json index 3a3c48f..19b1c51 100644 --- a/src/kimchi/API.json +++ b/src/kimchi/API.json @@ -125,6 +125,12 @@ "interface": { "description": "The name of a network interface on the host", "type": "string" + }, + "vlan_id": { + "description": "Network's VLAN ID", + "type": "integer", + "maximum": 4094, + "minimum": 1 } } }, diff --git a/src/kimchi/model.py b/src/kimchi/model.py index ed613b1..5281979 100644 --- a/src/kimchi/model.py +++ b/src/kimchi/model.py @@ -57,6 +57,7 @@ except ImportError: from kimchi import config from kimchi import netinfo from kimchi import network as knetwork +from kimchi import networkxml from kimchi import vnc from kimchi import xmlutils from kimchi.asynctask import AsyncTask @@ -66,7 +67,6 @@ from kimchi.exception import MissingParameter, NotFoundError, OperationFailed from kimchi.featuretests import FeatureTests from kimchi.iscsi import TargetClient from kimchi.isoinfo import IsoImage -from kimchi.networkxml import to_network_xml from kimchi.objectstore import ObjectStore from kimchi.scan import Scanner from kimchi.screenshot import VMScreenshot @@ -819,7 +819,12 @@ class Model(object): if netinfo.is_bridge(iface): params['bridge'] = iface elif netinfo.is_bare_nic(iface) or netinfo.is_bonding(iface): - params['forward']['dev'] = iface + if params.get('vlan_id') is None: + params['forward']['dev'] = iface + else: + params['bridge'] = \ + self._create_vlan_tagged_bridge(str(iface), + str(params['vlan_id'])) else: raise InvalidParameter("the interface should be bare nic, " "bonding or bridge device.") @@ -843,7 +848,7 @@ class Model(object): if connection == 'bridge': self._set_network_bridge(params)
- xml = to_network_xml(**params) + xml = networkxml.to_network_xml(**params)
try: network = conn.networkDefineXML(xml) @@ -901,8 +906,44 @@ class Model(object): if network.isActive(): raise InvalidOperation( "Unable to delete the active network %s" % name) + self._remove_vlan_tagged_bridge(network) network.undefine()
+ def _get_vlan_tagged_bridge_name(self, interface, vlan_id): + return '-'.join(('kimchi', interface, vlan_id)) + + def _is_vlan_tagged_bridge(self, bridge): + return bridge.startswith('kimchi-') + + def _create_vlan_tagged_bridge(self, interface, vlan_id): + br_name = self._get_vlan_tagged_bridge_name(interface, vlan_id) + br_xml = networkxml.create_vlan_tagged_bridge_xml(br_name, interface, + vlan_id) + conn = self.conn.get() + conn.changeBegin() + try: + vlan_tagged_br = conn.interfaceDefineXML(br_xml) + vlan_tagged_br.create() + except libvirt.libvirtError as e: + conn.changeRollback() + raise OperationFailed(e.message) + else: + conn.changeCommit() + return br_name + + def _remove_vlan_tagged_bridge(self, network): + try: + bridge = network.bridgeName() + except libvirt.libvirtError: + pass + else: + if self._is_vlan_tagged_bridge(bridge): + conn = self.conn.get() + iface = conn.interfaceLookupByName(bridge) + if iface.isActive(): + iface.destroy() + iface.undefine() + def add_task(self, target_uri, fn, opaque=None): id = self.next_taskid self.next_taskid = self.next_taskid + 1
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

It adds a checkbox to provide choice of enabling vlan. If it's checked, it all user to type the vlan ID. Signed-off-by: Mark Wu <wudxw@linux.vnet.ibm.com> --- po/en_US.po | 3 +++ po/kimchi.pot | 3 +++ po/pt_BR.po | 3 +++ po/zh_CN.po | 3 +++ ui/css/theme-default/network.css | 9 +++++++++ ui/js/src/kimchi.network.js | 10 +++++++++- ui/pages/tabs/network.html.tmpl | 6 ++++++ 7 files changed, 36 insertions(+), 1 deletion(-) diff --git a/po/en_US.po b/po/en_US.po index 1061b20..ebbe1ee 100644 --- a/po/en_US.po +++ b/po/en_US.po @@ -517,6 +517,9 @@ msgstr "Bridged: VMs are connected to physical network directly" msgid "Destination" msgstr "Destination" +msgid "Enable VLAN" +msgstr "Enable VLAN" + msgid "No templates found." msgstr "No templates found." diff --git a/po/kimchi.pot b/po/kimchi.pot index 5f3a106..a2bbcf5 100755 --- a/po/kimchi.pot +++ b/po/kimchi.pot @@ -494,6 +494,9 @@ msgstr "" msgid "Destination" msgstr "" +msgid "Enable VLAN" +msgstr "" + msgid "No templates found." msgstr "" diff --git a/po/pt_BR.po b/po/pt_BR.po index 737e5db..69bb554 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -531,6 +531,9 @@ msgstr "Bridged: Máquinas virtuais estão conectadas diretamente a rede física msgid "Destination" msgstr "Destino" +msgid "Enable VLAN" +msgstr "Ativar VLAN" + msgid "No templates found." msgstr "Nenhum modelo encontrado." diff --git a/po/zh_CN.po b/po/zh_CN.po index 47027e5..411fcaa 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -513,6 +513,9 @@ msgstr "桥接: 虚拟机直接接入物理网络" msgid "Destination" msgstr "目标设备" +msgid "Enable VLAN" +msgstr "启用VLAN" + msgid "No templates found." msgstr "没有发现模板" diff --git a/ui/css/theme-default/network.css b/ui/css/theme-default/network.css index 257540f..58c228b 100644 --- a/ui/css/theme-default/network.css +++ b/ui/css/theme-default/network.css @@ -207,6 +207,15 @@ margin-left: 28px; } +.network-config .VLAN { + margin-left: 28px; +} + +.network-config .VLAN input[type="text"] { + height: 25px; + width: 60px; +} + .network-config .input-hint-icon { margin: -1px 1px 0 0; display: inline-block; diff --git a/ui/js/src/kimchi.network.js b/ui/js/src/kimchi.network.js index cdcf099..65479b2 100644 --- a/ui/js/src/kimchi.network.js +++ b/ui/js/src/kimchi.network.js @@ -135,11 +135,13 @@ kimchi.initNetworkCreation = function() { var network = kimchi.getNetworkDialogValues(); var data = { name : network.name, - connection: network.type + connection: network.type, + vlan_id: network.vlan_id, }; if (network.type === kimchi.NETWORK_TYPE_BRIDGE) { data.connection = "bridge"; data.interface = network.interface; + data.vlan_id = network.vlan_id; } kimchi.createNetwork(data, function(result) { network.state = result.state === "active" ? "up" : "down"; @@ -194,6 +196,9 @@ kimchi.openNetworkDialog = function(okCallback) { okCallback(); $("#networkConfig").dialog("close"); }); + $("#enableVlan").on("click", function() { + $("#networkVlanID").prop("disabled", !this.checked); + }); $("#networkConfig").dialog("open"); }; @@ -211,6 +216,7 @@ kimchi.getNetworkDialogValues = function() { }; if (network.type === kimchi.NETWORK_TYPE_BRIDGE) { network.interface = $("#networkInterface").val(); + network.vlan_id = parseInt($("#networkVlanID").val()); } return network; }; @@ -225,6 +231,8 @@ kimchi.cleanNetworkDialog = function() { $("#networkInterface option").removeAttr("selected").find(":first").attr("selected", "selected"); $("#networkFormOk").off("click"); $("#networkFormOk").button("disable"); + $("#networkVlanID").prop("disabled", true); + $("#enableVlan").prop("checked", false); }; kimchi.setupNetworkFormEvent = function() { diff --git a/ui/pages/tabs/network.html.tmpl b/ui/pages/tabs/network.html.tmpl index 9c2ec2a..fcf8556 100644 --- a/ui/pages/tabs/network.html.tmpl +++ b/ui/pages/tabs/network.html.tmpl @@ -75,6 +75,12 @@ <label>$_("Destination"): </label> <select id="networkInterface"></select> </div> + <div class="VLAN"> + <label>$_("Enable VLAN"): </label> + <input id="enableVlan" type="checkbox" value=""/> + <label>$_("VLAN ID"): </label> + <input type="text" id="networkVlanID" disabled> + </div> </div> </div> </div> -- 1.8.4.2

Reviewed-by: ShaoHe Feng <shaohef@linux.vnet.ibm.com> On 01/10/2014 11:53 AM, Mark Wu wrote:
To support vlan tagged virtual network, kimchi needs create the underlying vlan and bridge interface. Libvirt's interface driver can do it with a given XML definition. This patch targets to generate the XML according to the interface and vlan id.
Signed-off-by: Mark Wu <wudxw@linux.vnet.ibm.com> --- Changes: v4->v5: Add missing dependency of python-lxml contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + docs/README.md | 6 +++--- src/kimchi/networkxml.py | 19 +++++++++++++++++++ tests/test_networkxml.py | 22 ++++++++++++++++++++++ tests/utils.py | 6 ++++++ 7 files changed, 53 insertions(+), 3 deletions(-)
diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index eecfb27..271bee4 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -18,6 +18,7 @@ Depends: python-cherrypy3 (>= 3.2.0), python-ethtool, sosreport, python-ipaddr, + python-lxml, open-iscsi Build-Depends: Maintainer: Aline Manera <alinefm@br.ibm.com> diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 38c72f1..cc8c298 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -24,6 +24,7 @@ Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: sos Requires: python-ipaddr +Requires: python-lxml Requires: nfs-utils Requires: iscsi-initiator-utils
diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 7be4984..d1aec7b 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -19,6 +19,7 @@ Requires: python-psutil >= 0.6.0 Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: python-ipaddr +Requires: python-lxml Requires: nfs-client Requires: iscsi-initiator-utils
diff --git a/docs/README.md b/docs/README.md index e7599ec..1ccea01 100644 --- a/docs/README.md +++ b/docs/README.md @@ -37,7 +37,7 @@ Install Dependencies libvirt libxml2-python python-imaging \ PyPAM m2crypto python-jsonschema rpm-build \ qemu-kvm python-psutil python-ethtool sos \ - python-ipaddr nfs-utils + python-ipaddr python-lxml nfs-utils # If using RHEL6, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict # Restart libvirt to allow configuration changes to take effect @@ -58,7 +58,7 @@ for more information on how to configure your system to access this repository. libvirt-bin python-libxml2 python-imaging \ python-pam python-m2crypto python-jsonschema \ qemu-kvm libtool python-psutil python-ethtool \ - sosreport python-ipaddr nfs-common + sosreport python-ipaddr python-lxml nfs-common
Packages version requirement: python-jsonschema >= 1.3.0 @@ -71,7 +71,7 @@ for more information on how to configure your system to access this repository. libvirt python-libxml2 python-imaging \ python-pam python-M2Crypto python-jsonschema \ rpm-build kvm python-psutil python-ethtool \ - python-ipaddr nfs-client + python-ipaddr python-lxml nfs-client
Packages version requirement: python-psutil >= 0.6.0 diff --git a/src/kimchi/networkxml.py b/src/kimchi/networkxml.py index 786cb69..63cb210 100644 --- a/src/kimchi/networkxml.py +++ b/src/kimchi/networkxml.py @@ -21,6 +21,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import ipaddr +import lxml.etree as ET + + +from lxml.builder import E
# FIXME, do not support ipv6 @@ -109,3 +113,18 @@ def to_network_xml(**kwargs): </network> """ % params return xml + + +def create_vlan_tagged_bridge_xml(bridge, interface, vlan_id): + vlan = E.vlan(E.interface(name=interface)) + vlan.set('tag', vlan_id) + m = E.interface( + E.start(mode='onboot'), + E.bridge( + E.interface( + vlan, + type='vlan', + name='.'.join([interface, vlan_id]))), + type='bridge', + name=bridge) + return ET.tostring(m) diff --git a/tests/test_networkxml.py b/tests/test_networkxml.py index 3073bce..42b3ea9 100644 --- a/tests/test_networkxml.py +++ b/tests/test_networkxml.py @@ -25,6 +25,9 @@ import unittest
import kimchi.networkxml as nxml +import utils + + from kimchi.xmlutils import xpath_get_text
@@ -151,3 +154,22 @@ class NetworkXmlTests(unittest.TestCase): netmask = xpath_get_text(xml, "/network/ip/@netmask")[0] self.assertEquals(netmask, str(ipaddr.IPNetwork(params["net"]).netmask)) + + +class InterfaceXmlTests(unittest.TestCase): + + def test_vlan_tagged_bridge_no_ip(self): + expected_xml = """ + <interface type='bridge' name='br10'> + <start mode='onboot'/> + <bridge> + <interface type='vlan' name='em1.10'> + <vlan tag='10'> + <interface name='em1'/> + </vlan> + </interface> + </bridge> + </interface> + """ + actual_xml = nxml.create_vlan_tagged_bridge_xml('br10', 'em1', '10') + self.assertEquals(actual_xml, utils.normalize_xml(expected_xml)) diff --git a/tests/utils.py b/tests/utils.py index 008f668..79fc2e2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -32,6 +32,7 @@ import unittest
from contextlib import closing +from lxml import etree
import kimchi.server @@ -159,3 +160,8 @@ def patch_auth():
import kimchi.auth kimchi.auth.authenticate = _authenticate + + +def normalize_xml(xml_str): + return etree.tostring(etree.fromstring(xml_str, + etree.XMLParser(remove_blank_text=True)))
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center

On 01/10/2014 11:53 AM, Mark Wu wrote:
To support vlan tagged virtual network, kimchi needs create the underlying vlan and bridge interface. Libvirt's interface driver can do it with a given XML definition. This patch targets to generate the XML according to the interface and vlan id.
Signed-off-by: Mark Wu <wudxw@linux.vnet.ibm.com> --- Changes: v4->v5: Add missing dependency of python-lxml usually we submit a cover-letter to keep track of change log. contrib/DEBIAN/control.in | 1 + contrib/kimchi.spec.fedora.in | 1 + contrib/kimchi.spec.suse.in | 1 + docs/README.md | 6 +++--- src/kimchi/networkxml.py | 19 +++++++++++++++++++ tests/test_networkxml.py | 22 ++++++++++++++++++++++ tests/utils.py | 6 ++++++ 7 files changed, 53 insertions(+), 3 deletions(-)
diff --git a/contrib/DEBIAN/control.in b/contrib/DEBIAN/control.in index eecfb27..271bee4 100644 --- a/contrib/DEBIAN/control.in +++ b/contrib/DEBIAN/control.in @@ -18,6 +18,7 @@ Depends: python-cherrypy3 (>= 3.2.0), python-ethtool, sosreport, python-ipaddr, + python-lxml, open-iscsi Build-Depends: Maintainer: Aline Manera <alinefm@br.ibm.com> diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in index 38c72f1..cc8c298 100644 --- a/contrib/kimchi.spec.fedora.in +++ b/contrib/kimchi.spec.fedora.in @@ -24,6 +24,7 @@ Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: sos Requires: python-ipaddr +Requires: python-lxml Requires: nfs-utils Requires: iscsi-initiator-utils
diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in index 7be4984..d1aec7b 100644 --- a/contrib/kimchi.spec.suse.in +++ b/contrib/kimchi.spec.suse.in @@ -19,6 +19,7 @@ Requires: python-psutil >= 0.6.0 Requires: python-jsonschema >= 1.3.0 Requires: python-ethtool Requires: python-ipaddr +Requires: python-lxml Requires: nfs-client Requires: iscsi-initiator-utils
diff --git a/docs/README.md b/docs/README.md index e7599ec..1ccea01 100644 --- a/docs/README.md +++ b/docs/README.md @@ -37,7 +37,7 @@ Install Dependencies libvirt libxml2-python python-imaging \ PyPAM m2crypto python-jsonschema rpm-build \ qemu-kvm python-psutil python-ethtool sos \ - python-ipaddr nfs-utils + python-ipaddr python-lxml nfs-utils # If using RHEL6, install the following additional packages: $ sudo yum install python-unittest2 python-ordereddict # Restart libvirt to allow configuration changes to take effect @@ -58,7 +58,7 @@ for more information on how to configure your system to access this repository. libvirt-bin python-libxml2 python-imaging \ python-pam python-m2crypto python-jsonschema \ qemu-kvm libtool python-psutil python-ethtool \ - sosreport python-ipaddr nfs-common + sosreport python-ipaddr python-lxml nfs-common
Packages version requirement: python-jsonschema >= 1.3.0 @@ -71,7 +71,7 @@ for more information on how to configure your system to access this repository. libvirt python-libxml2 python-imaging \ python-pam python-M2Crypto python-jsonschema \ rpm-build kvm python-psutil python-ethtool \ - python-ipaddr nfs-client + python-ipaddr python-lxml nfs-client
Packages version requirement: python-psutil >= 0.6.0 diff --git a/src/kimchi/networkxml.py b/src/kimchi/networkxml.py index 786cb69..63cb210 100644 --- a/src/kimchi/networkxml.py +++ b/src/kimchi/networkxml.py @@ -21,6 +21,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import ipaddr +import lxml.etree as ET + + +from lxml.builder import E
# FIXME, do not support ipv6 @@ -109,3 +113,18 @@ def to_network_xml(**kwargs): </network> """ % params return xml + + +def create_vlan_tagged_bridge_xml(bridge, interface, vlan_id): + vlan = E.vlan(E.interface(name=interface)) + vlan.set('tag', vlan_id) + m = E.interface( + E.start(mode='onboot'), + E.bridge( + E.interface( + vlan, + type='vlan', + name='.'.join([interface, vlan_id]))), + type='bridge', + name=bridge) + return ET.tostring(m) diff --git a/tests/test_networkxml.py b/tests/test_networkxml.py index 3073bce..42b3ea9 100644 --- a/tests/test_networkxml.py +++ b/tests/test_networkxml.py @@ -25,6 +25,9 @@ import unittest
import kimchi.networkxml as nxml +import utils + + from kimchi.xmlutils import xpath_get_text
@@ -151,3 +154,22 @@ class NetworkXmlTests(unittest.TestCase): netmask = xpath_get_text(xml, "/network/ip/@netmask")[0] self.assertEquals(netmask, str(ipaddr.IPNetwork(params["net"]).netmask)) + + +class InterfaceXmlTests(unittest.TestCase): + + def test_vlan_tagged_bridge_no_ip(self): + expected_xml = """ + <interface type='bridge' name='br10'> + <start mode='onboot'/> + <bridge> + <interface type='vlan' name='em1.10'> + <vlan tag='10'> + <interface name='em1'/> + </vlan> + </interface> + </bridge> + </interface> + """ + actual_xml = nxml.create_vlan_tagged_bridge_xml('br10', 'em1', '10') + self.assertEquals(actual_xml, utils.normalize_xml(expected_xml)) diff --git a/tests/utils.py b/tests/utils.py index 008f668..79fc2e2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -32,6 +32,7 @@ import unittest
from contextlib import closing +from lxml import etree
import kimchi.server @@ -159,3 +160,8 @@ def patch_auth():
import kimchi.auth kimchi.auth.authenticate = _authenticate + + +def normalize_xml(xml_str): + return etree.tostring(etree.fromstring(xml_str, + etree.XMLParser(remove_blank_text=True)))
-- Thanks and best regards! Sheldon Feng(冯少合)<shaohef@linux.vnet.ibm.com> IBM Linux Technology Center
participants (3)
-
Aline Manera
-
Mark Wu
-
Sheldon