"""
Module simplifying manipulation of XML described at
http://libvirt.org/formatdomain.html
"""

import logging
import platform

from .. import xml_utils
from .. import utils_misc
from ..libvirt_xml import base, accessors, xcepts
from ..libvirt_xml.devices import librarian


class VMXMLDevices(list):

    """
    List of device instances from classes handed out by librarian.get()
    """

    @staticmethod
    def __type_check__(other):
        try:
            # Raise error if object isn't dict-like or doesn't have key
            device_tag = other['device_tag']
            # Check that we have support for this type
            librarian.get(device_tag)
        except (AttributeError, TypeError, xcepts.LibvirtXMLError):
            # Required to always raise TypeError for list API in VMXML class
            raise TypeError("Unsupported item type: %s" % str(type(other)))

    def __setitem__(self, key, value):
        self.__type_check__(value)
        super(VMXMLDevices, self).__setitem__(key, value)
        return self

    def append(self, value):
        self.__type_check__(value)
        super(VMXMLDevices, self).append(value)
        return self

    def extend(self, iterable):
        # Make sure __type_check__ happens
        for item in iterable:
            self.append(item)
        return self

    def by_device_tag(self, tag):
        result = VMXMLDevices()
        for device in self:
            if device.device_tag == tag:
                result.append(device)
        return result


