Пример #1
0
def _update_unknown_device_info(vm):
    """
    Obtain info about unknown devices from libvirt domain and update the
    corresponding device structures.  Unknown device is a device that has an
    address but wasn't passed during VM creation request.

    :param vm: VM for which the device info should be updated
    :type vm: `class:Vm` instance

    """
    def isKnownDevice(alias):
        for dev in vm.conf['devices']:
            if dev.get('alias') == alias:
                return True
        return False

    for x in vmxml.children(vm.domain.devices):
        # Ignore empty nodes and devices without address
        if vmxml.find_first(x, 'address', None) is None:
            continue

        alias = core.find_device_alias(x)
        if not isKnownDevice(alias):
            address = vmxml.device_address(x)
            # In general case we assume that device has attribute 'type',
            # if it hasn't dom_attribute returns ''.
            device = vmxml.attr(x, 'type')
            newDev = {'type': vmxml.tag(x),
                      'alias': alias,
                      'device': device,
                      'address': address}
            vm.conf['devices'].append(newDev)
Пример #2
0
def _update_unknown_device_info(vm):
    """
    Obtain info about unknown devices from libvirt domain and update the
    corresponding device structures.  Unknown device is a device that has an
    address but wasn't passed during VM creation request.

    :param vm: VM for which the device info should be updated
    :type vm: `class:Vm` instance

    """
    def isKnownDevice(alias):
        for dev in vm.conf['devices']:
            if dev.get('alias') == alias:
                return True
        return False

    for x in vmxml.children(vm.domain.devices):
        # Ignore empty nodes and devices without address
        if vmxml.find_first(x, 'address', None) is None:
            continue

        alias = core.find_device_alias(x)
        if not isKnownDevice(alias):
            address = vmxml.device_address(x)
            # In general case we assume that device has attribute 'type',
            # if it hasn't dom_attribute returns ''.
            device = vmxml.attr(x, 'type')
            newDev = {
                'type': vmxml.tag(x),
                'alias': alias,
                'device': device,
                'address': address
            }
            vm.conf['devices'].append(newDev)
Пример #3
0
def dev_map_from_domain_xml(vmid, dom_desc, md_desc, log):
    """
    Create a device map - same format as empty_dev_map from a domain XML
    representation. The domain XML is accessed through a Domain Descriptor.

    :param vmid: UUID of the vm whose devices need to be initialized.
    :type vmid: basestring
    :param dom_desc: domain descriptor to provide access to the domain XML
    :type dom_desc: `class DomainDescriptor`
    :param md_desc: metadata descriptor to provide access to the device
                    metadata
    :type md_desc: `class metadata.Descriptor`
    :param log: logger instance to use for messages, and to pass to device
    objects.
    :type log: logger instance, as returned by logging.getLogger()
    :return: map of initialized devices, map of devices needing refresh.
    :rtype: A device map, in the same format as empty_dev_map() would return.
    """

    dev_map = empty_dev_map()
    for dev_elem in vmxml.children(dom_desc.devices):
        try:
            dev_type, dev_class = identify_from_xml_elem(dev_elem)
        except core.SkipDevice:
            log.debug('skipping unhandled device: %r', dev_elem.tag)
            continue

        dev_meta = {'vmid': vmid}
        attrs = dev_class.get_identifying_attrs(dev_elem)
        if attrs:
            with md_desc.device(**attrs) as dev_data:
                dev_meta.update(dev_data)
        dev_obj = dev_class.from_xml_tree(log, dev_elem, dev_meta)
        dev_map[dev_type].append(dev_obj)
    return dev_map
Пример #4
0
def _device_elements(dom_desc, log):
    for dev_elem in vmxml.children(dom_desc.devices):
        try:
            dev_type, dev_class = identify_from_xml_elem(dev_elem)
        except core.SkipDevice:
            log.debug('skipping unhandled device: %r', dev_elem.tag)
        else:
            yield dev_type, dev_class, dev_elem
Пример #5
0
def _device_elements(dom_desc, log):
    for dev_elem in vmxml.children(dom_desc.devices):
        try:
            dev_type, dev_class = identify_from_xml_elem(dev_elem)
        except core.SkipDevice:
            log.debug('skipping unhandled device: %r', dev_elem.tag)
        else:
            yield dev_type, dev_class, dev_elem
Пример #6
0
def _device_elements(dom_desc, log):
    for dev_elem in vmxml.children(dom_desc.devices):
        try:
            dev_type, dev_class = identify_from_xml_elem(dev_elem)
        except core.SkipDevice:
            pass
        else:
            yield dev_type, dev_class, dev_elem
Пример #7
0
def dev_elems_from_xml(vm, xml):
    """
    Return device instance building elements from provided XML.

    The XML must contain <devices> element with a single device subelement, the
    one to create the instance for.  Depending on the device kind <metadata>
    element may be required to provide device metadata; the element may and
    needn't contain unrelated metadata.  This function is used in device
    hot(un)plugs.

    Example `xml` value (top element tag may be arbitrary):

      <?xml version='1.0' encoding='UTF-8'?>
      <hotplug>
        <devices>
          <interface type="bridge">
            <mac address="66:55:44:33:22:11"/>
            <model type="virtio" />
            <source bridge="ovirtmgmt" />
            <filterref filter="vdsm-no-mac-spoofing" />
            <link state="up" />
            <bandwidth />
          </interface>
        </devices>
        <metadata xmlns:ns0="http://ovirt.org/vm/tune/1.0"
                  xmlns:ovirt-vm="http://ovirt.org/vm/1.0">
          <ovirt-vm:vm xmlns:ovirt-vm="http://ovirt.org/vm/1.0">
            <ovirt-vm:device mac_address='66:55:44:33:22:11'>
              <ovirt-vm:network>test</ovirt-vm:network>
              <ovirt-vm:portMirroring>
                <ovirt-vm:network>network1</ovirt-vm:network>
                <ovirt-vm:network>network2</ovirt-vm:network>
              </ovirt-vm:portMirroring>
            </ovirt-vm:device>
          </ovirt-vm:vm>
        </metadata>
      </hotplug>

    :param xml: XML specifying the device as described above.
    :type xml: basestring
    :returns: Triplet (device_class, device_element, device_meta) where
      `device_class` is the class to be used to create the device instance;
      `device_element` and `device_meta` are objects to be passed as arguments
      to device_class `from_xml_tree` method.
    """
    dom = xmlutils.fromstring(xml)
    devices = vmxml.find_first(dom, 'devices')
    dev_elem = next(vmxml.children(devices))
    _dev_type, dev_class = identify_from_xml_elem(dev_elem)
    meta = vmxml.find_first(dom, 'metadata', None)
    if meta is None:
        md_desc = metadata.Descriptor()
    else:
        md_desc = metadata.Descriptor.from_xml(xmlutils.tostring(meta))
    dev_meta = _get_metadata_from_elem_xml(vm.id, md_desc, dev_class, dev_elem)
    return dev_class, dev_elem, dev_meta
Пример #8
0
def dev_elems_from_xml(vm, xml):
    """
    Return device instance building elements from provided XML.

    The XML must contain <devices> element with a single device subelement, the
    one to create the instance for.  Depending on the device kind <metadata>
    element may be required to provide device metadata; the element may and
    needn't contain unrelated metadata.  This function is used in device
    hot(un)plugs.

    Example `xml` value (top element tag may be arbitrary):

      <?xml version='1.0' encoding='UTF-8'?>
      <hotplug>
        <devices>
          <interface type="bridge">
            <mac address="66:55:44:33:22:11"/>
            <model type="virtio" />
            <source bridge="ovirtmgmt" />
            <filterref filter="vdsm-no-mac-spoofing" />
            <link state="up" />
            <bandwidth />
          </interface>
        </devices>
        <metadata xmlns:ns0="http://ovirt.org/vm/tune/1.0"
                  xmlns:ovirt-vm="http://ovirt.org/vm/1.0">
          <ovirt-vm:vm xmlns:ovirt-vm="http://ovirt.org/vm/1.0">
            <ovirt-vm:device mac_address='66:55:44:33:22:11'>
              <ovirt-vm:network>test</ovirt-vm:network>
              <ovirt-vm:portMirroring>
                <ovirt-vm:network>network1</ovirt-vm:network>
                <ovirt-vm:network>network2</ovirt-vm:network>
              </ovirt-vm:portMirroring>
            </ovirt-vm:device>
          </ovirt-vm:vm>
        </metadata>
      </hotplug>

    :param xml: XML specifying the device as described above.
    :type xml: basestring
    :returns: Triplet (device_class, device_element, device_meta) where
      `device_class` is the class to be used to create the device instance;
      `device_element` and `device_meta` are objects to be passed as arguments
      to device_class `from_xml_tree` method.
    """
    dom = xmlutils.fromstring(xml)
    devices = vmxml.find_first(dom, 'devices')
    dev_elem = next(vmxml.children(devices))
    _dev_type, dev_class = identify_from_xml_elem(dev_elem)
    meta = vmxml.find_first(dom, 'metadata', None)
    if meta is None:
        md_desc = metadata.Descriptor()
    else:
        md_desc = metadata.Descriptor.from_xml(xmlutils.tostring(meta))
    dev_meta = _get_metadata_from_elem_xml(vm.id, md_desc, dev_class, dev_elem)
    return dev_class, dev_elem, dev_meta
