Get mounted filesystems list, which contains hardware info of disks and its
controllers, from QEMU guest agent 2.2+. Then, convert the hardware info
to corresponding device aliases for the disks.
Signed-off-by: Tomoki Sekiyama <tomoki.sekiyama(a)hds.com>
---
src/conf/domain_conf.c | 71 +++++++++++++++++++
src/conf/domain_conf.h | 6 ++
src/libvirt_private.syms | 1
src/qemu/qemu_agent.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_agent.h | 2 +
src/qemu/qemu_driver.c | 48 +++++++++++++
6 files changed, 304 insertions(+)
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f1c2f5f..68eef54 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -11172,6 +11172,60 @@ virDomainHostdevFind(virDomainDefPtr def,
return *found ? i : -1;
}
+static bool
+virDomainDiskControllerMatch(int controller_type, int disk_bus)
+{
+ if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI &&
+ disk_bus == VIR_DOMAIN_DISK_BUS_SCSI)
+ return true;
+
+ if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_FDC &&
+ disk_bus == VIR_DOMAIN_DISK_BUS_FDC)
+ return true;
+
+ if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_IDE &&
+ disk_bus == VIR_DOMAIN_DISK_BUS_IDE)
+ return true;
+
+ if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_SATA &&
+ disk_bus == VIR_DOMAIN_DISK_BUS_SATA)
+ return true;
+
+ return false;
+}
+
+int
+virDomainDiskIndexByAddress(virDomainDefPtr def,
+ virDevicePCIAddressPtr pci_address,
+ unsigned int bus, unsigned int target,
+ unsigned int unit)
+{
+ virDomainDiskDefPtr vdisk;
+ virDomainControllerDefPtr controller = NULL;
+ size_t i;
+ int cidx;
+
+ if ((cidx = virDomainControllerFindByPCIAddress(def, pci_address)) >= 0)
+ controller = def->controllers[cidx];
+
+ for (i = 0; i < def->ndisks; i++) {
+ vdisk = def->disks[i];
+ if (vdisk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
+ virDevicePCIAddressEqual(&vdisk->info.addr.pci, pci_address))
+ return i;
+ if (vdisk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
+ virDomainDeviceDriveAddressPtr drive = &vdisk->info.addr.drive;
+ if (controller &&
+ virDomainDiskControllerMatch(controller->type, vdisk->bus)
&&
+ drive->controller == controller->idx &&
+ drive->bus == bus && drive->target == target &&
+ drive->unit == unit)
+ return i;
+ }
+ }
+ return -1;
+}
+
int
virDomainDiskIndexByName(virDomainDefPtr def, const char *name,
bool allow_ambiguous)
@@ -11461,6 +11515,23 @@ virDomainControllerFind(virDomainDefPtr def,
return -1;
}
+int
+virDomainControllerFindByPCIAddress(virDomainDefPtr def,
+ virDevicePCIAddressPtr addr)
+{
+ size_t i;
+
+ for (i = 0; i < def->ncontrollers; i++) {
+ virDomainDeviceInfoPtr info = &def->controllers[i]->info;
+
+ if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
+ virDevicePCIAddressEqual(&info->addr.pci, addr))
+ return i;
+ }
+
+ return -1;
+}
+
virDomainControllerDefPtr
virDomainControllerRemove(virDomainDefPtr def, size_t i)
{
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 2dab1a4..0a609df 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2476,6 +2476,10 @@ int virDomainEmulatorPinDel(virDomainDefPtr def);
void virDomainRNGDefFree(virDomainRNGDefPtr def);
+int virDomainDiskIndexByAddress(virDomainDefPtr def,
+ virDevicePCIAddressPtr pci_controller,
+ unsigned int bus, unsigned int target,
+ unsigned int unit);
int virDomainDiskIndexByName(virDomainDefPtr def, const char *name,
bool allow_ambiguous);
const char *virDomainDiskPathByName(virDomainDefPtr, const char *name);
@@ -2545,6 +2549,8 @@ int virDomainControllerInsert(virDomainDefPtr def,
void virDomainControllerInsertPreAlloced(virDomainDefPtr def,
virDomainControllerDefPtr controller);
int virDomainControllerFind(virDomainDefPtr def, int type, int idx);
+int virDomainControllerFindByPCIAddress(virDomainDefPtr def,
+ virDevicePCIAddressPtr addr);
virDomainControllerDefPtr virDomainControllerRemove(virDomainDefPtr def, size_t i);
int virDomainLeaseIndex(virDomainDefPtr def,
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index daf4dd7..9a0a9f7 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -234,6 +234,7 @@ virDomainDiskGetDriver;
virDomainDiskGetFormat;
virDomainDiskGetSource;
virDomainDiskGetType;
+virDomainDiskIndexByAddress;
virDomainDiskIndexByName;
virDomainDiskInsert;
virDomainDiskInsertPreAlloced;
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c
index 8df1330..5fcc40f 100644
--- a/src/qemu/qemu_agent.c
+++ b/src/qemu/qemu_agent.c
@@ -1777,3 +1777,179 @@ qemuAgentSetTime(qemuAgentPtr mon,
virJSONValueFree(reply);
return ret;
}
+
+
+int
+qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
+ virDomainDefPtr vmdef)
+{
+ size_t i, j, k;
+ int ret = -1;
+ int ndata = 0, ndisk;
+ char **alias;
+ virJSONValuePtr cmd;
+ virJSONValuePtr reply = NULL;
+ virJSONValuePtr data;
+ virDomainFSInfoPtr *info_ret = NULL;
+ virDevicePCIAddress pci_address;
+
+ cmd = qemuAgentMakeCommand("guest-get-fsinfo", NULL);
+ if (!cmd)
+ return ret;
+
+ if (qemuAgentCommand(mon, cmd, &reply, true,
+ VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+ goto cleanup;
+
+ if (!(data = virJSONValueObjectGet(reply, "return"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest-get-fsinfo reply was missing return data"));
+ goto cleanup;
+ }
+
+ if (data->type != VIR_JSON_TYPE_ARRAY) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest-get-fsinfo return information was not "
+ "an array"));
+ goto cleanup;
+ }
+
+ ndata = virJSONValueArraySize(data);
+ if (!ndata) {
+ ret = 0;
+ goto cleanup;
+ }
+ if (VIR_ALLOC_N(info_ret, ndata) < 0)
+ goto cleanup;
+
+ for (i = 0; i < ndata; i++) {
+ /* Reverse the order to arrange in mount order */
+ virJSONValuePtr entry = virJSONValueArrayGet(data, ndata - 1 - i);
+
+ if (!entry) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("array element '%zd' of '%d' missing in
"
+ "guest-get-fsinfo return data"),
+ i, ndata);
+ goto cleanup;
+ }
+
+ if (VIR_ALLOC(info_ret[i]) < 0)
+ goto cleanup;
+
+ if (VIR_STRDUP(info_ret[i]->mountpoint,
+ virJSONValueObjectGetString(entry, "mountpoint")) <
0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("'mountpoint' missing in reply of "
+ "guest-get-fsinfo"));
+ goto cleanup;
+ }
+
+ if (VIR_STRDUP(info_ret[i]->name,
+ virJSONValueObjectGetString(entry, "name")) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("'name' missing in reply of
guest-get-fsinfo"));
+ goto cleanup;
+ }
+
+ if (VIR_STRDUP(info_ret[i]->fstype,
+ virJSONValueObjectGetString(entry, "type")) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("'type' missing in reply of
guest-get-fsinfo"));
+ goto cleanup;
+ }
+
+ if (!(entry = virJSONValueObjectGet(entry, "disk"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("'disk' missing in reply of
guest-get-fsinfo"));
+ goto cleanup;
+ }
+
+ if (entry->type != VIR_JSON_TYPE_ARRAY) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest-get-fsinfo 'disk' data was not an
array"));
+ goto cleanup;
+ }
+
+ ndisk = virJSONValueArraySize(entry);
+ if (!ndisk)
+ continue;
+ if (VIR_ALLOC_N(info_ret[i]->devAlias, ndisk) < 0)
+ goto cleanup;
+
+ alias = info_ret[i]->devAlias;
+ info_ret[i]->ndevAlias = 0;
+ for (j = 0; j < ndisk; j++) {
+ virJSONValuePtr disk = virJSONValueArrayGet(entry, j);
+ virJSONValuePtr pci;
+ int diskaddr[3], pciaddr[4], idx;
+ const char *diskaddr_comp[] = {"bus", "target",
"unit"};
+ const char *pciaddr_comp[] = {"domain", "bus",
"slot", "function"};
+
+ if (!disk) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("array element '%zd' of '%d'
missing in "
+ "guest-get-fsinfo 'disk' data"),
+ j, ndisk);
+ goto cleanup;
+ }
+
+ if (!(pci = virJSONValueObjectGet(disk, "pci-controller"))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("'pci-controller' missing in
guest-get-fsinfo "
+ "'disk' data"));
+ goto cleanup;
+ }
+
+ for (k = 0; k < 3; k++) {
+ if (virJSONValueObjectGetNumberInt(
+ disk, diskaddr_comp[k], &diskaddr[k]) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("'%s' missing in guest-get-fsinfo
"
+ "'disk' data"),
diskaddr_comp[k]);
+ goto cleanup;
+ }
+ }
+ for (k = 0; k < 4; k++) {
+ if (virJSONValueObjectGetNumberInt(
+ pci, pciaddr_comp[k], &pciaddr[k]) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("'%s' missing in guest-get-fsinfo
"
+ "'pci-address' data"),
pciaddr_comp[k]);
+ goto cleanup;
+ }
+ }
+
+ pci_address.domain = pciaddr[0];
+ pci_address.bus = pciaddr[1];
+ pci_address.slot = pciaddr[2];
+ pci_address.function = pciaddr[3];
+ if ((idx = virDomainDiskIndexByAddress(
+ vmdef, &pci_address,
+ diskaddr[0], diskaddr[1], diskaddr[2])) < 0)
+ continue;
+
+ if (VIR_STRDUP(*alias, vmdef->disks[idx]->dst) < 0)
+ goto cleanup;
+
+ if (*alias) {
+ alias++;
+ info_ret[i]->ndevAlias++;
+ }
+ }
+ }
+
+ *info = info_ret;
+ info_ret = NULL;
+ ret = ndata;
+
+ cleanup:
+ if (info_ret) {
+ for (i = 0; i < ndata; i++)
+ virDomainFSInfoFree(info_ret[i]);
+ VIR_FREE(info_ret);
+ }
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h
index 6cd6b49..c983828 100644
--- a/src/qemu/qemu_agent.h
+++ b/src/qemu/qemu_agent.h
@@ -73,6 +73,8 @@ int qemuAgentShutdown(qemuAgentPtr mon,
int qemuAgentFSFreeze(qemuAgentPtr mon,
const char **mountpoints, unsigned int nmountpoints);
int qemuAgentFSThaw(qemuAgentPtr mon);
+int qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
+ virDomainDefPtr vmdef);
int qemuAgentSuspend(qemuAgentPtr mon,
unsigned int target);
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 5936d6f..145b426 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -18826,6 +18826,53 @@ qemuNodeAllocPages(virConnectPtr conn,
}
+static int
+qemuDomainGetFSInfo(virDomainPtr dom,
+ virDomainFSInfoPtr **info,
+ unsigned int flags)
+{
+ virQEMUDriverPtr driver = dom->conn->privateData;
+ qemuDomainObjPrivatePtr priv;
+ virDomainObjPtr vm;
+ int ret = -1;
+
+ virCheckFlags(0, ret);
+
+ if (!(vm = qemuDomObjFromDomain(dom)))
+ return ret;
+
+ if (virDomainGetFSInfoEnsureACL(dom->conn, vm->def) < 0)
+ goto cleanup;
+
+ priv = vm->privateData;
+
+ if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto endjob;
+ }
+
+ if (!qemuDomainAgentAvailable(priv, true))
+ goto endjob;
+
+ qemuDomainObjEnterAgent(vm);
+ ret = qemuAgentGetFSInfo(priv->agent, info, vm->def);
+ qemuDomainObjExitAgent(vm);
+
+ endjob:
+ if (!qemuDomainObjEndJob(driver, vm))
+ vm = NULL;
+
+ cleanup:
+ if (vm)
+ virObjectUnlock(vm);
+ return ret;
+}
+
+
static virHypervisorDriver qemuDriver = {
.no = VIR_DRV_QEMU,
.name = QEMU_DRIVER_NAME,
@@ -19026,6 +19073,7 @@ static virHypervisorDriver qemuDriver = {
.connectGetDomainCapabilities = qemuConnectGetDomainCapabilities, /* 1.2.7 */
.connectGetAllDomainStats = qemuConnectGetAllDomainStats, /* 1.2.8 */
.nodeAllocPages = qemuNodeAllocPages, /* 1.2.9 */
+ .domainGetFSInfo = qemuDomainGetFSInfo, /* 1.2.11 */
};