mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-28 08:35:22 +00:00
921d67e03b
Convert the type of loop iterators named 'i', 'j', k', 'ii', 'jj', 'kk', to be 'size_t' instead of 'int' or 'unsigned int', also santizing 'ii', 'jj', 'kk' to use the normal 'i', 'j', 'k' naming Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
1643 lines
56 KiB
C
1643 lines
56 KiB
C
/*
|
|
* node_device_conf.c: config handling for node devices
|
|
*
|
|
* Copyright (C) 2009-2013 Red Hat, Inc.
|
|
* Copyright (C) 2008 Virtual Iron Software, Inc.
|
|
* Copyright (C) 2008 David F. Lively
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Author: David F. Lively <dlively@virtualiron.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include "virerror.h"
|
|
#include "datatypes.h"
|
|
#include "viralloc.h"
|
|
#include "virstring.h"
|
|
#include "node_device_conf.h"
|
|
#include "device_conf.h"
|
|
#include "virxml.h"
|
|
#include "virbuffer.h"
|
|
#include "viruuid.h"
|
|
#include "virrandom.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NODEDEV
|
|
|
|
VIR_ENUM_IMPL(virNodeDevCap, VIR_NODE_DEV_CAP_LAST,
|
|
"system",
|
|
"pci",
|
|
"usb_device",
|
|
"usb",
|
|
"net",
|
|
"scsi_host",
|
|
"scsi_target",
|
|
"scsi",
|
|
"storage",
|
|
"fc_host",
|
|
"vports",
|
|
"scsi_generic")
|
|
|
|
VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST,
|
|
"80203",
|
|
"80211")
|
|
|
|
static int
|
|
virNodeDevCapsDefParseString(const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
char **string)
|
|
{
|
|
char *s;
|
|
|
|
if (!(s = virXPathString(xpath, ctxt)))
|
|
return -1;
|
|
|
|
*string = s;
|
|
return 0;
|
|
}
|
|
|
|
int virNodeDeviceHasCap(const virNodeDeviceObjPtr dev, const char *cap)
|
|
{
|
|
virNodeDevCapsDefPtr caps = dev->def->caps;
|
|
while (caps) {
|
|
if (STREQ(cap, virNodeDevCapTypeToString(caps->type)))
|
|
return 1;
|
|
caps = caps->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
virNodeDeviceObjPtr
|
|
virNodeDeviceFindBySysfsPath(const virNodeDeviceObjListPtr devs,
|
|
const char *sysfs_path)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < devs->count; i++) {
|
|
virNodeDeviceObjLock(devs->objs[i]);
|
|
if ((devs->objs[i]->def->sysfs_path != NULL) &&
|
|
(STREQ(devs->objs[i]->def->sysfs_path, sysfs_path))) {
|
|
return devs->objs[i];
|
|
}
|
|
virNodeDeviceObjUnlock(devs->objs[i]);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
virNodeDeviceObjPtr virNodeDeviceFindByName(const virNodeDeviceObjListPtr devs,
|
|
const char *name)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < devs->count; i++) {
|
|
virNodeDeviceObjLock(devs->objs[i]);
|
|
if (STREQ(devs->objs[i]->def->name, name))
|
|
return devs->objs[i];
|
|
virNodeDeviceObjUnlock(devs->objs[i]);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void virNodeDeviceDefFree(virNodeDeviceDefPtr def)
|
|
{
|
|
virNodeDevCapsDefPtr caps;
|
|
|
|
if (!def)
|
|
return;
|
|
|
|
VIR_FREE(def->name);
|
|
VIR_FREE(def->parent);
|
|
VIR_FREE(def->driver);
|
|
VIR_FREE(def->sysfs_path);
|
|
VIR_FREE(def->parent_sysfs_path);
|
|
|
|
caps = def->caps;
|
|
while (caps) {
|
|
virNodeDevCapsDefPtr next = caps->next;
|
|
virNodeDevCapsDefFree(caps);
|
|
caps = next;
|
|
}
|
|
|
|
VIR_FREE(def);
|
|
}
|
|
|
|
void virNodeDeviceObjFree(virNodeDeviceObjPtr dev)
|
|
{
|
|
if (!dev)
|
|
return;
|
|
|
|
virNodeDeviceDefFree(dev->def);
|
|
if (dev->privateFree)
|
|
(*dev->privateFree)(dev->privateData);
|
|
|
|
virMutexDestroy(&dev->lock);
|
|
|
|
VIR_FREE(dev);
|
|
}
|
|
|
|
void virNodeDeviceObjListFree(virNodeDeviceObjListPtr devs)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < devs->count; i++)
|
|
virNodeDeviceObjFree(devs->objs[i]);
|
|
VIR_FREE(devs->objs);
|
|
devs->count = 0;
|
|
}
|
|
|
|
virNodeDeviceObjPtr virNodeDeviceAssignDef(virNodeDeviceObjListPtr devs,
|
|
const virNodeDeviceDefPtr def)
|
|
{
|
|
virNodeDeviceObjPtr device;
|
|
|
|
if ((device = virNodeDeviceFindByName(devs, def->name))) {
|
|
virNodeDeviceDefFree(device->def);
|
|
device->def = def;
|
|
return device;
|
|
}
|
|
|
|
if (VIR_ALLOC(device) < 0)
|
|
return NULL;
|
|
|
|
if (virMutexInit(&device->lock) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("cannot initialize mutex"));
|
|
VIR_FREE(device);
|
|
return NULL;
|
|
}
|
|
virNodeDeviceObjLock(device);
|
|
device->def = def;
|
|
|
|
if (VIR_REALLOC_N(devs->objs, devs->count+1) < 0) {
|
|
device->def = NULL;
|
|
virNodeDeviceObjUnlock(device);
|
|
virNodeDeviceObjFree(device);
|
|
return NULL;
|
|
}
|
|
devs->objs[devs->count++] = device;
|
|
|
|
return device;
|
|
|
|
}
|
|
|
|
void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs,
|
|
const virNodeDeviceObjPtr dev)
|
|
{
|
|
size_t i;
|
|
|
|
virNodeDeviceObjUnlock(dev);
|
|
|
|
for (i = 0; i < devs->count; i++) {
|
|
virNodeDeviceObjLock(dev);
|
|
if (devs->objs[i] == dev) {
|
|
virNodeDeviceObjUnlock(dev);
|
|
virNodeDeviceObjFree(devs->objs[i]);
|
|
|
|
if (i < (devs->count - 1))
|
|
memmove(devs->objs + i, devs->objs + i + 1,
|
|
sizeof(*(devs->objs)) * (devs->count - (i + 1)));
|
|
|
|
if (VIR_REALLOC_N(devs->objs, devs->count - 1) < 0) {
|
|
; /* Failure to reduce memory allocation isn't fatal */
|
|
}
|
|
devs->count--;
|
|
|
|
break;
|
|
}
|
|
virNodeDeviceObjUnlock(dev);
|
|
}
|
|
}
|
|
|
|
char *virNodeDeviceDefFormat(const virNodeDeviceDefPtr def)
|
|
{
|
|
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
|
virNodeDevCapsDefPtr caps;
|
|
size_t i = 0;
|
|
|
|
virBufferAddLit(&buf, "<device>\n");
|
|
virBufferEscapeString(&buf, " <name>%s</name>\n", def->name);
|
|
virBufferEscapeString(&buf, " <path>%s</path>\n", def->sysfs_path);
|
|
if (def->parent) {
|
|
virBufferEscapeString(&buf, " <parent>%s</parent>\n", def->parent);
|
|
}
|
|
if (def->driver) {
|
|
virBufferAddLit(&buf, " <driver>\n");
|
|
virBufferEscapeString(&buf, " <name>%s</name>\n", def->driver);
|
|
virBufferAddLit(&buf, " </driver>\n");
|
|
}
|
|
|
|
for (caps = def->caps; caps; caps = caps->next) {
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
union _virNodeDevCapData *data = &caps->data;
|
|
|
|
virBufferAsprintf(&buf, " <capability type='%s'>\n",
|
|
virNodeDevCapTypeToString(caps->type));
|
|
switch (caps->type) {
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
if (data->system.product_name)
|
|
virBufferEscapeString(&buf, " <product>%s</product>\n",
|
|
data->system.product_name);
|
|
virBufferAddLit(&buf, " <hardware>\n");
|
|
if (data->system.hardware.vendor_name)
|
|
virBufferEscapeString(&buf, " <vendor>%s</vendor>\n",
|
|
data->system.hardware.vendor_name);
|
|
if (data->system.hardware.version)
|
|
virBufferEscapeString(&buf, " <version>%s</version>\n",
|
|
data->system.hardware.version);
|
|
if (data->system.hardware.serial)
|
|
virBufferEscapeString(&buf, " <serial>%s</serial>\n",
|
|
data->system.hardware.serial);
|
|
virUUIDFormat(data->system.hardware.uuid, uuidstr);
|
|
virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", uuidstr);
|
|
virBufferAddLit(&buf, " </hardware>\n");
|
|
virBufferAddLit(&buf, " <firmware>\n");
|
|
if (data->system.firmware.vendor_name)
|
|
virBufferEscapeString(&buf, " <vendor>%s</vendor>\n",
|
|
data->system.firmware.vendor_name);
|
|
if (data->system.firmware.version)
|
|
virBufferEscapeString(&buf, " <version>%s</version>\n",
|
|
data->system.firmware.version);
|
|
if (data->system.firmware.release_date)
|
|
virBufferEscapeString(&buf,
|
|
" <release_date>%s</release_date>\n",
|
|
data->system.firmware.release_date);
|
|
virBufferAddLit(&buf, " </firmware>\n");
|
|
break;
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
|
virBufferAsprintf(&buf, " <domain>%d</domain>\n",
|
|
data->pci_dev.domain);
|
|
virBufferAsprintf(&buf, " <bus>%d</bus>\n", data->pci_dev.bus);
|
|
virBufferAsprintf(&buf, " <slot>%d</slot>\n",
|
|
data->pci_dev.slot);
|
|
virBufferAsprintf(&buf, " <function>%d</function>\n",
|
|
data->pci_dev.function);
|
|
virBufferAsprintf(&buf, " <product id='0x%04x'",
|
|
data->pci_dev.product);
|
|
if (data->pci_dev.product_name)
|
|
virBufferEscapeString(&buf, ">%s</product>\n",
|
|
data->pci_dev.product_name);
|
|
else
|
|
virBufferAddLit(&buf, " />\n");
|
|
virBufferAsprintf(&buf, " <vendor id='0x%04x'",
|
|
data->pci_dev.vendor);
|
|
if (data->pci_dev.vendor_name)
|
|
virBufferEscapeString(&buf, ">%s</vendor>\n",
|
|
data->pci_dev.vendor_name);
|
|
else
|
|
virBufferAddLit(&buf, " />\n");
|
|
if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION) {
|
|
virBufferAddLit(&buf, " <capability type='phys_function'>\n");
|
|
virBufferAsprintf(&buf,
|
|
" <address domain='0x%.4x' bus='0x%.2x' "
|
|
"slot='0x%.2x' function='0x%.1x'/>\n",
|
|
data->pci_dev.physical_function->domain,
|
|
data->pci_dev.physical_function->bus,
|
|
data->pci_dev.physical_function->slot,
|
|
data->pci_dev.physical_function->function);
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
}
|
|
if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION) {
|
|
virBufferAddLit(&buf, " <capability type='virt_functions'>\n");
|
|
for (i = 0; i < data->pci_dev.num_virtual_functions; i++) {
|
|
virBufferAsprintf(&buf,
|
|
" <address domain='0x%.4x' bus='0x%.2x' "
|
|
"slot='0x%.2x' function='0x%.1x'/>\n",
|
|
data->pci_dev.virtual_functions[i]->domain,
|
|
data->pci_dev.virtual_functions[i]->bus,
|
|
data->pci_dev.virtual_functions[i]->slot,
|
|
data->pci_dev.virtual_functions[i]->function);
|
|
}
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
}
|
|
if (data->pci_dev.nIommuGroupDevices) {
|
|
virBufferAsprintf(&buf, " <iommuGroup number='%d'>\n",
|
|
data->pci_dev.iommuGroupNumber);
|
|
for (i = 0; i < data->pci_dev.nIommuGroupDevices; i++) {
|
|
virBufferAsprintf(&buf,
|
|
" <address domain='0x%.4x' bus='0x%.2x' "
|
|
"slot='0x%.2x' function='0x%.1x'/>\n",
|
|
data->pci_dev.iommuGroupDevices[i]->domain,
|
|
data->pci_dev.iommuGroupDevices[i]->bus,
|
|
data->pci_dev.iommuGroupDevices[i]->slot,
|
|
data->pci_dev.iommuGroupDevices[i]->function);
|
|
}
|
|
virBufferAddLit(&buf, " </iommuGroup>\n");
|
|
}
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
|
virBufferAsprintf(&buf, " <bus>%d</bus>\n", data->usb_dev.bus);
|
|
virBufferAsprintf(&buf, " <device>%d</device>\n",
|
|
data->usb_dev.device);
|
|
virBufferAsprintf(&buf, " <product id='0x%04x'",
|
|
data->usb_dev.product);
|
|
if (data->usb_dev.product_name)
|
|
virBufferEscapeString(&buf, ">%s</product>\n",
|
|
data->usb_dev.product_name);
|
|
else
|
|
virBufferAddLit(&buf, " />\n");
|
|
virBufferAsprintf(&buf, " <vendor id='0x%04x'",
|
|
data->usb_dev.vendor);
|
|
if (data->usb_dev.vendor_name)
|
|
virBufferEscapeString(&buf, ">%s</vendor>\n",
|
|
data->usb_dev.vendor_name);
|
|
else
|
|
virBufferAddLit(&buf, " />\n");
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
|
virBufferAsprintf(&buf, " <number>%d</number>\n",
|
|
data->usb_if.number);
|
|
virBufferAsprintf(&buf, " <class>%d</class>\n",
|
|
data->usb_if._class);
|
|
virBufferAsprintf(&buf, " <subclass>%d</subclass>\n",
|
|
data->usb_if.subclass);
|
|
virBufferAsprintf(&buf, " <protocol>%d</protocol>\n",
|
|
data->usb_if.protocol);
|
|
if (data->usb_if.description)
|
|
virBufferEscapeString(&buf,
|
|
" <description>%s</description>\n",
|
|
data->usb_if.description);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_NET:
|
|
virBufferEscapeString(&buf, " <interface>%s</interface>\n",
|
|
data->net.ifname);
|
|
if (data->net.address)
|
|
virBufferEscapeString(&buf, " <address>%s</address>\n",
|
|
data->net.address);
|
|
if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) {
|
|
const char *subtyp =
|
|
virNodeDevNetCapTypeToString(data->net.subtype);
|
|
virBufferEscapeString(&buf, " <capability type='%s'/>\n",
|
|
subtyp);
|
|
}
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
|
virBufferAsprintf(&buf, " <host>%d</host>\n",
|
|
data->scsi_host.host);
|
|
if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
|
|
virBufferAddLit(&buf, " <capability type='fc_host'>\n");
|
|
virBufferEscapeString(&buf, " <wwnn>%s</wwnn>\n",
|
|
data->scsi_host.wwnn);
|
|
virBufferEscapeString(&buf, " <wwpn>%s</wwpn>\n",
|
|
data->scsi_host.wwpn);
|
|
virBufferEscapeString(&buf, " <fabric_wwn>%s</fabric_wwn>\n",
|
|
data->scsi_host.fabric_wwn);
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
}
|
|
if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) {
|
|
virBufferAddLit(&buf, " <capability type='vport_ops'>\n");
|
|
virBufferAsprintf(&buf, " <max_vports>%d</max_vports>\n",
|
|
data->scsi_host.max_vports);
|
|
virBufferAsprintf(&buf, " <vports>%d</vports>\n",
|
|
data->scsi_host.vports);
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_SCSI_TARGET:
|
|
virBufferEscapeString(&buf, " <target>%s</target>\n",
|
|
data->scsi_target.name);
|
|
break;
|
|
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
|
virBufferAsprintf(&buf, " <host>%d</host>\n", data->scsi.host);
|
|
virBufferAsprintf(&buf, " <bus>%d</bus>\n", data->scsi.bus);
|
|
virBufferAsprintf(&buf, " <target>%d</target>\n",
|
|
data->scsi.target);
|
|
virBufferAsprintf(&buf, " <lun>%d</lun>\n", data->scsi.lun);
|
|
if (data->scsi.type)
|
|
virBufferEscapeString(&buf, " <type>%s</type>\n",
|
|
data->scsi.type);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
|
virBufferEscapeString(&buf, " <block>%s</block>\n",
|
|
data->storage.block);
|
|
if (data->storage.bus)
|
|
virBufferEscapeString(&buf, " <bus>%s</bus>\n",
|
|
data->storage.bus);
|
|
if (data->storage.drive_type)
|
|
virBufferEscapeString(&buf, " <drive_type>%s</drive_type>\n",
|
|
data->storage.drive_type);
|
|
if (data->storage.model)
|
|
virBufferEscapeString(&buf, " <model>%s</model>\n",
|
|
data->storage.model);
|
|
if (data->storage.vendor)
|
|
virBufferEscapeString(&buf, " <vendor>%s</vendor>\n",
|
|
data->storage.vendor);
|
|
if (data->storage.serial)
|
|
virBufferAsprintf(&buf, " <serial>%s</serial>\n",
|
|
data->storage.serial);
|
|
if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE) {
|
|
int avl = data->storage.flags &
|
|
VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
|
|
virBufferAddLit(&buf, " <capability type='removable'>\n");
|
|
virBufferAsprintf(&buf,
|
|
" <media_available>%d"
|
|
"</media_available>\n", avl ? 1 : 0);
|
|
virBufferAsprintf(&buf, " <media_size>%llu</media_size>\n",
|
|
data->storage.removable_media_size);
|
|
if (data->storage.media_label)
|
|
virBufferEscapeString(&buf,
|
|
" <media_label>%s</media_label>\n",
|
|
data->storage.media_label);
|
|
|
|
if (data->storage.logical_block_size > 0)
|
|
virBufferAsprintf(&buf, " <logical_block_size>%llu"
|
|
"</logical_block_size>\n",
|
|
data->storage.logical_block_size);
|
|
if (data->storage.num_blocks > 0)
|
|
virBufferAsprintf(&buf,
|
|
" <num_blocks>%llu</num_blocks>\n",
|
|
data->storage.num_blocks);
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
} else {
|
|
virBufferAsprintf(&buf, " <size>%llu</size>\n",
|
|
data->storage.size);
|
|
if (data->storage.logical_block_size > 0)
|
|
virBufferAsprintf(&buf, " <logical_block_size>%llu"
|
|
"</logical_block_size>\n",
|
|
data->storage.logical_block_size);
|
|
if (data->storage.num_blocks > 0)
|
|
virBufferAsprintf(&buf,
|
|
" <num_blocks>%llu</num_blocks>\n",
|
|
data->storage.num_blocks);
|
|
}
|
|
if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE)
|
|
virBufferAddLit(&buf,
|
|
" <capability type='hotpluggable' />\n");
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_GENERIC:
|
|
virBufferEscapeString(&buf, " <char>%s</char>\n",
|
|
data->sg.path);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_FC_HOST:
|
|
case VIR_NODE_DEV_CAP_VPORTS:
|
|
case VIR_NODE_DEV_CAP_LAST:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
virBufferAddLit(&buf, " </capability>\n");
|
|
}
|
|
|
|
virBufferAddLit(&buf, "</device>\n");
|
|
|
|
if (virBufferError(&buf))
|
|
goto no_memory;
|
|
|
|
return virBufferContentAndReset(&buf);
|
|
|
|
no_memory:
|
|
virReportOOMError();
|
|
virBufferFreeAndReset(&buf);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapsDefParseULong(const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
unsigned *value,
|
|
virNodeDeviceDefPtr def,
|
|
const char *missing_error_fmt,
|
|
const char *invalid_error_fmt)
|
|
{
|
|
int ret;
|
|
unsigned long val;
|
|
|
|
ret = virXPathULong(xpath, ctxt, &val);
|
|
if (ret < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
ret == -1 ? missing_error_fmt : invalid_error_fmt,
|
|
def->name);
|
|
return -1;
|
|
}
|
|
|
|
*value = val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapsDefParseULongLong(const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
unsigned long long *value,
|
|
virNodeDeviceDefPtr def,
|
|
const char *missing_error_fmt,
|
|
const char *invalid_error_fmt)
|
|
{
|
|
int ret;
|
|
unsigned long long val;
|
|
|
|
ret = virXPathULongLong(xpath, ctxt, &val);
|
|
if (ret < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
ret == -1 ? missing_error_fmt : invalid_error_fmt,
|
|
def->name);
|
|
return -1;
|
|
}
|
|
|
|
*value = val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapStorageParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr orignode, *nodes = NULL;
|
|
size_t i;
|
|
int n, ret = -1;
|
|
unsigned long long val;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
data->storage.block = virXPathString("string(./block[1])", ctxt);
|
|
if (!data->storage.block) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("no block device path supplied for '%s'"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
|
|
data->storage.bus = virXPathString("string(./bus[1])", ctxt);
|
|
data->storage.drive_type = virXPathString("string(./drive_type[1])", ctxt);
|
|
data->storage.model = virXPathString("string(./model[1])", ctxt);
|
|
data->storage.vendor = virXPathString("string(./vendor[1])", ctxt);
|
|
data->storage.serial = virXPathString("string(./serial[1])", ctxt);
|
|
|
|
if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) {
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
char *type = virXMLPropString(nodes[i], "type");
|
|
|
|
if (!type) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("missing storage capability type for '%s'"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
|
|
if (STREQ(type, "hotpluggable"))
|
|
data->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE;
|
|
else if (STREQ(type, "removable")) {
|
|
xmlNodePtr orignode2;
|
|
|
|
data->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE;
|
|
|
|
orignode2 = ctxt->node;
|
|
ctxt->node = nodes[i];
|
|
|
|
if (virXPathBoolean("count(./media_available[. = '1']) > 0", ctxt))
|
|
data->storage.flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE;
|
|
|
|
data->storage.media_label = virXPathString("string(./media_label[1])", ctxt);
|
|
|
|
val = 0;
|
|
if (virNodeDevCapsDefParseULongLong("number(./media_size[1])", ctxt, &val, def,
|
|
_("no removable media size supplied for '%s'"),
|
|
_("invalid removable media size supplied for '%s'")) < 0) {
|
|
ctxt->node = orignode2;
|
|
VIR_FREE(type);
|
|
goto out;
|
|
}
|
|
data->storage.removable_media_size = val;
|
|
|
|
ctxt->node = orignode2;
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown storage capability type '%s' for '%s'"),
|
|
type, def->name);
|
|
VIR_FREE(type);
|
|
goto out;
|
|
}
|
|
|
|
VIR_FREE(type);
|
|
}
|
|
|
|
if (!(data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE)) {
|
|
val = 0;
|
|
if (virNodeDevCapsDefParseULongLong("number(./size[1])", ctxt, &val, def,
|
|
_("no size supplied for '%s'"),
|
|
_("invalid size supplied for '%s'")) < 0)
|
|
goto out;
|
|
data->storage.size = val;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
VIR_FREE(nodes);
|
|
ctxt->node = orignode;
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapScsiParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr orignode;
|
|
int ret = -1;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./host[1])", ctxt,
|
|
&data->scsi.host, def,
|
|
_("no SCSI host ID supplied for '%s'"),
|
|
_("invalid SCSI host ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./bus[1])", ctxt,
|
|
&data->scsi.bus, def,
|
|
_("no SCSI bus ID supplied for '%s'"),
|
|
_("invalid SCSI bus ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./target[1])", ctxt,
|
|
&data->scsi.target, def,
|
|
_("no SCSI target ID supplied for '%s'"),
|
|
_("invalid SCSI target ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./lun[1])", ctxt,
|
|
&data->scsi.lun, def,
|
|
_("no SCSI LUN ID supplied for '%s'"),
|
|
_("invalid SCSI LUN ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
data->scsi.type = virXPathString("string(./type[1])", ctxt);
|
|
|
|
ret = 0;
|
|
out:
|
|
ctxt->node = orignode;
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDevCapScsiTargetParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr orignode;
|
|
int ret = -1;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
data->scsi_target.name = virXPathString("string(./name[1])", ctxt);
|
|
if (!data->scsi_target.name) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("no target name supplied for '%s'"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
ctxt->node = orignode;
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDevCapScsiHostParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data,
|
|
int create,
|
|
const char *virt_type)
|
|
{
|
|
xmlNodePtr orignode, *nodes = NULL;
|
|
int ret = -1, n = 0;
|
|
size_t i;
|
|
char *type = NULL;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
if (create == EXISTING_DEVICE &&
|
|
virNodeDevCapsDefParseULong("number(./host[1])", ctxt,
|
|
&data->scsi_host.host, def,
|
|
_("no SCSI host ID supplied for '%s'"),
|
|
_("invalid SCSI host ID supplied for '%s'")) < 0) {
|
|
goto out;
|
|
}
|
|
|
|
if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) {
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
type = virXMLPropString(nodes[i], "type");
|
|
|
|
if (!type) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("missing SCSI host capability type for '%s'"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
|
|
if (STREQ(type, "vport_ops")) {
|
|
|
|
data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS;
|
|
|
|
} else if (STREQ(type, "fc_host")) {
|
|
|
|
xmlNodePtr orignode2;
|
|
|
|
data->scsi_host.flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST;
|
|
|
|
orignode2 = ctxt->node;
|
|
ctxt->node = nodes[i];
|
|
|
|
if (virNodeDevCapsDefParseString("string(./wwnn[1])",
|
|
ctxt,
|
|
&data->scsi_host.wwnn) < 0) {
|
|
if (virRandomGenerateWWN(&data->scsi_host.wwnn, virt_type) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("no WWNN supplied for '%s', and "
|
|
"auto-generation failed"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (virNodeDevCapsDefParseString("string(./wwpn[1])",
|
|
ctxt,
|
|
&data->scsi_host.wwpn) < 0) {
|
|
if (virRandomGenerateWWN(&data->scsi_host.wwpn, virt_type) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("no WWPN supplied for '%s', and "
|
|
"auto-generation failed"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ctxt->node = orignode2;
|
|
|
|
} else {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown SCSI host capability type '%s' for '%s'"),
|
|
type, def->name);
|
|
goto out;
|
|
}
|
|
|
|
VIR_FREE(type);
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
VIR_FREE(type);
|
|
ctxt->node = orignode;
|
|
VIR_FREE(nodes);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDevCapNetParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr orignode;
|
|
int ret = -1;
|
|
char *tmp;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
data->net.ifname = virXPathString("string(./interface[1])", ctxt);
|
|
if (!data->net.ifname) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("no network interface supplied for '%s'"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
|
|
data->net.address = virXPathString("string(./address[1])", ctxt);
|
|
|
|
data->net.subtype = VIR_NODE_DEV_CAP_NET_LAST;
|
|
|
|
tmp = virXPathString("string(./capability/@type)", ctxt);
|
|
if (tmp) {
|
|
int val = virNodeDevNetCapTypeFromString(tmp);
|
|
VIR_FREE(tmp);
|
|
if (val < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("invalid network type supplied for '%s'"),
|
|
def->name);
|
|
goto out;
|
|
}
|
|
data->net.subtype = val;
|
|
}
|
|
|
|
ret = 0;
|
|
out:
|
|
ctxt->node = orignode;
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapUsbInterfaceParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr orignode;
|
|
int ret = -1;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./number[1])", ctxt,
|
|
&data->usb_if.number, def,
|
|
_("no USB interface number supplied for '%s'"),
|
|
_("invalid USB interface number supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./class[1])", ctxt,
|
|
&data->usb_if._class, def,
|
|
_("no USB interface class supplied for '%s'"),
|
|
_("invalid USB interface class supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./subclass[1])", ctxt,
|
|
&data->usb_if.subclass, def,
|
|
_("no USB interface subclass supplied for '%s'"),
|
|
_("invalid USB interface subclass supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./protocol[1])", ctxt,
|
|
&data->usb_if.protocol, def,
|
|
_("no USB interface protocol supplied for '%s'"),
|
|
_("invalid USB interface protocol supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
data->usb_if.description = virXPathString("string(./description[1])", ctxt);
|
|
|
|
ret = 0;
|
|
out:
|
|
ctxt->node = orignode;
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapsDefParseHexId(const char *xpath,
|
|
xmlXPathContextPtr ctxt,
|
|
unsigned *value,
|
|
virNodeDeviceDefPtr def,
|
|
const char *missing_error_fmt,
|
|
const char *invalid_error_fmt)
|
|
{
|
|
int ret;
|
|
unsigned long val;
|
|
|
|
ret = virXPathULongHex(xpath, ctxt, &val);
|
|
if (ret < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
ret == -1 ? missing_error_fmt : invalid_error_fmt,
|
|
def->name);
|
|
return -1;
|
|
}
|
|
|
|
*value = val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapUsbDevParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr orignode;
|
|
int ret = -1;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./bus[1])", ctxt,
|
|
&data->usb_dev.bus, def,
|
|
_("no USB bus number supplied for '%s'"),
|
|
_("invalid USB bus number supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./device[1])", ctxt,
|
|
&data->usb_dev.device, def,
|
|
_("no USB device number supplied for '%s'"),
|
|
_("invalid USB device number supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseHexId("string(./vendor[1]/@id)", ctxt,
|
|
&data->usb_dev.vendor, def,
|
|
_("no USB vendor ID supplied for '%s'"),
|
|
_("invalid USB vendor ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseHexId("string(./product[1]/@id)", ctxt,
|
|
&data->usb_dev.product, def,
|
|
_("no USB product ID supplied for '%s'"),
|
|
_("invalid USB product ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
data->usb_dev.vendor_name = virXPathString("string(./vendor[1])", ctxt);
|
|
data->usb_dev.product_name = virXPathString("string(./product[1])", ctxt);
|
|
|
|
ret = 0;
|
|
out:
|
|
ctxt->node = orignode;
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapPciDevIommuGroupParseXML(xmlXPathContextPtr ctxt,
|
|
xmlNodePtr iommuGroupNode,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr origNode = ctxt->node;
|
|
xmlNodePtr *addrNodes = NULL;
|
|
char *numberStr = NULL;
|
|
int nAddrNodes, ret = -1;
|
|
size_t i;
|
|
virPCIDeviceAddressPtr pciAddr = NULL;
|
|
|
|
ctxt->node = iommuGroupNode;
|
|
|
|
numberStr = virXMLPropString(iommuGroupNode, "number");
|
|
if (!numberStr) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
"%s", _("missing iommuGroup number attribute"));
|
|
goto cleanup;
|
|
}
|
|
if (virStrToLong_ui(numberStr, NULL, 10,
|
|
&data->pci_dev.iommuGroupNumber) < 0) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("invalid iommuGroup number attribute '%s'"),
|
|
numberStr);
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((nAddrNodes = virXPathNodeSet("./address", ctxt, &addrNodes)) < 0)
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < nAddrNodes; i++) {
|
|
virDevicePCIAddress addr = { 0, 0, 0, 0, 0 };
|
|
if (virDevicePCIAddressParseXML(addrNodes[i], &addr) < 0)
|
|
goto cleanup;
|
|
if (VIR_ALLOC(pciAddr) < 0)
|
|
goto cleanup;
|
|
pciAddr->domain = addr.domain;
|
|
pciAddr->bus = addr.bus;
|
|
pciAddr->slot = addr.slot;
|
|
pciAddr->function = addr.function;
|
|
if (VIR_APPEND_ELEMENT(data->pci_dev.iommuGroupDevices,
|
|
data->pci_dev.nIommuGroupDevices,
|
|
pciAddr) < 0)
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
ctxt->node = origNode;
|
|
VIR_FREE(numberStr);
|
|
VIR_FREE(addrNodes);
|
|
VIR_FREE(pciAddr);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
virNodeDevCapPciDevParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr orignode, iommuGroupNode;
|
|
int ret = -1;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./domain[1])", ctxt,
|
|
&data->pci_dev.domain, def,
|
|
_("no PCI domain ID supplied for '%s'"),
|
|
_("invalid PCI domain ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./bus[1])", ctxt,
|
|
&data->pci_dev.bus, def,
|
|
_("no PCI bus ID supplied for '%s'"),
|
|
_("invalid PCI bus ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./slot[1])", ctxt,
|
|
&data->pci_dev.slot, def,
|
|
_("no PCI slot ID supplied for '%s'"),
|
|
_("invalid PCI slot ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseULong("number(./function[1])", ctxt,
|
|
&data->pci_dev.function, def,
|
|
_("no PCI function ID supplied for '%s'"),
|
|
_("invalid PCI function ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseHexId("string(./vendor[1]/@id)", ctxt,
|
|
&data->pci_dev.vendor, def,
|
|
_("no PCI vendor ID supplied for '%s'"),
|
|
_("invalid PCI vendor ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
if (virNodeDevCapsDefParseHexId("string(./product[1]/@id)", ctxt,
|
|
&data->pci_dev.product, def,
|
|
_("no PCI product ID supplied for '%s'"),
|
|
_("invalid PCI product ID supplied for '%s'")) < 0)
|
|
goto out;
|
|
|
|
data->pci_dev.vendor_name = virXPathString("string(./vendor[1])", ctxt);
|
|
data->pci_dev.product_name = virXPathString("string(./product[1])", ctxt);
|
|
|
|
if ((iommuGroupNode = virXPathNode("./iommuGroup[1]", ctxt))) {
|
|
if (virNodeDevCapPciDevIommuGroupParseXML(ctxt, iommuGroupNode,
|
|
data) < 0) {
|
|
goto out;
|
|
}
|
|
}
|
|
ret = 0;
|
|
out:
|
|
ctxt->node = orignode;
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
virNodeDevCapSystemParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
union _virNodeDevCapData *data)
|
|
{
|
|
xmlNodePtr orignode;
|
|
int ret = -1;
|
|
char *tmp;
|
|
|
|
orignode = ctxt->node;
|
|
ctxt->node = node;
|
|
|
|
data->system.product_name = virXPathString("string(./product[1])", ctxt);
|
|
|
|
data->system.hardware.vendor_name = virXPathString("string(./hardware/vendor[1])", ctxt);
|
|
data->system.hardware.version = virXPathString("string(./hardware/version[1])", ctxt);
|
|
data->system.hardware.serial = virXPathString("string(./hardware/serial[1])", ctxt);
|
|
|
|
tmp = virXPathString("string(./hardware/uuid[1])", ctxt);
|
|
if (!tmp) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("no system UUID supplied for '%s'"), def->name);
|
|
goto out;
|
|
}
|
|
|
|
if (virUUIDParse(tmp, data->system.hardware.uuid) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("malformed uuid element for '%s'"), def->name);
|
|
VIR_FREE(tmp);
|
|
goto out;
|
|
}
|
|
VIR_FREE(tmp);
|
|
|
|
data->system.firmware.vendor_name = virXPathString("string(./firmware/vendor[1])", ctxt);
|
|
data->system.firmware.version = virXPathString("string(./firmware/version[1])", ctxt);
|
|
data->system.firmware.release_date = virXPathString("string(./firmware/release_date[1])", ctxt);
|
|
|
|
ret = 0;
|
|
out:
|
|
ctxt->node = orignode;
|
|
return ret;
|
|
}
|
|
|
|
static virNodeDevCapsDefPtr
|
|
virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt,
|
|
virNodeDeviceDefPtr def,
|
|
xmlNodePtr node,
|
|
int create,
|
|
const char *virt_type)
|
|
{
|
|
virNodeDevCapsDefPtr caps;
|
|
char *tmp;
|
|
int val, ret;
|
|
|
|
if (VIR_ALLOC(caps) < 0)
|
|
return NULL;
|
|
|
|
tmp = virXMLPropString(node, "type");
|
|
if (!tmp) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("missing capability type"));
|
|
goto error;
|
|
}
|
|
|
|
if ((val = virNodeDevCapTypeFromString(tmp)) < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown capability type '%s'"), tmp);
|
|
VIR_FREE(tmp);
|
|
goto error;
|
|
}
|
|
caps->type = val;
|
|
VIR_FREE(tmp);
|
|
|
|
switch (caps->type) {
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
ret = virNodeDevCapSystemParseXML(ctxt, def, node, &caps->data);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
|
ret = virNodeDevCapPciDevParseXML(ctxt, def, node, &caps->data);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
|
ret = virNodeDevCapUsbDevParseXML(ctxt, def, node, &caps->data);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
|
ret = virNodeDevCapUsbInterfaceParseXML(ctxt, def, node, &caps->data);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_NET:
|
|
ret = virNodeDevCapNetParseXML(ctxt, def, node, &caps->data);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
|
ret = virNodeDevCapScsiHostParseXML(ctxt, def, node,
|
|
&caps->data,
|
|
create,
|
|
virt_type);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_TARGET:
|
|
ret = virNodeDevCapScsiTargetParseXML(ctxt, def, node, &caps->data);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
|
ret = virNodeDevCapScsiParseXML(ctxt, def, node, &caps->data);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
|
ret = virNodeDevCapStorageParseXML(ctxt, def, node, &caps->data);
|
|
break;
|
|
default:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("unknown capability type '%d' for '%s'"),
|
|
caps->type, def->name);
|
|
ret = -1;
|
|
break;
|
|
}
|
|
|
|
if (ret < 0)
|
|
goto error;
|
|
return caps;
|
|
|
|
error:
|
|
virNodeDevCapsDefFree(caps);
|
|
return NULL;
|
|
}
|
|
|
|
static virNodeDeviceDefPtr
|
|
virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt,
|
|
int create,
|
|
const char *virt_type)
|
|
{
|
|
virNodeDeviceDefPtr def;
|
|
virNodeDevCapsDefPtr *next_cap;
|
|
xmlNodePtr *nodes;
|
|
int n;
|
|
size_t i;
|
|
|
|
if (VIR_ALLOC(def) < 0)
|
|
return NULL;
|
|
|
|
/* Extract device name */
|
|
if (create == EXISTING_DEVICE) {
|
|
def->name = virXPathString("string(./name[1])", ctxt);
|
|
|
|
if (!def->name) {
|
|
virReportError(VIR_ERR_NO_NAME, NULL);
|
|
goto error;
|
|
}
|
|
} else {
|
|
if (VIR_STRDUP(def->name, "new device") < 0)
|
|
goto error;
|
|
}
|
|
|
|
/* Extract device parent, if any */
|
|
def->parent = virXPathString("string(./parent[1])", ctxt);
|
|
|
|
/* Parse device capabilities */
|
|
nodes = NULL;
|
|
if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (n == 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("no device capabilities for '%s'"),
|
|
def->name);
|
|
goto error;
|
|
}
|
|
|
|
next_cap = &def->caps;
|
|
for (i = 0; i < n; i++) {
|
|
*next_cap = virNodeDevCapsDefParseXML(ctxt, def,
|
|
nodes[i],
|
|
create,
|
|
virt_type);
|
|
if (!*next_cap) {
|
|
VIR_FREE(nodes);
|
|
goto error;
|
|
}
|
|
|
|
next_cap = &(*next_cap)->next;
|
|
}
|
|
VIR_FREE(nodes);
|
|
|
|
return def;
|
|
|
|
error:
|
|
virNodeDeviceDefFree(def);
|
|
return NULL;
|
|
}
|
|
|
|
virNodeDeviceDefPtr
|
|
virNodeDeviceDefParseNode(xmlDocPtr xml,
|
|
xmlNodePtr root,
|
|
int create,
|
|
const char *virt_type)
|
|
{
|
|
xmlXPathContextPtr ctxt = NULL;
|
|
virNodeDeviceDefPtr def = NULL;
|
|
|
|
if (!xmlStrEqual(root->name, BAD_CAST "device")) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("unexpected root element <%s> "
|
|
"expecting <device>"),
|
|
root->name);
|
|
return NULL;
|
|
}
|
|
|
|
ctxt = xmlXPathNewContext(xml);
|
|
if (ctxt == NULL) {
|
|
virReportOOMError();
|
|
goto cleanup;
|
|
}
|
|
|
|
ctxt->node = root;
|
|
def = virNodeDeviceDefParseXML(ctxt, create, virt_type);
|
|
|
|
cleanup:
|
|
xmlXPathFreeContext(ctxt);
|
|
return def;
|
|
}
|
|
|
|
static virNodeDeviceDefPtr
|
|
virNodeDeviceDefParse(const char *str,
|
|
const char *filename,
|
|
int create,
|
|
const char *virt_type)
|
|
{
|
|
xmlDocPtr xml;
|
|
virNodeDeviceDefPtr def = NULL;
|
|
|
|
if ((xml = virXMLParse(filename, str, _("(node_device_definition)")))) {
|
|
def = virNodeDeviceDefParseNode(xml, xmlDocGetRootElement(xml),
|
|
create, virt_type);
|
|
xmlFreeDoc(xml);
|
|
}
|
|
|
|
return def;
|
|
}
|
|
|
|
virNodeDeviceDefPtr
|
|
virNodeDeviceDefParseString(const char *str,
|
|
int create,
|
|
const char *virt_type)
|
|
{
|
|
return virNodeDeviceDefParse(str, NULL, create, virt_type);
|
|
}
|
|
|
|
virNodeDeviceDefPtr
|
|
virNodeDeviceDefParseFile(const char *filename,
|
|
int create,
|
|
const char *virt_type)
|
|
{
|
|
return virNodeDeviceDefParse(NULL, filename, create, virt_type);
|
|
}
|
|
|
|
/*
|
|
* Return fc_host dev's WWNN and WWPN
|
|
*/
|
|
int
|
|
virNodeDeviceGetWWNs(virNodeDeviceDefPtr def,
|
|
char **wwnn,
|
|
char **wwpn)
|
|
{
|
|
virNodeDevCapsDefPtr cap = NULL;
|
|
int ret = -1;
|
|
|
|
cap = def->caps;
|
|
while (cap != NULL) {
|
|
if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST &&
|
|
cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) {
|
|
if (VIR_STRDUP(*wwnn, cap->data.scsi_host.wwnn) < 0 ||
|
|
VIR_STRDUP(*wwpn, cap->data.scsi_host.wwpn) < 0) {
|
|
/* Free the other one, if allocated... */
|
|
VIR_FREE(*wwnn);
|
|
goto cleanup;
|
|
}
|
|
break;
|
|
}
|
|
|
|
cap = cap->next;
|
|
}
|
|
|
|
if (cap == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Device is not a fibre channel HBA"));
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = 0;
|
|
cleanup:
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Return the NPIV dev's parent device name
|
|
*/
|
|
int
|
|
virNodeDeviceGetParentHost(const virNodeDeviceObjListPtr devs,
|
|
const char *dev_name,
|
|
const char *parent_name,
|
|
int *parent_host)
|
|
{
|
|
virNodeDeviceObjPtr parent = NULL;
|
|
virNodeDevCapsDefPtr cap = NULL;
|
|
int ret = 0;
|
|
|
|
parent = virNodeDeviceFindByName(devs, parent_name);
|
|
if (parent == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Could not find parent device for '%s'"),
|
|
dev_name);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
cap = parent->def->caps;
|
|
while (cap != NULL) {
|
|
if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST &&
|
|
(cap->data.scsi_host.flags &
|
|
VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS)) {
|
|
*parent_host = cap->data.scsi_host.host;
|
|
break;
|
|
}
|
|
|
|
cap = cap->next;
|
|
}
|
|
|
|
if (cap == NULL) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Parent device %s is not capable "
|
|
"of vport operations"),
|
|
parent->def->name);
|
|
ret = -1;
|
|
}
|
|
|
|
virNodeDeviceObjUnlock(parent);
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
void virNodeDevCapsDefFree(virNodeDevCapsDefPtr caps)
|
|
{
|
|
size_t i = 0;
|
|
union _virNodeDevCapData *data = &caps->data;
|
|
|
|
switch (caps->type) {
|
|
case VIR_NODE_DEV_CAP_SYSTEM:
|
|
VIR_FREE(data->system.product_name);
|
|
VIR_FREE(data->system.hardware.vendor_name);
|
|
VIR_FREE(data->system.hardware.version);
|
|
VIR_FREE(data->system.hardware.serial);
|
|
VIR_FREE(data->system.firmware.vendor_name);
|
|
VIR_FREE(data->system.firmware.version);
|
|
VIR_FREE(data->system.firmware.release_date);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_PCI_DEV:
|
|
VIR_FREE(data->pci_dev.product_name);
|
|
VIR_FREE(data->pci_dev.vendor_name);
|
|
VIR_FREE(data->pci_dev.physical_function);
|
|
for (i = 0; i < data->pci_dev.num_virtual_functions; i++) {
|
|
VIR_FREE(data->pci_dev.virtual_functions[i]);
|
|
}
|
|
VIR_FREE(data->pci_dev.virtual_functions);
|
|
for (i = 0; i < data->pci_dev.nIommuGroupDevices; i++) {
|
|
VIR_FREE(data->pci_dev.iommuGroupDevices[i]);
|
|
}
|
|
VIR_FREE(data->pci_dev.iommuGroupDevices);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_DEV:
|
|
VIR_FREE(data->usb_dev.product_name);
|
|
VIR_FREE(data->usb_dev.vendor_name);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_USB_INTERFACE:
|
|
VIR_FREE(data->usb_if.description);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_NET:
|
|
VIR_FREE(data->net.ifname);
|
|
VIR_FREE(data->net.address);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_HOST:
|
|
VIR_FREE(data->scsi_host.wwnn);
|
|
VIR_FREE(data->scsi_host.wwpn);
|
|
VIR_FREE(data->scsi_host.fabric_wwn);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_TARGET:
|
|
VIR_FREE(data->scsi_target.name);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI:
|
|
VIR_FREE(data->scsi.type);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_STORAGE:
|
|
VIR_FREE(data->storage.block);
|
|
VIR_FREE(data->storage.bus);
|
|
VIR_FREE(data->storage.drive_type);
|
|
VIR_FREE(data->storage.model);
|
|
VIR_FREE(data->storage.vendor);
|
|
VIR_FREE(data->storage.serial);
|
|
VIR_FREE(data->storage.media_label);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_SCSI_GENERIC:
|
|
VIR_FREE(data->sg.path);
|
|
break;
|
|
case VIR_NODE_DEV_CAP_FC_HOST:
|
|
case VIR_NODE_DEV_CAP_VPORTS:
|
|
case VIR_NODE_DEV_CAP_LAST:
|
|
default:
|
|
/* This case is here to shutup the compiler */
|
|
break;
|
|
}
|
|
|
|
VIR_FREE(caps);
|
|
}
|
|
|
|
|
|
void virNodeDeviceObjLock(virNodeDeviceObjPtr obj)
|
|
{
|
|
virMutexLock(&obj->lock);
|
|
}
|
|
|
|
void virNodeDeviceObjUnlock(virNodeDeviceObjPtr obj)
|
|
{
|
|
virMutexUnlock(&obj->lock);
|
|
}
|
|
|
|
static bool
|
|
virNodeDeviceCapMatch(virNodeDeviceObjPtr devobj,
|
|
int type)
|
|
{
|
|
virNodeDevCapsDefPtr cap = NULL;
|
|
|
|
for (cap = devobj->def->caps; cap; cap = cap->next) {
|
|
if (type == cap->type)
|
|
return true;
|
|
|
|
if (cap->type == VIR_NODE_DEV_CAP_SCSI_HOST) {
|
|
if (type == VIR_CONNECT_LIST_NODE_DEVICES_CAP_FC_HOST &&
|
|
(cap->data.scsi_host.flags &
|
|
VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST))
|
|
return true;
|
|
|
|
if (type == VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPORTS &&
|
|
(cap->data.scsi_host.flags &
|
|
VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS))
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#define MATCH(FLAG) (flags & (FLAG))
|
|
static bool
|
|
virNodeDeviceMatch(virNodeDeviceObjPtr devobj,
|
|
unsigned int flags)
|
|
{
|
|
/* filter by cap type */
|
|
if (MATCH(VIR_CONNECT_LIST_NODE_DEVICES_FILTERS_CAP)) {
|
|
if (!((MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SYSTEM) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SYSTEM)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_PCI_DEV)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_DEV) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_USB_DEV)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_INTERFACE) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_USB_INTERFACE)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_NET)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_HOST) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SCSI_HOST)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_TARGET) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SCSI_TARGET)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SCSI)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_STORAGE) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_STORAGE)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_FC_HOST) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_FC_HOST)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_VPORTS) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_VPORTS)) ||
|
|
(MATCH(VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_GENERIC) &&
|
|
virNodeDeviceCapMatch(devobj, VIR_NODE_DEV_CAP_SCSI_GENERIC))))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#undef MATCH
|
|
|
|
int
|
|
virNodeDeviceObjListExport(virConnectPtr conn,
|
|
virNodeDeviceObjList devobjs,
|
|
virNodeDevicePtr **devices,
|
|
virNodeDeviceObjListFilter filter,
|
|
unsigned int flags)
|
|
{
|
|
virNodeDevicePtr *tmp_devices = NULL;
|
|
virNodeDevicePtr device = NULL;
|
|
int ndevices = 0;
|
|
int ret = -1;
|
|
size_t i;
|
|
|
|
if (devices && VIR_ALLOC_N(tmp_devices, devobjs.count + 1) < 0)
|
|
goto cleanup;
|
|
|
|
for (i = 0; i < devobjs.count; i++) {
|
|
virNodeDeviceObjPtr devobj = devobjs.objs[i];
|
|
virNodeDeviceObjLock(devobj);
|
|
if ((!filter || filter(conn, devobj->def)) &&
|
|
virNodeDeviceMatch(devobj, flags)) {
|
|
if (devices) {
|
|
if (!(device = virGetNodeDevice(conn,
|
|
devobj->def->name))) {
|
|
virNodeDeviceObjUnlock(devobj);
|
|
goto cleanup;
|
|
}
|
|
tmp_devices[ndevices] = device;
|
|
}
|
|
ndevices++;
|
|
}
|
|
virNodeDeviceObjUnlock(devobj);
|
|
}
|
|
|
|
if (tmp_devices) {
|
|
/* trim the array to the final size */
|
|
ignore_value(VIR_REALLOC_N(tmp_devices, ndevices + 1));
|
|
*devices = tmp_devices;
|
|
tmp_devices = NULL;
|
|
}
|
|
|
|
ret = ndevices;
|
|
|
|
cleanup:
|
|
if (tmp_devices) {
|
|
for (i = 0; i < ndevices; i++) {
|
|
if (tmp_devices[i])
|
|
virNodeDeviceFree(tmp_devices[i]);
|
|
}
|
|
}
|
|
|
|
VIR_FREE(tmp_devices);
|
|
return ret;
|
|
}
|