Пример #9
0
    def parse_volume_chain(self, disk_xml):
        """
        Parses libvirt xml and extracts volume chain from it.

        Arguments:
             disk_xml (ElementTree): libvirt xml to parse

        Returns:
            list: VolumeChainEntry[] - List of chain entries where
            each entry contains volume UUID, volume path
            and volume index. For the 'top' volume index
            is None, as 'top' volume have no indices at
            all.

            VolumeChainEntry is reversed in relation to
            libvirt xml: xml is ordered from top to base
            volume, while volume chain is ordered from
            base to the top.

        Raises:
            InvalidBackingStoreIndex exception when index value is not int.
        """
        volChain = []
        index = None
        source_attr = SOURCE_ATTR[self.diskType]
        while True:
            path = vmxml.find_attr(disk_xml, 'source', source_attr)
            if not path:
                break

            if index is not None:
                try:
                    index = int(index)
                except ValueError:
                    raise InvalidBackingStoreIndex(path, index)

            # TODO: Allocation information is not available in the XML.  Switch
            # to the new interface once it becomes available in libvirt.
            alloc = None
            backingstore = next(vmxml.children(disk_xml, 'backingStore'), None)
            if backingstore is None:
                self.log.warning(
                    "<backingStore/> missing from backing "
                    "chain for drive %s", self.name)
                break

            entry = VolumeChainEntry(self.volume_id(path), path, alloc, index)
            volChain.insert(0, entry)

            disk_xml = backingstore
            index = vmxml.attr(backingstore, 'index')
        return volChain or None
Пример #10
0
def collect_inner_elements(el, d):
    """
    This helper method collects all nodes in el and adds them
    to dictionary d.

    :param el: XML DOM element object with text only children
    :param d: Dictionary to add the values to
    """
    for chel in vmxml.children(el):
        try:
            d[vmxml.tag(chel)] = int(vmxml.text(chel))
        except (IndexError, ValueError):
            log.exception("Invalid value for %s", vmxml.tag(chel))
Пример #11
0
def collect_inner_elements(el, d):
    """
    This helper method collects all nodes in el and adds them
    to dictionary d.

    :param el: XML DOM element object with text only children
    :param d: Dictionary to add the values to
    """
    for chel in vmxml.children(el):
        try:
            d[vmxml.tag(chel)] = int(vmxml.text(chel))
        except (IndexError, ValueError):
            log.exception("Invalid value for %s", vmxml.tag(chel))
Пример #12
0
    def parse_volume_chain(self, disk_xml):
        """
        Parses libvirt xml and extracts volume chain from it.

        Arguments:
             disk_xml (ElementTree): libvirt xml to parse

        Returns:
            list: VolumeChainEntry[] - List of chain entries where
            each entry contains volume UUID, volume path
            and volume index. For the 'top' volume index
            is None, as 'top' volume have no indices at
            all.

            VolumeChainEntry is reversed in relation to
            libvirt xml: xml is ordered from top to base
            volume, while volume chain is ordered from
            base to the top.

        Raises:
            InvalidBackingStoreIndex exception when index value is not int.
        """
        volChain = []
        index = None
        source_attr = SOURCE_ATTR[self.diskType]
        while True:
            path = vmxml.find_attr(disk_xml, 'source', source_attr)
            if not path:
                break

            if index is not None:
                try:
                    index = int(index)
                except ValueError:
                    raise InvalidBackingStoreIndex(path, index)

            # TODO: Allocation information is not available in the XML.  Switch
            # to the new interface once it becomes available in libvirt.
            alloc = None
            backingstore = next(vmxml.children(disk_xml, 'backingStore'), None)
            if backingstore is None:
                self.log.warning("<backingStore/> missing from backing "
                                 "chain for drive %s", self.name)
                break

            entry = VolumeChainEntry(self.volume_id(path), path, alloc, index)
            volChain.insert(0, entry)

            disk_xml = backingstore
            index = vmxml.attr(backingstore, 'index')
        return volChain or None
Пример #13
0
def replace_devices_xml(domxml, devices_xml):
    devices = vmxml.find_first(domxml, 'devices', None)

    old_devs = [
        dev for dev in vmxml.children(devices) if dev.tag in hwclass.TO_REFRESH
    ]
    for old_dev in old_devs:
        vmxml.remove_child(devices, old_dev)

    for dev_class in hwclass.TO_REFRESH:
        for dev in devices_xml[dev_class]:
            vmxml.append_child(devices, etree_child=dev)

    return domxml
Пример #14
0
def replace_device_xml_with_hooks_xml(dom, vm_id, vm_custom, md_desc=None):
    """
    Process the before_device_create hook point. This means that
    some device XML snippet may be entirely replaced by the output
    of one hook.
    Hook are invoked only if a given device has custom properties.
    Please note that this explicitely required by the contract of
    the hook point, so we can't really do better than that.
    """
    if md_desc is None:
        md_desc = metadata.Descriptor.from_tree(dom)
    # 'devices' MUST be present, so let's fail loudly if it isn't.
    devs = vmxml.find_first(dom, 'devices')

    to_remove = []
    to_append = []

    for dev_elem in vmxml.children(devs):
        if dev_elem.tag == vmdevices.hwclass.DISK:
            # disk devices need special processing, to be done separately
            continue

        try:
            dev_meta = vmdevices.common.dev_meta_from_elem(
                dev_elem, vm_id, md_desc)
        except vmdevices.core.SkipDevice:
            # metadata is optional, so it is ok to just skip devices
            # with no metadata attached.
            continue

        try:
            dev_custom = dev_meta['custom']
        except KeyError:
            # custom properties are optional, and mostly used for
            # network devices. It is OK to just go ahead.
            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)
Пример #15
0
 def get_bandwidth_xml(specParams, oldBandwidth=None):
     """Returns a valid libvirt xml dom element object."""
     bandwidth = vmxml.Element('bandwidth')
     old = {} if oldBandwidth is None else dict(
         (vmxml.tag(elem), elem) for elem in vmxml.children(oldBandwidth))
     for key in ('inbound', 'outbound'):
         elem = specParams.get(key)
         if elem is None:  # Use the old setting if present
             if key in old:
                 bandwidth.appendChild(etree_element=old[key])
         elif elem:
             # Convert the values to string for adding them to the XML def
             attrs = dict((key, str(value)) for key, value in elem.items())
             bandwidth.appendChildWithArgs(key, **attrs)
     return bandwidth
Пример #16
0
def replace_devices_xml(domxml, devices_xml):
    devices = vmxml.find_first(domxml, 'devices', None)

    refreshable = get_refreshable_device_classes()

    old_devs = [
        dev for dev in vmxml.children(devices) if dev.tag in refreshable
    ]
    for old_dev in old_devs:
        vmxml.remove_child(devices, old_dev)

    for dev_class in refreshable:
        for dev in devices_xml[dev_class]:
            vmxml.append_child(devices, etree_child=dev)

    return domxml
Пример #17
0
def xml_device_by_alias(device_xml, alias):
    """
    Return an XML device having the given alias.

    :param device_xml: parsed <devices> element, typically taken
      from DomainDescriptor.devices
    :type device_xml: DOM object
    :param alias: device alias
    :type alias: string
    :returns: DOM object of the device element having the given alias
    :raises: `LookupError` if no device with `alias` is found
    """
    for dom in vmxml.children(device_xml):
        xml_alias = core.find_device_alias(dom)
        if xml_alias and xml_alias == alias:
            return dom
    raise LookupError("Unable to find matching XML for device %r" % (alias, ))
Пример #18
0
def update_disks_xml_from_objs(vm, dom, disk_devices):
    """
    Perform host-local changes to the XML disk configuration.

    The XML may change because of the following:

    - the after_disk_prepare hook point (aka the localdisk hook)
      The hook may change:
      * diskType
      * path
      * format
    - Vdsm itself needs to prepare the images.
      Vdsm may change:
      * path

    Engine can sometimes predict the path, but Vdsm is in charge
    to set up the images locally, so it can (and should be expected to)
    change the image path.
    """
    # 'devices' MUST be present, so let's fail loudly if it isn't.
    devs = vmxml.find_first(dom, 'devices')
    for dev_elem in vmxml.children(devs):
        if dev_elem.tag != vmdevices.hwclass.DISK:
            continue

        # we use the device name because
        # - `path` uniquely identifies a device, but it is expected to change
        # - `serial` uniquely identifies a device, but it is not available
        #   for LUN devices
        # TODO: user-provided aliases are the best solution, we need to
        # switch to them.
        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)
