The patch below provides a way to check the kvm version by parsing
the output of qemu-kvm, similar to how the qemu version is parsed.
The qemu-kvm binary help info starts with the line:
"QEMU PC emulator version major.minor.micro (KVM-ver)"
The patch is kind of ugly, with renamed functions and code
movement. The gist of it is:
- Moved the general run-a-command-and-grab-output into its own
function qemudExtractCommandOutput
- Created qemudExtract{Qemu,KVM}VersionInfo which call the above
function and scrape the output
- Use qemudExtractVersion to find the default binaries and call
the *VersionInfo functions
If there is a parsing error, or kvm support isn't found, the new
kvmVersion in the qemud_driver struct is just set to zero. I also
added a call to fill in the qemu and kvm versions on driver init.
The patch also fills in the previously discussed max vcpus piece,
which is dependent on the kvm version.
Any comments appreciated.
Thanks,
Cole
diff --git a/src/qemu_conf.c b/src/qemu_conf.c
index b17d0c8..3d45669 100644
--- a/src/qemu_conf.c
+++ b/src/qemu_conf.c
@@ -428,13 +428,16 @@ virCapsPtr qemudCapsInit(void) {
return NULL;
}
-
-static int qemudExtractVersionInfo(const char *qemu, int *version, int *flags) {
+static int qemudExtractCommandOutput(const char *app, char *buf, size_t size) {
pid_t child;
int newstdout[2];
+ struct stat sb;
- *flags = 0;
- *version = 0;
+ if (stat(app, &sb) < 0) {
+ qemudLog(QEMUD_ERR, _("Cannot find binary %s: %s"), app,
+ strerror(errno));
+ return -1;
+ }
if (pipe(newstdout) < 0) {
return -1;
@@ -458,21 +461,19 @@ static int qemudExtractVersionInfo(const char *qemu, int *version,
int *flags) {
/* Just in case QEMU is translated someday.. */
setenv("LANG", "C", 1);
- execl(qemu, qemu, (char*)NULL);
+ execl(app, app, (char *) NULL);
cleanup1:
_exit(-1); /* Just in case */
} else { /* Parent */
- char help[8192]; /* Ought to be enough to hold QEMU help screen */
- int got = 0, ret = -1;
- int major, minor, micro;
+ int got = 0, ret = -1;
if (close(newstdout[1]) < 0)
goto cleanup2;
- while (got < (sizeof(help)-1)) {
+ while (got < size-1) {
int len;
- if ((len = read(newstdout[0], help+got, sizeof(help)-got-1)) <= 0) {
+ if ((len = read(newstdout[0], buf+got, size-got-1)) <= 0) {
if (!len)
break;
if (errno == EINTR)
@@ -481,30 +482,9 @@ static int qemudExtractVersionInfo(const char *qemu, int *version,
int *flags) {
}
got += len;
}
- help[got] = '\0';
-
- if (sscanf(help, "QEMU PC emulator version %d.%d.%d",
&major,&minor, µ) != 3) {
- goto cleanup2;
- }
-
- *version = (major * 1000 * 1000) + (minor * 1000) + micro;
- if (strstr(help, "-no-kqemu"))
- *flags |= QEMUD_CMD_FLAG_KQEMU;
- if (strstr(help, "-no-reboot"))
- *flags |= QEMUD_CMD_FLAG_NO_REBOOT;
- if (strstr(help, "-name"))
- *flags |= QEMUD_CMD_FLAG_NAME;
- if (strstr(help, "-drive"))
- *flags |= QEMUD_CMD_FLAG_DRIVE;
- if (strstr(help, "boot=on"))
- *flags |= QEMUD_CMD_FLAG_DRIVE_BOOT;
- if (*version >= 9000)
- *flags |= QEMUD_CMD_FLAG_VNC_COLON;
+ buf[got] = '\0';
ret = 0;
- qemudDebug("Version %d %d %d Cooked version: %d, with flags ? %d",
- major, minor, micro, *version, *flags);
-
cleanup2:
if (close(newstdout[0]) < 0)
ret = -1;
@@ -515,8 +495,8 @@ static int qemudExtractVersionInfo(const char *qemu, int *version, int
*flags) {
goto rewait;
}
qemudLog(QEMUD_ERR,
- _("Unexpected exit status from qemu %d pid %lu"),
- got, (unsigned long)child);
+ _("Unexpected exit status from app %s, exit %d: pid
%lu"),
+ app, got, (unsigned long)child);
ret = -1;
}
/* Check & log unexpected exit status, but don't fail,
@@ -524,38 +504,119 @@ static int qemudExtractVersionInfo(const char *qemu, int *version,
int *flags) {
* actually read a valid version number above */
if (WEXITSTATUS(got) != 1) {
qemudLog(QEMUD_WARN,
- _("Unexpected exit status '%d', qemu probably
failed"),
- got);
+ _("Unexpected exit status '%d', app '%s'
probably failed"),
+ got, app);
}
return ret;
}
}
-int qemudExtractVersion(virConnectPtr conn,
- struct qemud_driver *driver) {
+static int qemudExtractQemuVersionInfo(const char *app, int *version,
+ int *flags) {
+
+ char help[8192]; /* Ought to be enough to hold QEMU help screen */
+ int major, minor, micro;
+ int ret = -1;
+
+ *flags = 0;
+ *version = 0;
+
+ if (qemudExtractCommandOutput(app, help, sizeof(help)) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to extract KVM output."));
+ goto error;
+ }
+
+ if (sscanf(help, "QEMU PC emulator version %d.%d.%d", &major,
&minor,
+ µ) != 3) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to parse QEMU version."));
+ goto error;
+ }
+
+ *version = (major * 1000 * 1000) + (minor * 1000) + micro;
+ if (strstr(help, "-no-kqemu"))
+ *flags |= QEMUD_CMD_FLAG_KQEMU;
+ if (strstr(help, "-no-reboot"))
+ *flags |= QEMUD_CMD_FLAG_NO_REBOOT;
+ if (strstr(help, "-name"))
+ *flags |= QEMUD_CMD_FLAG_NAME;
+ if (strstr(help, "-drive"))
+ *flags |= QEMUD_CMD_FLAG_DRIVE;
+ if (strstr(help, "boot=on"))
+ *flags |= QEMUD_CMD_FLAG_DRIVE_BOOT;
+ if (*version >= 9000)
+ *flags |= QEMUD_CMD_FLAG_VNC_COLON;
+
+ qemudDebug("Version %d %d %d Cooked version: %d, with flags ? %d",
+ major, minor, micro, *version, *flags);
+
+ ret = 0;
+ error:
+ return ret;
+}
+
+static int qemudExtractKVMVersionInfo(const char *app, int *version) {
+
+ char buf[8192]; /* Ought to be enough to hold QEMU help screen */
+ char ignore[50] = "\0";
+ int ret = -1;
+
+ *version = 0;
+
+ if (qemudExtractCommandOutput(app, buf, sizeof(buf)) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to extract KVM output."));
+ goto error;
+ }
+
+ // Format = "QEMU PC emulator version major.minor.micro(-cvs) (KVM-ver)"
+ if (sscanf(buf, "QEMU PC emulator version %s (kvm-%d", ignore,
+ version) != 2) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Failed to parse KVM version."));
+ goto error;
+ }
+
+ qemudDebug("KVM version: %d", *version);
+
+ ret = 0;
+ error:
+ return ret;
+}
+
+int qemudExtractVersion(struct qemud_driver *driver) {
const char *binary;
- struct stat sb;
int ignored;
+ struct utsname utsname;
- if (driver->qemuVersion > 0)
- return 0;
+ uname(&utsname);
- if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps,
- "hvm",
- "i686",
- "qemu")) == NULL)
- return -1;
+ if (driver->qemuVersion <= 0) {
- if (stat(binary, &sb) < 0) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("Cannot find QEMU binary %s: %s"), binary,
- strerror(errno));
- return -1;
+ if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps,
+ "hvm",
+ "i686",
+ "qemu")) == NULL)
+ return -1;
+
+ if (qemudExtractQemuVersionInfo(binary, &driver->qemuVersion,
+ &ignored) < 0)
+ return -1;
}
- if (qemudExtractVersionInfo(binary, &driver->qemuVersion, &ignored) <
0) {
- return -1;
+ if (driver->kvmVersion <= 0) {
+ // If kvm isn't supported, don't return error since kvm is optional
+ if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps,
+ "hvm",
+ utsname.machine,
+ "kvm")) == NULL)
+ return 0;
+
+ if (qemudExtractKVMVersionInfo(binary, &driver->kvmVersion) < 0) {
+ return -1;
+ }
}
return 0;
@@ -2324,9 +2385,9 @@ int qemudBuildCommandLine(virConnectPtr conn,
}
if (vm->qemuVersion == 0) {
- if (qemudExtractVersionInfo(vm->def->os.binary,
- &(vm->qemuVersion),
- &(vm->qemuCmdFlags)) < 0)
+ if (qemudExtractQemuVersionInfo(vm->def->os.binary,
+ &(vm->qemuVersion),
+ &(vm->qemuCmdFlags)) < 0)
return -1;
}
diff --git a/src/qemu_conf.h b/src/qemu_conf.h
index 5d07c89..914d418 100644
--- a/src/qemu_conf.h
+++ b/src/qemu_conf.h
@@ -392,6 +392,7 @@ struct qemud_network {
/* Main driver state */
struct qemud_driver {
int qemuVersion;
+ int kvmVersion;
int nactivevms;
int ninactivevms;
struct qemud_vm *vms;
@@ -451,8 +452,7 @@ struct qemud_network *qemudFindNetworkByName(const struct qemud_driver
*driver,
virCapsPtr qemudCapsInit (void);
-int qemudExtractVersion (virConnectPtr conn,
- struct qemud_driver *driver);
+int qemudExtractVersion (struct qemud_driver *driver);
int qemudBuildCommandLine (virConnectPtr conn,
struct qemud_driver *driver,
struct qemud_vm *vm,
diff --git a/src/qemu_driver.c b/src/qemu_driver.c
index 34193bd..4df9d59 100644
--- a/src/qemu_driver.c
+++ b/src/qemu_driver.c
@@ -222,6 +222,12 @@ qemudStartup(void) {
if ((qemu_driver->caps = qemudCapsInit()) == NULL)
goto out_of_memory;
+ // Dependent on capabilities being initialized
+ if (qemudExtractVersion(qemu_driver) < 0) {
+ qemudShutdown();
+ return -1;
+ }
+
if (qemudLoadDriverConfig(qemu_driver, driverConf) < 0) {
qemudShutdown();
return -1;
@@ -1566,16 +1572,24 @@ static const char *qemudGetType(virConnectPtr conn
ATTRIBUTE_UNUSED) {
}
static int qemudGetMaxVCPUs(virConnectPtr conn, const char *type) {
+ struct qemud_driver *driver = (struct qemud_driver *)conn->privateData;
+
if (!type)
return 16;
if (STRCASEEQ(type, "qemu"))
return 16;
- /* XXX future KVM will support SMP. Need to probe
- kernel to figure out KVM module version i guess */
- if (STRCASEEQ(type, "kvm"))
- return 1;
+ if (STRCASEEQ(type, "kvm")) {
+ // KVM-30 added support for up to 4 vcpus
+ // KVM-62 raised this to 16
+ if (driver->kvmVersion < 30)
+ return 1;
+ else if (driver->kvmVersion < 62)
+ return 4;
+ else
+ return 16;
+ }
if (STRCASEEQ(type, "kqemu"))
return 1;
@@ -1690,7 +1704,7 @@ static virDomainPtr qemudDomainLookupByName(virConnectPtr conn,
static int qemudGetVersion(virConnectPtr conn, unsigned long *version) {
struct qemud_driver *driver = (struct qemud_driver *)conn->privateData;
- if (qemudExtractVersion(conn, driver) < 0)
+ if (qemudExtractVersion(driver) < 0)
return -1;
*version = qemu_driver->qemuVersion;