Persist qemu capabilities in the domain status file

To cope with the QEMU binary being changed while a VM is running,
it is neccessary to persist the original qemu capabilities at the
time the VM is booted.

* src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h: Add
  an enum for a string rep of every capability
* src/qemu/qemu_domain.c, src/qemu/qemu_domain.h: Support for
  storing capabilities in the domain status XML
* src/qemu/qemu_process.c: Populate & free QEMU capabilities at
  domain startup
This commit is contained in:
Daniel P. Berrange 2011-05-04 12:55:38 +01:00
parent 9b889aacef
commit 43c01d3838
5 changed files with 148 additions and 21 deletions

View File

@ -43,6 +43,84 @@
#define VIR_FROM_THIS VIR_FROM_QEMU
/* While not public, these strings must not change. They
* are used in domain status files which are read on
* daemon restarts
*/
VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
"kqemu", /* 0 */
"vnc-colon",
"no-reboot",
"drive",
"drive-boot",
"name", /* 5 */
"uuid",
"domid",
"vnet-hdr",
"migrate-kvm-stdio",
"migrate-qemu-tcp", /* 10 */
"migrate-qemu-exec",
"drive-cache-v2",
"kvm",
"drive-format",
"vga", /* 15 */
"0.10",
"pci-device",
"mem-path",
"drive-serial",
"xen-domid", /* 20 */
"migrate-qemu-unix",
"chardev",
"enable-kvm",
"monitor-json",
"balloon", /* 25 */
"device",
"sdl",
"smp-topology",
"netdev",
"rtc", /* 30 */
"vnet-host",
"rtc-td-hack",
"no-hpet",
"no-kvm-pit",
"tdf", /* 35 */
"pci-configfd",
"nodefconfig",
"boot-menu",
"enable-kqemu",
"fsdev", /* 40 */
"nesting",
"name-process",
"drive-readonly",
"smbios-type",
"vga-qxl", /* 45 */
"spice",
"vga-none",
"migrate-qemu-fd",
"boot-index",
"hda-duplex", /* 50 */
"drive-aio",
"pci-multibus",
"pci-bootindex",
"ccid-emulated",
"ccid-passthru", /* 55 */
"chardev-spicevmc",
"device-spicevmc",
"virtio-tx-alg",
"device-qxl-vga",
);
struct qemu_feature_flags {
const char *name;
const int default_on;

View File

@ -141,5 +141,6 @@ int qemuCapsParseHelpStr(const char *qemu,
int qemuCapsParseDeviceStr(const char *str,
virBitmapPtr qemuCaps);
VIR_ENUM_DECL(qemuCaps);
#endif /* __QEMU_CAPABILITIES_H__*/

View File

@ -25,6 +25,7 @@
#include "qemu_domain.h"
#include "qemu_command.h"
#include "qemu_capabilities.h"
#include "memory.h"
#include "logging.h"
#include "virterror_internal.h"
@ -113,6 +114,8 @@ static void qemuDomainObjPrivateFree(void *data)
{
qemuDomainObjPrivatePtr priv = data;
qemuCapsFree(priv->qemuCaps);
qemuDomainPCIAddressSetFree(priv->pciaddrs);
virDomainChrSourceDefFree(priv->monConfig);
VIR_FREE(priv->vcpupids);
@ -160,6 +163,18 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data)
virBufferAddLit(buf, " </vcpus>\n");
}
if (priv->qemuCaps) {
int i;
virBufferAddLit(buf, " <qemuCaps>\n");
for (i = 0 ; i < QEMU_CAPS_LAST ; i++) {
if (qemuCapsGet(priv->qemuCaps, i)) {
virBufferVSprintf(buf, " <flag name='%s'/>\n",
qemuCapsTypeToString(i));
}
}
virBufferAddLit(buf, " </qemuCaps>\n");
}
return 0;
}
@ -170,6 +185,7 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data)
char *tmp;
int n, i;
xmlNodePtr *nodes = NULL;
virBitmapPtr qemuCaps = NULL;
if (VIR_ALLOC(priv->monConfig) < 0) {
virReportOOMError();
@ -235,12 +251,41 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data)
VIR_FREE(nodes);
}
if ((n = virXPathNodeSet("./qemuCaps/flag", ctxt, &nodes)) < 0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("failed to parse qemu capabilities flags"));
goto error;
}
if (n > 0) {
if (!(qemuCaps = qemuCapsNew()))
goto error;
for (i = 0 ; i < n ; i++) {
char *str = virXMLPropString(nodes[i], "name");
if (str) {
int flag = qemuCapsTypeFromString(str);
VIR_FREE(str);
if (flag < 0) {
qemuReportError(VIR_ERR_INTERNAL_ERROR,
_("Unknown qemu capabilities flag %s"), str);
goto error;
}
qemuCapsSet(qemuCaps, flag);
}
}
priv->qemuCaps = qemuCaps;
}
VIR_FREE(nodes);
return 0;
error:
virDomainChrSourceDefFree(priv->monConfig);
priv->monConfig = NULL;
VIR_FREE(nodes);
qemuCapsFree(qemuCaps);
return -1;
}

View File