Пример #19
0
def update_sysinfo(dom, osname, osversion, hostserial):
    sys_info = vmxml.find_first(dom, 'sysinfo/system', None)
    if sys_info is None:
        # TODO: log?
        return

    replaceables = {
        'product': ('OS-NAME:', osname),
        'version': ('OS-VERSION:', osversion),
        'serial': ('HOST-SERIAL:', hostserial),
    }

    for entry in vmxml.children(sys_info):
        name = entry.attrib.get('name', None)
        if name not in replaceables:
            continue

        placeholder, value = replaceables[name]
        if entry.text.startswith(placeholder):
            entry.text = value
Пример #20
0
def update_leases_xml_from_disk_objs(vm, dom, disk_devices):
    # 'devices' MUST be present, so let's fail loudly if it isn't.
    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')
            # must be a vm lease, let's skip it
            continue

        vmdevices.lease.update_lease_element_from_info(
            dev_elem, info, params, vm.log)
Пример #21
0
def replace_disks_xml(dom, disk_devices):
    """
    Replace the XML snippet of all disk devices with fresh
    configuration obtained from Vdsm.
    Vdsm initializes its disk device objects from the XML.
    Vdsm also needs to do some per-host adjustments on
    the disk devices (e.g. prepare the paths), so we can't
    avoid this replacement.
    """
    # 'devices' MUST be present, so let's fail loudly if it isn't.
    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
Пример #22
0
 def test_children(self, start_tag, tag, number):
     element = vmxml.find_first(self._dom, start_tag)
     self.assertEqual(len(list(vmxml.children(element, tag))), number)
Пример #23
0
    def update_device_info(cls, vm, device_conf):
        for x in vm.domain.get_device_elements('interface'):
            devType = vmxml.attr(x, 'type')
            mac = vmxml.find_attr(x, 'mac', 'address')
            alias = core.find_device_alias(x)
            xdrivers = vmxml.find_first(x, 'driver', None)
            driver = ({
                'name': vmxml.attr(xdrivers, 'name'),
                'queues': vmxml.attr(xdrivers, 'queues')
            } if xdrivers is not None else {})
            if devType == 'hostdev':
                name = alias
                model = 'passthrough'
            else:
                name = vmxml.find_attr(x, 'target', 'dev')
                model = vmxml.find_attr(x, 'model', 'type')

            network = None
            try:
                if vmxml.find_attr(x, 'link', 'state') == 'down':
                    linkActive = False
                else:
                    linkActive = True
            except IndexError:
                linkActive = True
            source = vmxml.find_first(x, 'source', None)
            if source is not None:
                network = vmxml.attr(source, 'bridge')
                if not network:
                    network = libvirtnetwork.netname_l2o(
                        vmxml.attr(source, 'network'))

            # Get nic address
            address = {}
            # TODO: fix vmxml.device_address and its users to have this code.
            for child in vmxml.children(x, 'address'):
                address = dict((k.strip(), v.strip())
                               for k, v in vmxml.attributes(child).items())
                break

            for nic in device_conf:
                if nic.macAddr.lower() == mac.lower():
                    nic.name = name
                    nic.alias = alias
                    nic.address = address
                    nic.linkActive = linkActive
                    if driver:
                        # If a driver was reported, pass it back to libvirt.
                        # Engine (vm's conf) is not interested in this value.
                        nic.driver = driver
            # Update vm's conf with address for known nic devices
            knownDev = False
            for dev in vm.conf['devices']:
                if (dev['type'] == hwclass.NIC
                        and dev['macAddr'].lower() == mac.lower()):
                    dev['address'] = address
                    dev['alias'] = alias
                    dev['name'] = name
                    dev['linkActive'] = linkActive
                    knownDev = True
            # Add unknown nic device to vm's conf
            if not knownDev:
                nicDev = {
                    'type': hwclass.NIC,
                    'device': devType,
                    'macAddr': mac,
                    'nicModel': model,
                    'address': address,
                    'alias': alias,
                    'name': name,
                    'linkActive': linkActive
                }
                if network:
                    nicDev['network'] = network
                vm.conf['devices'].append(nicDev)
Пример #24
0
 def test_children(self, start_tag, tag, number):
     element = vmxml.find_first(self._dom, start_tag)
     self.assertEqual(len(list(vmxml.children(element, tag))), number)