class VMXMLBase(base.LibvirtXMLBase):

    """
    Accessor methods for VMXML class properties (items in __slots__)

    Properties:
        hypervisor_type: string, hypervisor type name
            get: return domain's type attribute value
            set: change domain type attribute value
            del: raise xcepts.LibvirtXMLError
        vm_name: string, name of the vm
            get: return text value of name tag
            set: set text value of name tag
            del: raise xcepts.LibvirtXMLError
        uuid: string, uuid string for vm
            get: return text value of uuid tag
            set: set text value for (new) uuid tag (unvalidated)
            del: remove uuid tag
        title: string, a short description of vm
            get: return text value of title tag
            set: set text value for title tag
            del: remove title tag
        vcpu, memory, max_mem, current_mem, iothreads: integers
            get: returns integer
            set: set integer
            del: removes tag
        dumpcore: string,  control guest OS memory dump
            get: return text value
            set: set 'on' or 'off' for guest OS memory dump
            del: removes tag
        numa_memory: dictionary
            get: return dictionary of numatune/memory attributes
            set: set numatune/memory attributes from dictionary
            del: remove numatune/memory tag
        numa_memnode: list dict of memnode attributes cellid, mode and nodeset
            get: return list of dictionary with numatune/memnode attributes
            set: set multiple numatune/memnode attributes from dictionary list
            del: remove numatune/memnode tag
        on_poweroff: string, action to take when the guest requests a poweroff
            get: returns text value of on_poweroff tag
            set: set test of on_poweroff tag
            del: remove on_poweroff tag
        on_reboot: string, action to take when the guest requests a reboot
            get: returns text value of on_reboot tag
            set: set test of on_reboot tag
            del: remove on_reboot tag
        on_crash: string, action to take when the guest crashes
            get: returns text value of on_crash tag
            set: set test of on_crash tag
            del: remove on_crash tag
        devices: VMXMLDevices (list-like)
            get: returns VMXMLDevices instance for all devices
            set: Define all devices from VMXMLDevices instance
            del: remove all devices
        cputune: VMCPUTuneXML
            get: return VMCPUTuneXML instance for the domain.
            set: Define cputune tag from a VMCPUTuneXML instance.
            del: remove cputune tag
        cpu: VMCPUXML
            get: return VMCPUXML instance for the domain.
            set: Define cpu tag from a VMCPUXML instance.
            del: remove cpu tag
        current_vcpu: string, 'current' attribute of vcpu tag
            get: return a string for 'current' attribute of vcpu
            set: change 'current' attribute of vcpu
            del: remove 'current' attribute of vcpu
        placement: string, 'placement' attribute of vcpu tag
            get: return a string for 'placement' attribute of vcpu
            set: change 'placement' attribute of vcpu
            del: remove 'placement' attribute of vcpu
        cpuset: string, 'cpuset' attribute of vcpu tag
            get: return a string for 'cpuset' attribute of vcpu
            set: change 'cpuset' attribute of vcpu
            del: remove 'cpuset' attribute of vcpu
        vcpus: VMVCPUSXML
            get: return VMVCPUSXML instance for the domain
            set: define vcpus tag from a VMVCPUSXML instance
            del: remove vcpus tag
        emulatorpin: string, cpuset value (see man virsh: cpulist)
            get: return text value of cputune/emulatorpin attributes
            set: set cputune/emulatorpin attributes from string
            del: remove cputune/emulatorpin tag
        features: VMFeaturesXML
            get: return VMFeaturesXML instances for the domain.
            set: define features tag from a VMFeaturesXML instances.
            del: remove features tag
        mem_backing: VMMemBackingXML
            get: return VMMemBackingXML instances for the domain.
            set: define memoryBacking tag from a VMMemBackingXML instances.
            del: remove memoryBacking tag
        max_mem_unit: string, 'unit' attribute of memory
            get: return text value of memory unit attribute
            set: set memory unit attribute
            del: remove memory unit attribute
        current_mem_unit: string, 'unit' attribute of current_memory
            get: return text value of current_memory unit attribute
            set: set current_memory unit attribute
            del: remove current_memory unit attribute
        memory_unit: string, 'unit' attribute of memory
            get: return text value of memory unit attribute
            set: set memory unit attribute
            del: remove memory unit attribute
        memtune: VMMemTuneXML
            get: return VMMemTuneXML instance for the domain.
            set: Define memtune tag from a VMCPUTuneXML instance.
            del: remove memtune tag
    """

    # Additional names of attributes and dictionary-keys instances may contain
    __slots__ = ('hypervisor_type', 'vm_name', 'uuid', 'title', 'vcpu', 'max_mem',
                 'current_mem', 'dumpcore', 'numa_memory', 'numa_memnode',
                 'devices', 'seclabel', 'cputune', 'placement', 'cpuset',
                 'current_vcpu', 'vcpus', 'os', 'cpu', 'pm', 'on_poweroff',
                 'on_reboot', 'on_crash', 'features', 'mb', 'max_mem_unit',
                 'current_mem_unit', 'memtune', 'max_mem_rt', 'max_mem_rt_unit',
                 'max_mem_rt_slots', 'iothreads', 'iothreadids', 'memory', 'perf')

    __uncompareable__ = base.LibvirtXMLBase.__uncompareable__

    __schema_name__ = "domain"

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLAttribute(property_name="hypervisor_type",
                               libvirtxml=self,
                               forbidden=None,
                               parent_xpath='/',
                               tag_name='domain',
                               attribute='type')
        accessors.XMLElementText(property_name="vm_name",
                                 libvirtxml=self,
                                 forbidden=None,
                                 parent_xpath='/',
                                 tag_name='name')
        accessors.XMLElementText(property_name="uuid",
                                 libvirtxml=self,
                                 forbidden=None,
                                 parent_xpath='/',
                                 tag_name='uuid')
        accessors.XMLElementText(property_name="title",
                                 libvirtxml=self,
                                 forbidden=None,
                                 parent_xpath='/',
                                 tag_name='title')
        accessors.XMLElementInt(property_name="iothreads",
                                libvirtxml=self,
                                forbidden=None,
                                parent_xpath='/',
                                tag_name='iothreads')
        accessors.XMLElementInt(property_name="vcpu",
                                libvirtxml=self,
                                forbidden=None,
                                parent_xpath='/',
                                tag_name='vcpu')
        accessors.XMLAttribute(property_name="current_vcpu",
                               libvirtxml=self,
                               forbidden=None,
                               parent_xpath='/',
                               tag_name='vcpu',
                               attribute='current')
        accessors.XMLAttribute(property_name="placement",
                               libvirtxml=self,
                               forbidden=None,
                               parent_xpath='/',
                               tag_name='vcpu',
                               attribute='placement')
        accessors.XMLAttribute(property_name="cpuset",
                               libvirtxml=self,
                               forbidden=None,
                               parent_xpath='/',
                               tag_name='vcpu',
                               attribute='cpuset')
        accessors.XMLElementInt(property_name="max_mem",
                                libvirtxml=self,
                                forbidden=None,
                                parent_xpath='/',
                                tag_name='memory')
        accessors.XMLAttribute(property_name="max_mem_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='memory',
                               attribute='unit')
        accessors.XMLAttribute(property_name="dumpcore",
                               libvirtxml=self,
                               forbidden=None,
                               parent_xpath='/',
                               tag_name='memory',
                               attribute='dumpCore')
        accessors.XMLElementInt(property_name="current_mem",
                                libvirtxml=self,
                                forbidden=None,
                                parent_xpath='/',
                                tag_name='currentMemory')
        accessors.XMLAttribute(property_name="current_mem_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='currentMemory',
                               attribute='unit')
        accessors.XMLElementInt(property_name="max_mem_rt",
                                libvirtxml=self,
                                forbidden=None,
                                parent_xpath='/',
                                tag_name='maxMemory')
        accessors.XMLAttribute(property_name="max_mem_rt_slots",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='maxMemory',
                               attribute='slots')
        accessors.XMLAttribute(property_name="max_mem_rt_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='maxMemory',
                               attribute='unit')
        accessors.XMLElementInt(property_name="memory",
                                libvirtxml=self,
                                forbidden=None,
                                parent_xpath='/',
                                tag_name='memory')
        accessors.XMLAttribute(property_name="memory_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='memory',
                               attribute='unit')
        accessors.XMLElementNest(property_name='os',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='os',
                                 subclass=VMOSXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementDict(property_name="numa_memory",
                                 libvirtxml=self,
                                 forbidden=None,
                                 parent_xpath='numatune',
                                 tag_name='memory')
        accessors.XMLElementList(property_name="numa_memnode",
                                 libvirtxml=self,
                                 parent_xpath='numatune',
                                 marshal_from=self.marshal_from_memnode,
                                 marshal_to=self.marshal_to_memnode)
        accessors.XMLElementNest(property_name="perf",
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='perf',
                                 subclass=VMPerfXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementNest(property_name='cputune',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='cputune',
                                 subclass=VMCPUTuneXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementNest(property_name='cpu',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='cpu',
                                 subclass=VMCPUXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementNest(property_name='vcpus',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='vcpus',
                                 subclass=VMVCPUSXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementNest(property_name='pm',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='pm',
                                 subclass=VMPMXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementText(property_name="on_poweroff",
                                 libvirtxml=self,
                                 forbidden=None,
                                 parent_xpath='/',
                                 tag_name='on_poweroff')
        accessors.XMLElementText(property_name="on_reboot",
                                 libvirtxml=self,
                                 forbidden=None,
                                 parent_xpath='/',
                                 tag_name='on_reboot')
        accessors.XMLElementText(property_name="on_crash",
                                 libvirtxml=self,
                                 forbidden=None,
                                 parent_xpath='/',
                                 tag_name='on_crash')
        accessors.XMLElementNest(property_name='features',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='features',
                                 subclass=VMFeaturesXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementNest(property_name='mb',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='memoryBacking',
                                 subclass=VMMemBackingXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementNest(property_name='memtune',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='memtune',
                                 subclass=VMMemTuneXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLElementNest(property_name='iothreadids',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='iothreadids',
                                 subclass=VMIothreadidsXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        super(VMXMLBase, self).__init__(virsh_instance=virsh_instance)

    @staticmethod
    def marshal_from_memnode(item, index, libvirtxml):
        """
        Convert a dict to memnode tag and attributes.
        """
        del index
        del libvirtxml
        if not isinstance(item, dict):
            raise xcepts.LibvirtXMLError("Expected a dictionary of memnode "
                                         "attributes, not a %s"
                                         % str(item))
        return ('memnode', dict(item))

    @staticmethod
    def marshal_to_memnode(tag, attr_dict, index, libvirtxml):
        """
        Convert a memnode tag and attributes to a dict.
        """
        del index
        del libvirtxml
        if tag != 'memnode':
            return None
        return dict(attr_dict)

    def get_devices(self, device_type=None):
        """
        Put all nodes of devices into a VMXMLDevices instance.
        """
        devices = VMXMLDevices()
        all_devices = self.xmltreefile.find('devices')
        if device_type is not None:
            device_nodes = all_devices.findall(device_type)
        else:
            device_nodes = all_devices
        for node in device_nodes:
            device_tag = node.tag
            device_class = librarian.get(device_tag)
            new_one = device_class.new_from_element(node,
                                                    virsh_instance=self.virsh)
            devices.append(new_one)
        return devices

    def set_devices(self, value):
        """
        Define devices based on contents of VMXMLDevices instance
        """
        value_type = type(value)
        if not issubclass(value_type, VMXMLDevices):
            raise xcepts.LibvirtXMLError("Value %s Must be a VMXMLDevices or "
                                         "subclass not a %s"
                                         % (str(value), str(value_type)))
        # Start with clean slate
        exist_dev = self.xmltreefile.find('devices')
        if exist_dev is not None:
            self.del_devices()
        if len(value) > 0:
            devices_element = xml_utils.ElementTree.SubElement(
                self.xmltreefile.getroot(), 'devices')
            for device in value:
                # Separate the element from the tree
                device_element = device.xmltreefile.getroot()
                devices_element.append(device_element)
        self.xmltreefile.write()

    def del_devices(self):
        """
        Remove all devices
        """
        try:
            self.xmltreefile.remove_by_xpath('/devices', remove_all=True)
        except (AttributeError, TypeError):
            pass  # Element already doesn't exist
        self.xmltreefile.write()

    def get_seclabel(self):
        """
        Return seclabel + child attribute dict list or raise LibvirtXML error

        :return: None if no seclabel in xml,
                 list contains dict of seclabel's attributs and children.
        """
        __children_list__ = ['label', 'baselabel', 'imagelabel']

        seclabel_node = self.xmltreefile.findall("seclabel")
        # no seclabel tag found in xml.
        if seclabel_node == []:
            raise xcepts.LibvirtXMLError("Seclabel for this domain does not "
                                         "exist")
        seclabels = []
        for i in range(len(seclabel_node)):
            seclabel = dict(list(seclabel_node[i].items()))
            for child_name in __children_list__:
                child_node = seclabel_node[i].find(child_name)
                if child_node is not None:
                    seclabel[child_name] = child_node.text
            seclabels.append(seclabel)

        return seclabels

    def set_seclabel(self, seclabel_dict_list):
        """
        Set seclabel of vm. Delete all seclabels if seclabel exists, create
        new seclabels use dict values from given seclabel_dict_list in
        xmltreefile.
        """
        __attributs_list__ = ['type', 'model', 'relabel']
        __children_list__ = ['label', 'baselabel', 'imagelabel']

        # check the type of seclabel_dict_list and value.
        if not isinstance(seclabel_dict_list, list):
            raise xcepts.LibvirtXMLError("seclabel_dict_list should be a "
                                         "instance of list, but not a %s.\n"
                                         % type(seclabel_dict_list))
        for seclabel_dict in seclabel_dict_list:
            if not isinstance(seclabel_dict, dict):
                raise xcepts.LibvirtXMLError("value in seclabel_dict_list"
                                             "should be a instance of dict "
                                             "but not a %s.\n"
                                             % type(seclabel_dict))

        seclabel_nodes = self.xmltreefile.findall("seclabel")
        if seclabel_nodes is not None:
            for i in range(len(seclabel_nodes)):
                self.del_seclabel()
        for i in range(len(seclabel_dict_list)):
            seclabel_node = xml_utils.ElementTree.SubElement(
                self.xmltreefile.getroot(),
                "seclabel")

            for key, value in list(seclabel_dict_list[i].items()):
                if key in __children_list__:
                    child_node = seclabel_node.find(key)
                    if child_node is None:
                        child_node = xml_utils.ElementTree.SubElement(
                            seclabel_node,
                            key)
                    child_node.text = value

                elif key in __attributs_list__:
                    seclabel_node.set(key, value)

                else:
                    continue

            self.xmltreefile.write()

    def del_seclabel(self, by_attr=None):
        """
        Remove seclabel tags from a domain. The set of seclabels for removal
        can be restricted by matching attributes in by_attr.
        :param by_attr: List of tuples describing the seclabel to be removed,
        e.g.
        1. [('model', 'selinux'), ('relabel', 'yes')] removes selinux seclabel
         only if relabling is enabled.
        2. [('type', 'dynamic')] removes all seclables that should get unique
         security labels by libvirt.
        3. by_attr=None will remove all seclabel tags.
        """
        tag = "/seclabel"
        try:
            if by_attr:
                for seclabel in self.xmltreefile.findall(tag):
                    if all(attr in seclabel.items() for attr in by_attr):
                        self.xmltreefile.remove(seclabel)
            else:
                self.xmltreefile.remove_by_xpath(tag, remove_all=True)
        except (AttributeError, TypeError):
            pass  # Element already doesn't exist
        self.xmltreefile.write()

    def set_controller(self, controller_list):
        """
        Set controller of vm. Create new controllers use xmltreefile
        from given controller_list.
        """

        # check the type of controller_list and value.
        if not isinstance(controller_list, list):
            raise xcepts.LibvirtXMLError("controller_element_list should be a"
                                         "instance of list, but not a %s.\n"
                                         % type(controller_list))

        devices_element = self.xmltreefile.find("devices")
        for contl in controller_list:
            element = xml_utils.ElementTree.ElementTree(
                file=contl.xml)
            devices_element.append(element.getroot())
        self.xmltreefile.write()

    def del_controller(self, controller_type=None):
        """
        Delete controllers according controller type

        :return: None if deleting all controllers
        """
        # no seclabel tag found in xml.
        del_controllers = self.get_controllers(controller_type=controller_type)
        if del_controllers == []:
            logging.debug("Controller %s for this domain does not "
                          "exist" % controller_type)

        for controller in del_controllers:
            self.xmltreefile.remove(controller)

    def get_controllers(self, controller_type=None, model=None):
        """
        Get controllers according controller type and/or model type

        :param controller_type: type of controllers need to get
        :param model: model of controllers need to get
        :return: controller list
        """
        all_controllers = self.xmltreefile.findall("devices/controller")
        type_controllers = []
        for controller in all_controllers:
            if ((controller_type is not None and controller.get("type") != controller_type) or
                    (model is not None and model != controller.get("model"))):
                continue
            type_controllers.append(controller)
        return type_controllers


class VMXML(VMXMLBase):

    """
    Higher-level manipulations related to VM's XML or guest/host state
    """

    # Must copy these here or there will be descriptor problems
    __slots__ = []

    def __init__(self, hypervisor_type='kvm', virsh_instance=base.virsh):
        """
        Create new VM XML instance
        """
        super(VMXML, self).__init__(virsh_instance=virsh_instance)
        # Setup some bare-bones XML to build upon
        self.xml = u"<domain type='%s'></domain>" % hypervisor_type

    @staticmethod  # static method (no self) needed b/c calls VMXML.__new__
    def new_from_dumpxml(vm_name, options="", virsh_instance=base.virsh):
        """
        Return new VMXML instance from virsh dumpxml command

        :param vm_name: Name of VM to dumpxml
        :param virsh_instance: virsh module or instance to use
        :return: New initialized VMXML instance
        """
        # TODO: Look up hypervisor_type on incoming XML
        vmxml = VMXML(virsh_instance=virsh_instance)
        result = virsh_instance.dumpxml(vm_name, extra=options)
        vmxml['xml'] = result.stdout_text.strip()
        return vmxml

    @staticmethod
    def new_from_inactive_dumpxml(vm_name, options="", virsh_instance=base.virsh):
        """
        Return new VMXML instance of inactive domain from virsh dumpxml command

        :param vm_name: Name of VM to dumpxml
        :param options: virsh dumpxml command's options
        :param virsh_instance: virsh module or instance to use
        :return: New initialized VMXML instance
        """
        if options.find("--inactive") == -1:
            options += " --inactive"
        return VMXML.new_from_dumpxml(vm_name, options, virsh_instance)

    @staticmethod
    def get_device_class(type_name):
        """
        Return class that handles type_name devices, or raise exception.
        """
        return librarian.get(type_name)

    def undefine(self, options=None, virsh_instance=base.virsh):
        """Undefine this VM with libvirt retaining XML in instance"""
        try:
            nvram = getattr(getattr(self, "os"), "nvram")
        except xcepts.LibvirtXMLNotFoundError:
            nvram = None

        if nvram:
            if options is None:
                options = "--nvram"
            if "--nvram" not in options:
                options += " --nvram"

        return virsh_instance.remove_domain(self.vm_name, options)

    def define(self, virsh_instance=base.virsh):
        """Define VM with virsh from this instance"""
        result = virsh_instance.define(self.xml)
        if result.exit_status:
            logging.debug("Define %s failed.\n"
                          "Detail: %s.", self.vm_name,
                          result.stderr_text)
            return False
        return True

    def sync(self, options=None, virsh_instance=base.virsh):
        """Rebuild VM with the config file."""
        # If target vm no longer exist, this will raise an exception.
        try:
            backup = self.new_from_dumpxml(self.vm_name, virsh_instance=virsh_instance)
        except IOError:
            logging.debug("Failed to backup %s.", self.vm_name)
            backup = None

        if not self.undefine(options, virsh_instance=virsh_instance):
            raise xcepts.LibvirtXMLError("Failed to undefine %s."
                                         % self.vm_name)
        if not self.define(virsh_instance=virsh_instance):
            if backup:
                backup.define(virsh_instance=virsh_instance)
            raise xcepts.LibvirtXMLError("Failed to define %s from xml:\n%s"
                                         % (self.vm_name, self.xmltreefile))

    @staticmethod
    def vm_rename(vm, new_name, uuid=None, virsh_instance=base.virsh):
        """
        Rename a vm from its XML.

        :param vm: VM class type instance
        :param new_name: new name of vm
        :param uuid: new_vm's uuid, if None libvirt will generate.
        :return: a new VM instance or raise LibvirtXMLError
        """
        def _cleanup(details=""):
            backup.define()
            if start_vm:
                vm.start()
            raise xcepts.LibvirtXMLError(details)

        start_vm = False
        if vm.is_alive():
            vm.destroy(gracefully=True)
            start_vm = True
        vmxml = VMXML.new_from_dumpxml(vm.name, virsh_instance=virsh_instance)
        backup = vmxml.copy()

        # Undefine old VM firstly
        if not vmxml.undefine():
            _cleanup(details="Undefine VM %s failed" % vm.name)
        # Alter the XML
        str_old = "domain-" + vm.name
        str_new = "domain-" + new_name
        vmxml.vm_name = new_name
        for channel in vmxml.get_agent_channels():
            for child in channel._children:
                if 'path' in list(child.attrib.keys()):
                    child.attrib['path'] = child.attrib['path'].replace(str_old, str_new)
        if uuid is None:
            # UUID will be regenerated automatically
            del vmxml.uuid
        else:
            vmxml.uuid = uuid
        logging.debug("Rename %s to %s.", vm.name, new_name)
        if not vmxml.define():
            _cleanup(details="Define VM %s failed" % new_name)
        # Update the name and uuid property for VM object
        vm.name = new_name
        vm.uuid = VMXML.new_from_dumpxml(new_name,
                                         virsh_instance=virsh_instance).uuid
        if uuid is not None and utils_misc.compare_uuid(vm.uuid, uuid) != 0:
            _cleanup(details="UUID %s is not expected %s" % (vm.uuid, uuid))
        if start_vm:
            vm.start()
        return vm

    @staticmethod
    def set_pm_suspend(vm_name, mem="yes", disk="yes", virsh_instance=base.virsh):
        """
        Add/set pm suspend Support

        :params vm_name: Name of defined vm
        :params mem: Enable suspend to memory
        :params disk: Enable suspend to disk
        """
        # Build a instance of class VMPMXML.
        pm = VMPMXML()
        pm.mem_enabled = mem
        pm.disk_enabled = disk
        # Set pm to the new instance.
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        vmxml.pm = pm
        vmxml.sync()

    @staticmethod
    def set_vm_vcpus(vm_name, vcpus, current=None, sockets=None, cores=None,
                     threads=None, add_topology=False, topology_correction=False,
                     update_numa=True, numa_number=None, virsh_instance=base.virsh):
        """
        Convenience method for updating 'vcpu', 'current' and
        'cpu topology' attribute property with of a defined VM

        :param vm_name: Name of defined vm to change vcpu elemnet data
        :param vcpus: New vcpus count, None to delete.
        :param current: New current value, None will not change current value
        :param sockets: number of socket, default None
        :param cores: number of cores, default None
        :param threads: number of threads, default None
        :param add_topology: True to add new topology definition if not present
        :param topology_correction: Correct topology if wrong already
        :param update_numa: Update numa
        :param numa_number: number of numa node
        :parma virsh_instance: virsh instance
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        if vcpus is not None:
            if current is not None:
                try:
                    if(int(current) > vcpus):
                        raise xcepts.LibvirtXMLError("The cpu current value %s "
                                                     "is larger than max "
                                                     "number %s" % (current,
                                                                    vcpus))
                    else:
                        vmxml['current_vcpu'] = current
                except ValueError:
                    raise xcepts.LibvirtXMLError("Invalid 'current' value '%s'"
                                                 % current)
            topology = vmxml.get_cpu_topology()
            if topology:
                if not sockets:
                    sockets = topology['sockets']
                if not cores:
                    cores = topology['cores']
                if not threads:
                    threads = topology['threads']
            if (topology or add_topology) and (sockets or cores or threads):
                # Only operate topology tag, other tags doesn't change
                try:
                    vmcpu_xml = vmxml['cpu']
                except xcepts.LibvirtXMLNotFoundError:
                    logging.debug("Can not find any cpu tag, now create one.")
                    vmcpu_xml = VMCPUXML()

                if topology_correction and ((int(sockets) * int(cores) * int(threads)) != vcpus):
                    cores = vcpus
                    sockets = 1
                    threads = 1
                vmcpu_xml['topology'] = {'sockets': sockets,
                                         'cores': cores,
                                         'threads': threads}
                vmxml['cpu'] = vmcpu_xml
            try:
                vmcpu_xml = vmxml['cpu']
                if (update_numa and vmxml.cpu.numa_cell):
                    no_numa_cell = len(vmxml.cpu.numa_cell)
                elif numa_number is not None:
                    numa_number = int(numa_number)
                    if 0 < int(numa_number) <= vcpus:
                        no_numa_cell = numa_number
                    else:
                        raise xcepts.LibvirtXMLError("The numa number %d "
                                                     "is larger than vcpus "
                                                     "number %s or not positive" % (numa_number, vcpus))
                if 'no_numa_cell' in locals() and no_numa_cell > 0:
                    if vcpus >= no_numa_cell:
                        vcpus_num = vcpus // no_numa_cell
                        vcpu_rem = vcpus % no_numa_cell
                        index = 0
                        nodexml_list = []
                        for node in range(no_numa_cell):
                            if vmxml.cpu.numa_cell:
                                nodexml = vmcpu_xml.numa_cell[node]
                            else:
                                nodexml = {}
                            if vcpus_num > 1:
                                if (node == no_numa_cell - 1) and vcpu_rem > 0:
                                    nodexml["cpus"] = "%s-%s" % (index, index + vcpus_num + vcpu_rem - 1)
                                else:
                                    nodexml["cpus"] = "%s-%s" % (index, index + vcpus_num - 1)
                            else:
                                if (node == no_numa_cell - 1) and vcpu_rem > 0:
                                    nodexml["cpus"] = str(index + vcpu_rem)
                                else:
                                    nodexml["cpus"] = str(index)
                            if numa_number is not None and numa_number > 0:
                                nodexml['id'] = str(node)
                                cell_mem_size = vmxml.max_mem // numa_number

                                # PPC memory size should align to 256M
                                if 'ppc64le' in platform.machine().lower():
                                    cell_mem_size = (vmxml.max_mem // numa_number // 262144) * 262144
                                nodexml['memory'] = str(cell_mem_size)
                            index = vcpus_num * (node + 1)
                            nodexml_list.append(nodexml)
                        if numa_number is not None and numa_number > 0:
                            vmcpu_xml.xmltreefile.create_by_xpath('/numa')
                            vmcpu_xml.numa_cell = nodexml_list
                        else:
                            vmcpu_xml.set_numa_cell(nodexml_list)
                    else:
                        logging.warning("Guest numa could not be updated, expect "
                                        "failures if guest numa is checked")
                vmxml['cpu'] = vmcpu_xml
            except xcepts.LibvirtXMLNotFoundError:
                pass
            vmxml['vcpu'] = vcpus  # call accessor method to change XML
        else:  # value is None
            del vmxml.vcpu
        vmxml.sync()
        # Temporary files for vmxml cleaned up automatically
        # when it goes out of scope here.

    @staticmethod
    def check_cpu_mode(mode):
        """
        Check input cpu mode invalid or not.

        :param mode: the mode of cpu:'host-model'...
        """
        # Possible values for the mode attribute are:
        # "custom", "host-model", "host-passthrough"
        cpu_mode = ["custom", "host-model", "host-passthrough"]
        if mode.strip() not in cpu_mode:
            raise xcepts.LibvirtXMLError(
                "The cpu mode '%s' is invalid!" % mode)

    def get_cpu_topology(self):
        """
        Return cpu topology dict
        """
        topology = {}
        try:
            topology = self.cpu.topology
        except Exception:
            logging.debug("<cpu>/<topology> xml element not found")
        return topology

    def get_disk_all(self):
        """
        Return VM's disk from XML definition, None if not set
        """
        disk_nodes = self.xmltreefile.find('devices').findall('disk')
        disks = {}
        for node in disk_nodes:
            dev = node.find('target').get('dev')
            disks[dev] = node
        return disks

    @staticmethod
    def get_disk_source(vm_name, option="", virsh_instance=base.virsh):
        """
        Get block device  of a defined VM's disks.

        :param vm_name: Name of defined vm.
        :param option: extra option.
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, option,
                                       virsh_instance=virsh_instance)
        disks = vmxml.get_disk_all()
        return list(disks.values())

    @staticmethod
    def get_disk_blk(vm_name, virsh_instance=base.virsh):
        """
        Get block device  of a defined VM's disks.

        :param vm_name: Name of defined vm.
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        disks = vmxml.get_disk_all()
        return list(disks.keys())

    @staticmethod
    def get_disk_count(vm_name, virsh_instance=base.virsh):
        """
        Get count of VM's disks.

        :param vm_name: Name of defined vm.
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        disks = vmxml.get_disk_all()
        if disks is not None:
            return len(disks)
        return 0

    @staticmethod
    def get_disk_attr(vm_name, target, tag, attr, virsh_instance=base.virsh):
        """
        Get value of disk tag attribute for a given target dev.
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        attr_value = None
        try:
            disk = vmxml.get_disk_all()[target]
            if tag in ["driver", "boot", "address", "alias", "source"]:
                attr_value = disk.find(tag).get(attr)
        except AttributeError:
            logging.error("No %s/%s found.", tag, attr)

        return attr_value

    @staticmethod
    def check_disk_exist(vm_name, disk_src, virsh_instance=base.virsh):
        """
        Check if given disk exist in VM.

        :param vm_name: Domain name.
        :param disk_src: Domian disk source path or darget dev.
        :return: True/False
        """
        found = False
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        if not vmxml.get_disk_count(vm_name, virsh_instance=virsh_instance):
            raise xcepts.LibvirtXMLError("No disk in domain %s." % vm_name)
        blk_list = vmxml.get_disk_blk(vm_name, virsh_instance=virsh_instance)
        disk_list = vmxml.get_disk_source(
            vm_name, virsh_instance=virsh_instance)
        try:
            file_list = []
            for disk in disk_list:
                file_list.append(disk.find('source').get('file'))
        except AttributeError:
            logging.debug("No 'file' type disk.")
        if disk_src in file_list + blk_list:
            found = True
        return found

    @staticmethod
    def check_disk_type(vm_name, disk_src, disk_type, virsh_instance=base.virsh):
        """
        Check if disk type is correct in VM

        :param vm_name: Domain name.
        :param disk_src: Domain disk source path
        :param disk_type: Domain disk type
        :return: True/False
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        if not vmxml.get_disk_count(vm_name, virsh_instance=virsh_instance):
            raise xcepts.LibvirtXMLError("No disk in domain %s." % vm_name)
        disks = vmxml.get_disk_source(vm_name, virsh_instance=virsh_instance)

        found = False
        for disk in disks:
            try:
                disk_dev = ""
                if disk_type == "file":
                    disk_dev = disk.find('source').get('file')
                elif disk_type == "block":
                    disk_dev = disk.find('source').get('dev')
                if disk_src == disk_dev:
                    found = True
            except AttributeError as detail:
                logging.debug(str(detail))
                continue
        return found

    @staticmethod
    def get_disk_serial(vm_name, disk_target, virsh_instance=base.virsh):
        """
        Get disk serial in VM

        :param vm_name: Domain name.
        :param disk_target: Domain disk target
        :return: disk serial
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        if not vmxml.get_disk_count(vm_name, virsh_instance=virsh_instance):
            raise xcepts.LibvirtXMLError("No disk in domain %s." % vm_name)
        try:
            disk = vmxml.get_disk_all()[disk_target]
        except KeyError:
            raise xcepts.LibvirtXMLError("Wrong disk target:%s." % disk_target)
        serial = ""
        try:
            serial = disk.find("serial").text
        except AttributeError:
            logging.debug("No serial assigned.")

        return serial

    @staticmethod
    def get_disk_address(vm_name, disk_target, virsh_instance=base.virsh):
        """
        Get disk address in VM

        :param vm_name: Domain name.
        :param disk_target: Domain disk target
        :return: disk address
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        if not vmxml.get_disk_count(vm_name, virsh_instance=virsh_instance):
            raise xcepts.LibvirtXMLError("No disk in domain %s." % vm_name)
        try:
            disk = vmxml.get_disk_all()[disk_target]
        except KeyError:
            raise xcepts.LibvirtXMLError("Wrong disk target:%s." % disk_target)
        address_str = ""
        try:
            disk_bus = disk.find("target").get("bus")
            address = disk.find("address")
            add_type = address.get("type")
            logging.info("add_type %s", add_type)
            if add_type == "ccw":
                cssid = address.get("cssid")
                ssid = address.get("ssid")
                devno = address.get("devno")
                address_str = "%s:%s.%s.%s" % (add_type, cssid, ssid, devno)
            elif disk_bus == "virtio":
                add_domain = address.get("domain")
                add_bus = address.get("bus")
                add_slot = address.get("slot")
                add_func = address.get("function")
                address_str = ("%s:%s.%s.%s.%s"
                               % (add_type, add_domain, add_bus,
                                  add_slot, add_func))
            elif disk_bus in ["ide", "scsi"]:
                bus = address.get("bus")
                target = address.get("target")
                unit = address.get("unit")
                address_str = "%s:%s.%s.%s" % (disk_bus, bus, target, unit)
        except AttributeError as e:
            raise xcepts.LibvirtXMLError("Get wrong attribute: %s" % str(e))
        return address_str

    @staticmethod
    def get_numa_memory_params(vm_name, virsh_instance=base.virsh):
        """
        Return VM's numa memory setting from XML definition
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        return vmxml.numa_memory

    @staticmethod
    def get_numa_memnode_params(vm_name, virsh_instance=base.virsh):
        """
        Return VM's numa memnode setting from XML definition
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        return vmxml.numa_memnode

    def get_primary_serial(self):
        """
        Get a dict with primary serial features.
        """
        xmltreefile = self.__dict_get__('xml')
        primary_serial = xmltreefile.find('devices').find('serial')
        serial_features = {}
        serial_type = primary_serial.get('type')
        serial_port = primary_serial.find('target').get('port')
        # Support node here for more features
        serial_features['serial'] = primary_serial
        # Necessary features
        serial_features['type'] = serial_type
        serial_features['port'] = serial_port
        return serial_features

    @staticmethod
    def set_primary_serial(vm_name, dev_type, port, path=None,
                           virsh_instance=base.virsh):
        """
        Set primary serial's features of vm_name.

        :param vm_name: Name of defined vm to set primary serial.
        :param dev_type: the type of ``serial:pty,file...``
        :param port: the port of serial
        :param path: the path of serial, it is not necessary for pty
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        xmltreefile = vmxml.__dict_get__('xml')
        try:
            serial = vmxml.get_primary_serial()['serial']
        except AttributeError:
            logging.debug("Can not find any serial, now create one.")
            # Create serial tree, default is pty
            serial = xml_utils.ElementTree.SubElement(
                xmltreefile.find('devices'),
                'serial', {'type': 'pty'})
            # Create elements of serial target, default port is 0
            xml_utils.ElementTree.SubElement(serial, 'target', {'port': '0'})

        serial.set('type', dev_type)
        serial.find('target').set('port', port)
        # path may not be exist.
        if path is not None:
            serial.find('source').set('path', path)
        else:
            try:
                source = serial.find('source')
                serial.remove(source)
            except AssertionError:
                pass  # Element not found, already removed.
        xmltreefile.write()
        vmxml.set_xml(xmltreefile.name)
        vmxml.undefine()
        vmxml.define()

    def get_agent_channels(self):
        """
        Get all qemu guest agent channels
        """
        ga_channels = []
        try:
            channels = self.xmltreefile.findall("./devices/channel")
            for channel in channels:
                target = channel.find('./target')
                if target is not None:
                    name = target.get('name')
                    if name and name.startswith("org.qemu.guest_agent"):
                        ga_channels.append(channel)
            return ga_channels
        except xcepts.LibvirtXMLError:
            return ga_channels

    def set_agent_channel(self, src_path=None,
                          tgt_name='org.qemu.guest_agent.0',
                          ignore_exist=False):
        """
        Add a channel for guest agent if non exists.

        :param src_path: Source path of the channel
        :param tgt_name: Target name of the channel
        :param ignore_exist: Whether add a channel even if another already exists.
        """
        if not ignore_exist and self.get_agent_channels():
            logging.debug("Guest agent channel already exists")
            return

        if not src_path:
            src_path = '/var/lib/libvirt/qemu/%s-guest.agent' % self.vm_name
        channel = self.get_device_class('channel')(type_name='unix')
        channel.add_source(mode='bind', path=src_path)
        channel.add_target(type='virtio', name=tgt_name)
        self.devices = self.devices.append(channel)

    def remove_agent_channels(self):
        """
        Delete all channels for guest agent
        """
        for channel in self.get_agent_channels():
            self.xmltreefile.remove(channel)

    def get_iface_all(self):
        """
        Get a dict with interface's mac and node.
        """
        iface_nodes = self.xmltreefile.find('devices').findall('interface')
        interfaces = {}
        for node in iface_nodes:
            mac_addr = node.find('mac').get('address')
            interfaces[mac_addr] = node
        return interfaces

    @staticmethod
    def get_iface_by_mac(vm_name, mac, virsh_instance=base.virsh):
        """
        Get the interface if mac is matched.

        :param vm_name: Name of defined vm.
        :param mac: a mac address.
        :return: return a dict include main interface's features
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        interfaces = vmxml.get_iface_all()
        try:
            interface = interfaces[mac]
        except KeyError:
            interface = None
        if interface is not None:  # matched mac exists.
            features = {}
            iface_type = interface.get('type')
            if iface_type == "direct":
                features['source'] = interface.find('source').attrib
            else:
                features['source'] = interface.find('source').get(iface_type)
            features['type'] = iface_type
            features['mac'] = mac
            if interface.find('target') is not None:
                features['target'] = interface.find('target').attrib
            features['model'] = interface.find('model').get('type')
            if interface.find('bandwidth') is not None:
                features['outbound'] = interface.find('bandwidth/outbound').attrib
                features['inbound'] = interface.find('bandwidth/inbound').attrib
            if interface.find('backend') is not None:
                features['backend'] = interface.find('backend').attrib
            if interface.find('rom') is not None:
                features['rom'] = interface.find('rom').attrib
            if interface.find('boot') is not None:
                features['boot'] = interface.find('boot').get('order')
            if interface.find('link') is not None:
                features['link'] = interface.find('link').get('state')
            if interface.find('driver') is not None:
                features['driver'] = interface.find('driver').attrib
                features['driver_host'] = interface.find('driver/host').attrib
                features['driver_guest'] = interface.find('driver/guest').attrib
            if interface.find('alias') is not None:
                features['alias'] = interface.find('alias').attrib
            return features
        else:
            return None

    @staticmethod
    def get_iface_dev(vm_name, virsh_instance=base.virsh):
        """
        Return VM's interface device from XML definition, None if not set
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        ifaces = vmxml.get_iface_all()
        if ifaces:
            return list(ifaces.keys())
        return None

    @staticmethod
    def get_first_mac_by_name(vm_name, virsh_instance=base.virsh):
        """
        Convenience method for getting first mac of a defined VM

        :param: vm_name: Name of defined vm to get mac
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        xmltreefile = vmxml.__dict_get__('xml')
        try:
            iface = xmltreefile.find('devices').find('interface')
            return iface.find('mac').get('address')
        except AttributeError:
            return None

    @staticmethod
    def get_iftune_params(vm_name, options="", virsh_instance=base.virsh):
        """
        Return VM's interface tuning setting from XML definition
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, options=options,
                                       virsh_instance=virsh_instance)
        xmltreefile = vmxml.__dict_get__('xml')
        iftune_params = {}
        bandwidth = None
        try:
            bandwidth = xmltreefile.find('devices/interface/bandwidth')
            try:
                iftune_params['inbound'] = bandwidth.find('inbound')
                iftune_params['outbound'] = bandwidth.find('outbound')
            except AttributeError:
                logging.error("Can't find <inbound> or <outbound> element")
        except AttributeError:
            logging.error("Can't find <bandwidth> element")

        return iftune_params

    def get_net_all(self):
        """
        Return VM's net from XML definition, None if not set
        """
        xmltreefile = self.__dict_get__('xml')
        net_nodes = xmltreefile.find('devices').findall('interface')
        nets = {}
        for node in net_nodes:
            dev = node.find('target').get('dev')
            nets[dev] = node
        return nets

    @staticmethod
    def set_multiqueues(vm_name, queues, index=0):
        """
        Set multiqueues for interface.

        :param queues: the count of queues for interface
        :param index: the index of interface
        """
        driver_params = {'name': "vhost", 'queues': queues}
        vmxml = VMXML.new_from_dumpxml(vm_name)
        nets = vmxml.__dict_get__('xml').find('devices').findall('interface')
        if index >= len(nets):
            raise xcepts.LibvirtXMLError("Couldn't find %s-th interface for %s"
                                         % (index, vm_name))
        net = nets[index]
        iface = vmxml.get_device_class('interface').new_from_element(net)
        iface.model = "virtio"
        iface.driver = iface.new_driver(driver_attr=driver_params)
        # Update devices: Remove all interfaces and attach new one
        vmxml.__dict_get__('xml').find('devices').remove(net)
        vmxml.devices = vmxml.devices.append(iface)
        vmxml.sync()

    # TODO re-visit this method after the libvirt_xml.devices.interface module
    #     is implemented
    @staticmethod
    def get_net_dev(vm_name):
        """
        Get net device of a defined VM's nets.

        :param vm_name: Name of defined vm.
        """
        vmxml = VMXML.new_from_dumpxml(vm_name)
        nets = vmxml.get_net_all()
        if nets is not None:
            return list(nets.keys())
        return None

    @staticmethod
    def set_cpu_mode(vm_name, mode='host-model', model='',
                     fallback='', match='', check=''):
        """
        Set cpu's mode and respective attributes of VM.

        :param vm_name: Name of defined vm to set cpu mode.
        :param mode: the mode of cpu: host-model, host-passthrough, custom
        :param model: cpu model (power8, core2duo etc)
        :param fallback: forbid, allow*
        :param match: minimum, exact*, strict
        :param check: none, partial, full
        * - default values
        """
        vmxml = VMXML.new_from_dumpxml(vm_name)
        vmxml.check_cpu_mode(mode)
        try:
            cpuxml = vmxml['cpu']
        except xcepts.LibvirtXMLNotFoundError:
            logging.debug("Can not find any cpu tag, now create one.")
            cpuxml = VMCPUXML()
        cpuxml['mode'] = mode
        if model:
            cpuxml['model'] = model
        if fallback:
            cpuxml['fallback'] = fallback
        if match:
            cpuxml['match'] = match
        if check:
            cpuxml['check'] = check
        vmxml['cpu'] = cpuxml
        vmxml.sync()

    def add_device(self, value, allow_dup=False):
        """
        Add a device into VMXML.

        :param value: instance of device in libvirt_xml/devices/
        :param allow_dup: Boolean value. True for allow to add duplicated devices
        """
        devices = self.get_devices()
        if not allow_dup:
            for device in devices:
                if device == value:
                    logging.debug("Device %s is already in VM %s.",
                                  value, self.vm_name)
                    return
        devices.append(value)
        self.set_devices(devices)

    def del_device(self, value, by_tag=False):
        """
        Remove a device from VMXML

        :param value: instance of device in libvirt_xml/devices/ or device tag
        :param by_tag: Boolean value. True for delete device by tag name
        """
        devices = self.get_devices()
        not_found = True
        for device in devices:
            device_value = device
            if by_tag:
                device_value = device.device_tag
            if device_value == value:
                not_found = False
                devices.remove(device)
                break
        if not_found:
            logging.debug("Device %s does not exist in VM %s.",
                          value, self.vm_name)
            return
        self.set_devices(devices)

    @staticmethod
    def add_security_info(vmxml, passwd, virsh_instance=base.virsh):
        """
        Add passwd for graphic

        :param vmxml: instance of VMXML
        :param passwd: Password you want to set
        """
        devices = vmxml.devices
        try:
            graphics_index = devices.index(devices.by_device_tag('graphics')[0])
        except IndexError:
            raise xcepts.LibvirtXMLError("No graphics device defined in guest xml")
        graphics = devices[graphics_index]
        graphics.passwd = passwd
        vmxml.devices = devices
        vmxml.define(virsh_instance)

    @staticmethod
    def set_graphics_attr(vm_name, attr, index=0, virsh_instance=base.virsh):
        """
        Set attributes of graphics label of vm xml

        :param vm_name: name of vm
        :param attr: attributes dict to set
        :param index: index of graphics label to set
        :param virsh_instance: virsh instance
        """
        vmxml = VMXML.new_from_inactive_dumpxml(
                vm_name, virsh_instance=virsh_instance)
        graphic = vmxml.xmltreefile.find('devices').findall('graphics')
        for key in attr:
            logging.debug("Set %s='%s'" % (key, attr[key]))
            graphic[index].set(key, attr[key])
        vmxml.sync(virsh_instance=virsh_instance)

    def get_graphics_devices(self, type_name=""):
        """
        Get all graphics devices or desired type graphics devices

        :param type_name: graphic type, vnc or spice
        """
        devices = self.get_devices()
        graphics_devices = devices.by_device_tag('graphics')
        graphics_list = []
        for graphics_device in graphics_devices:
            graphics_index = devices.index(graphics_device)
            graphics = devices[graphics_index]
            if not type_name:
                graphics_list.append(graphics)
            elif graphics.type_name == type_name:
                graphics_list.append(graphics)
        return graphics_list

    def remove_all_graphics(self):
        """
        Remove all graphics devices.
        """
        self.remove_all_device_by_type('graphics')

    def remove_all_device_by_type(self, device_type):
        """
        Remove all devices of a given type.

        :param type: Type name for devices should be removed.
        """
        try:
            self.xmltreefile.remove_by_xpath(
                '/devices/%s' % device_type,
                remove_all=True)
        except (AttributeError, TypeError):
            pass  # Element already doesn't exist
        self.xmltreefile.write()

    def add_hostdev(self, source_address, mode='subsystem',
                    hostdev_type='pci',
                    managed='yes',
                    boot_order=None):
        """
        Add a hostdev device to guest.

        :param source_address: A dict include slot, function, bus, domain
        """
        dev = self.get_device_class('hostdev')()
        dev.mode = mode
        dev.type = hostdev_type
        dev.managed = managed
        if boot_order:
            dev.boot_order = boot_order
        dev.source = dev.new_source(**source_address)
        self.add_device(dev)

    @staticmethod
    def get_blkio_params(vm_name, options="", virsh_instance=base.virsh):
        """
        Return VM's block I/O setting from XML definition
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, options=options,
                                       virsh_instance=virsh_instance)
        xmltreefile = vmxml.__dict_get__('xml')
        blkio_params = {}
        try:
            blkio = xmltreefile.find('blkiotune')
            try:
                blkio_params['weight'] = blkio.find('weight').text
            except AttributeError:
                logging.error("Can't find <weight> element")
        except AttributeError:
            logging.error("Can't find <blkiotune> element")

        if blkio and blkio.find('device'):
            blkio_params['device_weights_path'] = \
                blkio.find('device').find('path').text
            blkio_params['device_weights_weight'] = \
                blkio.find('device').find('weight').text

        return blkio_params

    @staticmethod
    def get_blkdevio_params(vm_name, options="", virsh_instance=base.virsh):
        """
        Return VM's block I/O tuning setting from XML definition
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, options=options,
                                       virsh_instance=virsh_instance)
        xmltreefile = vmxml.__dict_get__('xml')
        blkdevio_params = {}
        iotune = None
        blkdevio_list = ['total_bytes_sec', 'read_bytes_sec',
                         'write_bytes_sec', 'total_iops_sec',
                         'read_iops_sec', 'write_iops_sec']

        # Initialize all of arguments to zero
        for k in blkdevio_list:
            blkdevio_params[k] = 0

        try:
            iotune = xmltreefile.find('/devices/disk/iotune')
            for k in blkdevio_list:
                if iotune.findall(k):
                    blkdevio_params[k] = int(iotune.find(k).text)
        except AttributeError:
            xcepts.LibvirtXMLError("Can't find <iotune> element")

        return blkdevio_params

    @staticmethod
    def set_memoryBacking_tag(vm_name, hpgs=True, nosp=False, locked=False,
                              virsh_instance=base.virsh, access_mode=None):
        """
        let the guest using hugepages.
        """
        # Create a new memoryBacking tag
        mb_xml = VMMemBackingXML()
        mb_xml.nosharepages = nosp
        mb_xml.locked = locked
        if hpgs:
            hpgs = VMHugepagesXML()
            mb_xml.hugepages = hpgs
        if access_mode is not None:
            mb_xml.access_mode = access_mode
        # Set memoryBacking to the new instance.
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        vmxml.mb = mb_xml
        vmxml.sync()

    @staticmethod
    def del_memoryBacking_tag(vm_name, virsh_instance=base.virsh):
        """
        Remove the memoryBacking tag from a domain
        """
        vmxml = VMXML.new_from_dumpxml(vm_name, virsh_instance=virsh_instance)
        try:
            vmxml.xmltreefile.remove_by_xpath(
                "/memoryBacking", remove_all=True)
            vmxml.sync()
        except (AttributeError, TypeError):
            pass  # Element already doesn't exist

    def remove_all_boots(self):
        """
        Remove all OS boots
        """
        try:
            self.xmltreefile.remove_by_xpath('/os/boot', remove_all=True)
        except (AttributeError, TypeError):
            pass  # Element already doesn't exist
        self.xmltreefile.write()

    def set_boot_order_by_target_dev(self, target, order):
        """
        Set boot order by target dev

        :param target: The target dev on host
        :param order: The boot order number
        """
        devices = self.get_devices()
        for device in devices:
            if device.device_tag == "disk":
                if device.target.get("dev") == target:
                    device.boot = order
        self.set_devices(devices)

    def set_os_attrs(self, **attr_dict):
        """
        Set attributes of VMOSXML

        :param attr_dict: The key words of os attributes
        """
        try:
            os_xml = getattr(self, "os")
            if attr_dict:
                for name, value in list(attr_dict.items()):
                    setattr(os_xml, name, value)
            setattr(self, "os", os_xml)
            self.xmltreefile.write()
        except (AttributeError, TypeError, ValueError) as detail:
            raise xcepts.LibvirtXMLError("Invalid os tag or attribute: %s" % detail)

    def remove_all_disk(self):
        """
        Remove all disk devices.
        """
        self.remove_all_device_by_type('disk')

    @staticmethod
    def set_vm_features(vm_name, **attrs):
        """
        Set attrs of vm features xml

        :param vm_name: The name of vm to be set
        :param attrs: attributes to be set
        """
        vmxml = VMXML.new_from_inactive_dumpxml(vm_name)
        if vmxml.xmltreefile.find('/features'):
            features_xml = vmxml.features
        else:
            features_xml = VMFeaturesXML()
        try:
            for attr_key, value in attrs.items():
                setattr(features_xml, attr_key, value)
            logging.debug('New features_xml: %s', features_xml)
            vmxml.features = features_xml
            vmxml.sync()
        except (AttributeError, TypeError, ValueError) as detail:
            raise xcepts.LibvirtXMLError(
                "Invalid feature tag or attribute: %s" % detail)


class VMCPUXML(base.LibvirtXMLBase):

    """
    Higher-level manipulations related to VM's XML(CPU)
    """

    # Must copy these here or there will be descriptor problems
    __slots__ = ('model', 'vendor', 'feature_list', 'mode', 'match',
                 'fallback', 'topology', 'numa_cell', 'check',
                 'cache', 'vendor_id')

    def __init__(self, virsh_instance=base.virsh):
        """
        Create new VMCPU XML instance
        """
        # The set action is for test.
        accessors.XMLAttribute(property_name="mode",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='cpu',
                               attribute='mode')
        accessors.XMLAttribute(property_name="match",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='cpu',
                               attribute='match')
        accessors.XMLAttribute(property_name="check",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='cpu',
                               attribute='check')
        accessors.XMLElementText(property_name="model",
                                 libvirtxml=self,
                                 forbidden=[],
                                 parent_xpath='/',
                                 tag_name='model')
        accessors.XMLElementText(property_name="vendor",
                                 libvirtxml=self,
                                 forbidden=[],
                                 parent_xpath='/',
                                 tag_name='vendor')
        accessors.XMLAttribute(property_name="fallback",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='model',
                               attribute='fallback')
        accessors.XMLAttribute(property_name="vendor_id",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='model',
                               attribute='vendor_id')
        accessors.XMLElementDict(property_name="topology",
                                 libvirtxml=self,
                                 forbidden=[],
                                 parent_xpath='/',
                                 tag_name='topology')
        accessors.XMLElementList(property_name="numa_cell",
                                 libvirtxml=self,
                                 parent_xpath='numa',
                                 marshal_from=self.marshal_from_cell,
                                 marshal_to=self.marshal_to_cell)
        accessors.XMLElementDict(property_name="cache",
                                 libvirtxml=self,
                                 forbidden=[],
                                 parent_xpath='/',
                                 tag_name='cache')
        # This will skip self.get_feature_list() defined below
        accessors.AllForbidden(property_name="feature_list",
                               libvirtxml=self)
        super(VMCPUXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<cpu/>'

    @staticmethod
    def marshal_from_cell(item, index, libvirtxml):
        """
        Convert a dict to cell tag and attributes.
        """
        del index
        del libvirtxml
        if not isinstance(item, dict):
            raise xcepts.LibvirtXMLError("Expected a dictionary of cell "
                                         "attributes, not a %s"
                                         % str(item))
        return ('cell', dict(item))

    @staticmethod
    def marshal_to_cell(tag, attr_dict, index, libvirtxml):
        """
        Convert a cell tag and attributes to a dict.
        """
        del index
        del libvirtxml
        if tag != 'cell':
            return None
        return dict(attr_dict)

    def get_feature_list(self):
        """
        Accessor method for feature_list property (in __slots__)
        """
        feature_list = []
        xmltreefile = self.__dict_get__('xml')
        for feature_node in xmltreefile.findall('/feature'):
            feature_list.append(feature_node)
        return feature_list

    def get_feature(self, num):
        """
        Get a feature element from feature list by number

        :return: Feature element
        """
        count = len(self.feature_list)
        try:
            num = int(num)
            return self.feature_list[num]
        except (ValueError, TypeError):
            raise xcepts.LibvirtXMLError("Invalid feature number %s" % num)
        except IndexError:
            raise xcepts.LibvirtXMLError("Only %d feature(s)" % count)

    def get_feature_name(self, num):
        """
        Get feature name

        :param num: Number in feature list
        :return: Feature name
        """
        return self.get_feature(num).get('name')

    def get_feature_policy(self, num):
        """
        Get feature policy

        :param num: Number in feature list
        :return: Feature policy
        """
        return self.get_feature(num).get('policy')

    def remove_feature(self, num):
        """
        Remove a feature from xml

        :param num: Number in feature list
        """
        xmltreefile = self.__dict_get__('xml')
        node = xmltreefile.getroot()
        node.remove(self.get_feature(num))

    @staticmethod
    def check_feature_name(value):
        """
        Check feature name valid or not.

        :param value: Feature name
        :return: True if check pass
        """
        sys_feature = []
        cpu_xml_file = open('/proc/cpuinfo', 'r')
        for line in cpu_xml_file.readlines():
            if line.find('flags') != -1:
                feature_names = line.split(':')[1].strip()
                sys_sub_feature = feature_names.split(' ')
                sys_feature = list(set(sys_feature + sys_sub_feature))
        cpu_xml_file.close()
        return (value in sys_feature)

    def set_feature(self, num, name='', policy=''):
        """
        Set feature name (and policy) to xml

        :param num: Number in feature list
        :param name: New feature name
        :param policy: New feature policy
        """
        feature_set_node = self.get_feature(num)
        if name:
            feature_set_node.set('name', name)
        if policy:
            feature_set_node.set('policy', policy)

    def add_feature(self, name, policy=''):
        """
        Add a feature element to xml

        :param name: New feature name
        :param policy: New feature policy
        """
        xmltreefile = self.__dict_get__('xml')
        node = xmltreefile.getroot()
        feature_node = {'name': name}
        if policy:
            feature_node.update({'policy': policy})
        xml_utils.ElementTree.SubElement(node, 'feature', feature_node)

    def remove_numa_cells(self):
        """
        Remove numa cells from xml
        """
        try:
            self.xmltreefile.remove_by_xpath('/numa', remove_all=True)
        except (AttributeError, TypeError):
            pass  # Element already doesn't exist
        self.xmltreefile.write()

    @staticmethod
    def from_domcapabilities(domcaps_xml):
        """
        Construct a cpu definition from domcapabilities host-model definition.

        :param domcaps_xml: DomCapabilityXML with host-model definition
        :return: None
        """
        cpu_xml = VMCPUXML()
        cpu_xml['model'] = domcaps_xml.get_hostmodel_name()
        features = domcaps_xml.get_additional_feature_list('host-model')
        feature_names = [name for f_list in [list(d.keys()) for d in features]
                         for name in f_list]
        for feature_name in feature_names:
            cpu_xml.add_feature(feature_name)

        return cpu_xml


class VMClockXML(VMXML):

    """
    Higher-level manipulations related to VM's XML(Clock)
    """

    # Must copy these here or there will be descriptor problems
    __slots__ = ('offset', 'timezone', 'adjustment', 'timers')

    def __init__(self, virsh_instance=base.virsh, offset="utc"):
        """
        Create new VMClock XML instance
        """
        # The set action is for test.
        accessors.XMLAttribute(property_name="offset",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='clock',
                               attribute='offset')
        accessors.XMLAttribute(property_name="timezone",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='clock',
                               attribute='timezone')
        accessors.XMLAttribute(property_name="adjustment",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='clock',
                               attribute='adjustment')
        accessors.XMLElementList(property_name="timers",
                                 libvirtxml=self,
                                 forbidden=[],
                                 parent_xpath="/clock",
                                 marshal_from=self.marshal_from_timer,
                                 marshal_to=self.marshal_to_timer)
        super(VMClockXML, self).__init__(virsh_instance=virsh_instance)
        # Set default offset for clock
        self.offset = offset

    def from_dumpxml(self, vm_name, virsh_instance=base.virsh):
        """Helper to load xml from domain."""
        self.xml = VMXML.new_from_dumpxml(vm_name,
                                          virsh_instance=virsh_instance).xml

    # Sub-element of clock
    class TimerXML(VMXML):

        """Timer element of clock"""

        __slots__ = ('name', 'present', 'track', 'tickpolicy', 'frequency',
                     'mode', 'catchup_threshold', 'catchup_slew',
                     'catchup_limit')

        def __init__(self, virsh_instance=base.virsh, timer_name="tsc"):
            """
            Create new TimerXML instance
            """
            # The set action is for test.
            accessors.XMLAttribute(property_name="name",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock',
                                   tag_name='timer',
                                   attribute='name')
            accessors.XMLAttribute(property_name="present",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock',
                                   tag_name='timer',
                                   attribute='present')
            accessors.XMLAttribute(property_name="track",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock',
                                   tag_name='timer',
                                   attribute='track')
            accessors.XMLAttribute(property_name="tickpolicy",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock',
                                   tag_name='timer',
                                   attribute='tickpolicy')
            accessors.XMLAttribute(property_name="frequency",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock',
                                   tag_name='timer',
                                   attribute='frequency')
            accessors.XMLAttribute(property_name="mode",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock',
                                   tag_name='timer',
                                   attribute='mode')
            accessors.XMLAttribute(property_name="catchup_threshold",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock/timer',
                                   tag_name='catchup',
                                   attribute='threshold')
            accessors.XMLAttribute(property_name="catchup_slew",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock/timer',
                                   tag_name='catchup',
                                   attribute='slew')
            accessors.XMLAttribute(property_name="catchup_limit",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/clock/timer',
                                   tag_name='catchup',
                                   attribute='limit')
            super(VMClockXML.TimerXML, self).__init__(
                virsh_instance=virsh_instance)
            # name is mandatory for timer
            self.name = timer_name

        def update(self, attr_dict):
            for attr, value in list(attr_dict.items()):
                setattr(self, attr, value)

    @staticmethod
    def marshal_from_timer(item, index, libvirtxml):
        """Convert a TimerXML instance into tag + attributes"""
        del index
        del libvirtxml
        timer = item.xmltreefile.find("clock/timer")
        try:
            return (timer.tag, dict(list(timer.items())))
        except AttributeError:  # Didn't find timer
            raise xcepts.LibvirtXMLError("Expected a list of timer "
                                         "instances, not a %s" % str(item))

    @staticmethod
    def marshal_to_timer(tag, attr_dict, index, libvirtxml):
        """Convert a tag + attributes to a TimerXML instance"""
        del index
        if tag == 'timer':
            newone = VMClockXML.TimerXML(virsh_instance=libvirtxml.virsh)
            newone.update(attr_dict)
            return newone
        else:
            return None


class CacheTuneXML(VMXML):

    """CacheTune XML"""

    __slots__ = ('vcpus', 'cache', 'monitor')

    def __init__(self, virsh_instance=base.virsh):
        """
        Create new CacheTuneXML instance
        """
        accessors.XMLAttribute(property_name="vcpus",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='cachetune',
                               attribute='vcpus')

        super(CacheTuneXML, self).__init__(
            virsh_instance=virsh_instance)
        self.xml = '<cachetune/>'

    # Sub-element of CacheTuneXML
    class CacheXML(VMXML):

        """Cache element of CacheTuneXML"""

        __slots__ = ('id', 'level', 'type', 'size', 'unit')

        def __init__(self, virsh_instance=base.virsh):
            """
            Create new NodeXML instance
            """
            accessors.XMLAttribute(property_name="id",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='cache',
                                   attribute='id')
            accessors.XMLAttribute(property_name='level',
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='cache',
                                   attribute='level')
            accessors.XMLAttribute(property_name="type",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='cache',
                                   attribute='type')
            accessors.XMLAttribute(property_name='size',
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='cache',
                                   attribute='size')
            accessors.XMLAttribute(property_name='unit',
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='cache',
                                   attribute='unit')

            super(CacheTuneXML.CacheXML, self).__init__(
                virsh_instance=virsh_instance)
            self.xml = '<cache/>'

    def get_cache(self):
        xmltreefile = self.__dict_get__('xml')
        try:
            cache_root = xmltreefile.reroot('/cache')
        except KeyError as detail:
            raise xcepts.LibvirtXMLError(detail)
        cachexml = CacheTuneXML.CacheXML(virsh_instance=self.__dict_get__('virsh'))
        cachexml.xmltreefile = cache_root
        return cachexml

    def set_cache(self, value):
        if not issubclass(type(value), CacheTuneXML.CacheXML):
            raise xcepts.LibvirtXMLError("value must be a Cache or subclass")
        xmltreefile = self.__dict_get__('xml')
        # CacheXML root element is whole Cache element tree
        root = xmltreefile.getroot()
        root.append(value.xmltreefile.getroot())
        xmltreefile.write()

    def del_cache(self):
        xmltreefile = self.__dict_get__('xml')
        element = xmltreefile.find('/cache')
        if element is not None:
            xmltreefile.remove(element)
            xmltreefile.write()

    # Sub-element of CacheTuneXML
    class MonitorXML(VMXML):

        """Monitor element of CacheTuneXML"""

        __slots__ = ('level', 'vcpus')

        def __init__(self, virsh_instance=base.virsh):
            """
            Create new MonitorXML instance
            """
            accessors.XMLAttribute(property_name='level',
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='monitor',
                                   attribute='level')
            accessors.XMLAttribute(property_name="vcpus",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='monitor',
                                   attribute='vcpus')

            super(CacheTuneXML.MonitorXML, self).__init__(
                virsh_instance=virsh_instance)
            self.xml = '<monitor/>'

    def get_monitor(self):
        xmltreefile = self.__dict_get__('xml')
        try:
            monitor_root = xmltreefile.reroot('/monitor')
        except KeyError as detail:
            raise xcepts.LibvirtXMLError(detail)
        monitorxml = CacheTuneXML.MonitorXML(virsh_instance=self.__dict_get__('virsh'))
        monitorxml.xmltreefile = monitor_root
        return monitorxml

    def set_monitor(self, value):
        if not issubclass(type(value), CacheTuneXML.MonitorXML):
            raise xcepts.LibvirtXMLError("value must be a Monitor or subclass")
        xmltreefile = self.__dict_get__('xml')
        # MonitorXML root element is whole Monitor element tree
        root = xmltreefile.getroot()
        root.append(value.xmltreefile.getroot())
        xmltreefile.write()

    def del_monitor(self):
        xmltreefile = self.__dict_get__('xml')
        element = xmltreefile.find('/monitor')
        if element is not None:
            xmltreefile.remove(element)
            xmltreefile.write()


class MemoryTuneXML(VMXML):

    """Event element of perf"""

    __slots__ = ('vcpus', 'node', 'monitor')

    def __init__(self, virsh_instance=base.virsh):
        """
        Create new MemoryTuneXML instance
        """
        accessors.XMLAttribute(property_name="vcpus",
                               libvirtxml=self,
                               forbidden=[],
                               parent_xpath='/',
                               tag_name='memorytune',
                               attribute='vcpus')

        super(MemoryTuneXML, self).__init__(
            virsh_instance=virsh_instance)
        self.xml = '<memorytune/>'

    # Sub-element of MemoryTuneXML
    class NodeXML(VMXML):

        """Node element of MemoryTuneXML"""

        __slots__ = ('id', 'bandwidth')

        def __init__(self, virsh_instance=base.virsh):
            """
            Create new NodeXML instance
            """
            accessors.XMLAttribute(property_name="id",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='node',
                                   attribute='id')
            accessors.XMLAttribute(property_name='bandwidth',
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='node',
                                   attribute='bandwidth')

            super(MemoryTuneXML.NodeXML, self).__init__(
                virsh_instance=virsh_instance)
            self.xml = '<node/>'

    def get_node(self):
        xmltreefile = self.__dict_get__('xml')
        try:
            node_root = xmltreefile.reroot('/node')
        except KeyError as detail:
            raise xcepts.LibvirtXMLError(detail)
        nodexml = MemoryTuneXML.NodeXML(virsh_instance=self.__dict_get__('virsh'))
        nodexml.xmltreefile = node_root
        return nodexml

    def set_node(self, value):
        if not issubclass(type(value), MemoryTuneXML.NodeXML):
            raise xcepts.LibvirtXMLError("value must be a Node or subclass")
        xmltreefile = self.__dict_get__('xml')
        # NodeXML root element is whole Node element tree
        root = xmltreefile.getroot()
        root.append(value.xmltreefile.getroot())
        xmltreefile.write()

    def del_node(self):
        xmltreefile = self.__dict_get__('xml')
        element = xmltreefile.find('/node')
        if element is not None:
            xmltreefile.remove(element)
            xmltreefile.write()

    # Sub-element of MemoryTuneXML
    class MonitorXML(VMXML):

        """Monitor element of MemoryTuneXML"""

        __slots__ = ('vcpus')

        def __init__(self, virsh_instance=base.virsh):
            """
            Create new MonitorXML instance
            """
            accessors.XMLAttribute(property_name="vcpus",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/',
                                   tag_name='monitor',
                                   attribute='vcpus')

            super(MemoryTuneXML.MonitorXML, self).__init__(
                virsh_instance=virsh_instance)
            self.xml = '<monitor/>'

    def get_monitor(self):
        xmltreefile = self.__dict_get__('xml')
        try:
            monitor_root = xmltreefile.reroot('/monitor')
        except KeyError as detail:
            raise xcepts.LibvirtXMLError(detail)
        monitorxml = MemoryTuneXML.MonitorXML(virsh_instance=self.__dict_get__('virsh'))
        monitorxml.xmltreefile = monitor_root
        return monitorxml

    def set_monitor(self, value):
        if not issubclass(type(value), MemoryTuneXML.MonitorXML):
            raise xcepts.LibvirtXMLError("value must be a Monitor or subclass")
        xmltreefile = self.__dict_get__('xml')
        # MonitorXML root element is whole Monitor element tree
        root = xmltreefile.getroot()
        root.append(value.xmltreefile.getroot())
        xmltreefile.write()

    def del_monitor(self):
        xmltreefile = self.__dict_get__('xml')
        element = xmltreefile.find('/monitor')
        if element is not None:
            xmltreefile.remove(element)
            xmltreefile.write()


class VMCPUTuneXML(base.LibvirtXMLBase):
    """
    CPU tuning tag XML class

    Elements:
        vcpupins:             list of dict - vcpu, cpuset
        iothreadscheds:       list of dict - iothreads, scheduler, priority
        emulatorpin:          attribute    - cpuset
        shares:               int
        period:               int
        quota:                int
        emulator_period:      int
        emulator_quota:       int
        iothread_period:      int
        iothread_quota:       int
    """

    __slots__ = ('vcpupins', 'emulatorpin', 'shares', 'period', 'quota',
                 'emulator_period', 'emulator_quota', 'iothreadpins',
                 'cachetune', 'memorytune', 'iothreadscheds',
                 'iothread_period', 'iothread_quota')

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLElementList('vcpupins', self, parent_xpath='/',
                                 marshal_from=self.marshal_from_vcpupins,
                                 marshal_to=self.marshal_to_vcpupins)
        accessors.XMLAttribute('emulatorpin', self, parent_xpath='/',
                               tag_name='emulatorpin', attribute='cpuset')
        accessors.XMLElementList('iothreadpins', self, parent_xpath='/',
                                 marshal_from=self.marshal_from_iothreadpins,
                                 marshal_to=self.marshal_to_iothreadpins)
        accessors.XMLElementList('iothreadscheds', self, parent_xpath='/',
                                 marshal_from=self.marshal_from_iothreadscheds,
                                 marshal_to=self.marshal_to_iothreadscheds)
        for slot in self.__all_slots__:
            if slot in ('shares', 'period', 'quota', 'emulator_period',
                        'emulator_quota', 'iothread_period', 'iothread_quota'):
                accessors.XMLElementInt(slot, self, parent_xpath='/',
                                        tag_name=slot)
        super(VMCPUTuneXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<cputune/>'

    def update(self, attr_dict):
        for attr, value in list(attr_dict.items()):
            setattr(self, attr, value)

    def get_memorytune(self):
        xmltreefile = self.__dict_get__('xml')
        try:
            memorytune_root = xmltreefile.reroot('/memorytune')
        except KeyError as detail:
            raise xcepts.LibvirtXMLError(detail)
        memorytunexml = MemoryTuneXML(virsh_instance=self.__dict_get__('virsh'))
        memorytunexml.xmltreefile = memorytune_root
        return memorytunexml

    def set_memorytune(self, value):
        """
        Define memorytune based on the content of MemoryTuneXML instance

        :params value: the instance of MemoryTuneXML
        """
        if not issubclass(type(value), MemoryTuneXML):
            raise xcepts.LibvirtXMLError("value must be a MemoryTuneXML \
                                         or subclass")
        xmltreefile = self.__dict_get__('xml')
        # MemoryTuneXML root element is whole MemoryTune element tree
        root = xmltreefile.getroot()
        root.append(value.xmltreefile.getroot())
        xmltreefile.write()

    def del_memorytune(self):
        xmltreefile = self.__dict_get__('xml')
        element = xmltreefile.find('/memorytune')
        if element is not None:
            xmltreefile.remove(element)
            xmltreefile.write()

    def get_cachetune(self):
        xmltreefile = self.__dict_get__('xml')
        try:
            cachetune_root = xmltreefile.reroot('/cachetune')
        except KeyError as detail:
            raise xcepts.LibvirtXMLError(detail)
        cachetunexml = CacheTuneXML(virsh_instance=self.__dict_get__('virsh'))
        cachetunexml.xmltreefile = cachetune_root
        return cachetunexml

    def set_cachetune(self, value):
        """
        Define cachetune based on the content of CacheTuneXML instance

        :params value: the instance of CacheTuneXML
        """
        if not issubclass(type(value), CacheTuneXML):
            raise xcepts.LibvirtXMLError("value must be a CacheTuneXML or subclass")
        xmltreefile = self.__dict_get__('xml')
        # CacheTuneXML root element is whole CacheTune element tree
        root = xmltreefile.getroot()
        root.append(value.xmltreefile.getroot())
        xmltreefile.write()

    def del_cachetune(self):
        xmltreefile = self.__dict_get__('xml')
        element = xmltreefile.find('/cachetune')
        if element is not None:
            xmltreefile.remove(element)
            xmltreefile.write()

    @staticmethod
    def marshal_from_vcpupins(item, index, libvirtxml):
        """
        Convert a dict to vcpupin tag and attributes.
        """
        del index
        del libvirtxml
        if not isinstance(item, dict):
            raise xcepts.LibvirtXMLError("Expected a dictionary of host "
                                         "attributes, not a %s"
                                         % str(item))
        return ('vcpupin', dict(item))

    @staticmethod
    def marshal_to_vcpupins(tag, attr_dict, index, libvirtxml):
        """
        Convert a vcpupin tag and attributes to a dict.
        """
        del index
        del libvirtxml
        if tag != 'vcpupin':
            return None
        return dict(attr_dict)

    @staticmethod
    def marshal_from_iothreadpins(item, index, libvirtxml):
        """
        Convert a dict to iothreadpin tag and attributes.
        """
        del index
        del libvirtxml
        if not isinstance(item, dict):
            raise xcepts.LibvirtXMLError("Expected a dictionary of host "
                                         "attributes, not a %s"
                                         % str(item))
        return ('iothreadpin', dict(item))

    @staticmethod
    def marshal_to_iothreadpins(tag, attr_dict, index, libvirtxml):
        """
        Convert a iothreadpin tag and attributes to a dict.
        """
        del index
        del libvirtxml
        if tag != 'iothreadpin':
            return None
        return dict(attr_dict)

    @staticmethod
    def marshal_from_iothreadscheds(item, index, libvirtxml):
        """
        Convert a dict to iothreadsched tag and attributes.
        """
        del index
        del libvirtxml
        if not isinstance(item, dict):
            raise xcepts.LibvirtXMLError("Expected a dictionary of host "
                                         "attributes, not a %s"
                                         % str(item))
        return ('iothreadsched', dict(item))

    @staticmethod
    def marshal_to_iothreadscheds(tag, attr_dict, index, libvirtxml):
        """
        Convert a iothreadsched tag and attributes to a dict.
        """
        del index
        del libvirtxml
        if tag != 'iothreadsched':
            return None
        return dict(attr_dict)


class VMOSXML(base.LibvirtXMLBase):

    """
    Class to access <os> tag of domain XML.

    Elements:
        os:           list attributes - firmware
        type:         text attributes - arch, machine
        loader:       path
        boots:        list attributes - dev
        bootmenu:          attributes - enable, timeout
        smbios:            attributes - mode
        bios:              attributes - useserial, rebootTimeout
        init:         text
        bootloader:   text
        bootloader_args:   text
        kernel:       text
        initrd:       text
        cmdline:      text
        dtb:          text
    TODO:
        initargs:     list
    """

    __slots__ = ('type', 'arch', 'machine', 'loader', 'boots', 'bootmenu_enable',
                 'smbios_mode', 'bios_useserial', 'bios_reboot_timeout', 'init',
                 'bootloader', 'bootloader_args', 'kernel', 'initrd', 'cmdline',
                 'dtb', 'initargs', 'loader_readonly', 'loader_type', 'nvram',
                 'nvram_template', 'secure', 'bootmenu_timeout', 'os_firmware')

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLElementText('type', self, parent_xpath='/',
                                 tag_name='type')
        accessors.XMLElementText('loader', self, parent_xpath='/',
                                 tag_name='loader')
        accessors.XMLAttribute('arch', self, parent_xpath='/',
                               tag_name='type', attribute='arch')
        accessors.XMLAttribute('machine', self, parent_xpath='/',
                               tag_name='type', attribute='machine')
        accessors.XMLElementList('boots', self, parent_xpath='/',
                                 marshal_from=self.marshal_from_boots,
                                 marshal_to=self.marshal_to_boots)
        accessors.XMLAttribute('bootmenu_enable', self, parent_xpath='/',
                               tag_name='bootmenu', attribute='enable')
        accessors.XMLAttribute('bootmenu_timeout', self, parent_xpath='/',
                               tag_name='bootmenu', attribute='timeout')
        accessors.XMLAttribute('smbios_mode', self, parent_xpath='/',
                               tag_name='smbios', attribute='mode')
        accessors.XMLAttribute('bios_useserial', self, parent_xpath='/',
                               tag_name='bios', attribute='useserial')
        accessors.XMLAttribute('bios_reboot_timeout', self, parent_xpath='/',
                               tag_name='bios', attribute='rebootTimeout')
        accessors.XMLElementText('bootloader', self, parent_xpath='/',
                                 tag_name='bootloader')
        accessors.XMLElementText('bootloader_args', self, parent_xpath='/',
                                 tag_name='bootloader_args')
        accessors.XMLElementText('kernel', self, parent_xpath='/',
                                 tag_name='kernel')
        accessors.XMLElementText('initrd', self, parent_xpath='/',
                                 tag_name='initrd')
        accessors.XMLElementText('cmdline', self, parent_xpath='/',
                                 tag_name='cmdline')
        accessors.XMLElementText('dtb', self, parent_xpath='/',
                                 tag_name='dtb')
        accessors.XMLElementText('init', self, parent_xpath='/',
                                 tag_name='init')
        accessors.XMLAttribute('loader_readonly', self, parent_xpath='/',
                               tag_name='loader', attribute='readonly')
        accessors.XMLAttribute('loader_type', self, parent_xpath='/',
                               tag_name='loader', attribute='type')
        accessors.XMLAttribute('nvram_template', self, parent_xpath='/',
                               tag_name='nvram', attribute='template')
        accessors.XMLElementText('nvram', self, parent_xpath='/',
                                 tag_name='nvram')
        accessors.XMLAttribute('secure', self, parent_xpath='/',
                               tag_name='loader', attribute='secure')
        accessors.XMLAttribute('os_firmware', self, parent_xpath='/',
                               tag_name='os', attribute='firmware')
        super(VMOSXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<os/>'

    @staticmethod
    def marshal_from_boots(item, index, libvirtxml):
        """
        Convert a string to boot tag and attributes.
        """
        del index
        del libvirtxml
        return ('boot', {'dev': item})

    @staticmethod
    def marshal_to_boots(tag, attr_dict, index, libvirtxml):
        """
        Convert a boot tag and attributes to a string.
        """
        del index
        del libvirtxml
        if tag != 'boot':
            return None
        return attr_dict['dev']


class VMPMXML(base.LibvirtXMLBase):

    """
    VM power management tag XML class

    Elements:
        suspend-to-disk:        attribute    - enabled
        suspend-to-mem:         attribute    - enabled
    """

    __slots__ = ('disk_enabled', 'mem_enabled')

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLAttribute('disk_enabled', self, parent_xpath='/',
                               tag_name='suspend-to-disk', attribute='enabled')
        accessors.XMLAttribute('mem_enabled', self, parent_xpath='/',
                               tag_name='suspend-to-mem', attribute='enabled')
        super(VMPMXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<pm/>'


class VMFeaturesXML(base.LibvirtXMLBase):

    """
    Class to access <features> tag of domain XML.

    Elements:
        feature_list       list of top level element
        hyperv_relaxed:    attribute - state
        hyperv_vapic:      attribute - state
        hyperv_spinlocks:  attributes - state, retries
        kvm_hidden:        attribute - state
        pvspinlock:        attribute - state
        smm:               attribute - state
    """

    __slots__ = ('feature_list', 'hyperv_relaxed_state', 'hyperv_vapic_state',
                 'hyperv_spinlocks_state', 'hyperv_spinlocks_retries',
                 'hyperv_tlbflush_state', 'hyperv_frequencies_state',
                 'hyperv_reenlightenment_state', 'hyperv_vpindex_state',
                 'kvm_hidden_state', 'pvspinlock_state', 'smm', 'hpt',
                 'htm', 'smm_tseg_unit', 'smm_tseg', 'nested_hv',
                 'pmu')

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLAttribute(property_name='hyperv_relaxed_state',
                               libvirtxml=self,
                               parent_xpath='/hyperv',
                               tag_name='relaxed',
                               attribute='state')
        accessors.XMLAttribute(property_name='hyperv_vapic_state',
                               libvirtxml=self,
                               parent_xpath='/hyperv',
                               tag_name='vapic',
                               attribute='state')
        accessors.XMLAttribute(property_name='hyperv_spinlocks_state',
                               libvirtxml=self,
                               parent_xpath='/hyperv',
                               tag_name='spinlocks',
                               attribute='state')
        accessors.XMLAttribute(property_name='hyperv_spinlocks_retries',
                               libvirtxml=self,
                               parent_xpath='/hyperv',
                               tag_name='spinlocks',
                               attribute='retries')
        accessors.XMLAttribute(property_name='hyperv_vpindex_state',
                               libvirtxml=self,
                               parent_xpath='/hyperv',
                               tag_name='vpindex',
                               attribute='state')
        accessors.XMLAttribute(property_name='hyperv_tlbflush_state',
                               libvirtxml=self,
                               parent_xpath='/hyperv',
                               tag_name='tlbflush',
                               attribute='state')
        accessors.XMLAttribute(property_name='hyperv_frequencies_state',
                               libvirtxml=self,
                               parent_xpath='/hyperv',
                               tag_name='frequencies',
                               attribute='state')
        accessors.XMLAttribute(property_name='hyperv_reenlightenment_state',
                               libvirtxml=self,
                               parent_xpath='/hyperv',
                               tag_name='reenlightenment',
                               attribute='state')
        accessors.XMLAttribute(property_name='kvm_hidden_state',
                               libvirtxml=self,
                               parent_xpath='/kvm',
                               tag_name='hidden',
                               attribute='state')
        accessors.XMLAttribute(property_name='pvspinlock_state',
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='pvspinlock',
                               attribute='state')
        accessors.XMLAttribute(property_name='smm',
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='smm',
                               attribute='state')
        accessors.XMLAttribute(property_name='smm_tseg_unit',
                               libvirtxml=self,
                               parent_xpath='/smm',
                               tag_name='tseg',
                               attribute='unit')
        accessors.XMLAttribute(property_name='nested_hv',
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='nested-hv',
                               attribute='state')
        accessors.XMLElementText('smm_tseg', self, parent_xpath='/smm',
                                 tag_name='tseg')
        accessors.XMLElementNest(property_name='hpt',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='hpt',
                                 subclass=VMFeaturesHptXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        accessors.XMLAttribute(property_name='htm',
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='htm',
                               attribute='state')
        accessors.XMLAttribute(property_name='pmu',
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='pmu',
                               attribute='state')
        accessors.AllForbidden(property_name="feature_list",
                               libvirtxml=self)
        super(VMFeaturesXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<features/>'

    def get_feature_list(self):
        """
        Return all features(top level elements) in xml
        """
        feature_list = []
        root = self.__dict_get__('xml').getroot()
        for feature in root:
            feature_list.append(feature.tag)
        return feature_list

    def has_feature(self, name):
        """
        Return true if the given feature exist in xml
        """
        return name in self.get_feature_list()

    def add_feature(self, name, attr_name='', attr_value=''):
        """
        Add a feature element to xml

        :params name: Feature name
        """
        if self.has_feature(name):
            logging.debug("Feature %s already exist, so remove it", name)
            self.remove_feature(name)
        root = self.__dict_get__('xml').getroot()
        new_attr = {}
        if attr_name:
            new_attr = {attr_name: attr_value}
        xml_utils.ElementTree.SubElement(root, name, new_attr)

    def remove_feature(self, name):
        """
        Remove a feature element from xml

        :params name: Feature name
        """
        root = self.__dict_get__('xml').getroot()
        remove_feature = root.find(name)
        if remove_feature is None:
            logging.error("Feature %s doesn't exist", name)
        else:
            root.remove(remove_feature)


class VMVCPUSXML(base.LibvirtXMLBase):

    """
    vcpus tag XML class

    Elements:
        vcpu: list of dict - id, enabled, hotpluggable, order
    """

    __slots__ = ('vcpu',)

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLElementList('vcpu', self, parent_xpath="/",
                                 marshal_from=self.marshal_from_vcpu,
                                 marshal_to=self.marshal_to_vcpu)
        super(VMVCPUSXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<vcpus/>'

    @staticmethod
    def marshal_from_vcpu(item, index, libvirtxml):
        """
        Convert a dict to vcpu tag and attributes
        """
        del index
        del libvirtxml
        if not isinstance(item, dict):
            raise xcepts.LibvirtXMLError("Expected a dictionary of vcpu "
                                         "attributes, not a %s"
                                         % str(item))
        return ('vcpu', dict(item))

    @staticmethod
    def marshal_to_vcpu(tag, attr_dict, index, libvirtxml):
        """
        Convert a vcpu tag and attributes to a dict
        """
        del index
        del libvirtxml
        if tag != 'vcpu':
            return None
        return dict(attr_dict)


# Sub-element of memoryBacking
class VMHugepagesXML(VMXML):

    """hugepages element"""

    __slots__ = ('pages',)

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLElementList('pages',
                                 libvirtxml=self,
                                 forbidden=[],
                                 parent_xpath="/",
                                 marshal_from=self.marshal_from_page,
                                 marshal_to=self.marshal_to_page)
        super(VMHugepagesXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<hugepages/>'

    # Sub-element of hugepages
    class PageXML(VMXML):

        """Page element of hugepages"""

        __slots__ = ('size', 'unit', 'nodeset')

        def __init__(self, virsh_instance=base.virsh):
            """
            Create new PageXML instance
            """
            accessors.XMLAttribute(property_name="size",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/hugepages',
                                   tag_name='page',
                                   attribute='size')
            accessors.XMLAttribute(property_name="unit",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/hugepages',
                                   tag_name='page',
                                   attribute='unit')
            accessors.XMLAttribute(property_name="nodeset",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/hugepages',
                                   tag_name='page',
                                   attribute='nodeset')
            super(VMHugepagesXML.PageXML, self).__init__(
                virsh_instance=virsh_instance)

        def update(self, attr_dict):
            for attr, value in list(attr_dict.items()):
                setattr(self, attr, value)

    @staticmethod
    def marshal_from_page(item, index, libvirtxml):
        """Convert a PageXML instance into tag + attributes"""
        del index
        del libvirtxml
        page = item.xmltreefile.find("/hugepages/page")
        try:
            return (page.tag, dict(list(page.items())))
        except AttributeError:  # Didn't find page
            raise xcepts.LibvirtXMLError("Expected a list of page "
                                         "instances, not a %s" % str(item))

    @staticmethod
    def marshal_to_page(tag, attr_dict, index, libvirtxml):
        """Convert a tag + attributes to a PageXML instance"""
        del index
        if tag == 'page':
            newone = VMHugepagesXML.PageXML(virsh_instance=libvirtxml.virsh)
            newone.update(attr_dict)
            return newone
        else:
            return None


class VMMemBackingXML(VMXML):

    """
    memoryBacking tag XML class

    Elements:
        hugepages
        nosharepages
        locked
        source
        access
        discard
    """

    __slots__ = ('hugepages', 'nosharepages', 'locked', 'source', 'access',
                 'discard', 'source_type', 'access_mode')

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLElementNest(property_name='hugepages',
                                 libvirtxml=self,
                                 parent_xpath='/',
                                 tag_name='hugepages',
                                 subclass=VMHugepagesXML,
                                 subclass_dargs={
                                     'virsh_instance': virsh_instance})
        for slot in ('nosharepages', 'locked', 'discard'):
            accessors.XMLElementBool(slot, self, parent_xpath='/',
                                     tag_name=slot)
        accessors.XMLElementText('source', self, parent_xpath='/',
                                 tag_name='source')
        accessors.XMLAttribute(property_name="source_type",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='source',
                               attribute='type')
        accessors.XMLElementText('access', self, parent_xpath='/',
                                 tag_name='access')
        accessors.XMLAttribute(property_name="access_mode",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='access',
                               attribute='mode')
        super(VMMemBackingXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<memoryBacking/>'


class VMMemTuneXML(base.LibvirtXMLBase):

    """
    Memory Tuning tag XML class

    Element:
        hard_limit:            int
        hard_limit_unit:       attribute
        soft_limit:            int
        soft_limit_unit:       attribute
        swap_hard_limit:       int
        swap_limit_unit:       attribute
        min_guarantee:         int
        min_guarantee_unit:    attribute
    """

    __slots__ = ('hard_limit', 'soft_limit', 'swap_hard_limit', 'min_guarantee',
                 'hard_limit_unit', 'soft_limit_unit', 'swap_limit_unit',
                 'min_guarantee_unit')

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLElementInt(property_name='hard_limit',
                                libvirtxml=self,
                                parent_xpath='/',
                                tag_name='hard_limit')
        accessors.XMLAttribute(property_name="hard_limit_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='hard_limit',
                               attribute='unit')
        accessors.XMLElementInt(property_name='soft_limit',
                                libvirtxml=self,
                                parent_xpath='/',
                                tag_name='soft_limit')
        accessors.XMLAttribute(property_name="soft_limit_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='soft_limit',
                               attribute='unit')
        accessors.XMLElementInt(property_name='swap_hard_limit',
                                libvirtxml=self,
                                parent_xpath='/',
                                tag_name='swap_hard_limit')
        accessors.XMLAttribute(property_name="swap_limit_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='swap_hard_limit',
                               attribute='unit')
        accessors.XMLElementInt(property_name='min_guarantee',
                                libvirtxml=self,
                                parent_xpath='/',
                                tag_name='min_guarantee')
        accessors.XMLAttribute(property_name="min_guarantee_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='min_guarantee',
                               attribute='unit')
        super(VMMemTuneXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<memtune/>'


class VMPerfXML(VMXML):

    """
    perf tag XML class

    Properties:
        event :
            dict, keys: name, enabled
    """

    __slots__ = ('events',)

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLElementList('events', self, forbidden=[],
                                 parent_xpath='/',
                                 marshal_from=self.marshal_from_event,
                                 marshal_to=self.marshal_to_event)

        super(VMPerfXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<perf/>'

    # Sub-element of perf
    class EventXML(VMXML):

        """Event element of perf"""

        __slots__ = ('name', 'enabled')

        def __init__(self, virsh_instance=base.virsh):
            """
            Create new EventXML instance
            """
            accessors.XMLAttribute(property_name="name",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/perf',
                                   tag_name='event',
                                   attribute='name')
            accessors.XMLAttribute(property_name="enabled",
                                   libvirtxml=self,
                                   forbidden=[],
                                   parent_xpath='/perf',
                                   tag_name='event',
                                   attribute='enabled')
            super(VMPerfXML.EventXML, self).__init__(
                virsh_instance=virsh_instance)

        def update(self, attr_dict):
            for attr, value in list(attr_dict.items()):
                setattr(self, attr, value)

    @staticmethod
    def marshal_from_event(item, index, libvirtxml):
        """
        Convert a EventXML instance to tag and attributes
        """
        del index
        del libvirtxml

        event = item.xmltreefile.find("/perf/event")
        try:
            return (event.tag, dict(list(event.items())))
        except AttributeError:  # Didn't find event
            raise xcepts.LibvirtXMLError("Expected a list of event "
                                         "instances, not a %s" % str(item))

    @staticmethod
    def marshal_to_event(tag, attr_dict, index, libvirtxml):
        """
        Convert a tag and attributes to a EventXML instance
        """
        del index
        if tag == 'event':
            newone = VMPerfXML.EventXML(virsh_instance=libvirtxml.virsh)
            newone.update(attr_dict)
            return newone
        else:
            return None


class VMIothreadidsXML(VMXML):

    """
    iothreadids tag XML class

    Elements:
        iothread
    """

    __slots__ = ('iothread',)

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLElementList('iothread', self, parent_xpath='/',
                                 marshal_from=self.marshal_from_iothreads,
                                 marshal_to=self.marshal_to_iothreads)
        super(VMIothreadidsXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<iothreadids/>'

    @staticmethod
    def marshal_from_iothreads(item, index, libvirtxml):
        """
        Convert a string to iothread tag and attributes.
        """
        del index
        del libvirtxml
        return ('iothread', {'id': item})

    @staticmethod
    def marshal_to_iothreads(tag, attr_dict, index, libvirtxml):
        """
        Convert a iothread tag and attributes to a string.
        """
        del index
        del libvirtxml
        if tag != 'iothread':
            return None
        return attr_dict['id']


class VMFeaturesHptXML(base.LibvirtXMLBase):

    """
    Hpt tag XML class of features tag

    Element:
        resizing:               text
        maxpagesize_unit:       attribute
        maxpagesize:            int
    Example:
        <hpt resizing="required">
            <maxpagesize unit="KiB">64</maxpagesize>
        </hpt>
    """

    __slots__ = ('resizing', 'maxpagesize_unit', 'maxpagesize')

    def __init__(self, virsh_instance=base.virsh):
        accessors.XMLAttribute(property_name="resizing",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='hpt',
                               attribute='resizing')
        accessors.XMLElementInt(property_name='maxpagesize',
                                libvirtxml=self,
                                parent_xpath='/',
                                tag_name='maxpagesize')
        accessors.XMLAttribute(property_name="maxpagesize_unit",
                               libvirtxml=self,
                               parent_xpath='/',
                               tag_name='maxpagesize',
                               attribute='unit')
        super(VMFeaturesHptXML, self).__init__(virsh_instance=virsh_instance)
        self.xml = '<hpt/>'
