
Also move former xmlutils.py to xmlutils/utils.py and adjust the imports accordingly to it. Signed-off-by: Aline Manera <alinefm@linux.vnet.ibm.com> --- src/kimchi/config.py.in | 2 +- src/kimchi/model/host.py | 4 +-- src/kimchi/model/hostdev.py | 2 +- src/kimchi/model/networks.py | 22 ++++++------- src/kimchi/model/storagepools.py | 16 +++++----- src/kimchi/model/storagevolumes.py | 5 ++- src/kimchi/model/templates.py | 8 ++--- src/kimchi/model/vms.py | 21 ++++++------- src/kimchi/xmlutils.py | 63 -------------------------------------- src/kimchi/xmlutils/__init__.py | 18 +++++++++++ src/kimchi/xmlutils/utils.py | 63 ++++++++++++++++++++++++++++++++++++++ tests/test_networkxml.py | 2 +- tests/test_vmtemplate.py | 2 +- 13 files changed, 121 insertions(+), 107 deletions(-) delete mode 100644 src/kimchi/xmlutils.py create mode 100644 src/kimchi/xmlutils/__init__.py create mode 100644 src/kimchi/xmlutils/utils.py diff --git a/src/kimchi/config.py.in b/src/kimchi/config.py.in index 097c017..4792421 100644 --- a/src/kimchi/config.py.in +++ b/src/kimchi/config.py.in @@ -27,7 +27,7 @@ import threading from ConfigParser import SafeConfigParser -from kimchi.xmlutils import xpath_get_text +from kimchi.xmlutils.utils import xpath_get_text __version__ = "@kimchiversion@" __release__ = "@kimchirelease@" diff --git a/src/kimchi/model/host.py b/src/kimchi/model/host.py index 5d31809..1bc3ca2 100644 --- a/src/kimchi/model/host.py +++ b/src/kimchi/model/host.py @@ -30,7 +30,6 @@ from cherrypy.process.plugins import BackgroundTask from kimchi import disks from kimchi import netinfo -from kimchi import xmlutils from kimchi.basemodel import Singleton from kimchi.model import hostdev from kimchi.exception import InvalidOperation, InvalidParameter @@ -41,6 +40,7 @@ from kimchi.model.vms import DOM_STATE_MAP from kimchi.repositories import Repositories from kimchi.swupdate import SoftwareUpdate from kimchi.utils import add_task, kimchi_log +from kimchi.xmlutils.utils import xpath_get_text HOST_STATS_INTERVAL = 1 @@ -348,7 +348,7 @@ class DevicesModel(object): for host in scsi_hosts: xml = conn.nodeDeviceLookupByName(host).XMLDesc(0) path = '/device/capability/capability/@type' - if 'fc_host' in xmlutils.xpath_get_text(xml, path): + if 'fc_host' in xpath_get_text(xml, path): ret.append(host) return ret # Double verification to catch the case where the libvirt diff --git a/src/kimchi/model/hostdev.py b/src/kimchi/model/hostdev.py index 58ca92e..2a4a311 100644 --- a/src/kimchi/model/hostdev.py +++ b/src/kimchi/model/hostdev.py @@ -23,7 +23,7 @@ from pprint import pprint from kimchi.model.libvirtconnection import LibvirtConnection from kimchi.utils import kimchi_log -from kimchi.xmlutils import dictize +from kimchi.xmlutils.utils import dictize def _get_all_host_dev_infos(libvirt_conn): diff --git a/src/kimchi/model/networks.py b/src/kimchi/model/networks.py index 43f9d50..92fae4d 100644 --- a/src/kimchi/model/networks.py +++ b/src/kimchi/model/networks.py @@ -28,11 +28,11 @@ from xml.sax.saxutils import escape from kimchi import netinfo from kimchi import network as knetwork from kimchi import networkxml -from kimchi import xmlutils from kimchi.exception import InvalidOperation, InvalidParameter from kimchi.exception import MissingParameter, NotFoundError, OperationFailed from kimchi.rollbackcontext import RollbackContext from kimchi.utils import kimchi_log, run_command +from kimchi.xmlutils.utils import xpath_get_text KIMCHI_BRIDGE_PREFIX = 'kb' @@ -301,7 +301,7 @@ class NetworkModel(object): def _vm_get_networks(self, dom): xml = dom.XMLDesc(0) xpath = "/domain/devices/interface[@type='network']/source/@network" - return xmlutils.xpath_get_text(xml, xpath) + return xpath_get_text(xml, xpath) def activate(self, name): network = self.get_network(self.conn.get(), name) @@ -335,25 +335,23 @@ class NetworkModel(object): @staticmethod def get_network_from_xml(xml): - address = xmlutils.xpath_get_text(xml, "/network/ip/@address") + address = xpath_get_text(xml, "/network/ip/@address") address = address and address[0] or '' - netmask = xmlutils.xpath_get_text(xml, "/network/ip/@netmask") + netmask = xpath_get_text(xml, "/network/ip/@netmask") netmask = netmask and netmask[0] or '' net = address and netmask and "/".join([address, netmask]) or '' - dhcp_start = xmlutils.xpath_get_text(xml, - "/network/ip/dhcp/range/@start") + dhcp_start = xpath_get_text(xml, "/network/ip/dhcp/range/@start") dhcp_start = dhcp_start and dhcp_start[0] or '' - dhcp_end = xmlutils.xpath_get_text(xml, "/network/ip/dhcp/range/@end") + dhcp_end = xpath_get_text(xml, "/network/ip/dhcp/range/@end") dhcp_end = dhcp_end and dhcp_end[0] or '' dhcp = {'start': dhcp_start, 'end': dhcp_end} - forward_mode = xmlutils.xpath_get_text(xml, "/network/forward/@mode") + forward_mode = xpath_get_text(xml, "/network/forward/@mode") forward_mode = forward_mode and forward_mode[0] or '' - forward_if = xmlutils.xpath_get_text(xml, - "/network/forward/interface/@dev") - forward_pf = xmlutils.xpath_get_text(xml, "/network/forward/pf/@dev") - bridge = xmlutils.xpath_get_text(xml, "/network/bridge/@name") + forward_if = xpath_get_text(xml, "/network/forward/interface/@dev") + forward_pf = xpath_get_text(xml, "/network/forward/pf/@dev") + bridge = xpath_get_text(xml, "/network/bridge/@name") bridge = bridge and bridge[0] or '' return {'subnet': net, 'dhcp': dhcp, 'bridge': bridge, 'forward': {'mode': forward_mode, diff --git a/src/kimchi/model/storagepools.py b/src/kimchi/model/storagepools.py index 49b2f6a..d44e079 100644 --- a/src/kimchi/model/storagepools.py +++ b/src/kimchi/model/storagepools.py @@ -19,7 +19,6 @@ import libvirt -from kimchi import xmlutils from kimchi.scan import Scanner from kimchi.exception import InvalidOperation, MissingParameter from kimchi.exception import NotFoundError, OperationFailed @@ -27,6 +26,7 @@ from kimchi.model.config import CapabilitiesModel from kimchi.model.host import DeviceModel from kimchi.model.libvirtstoragepool import StoragePoolDef from kimchi.utils import add_task, kimchi_log, pool_name_from_uri, run_command +from kimchi.xmlutils.utils import xpath_get_text ISO_POOL_NAME = u'kimchi_isos' @@ -212,7 +212,7 @@ class StoragePoolModel(object): return source for key, val in STORAGE_SOURCES[pool_type].items(): - res = xmlutils.xpath_get_text(pool_xml, val) + res = xpath_get_text(pool_xml, val) if len(res) == 1: source[key] = res[0] elif len(res) == 0: @@ -224,7 +224,7 @@ class StoragePoolModel(object): def _nfs_status_online(self, pool, poolArgs=None): if not poolArgs: xml = pool.XMLDesc(0) - pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0] + pool_type = xpath_get_text(xml, "/pool/@type")[0] source = self._get_storage_source(pool_type, xml) poolArgs = {} poolArgs['name'] = pool.name() @@ -245,8 +245,8 @@ class StoragePoolModel(object): autostart = True if pool.autostart() else False persistent = True if pool.isPersistent() else False xml = pool.XMLDesc(0) - path = xmlutils.xpath_get_text(xml, "/pool/target/path")[0] - pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0] + path = xpath_get_text(xml, "/pool/target/path")[0] + pool_type = xpath_get_text(xml, "/pool/@type")[0] source = self._get_storage_source(pool_type, xml) # FIXME: nfs workaround - prevent any libvirt operation # for a nfs if the corresponding NFS server is down. @@ -319,7 +319,7 @@ class StoragePoolModel(object): if 'disks' in params: # check if pool is type 'logical' xml = pool.XMLDesc(0) - pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0] + pool_type = xpath_get_text(xml, "/pool/@type")[0] if pool_type != 'logical': raise InvalidOperation('KCHPOOL0029E') self._update_lvm_disks(name, params['disks']) @@ -331,7 +331,7 @@ class StoragePoolModel(object): # FIXME: nfs workaround - do not activate a NFS pool # if the NFS server is not reachable. xml = pool.XMLDesc(0) - pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0] + pool_type = xpath_get_text(xml, "/pool/@type")[0] if pool_type == 'netfs' and not self._nfs_status_online(pool): # block the user from activating the pool. source = self._get_storage_source(pool_type, xml) @@ -362,7 +362,7 @@ class StoragePoolModel(object): # FIXME: nfs workaround - do not try to deactivate a NFS pool # if the NFS server is not reachable. xml = pool.XMLDesc(0) - pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0] + pool_type = xpath_get_text(xml, "/pool/@type")[0] if pool_type == 'netfs' and not self._nfs_status_online(pool): # block the user from dactivating the pool. source = self._get_storage_source(pool_type, xml) diff --git a/src/kimchi/model/storagevolumes.py b/src/kimchi/model/storagevolumes.py index 23927e3..1ee8d0a 100644 --- a/src/kimchi/model/storagevolumes.py +++ b/src/kimchi/model/storagevolumes.py @@ -24,7 +24,6 @@ import urllib2 import libvirt -from kimchi import xmlutils from kimchi.config import READONLY_POOL_TYPE from kimchi.exception import InvalidOperation, InvalidParameter, IsoFormatError from kimchi.exception import MissingParameter, NotFoundError, OperationFailed @@ -34,6 +33,7 @@ from kimchi.model.tasks import TaskModel from kimchi.model.vms import VMsModel, VMModel from kimchi.utils import add_task, kimchi_log from kimchi.vmdisks import get_vm_disk, get_vm_disk_list +from kimchi.xmlutils.utils import xpath_get_text VOLUME_TYPE_MAP = {0: 'file', @@ -286,8 +286,7 @@ class StorageVolumeModel(object): info = vol.info() xml = vol.XMLDesc(0) try: - fmt = xmlutils.xpath_get_text( - xml, "/volume/target/format/@type")[0] + fmt = xpath_get_text(xml, "/volume/target/format/@type")[0] except IndexError: # Not all types of libvirt storage can provide volume format # infomation. When there is no format information, we assume diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py index 9278cdc..bb5bd3a 100644 --- a/src/kimchi/model/templates.py +++ b/src/kimchi/model/templates.py @@ -22,13 +22,13 @@ import os import libvirt -from kimchi import xmlutils from kimchi.exception import InvalidOperation, InvalidParameter from kimchi.exception import NotFoundError, OperationFailed from kimchi.kvmusertests import UserTests from kimchi.utils import pool_name_from_uri from kimchi.utils import probe_file_permission_as_user from kimchi.vmtemplate import VMTemplate +from kimchi.xmlutils.utils import xpath_get_text class TemplatesModel(object): @@ -88,7 +88,7 @@ class TemplatesModel(object): def template_volume_validate(self, tmp_volumes, pool): kwargs = {'conn': self.conn, 'objstore': self.objstore} - pool_type = xmlutils.xpath_get_text(pool.XMLDesc(0), "/pool/@type")[0] + pool_type = xpath_get_text(pool.XMLDesc(0), "/pool/@type")[0] pool_name = pool.name() # as we discussion, we do not mix disks from 2 different types of @@ -235,12 +235,12 @@ class LibvirtVMTemplate(VMTemplate): def _get_storage_path(self): pool = self._storage_validate() xml = pool.XMLDesc(0) - return xmlutils.xpath_get_text(xml, "/pool/target/path")[0] + return xpath_get_text(xml, "/pool/target/path")[0] def _get_storage_type(self): pool = self._storage_validate() xml = pool.XMLDesc(0) - return xmlutils.xpath_get_text(xml, "/pool/@type")[0] + return xpath_get_text(xml, "/pool/@type")[0] def _get_volume_path(self, pool, vol): pool = self._storage_validate() diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py index 58686cd..1089464 100644 --- a/src/kimchi/model/vms.py +++ b/src/kimchi/model/vms.py @@ -31,7 +31,6 @@ import libvirt from cherrypy.process.plugins import BackgroundTask from kimchi import vnc -from kimchi import xmlutils from kimchi.config import READONLY_POOL_TYPE from kimchi.exception import InvalidOperation, InvalidParameter from kimchi.exception import NotFoundError, OperationFailed @@ -43,7 +42,7 @@ from kimchi.model.utils import set_metadata_node from kimchi.screenshot import VMScreenshot from kimchi.utils import import_class, kimchi_log, run_setfacl_set_attr from kimchi.utils import template_name_from_uri -from kimchi.xmlutils import xpath_get_text +from kimchi.xmlutils.utils import xpath_get_text, xml_item_update DOM_STATE_MAP = {0: 'nostate', @@ -352,7 +351,7 @@ class VMModel(object): if type(val) == int: val = str(val) xpath = VM_STATIC_UPDATE_PARAMS[key] - new_xml = xmlutils.xml_item_update(new_xml, xpath, val) + new_xml = xml_item_update(new_xml, xpath, val) if 'graphics' in params: new_xml = self._update_graphics(dom, new_xml, params) @@ -445,7 +444,7 @@ class VMModel(object): def _vm_get_disk_paths(self, dom): xml = dom.XMLDesc(0) xpath = "/domain/devices/disk[@device='disk']/source/@file" - return xmlutils.xpath_get_text(xml, xpath) + return xpath_get_text(xml, xpath) @staticmethod def get_vm(name, conn): @@ -480,7 +479,7 @@ class VMModel(object): vol = conn.storageVolLookupByPath(path) pool = vol.storagePoolLookupByVolume() xml = pool.XMLDesc(0) - pool_type = xmlutils.xpath_get_text(xml, "/pool/@type")[0] + pool_type = xpath_get_text(xml, "/pool/@type")[0] if pool_type not in READONLY_POOL_TYPE: vol.delete(0) try: @@ -498,7 +497,7 @@ class VMModel(object): dom = self.get_vm(name, self.conn) xml = dom.XMLDesc(0) xpath = "/domain/devices/disk[@device='cdrom']/source/@file" - isofiles = xmlutils.xpath_get_text(xml, xpath) + isofiles = xpath_get_text(xml, xpath) for iso in isofiles: run_setfacl_set_attr(iso) @@ -538,25 +537,25 @@ class VMModel(object): xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE) expr = "/domain/devices/graphics/@type" - res = xmlutils.xpath_get_text(xml, expr) + res = xpath_get_text(xml, expr) graphics_type = res[0] if res else None expr = "/domain/devices/graphics/@listen" - res = xmlutils.xpath_get_text(xml, expr) + res = xpath_get_text(xml, expr) graphics_listen = res[0] if res else None graphics_port = graphics_passwd = graphics_passwdValidTo = None if graphics_type: expr = "/domain/devices/graphics[@type='%s']/@port" - res = xmlutils.xpath_get_text(xml, expr % graphics_type) + res = xpath_get_text(xml, expr % graphics_type) graphics_port = int(res[0]) if res else None expr = "/domain/devices/graphics[@type='%s']/@passwd" - res = xmlutils.xpath_get_text(xml, expr % graphics_type) + res = xpath_get_text(xml, expr % graphics_type) graphics_passwd = res[0] if res else None expr = "/domain/devices/graphics[@type='%s']/@passwdValidTo" - res = xmlutils.xpath_get_text(xml, expr % graphics_type) + res = xpath_get_text(xml, expr % graphics_type) if res: to = time.mktime(time.strptime(res[0], '%Y-%m-%dT%H:%M:%S')) graphics_passwdValidTo = to - time.mktime(time.gmtime()) diff --git a/src/kimchi/xmlutils.py b/src/kimchi/xmlutils.py deleted file mode 100644 index 00a9d55..0000000 --- a/src/kimchi/xmlutils.py +++ /dev/null @@ -1,63 +0,0 @@ -# -# Project Kimchi -# -# Copyright IBM, Corp. 2013-2014 -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -import libxml2 -from lxml import objectify - - -from xml.etree import ElementTree - - -def xpath_get_text(xml, expr): - doc = libxml2.parseDoc(xml) - res = doc.xpathEval(expr) - ret = [None if x.children is None else x.children.content for x in res] - - doc.freeDoc() - return ret - - -def xml_item_update(xml, xpath, value): - root = ElementTree.fromstring(xml) - item = root.find(xpath) - item.text = value - return ElementTree.tostring(root, encoding="utf-8") - - -def dictize(xmlstr): - root = objectify.fromstring(xmlstr) - return {root.tag: _dictize(root)} - - -def _dictize(e): - d = {} - if e.text is not None: - if not e.attrib and e.countchildren() == 0: - return e.pyval - d['pyval'] = e.pyval - d.update(e.attrib) - for child in e.iterchildren(): - if child.tag in d: - continue - if len(child) > 1: - d[child.tag] = [ - _dictize(same_tag_child) for same_tag_child in child] - else: - d[child.tag] = _dictize(child) - return d diff --git a/src/kimchi/xmlutils/__init__.py b/src/kimchi/xmlutils/__init__.py new file mode 100644 index 0000000..ca7ede4 --- /dev/null +++ b/src/kimchi/xmlutils/__init__.py @@ -0,0 +1,18 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/src/kimchi/xmlutils/utils.py b/src/kimchi/xmlutils/utils.py new file mode 100644 index 0000000..2af1c2c --- /dev/null +++ b/src/kimchi/xmlutils/utils.py @@ -0,0 +1,63 @@ +# +# Project Kimchi +# +# Copyright IBM, Corp. 2014 +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +import libxml2 +from lxml import objectify + + +from xml.etree import ElementTree + + +def xpath_get_text(xml, expr): + doc = libxml2.parseDoc(xml) + res = doc.xpathEval(expr) + ret = [None if x.children is None else x.children.content for x in res] + + doc.freeDoc() + return ret + + +def xml_item_update(xml, xpath, value): + root = ElementTree.fromstring(xml) + item = root.find(xpath) + item.text = value + return ElementTree.tostring(root, encoding="utf-8") + + +def dictize(xmlstr): + root = objectify.fromstring(xmlstr) + return {root.tag: _dictize(root)} + + +def _dictize(e): + d = {} + if e.text is not None: + if not e.attrib and e.countchildren() == 0: + return e.pyval + d['pyval'] = e.pyval + d.update(e.attrib) + for child in e.iterchildren(): + if child.tag in d: + continue + if len(child) > 1: + d[child.tag] = [ + _dictize(same_tag_child) for same_tag_child in child] + else: + d[child.tag] = _dictize(child) + return d diff --git a/tests/test_networkxml.py b/tests/test_networkxml.py index d714413..674008d 100644 --- a/tests/test_networkxml.py +++ b/tests/test_networkxml.py @@ -25,7 +25,7 @@ import kimchi.networkxml as nxml import utils -from kimchi.xmlutils import xpath_get_text +from kimchi.xmlutils.utils import xpath_get_text class NetworkXmlTests(unittest.TestCase): diff --git a/tests/test_vmtemplate.py b/tests/test_vmtemplate.py index 2a6fb8e..550bb2a 100644 --- a/tests/test_vmtemplate.py +++ b/tests/test_vmtemplate.py @@ -23,7 +23,7 @@ import uuid from kimchi.vmtemplate import VMTemplate -from kimchi.xmlutils import xpath_get_text +from kimchi.xmlutils.utils import xpath_get_text class VMTemplateTests(unittest.TestCase): -- 1.9.3