@ -28,6 +28,7 @@
# include "domain_conf.h"
# include "qemu_monitor.h"
# include "qemu_conf.h"
# include "bitmap.h"
/* Only 1 job is allowed at any time
* A job includes *all* monitor commands, even those just querying
@ -77,6 +78,8 @@ struct _qemuDomainObjPrivate {
qemuDomainPCIAddressSetPtr pciaddrs;
int persistentAddrs;
virBitmapPtr qemuCaps;
};
struct qemuDomainWatchdogEvent

View File

@ -1288,8 +1288,7 @@ qemuProcessSetVcpuAffinites(virConnectPtr conn,
static int
qemuProcessInitPasswords(virConnectPtr conn,
struct qemud_driver *driver,
virDomainObjPtr vm,
virBitmapPtr qemuCaps)
virDomainObjPtr vm)
{
int ret = 0;
qemuDomainObjPrivatePtr priv = vm->privateData;
@ -1311,7 +1310,7 @@ qemuProcessInitPasswords(virConnectPtr conn,
if (ret < 0)
goto cleanup;
if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
int i;
for (i = 0 ; i < vm->def->ndisks ; i++) {
@ -1965,7 +1964,6 @@ qemuProcessReconnect(void *payload, const void *name ATTRIBUTE_UNUSED, void *opa
struct qemuProcessReconnectData *data = opaque;
struct qemud_driver *driver = data->driver;
qemuDomainObjPrivatePtr priv;
virBitmapPtr qemuCaps = NULL;
virConnectPtr conn = data->conn;
virDomainObjLock(obj);
@ -1986,13 +1984,16 @@ qemuProcessReconnect(void *payload, const void *name ATTRIBUTE_UNUSED, void *opa
goto error;
}
/* XXX we should be persisting the original flags in the XML
* not re-detecting them, since the binary may have changed
* since launch time */
if (qemuCapsExtractVersionInfo(obj->def->emulator, obj->def->os.arch,
/* If upgrading from old libvirtd we won't have found any
* caps in the domain status, so re-query them
*/
if (!priv->qemuCaps &&
qemuCapsExtractVersionInfo(obj->def->emulator, obj->def->os.arch,
NULL,
&qemuCaps) >= 0 &&
qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
&priv->qemuCaps) < 0)
goto error;
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
priv->persistentAddrs = 1;
if (!(priv->pciaddrs = qemuDomainPCIAddressSetCreate(obj->def)) ||
@ -2012,11 +2013,9 @@ qemuProcessReconnect(void *payload, const void *name ATTRIBUTE_UNUSED, void *opa
if (virDomainObjUnref(obj) > 0)
virDomainObjUnlock(obj);
qemuCapsFree(qemuCaps);
return;
error:
qemuCapsFree(qemuCaps);
if (!virDomainObjIsActive(obj)) {
if (virDomainObjUnref(obj) > 0)
virDomainObjUnlock(obj);
@ -2058,7 +2057,6 @@ int qemuProcessStart(virConnectPtr conn,
enum virVMOperationType vmop)
{
int ret;
virBitmapPtr qemuCaps = NULL;
off_t pos = -1;
char ebuf[1024];
char *pidfile = NULL;
@ -2204,9 +2202,11 @@ int qemuProcessStart(virConnectPtr conn,
goto cleanup;
VIR_DEBUG0("Determining emulator version");
qemuCapsFree(priv->qemuCaps);
priv->qemuCaps = NULL;
if (qemuCapsExtractVersionInfo(vm->def->emulator, vm->def->os.arch,
NULL,
&qemuCaps) < 0)
&priv->qemuCaps) < 0)
goto cleanup;
VIR_DEBUG0("Setting up domain cgroup (if required)");
@ -2223,7 +2223,7 @@ int qemuProcessStart(virConnectPtr conn,
goto cleanup;
#if HAVE_YAJL
if (qemuCapsGet(qemuCaps, QEMU_CAPS_MONITOR_JSON))
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_MONITOR_JSON))
priv->monJSON = 1;
else
#endif
@ -2252,7 +2252,7 @@ int qemuProcessStart(virConnectPtr conn,
* we also need to populate the PCi address set cache for later
* use in hotplug
*/
if (qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
VIR_DEBUG0("Assigning domain PCI addresses");
/* Populate cache with current addresses */
if (priv->pciaddrs) {
@ -2274,7 +2274,7 @@ int qemuProcessStart(virConnectPtr conn,
VIR_DEBUG0("Building emulator command line");
if (!(cmd = qemuBuildCommandLine(conn, driver, vm->def, priv->monConfig,
priv->monJSON != 0, qemuCaps,
priv->monJSON != 0, priv->qemuCaps,
migrateFrom, stdin_fd,
vm->current_snapshot, vmop)))
goto cleanup;
@ -2385,12 +2385,12 @@ int qemuProcessStart(virConnectPtr conn,
goto cleanup;
VIR_DEBUG0("Setting any required VM passwords");
if (qemuProcessInitPasswords(conn, driver, vm, qemuCaps) < 0)
if (qemuProcessInitPasswords(conn, driver, vm) < 0)
goto cleanup;
/* If we have -device, then addresses are assigned explicitly.
* If not, then we have to detect dynamic ones here */
if (!qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
if (!qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) {
VIR_DEBUG0("Determining domain device PCI addresses");
if (qemuProcessInitPCIAddresses(driver, vm) < 0)
goto cleanup;
@ -2421,7 +2421,6 @@ int qemuProcessStart(virConnectPtr conn,
if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
goto cleanup;
qemuCapsFree(qemuCaps);
virCommandFree(cmd);
VIR_FORCE_CLOSE(logfile);
@ -2431,7 +2430,6 @@ cleanup:
/* We jump here if we failed to start the VM for any reason, or
* if we failed to initialize the now running VM. kill it off and
* pretend we never started it */
qemuCapsFree(qemuCaps);
virCommandFree(cmd);
VIR_FORCE_CLOSE(logfile);
qemuProcessStop(driver, vm, 0);
@ -2602,6 +2600,8 @@ retry:
vm->state = VIR_DOMAIN_SHUTOFF;
VIR_FREE(priv->vcpupids);
priv->nvcpupids = 0;
qemuCapsFree(priv->qemuCaps);
priv->qemuCaps = NULL;
/* The "release" hook cleans up additional resources */
if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {