
from __future__ import absolute_import
from __future__ import division


from vdsm import constants
from vdsm import host
from vdsm import osinfo

from vdsm.common import cpuarch
from vdsm.common import hooks
from vdsm.common import xmlutils
from vdsm.virt import domain_descriptor
from vdsm.virt import libvirtxml
from vdsm.virt import metadata
from vdsm.virt import vmdevices
from vdsm.virt import vmxml


def replace_disks_xml(dom, disk_devices):
    devs = vmxml.find_first(dom, 'devices')

    to_remove = [
        dev_elem
        for dev_elem in vmxml.children(devs)
        if dev_elem.tag == vmdevices.hwclass.DISK
    ]
    for dev_elem in to_remove:
        vmxml.remove_child(devs, dev_elem)

    for dev in disk_devices:
        vmxml.append_child(devs, child=dev.getXML())

    return dom


def update_leases_xml_from_disk_objs(vm, dom, disk_devices):
    devs = vmxml.find_first(dom, 'devices')

    for dev_elem in vmxml.children(devs):
        if dev_elem.tag != vmdevices.hwclass.LEASE:
            continue

        params = vmdevices.lease.parse_xml(dev_elem, {})
        if not params:
            vm.log.warning('could not parse lease: %s',
                           xmlutils.tostring(dev_elem))
            continue

        info = vmdevices.lease.find_drive_lease_info(
            params['sd_id'], params['lease_id'], disk_devices)
        if info is None:
            vm.log.debug('lease with not corresponding drive info, skipped')
            continue

        vmdevices.lease.update_lease_element_from_info(
            dev_elem, info, params, vm.log)


def update_disks_xml_from_objs(vm, dom, disk_devices):
    devs = vmxml.find_first(dom, 'devices')
    for dev_elem in vmxml.children(devs):
        if dev_elem.tag != vmdevices.hwclass.DISK:
            continue

        attrs = vmdevices.storage.Drive.get_identifying_attrs(dev_elem)
        if not attrs:
            vm.log.warning('could not identify drive: %s',
                           xmlutils.tostring(dev_elem))
            continue

        try:
            disk_obj = vmdevices.lookup.drive_by_name(disk_devices,
                                                      attrs['name'])
        except LookupError:
            vm.log.warning('unknown drive %r, skipped', attrs['name'])
            continue

        vmdevices.storagexml.update_disk_element_from_object(
            dev_elem, disk_obj, vm.log, replace_attribs=True)


def replace_device_xml_with_hooks_xml(dom, vm_id, vm_custom, md_desc=None):
    if md_desc is None:
        md_desc = metadata.Descriptor.from_tree(dom)
    devs = vmxml.find_first(dom, 'devices')

    to_remove = []
    to_append = []

    for dev_elem in vmxml.children(devs):
        if dev_elem.tag == vmdevices.hwclass.DISK:
            continue

        try:
            dev_meta = vmdevices.common.dev_meta_from_elem(
                dev_elem, vm_id, md_desc)
        except vmdevices.core.SkipDevice:
            continue

        try:
            dev_custom = dev_meta['custom']
        except KeyError:
            continue

        hook_xml = hooks.before_device_create(
            xmlutils.tostring(dev_elem, pretty=True),
            vm_custom,
            dev_custom)

        to_remove.append(dev_elem)
        to_append.append(xmlutils.fromstring(hook_xml))

    for dev_elem in to_remove:
        vmxml.remove_child(devs, dev_elem)

    for dev_elem in to_append:
        vmxml.append_child(devs, etree_child=dev_elem)


def replace_placeholders(dom, arch, serial=None):
    if cpuarch.is_x86(arch):
        osd = osinfo.version()
        os_version = osd.get('version', '') + '-' + osd.get('release', '')
        serial_number = host.uuid() if serial is None else serial
        libvirtxml.update_sysinfo(
            dom, constants.SMBIOS_OSNAME, os_version, serial_number)


def add_mediated_device(dom, mdev_uuid):
    devs = vmxml.find_first(dom, 'devices')
    hostdev = libvirtxml.make_mdev_element(mdev_uuid)
    devs.append(hostdev)


def _make_disk_devices(engine_xml, log):
    engine_domain = domain_descriptor.DomainDescriptor(engine_xml)
    engine_md = metadata.Descriptor.from_xml(engine_xml)
    params = vmdevices.common.storage_device_params_from_domain_xml(
        engine_domain.id, engine_domain, engine_md, log)
    return [vmdevices.storage.Drive(log, **p) for p in params]
