[Libvir] [RFC] 0/3 Add Linux Container support to libvirt
by Dave Leskovec
The following set of patches add the first batch of linux container
support to libvirt. The work is not complete but I wanted to start
getting some of this out for comments. This set of patches supports the
following:
* new switch --with-lxc to enable linux container support (off by default)
* virConnectOpen
* virConnectClose
* virConnectListDomains (of course none are listed since we can't start
the containers yet)
* virConnectNumOfDomains
* virConnectListDefinedDomains
* virConnectNumOfDefinedDomains
* virDomainLookupByUUID
* virDomainLookupByName
* virDomainGetInfo
* virDomainGetXMLDesc
* virDomainDefineXML
* virDomainUndefine
A sample XML format that can be used to define a linux container domain:
<domain type='linuxcontainer'>
<name>TestContainer3</name>
<container>
<filesystem type='mount'>
<source dir='/home/user/lxc_files/etc/'/>
<target dir='/etc/'/>
</filesystem>
<filesystem type='mount'>
<source dir='/home/user/lxc_files/var/'/>
<target dir='/var/'/>
</filesystem>
<init>/usr/sbin/container_init</init>
</container>
<memory>65536</memory>
<devices>
<console tty='/dev/pts/4'/>
</devices>
</domain>
I've left out the network definition as I'll be tackling that a little
later on...
I'm currently working on starting a container.
Any and all questions and comments are welcome.
Thanks!
--
Best Regards,
Dave Leskovec
IBM Linux Technology Center
Open Virtualization
16 years, 9 months
[Libvir] [PATCH] Rewrite openvzSetUUID.
by Jim Meyering
There were several unchecked syscalls in this function, along with the
at-least-theoretical risk of a file descriptor leak, so I rewrote this
function to avoid all that, using a stream rather than a bare file
descriptor.
Subject: [PATCH] Rewrite openvzSetUUID.
* src/openvz_conf.c (openvzSetUUID): Rewrite to avoid unchecked
lseek, write, and close as well as a potential file descriptor leak.
Regarding style, the if (expr1 + expr2 + expr3) below might look
odd, but it's better than "if (expr1 || expr2 || expr3)", which
loses if expr1 or expr2 is true, since it short-circuits and
skips expr3, which is the required fclose call.
I could have used "|", but that seemed worse,
and I didn't like the duplication in
if (expr1)
ret = -1;
if (expr2)
ret = -1;
if (expr3)
ret = -1;
In an early iteration, I even wrote this (which still
has too much duplication but isn't that bad):
ret = 0;
ret |= e1;
ret |= e2;
ret |= e3;
Anyway, just trying to avoid a drawn-out discussion on style....
Signed-off-by: Jim Meyering <meyering(a)redhat.com>
---
src/openvz_conf.c | 27 ++++++++++++++-------------
1 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/src/openvz_conf.c b/src/openvz_conf.c
index a274223..1f45c24 100644
--- a/src/openvz_conf.c
+++ b/src/openvz_conf.c
@@ -680,7 +680,7 @@ openvzSetUUID(int vpsid)
char uuidstr[VIR_UUID_STRING_BUFLEN];
unsigned char uuid[VIR_UUID_BUFLEN];
char *conf_dir;
- int fd, ret;
+ int ret;
conf_dir = openvzLocateConfDir();
if (conf_dir == NULL)
@@ -688,26 +688,27 @@ openvzSetUUID(int vpsid)
sprintf(conf_file, "%s/%d.conf", conf_dir, vpsid);
free(conf_dir);
- fd = open(conf_file, O_RDWR);
- if(fd == -1)
- return -1;
-
ret = openvzGetVPSUUID(vpsid, uuidstr);
- if(ret == -1)
+ if (ret)
return -1;
- if(uuidstr[0] == 0) {
+ if (uuidstr[0] == 0) {
+ FILE *fp = fopen(conf_file, "a");
+ if (fp == NULL)
+ return -1;
+
virUUIDGenerate(uuid);
virUUIDFormat(uuid, uuidstr);
- lseek(fd, 0, SEEK_END);
- write(fd, "\n#UUID: ", 8);
- write(fd, uuidstr, strlen(uuidstr));
- write(fd, "\n", 1);
- close(fd);
+ /* Record failure if any of these fails,
+ and be careful always to close the stream. */
+ if ((fseek(fp, 0, SEEK_END) < 0)
+ + (fprintf(fp, "\n#UUID: %s\n", uuidstr) < 0);
+ + (fclose(fp) == EOF))
+ ret = -1;
}
- return 0;
+ return ret;
}
/*
--
1.5.4.2.134.g82883
16 years, 9 months
[Libvir] [RFC] 1/3 Base linux container support
by Dave Leskovec
This patch contains the base linux container support
* new switch --with-lxc to enable support (off by default)
* Add new source files to Makefile.am
* Add define for lxc in driver.h
* Add call to lxcRegister() in libvirt.c
* Add define for errors from lxc in virterror.h
* Add case for errors from lxc in virterror.c
Index: configure.in
===================================================================
RCS file: /data/cvs/libvirt/configure.in,v
retrieving revision 1.124
diff -u -p -r1.124 configure.in
--- configure.in 23 Jan 2008 19:37:10 -0000 1.124
+++ configure.in 19 Feb 2008 18:50:55 -0000
@@ -131,6 +131,8 @@ AC_ARG_WITH(qemu,
[ --with-qemu add QEMU/KVM support (on)],[],[with_qemu=yes])
AC_ARG_WITH(openvz,
[ --with-openvz add OpenVZ support (off)],[],[with_openvz=no])
+AC_ARG_WITH(lxc,
+[ --with-lxc add Linux Container support
(off)],[],[with_lxc=no])
AC_ARG_WITH(test,
[ --with-test add test driver support
(on)],[],[with_test=yes])
AC_ARG_WITH(remote,
@@ -228,6 +230,9 @@ WITH_XEN=0
if test "$with_openvz" = "yes" ; then
LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_OPENVZ"
fi
+if test "$with_lxc" = "yes" ; then
+ LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_LXC"
+fi
if test "$with_qemu" = "yes" ; then
LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_QEMU"
fi
@@ -720,6 +725,7 @@ AC_MSG_NOTICE([ Xen: $with_xen])
AC_MSG_NOTICE([ Proxy: $with_xen_proxy])
AC_MSG_NOTICE([ QEMU: $with_qemu])
AC_MSG_NOTICE([ OpenVZ: $with_openvz])
+AC_MSG_NOTICE([ LXC: $with_lxc])
AC_MSG_NOTICE([ Test: $with_test])
AC_MSG_NOTICE([ Remote: $with_remote])
AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
Index: include/libvirt/virterror.h
===================================================================
RCS file: /data/cvs/libvirt/include/libvirt/virterror.h,v
retrieving revision 1.31
diff -u -p -r1.31 virterror.h
--- include/libvirt/virterror.h 5 Dec 2007 15:24:15 -0000 1.31
+++ include/libvirt/virterror.h 19 Feb 2008 18:50:56 -0000
@@ -54,6 +54,7 @@ typedef enum {
VIR_FROM_OPENVZ, /* Error from OpenVZ driver */
VIR_FROM_XENXM, /* Error at Xen XM layer */
VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */
+ VIR_FROM_LXC, /* Error from Linux Container driver */
} virErrorDomain;
Index: src/Makefile.am
===================================================================
RCS file: /data/cvs/libvirt/src/Makefile.am,v
retrieving revision 1.62
diff -u -p -r1.62 Makefile.am
--- src/Makefile.am 5 Jan 2008 16:06:36 -0000 1.62
+++ src/Makefile.am 19 Feb 2008 18:50:56 -0000
@@ -57,6 +57,8 @@ CLIENT_SOURCES = \
qemu_conf.c qemu_conf.h \
openvz_conf.c openvz_conf.h \
openvz_driver.c openvz_driver.h \
+ lxc_driver.c lxc_driver.h \
+ lxc_conf.c lxc_conf.h \
nodeinfo.h nodeinfo.c \
util.c util.h
Index: src/driver.h
===================================================================
RCS file: /data/cvs/libvirt/src/driver.h,v
retrieving revision 1.41
diff -u -p -r1.41 driver.h
--- src/driver.h 5 Feb 2008 19:27:37 -0000 1.41
+++ src/driver.h 19 Feb 2008 18:50:56 -0000
@@ -24,6 +24,7 @@ typedef enum {
VIR_DRV_QEMU = 3,
VIR_DRV_REMOTE = 4,
VIR_DRV_OPENVZ = 5,
+ VIR_DRV_LXC = 6
} virDrvNo;
Index: src/libvirt.c
===================================================================
RCS file: /data/cvs/libvirt/src/libvirt.c,v
retrieving revision 1.120
diff -u -p -r1.120 libvirt.c
--- src/libvirt.c 5 Feb 2008 19:27:37 -0000 1.120
+++ src/libvirt.c 19 Feb 2008 18:50:57 -0000
@@ -40,6 +40,9 @@
#ifdef WITH_OPENVZ
#include "openvz_driver.h"
#endif
+#ifdef WITH_LXC
+#include "lxc_driver.h"
+#endif
/*
* TODO:
@@ -213,6 +216,9 @@ virInitialize(void)
#ifdef WITH_OPENVZ
if (openvzRegister() == -1) return -1;
#endif
+#ifdef WITH_LXC
+ if (lxcRegister() == -1) return -1;
+#endif
#ifdef WITH_REMOTE
if (remoteRegister () == -1) return -1;
#endif
Index: src/virterror.c
===================================================================
RCS file: /data/cvs/libvirt/src/virterror.c,v
retrieving revision 1.36
diff -u -p -r1.36 virterror.c
--- src/virterror.c 5 Feb 2008 19:27:37 -0000 1.36
+++ src/virterror.c 19 Feb 2008 18:50:57 -0000
@@ -296,6 +296,9 @@ virDefaultErrorFunc(virErrorPtr err)
case VIR_FROM_STATS_LINUX:
dom = "Linux Stats ";
break;
+ case VIR_FROM_LXC:
+ dom = "LXC ";
+ break;
}
if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
--
Best Regards,
Dave Leskovec
IBM Linux Technology Center
Open Virtualization
16 years, 9 months
Fwd: [Libvir] [PATCH] Useless statement in openvzGetVPSInfo
by Anton Protopopov
Oops, forgot the list...
---------- Forwarded message ----------
From: Anton Protopopov <aspsk2(a)gmail.com>
Date: 21.02.2008 21:25
Subject: Re: [Libvir] [PATCH] Useless statement in openvzGetVPSInfo
To: veillard(a)redhat.com
I think, that if
pnext = &vm;
and
*pnext = calloc(...);
then
vm = calloc(...);
Isn't it?
2008/2/21, Daniel Veillard <veillard(a)redhat.com>:
>
> On Thu, Feb 21, 2008 at 08:59:01PM +0300, Anton Protopopov wrote:
> >
> > Hi,
> > the patch is now in attachement..
> > I think, that the statement
> > if (!vm)
> > vm = *pnext;
> > will never be executed: in the first pass, we have already check
> this
> > a few lines before; in next passes, vm remains the same...
> > Anton
>
>
> Hum, that doesn't match what I see. vm is initialized to NULL on entry
> and no changed before the loop, so on first iteration of the loop vm is
> set using that code.
> I don't understand the logic of your patch,
>
> Daniel
>
>
> --
> Red Hat Virtualization group http://redhat.com/virtualization/
> Daniel Veillard | virtualization library http://libvirt.org/
> veillard(a)redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/
> http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/
>
16 years, 9 months
[Libvir] [PATCH] Handle failed openvzLocateConfDir.
by Jim Meyering
While looking at misuses of write, I found problems in src/openvz_conf.c
Here's the first fix:
Handle failed openvzLocateConfDir.
* src/openvz_conf.c (openvzLocateConfDir, openvzGetVPSUUID):
(openvzSetUUID): Don't dereference NULL upon failure.
Signed-off-by: Jim Meyering <meyering(a)redhat.com>
---
src/openvz_conf.c | 6 ++++++
1 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/src/openvz_conf.c b/src/openvz_conf.c
index 482de82..fb38b43 100644
--- a/src/openvz_conf.c
+++ b/src/openvz_conf.c
@@ -641,6 +641,8 @@ openvzGetVPSUUID(int vpsid, char *uuidstr)
int fd, ret;
conf_dir = openvzLocateConfDir();
+ if (conf_dir != NULL)
+ return -1
sprintf(conf_file, "%s/%d.conf", conf_dir, vpsid);
free(conf_dir);
@@ -681,6 +683,8 @@ openvzSetUUID(int vpsid)
int fd, ret;
conf_dir = openvzLocateConfDir();
+ if (conf_dir != NULL)
+ return -1;
sprintf(conf_file, "%s/%d.conf", conf_dir, vpsid);
free(conf_dir);
@@ -722,6 +726,8 @@ int openvzAssignUUIDs(void)
char ext[8];
conf_dir = openvzLocateConfDir();
+ if (conf_dir != NULL)
+ return -1;
dp = opendir(conf_dir);
if(dp == NULL) {
--
1.5.4.2.134.g82883
16 years, 9 months
[Libvir] [PATCH] Useless statement in openvzGetVPSInfo
by Anton Protopopov
Hi,
the patch is now in attachement..
I think, that the statement
if (!vm)
vm = *pnext;
will never be executed: in the first pass, we have already check this a few
lines before; in next passes, vm remains the same...
Anton
16 years, 9 months
[libvir] [PATCH] Double free in openvzGetVPSInfo
by Anton Protopopov
Hi,
without this openvzGetVPSInfo() will cause "double free" when parse bad (i.e.,
really bad or empty) output from vzlist.
diff --git a/src/openvz_conf.c b/src/openvz_conf.c
index 482de82..0bc7084 100644
--- a/src/openvz_conf.c
+++ b/src/openvz_conf.c
@@ -536,7 +536,6 @@ openvzGetVPSInfo(virConnectPtr conn) {
if (fscanf(fp, "%d %s\n", &veid, status) != 2) {
error(conn, VIR_ERR_INTERNAL_ERROR,
"Failed to parse vzlist output");
- free(*pnext);
goto error;
}
if(strcmp(status, "stopped")) {
@@ -557,7 +556,6 @@ openvzGetVPSInfo(virConnectPtr conn) {
vmdef = calloc(1, sizeof(*vmdef));
if(!vmdef) {
error(conn, VIR_ERR_INTERNAL_ERROR, "calloc failed");
- free(*pnext);
goto error;
}
@@ -568,7 +566,6 @@ openvzGetVPSInfo(virConnectPtr conn) {
if(ret == -1) {
error(conn, VIR_ERR_INTERNAL_ERROR,
"UUID in config file malformed");
- free(*pnext);
free(vmdef);
goto error;
}
16 years, 9 months
[Libvir] [RFC] 3/3 Driver functions for linux container support
by Dave Leskovec
This patch contains the new files lxc_driver.c and lxc_driver.h
Index: src/lxc_driver.c
===================================================================
RCS file: src/lxc_driver.c
diff -N src/lxc_driver.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/lxc_driver.c 19 Feb 2008 18:54:30 -0000
@@ -0,0 +1,472 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_driver.c: linux container driver functions
+ *
+ * Authors:
+ * David L. Leskovec <dlesko at linux.vnet.ibm.com>
+ *
+ * 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
+ */
+
+#include <config.h>
+
+#ifdef WITH_LXC
+
+#include <sched.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <wait.h>
+
+#include "lxc_conf.h"
+#include "lxc_driver.h"
+#include "driver.h"
+#include "internal.h"
+
+/* debug macros */
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
+static int lxcStartup(virConnectPtr conn);
+static int lxcShutdown(virConnectPtr conn);
+
+/* Functions */
+static int lxcDummyChild( void *argv ATTRIBUTE_UNUSED )
+{
+ exit(0);
+}
+
+static int lxcCheckContainerSupport( void )
+{
+ int rc = 0;
+ int flags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWUSER|
+ CLONE_NEWIPC|SIGCHLD;
+ int cpid;
+ char *childStack;
+ char *stack;
+ int childStatus;
+
+ stack = malloc(getpagesize() * 4);
+ if(!stack) {
+ DEBUG0("Unable to allocate stack");
+ rc = -1;
+ goto check_complete;
+ }
+
+ childStack = stack + (getpagesize() * 4);
+
+ cpid = clone(lxcDummyChild, childStack, flags, NULL);
+ if ((0 > cpid) && (EINVAL == errno)) {
+ DEBUG0("clone call returned EINVAL, container support is not
enabled");
+ rc = -1;
+ } else {
+ waitpid(cpid, &childStatus, 0);
+ }
+
+ free(stack);
+
+check_complete:
+ return rc;
+}
+
+static virDrvOpenStatus lxcOpen(virConnectPtr conn,
+ xmlURIPtr uri,
+ virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+ int flags ATTRIBUTE_UNUSED)
+{
+ uid_t uid = getuid();
+
+ /* Check that the user is root */
+ if (0 != uid) {
+ goto declineConnection;
+ }
+
+ /* Verify uri was specified */
+ if ((NULL == uri) || (NULL == uri->scheme)) {
+ goto declineConnection;
+ }
+
+ /* Check that the uri scheme is lxc */
+ if (STRNEQ(uri->scheme, "lxc")) {
+ goto declineConnection;
+ }
+
+ /* Check that this is a container enabled kernel */
+ if(0 != lxcCheckContainerSupport()) {
+ goto declineConnection;
+ }
+
+ /* initialize driver data */
+ if (0 > lxcStartup(conn)) {
+ goto declineConnection;
+ }
+
+ return VIR_DRV_OPEN_SUCCESS;
+
+declineConnection:
+ return VIR_DRV_OPEN_DECLINED;
+}
+
+static int lxcClose(virConnectPtr conn)
+{
+ return lxcShutdown(conn);
+}
+
+static virDomainPtr lxcDomainLookupByID(virConnectPtr conn,
+ int id)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ lxc_vm_t *vm = lxcFindVMByID(driver, id);
+ virDomainPtr dom;
+
+ if (!vm) {
+ lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) {
+ dom->id = vm->id;
+ }
+
+ return dom;
+}
+
+static virDomainPtr lxcDomainLookupByUUID(virConnectPtr conn,
+ const unsigned char *uuid)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ lxc_vm_t *vm = lxcFindVMByUUID(driver, uuid);
+ virDomainPtr dom;
+
+ if (!vm) {
+ lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) {
+ dom->id = vm->id;
+ }
+
+ return dom;
+}
+
+static virDomainPtr lxcDomainLookupByName(virConnectPtr conn,
+ const char *name)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ lxc_vm_t *vm = lxcFindVMByName(driver, name);
+ virDomainPtr dom;
+
+ if (!vm) {
+ lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) {
+ dom->id = vm->id;
+ }
+
+ return dom;
+}
+
+static int lxcListDomains(virConnectPtr conn, int *ids, int nids)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ lxc_vm_t *vm;
+ int numDoms = 0;
+
+ for (vm = driver->vms; vm && (numDoms < nids); vm = vm->next) {
+ if (lxcIsActiveVM(vm)) {
+ ids[numDoms] = vm->id;
+ numDoms++;
+ }
+ }
+
+ return numDoms;
+}
+
+static int lxcNumDomains(virConnectPtr conn)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ return driver->nactivevms;
+}
+
+static int lxcListDefinedDomains(virConnectPtr conn,
+ char **const names, int nnames)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ lxc_vm_t *vm;
+ int numDoms = 0;
+ int i;
+
+ for (vm = driver->vms; vm && (numDoms < nnames); numDoms++) {
+ if (!lxcIsActiveVM(vm)) {
+ if (!(names[numDoms] = strdup(vm->def->name))) {
+ lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "names");
+ goto cleanup;
+ }
+ }
+
+ vm = vm->next;
+ }
+
+ return numDoms;
+
+ cleanup:
+ for (i = 0 ; i < numDoms ; i++) {
+ free(names[i]);
+ }
+
+ return -1;
+}
+
+
+static int lxcNumDefinedDomains(virConnectPtr conn)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ return driver->ninactivevms;
+}
+
+static virDomainPtr lxcDomainDefine(virConnectPtr conn, const char *xml)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ lxc_vm_def_t *def;
+ lxc_vm_t *vm;
+ virDomainPtr dom;
+
+ if (!(def = lxcParseVMDef(conn, xml, NULL))) {
+ return NULL;
+ }
+
+ if (!(vm = lxcAssignVMDef(conn, driver, def))) {
+ lxcFreeVMDef(def);
+ return NULL;
+ }
+
+ if (lxcSaveVMDef(conn, driver, vm, def) < 0) {
+ lxcRemoveInactiveVM(driver, vm);
+ return NULL;
+ }
+
+ dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+ if (dom) {
+ dom->id = vm->id;
+ }
+
+ return dom;
+}
+
+static int lxcDomainUndefine(virDomainPtr dom)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
+ lxc_vm_t *vm = lxcFindVMByUUID(driver, dom->uuid);
+
+ if (!vm) {
+ lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
+ "no domain with matching uuid");
+ return -1;
+ }
+
+ if (lxcIsActiveVM(vm)) {
+ lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
+ "cannot delete active domain");
+ return -1;
+ }
+
+ if (lxcDeleteConfig(dom->conn, driver, vm->configFile,
vm->def->name) < 0) {
+ return -1;
+ }
+
+ vm->configFile[0] = '\0';
+
+ lxcRemoveInactiveVM(driver, vm);
+
+ return 0;
+}
+
+static int lxcDomainGetInfo(virDomainPtr dom,
+ virDomainInfoPtr info)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
+ lxc_vm_t *vm = lxcFindVMByUUID(driver, dom->uuid);
+
+ if (!vm) {
+ lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
+ "no domain with matching uuid");
+ return -1;
+ }
+
+ info->state = vm->state;
+
+ if (!lxcIsActiveVM(vm)) {
+ info->cpuTime = 0;
+ } else {
+ info->cpuTime = 0;
+ }
+
+ info->maxMem = vm->def->maxMemory;
+ info->memory = vm->def->maxMemory;
+ info->nrVirtCpu = 1;
+
+ return 0;
+}
+
+static char *lxcGetOSType(virDomainPtr dom ATTRIBUTE_UNUSED)
+{
+ /* Linux containers only run on Linux */
+ return strdup("linux");
+}
+
+static char *lxcDomainDumpXML(virDomainPtr dom,
+ int flags ATTRIBUTE_UNUSED)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
+ lxc_vm_t *vm = lxcFindVMByUUID(driver, dom->uuid);
+
+ if (!vm) {
+ lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
+ "no domain with matching uuid");
+ return NULL;
+ }
+
+ return lxcGenerateXML(dom->conn, driver, vm, vm->def, 1);
+}
+
+static int lxcStartup(virConnectPtr conn)
+{
+ lxc_driver_t *driver;
+
+ driver = calloc(1, sizeof(lxc_driver_t));
+ if (NULL == driver) {
+ return -1;
+ }
+
+ conn->privateData = driver;
+
+ /* Call function to load lxc driver configuration information */
+ if (lxcLoadDriverConfig(conn) < 0) {
+ lxcShutdown(conn);
+ return -1;
+ }
+
+ /* Call function to load the container configuration files */
+ if (lxcLoadContainerInfo(conn) < 0) {
+ lxcShutdown(conn);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void lxcFreeDriver(lxc_driver_t *driver)
+{
+ free(driver->configDir);
+ free(driver);
+}
+
+static int lxcShutdown(virConnectPtr conn)
+{
+ lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+ lxc_vm_t *vms = driver->vms;
+
+ lxcFreeVMs(vms);
+ driver->vms = NULL;
+ lxcFreeDriver(driver);
+ conn->privateData = NULL;
+
+ return 0;
+}
+
+/* Function Tables */
+static virDriver lxcDriver = {
+ VIR_DRV_LXC, /* the number virDrvNo */
+ "LXC", /* the name of the driver */
+ LIBVIR_VERSION_NUMBER, /* the version of the backend */
+ lxcOpen, /* open */
+ lxcClose, /* close */
+ NULL, /* supports_feature */
+ NULL, /* type */
+ NULL, /* version */
+ NULL, /* getHostname */
+ NULL, /* getURI */
+ NULL, /* getMaxVcpus */
+ NULL, /* nodeGetInfo */
+ NULL, /* getCapabilities */
+ lxcListDomains, /* listDomains */
+ lxcNumDomains, /* numOfDomains */
+ NULL/*lxcDomainCreateLinux*/, /* domainCreateLinux */
+ lxcDomainLookupByID, /* domainLookupByID */
+ lxcDomainLookupByUUID, /* domainLookupByUUID */
+ lxcDomainLookupByName, /* domainLookupByName */
+ NULL, /* domainSuspend */
+ NULL, /* domainResume */
+ NULL, /* domainShutdown */
+ NULL, /* domainReboot */
+ NULL, /* domainDestroy */
+ lxcGetOSType, /* domainGetOSType */
+ NULL, /* domainGetMaxMemory */
+ NULL, /* domainSetMaxMemory */
+ NULL, /* domainSetMemory */
+ lxcDomainGetInfo, /* domainGetInfo */
+ NULL, /* domainSave */
+ NULL, /* domainRestore */
+ NULL, /* domainCoreDump */
+ NULL, /* domainSetVcpus */
+ NULL, /* domainPinVcpu */
+ NULL, /* domainGetVcpus */
+ NULL, /* domainGetMaxVcpus */
+ lxcDomainDumpXML, /* domainDumpXML */
+ lxcListDefinedDomains, /* listDefinedDomains */
+ lxcNumDefinedDomains, /* numOfDefinedDomains */
+ NULL, /* domainCreate */
+ lxcDomainDefine, /* domainDefineXML */
+ lxcDomainUndefine, /* domainUndefine */
+ NULL, /* domainAttachDevice */
+ NULL, /* domainDetachDevice */
+ NULL, /* domainGetAutostart */
+ NULL, /* domainSetAutostart */
+ NULL, /* domainGetSchedulerType */
+ NULL, /* domainGetSchedulerParameters */
+ NULL, /* domainSetSchedulerParameters */
+ NULL, /* domainMigratePrepare */
+ NULL, /* domainMigratePerform */
+ NULL, /* domainMigrateFinish */
+ NULL, /* domainBlockStats */
+ NULL, /* domainInterfaceStats */
+ NULL, /* nodeGetCellsFreeMemory */
+ NULL, /* getFreeMemory */
+};
+
+int lxcRegister(void)
+{
+ virRegisterDriver(&lxcDriver);
+ return 0;
+}
+
+#endif /* WITH_LXC */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
Index: src/lxc_driver.h
===================================================================
RCS file: src/lxc_driver.h
diff -N src/lxc_driver.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/lxc_driver.h 19 Feb 2008 18:54:30 -0000
@@ -0,0 +1,45 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_driver.h: header file for linux container driver functions
+ *
+ * Authors:
+ * David L. Leskovec <dlesko at linux.vnet.ibm.com>
+ *
+ * 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
+ */
+
+#ifndef LXC_DRIVER_H
+#define LXC_DRIVER_H
+
+#include <config.h>
+
+#ifdef WITH_LXC
+
+/* Function declarations */
+int lxcRegister(void);
+
+#endif /* WITH_LXC */
+
+#endif /* LXC_DRIVER_H */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: nil
+ * c-indent-level: 4
+ * c-basic-offset: 4
+ * tab-width: 4
+ * End:
+ */
--
Best Regards,
Dave Leskovec
IBM Linux Technology Center
Open Virtualization
16 years, 9 months
[Libvir] [RFC] 2/3 Config functions for managing linux containers
by Dave Leskovec
This patch contains the new files lxc_conf.c and lxc_conf.h
Index: src/lxc_conf.c
===================================================================
RCS file: src/lxc_conf.c
diff -N src/lxc_conf.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/lxc_conf.c 20 Feb 2008 23:02:14 -0000
@@ -0,0 +1,902 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_conf.c: config functions for managing linux containers
+ *
+ * Authors:
+ * David L. Leskovec <dlesko at linux.vnet.ibm.com>
+ *
+ * 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
+ *
+ */
+
+/* includes */
+#include <config.h>
+
+#ifdef WITH_LXC
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+#include <libxml/xpath.h>
+
+#include "buf.h"
+#include "util.h"
+#include "uuid.h"
+
+#include "lxc_conf.h"
+
+/* debug macros */
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
+/* Functions */
+void lxcError(virConnectPtr conn, virDomainPtr dom, int code,
+ const char *fmt, ...)
+{
+ va_list args;
+ char errorMessage[LXC_MAX_ERROR_LEN];
+ const char *codeErrorMessage;
+
+ if (fmt) {
+ va_start(args, fmt);
+ vsnprintf(errorMessage, LXC_MAX_ERROR_LEN-1, fmt, args);
+ va_end(args);
+ } else {
+ errorMessage[0] = '\0';
+ }
+
+ codeErrorMessage = __virErrorMsg(code, fmt);
+ __virRaiseError(conn, dom, NULL, VIR_FROM_LXC, code, VIR_ERR_ERROR,
+ codeErrorMessage, errorMessage, NULL, 0, 0,
+ codeErrorMessage, errorMessage);
+}
+
+static inline int lxcIsEmptyXPathStringObj(xmlXPathObjectPtr xpathObj)
+{
+ if ((xpathObj == NULL) ||
+ (xpathObj->type != XPATH_STRING) ||
+ (xpathObj->stringval == NULL) ||
+ (xpathObj->stringval[0] == 0)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int lxcParseMountXML(virConnectPtr conn, xmlNodePtr nodePtr,
+ lxc_mount_t *lxcMount)
+{
+ xmlChar *fsType = NULL;
+ xmlNodePtr curNode;
+ xmlChar *mountSource = NULL;
+ xmlChar *mountTarget = NULL;
+ int strLen;
+ int rc = -1;
+
+ if (NULL == (fsType = xmlGetProp(nodePtr, BAD_CAST "type"))) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "missing filesystem type");
+ goto error;
+ }
+
+ if (xmlStrEqual(fsType, BAD_CAST "mount") == 0) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "invalid filesystem type");
+ goto error;
+ }
+
+ for (curNode = nodePtr->children;
+ NULL != curNode;
+ curNode = curNode->next) {
+ if (curNode->type != XML_ELEMENT_NODE) {
+ continue;
+ }
+
+ if ((mountSource == NULL) &&
+ (xmlStrEqual(curNode->name, BAD_CAST "source"))) {
+ mountSource = xmlGetProp(curNode, BAD_CAST "dir");
+ } else if ((mountTarget == NULL) &&
+ (xmlStrEqual(curNode->name, BAD_CAST "target"))) {
+ mountTarget = xmlGetProp(curNode, BAD_CAST "dir");
+ }
+ }
+
+ if (mountSource == NULL) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "missing mount source");
+ goto error;
+ }
+
+ strLen = xmlStrlen(mountSource);
+ if ((strLen > (PATH_MAX-1)) || (0 == strLen)) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "empty or invalid mount source");
+ goto error;
+ }
+
+ strncpy(lxcMount->source, (char *)mountSource, strLen);
+ lxcMount->source[strLen] = '\0';
+
+ if (mountTarget == NULL) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "missing mount target");
+ goto error;
+ }
+
+ strLen = xmlStrlen(mountTarget);
+ if ((strLen > (PATH_MAX-1)) || (0 == strLen)) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "empty or invalid mount target");
+ goto error;
+ }
+
+ strncpy(lxcMount->target, (char *)mountTarget, strLen);
+ lxcMount->target[strLen] = '\0';
+
+ rc = 0;
+
+error:
+ xmlFree(mountSource);
+ xmlFree(mountTarget);
+
+ return rc;
+}
+
+static int lxcParseDomainName(virConnectPtr conn, char **name,
+ xmlXPathContextPtr contextPtr)
+{
+ int rc = -1;
+ xmlXPathObjectPtr xpathObj = NULL;
+
+ xpathObj = xmlXPathEval(BAD_CAST "string(/domain/name[1])",
contextPtr);
+ if (lxcIsEmptyXPathStringObj(xpathObj)) {
+ lxcError(conn, NULL, VIR_ERR_NO_NAME, NULL);
+ goto parse_complete;
+ }
+
+ *name = strdup((const char *)xpathObj->stringval);
+
+ rc = 0;
+
+parse_complete:
+ xmlXPathFreeObject(xpathObj);
+ return rc;
+}
+
+static int lxcParseDomainUUID(virConnectPtr conn, unsigned char *uuid,
+ xmlXPathContextPtr contextPtr)
+{
+ int rc = -1;
+ xmlXPathObjectPtr xpathObj = NULL;
+
+ xpathObj = xmlXPathEval(BAD_CAST "string(/domain/uuid[1])",
contextPtr);
+ if (lxcIsEmptyXPathStringObj(xpathObj)) {
+ if ((rc = virUUIDGenerate(uuid))) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "failed to generate uuid: %s", strerror(rc));
+ goto parse_complete;
+ }
+ } else {
+ if (virUUIDParse((const char *)xpathObj->stringval, uuid) < 0) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "invalid uuid element");
+ goto parse_complete;
+ }
+ }
+
+ rc = 0;
+
+parse_complete:
+ xmlXPathFreeObject(xpathObj);
+ return rc;
+}
+
+static int lxcParseDomainMounts(virConnectPtr conn,
+ lxc_mount_t **mounts,
+ xmlXPathContextPtr contextPtr)
+{
+ int rc = -1;
+ xmlXPathObjectPtr xpathObj = NULL;
+ int i;
+ lxc_mount_t *mountObj;
+ lxc_mount_t *prevObj = NULL;
+ int nmounts = 0;
+
+ xpathObj = xmlXPathEval(BAD_CAST "/domain/container/filesystem",
+ contextPtr);
+ if ((xpathObj != NULL) &&
+ (xpathObj->type == XPATH_NODESET) &&
+ (xpathObj->nodesetval != NULL) &&
+ (xpathObj->nodesetval->nodeNr >= 0)) {
+ for (i = 0; i < xpathObj->nodesetval->nodeNr; ++i) {
+ mountObj = calloc(1, sizeof(lxc_mount_t));
+ if (NULL == mountObj) {
+ lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "mount");
+ goto parse_complete;
+ }
+
+ rc = lxcParseMountXML(conn, xpathObj->nodesetval->nodeTab[i],
+ mountObj);
+ if (0 > rc) {
+ free(mountObj);
+ goto parse_complete;
+ }
+
+ /* set the linked list pointers */
+ nmounts++;
+ mountObj->next = NULL;
+ if (0 == i) {
+ *mounts = mountObj;
+ } else {
+ prevObj->next = mountObj;
+ }
+ prevObj = mountObj;
+ }
+ }
+
+ rc = nmounts;
+
+parse_complete:
+ xmlXPathFreeObject(xpathObj);
+
+ return rc;
+}
+
+static int lxcParseDomainInit(virConnectPtr conn, char* init,
xmlXPathContextPtr contextPtr)
+{
+ int rc = -1;
+ xmlXPathObjectPtr xpathObj = NULL;
+
+ xpathObj = xmlXPathEval(BAD_CAST "string(/domain/container/init[1])",
+ contextPtr);
+ if (lxcIsEmptyXPathStringObj(xpathObj)) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "invalid or missing init element");
+ goto parse_complete;
+ }
+
+ if (strlen((const char*)xpathObj->stringval) >= PATH_MAX - 1) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "init string too long");
+ goto parse_complete;
+ }
+
+ strcpy(init, (const char *)xpathObj->stringval);
+
+ rc = 0;
+
+parse_complete:
+ xmlXPathFreeObject(xpathObj);
+
+ return rc;
+}
+
+
+static int lxcParseDomainTty(virConnectPtr conn, char *tty,
xmlXPathContextPtr contextPtr)
+{
+ int rc = -1;
+ xmlXPathObjectPtr xpathObj = NULL;
+
+ xpathObj = xmlXPathEval(BAD_CAST
"string(/domain/devices/console[1]/@tty)",
+ contextPtr);
+ if (lxcIsEmptyXPathStringObj(xpathObj)) {
+ /* make sure the tty string is empty */
+ tty[0] = 0x00;
+ } else {
+ /* check the source string length */
+ if (strlen((const char*)xpathObj->stringval) >=
LXC_MAX_TTY_NAME - 1) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "tty too long");
+ goto parse_complete;
+ }
+ strcpy(tty, (const char *)xpathObj->stringval);
+ }
+
+ rc = 0;
+
+parse_complete:
+ xmlXPathFreeObject(xpathObj);
+
+ return rc;
+}
+
+static int lxcParseDomainMemory(virConnectPtr conn, int* memory,
xmlXPathContextPtr contextPtr)
+{
+ int rc = -1;
+ xmlXPathObjectPtr xpathObj = NULL;
+ char *endChar = NULL;
+
+ xpathObj = xmlXPathEval(BAD_CAST "string(/domain/memory[1])",
contextPtr);
+ if (lxcIsEmptyXPathStringObj(xpathObj)) {
+ /* not an error, default to an invalid value so it's not used */
+ *memory = -1;
+ } else {
+ *memory = strtoll((const char*)xpathObj->stringval,
+ &endChar, 10);
+ if ((endChar == (const char*)xpathObj->stringval) ||
+ (*endChar != '\0')) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "invalid memory value");
+ goto parse_complete;
+ }
+ }
+
+ rc = 0;
+
+parse_complete:
+ xmlXPathFreeObject(xpathObj);
+
+ return rc;
+}
+
+static lxc_vm_def_t * lxcParseXML(virConnectPtr conn, xmlDocPtr docPtr)
+{
+ xmlNodePtr rootNodePtr = NULL;
+ xmlXPathContextPtr contextPtr = NULL;
+ xmlChar *xmlProp = NULL;
+ lxc_vm_def_t *containerDef;
+
+ if (!(containerDef = calloc(1, sizeof(*containerDef)))) {
+ lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "containerDef");
+ return NULL;
+ }
+
+ /* Prepare parser / xpath context */
+ rootNodePtr = xmlDocGetRootElement(docPtr);
+ if ((rootNodePtr == NULL) ||
+ (!xmlStrEqual(rootNodePtr->name, BAD_CAST "domain"))) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "invalid root element");
+ goto error;
+ }
+
+ contextPtr = xmlXPathNewContext(docPtr);
+ if (contextPtr == NULL) {
+ lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "context");
+ goto error;
+ }
+
+ /* Verify the domain type is linuxcontainer */
+ if (!(xmlProp = xmlGetProp(rootNodePtr, BAD_CAST "type"))) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "missing domain type");
+ goto error;
+ }
+
+ if (!(xmlStrEqual(xmlProp, BAD_CAST LXC_DOMAIN_TYPE))) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "invalid domain type");
+ goto error;
+ }
+ free(xmlProp);
+ xmlProp = NULL;
+
+ if (lxcParseDomainName(conn, &(containerDef->name), contextPtr) < 0) {
+ goto error;
+ }
+
+
+ if (lxcParseDomainUUID(conn, containerDef->uuid, contextPtr) < 0) {
+ goto error;
+ }
+
+ containerDef->nmounts = lxcParseDomainMounts(conn,
&(containerDef->mounts),
+ contextPtr);
+ if (0 > containerDef->nmounts) {
+ goto error;
+ }
+
+ if (lxcParseDomainInit(conn, containerDef->init, contextPtr) < 0) {
+ goto error;
+ }
+
+ if (lxcParseDomainTty(conn, containerDef->tty, contextPtr) < 0) {
+ goto error;
+ }
+
+ if (lxcParseDomainMemory(conn, &(containerDef->maxMemory),
contextPtr) < 0) {
+ goto error;
+ }
+
+ xmlXPathFreeContext(contextPtr);
+
+ return containerDef;
+
+ error:
+ free(xmlProp);
+ xmlXPathFreeContext(contextPtr);
+ lxcFreeVMDef(containerDef);
+
+ return NULL;
+}
+
+
+lxc_vm_def_t * lxcParseVMDef(virConnectPtr conn,
+ const char* xmlString,
+ const char* fileName)
+{
+ xmlDocPtr xml;
+ lxc_vm_def_t *containerDef;
+
+ xml = xmlReadDoc(BAD_CAST xmlString,
+ fileName ? fileName : "domain.xml",
+ NULL, XML_PARSE_NOENT |
+ XML_PARSE_NONET | XML_PARSE_NOERROR |
+ XML_PARSE_NOWARNING);
+ if (!xml) {
+ lxcError(conn, NULL, VIR_ERR_XML_ERROR, NULL);
+ return NULL;
+ }
+
+ containerDef = lxcParseXML(conn, xml);
+
+ xmlFreeDoc(xml);
+
+ return containerDef;
+}
+
+lxc_vm_t * lxcAssignVMDef(virConnectPtr conn,
+ lxc_driver_t *driver,
+ lxc_vm_def_t *def)
+{
+ lxc_vm_t *vm = NULL;
+
+ if ((vm = lxcFindVMByName(driver, def->name))) {
+ if (!lxcIsActiveVM(vm)) {
+ lxcFreeVMDef(vm->def);
+ vm->def = def;
+ } else {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "Can't redefine active VM with name %s", def->name);
+ return NULL;
+ }
+
+ return vm;
+ }
+
+ if (!(vm = calloc(1, sizeof(lxc_vm_t)))) {
+ lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "vm");
+ return NULL;
+ }
+
+ vm->pid = -1;
+ vm->id = -1;
+ vm->state = VIR_DOMAIN_SHUTOFF;
+ vm->def = def;
+ vm->next = driver->vms;
+
+ driver->vms = vm;
+ driver->ninactivevms++;
+
+ return vm;
+}
+
+void lxcRemoveInactiveVM(lxc_driver_t *driver,
+ lxc_vm_t *vm)
+{
+ lxc_vm_t *prevVm = NULL;
+ lxc_vm_t *curVm;
+
+ for (curVm = driver->vms;
+ (curVm != vm) && (NULL != curVm);
+ curVm = curVm->next) {
+ prevVm = curVm;
+ }
+
+ if (curVm) {
+ if (prevVm) {
+ prevVm->next = curVm->next;
+ } else {
+ driver->vms = curVm->next;
+ }
+
+ driver->ninactivevms--;
+ }
+
+ lxcFreeVM(vm);
+}
+
+/* Save a container's config data into a persistent file */
+static int lxcSaveConfig(virConnectPtr conn,
+ lxc_driver_t *driver,
+ lxc_vm_t *vm,
+ lxc_vm_def_t *def)
+{
+ int rc = -1;
+ char *xmlDef;
+ int fd = -1;
+ int amtToWrite;
+
+ if (!(xmlDef = lxcGenerateXML(conn, driver, vm, def, 0))) {
+ return -1;
+ }
+
+ if ((fd = open(vm->configFile,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR )) < 0) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "cannot create config file %s: %s",
+ vm->configFile, strerror(errno));
+ goto cleanup;
+ }
+
+ amtToWrite = strlen(xmlDef);
+ if (safewrite(fd, xmlDef, amtToWrite) < 0) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "cannot write config file %s: %s",
+ vm->configFile, strerror(errno));
+ goto cleanup;
+ }
+
+ if (close(fd) < 0) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "cannot save config file %s: %s",
+ vm->configFile, strerror(errno));
+ goto cleanup;
+ }
+
+ rc = 0;
+
+ cleanup:
+ if (fd != -1) {
+ close(fd);
+ }
+
+ free(xmlDef);
+
+ return rc;
+}
+
+int lxcSaveVMDef(virConnectPtr conn,
+ lxc_driver_t *driver,
+ lxc_vm_t *vm,
+ lxc_vm_def_t *def)
+{
+ int rc = -1;
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ if (vm->configFile[0] == '\0') {
+ if ((rc = virFileMakePath(driver->configDir))) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "cannot create config directory %s: %s",
+ driver->configDir, strerror(rc));
+ goto save_complete;
+ }
+
+ virUUIDFormat(def->uuid, uuidstr);
+ if (virFileBuildPath(driver->configDir, uuidstr, ".xml",
+ vm->configFile, PATH_MAX) < 0) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "cannot construct config file path");
+ goto save_complete;
+ }
+
+ }
+
+ rc = lxcSaveConfig(conn, driver, vm, def);
+
+save_complete:
+ return rc;
+}
+
+
+
+static lxc_vm_t * lxcLoadConfig(lxc_driver_t *driver,
+ const char *file,
+ const char *fullFilePath,
+ const char *xmlData)
+{
+ lxc_vm_def_t *containerDef;
+ lxc_vm_t * vm;
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+ containerDef = lxcParseVMDef(NULL, xmlData, file);
+ if (NULL == containerDef) {
+ DEBUG0("Error parsing container config");
+ return NULL;
+ }
+
+ virUUIDFormat(containerDef->uuid, uuidstr);
+ if (!virFileMatchesNameSuffix(file, uuidstr, ".xml")) {
+ DEBUG0("Container name does not match config file name");
+ lxcFreeVMDef(containerDef);
+ return NULL;
+ }
+
+ vm = lxcAssignVMDef(NULL, driver, containerDef);
+ if (NULL == vm) {
+ DEBUG0("Failed to load container config");
+ lxcFreeVMDef(containerDef);
+ return NULL;
+ }
+
+ strncpy(vm->configFile, fullFilePath, PATH_MAX);
+ vm->configFile[PATH_MAX-1] = '\0';
+
+ return vm;
+}
+
+int lxcLoadDriverConfig(virConnectPtr conn)
+{
+ lxc_driver_t *driverPtr = (lxc_driver_t*)conn->privateData;
+
+ /* Set the container configuration directory */
+ driverPtr->configDir = strdup("/etc/libvirt/lxc");
+
+ return 0;
+}
+
+int lxcLoadContainerInfo(virConnectPtr conn)
+{
+ int rc = -1;
+ lxc_driver_t *driverPtr = (lxc_driver_t*)conn->privateData;
+ DIR *dir;
+ struct dirent *dirEntry;
+ char tempPath[PATH_MAX];
+ char* xmlData;
+
+ if (!(dir = opendir(driverPtr->configDir))) {
+ if (ENOENT == errno) {
+ /* no config dir => no containers */
+ rc = 0;
+ } else {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "failed to open config directory: %s",
strerror(errno));
+ }
+
+ goto load_complete;
+ }
+
+ while ((dirEntry = readdir(dir))) {
+ if (dirEntry->d_name[0] == '.') {
+ continue;
+ }
+
+ if (!virFileHasSuffix(dirEntry->d_name, ".xml")) {
+ continue;
+ }
+
+ rc = virFileBuildPath(driverPtr->configDir, dirEntry->d_name, NULL,
+ tempPath, PATH_MAX);
+ if (0 > rc) {
+ DEBUG0("config file name too long");
+ continue;
+ }
+
+ if (virFileReadAll(tempPath, LXC_MAX_XML_LENGTH, &xmlData) < 0) {
+ continue;
+ }
+
+ lxcLoadConfig(driverPtr, dirEntry->d_name, tempPath, xmlData);
+
+ free(xmlData);
+ }
+
+ closedir(dir);
+
+ rc = 0;
+
+load_complete:
+ return rc;
+}
+
+/* Generate an XML document describing the vm's configuration */
+char *lxcGenerateXML(virConnectPtr conn,
+ lxc_driver_t *driver ATTRIBUTE_UNUSED,
+ lxc_vm_t *vm,
+ lxc_vm_def_t *def,
+ int live)
+{
+ virBufferPtr buf = 0;
+ unsigned char *uuid;
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ lxc_mount_t *mount;
+
+ buf = virBufferNew(LXC_MAX_XML_LENGTH);
+ if (!buf) {
+ goto no_memory;
+ }
+
+ if (lxcIsActiveVM(vm) && live) {
+ if (virBufferVSprintf(buf, "<domain type='linuxcontainer'
id='%d'>\n",
+ vm->id) < 0) {
+ goto no_memory;
+ }
+ } else {
+ if (virBufferVSprintf(buf, "<domain type='linuxcontainer'>\n")
< 0) {
+ goto no_memory;
+ }
+ }
+
+ if (virBufferVSprintf(buf, " <name>%s</name>\n", def->name) < 0) {
+ goto no_memory;
+ }
+
+ uuid = def->uuid;
+ virUUIDFormat(uuid, uuidstr);
+ if (virBufferVSprintf(buf, " <uuid>%s</uuid>\n", uuidstr) < 0) {
+ goto no_memory;
+ }
+
+ if (virBufferVSprintf(buf, " <container>\n") < 0) {
+ goto no_memory;
+ }
+
+ /* loop adding mounts */
+ for (mount = def->mounts; mount; mount = mount->next) {
+ if (virBufferVSprintf(buf, " <filesystem
type='mount'>\n") < 0) {
+ goto no_memory;
+ }
+
+ if (virBufferVSprintf(buf, " <source dir='%s'/>\n",
+ mount->source) < 0) {
+ goto no_memory;
+ }
+
+ if (virBufferVSprintf(buf, " <target dir='%s'/>\n",
+ mount->target) < 0) {
+ goto no_memory;
+ }
+
+ if (virBufferVSprintf(buf, " </filesystem>\n") < 0) {
+ goto no_memory;
+ }
+
+ }
+
+ if (virBufferVSprintf(buf, " <init>%s</init>\n", def->init)
< 0) {
+ goto no_memory;
+ }
+
+ if (virBufferVSprintf(buf, " </container>\n") < 0) {
+ goto no_memory;
+ }
+
+
+ if (virBufferVSprintf(buf, " <memory>%d</memory>\n",
def->maxMemory) < 0) {
+ goto no_memory;
+ }
+
+ if (virBufferVSprintf(buf, " <devices>\n") < 0) {
+ goto no_memory;
+ }
+
+ if (virBufferVSprintf(buf, " <console tty='%s'/>\n",
def->tty) < 0) {
+ goto no_memory;
+ }
+
+ if (virBufferAddLit(buf, " </devices>\n") < 0) {
+ goto no_memory;
+ }
+
+ if (virBufferAddLit(buf, "</domain>\n") < 0) {
+ goto no_memory;
+ }
+
+ return virBufferContentAndFree(buf);
+
+no_memory:
+ lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "generateXml");
+ virBufferFree(buf);
+
+ return NULL;
+}
+
+void lxcFreeVMDef(lxc_vm_def_t *vmdef)
+{
+ lxc_mount_t *curMount = vmdef->mounts;
+ lxc_mount_t *nextMount;
+
+ while (curMount) {
+ nextMount = curMount->next;
+ free(curMount);
+ curMount = nextMount;
+ }
+
+ free(vmdef->name);
+ vmdef->name = NULL;
+
+}
+
+void lxcFreeVMs(lxc_vm_t *vms)
+{
+ lxc_vm_t *curVm = vms;
+ lxc_vm_t *nextVm;
+
+ while (curVm) {
+ lxcFreeVM(curVm);
+ nextVm = curVm->next;
+ free(curVm);
+ curVm = nextVm;
+ }
+}
+
+void lxcFreeVM(lxc_vm_t *vm)
+{
+ lxcFreeVMDef(vm->def);
+ free(vm->def);
+}
+
+lxc_vm_t *lxcFindVMByID(const lxc_driver_t *driver, int id)
+{
+ lxc_vm_t *vm;
+
+ for (vm = driver->vms; vm; vm = vm->next) {
+ if (lxcIsActiveVM(vm) && (vm->id == id)) {
+ return vm;
+ }
+
+ }
+
+ return NULL;
+}
+
+lxc_vm_t *lxcFindVMByUUID(const lxc_driver_t *driver,
+ const unsigned char *uuid)
+{
+ lxc_vm_t *vm;
+
+ for (vm = driver->vms; vm; vm = vm->next) {
+ if (!memcmp(vm->def->uuid, uuid, VIR_UUID_BUFLEN)) {
+ return vm;
+ }
+ }
+
+ return NULL;
+}
+
+lxc_vm_t *lxcFindVMByName(const lxc_driver_t *driver,
+ const char *name)
+{
+ lxc_vm_t *vm;
+
+ for (vm = driver->vms; vm; vm = vm->next) {
+ if (!strcmp(vm->def->name, name)) {
+ return vm;
+ }
+ }
+
+ return NULL;
+}
+
+int lxcDeleteConfig(virConnectPtr conn,
+ lxc_driver_t *driver ATTRIBUTE_UNUSED,
+ const char *configFile,
+ const char *name)
+{
+ if (!configFile[0]) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "no config file for %s", name);
+ return -1;
+ }
+
+ if (unlink(configFile) < 0) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ "cannot remove config for %s", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* WITH_LXC */
+
Index: src/lxc_conf.h
===================================================================
RCS file: src/lxc_conf.h
diff -N src/lxc_conf.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ src/lxc_conf.h 20 Feb 2008 23:02:14 -0000
@@ -0,0 +1,134 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_conf.h: header file for linux container config functions
+ *
+ * Authors:
+ * David L. Leskovec <dlesko at linux.vnet.ibm.com>
+ *
+ * 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
+ */
+
+#ifndef LXC_CONF_H
+#define LXC_CONF_H
+
+#include <config.h>
+
+#ifdef WITH_LXC
+
+#include "internal.h"
+
+/* Defines */
+#define LXC_MAX_TTY_NAME 32
+#define LXC_MAX_XML_LENGTH 4096
+#define LXC_MAX_ERROR_LEN 1024
+#define LXC_DOMAIN_TYPE "linuxcontainer"
+
+typedef struct __lxc_mount lxc_mount_t;
+struct __lxc_mount {
+ char source[PATH_MAX]; /* user's directory */
+ char target[PATH_MAX];
+
+ lxc_mount_t *next;
+};
+
+typedef struct __lxc_vm_def lxc_vm_def_t;
+struct __lxc_vm_def {
+ unsigned char uuid[VIR_UUID_BUFLEN];
+ char* name;
+
+ /* init command string */
+ char init[PATH_MAX];
+
+ int maxMemory;
+
+ /* mounts - list of mount structs */
+ int nmounts;
+ lxc_mount_t *mounts;
+
+ /* tty device */
+ char tty[LXC_MAX_TTY_NAME];
+};
+
+typedef struct __lxc_vm lxc_vm_t;
+struct __lxc_vm {
+ int pid;
+ int id;
+ int state;
+
+ char configFile[PATH_MAX];
+
+ lxc_vm_def_t *def;
+
+ lxc_vm_t *next;
+};
+
+typedef struct __lxc_driver lxc_driver_t;
+struct __lxc_driver {
+ lxc_vm_t *vms;
+ int nactivevms;
+ int ninactivevms;
+ char* configDir;
+};
+
+/* Types and structs */
+
+/* Inline Functions */
+static inline int lxcIsActiveVM(lxc_vm_t *vm)
+{
+ return vm->id != -1;
+}
+
+/* Function declarations */
+lxc_vm_def_t * lxcParseVMDef(virConnectPtr conn,
+ const char* xmlString,
+ const char* fileName);
+int lxcSaveVMDef(virConnectPtr conn,
+ lxc_driver_t *driver,
+ lxc_vm_t *vm,
+ lxc_vm_def_t *def);
+int lxcLoadDriverConfig(virConnectPtr conn);
+int lxcLoadContainerInfo(virConnectPtr conn);
+lxc_vm_t * lxcAssignVMDef(virConnectPtr conn,
+ lxc_driver_t *driver,
+ lxc_vm_def_t *def);
+char *lxcGenerateXML(virConnectPtr conn,
+ lxc_driver_t *driver,
+ lxc_vm_t *vm,
+ lxc_vm_def_t *def,
+ int live);
+lxc_vm_t *lxcFindVMByID(const lxc_driver_t *driver, int id);
+lxc_vm_t *lxcFindVMByUUID(const lxc_driver_t *driver,
+ const unsigned char *uuid);
+lxc_vm_t *lxcFindVMByName(const lxc_driver_t *driver,
+ const char *name);
+void lxcRemoveInactiveVM(lxc_driver_t *driver,
+ lxc_vm_t *vm);
+void lxcFreeVMs(lxc_vm_t *vms);
+void lxcFreeVM(lxc_vm_t *vm);
+void lxcFreeVMDef(lxc_vm_def_t *vmdef);
+int lxcDeleteConfig(virConnectPtr conn,
+ lxc_driver_t *driver,
+ const char *configFile,
+ const char *name);
+
+void lxcError(virConnectPtr conn,
+ virDomainPtr dom,
+ int code, const char *fmt, ...)
+ ATTRIBUTE_FORMAT(printf,4,5);
+
+#endif /* WITH_LXC */
+#endif /* LXC_CONF_H */
+
--
Best Regards,
Dave Leskovec
IBM Linux Technology Center
Open Virtualization
16 years, 9 months
[Libvir] RFC: Updated async job public API
by Daniel P. Berrange
A little over a month ago I proposed an API extension for enabling long
operations to be done as asynchronous background jobs.
http://www.redhat.com/archives/libvir-list/2008-January/msg00040.html
While the proof of concept definitely worked, having played around & thought
about it a little more I feel we can do better than that initial proposal.
Some things I didn't like about it:
- Complicated reference counting. This was because jobs may or may not be
tied to a virDomainPtr object depending on whether the job was a creation
attempt, or an operation on an existing object.
- Complicated app code because they now had to track both Job & Domain
objects together.
- Added too many new APIs - this was a consequence of needing the separate
top level virJob object, adding lots of XXXJob variants for APIs.
- Complicated internal drivers due to the separate Job object.
So to try and address this I've come up with an alternative, simpler design
for this. The key idea is to get rid of the top level 'Job' object and make
use of the 'flags' field present in (almost) all the APIs which need to have
async behaviour.
First of all, for the couple of APIs which don't have 'flags' we add new
variants taking a 'flags' parameter.
For each API needing to allow async operation, we define a flag for this
eg,
virDomainCreateXML -> VIR_DOMAIN_CREATE_XML_ASYNC
virDomainRestore -> VIR_DOMAIN_RESTORE_ASYNC
virStoragePoolBuild -> VIR_STORAGE_POOL_BUILD_ASYNC
If the flag is omitted, current semantics are unchanged, so existing apps
are not impacted.
If you pass the appropriate XXX_ASYNC flag, then you cannot assume that
the operation is complete upon returning from the call. To check completion
status you ask for the virJobInfo data. This can be done with whichever of
these APIs matches:
virDomainGetJobInfo(virDomainPtr dom, virJobInfoPtr job)
virStoragePoolGetJobInfo(virStoragePoolPtr vol, virJobInfoPtr job)
virStorageVolGetJobInfo(virStorageVolPtr vol, virJobInfoPtr job)
Finally, you can cancel an operation:
virDomainCancelJob(virDomainPtr dom);
virStoragePoolCancelJob(virStoragePoolPtr pool);
virStorageVolCancelJob(virStorageVolPtr vol);
If you passed the XXX_ASYNC flag, it is forbidden to call any APIs on the
object other than the XXXCancelJob() or XXXGetJobInfo() methods, until
the job is complete. This was true of the previous proof of concept I did
but I didn't spell it out at the time.
I've not done the intenral driver impl of this proposal yet, but it will
be easier/clearer than the previous impl, and the virsh changes will be
much less intrusive since we're merely passing a flag instead of calling
a completely separate API.
The patch below illustrates the proposed public API - note how its only
adding a handful of new entry points - 3 virDomain methods needed an extra
flags parameter - all the storage APIs were written to take flags already
Dan.
diff -r 3b8d18bc4d3e include/libvirt/libvirt.h.in
--- a/include/libvirt/libvirt.h.in Wed Feb 20 12:20:50 2008 -0500
+++ b/include/libvirt/libvirt.h.in Wed Feb 20 14:14:09 2008 -0500
@@ -408,6 +408,62 @@ char * virConnectGetCap
unsigned long long virNodeGetFreeMemory (virConnectPtr conn);
+
+
+/*
+ * Asynchronous background jobs
+ */
+
+/**
+ * virJobType;
+ *
+ * A job may have a finite bounded progress, or may be
+ * unbounded.
+ */
+typedef enum {
+ VIR_JOB_BOUNDED = 0, /* finite, 0-> 100 completion */
+ VIR_JOB_UNBOUNDED = 1, /* unknown completion percent */
+} virJobType;
+
+
+/**
+ * virJobState;
+ *
+ * A job may be in one of several states
+ */
+typedef enum {
+ VIR_JOB_RUNNING = 0, /* Still active */
+ VIR_JOB_COMPLETE = 1, /* Completed successfully */
+ VIR_JOB_FAILED = 2, /* Failed to complete see virJobGetError */
+ VIR_JOB_CANCELLED = 3, /* User requested cancellation */
+} virJobState;
+
+/**
+ * virJobInfoPtr:
+ *
+ * a virJobInfo is a structure filled by virJobGetInfo() and extracting
+ * runtime informations for a given active Job
+ */
+
+typedef struct _virJobInfo virJobInfo;
+
+struct _virJobInfo {
+ int type; /* One of virJobType constants */
+ int state; /* One of virJobState constants */
+ unsigned int runningTime; /* Actual running time in seconds */
+ unsigned int remainingTime;/* Estimated remaining time in seconds */
+ int percentComplete; /* Completion progress 0 -> 100, if VIR_JOB_BOUNDED */
+};
+
+/**
+ * virJobInfoPtr:
+ *
+ * a virJobInfoPtr is a pointer to a virJobInfo structure.
+ */
+
+typedef virJobInfo *virJobInfoPtr;
+
+
/*
* Gather list of running domains
*/
@@ -447,6 +503,11 @@ int virDomainDestroy (virDomainPtr dom
int virDomainDestroy (virDomainPtr domain);
int virDomainFree (virDomainPtr domain);
+
+int virDomainGetJobInfo (virDomainPtr dom,
+ virJobInfoPtr info);
+int virDomainCancelJob (virDomainPtr dom);
+
/*
* Domain suspend/resume
*/
@@ -458,8 +519,14 @@ int virDomainResume (virDomainPtr dom
*/
int virDomainSave (virDomainPtr domain,
const char *to);
+int virDomainSaveFlags (virDomainPtr domain,
+ const char *to,
+ unsigned int flags);
int virDomainRestore (virConnectPtr conn,
const char *from);
+int virDomainRestoreFlags (virConnectPtr conn,
+ const char *from,
+ unsigned int flags);
/*
* Domain core dump
@@ -535,6 +602,8 @@ int virConnectListDefinedDomains (virC
char **const names,
int maxnames);
int virDomainCreate (virDomainPtr domain);
+int virDomainCreateFlags (virDomainPtr domain,
+ unsigned int flags);
int virDomainGetAutostart (virDomainPtr domain,
int *autostart);
@@ -897,6 +966,11 @@ int virStoragePoolRefresh (virStorage
int virStoragePoolRefresh (virStoragePoolPtr pool,
unsigned int flags);
+int virStoragePoolGetJobInfo (virStoragePoolPtr pool,
+ virJobInfoPtr info);
+int virStoragePoolCancelJob (virStoragePoolPtr pool);
+
+
/*
* StoragePool information
*/
@@ -955,6 +1029,10 @@ char * virStorageVolGetXMLDesc (virSt
char * virStorageVolGetPath (virStorageVolPtr vol);
+int virStorageVolGetJobInfo (virStoragePoolPtr pool,
+ virJobInfoPtr info);
+int virStorageVolCancelJob (virStoragePoolPtr pool);
+
#ifdef __cplusplus
}
#endif
.
--
|=- 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 -=|
16 years, 9 months