[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[Libvir] PATCH: Separate QEMU impl of nodeinfo API



While fixing the QEMU nodeinfo API to correctly deal with case where CPU
sockets have sparse numbering (eg sockets 0 & 3 are populated), I realized
that OpenVZ doesn't have a nodeinfo API, and its requirements are basically
identical to the QEMU driver's. So this patch moves the impl of the nodeinfo
API into a nodeinfo.c file, and makes both the QEMU and OpenVZ driver call
out to this shared impl. I also put #ifdef __linux__ around the impl since
code reading /proc/cpuinfo is never going to work on any non-Linux platform.
For non linux I just return -1 which'll get treated as not-implemented.
If QEMU driver is ported to work on Solaris, the nodeinfo.c file can be
easily extended for their custom impl. Finally I'm adding a testcase with
a bunch of example /proc/cpuinfo files to validate correctness

Regards,
Dan.
-- 
|=- Red Hat, Engineering, Emerging Technologies, Boston.  +1 978 392 2496 -=|
|=-           Perl modules: http://search.cpan.org/~danberr/              -=|
|=-               Projects: http://freshmeat.net/~danielpb/               -=|
|=-  GnuPG: 7D3B9505   F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505  -=| 
? tests/nodeinfodata
? tests/nodeinfotest
Index: tests/Makefile.am
===================================================================
RCS file: /data/cvs/libvirt/tests/Makefile.am,v
retrieving revision 1.21
diff -u -p -u -p -r1.21 Makefile.am
--- tests/Makefile.am	18 Jul 2007 21:34:22 -0000	1.21
+++ tests/Makefile.am	24 Jul 2007 17:36:08 -0000
@@ -32,10 +32,11 @@ LDADDS = \
 EXTRA_DIST = xmlrpcserver.py test_conf.sh
 
 noinst_PROGRAMS = xmlrpctest xml2sexprtest sexpr2xmltest virshtest conftest \
-	reconnect xmconfigtest xencapstest qemuxml2argvtest qemuxml2xmltest
+	reconnect xmconfigtest xencapstest qemuxml2argvtest qemuxml2xmltest \
+        nodeinfotest
 
 TESTS = xml2sexprtest sexpr2xmltest virshtest test_conf.sh xmconfigtest \
-        xencapstest qemuxml2argvtest qemuxml2xmltest
+        xencapstest qemuxml2argvtest qemuxml2xmltest nodeinfotest
 if ENABLE_XEN_TESTS
   TESTS += reconnect
 endif
@@ -99,6 +100,11 @@ xencapstest_SOURCES = \
 xencapstest_LDFLAGS =
 xencapstest_LDADD = $(LDADDS)
 
+nodeinfotest_SOURCES = \
+	nodeinfotest.c testutils.h testutils.c
+nodeinfotest_LDFLAGS =
+nodeinfotest_LDADD = $(LDADDS)
+
 reconnect_SOURCES = \
 	reconnect.c
 reconnect_LDFLAGS =
Index: tests/nodeinfotest.c
===================================================================
RCS file: tests/nodeinfotest.c
diff -N tests/nodeinfotest.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/nodeinfotest.c	24 Jul 2007 17:36:08 -0000
@@ -0,0 +1,117 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "config.h"
+
+#include <string.h>
+
+#include "testutils.h"
+#include "internal.h"
+#include "nodeinfo.h"
+
+static char *progname;
+
+#define MAX_FILE 4096
+
+#ifdef __linux__
+
+extern int linuxNodeInfoCPUPopulate(FILE *cpuinfo, virNodeInfoPtr nodeinfo);
+extern int linuxNodeInfoMemPopulate(FILE *meminfo, virNodeInfoPtr nodeinfo);
+
+static int linuxTestCompareFiles(const char *cpuinfofile, const char *meminfofile, const char *outputfile) {
+    char actualData[MAX_FILE];
+    char expectData[MAX_FILE];
+    char *expect = &expectData[0];
+    virNodeInfo nodeinfo;
+    FILE *cpuinfo, *meminfo;
+
+    if (virtTestLoadFile(outputfile, &expect, MAX_FILE) < 0)
+        return -1;
+
+    cpuinfo = fopen(cpuinfofile, "r");
+    if (!cpuinfo)
+        return -1;
+    if (linuxNodeInfoCPUPopulate(cpuinfo, &nodeinfo) < 0) {
+        fclose(cpuinfo);
+        return -1;
+    }
+    fclose(cpuinfo);
+
+    meminfo = fopen(meminfofile, "r");
+    if (!meminfo)
+        return -1;
+    if (linuxNodeInfoMemPopulate(meminfo, &nodeinfo) < 0) {
+        fclose(meminfo);
+        return -1;
+    }
+    fclose(meminfo);
+
+    snprintf(actualData, MAX_FILE,
+             "CPUs: %u, MHz: %u, Nodes: %u, Sockets: %u, Cores: %u, Threads: %u, Memory: %lu\n",
+             nodeinfo.cpus, nodeinfo.mhz, nodeinfo.nodes, nodeinfo.sockets,
+             nodeinfo.cores, nodeinfo.threads, nodeinfo.memory);
+
+    if (STRNEQ(actualData, expectData)) {
+        if (getenv("DEBUG_TESTS")) {
+            printf("Expect %d '%s'\n", (int)strlen(expectData), expectData);
+            printf("Actual %d '%s'\n", (int)strlen(actualData), actualData);
+        }
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int linuxTestNodeInfo(const void *data) {
+    char cpuinfo[PATH_MAX];
+    char meminfo[PATH_MAX];
+    char output[PATH_MAX];
+    snprintf(cpuinfo, PATH_MAX, "nodeinfodata/linux-%s.cpuinfo", (const char*)data);
+    snprintf(meminfo, PATH_MAX, "nodeinfodata/linux-%s.meminfo", (const char*)data);
+    snprintf(output, PATH_MAX, "nodeinfodata/linux-%s.txt", (const char*)data);
+    return linuxTestCompareFiles(cpuinfo, meminfo, output);
+}
+#endif
+
+
+int
+main(int argc, char **argv)
+{
+    int ret = 0;
+#ifdef __linux__
+    int i;
+    const char *nodeData[] = {
+        "nodeinfo-1",
+        "nodeinfo-2",
+        "nodeinfo-3",
+        "nodeinfo-4",
+        "nodeinfo-5",
+        "nodeinfo-6",
+    };
+
+    progname = argv[0];
+
+    if (argc > 1) {
+        fprintf(stderr, "Usage: %s\n", progname);
+        exit(EXIT_FAILURE);
+    }
+
+    virInitialize();
+
+    for (i = 0 ; i < (sizeof(nodeData)/sizeof(nodeData[0])) ; i++)
+      if (virtTestRun(nodeData[i], 1, linuxTestNodeInfo, nodeData[i]) != 0)
+	ret = -1;
+#endif
+
+    exit(ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
Index: src/Makefile.am
===================================================================
RCS file: /data/cvs/libvirt/src/Makefile.am,v
retrieving revision 1.46
diff -u -p -u -p -r1.46 Makefile.am
--- src/Makefile.am	19 Jul 2007 16:22:40 -0000	1.46
+++ src/Makefile.am	24 Jul 2007 17:40:32 -0000
@@ -50,6 +50,7 @@ CLIENT_SOURCES =						\
 		qemu_conf.c qemu_conf.h					\
 		openvz_conf.c openvz_conf.h				\
 		openvz_driver.c openvz_driver.h 		\
+                nodeinfo.h nodeinfo.c                           \
 		util.c util.h
 
 SERVER_SOURCES = 						\
Index: src/nodeinfo.c
===================================================================
RCS file: src/nodeinfo.c
diff -N src/nodeinfo.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/nodeinfo.c	24 Jul 2007 17:40:32 -0000
@@ -0,0 +1,153 @@
+/*
+ * nodeinfo.c: Helper routines for OS specific node information
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/utsname.h>
+
+#include "internal.h"
+#include "nodeinfo.h"
+
+#ifdef __linux__
+#define MEMINFO_PATH "/proc/meminfo"
+#define CPUINFO_PATH "/proc/cpuinfo"
+
+/* NB, these are not static as we need to call them from testsuite */
+int linuxNodeInfoCPUPopulate(FILE *cpuinfo, virNodeInfoPtr nodeinfo);
+int linuxNodeInfoMemPopulate(FILE *meminfo, virNodeInfoPtr nodeinfo);
+
+int linuxNodeInfoCPUPopulate(FILE *cpuinfo, virNodeInfoPtr nodeinfo) {
+    char line[1024];
+
+    nodeinfo->cpus = 0;
+    nodeinfo->mhz = 0;
+    nodeinfo->nodes = nodeinfo->sockets = nodeinfo->cores = nodeinfo->threads = 1;
+
+    /* NB: It is impossible to fill our nodes, since cpuinfo
+     * has not knowledge of NUMA nodes */
+
+    /* XXX hyperthreads */
+    while (fgets(line, sizeof(line), cpuinfo) != NULL) {
+        if (!strncmp(line, "processor", 9)) { /* aka a single logical CPU */
+            nodeinfo->cpus++;
+        } else if (!strncmp(line, "cpu MHz", 7)) {
+            char *offset = strchr(line, ':');
+            if (!offset)
+                continue;
+            offset++;
+            if (!*offset)
+                continue;
+            nodeinfo->mhz = (unsigned int)strtol(offset, NULL, 10);
+        } else if (!strncmp(line, "cpu cores", 8)) { /* aka cores */
+            unsigned int id;
+            char *offset = strchr(line, ':');
+            if (!offset)
+                continue;
+            offset++;
+            if (!*offset)
+                continue;
+            id = (unsigned int)strtol(offset, NULL, 10);
+            if (id > nodeinfo->cores)
+                nodeinfo->cores = id;
+        }
+    }
+
+    if (!nodeinfo->cpus)
+        return -1;
+
+    /*
+     * Can't reliably count sockets from proc metadata, so
+     * infer it based on total CPUs vs cores.
+     * XXX hyperthreads
+     */
+    nodeinfo->sockets = nodeinfo->cpus / nodeinfo->cores;
+
+    return 0;
+}
+
+
+int linuxNodeInfoMemPopulate(FILE *meminfo, virNodeInfoPtr nodeinfo) {
+    char line[1024];
+
+    nodeinfo->memory = 0;
+
+    while (fgets(line, sizeof(line), meminfo) != NULL) {
+        if (!strncmp(line, "MemTotal:", 9)) {
+            nodeinfo->memory = (unsigned int)strtol(line + 10, NULL, 10);
+        }
+    }
+    if (!nodeinfo->memory)
+        return -1;
+
+    return 0;
+}
+
+
+#endif
+
+int virNodeInfoPopulate(virNodeInfoPtr nodeinfo) {
+    struct utsname info;
+#ifdef __linux__
+    int ret;
+    FILE *cpuinfo, *meminfo;
+#endif
+
+    if (uname(&info) < 0)
+        return -1;
+
+    strncpy(nodeinfo->model, info.machine, sizeof(nodeinfo->model)-1);
+    nodeinfo->model[sizeof(nodeinfo->model)-1] = '\0';
+
+#ifdef __linux__
+    cpuinfo = fopen(CPUINFO_PATH, "r");
+    if (!cpuinfo)
+        return -1;
+    ret = linuxNodeInfoCPUPopulate(cpuinfo, nodeinfo);
+    fclose(cpuinfo);
+    if (ret < 0)
+        return -1;
+
+    meminfo = fopen(MEMINFO_PATH, "r");
+    if (!meminfo)
+        return -1;
+    ret = linuxNodeInfoMemPopulate(meminfo, nodeinfo);
+    fclose(meminfo);
+
+    return ret;
+#else
+    /* XXX Solaris will need an impl later if they port QEMU driver */
+    fprintf(stderr, "%s:%s not implemented on this platform\n", __FILE__, __FUNCTION__);
+    return -1;
+#endif
+}
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
Index: src/nodeinfo.h
===================================================================
RCS file: src/nodeinfo.h
diff -N src/nodeinfo.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ src/nodeinfo.h	24 Jul 2007 17:40:32 -0000
@@ -0,0 +1,37 @@
+/*
+ * nodeinfo.c: Helper routines for OS specific node information
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#ifndef __VIR_NODEINFO_H__
+#define __VIR_NODEINFO_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  int virNodeInfoPopulate(virNodeInfoPtr nodeinfo);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __VIR_NODEINFO_H__*/
Index: src/openvz_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/openvz_driver.c,v
retrieving revision 1.2
diff -u -p -u -p -r1.2 openvz_driver.c
--- src/openvz_driver.c	19 Jul 2007 16:22:40 -0000	1.2
+++ src/openvz_driver.c	24 Jul 2007 17:40:32 -0000
@@ -55,12 +55,14 @@
 #include "util.h"
 #include "openvz_driver.h"
 #include "openvz_conf.h"
+#include "nodeinfo.h"
 
 
 #define openvzLog(level, msg...) fprintf(stderr, msg)
 
 static virDomainPtr openvzDomainLookupByID(virConnectPtr conn, int id);
 static char *openvzGetOSType(virDomainPtr dom);
+static int openvzGetNodeInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo);
 static virDomainPtr openvzDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid);
 static virDomainPtr openvzDomainLookupByName(virConnectPtr conn, const char *name);
 static int openvzDomainGetInfo(virDomainPtr dom, virDomainInfoPtr info);
@@ -327,6 +329,12 @@ static const char *openvzGetType(virConn
 }
 
 
+static int openvzGetNodeInfo(virConnectPtr conn ATTRIBUTE_UNUSED,
+                             virNodeInfoPtr nodeinfo) {
+    return virNodeInfoPopulate(nodeinfo);
+}
+
+
 static int openvzListDomains(virConnectPtr conn, int *ids, int nids) {
     int got = 0;
     int veid, pid, outfd, errfd;
@@ -431,7 +439,7 @@ static virDriver openvzDriver = {
     NULL, /* hostname */
     NULL, /* uri */
     NULL, /* getMaxVcpus */
-    NULL, /* nodeGetInfo */
+    openvzGetNodeInfo, /* nodeGetInfo */
     NULL, /* getCapabilities */
     openvzListDomains, /* listDomains */
     openvzNumDomains, /* numOfDomains */
Index: src/qemu_driver.c
===================================================================
RCS file: /data/cvs/libvirt/src/qemu_driver.c,v
retrieving revision 1.11
diff -u -p -u -p -r1.11 qemu_driver.c
--- src/qemu_driver.c	24 Jul 2007 14:24:52 -0000	1.11
+++ src/qemu_driver.c	24 Jul 2007 17:40:32 -0000
@@ -53,6 +53,7 @@
 #include "util.h"
 #include "qemu_driver.h"
 #include "qemu_conf.h"
+#include "nodeinfo.h"
 
 static int qemudShutdown(void);
 
@@ -1358,81 +1359,6 @@ static int qemudMonitorCommand(struct qe
 }
 
 
-static int qemudGetMemInfo(unsigned long *memory) {
-    FILE *meminfo = fopen("/proc/meminfo", "r");
-    char line[1024];
-
-    *memory = 0;
-
-    if (!meminfo) {
-        return -1;
-    }
-
-    /* XXX NUMA and hyperthreads ? */
-    while (fgets(line, sizeof(line), meminfo) != NULL) {
-        if (!strncmp(line, "MemTotal:", 9)) {
-            *memory = (unsigned int)strtol(line + 10, NULL, 10);
-        }
-    }
-    fclose(meminfo);
-    return 0;
-}
-
-static int qemudGetCPUInfo(unsigned int *cpus, unsigned int *mhz,
-                           unsigned int *nodes, unsigned int *sockets,
-                           unsigned int *cores, unsigned int *threads) {
-    FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
-    char line[1024];
-
-    *cpus = 0;
-    *mhz = 0;
-    *nodes = *sockets = *cores = *threads = 1;
-
-    if (!cpuinfo) {
-        return -1;
-    }
-
-    /* XXX NUMA and hyperthreads ? */
-    while (fgets(line, sizeof(line), cpuinfo) != NULL) {
-        if (!strncmp(line, "processor\t", 10)) { /* aka a single logical CPU */
-            (*cpus)++;
-        } else if (!strncmp(line, "cpu MHz\t", 8)) {
-            char *offset = strchr(line, ':');
-            if (!offset)
-                continue;
-            offset++;
-            if (!*offset)
-                continue;
-            *mhz = (unsigned int)strtol(offset, NULL, 10);
-        } else if (!strncmp(line, "physical id\t", 12)) { /* aka socket */
-            unsigned int id;
-            char *offset = strchr(line, ':');
-            if (!offset)
-                continue;
-            offset++;
-            if (!*offset)
-                continue;
-            id = (unsigned int)strtol(offset, NULL, 10);
-            if ((id+1) > *sockets)
-                *sockets = (id + 1);
-        } else if (!strncmp(line, "cpu cores\t", 9)) { /* aka cores */
-            unsigned int id;
-            char *offset = strchr(line, ':');
-            if (!offset)
-                continue;
-            offset++;
-            if (!*offset)
-                continue;
-            id = (unsigned int)strtol(offset, NULL, 10);
-            if (id > *cores)
-                *cores = id;
-        }
-    }
-    fclose(cpuinfo);
-
-    return 0;
-}
-
 static virDrvOpenStatus qemudOpen(virConnectPtr conn,
                            const char *name,
                            int flags ATTRIBUTE_UNUSED) {
@@ -1486,22 +1412,8 @@ static int qemudGetMaxVCPUs(virConnectPt
 }
 
 static int qemudGetNodeInfo(virConnectPtr conn ATTRIBUTE_UNUSED,
-                     virNodeInfoPtr node) {
-    struct utsname info;
-
-    if (uname(&info) < 0)
-        return -1;
-
-    strncpy(node->model, info.machine, sizeof(node->model)-1);
-    node->model[sizeof(node->model)-1] = '\0';
-
-    if (qemudGetMemInfo(&(node->memory)) < 0)
-        return -1;
-
-    if (qemudGetCPUInfo(&(node->cpus), &(node->mhz), &(node->nodes),
-                        &(node->sockets), &(node->cores), &(node->threads)) < 0)
-        return -1;
-    return 0;
+                            virNodeInfoPtr nodeinfo) {
+    return virNodeInfoPopulate(nodeinfo);
 }
 
 static char *qemudGetCapabilities(virConnectPtr conn ATTRIBUTE_UNUSED) {

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]